Copyright 2014 Netflix, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
/** * Copyright 2014 Netflix, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */
package com.netflix.config; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.ConcurrentModificationException; import java.util.Iterator; import java.util.LinkedHashSet; import java.util.LinkedList; import java.util.List; import java.util.ListIterator; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CopyOnWriteArrayList; import org.apache.commons.configuration.AbstractConfiguration; import org.apache.commons.configuration.Configuration; import org.apache.commons.configuration.ConfigurationRuntimeException; import org.apache.commons.configuration.ConfigurationUtils; import org.apache.commons.configuration.HierarchicalConfiguration; import org.apache.commons.configuration.event.ConfigurationEvent; import org.apache.commons.configuration.event.ConfigurationListener; import org.slf4j.Logger; import org.slf4j.LoggerFactory;
This class maintains a hierarchy of configurations in a list structure. The order of the list stands for the descending priority of the configurations when a property value is to be determined. For example, if you add Configuration1, and then Configuration2, getProperty(String) will return any properties defined by Configuration1. Only if Configuration1 doesn't have the property, then Configuration2 will be checked.

There are two internal configurations for properties that are programmatically set:
  • Configuration to hold any property introduced by addProperty(String, Object) or setProperty(String, Object) called directly on this class. This configuration will be called "container configuration" as it serves as the container of such properties. By default, this configuration remains at the last of the configurations list. It can be treated as a "base line" configuration that holds hard-coded parameters that can be overridden by any of other configurations added at runtime. You can replace this configuration by your own and change the position of the configuration in the list by calling setContainerConfiguration(AbstractConfiguration, String, int).
  • Configuration to hold properties that are programmatically set (using setOverrideProperty(String, Object)) to override values from any other configurations on the list. As contrast to container configuration, this configuration is always consulted first in getProperty(String).
When adding configuration to this class, it is recommended to convert it into ConcurrentMapConfiguration or ConcurrentCompositeConfiguration using ConfigurationUtils to achieve maximal performance and thread safety.

Example:

  // configuration from local properties file
