public class CombinedConfiguration extends BaseHierarchicalConfiguration implements EventListener<ConfigurationEvent>
A hierarchical composite configuration class.
This class maintains a list of configuration objects, which can be added using the diverse addConfiguration()
methods. After that the configurations can be accessed either by name (if one was provided when the configuration was
added) or by index. For the whole set of managed configurations a logical node structure is constructed. For this
purpose a NodeCombiner
object can be set. This makes it
possible to specify different algorithms for the combination process.
The big advantage of this class is that it creates a truly hierarchical structure of all the properties stored in the contained configurations - even if some of them are no hierarchical configurations per se. So all enhanced features provided by a hierarchical configuration (e.g. choosing an expression engine) are applicable.
The class works by registering itself as an event listener at all added configurations. So it gets notified whenever
one of these configurations is changed and can invalidate its internal node structure. The next time a property is
accessed the node structure will be re-constructed using the current state of the managed configurations. Note that,
depending on the used NodeCombiner
, this may be a complex operation.
Because of the way a CombinedConfiguration
is working it has more or less view character: it provides a logic
view on the configurations it contains. In this constellation not all methods defined for hierarchical configurations
- especially methods that update the stored properties - can be implemented in a consistent manner. Using such
methods (like addProperty()
, or clearProperty()
on a CombinedConfiguration
is not strictly
forbidden, however, depending on the current NodeCombiner
and the involved properties, the results may be
different than expected. Some examples may illustrate this:
CombinedConfiguration
cc containing two child configurations with the following
content:
gui.background = blue gui.position = (10, 10, 400, 200)
gui.background = black gui.foreground = white home.dir = /data
NodeCombiner
a OverrideCombiner
is used.
This combiner will ensure that defined user settings take precedence over the default values. If the resulting
CombinedConfiguration
is queried for the background color, blue
will be returned because this value
is defined in user.properties
. Now consider what happens if the key gui.background
is removed from
the CombinedConfiguration
:
cc.clearProperty("gui.background");Will a
cc.containsKey("gui.background")
now return false? No, it won't! The clearProperty()
operation is executed on the node set of the combined configuration, which was constructed from the nodes of the two
child configurations. It causes the value of the background node to be cleared, which is also part of the
first child configuration. This modification of one of its child configurations causes the
CombinedConfiguration
to be re-constructed. This time the OverrideCombiner
cannot find a
gui.background
property in the first child configuration, but it finds one in the second, and adds it to the
resulting combined configuration. So the property is still present (with a different value now).addProperty()
can also be problematic: Most node combiners use special view nodes for linking parts of
the original configurations' data together. If new properties are added to such a special node, they do not belong to
any of the managed configurations and thus hang in the air. Using the same configurations as in the last example, the
statement
addProperty("database.user", "scott");would cause such a hanging property. If now one of the child configurations is changed and the
CombinedConfiguration
is re-constructed, this property will disappear! (Add operations are not problematic if
they result in a child configuration being updated. For instance an addProperty("home.url", "localhost");
will alter the second child configuration - because the prefix home is here already present; when the
CombinedConfiguration
is re-constructed, this change is taken into account.)Because of such problems it is recommended to perform updates only on the managed child configurations.
Whenever the node structure of a CombinedConfiguration
becomes invalid (either because one of the contained
configurations was modified or because the invalidate()
method was directly called) an event is generated. So
this can be detected by interested event listeners. This also makes it possible to add a combined configuration into
another one.
Notes about thread-safety: This configuration implementation uses a Synchronizer
object to protect instances
against concurrent access. The concrete Synchronizer
implementation used determines whether an instance of
this class is thread-safe or not. In contrast to other implementations derived from
BaseHierarchicalConfiguration
, thread-safety is an issue here because the nodes structure used by this
configuration has to be constructed dynamically when a child configuration is changed. Therefore, when multiple
threads are involved which also manipulate one of the child configurations, a proper Synchronizer
object
should be set. Note that the Synchronizer
objects used by the child configurations do not really matter.
Because immutable in-memory nodes structures are used for them there is no danger that updates on child
configurations could interfere with read operations on the combined configuration.
BaseHierarchicalConfiguration.BuilderVisitor
Modifier and Type | Field and Description |
---|---|
static EventType<ConfigurationEvent> |
COMBINED_INVALIDATE
Constant for the event type fired when the internal node structure of a combined configuration becomes invalid.
|
Constructor and Description |
---|
CombinedConfiguration()
Creates a new instance of
CombinedConfiguration that uses a union combiner. |
CombinedConfiguration(NodeCombiner comb)
Creates a new instance of
CombinedConfiguration and initializes the combiner to be used. |
Modifier and Type | Method and Description |
---|---|
void |
addConfiguration(Configuration config)
Adds a new configuration to this combined configuration.
|
void |
addConfiguration(Configuration config,
String name)
Adds a new configuration to this combined configuration with an optional name.
|
void |
addConfiguration(Configuration config,
String name,
String at)
Adds a new configuration to this combined configuration.
|
protected void |
beginRead(boolean optimize)
Notifies this configuration's
Synchronizer that a read operation is about to start. |
protected void |
beginWrite(boolean optimize)
Notifies this configuration's
Synchronizer that an update operation is about to start. |
protected void |
clearInternal()
Clears this configuration.
|
Object |
clone()
Returns a copy of this object.
|
Configuration |
getConfiguration(int index)
Returns the configuration at the specified index.
|
Configuration |
getConfiguration(String name)
Returns the configuration with the given name.
|
List<String> |
getConfigurationNameList()
Returns a List of the names of all the configurations that have been added in the order they were added.
|
Set<String> |
getConfigurationNames()
Returns a set with the names of all configurations contained in this combined configuration.
|
List<Configuration> |
getConfigurations()
Returns a List of all the configurations that have been added.
|
ExpressionEngine |
getConversionExpressionEngine()
Returns the
ExpressionEngine for converting flat child configurations to hierarchical ones. |
NodeCombiner |
getNodeCombiner()
Returns the node combiner that is used for creating the combined node structure.
|
int |
getNumberOfConfigurations()
Returns the number of configurations that are contained in this combined configuration.
|
Configuration |
getSource(String key)
Returns the configuration source, in which the specified key is defined.
|
Set<Configuration> |
getSources(String key)
Returns a set with the configuration sources, in which the specified key is defined.
|
void |
invalidate()
Invalidates this combined configuration.
|
void |
onEvent(ConfigurationEvent event)
Event listener call back for configuration update events.
|
boolean |
removeConfiguration(Configuration config)
Removes the specified configuration from this combined configuration.
|
Configuration |
removeConfiguration(String name)
Removes the configuration with the specified name.
|
Configuration |
removeConfigurationAt(int index)
Removes the configuration at the specified index.
|
void |
setConversionExpressionEngine(ExpressionEngine conversionExpressionEngine)
Sets the
ExpressionEngine for converting flat child configurations to hierarchical ones. |
void |
setNodeCombiner(NodeCombiner nodeCombiner)
Sets the node combiner.
|
childConfigurationsAt, childConfigurationsAt, cloneNodeModel, configurationAt, configurationAt, configurationsAt, configurationsAt, createSubConfigurationForTrackedNode, getNodeModel, getSubConfigurationNodeSelector, getSubConfigurationParentModel, immutableChildConfigurationsAt, immutableConfigurationAt, immutableConfigurationAt, immutableConfigurationsAt, initSubConfigurationForThisParent, interpolatedConfiguration, subnodeConfigurationChanged, subset
addNodes, addNodesInternal, addPropertyDirect, addPropertyInternal, clearPropertyDirect, clearTree, clearTreeInternal, containsKeyInternal, fetchNodeList, getExpressionEngine, getKeysInternal, getKeysInternal, getMaxIndex, getMaxIndexInternal, getModel, getPropertyInternal, getRootElementName, getRootElementNameInternal, isEmptyInternal, nodeDefined, nodeKey, resolveAddKey, resolveKey, resolveNodeKey, resolveUpdateKey, setExpressionEngine, setPropertyInternal, sizeInternal, toString
addErrorLogListener, addProperty, append, clear, clearProperty, cloneInterpolator, containsKey, copy, endRead, endWrite, get, get, getArray, getArray, getBigDecimal, getBigDecimal, getBigInteger, getBigInteger, getBoolean, getBoolean, getBoolean, getByte, getByte, getByte, getCollection, getCollection, getConfigurationDecoder, getConversionHandler, getDouble, getDouble, getDouble, getDuration, getDuration, getEncodedString, getEncodedString, getFloat, getFloat, getFloat, getInt, getInt, getInteger, getInterpolator, getKeys, getKeys, getList, getList, getList, getList, getListDelimiterHandler, getLogger, getLong, getLong, getLong, getProperties, getProperties, getProperty, getShort, getShort, getShort, getString, getString, getStringArray, getSynchronizer, immutableSubset, initLogger, installInterpolator, interpolate, interpolate, isEmpty, isScalarValue, isThrowExceptionOnMissing, lock, setConfigurationDecoder, setConversionHandler, setDefaultLookups, setInterpolator, setListDelimiterHandler, setLogger, setParentInterpolator, setPrefixLookups, setProperty, setSynchronizer, setThrowExceptionOnMissing, size, unlock
addEventListener, clearErrorListeners, clearEventListeners, copyEventListeners, createErrorEvent, createEvent, fireError, fireEvent, getEventListenerRegistrations, getEventListeners, isDetailEvents, removeEventListener, setDetailEvents
equals, finalize, getClass, hashCode, notify, notifyAll, wait, wait, wait
addProperty, clear, clearProperty, getInterpolator, installInterpolator, setInterpolator, setProperty
getSynchronizer, lock, setSynchronizer, unlock
containsKey, get, get, getArray, getArray, getBigDecimal, getBigDecimal, getBigInteger, getBigInteger, getBoolean, getBoolean, getBoolean, getByte, getByte, getByte, getCollection, getCollection, getDouble, getDouble, getDouble, getDuration, getDuration, getEncodedString, getEncodedString, getEnum, getEnum, getFloat, getFloat, getFloat, getInt, getInt, getInteger, getKeys, getKeys, getList, getList, getList, getList, getLong, getLong, getLong, getProperties, getProperty, getShort, getShort, getShort, getString, getString, getStringArray, immutableSubset, isEmpty, size
public static final EventType<ConfigurationEvent> COMBINED_INVALIDATE
public CombinedConfiguration(NodeCombiner comb)
CombinedConfiguration
and initializes the combiner to be used.comb
- the node combiner (can be null, then a union combiner is used as default)public CombinedConfiguration()
CombinedConfiguration
that uses a union combiner.UnionCombiner
public NodeCombiner getNodeCombiner()
public void setNodeCombiner(NodeCombiner nodeCombiner)
IllegalArgumentException
exception is thrown. Changing the node combiner causes
an invalidation of this combined configuration, so that the new combiner immediately takes effect.nodeCombiner
- the node combinerpublic ExpressionEngine getConversionExpressionEngine()
ExpressionEngine
for converting flat child configurations to hierarchical ones.public void setConversionExpressionEngine(ExpressionEngine conversionExpressionEngine)
ExpressionEngine
for converting flat child configurations to hierarchical ones. When constructing
the root node for this combined configuration the properties of all child configurations must be combined to a single
hierarchical node structure. In this process, non hierarchical configurations are converted to hierarchical ones
first. This can be problematic if a child configuration contains keys that are no compatible with the default
expression engine used by hierarchical configurations. Therefore it is possible to specify a specific expression
engine to be used for this purpose.conversionExpressionEngine
- the conversion expression engineConfigurationUtils.convertToHierarchical(Configuration, ExpressionEngine)
public void addConfiguration(Configuration config, String name, String at)
ConfigurationRuntimeException
will be thrown.
With the optional at
argument you can specify where in the resulting node structure the content of the added
configuration should appear. This is a string that uses dots as property delimiters (independent on the current
expression engine). For instance if you pass in the string "database.tables"
, all properties of the added
configuration will occur in this branch.config
- the configuration to add (must not be null)name
- the name of this configuration (can be null)at
- the position of this configuration in the combined tree (can be null)public void addConfiguration(Configuration config, String name)
config
- the configuration to add (must not be null)name
- the name of this configuration (can be null)public void addConfiguration(Configuration config)
config
- the configuration to add (must not be null)public int getNumberOfConfigurations()
public Configuration getConfiguration(int index)
index
- the indexpublic Configuration getConfiguration(String name)
name
- the name of the configurationpublic List<Configuration> getConfigurations()
public List<String> getConfigurationNameList()
public boolean removeConfiguration(Configuration config)
config
- the configuration to be removedpublic Configuration removeConfigurationAt(int index)
index
- the indexpublic Configuration removeConfiguration(String name)
name
- the name of the configuration to be removedpublic Set<String> getConfigurationNames()
public void invalidate()
EVENT_COMBINED_INVALIDATE
is fired. Note that while other events most times appear twice (once before and
once after an update), this event is only fired once (after update).public void onEvent(ConfigurationEvent event)
onEvent
in interface EventListener<ConfigurationEvent>
event
- the update eventprotected void clearInternal()
clearInternal
in class AbstractHierarchicalConfiguration<ImmutableNode>
public Object clone()
clone
in class AbstractHierarchicalConfiguration<ImmutableNode>
public Configuration getSource(String key)
IllegalArgumentException
is thrown (in this case no unique source can be determined).key
- the key of a configuration propertyIllegalArgumentException
- if the key maps to multiple properties and the source cannot be determined, or if
the key is nullpublic Set<Configuration> getSources(String key)
key
- the key of a configuration propertyprotected void beginRead(boolean optimize)
Synchronizer
that a read operation is about to start. This method is called by
all methods which access this configuration in a read-only mode. Subclasses may override it to perform additional
actions before this read operation. The boolean optimize argument can be evaluated by overridden methods in
derived classes. Some operations which require a lock do not need a fully initialized configuration object. By
setting this flag to true, such operations can give a corresponding hint. An overridden
implementation of beginRead()
can then decide to skip some initialization steps. All basic operations in this
class (and most of the basic Configuration
implementations) call this method with a parameter value of
false. In any case the inherited method must be called! Otherwise, proper synchronization is
not guaranteed. This implementation checks whether a combined root node is available. If not, it is constructed by
requesting a write lock.beginRead
in class AbstractConfiguration
optimize
- a flag whether optimization can be performedprotected void beginWrite(boolean optimize)
Synchronizer
that an update operation is about to start. This method is called
by all methods which modify this configuration. Subclasses may override it to perform additional operations before an
update. For a description of the boolean optimize argument refer to the documentation of
beginRead()
. In any case the inherited method must be called! Otherwise, proper synchronization is
not guaranteed. This implementation checks whether a combined root node is available. If not, it is constructed now.beginWrite
in class AbstractConfiguration
optimize
- a flag whether optimization can be performedAbstractConfiguration.beginRead(boolean)
Copyright © 2001–2022 The Apache Software Foundation. All rights reserved.