-
Notifications
You must be signed in to change notification settings - Fork 123
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
XCOMMONS-2921: Add support for overwritting the configuration from the execution context #760
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not happy about this one, but I don't have a better option. I'm open to suggestions. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not sure what we need is actually a configuration specific thing. To me, the real need is to pass a callable which is executed between an ExecutionContext set/unset and that's useful for various other use cases than just the configuration. You would then simply call the default ConfigutationSource in the callable you pass to the helper. For example, it could simply be something like There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Actually, that proposal does not work well with custom storage in the ExecutionContext, which is most probably the case of the ExecutionContext ConfigurationSource. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
/* | ||
* See the NOTICE file distributed with this work for additional | ||
* information regarding copyright ownership. | ||
* | ||
* This is free software; you can redistribute it and/or modify it | ||
* under the terms of the GNU Lesser General Public License as | ||
* published by the Free Software Foundation; either version 2.1 of | ||
* the License, or (at your option) any later version. | ||
* | ||
* This software is distributed in the hope that it will be useful, | ||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
* Lesser General Public License for more details. | ||
* | ||
* You should have received a copy of the GNU Lesser General Public | ||
* License along with this software; if not, write to the Free | ||
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA | ||
* 02110-1301 USA, or see the FSF site: http://www.fsf.org. | ||
*/ | ||
package org.xwiki.configuration; | ||
|
||
import java.util.Map; | ||
import java.util.concurrent.Callable; | ||
|
||
import org.xwiki.component.annotation.Role; | ||
import org.xwiki.stability.Unstable; | ||
|
||
/** | ||
* Executes a {@link Callable} using a temporary configuration. | ||
* | ||
* @version $Id$ | ||
* @since 16.1.0RC1 | ||
* @since 15.10.6 | ||
*/ | ||
@Role | ||
@Unstable | ||
public interface TemporaryConfigurationExecutor | ||
{ | ||
/** | ||
* Executes the passed {@link Callable} using the given temporary configuration. | ||
* | ||
* @param sourceHint indicates the configuration source that should receive the temporary configuration | ||
* @param temporaryConfiguration the temporary configuration to use while executing the passed {@link Callable} | ||
* @param callable the code to execute | ||
* @param <V> the type of value returned by the passed {@link Callable} | ||
* @return the value returned by the passed {@link Callable} | ||
* @throws Exception if the passed {@link Callable} throws an exception | ||
*/ | ||
<V> V call(String sourceHint, Map<String, Object> temporaryConfiguration, Callable<V> callable) throws Exception; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,122 @@ | ||
/* | ||
* See the NOTICE file distributed with this work for additional | ||
* information regarding copyright ownership. | ||
* | ||
* This is free software; you can redistribute it and/or modify it | ||
* under the terms of the GNU Lesser General Public License as | ||
* published by the Free Software Foundation; either version 2.1 of | ||
* the License, or (at your option) any later version. | ||
* | ||
* This software is distributed in the hope that it will be useful, | ||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
* Lesser General Public License for more details. | ||
* | ||
* You should have received a copy of the GNU Lesser General Public | ||
* License along with this software; if not, write to the Free | ||
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA | ||
* 02110-1301 USA, or see the FSF site: http://www.fsf.org. | ||
*/ | ||
package org.xwiki.configuration.internal; | ||
|
||
import java.util.ArrayList; | ||
import java.util.List; | ||
import java.util.Map; | ||
|
||
import org.apache.commons.beanutils.ConvertUtils; | ||
|
||
/** | ||
* Base class for configuration sources that store the configuration in memory. | ||
* | ||
* @version $Id$ | ||
* @since 16.1.0RC1 | ||
* @since 15.10.6 | ||
*/ | ||
public abstract class AbstractMemoryConfigurationSource extends AbstractConfigurationSource | ||
{ | ||
protected abstract Map<String, Object> getProperties(); | ||
|
||
@Override | ||
public void setProperties(Map<String, Object> newProperties) | ||
{ | ||
Map<String, Object> currentProperties = getProperties(); | ||
currentProperties.clear(); | ||
currentProperties.putAll(newProperties); | ||
} | ||
|
||
@Override | ||
public void setProperty(String key, Object value) | ||
{ | ||
getProperties().put(key, value); | ||
} | ||
|
||
@SuppressWarnings("unchecked") | ||
@Override | ||
public <T> T removeProperty(String key) | ||
{ | ||
return (T) getProperties().remove(key); | ||
} | ||
|
||
@SuppressWarnings("unchecked") | ||
@Override | ||
public <T> T getProperty(String key, T defaultValue) | ||
{ | ||
T result; | ||
|
||
if (getProperties().containsKey(key)) { | ||
Object value = getProperties().get(key); | ||
if (value != null && defaultValue != null && !defaultValue.getClass().isInstance(value)) { | ||
value = ConvertUtils.convert(value, defaultValue.getClass()); | ||
} | ||
result = (T) value; | ||
} else { | ||
result = defaultValue; | ||
} | ||
|
||
return result; | ||
} | ||
|
||
@SuppressWarnings("unchecked") | ||
@Override | ||
public <T> T getProperty(String key, Class<T> valueClass) | ||
{ | ||
T result; | ||
|
||
if (getProperties().containsKey(key)) { | ||
Object value = getProperties().get(key); | ||
if (value != null && valueClass != null && !valueClass.isInstance(value)) { | ||
value = ConvertUtils.convert(value, valueClass); | ||
} | ||
result = (T) value; | ||
} else { | ||
result = getDefault(valueClass); | ||
} | ||
|
||
return result; | ||
} | ||
|
||
@SuppressWarnings("unchecked") | ||
@Override | ||
public <T> T getProperty(String key) | ||
{ | ||
return (T) getProperties().get(key); | ||
} | ||
|
||
@Override | ||
public List<String> getKeys() | ||
{ | ||
return new ArrayList<>(getProperties().keySet()); | ||
} | ||
|
||
@Override | ||
public boolean containsKey(String key) | ||
{ | ||
return getProperties().containsKey(key); | ||
} | ||
|
||
@Override | ||
public boolean isEmpty() | ||
{ | ||
return getProperties().isEmpty(); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
/* | ||
* See the NOTICE file distributed with this work for additional | ||
* information regarding copyright ownership. | ||
* | ||
* This is free software; you can redistribute it and/or modify it | ||
* under the terms of the GNU Lesser General Public License as | ||
* published by the Free Software Foundation; either version 2.1 of | ||
* the License, or (at your option) any later version. | ||
* | ||
* This software is distributed in the hope that it will be useful, | ||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
* Lesser General Public License for more details. | ||
* | ||
* You should have received a copy of the GNU Lesser General Public | ||
* License along with this software; if not, write to the Free | ||
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA | ||
* 02110-1301 USA, or see the FSF site: http://www.fsf.org. | ||
*/ | ||
package org.xwiki.configuration.internal; | ||
|
||
import java.util.HashMap; | ||
import java.util.Map; | ||
import java.util.concurrent.Callable; | ||
|
||
import javax.inject.Inject; | ||
import javax.inject.Named; | ||
import javax.inject.Provider; | ||
import javax.inject.Singleton; | ||
|
||
import org.apache.commons.lang3.tuple.ImmutablePair; | ||
import org.apache.commons.lang3.tuple.Pair; | ||
import org.xwiki.component.annotation.Component; | ||
import org.xwiki.component.manager.ComponentLookupException; | ||
import org.xwiki.component.manager.ComponentManager; | ||
import org.xwiki.configuration.ConfigurationSaveException; | ||
import org.xwiki.configuration.ConfigurationSource; | ||
import org.xwiki.configuration.TemporaryConfigurationExecutor; | ||
|
||
/** | ||
* Default implementation of {@link TemporaryConfigurationExecutor}. | ||
* | ||
* @version $Id$ | ||
* @since 16.1.0RC1 | ||
* @since 15.10.6 | ||
*/ | ||
@Component | ||
@Singleton | ||
public class DefaultTemporaryConfigurationExecutor implements TemporaryConfigurationExecutor | ||
{ | ||
@Inject | ||
@Named("context") | ||
private Provider<ComponentManager> componentManagerProvider; | ||
|
||
@Override | ||
public <V> V call(String sourceHint, Map<String, Object> temporaryConfiguration, Callable<V> callable) | ||
throws Exception | ||
{ | ||
ConfigurationSource configurationSource = getConfigurationSource(sourceHint); | ||
Map<String, Pair<Boolean, Object>> backup = setConfiguration(configurationSource, temporaryConfiguration); | ||
try { | ||
return callable.call(); | ||
} finally { | ||
restoreConfiguration(configurationSource, backup); | ||
} | ||
} | ||
|
||
private ConfigurationSource getConfigurationSource(String sourceHint) throws ComponentLookupException | ||
{ | ||
ComponentManager componentManager = this.componentManagerProvider.get(); | ||
return componentManager.getInstance(ConfigurationSource.class, sourceHint); | ||
} | ||
|
||
private Map<String, Pair<Boolean, Object>> setConfiguration(ConfigurationSource configurationSource, | ||
Map<String, Object> temporaryConfiguration) throws ConfigurationSaveException | ||
{ | ||
Map<String, Pair<Boolean, Object>> backup = new HashMap<>(); | ||
for (Map.Entry<String, Object> entry : temporaryConfiguration.entrySet()) { | ||
backup.put(entry.getKey(), new ImmutablePair<>(configurationSource.containsKey(entry.getKey()), | ||
configurationSource.getProperty(entry.getKey()))); | ||
configurationSource.setProperty(entry.getKey(), entry.getValue()); | ||
} | ||
return backup; | ||
} | ||
|
||
private void restoreConfiguration(ConfigurationSource configurationSource, | ||
Map<String, Pair<Boolean, Object>> backup) throws ConfigurationSaveException | ||
{ | ||
for (Map.Entry<String, Pair<Boolean, Object>> entry : backup.entrySet()) { | ||
if (Boolean.TRUE.equals(entry.getValue().getLeft())) { | ||
// The property existed before, restore its previous value. | ||
configurationSource.setProperty(entry.getKey(), entry.getValue().getRight()); | ||
} else { | ||
// The property didn't exist before, remove it. | ||
configurationSource.removeProperty(entry.getKey()); | ||
} | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The alternative was to put the code in
xwiki-commons-context
and add there the dependency onxwiki-commons-configuration-api
, but @tmortagne noted that it's more probable for a module that requires the configuration API to also require the context API, than the other way.