String fileName = "...";
ConcurrentMapConfiguration configFromPropertiesFile =
new ConcurrentMapConfiguration(new PropertiesConfiguration(fileName));
// configuration from system properties
ConcurrentMapConfiguration configFromSystemProperties = 
new ConcurrentMapConfiguration(new SystemConfiguration());
// configuration from a dynamic source
PolledConfigurationSource source = createMyOwnSource();
AbstractPollingScheduler scheduler = createMyOwnScheduler();
DynamicConfiguration dynamicConfiguration =
new DynamicConfiguration(source, scheduler);
// create a hierarchy of configuration that makes
// 1) dynamic configuration source override system properties and,
// 2) system properties override properties file
ConcurrentCompositeConfiguration finalConfig = new ConcurrentCompositeConfiguration();
finalConfig.add(dynamicConfiguration, "dynamicConfig");
finalConfig.add(configFromSystemProperties, "systemConfig");
finalConfig.add(configFromPropertiesFile, "fileConfig");
// register with DynamicPropertyFactory so that finalConfig
// becomes the source of dynamic properties
DynamicPropertyFactory.initWithConfigurationSource(finalConfig);    
Author:awang
/** * This class maintains a hierarchy of configurations in a list structure. The order of the list stands for the descending * priority of the configurations when a property value is to be determined. * For example, if you add Configuration1, and then Configuration2, * {@link #getProperty(String)} will return any properties defined by Configuration1. * Only if Configuration1 doesn't have the property, then * Configuration2 will be checked. </p> * There are two internal configurations for properties that are programmatically set: * <ul> * <li>Configuration to hold any property introduced by {@link #addProperty(String, Object)} or {@link #setProperty(String, Object)} * called directly on this class. This configuration will be called "container configuration" as it serves as the container of * such properties. By default, this configuration remains at the last of the configurations list. It can be treated as * a "base line" configuration that holds hard-coded parameters that can be overridden by any of other configurations added at runtime. * You can replace this configuration by your own and change the position of the configuration in the list by calling * {@link #setContainerConfiguration(AbstractConfiguration, String, int)}. * <li>Configuration to hold properties that are programmatically set (using {@link #setOverrideProperty(String, Object)}) to override values from any other * configurations on the list. As contrast to container configuration, this configuration is always consulted first in * {@link #getProperty(String)}. * </ul> * * When adding configuration to this class, it is recommended to convert it into * {@link ConcurrentMapConfiguration} or ConcurrentCompositeConfiguration using * {@link com.netflix.config.util.ConfigurationUtils} to achieve * maximal performance and thread safety. * * <p> * Example: * <pre> * // configuration from local properties file String fileName = "..."; ConcurrentMapConfiguration configFromPropertiesFile = new ConcurrentMapConfiguration(new PropertiesConfiguration(fileName)); // configuration from system properties ConcurrentMapConfiguration configFromSystemProperties = new ConcurrentMapConfiguration(new SystemConfiguration()); // configuration from a dynamic source PolledConfigurationSource source = createMyOwnSource(); AbstractPollingScheduler scheduler = createMyOwnScheduler(); DynamicConfiguration dynamicConfiguration = new DynamicConfiguration(source, scheduler); // create a hierarchy of configuration that makes // 1) dynamic configuration source override system properties and, // 2) system properties override properties file ConcurrentCompositeConfiguration finalConfig = new ConcurrentCompositeConfiguration(); finalConfig.add(dynamicConfiguration, "dynamicConfig"); finalConfig.add(configFromSystemProperties, "systemConfig"); finalConfig.add(configFromPropertiesFile, "fileConfig"); // register with DynamicPropertyFactory so that finalConfig // becomes the source of dynamic properties DynamicPropertyFactory.initWithConfigurationSource(finalConfig); * </pre> * * @author awang * */
public class ConcurrentCompositeConfiguration extends ConcurrentMapConfiguration implements AggregatedConfiguration, ConfigurationListener, Cloneable { private Map<String, AbstractConfiguration> namedConfigurations = new ConcurrentHashMap<String, AbstractConfiguration>(); private List<AbstractConfiguration> configList = new CopyOnWriteArrayList<AbstractConfiguration>(); private static final Logger logger = LoggerFactory.getLogger(ConcurrentCompositeConfiguration.class); public static final int EVENT_CONFIGURATION_SOURCE_CHANGED = 10001; private volatile boolean propagateEventToParent = true; private AbstractConfiguration overrideProperties;
Configuration that holds properties set directly with setProperty(String, Object)
/** * Configuration that holds properties set directly with {@link #setProperty(String, Object)} */
private AbstractConfiguration containerConfiguration;
Stores a flag whether the current in-memory configuration is also a child configuration.
/** * Stores a flag whether the current in-memory configuration is also a * child configuration. */
private volatile boolean containerConfigurationChanged = true; private ConfigurationListener eventPropagater = new ConfigurationListener() { @Override public void configurationChanged(ConfigurationEvent event) { boolean beforeUpdate = event.isBeforeUpdate(); if (propagateEventToParent) { int type = event.getType(); String name = event.getPropertyName(); Object value = event.getPropertyValue(); Object finalValue; switch(type) { case HierarchicalConfiguration.EVENT_ADD_NODES: case EVENT_CLEAR: case EVENT_CONFIGURATION_SOURCE_CHANGED: fireEvent(type, name, value, beforeUpdate); break; case EVENT_ADD_PROPERTY: case EVENT_SET_PROPERTY: if (beforeUpdate) { // we want the validators to run even if the source is not // the winning configuration fireEvent(type, name, value, beforeUpdate); } else { AbstractConfiguration sourceConfig = (AbstractConfiguration) event.getSource(); AbstractConfiguration winningConf = (AbstractConfiguration) getSource(name); if (winningConf == null || getIndexOfConfiguration(sourceConfig) <= getIndexOfConfiguration(winningConf)) { fireEvent(type, name, value, beforeUpdate); } } break; case EVENT_CLEAR_PROPERTY: finalValue = ConcurrentCompositeConfiguration.this.getProperty(name); if (finalValue == null) { fireEvent(type, name, value, beforeUpdate); } else { fireEvent(EVENT_SET_PROPERTY, name, finalValue, beforeUpdate); } break; default: break; } } } };
Creates an empty CompositeConfiguration object which can then be added some other Configuration files
/** * Creates an empty CompositeConfiguration object which can then * be added some other Configuration files */
public ConcurrentCompositeConfiguration() { clear(); }
Creates a ConcurrentCompositeConfiguration object with a specified container configuration. This configuration will store any changes made by setProperty(String, Object) and addProperty(String, Object).
Params:
  • containerConfiguration – the configuration to use as container configuration
/** * Creates a ConcurrentCompositeConfiguration object with a specified <em>container * configuration</em>. This configuration will store any changes made by {@link #setProperty(String, Object)} * and {@link #addProperty(String, Object)}. * * @param containerConfiguration the configuration to use as container configuration */
public ConcurrentCompositeConfiguration(AbstractConfiguration containerConfiguration) { configList.clear(); this.containerConfiguration = containerConfiguration; configList.add(containerConfiguration); }
Creates a ConcurrentCompositeConfiguration with a specified container configuration, and then adds the given collection of configurations.
Params:
  • containerConfiguration – container configuration to use
  • configurations – the collection of configurations to add
/** * Creates a ConcurrentCompositeConfiguration with a specified <em>container * configuration</em>, and then adds the given collection of configurations. * * @param containerConfiguration container configuration to use * @param configurations the collection of configurations to add */
public ConcurrentCompositeConfiguration(AbstractConfiguration containerConfiguration, Collection<? extends AbstractConfiguration> configurations) { this(containerConfiguration); if (configurations != null) { for (AbstractConfiguration c : configurations) { addConfiguration(c); } } }
Event listener call back for configuration update events. This method is called whenever one of the contained configurations was modified. This method does nothing.
Params:
  • event – the update event
/** * Event listener call back for configuration update events. This method is * called whenever one of the contained configurations was modified. This method * does nothing. * * @param event the update event */
@Override public void configurationChanged(ConfigurationEvent event) { } public void invalidate() { }
Add a child configuration without a name. Make a call to addConfiguration(AbstractConfiguration, String) with the name being null.
Params:
  • config – the configuration to add
/** * Add a child configuration without a name. Make a call to {@link #addConfiguration(AbstractConfiguration, String)} * with the name being null. * * @param config the configuration to add */
public final void addConfiguration(AbstractConfiguration config) { addConfiguration(config, null); }
Adds a new child configuration to this configuration with an optional name. The configuration will be added to the end of the list if container configuration has been changed to new one or no longer at the end of the list. Otherwise it will be added in front of the container configuration.
Params:
  • config – the configuration to add (must not be null)
  • name – the name of this configuration (can be null)
/** * Adds a new child configuration to this configuration with an optional * name. The configuration will be added to the end of the list * if <em>container configuration</em> has been changed to new one or no longer at the end of * the list. Otherwise it will be added in front of the <em>container configuration</em>. * * @param config the configuration to add (must not be <b>null</b>) * @param name the name of this configuration (can be <b>null</b>) */
public void addConfiguration(AbstractConfiguration config, String name) { if (containerConfigurationChanged) { addConfigurationAtIndex(config, name, configList.size()); } else { addConfigurationAtIndex(config, name, configList.indexOf(containerConfiguration)); } }
Get the configurations added.
/** * Get the configurations added. */
public List<AbstractConfiguration> getConfigurations() { return Collections.unmodifiableList(configList); } public List<String> getConfigurationNameList() { List<String> list = new ArrayList<String>(configList.size()); for (AbstractConfiguration configuration: configList) { boolean foundName = false; for (String name: namedConfigurations.keySet()) { if (configuration == namedConfigurations.get(name)) { foundName = true; list.add(name); break; } } if (!foundName) { list.add(null); } } return list; } public int getIndexOfConfiguration(AbstractConfiguration config) { return configList.indexOf(config); } public int getIndexOfContainerConfiguration() { return configList.indexOf(containerConfiguration); } private void checkIndex(int newIndex) { if (newIndex < 0 || newIndex > configList.size()) { throw new IndexOutOfBoundsException(newIndex + " is out of bounds of the size of configuration list " + configList.size()); } }
Adds a child configuration and makes it the container configuration. This means that all future property write operations are executed on this configuration. Note that the current container configuration stays in the list of child configurations at its current position, but it passes its role as container configuration to the new one.
Params:
  • config – the configuration to be added
  • name – the name of the configuration to be added
  • index – index to add this configuration
Throws:
/** * Adds a child configuration and makes it the <em>container * configuration</em>. This means that all future property write operations * are executed on this configuration. Note that the current container * configuration stays in the list of child configurations * at its current position, but it passes its role as container * configuration to the new one. * * @param config the configuration to be added * @param name the name of the configuration to be added * @param index index to add this configuration * * @throws IndexOutOfBoundsException */
public void setContainerConfiguration(AbstractConfiguration config, String name, int index) throws IndexOutOfBoundsException { if (!configList.contains(config)) { checkIndex(index); containerConfigurationChanged = true; containerConfiguration = config; addConfigurationAtIndex(config, name, index); } else { logger.warn(config + " is not added as it already exits"); } }
Change the position of the container configuration to a new index.
Throws:
  • IndexOutOfBoundsException –
/** * Change the position of the <em>container configuration</em> to a new index. * * @throws IndexOutOfBoundsException */
public void setContainerConfigurationIndex(int newIndex) throws IndexOutOfBoundsException { if (newIndex < 0 || newIndex >= configList.size()) { throw new IndexOutOfBoundsException("Cannot change to the new index " + newIndex + " in the list of size " + configList.size()); } else if (newIndex == configList.indexOf(containerConfiguration)) { // nothing to do return; } containerConfigurationChanged = true; configList.remove(containerConfiguration); configList.add(newIndex, containerConfiguration); }
Add a configuration with a name at a particular index.
Throws:
  • IndexOutOfBoundsException –
/** * Add a configuration with a name at a particular index. * * @throws IndexOutOfBoundsException */
public void addConfigurationAtIndex(AbstractConfiguration config, String name, int index) throws IndexOutOfBoundsException { if (!configList.contains(config)) { checkIndex(index); configList.add(index, config); if (name != null) { namedConfigurations.put(name, config); } config.addConfigurationListener(eventPropagater); fireEvent(EVENT_CONFIGURATION_SOURCE_CHANGED, null, null, false); } else { logger.warn(config + " is not added as it already exits"); } } public void addConfigurationAtFront(AbstractConfiguration config, String name) { addConfigurationAtIndex(config, name, 0); }
Remove a configuration. The container configuration cannot be removed.
Params:
  • config – The configuration to remove
/** * Remove a configuration. The container configuration cannot be removed. * * @param config The configuration to remove */
public boolean removeConfiguration(Configuration config) { // Make sure that you can't remove the inMemoryConfiguration from // the CompositeConfiguration object if (!config.equals(containerConfiguration)) { String configName = getNameForConfiguration(config); if (configName != null) { namedConfigurations.remove(configName); } return configList.remove(config); } else { throw new IllegalArgumentException("Can't remove container configuration"); } } public AbstractConfiguration removeConfigurationAt(int index) { AbstractConfiguration config = configList.remove(index); String nameFound = getNameForConfiguration(config); if (nameFound != null) { namedConfigurations.remove(nameFound); } return config; }
Removes the configuration with the specified name.
Params:
  • name – the name of the configuration to be removed
Returns:the removed configuration (null if this configuration was not found)
/** * Removes the configuration with the specified name. * * @param name the name of the configuration to be removed * @return the removed configuration (<b>null</b> if this configuration * was not found) */
public Configuration removeConfiguration(String name) { Configuration conf = getConfiguration(name); if (conf != null && !conf.equals(containerConfiguration)) { configList.remove(conf); namedConfigurations.remove(name); } else if (conf != null && conf.equals(containerConfiguration)) { throw new IllegalArgumentException("Can't remove container configuration"); } return conf; }
Return the number of configurations.
Returns:the number of configuration
/** * Return the number of configurations. * * @return the number of configuration */
public int getNumberOfConfigurations() { return configList.size(); }
Removes all child configurations and reinitializes the container configuration. Attention: A new container configuration is created; the old one is lost.
/** * Removes all child configurations and reinitializes the <em>container * configuration</em>. <strong>Attention:</strong> A new container * configuration is created; the old one is lost. */
@Override public final void clear() { fireEvent(EVENT_CLEAR, null, null, true); configList.clear(); namedConfigurations.clear(); // recreate the in memory configuration containerConfiguration = new ConcurrentMapConfiguration(); containerConfiguration.setThrowExceptionOnMissing(isThrowExceptionOnMissing()); containerConfiguration.setListDelimiter(getListDelimiter()); containerConfiguration.setDelimiterParsingDisabled(isDelimiterParsingDisabled()); containerConfiguration.addConfigurationListener(eventPropagater); configList.add(containerConfiguration); overrideProperties = new ConcurrentMapConfiguration(); overrideProperties.setThrowExceptionOnMissing(isThrowExceptionOnMissing()); overrideProperties.setListDelimiter(getListDelimiter()); overrideProperties.setDelimiterParsingDisabled(isDelimiterParsingDisabled()); overrideProperties.addConfigurationListener(eventPropagater); fireEvent(EVENT_CLEAR, null, null, false); containerConfigurationChanged = false; invalidate(); }
Override the same property in any other configurations in the list.
/** * Override the same property in any other configurations in the list. */
public void setOverrideProperty(String key, Object finalValue) { overrideProperties.setProperty(key, finalValue); }
Remove the overriding property set by setOverrideProperty(String, Object)
/** * Remove the overriding property set by {@link #setOverrideProperty(String, Object)} */
public void clearOverrideProperty(String key) { overrideProperties.clearProperty(key); }
Set the property with the container configuration. Warning: getProperty(String) on this key may not return the same value set by this method if there is any other configuration that contain the same property and is in front of the container configuration in the configurations list.
/** * Set the property with the <em>container configuration</em>. * <b>Warning: </b>{@link #getProperty(String)} on this key may not return the same value set by this method * if there is any other configuration that contain the same property and is in front of the * <em>container configuration</em> in the configurations list. */
@Override public void setProperty(String key, Object value) { containerConfiguration.setProperty(key, value); }
Add the property with the container configuration. Warning: getProperty(String) on this key may not return the same value set by this method if there is any other configuration that contain the same property and is in front of the container configuration in the configurations list.
/** * Add the property with the <em>container configuration</em>. * <b>Warning: </b>{@link #getProperty(String)} on this key may not return the same value set by this method * if there is any other configuration that contain the same property and is in front of the * <em>container configuration</em> in the configurations list. */
@Override public void addProperty(String key, Object value) { containerConfiguration.addProperty(key, value); }
Clear the property with the container configuration. Warning: getProperty(String) on this key may still return some value if there is any other configuration that contain the same property and is in front of the container configuration in the configurations list.
/** * Clear the property with the <em>container configuration</em>. * <b>Warning: </b>{@link #getProperty(String)} on this key may still return some value * if there is any other configuration that contain the same property and is in front of the * <em>container configuration</em> in the configurations list. */
@Override public void clearProperty(String key) { containerConfiguration.clearProperty(key); }
Read property from underlying composite. It first checks if the property has been overridden by setOverrideProperty(String, Object) and if so return the overriding value. Otherwise, it iterates through the list of sub configurations until it finds one that contains the property and return the value from that sub configuration. It returns null of the property does not exist.
Params:
  • key – key to use for mapping
Returns:object associated with the given configuration key. null if it does not exist.
/** * Read property from underlying composite. It first checks if the property has been overridden * by {@link #setOverrideProperty(String, Object)} and if so return the overriding value. * Otherwise, it iterates through the list of sub configurations until it finds one that contains the * property and return the value from that sub configuration. It returns null of the property does * not exist. * * @param key key to use for mapping * * @return object associated with the given configuration key. null if it does not exist. */
public Object getProperty(String key) { if (overrideProperties.containsKey(key)) { return overrideProperties.getProperty(key); } Configuration firstMatchingConfiguration = null; for (Configuration config : configList) { if (config.containsKey(key)) { firstMatchingConfiguration = config; break; } } if (firstMatchingConfiguration != null) { return firstMatchingConfiguration.getProperty(key); } else { return null; } }
Get all the keys contained by sub configurations.
Throws:
  • ConcurrentModificationException – if concurrent modification happens on any sub configuration when it is iterated to get all the keys
/** * Get all the keys contained by sub configurations. * * @throws ConcurrentModificationException if concurrent modification happens on any sub configuration * when it is iterated to get all the keys * */
public Iterator<String> getKeys() throws ConcurrentModificationException { Set<String> keys = new LinkedHashSet<String>(); for (Iterator<String> it = overrideProperties.getKeys(); it.hasNext();) { keys.add(it.next()); } for (Configuration config : configList) { for (Iterator<String> it = config.getKeys(); it.hasNext();) { try { keys.add(it.next()); } catch (ConcurrentModificationException e) { logger.error("unexpected exception when iterating the keys for configuration " + config + " with name " + getNameForConfiguration(config)); throw e; } } } return keys.iterator(); } private String getNameForConfiguration(Configuration config) { for (Map.Entry<String, AbstractConfiguration> entry: namedConfigurations.entrySet()) { if (entry.getValue() == config) { return entry.getKey(); } } return null; }
Get the list of the keys contained in the sub configurations that match the specified prefix.
/** * Get the list of the keys contained in the sub configurations that match the * specified prefix. * */
@Override public Iterator<String> getKeys(String prefix) { Set<String> keys = new LinkedHashSet<String>(); for (Iterator<String> it = overrideProperties.getKeys(prefix); it.hasNext();) { keys.add(it.next()); } for (Configuration config : configList) { for (Iterator<String> it = config.getKeys(prefix); it.hasNext();) { keys.add(it.next()); } } return keys.iterator(); }
Returns a set with the names of all configurations contained in this configuration. Of course here are only these configurations listed, for which a name was specified when they were added.
Returns:a set with the names of the contained configurations (never null)
/** * Returns a set with the names of all configurations contained in this * configuration. Of course here are only these configurations * listed, for which a name was specified when they were added. * * @return a set with the names of the contained configurations (never * <b>null</b>) */
public Set<String> getConfigurationNames() { return namedConfigurations.keySet(); } @Override public boolean isEmpty() { if (overrideProperties.isEmpty()) { return false; } for (Configuration config : configList) { if (!config.isEmpty()) { return false; } } return true; }
Check if the any of the sub configurations contains the specified key.
Params:
  • key – the key whose presence in this configuration is to be tested
Returns:true if the configuration contains a value for this key, false otherwise
/** * Check if the any of the sub configurations contains the specified key. * * @param key the key whose presence in this configuration is to be tested * * @return <code>true</code> if the configuration contains a value for this * key, <code>false</code> otherwise * */
@Override public boolean containsKey(String key) { if (overrideProperties.containsKey(key)) { return true; } for (Configuration config : configList) { if (config.containsKey(key)) { return true; } } return false; }
Get a List of objects associated with the given configuration key. If the key doesn't map to an existing object, the default value is returned.
Params:
  • key – The configuration key.
  • defaultValue – The default value.
Returns:The associated List of value.
/** * Get a List of objects associated with the given configuration key. * If the key doesn't map to an existing object, the default value * is returned. * * @param key The configuration key. * @param defaultValue The default value. * @return The associated List of value. * */
@Override public List getList(String key, List defaultValue) { List<Object> list = new ArrayList<Object>(); // add all elements from the first configuration containing the requested key Iterator<AbstractConfiguration> it = configList.iterator(); if (overrideProperties.containsKey(key)) { appendListProperty(list, overrideProperties, key); } while (it.hasNext() && list.isEmpty()) { Configuration config = it.next(); if ((config != containerConfiguration || containerConfigurationChanged) && config.containsKey(key)) { appendListProperty(list, config, key); } } // add all elements from the in memory configuration if (list.isEmpty()) { appendListProperty(list, containerConfiguration, key); } if (list.isEmpty()) { return defaultValue; } ListIterator<Object> lit = list.listIterator(); while (lit.hasNext()) { lit.set(interpolate(lit.next())); } return list; }
Get an array of strings associated with the given configuration key. If the key doesn't map to an existing object an empty array is returned
Params:
  • key – The configuration key.
Returns:The associated string array if key is found.
/** * Get an array of strings associated with the given configuration key. * If the key doesn't map to an existing object an empty array is returned * * @param key The configuration key. * @return The associated string array if key is found. * */
@Override public String[] getStringArray(String key) { List<Object> list = getList(key); // transform property values into strings String[] tokens = new String[list.size()]; for (int i = 0; i < tokens.length; i++) { tokens[i] = String.valueOf(list.get(i)); } return tokens; }
Return the configuration at the specified index.
Params:
  • index – The index of the configuration to retrieve
Returns:the configuration at this index
/** * Return the configuration at the specified index. * * @param index The index of the configuration to retrieve * @return the configuration at this index */
public Configuration getConfiguration(int index) { return configList.get(index); }
Returns the configuration with the given name. This can be null if no such configuration exists.
Params:
  • name – the name of the configuration
Returns:the configuration with this name
/** * Returns the configuration with the given name. This can be <b>null</b> * if no such configuration exists. * * @param name the name of the configuration * @return the configuration with this name */
public Configuration getConfiguration(String name) { return namedConfigurations.get(name); }
Returns the container configuration In this configuration changes are stored.
Returns:the container configuration
/** * Returns the <em>container configuration</em> In this configuration * changes are stored. * * @return the container configuration */
public Configuration getContainerConfiguration() { return containerConfiguration; }
Returns a copy of this object. This implementation will create a deep clone, i.e. all configurations contained in this composite will also be cloned. This only works if all contained configurations support cloning; otherwise a runtime exception will be thrown. Registered event handlers won't get cloned.
/** * Returns a copy of this object. This implementation will create a deep * clone, i.e. all configurations contained in this composite will also be * cloned. This only works if all contained configurations support cloning; * otherwise a runtime exception will be thrown. Registered event handlers * won't get cloned. * */
@Override public Object clone() { try { ConcurrentCompositeConfiguration copy = (ConcurrentCompositeConfiguration) super .clone(); copy.clearConfigurationListeners(); copy.configList = new LinkedList<AbstractConfiguration>(); copy.containerConfiguration = (AbstractConfiguration) ConfigurationUtils .cloneConfiguration(getContainerConfiguration()); copy.configList.add(copy.containerConfiguration); for (Configuration config : configList) { if (config != getContainerConfiguration()) { copy.addConfiguration((AbstractConfiguration) ConfigurationUtils .cloneConfiguration(config)); } } return copy; } catch (CloneNotSupportedException cnex) { // cannot happen throw new ConfigurationRuntimeException(cnex); } }
Sets a flag whether added values for string properties should be checked for the list delimiter. This implementation ensures that the container configuration is correctly initialized.
Params:
  • delimiterParsingDisabled – the new value of the flag
/** * Sets a flag whether added values for string properties should be checked * for the list delimiter. This implementation ensures that the container * configuration is correctly initialized. * * @param delimiterParsingDisabled the new value of the flag */
@Override public void setDelimiterParsingDisabled(boolean delimiterParsingDisabled) { containerConfiguration.setDelimiterParsingDisabled(delimiterParsingDisabled); super.setDelimiterParsingDisabled(delimiterParsingDisabled); }
Sets the character that is used as list delimiter. This implementation ensures that the container configuration is correctly initialized.
Params:
  • listDelimiter – the new list delimiter character
/** * Sets the character that is used as list delimiter. This implementation * ensures that the container configuration is correctly initialized. * * @param listDelimiter the new list delimiter character */
@Override public void setListDelimiter(char listDelimiter) { containerConfiguration.setListDelimiter(listDelimiter); super.setListDelimiter(listDelimiter); }
Returns the configuration source, in which the specified key is defined. This method will iterate over all existing child configurations and check whether they contain the specified key. The following constellations are possible:
  • If the child configurations contains this key, the first one is returned.
  • If none of the child configurations contain the key, null is returned.
Params:
  • key – the key to be checked
Returns:the source configuration of this key
/** * Returns the configuration source, in which the specified key is defined. * This method will iterate over all existing child configurations and check * whether they contain the specified key. The following constellations are * possible: * <ul> * <li>If the child configurations contains this key, the first one is returned.</li> * <li>If none of the child configurations contain the key, <b>null</b> is * returned.</li> * </ul> * * @param key the key to be checked * @return the source configuration of this key */
public Configuration getSource(String key) { if (key == null) { throw new IllegalArgumentException("Key must not be null!"); } if (overrideProperties.containsKey(key)) { return overrideProperties; } for (Configuration conf : configList) { if (conf.containsKey(key)) { return conf; } } return null; }
Adds the value of a property to the given list. This method is used by getList() for gathering property values from the child configurations.
Params:
  • dest – the list for collecting the data
  • config – the configuration to query
  • key – the key of the property
/** * Adds the value of a property to the given list. This method is used by * {@code getList()} for gathering property values from the child * configurations. * * @param dest the list for collecting the data * @param config the configuration to query * @param key the key of the property */
private static void appendListProperty(List<Object> dest, Configuration config, String key) { Object value = config.getProperty(key); if (value != null) { if (value instanceof Collection) { Collection<?> col = (Collection<?>) value; dest.addAll(col); } else { dest.add(value); } } }
Return whether sub configurations should propagate events to listeners to this configuration.
/** * Return whether sub configurations should propagate events to * listeners to this configuration. */
public final boolean isPropagateEventFromSubConfigurations() { return propagateEventToParent; }
Set whether sub configurations should propagate events to listeners to this configuration. This is needed if this configuration is used as the configuration source of DynamicPropertyFactory.
Params:
  • propagateEventToParent – value to set
/** * Set whether sub configurations should propagate events to * listeners to this configuration. This is needed if this configuration * is used as the configuration source of {@link DynamicPropertyFactory}. * * @param propagateEventToParent value to set */
public final void setPropagateEventFromSubConfigurations(boolean propagateEventToParent) { this.propagateEventToParent = propagateEventToParent; } }