Hi everyone,
I created XCOMMONS-2921: Add support for overwritting the configuration from the execution context by mflorea · Pull Request #760 · xwiki/xwiki-commons · GitHub . The part that doesn’t feel right is the TemporaryConfigurationExecutor
.
<V> V call(String sourceHint, Map<String, Object> temporaryConfiguration, Callable<V> callable) throws Exception;
I had a discussion with @tmortagne where he suggested that in the end it’s probably better to add the temporarySetProperty
method to the ConfigurationSource
role and implement an abstract configuration source that uses ThreadLocal
to overwrite the configuration properties for the current thread (configuration sources are normally shared by all threads and a temporary configuration overwrite should affect only the current thread). One benefit of this is that you can overwrite the configuration at a specific level without affecting the entire chain, but I’m not sure how useful this is.
While trying to implement Thomas’ suggestion (based on Simon’s suggestion) I discovered that it poses some problems:
- Even if we provide an abstract class, existing configuration sources can’t “receive” the new behavior without refactoring (i.e. the
getProperty
methods have to be modified to call the base class first, to check the thread local variable, and then fall-back on the standard behavior)if (hasTemporaryProperty(key)) {
return getTemporaryProperty(key);
} else {
// getPermanentProperty() is the abstract method that concrete implementations have to provide,.
return getPermanentProperty(key);
}
- The code executed with temporary configuration can call
setProperty
. What should be the behavior in this case? I think it makes sense for the setProperty
to overwrite the thread local value (at least that’s what the code calling setProperty
expects), but then we end up with two sets of permanent properties: those set before the temporary call and those set within the temporary call. The temporary properties would overwrite the first group but be overwritten by the second group. We can find ways to implement this I guess, but it feels too complex.configSource.temporarySetProperties(map, () -> {
// Somewhere in a nested call:
configSource.setProperty(key, value);
// expecting getProperty to return the set value, not the previously set temporary value.
});
- The code executed with temporary configuration can trigger another, nested, temporary configuration call, which forces us to keep a stack of the temporary thread-local properties, so that we can properly restore after a nested temporary call, without reverting the outer temporary call too soon. Again, this feels too complex.
configSource.temporarySetProperties(map, () -> {
// Somewhere in a nested call:
configSource.temporarySetProperties(anotherMap, () -> {
// expecting here anotherMap to __partially__ overwrite (merge with) the previous map.
}
});
So WDYT? Is it worth implementing this complex temporary support directly inside a configuration source? I have doubts. I find it more simple to have a dedicated “execution context” (or “thread local”) configuration source, and to make the default configuration source check it first (in the chain), even if the TemporaryConfigurationExecutor
feels a bit awkward.
Thanks,
Marius