| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You 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 org.apache.commons.configuration2; |
| |
| import java.math.BigDecimal; |
| import java.math.BigInteger; |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.HashMap; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Properties; |
| import java.util.Set; |
| import java.util.concurrent.ConcurrentHashMap; |
| import java.util.concurrent.ConcurrentMap; |
| |
| import org.apache.commons.configuration2.event.Event; |
| import org.apache.commons.configuration2.event.EventListener; |
| import org.apache.commons.configuration2.event.EventType; |
| import org.apache.commons.configuration2.interpol.ConfigurationInterpolator; |
| import org.apache.commons.configuration2.interpol.Lookup; |
| import org.apache.commons.configuration2.io.ConfigurationLogger; |
| import org.apache.commons.configuration2.tree.ExpressionEngine; |
| import org.apache.commons.configuration2.tree.ImmutableNode; |
| import org.apache.commons.configuration2.tree.NodeCombiner; |
| |
| /** |
| * <p> |
| * DynamicCombinedConfiguration allows a set of CombinedConfigurations to be |
| * used. |
| * </p> |
| * <p> |
| * Each CombinedConfiguration is referenced by a key that is dynamically |
| * constructed from a key pattern on each call. The key pattern will be resolved |
| * using the configured ConfigurationInterpolator. |
| * </p> |
| * <p> |
| * This Configuration implementation uses the configured {@code Synchronizer} to |
| * guard itself against concurrent access. If there are multiple threads |
| * accessing an instance concurrently, a fully functional {@code Synchronizer} |
| * implementation (e.g. {@code ReadWriteSynchronizer}) has to be used to ensure |
| * consistency and to avoid exceptions. The {@code Synchronizer} assigned to an |
| * instance is also passed to child configuration objects when they are created. |
| * </p> |
| * |
| * @since 1.6 |
| * @version $Id$ |
| */ |
| public class DynamicCombinedConfiguration extends CombinedConfiguration |
| { |
| /** |
| * Stores the current configuration for each involved thread. This value is |
| * set at the beginning of an operation and removed at the end. |
| */ |
| private static final ThreadLocal<CurrentConfigHolder> CURRENT_CONFIG = |
| new ThreadLocal<CurrentConfigHolder>(); |
| |
| /** The CombinedConfigurations */ |
| private final ConcurrentMap<String, CombinedConfiguration> configs = |
| new ConcurrentHashMap<String, CombinedConfiguration>(); |
| |
| /** Stores a list with the contained configurations. */ |
| private final List<ConfigData> configurations = new ArrayList<ConfigData>(); |
| |
| /** Stores a map with the named configurations. */ |
| private final Map<String, Configuration> namedConfigurations = |
| new HashMap<String, Configuration>(); |
| |
| /** The key pattern for the CombinedConfiguration map */ |
| private String keyPattern; |
| |
| /** Stores the combiner. */ |
| private NodeCombiner nodeCombiner; |
| |
| /** The name of the logger to use for each CombinedConfiguration */ |
| private String loggerName = DynamicCombinedConfiguration.class.getName(); |
| |
| /** The object for handling variable substitution in key patterns. */ |
| private final ConfigurationInterpolator localSubst; |
| |
| /** |
| * Creates a new instance of {@code DynamicCombinedConfiguration} and |
| * initializes the combiner to be used. |
| * |
| * @param comb the node combiner (can be <b>null</b>, then a union combiner |
| * is used as default) |
| */ |
| public DynamicCombinedConfiguration(NodeCombiner comb) |
| { |
| super(); |
| setNodeCombiner(comb); |
| initLogger(new ConfigurationLogger(DynamicCombinedConfiguration.class)); |
| localSubst = initLocalInterpolator(); |
| } |
| |
| /** |
| * Creates a new instance of {@code DynamicCombinedConfiguration} that uses |
| * a union combiner. |
| * |
| * @see org.apache.commons.configuration2.tree.UnionCombiner |
| */ |
| public DynamicCombinedConfiguration() |
| { |
| super(); |
| initLogger(new ConfigurationLogger(DynamicCombinedConfiguration.class)); |
| localSubst = initLocalInterpolator(); |
| } |
| |
| public void setKeyPattern(String pattern) |
| { |
| this.keyPattern = pattern; |
| } |
| |
| public String getKeyPattern() |
| { |
| return this.keyPattern; |
| } |
| |
| /** |
| * Set the name of the Logger to use on each CombinedConfiguration. |
| * @param name The Logger name. |
| */ |
| public void setLoggerName(String name) |
| { |
| this.loggerName = name; |
| } |
| |
| /** |
| * Returns the node combiner that is used for creating the combined node |
| * structure. |
| * |
| * @return the node combiner |
| */ |
| @Override |
| public NodeCombiner getNodeCombiner() |
| { |
| return nodeCombiner; |
| } |
| |
| /** |
| * Sets the node combiner. This object will be used when the combined node |
| * structure is to be constructed. It must not be <b>null</b>, otherwise an |
| * {@code IllegalArgumentException} exception is thrown. Changing the |
| * node combiner causes an invalidation of this combined configuration, so |
| * that the new combiner immediately takes effect. |
| * |
| * @param nodeCombiner the node combiner |
| */ |
| @Override |
| public void setNodeCombiner(NodeCombiner nodeCombiner) |
| { |
| if (nodeCombiner == null) |
| { |
| throw new IllegalArgumentException( |
| "Node combiner must not be null!"); |
| } |
| this.nodeCombiner = nodeCombiner; |
| invalidateAll(); |
| } |
| /** |
| * Adds a new configuration to this combined configuration. It is possible |
| * (but not mandatory) to give the new configuration a name. This name must |
| * be unique, otherwise a {@code ConfigurationRuntimeException} will |
| * be thrown. With the optional {@code 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 {@code "database.tables"}, |
| * all properties of the added configuration will occur in this branch. |
| * |
| * @param config the configuration to add (must not be <b>null</b>) |
| * @param name the name of this configuration (can be <b>null</b>) |
| * @param at the position of this configuration in the combined tree (can be |
| * <b>null</b>) |
| */ |
| @Override |
| public void addConfiguration(Configuration config, String name, |
| String at) |
| { |
| beginWrite(true); |
| try |
| { |
| ConfigData cd = new ConfigData(config, name, at); |
| configurations.add(cd); |
| if (name != null) |
| { |
| namedConfigurations.put(name, config); |
| } |
| |
| // clear cache of all child configurations |
| configs.clear(); |
| } |
| finally |
| { |
| endWrite(); |
| } |
| } |
| /** |
| * Returns the number of configurations that are contained in this combined |
| * configuration. |
| * |
| * @return the number of contained configurations |
| */ |
| @Override |
| public int getNumberOfConfigurations() |
| { |
| beginRead(false); |
| try |
| { |
| return configurations.size(); |
| } |
| finally |
| { |
| endRead(); |
| } |
| } |
| |
| /** |
| * Returns the configuration at the specified index. The contained |
| * configurations are numbered in the order they were added to this combined |
| * configuration. The index of the first configuration is 0. |
| * |
| * @param index the index |
| * @return the configuration at this index |
| */ |
| @Override |
| public Configuration getConfiguration(int index) |
| { |
| beginRead(false); |
| try |
| { |
| ConfigData cd = configurations.get(index); |
| return cd.getConfiguration(); |
| } |
| finally |
| { |
| endRead(); |
| } |
| } |
| |
| /** |
| * 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 |
| */ |
| @Override |
| public Configuration getConfiguration(String name) |
| { |
| beginRead(false); |
| try |
| { |
| return namedConfigurations.get(name); |
| } |
| finally |
| { |
| endRead(); |
| } |
| } |
| |
| /** |
| * Returns a set with the names of all configurations contained in this |
| * combined 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>) |
| */ |
| @Override |
| public Set<String> getConfigurationNames() |
| { |
| beginRead(false); |
| try |
| { |
| return namedConfigurations.keySet(); |
| } |
| finally |
| { |
| endRead(); |
| } |
| } |
| |
| /** |
| * 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) |
| */ |
| @Override |
| public Configuration removeConfiguration(String name) |
| { |
| Configuration conf = getConfiguration(name); |
| if (conf != null) |
| { |
| removeConfiguration(conf); |
| } |
| return conf; |
| } |
| |
| /** |
| * Removes the specified configuration from this combined configuration. |
| * |
| * @param config the configuration to be removed |
| * @return a flag whether this configuration was found and could be removed |
| */ |
| @Override |
| public boolean removeConfiguration(Configuration config) |
| { |
| beginWrite(false); |
| try |
| { |
| for (int index = 0; index < getNumberOfConfigurations(); index++) |
| { |
| if (configurations.get(index).getConfiguration() == config) |
| { |
| removeConfigurationAt(index); |
| return true; |
| } |
| } |
| |
| return false; |
| } |
| finally |
| { |
| endWrite(); |
| } |
| } |
| |
| /** |
| * Removes the configuration at the specified index. |
| * |
| * @param index the index |
| * @return the removed configuration |
| */ |
| @Override |
| public Configuration removeConfigurationAt(int index) |
| { |
| beginWrite(false); |
| try |
| { |
| ConfigData cd = configurations.remove(index); |
| if (cd.getName() != null) |
| { |
| namedConfigurations.remove(cd.getName()); |
| } |
| return cd.getConfiguration(); |
| } |
| finally |
| { |
| endWrite(); |
| } |
| } |
| |
| @Override |
| protected void addPropertyInternal(String key, Object value) |
| { |
| this.getCurrentConfig().addProperty(key, value); |
| } |
| |
| @Override |
| protected void clearInternal() |
| { |
| if (configs != null) |
| { |
| this.getCurrentConfig().clear(); |
| } |
| } |
| |
| @Override |
| protected void clearPropertyDirect(String key) |
| { |
| this.getCurrentConfig().clearProperty(key); |
| } |
| |
| @Override |
| protected boolean containsKeyInternal(String key) |
| { |
| return this.getCurrentConfig().containsKey(key); |
| } |
| |
| @Override |
| public BigDecimal getBigDecimal(String key, BigDecimal defaultValue) |
| { |
| return this.getCurrentConfig().getBigDecimal(key, defaultValue); |
| } |
| |
| @Override |
| public BigDecimal getBigDecimal(String key) |
| { |
| return this.getCurrentConfig().getBigDecimal(key); |
| } |
| |
| @Override |
| public BigInteger getBigInteger(String key, BigInteger defaultValue) |
| { |
| return this.getCurrentConfig().getBigInteger(key, defaultValue); |
| } |
| |
| @Override |
| public BigInteger getBigInteger(String key) |
| { |
| return this.getCurrentConfig().getBigInteger(key); |
| } |
| |
| @Override |
| public boolean getBoolean(String key, boolean defaultValue) |
| { |
| return this.getCurrentConfig().getBoolean(key, defaultValue); |
| } |
| |
| @Override |
| public Boolean getBoolean(String key, Boolean defaultValue) |
| { |
| return this.getCurrentConfig().getBoolean(key, defaultValue); |
| } |
| |
| @Override |
| public boolean getBoolean(String key) |
| { |
| return this.getCurrentConfig().getBoolean(key); |
| } |
| |
| @Override |
| public byte getByte(String key, byte defaultValue) |
| { |
| return this.getCurrentConfig().getByte(key, defaultValue); |
| } |
| |
| @Override |
| public Byte getByte(String key, Byte defaultValue) |
| { |
| return this.getCurrentConfig().getByte(key, defaultValue); |
| } |
| |
| @Override |
| public byte getByte(String key) |
| { |
| return this.getCurrentConfig().getByte(key); |
| } |
| |
| @Override |
| public double getDouble(String key, double defaultValue) |
| { |
| return this.getCurrentConfig().getDouble(key, defaultValue); |
| } |
| |
| @Override |
| public Double getDouble(String key, Double defaultValue) |
| { |
| return this.getCurrentConfig().getDouble(key, defaultValue); |
| } |
| |
| @Override |
| public double getDouble(String key) |
| { |
| return this.getCurrentConfig().getDouble(key); |
| } |
| |
| @Override |
| public float getFloat(String key, float defaultValue) |
| { |
| return this.getCurrentConfig().getFloat(key, defaultValue); |
| } |
| |
| @Override |
| public Float getFloat(String key, Float defaultValue) |
| { |
| return this.getCurrentConfig().getFloat(key, defaultValue); |
| } |
| |
| @Override |
| public float getFloat(String key) |
| { |
| return this.getCurrentConfig().getFloat(key); |
| } |
| |
| @Override |
| public int getInt(String key, int defaultValue) |
| { |
| return this.getCurrentConfig().getInt(key, defaultValue); |
| } |
| |
| @Override |
| public int getInt(String key) |
| { |
| return this.getCurrentConfig().getInt(key); |
| } |
| |
| @Override |
| public Integer getInteger(String key, Integer defaultValue) |
| { |
| return this.getCurrentConfig().getInteger(key, defaultValue); |
| } |
| |
| @Override |
| protected Iterator<String> getKeysInternal() |
| { |
| return this.getCurrentConfig().getKeys(); |
| } |
| |
| @Override |
| protected Iterator<String> getKeysInternal(String prefix) |
| { |
| return this.getCurrentConfig().getKeys(prefix); |
| } |
| |
| @Override |
| public List<Object> getList(String key, List<?> defaultValue) |
| { |
| return this.getCurrentConfig().getList(key, defaultValue); |
| } |
| |
| @Override |
| public List<Object> getList(String key) |
| { |
| return this.getCurrentConfig().getList(key); |
| } |
| |
| @Override |
| public long getLong(String key, long defaultValue) |
| { |
| return this.getCurrentConfig().getLong(key, defaultValue); |
| } |
| |
| @Override |
| public Long getLong(String key, Long defaultValue) |
| { |
| return this.getCurrentConfig().getLong(key, defaultValue); |
| } |
| |
| @Override |
| public long getLong(String key) |
| { |
| return this.getCurrentConfig().getLong(key); |
| } |
| |
| @Override |
| public Properties getProperties(String key) |
| { |
| return this.getCurrentConfig().getProperties(key); |
| } |
| |
| @Override |
| protected Object getPropertyInternal(String key) |
| { |
| return this.getCurrentConfig().getProperty(key); |
| } |
| |
| @Override |
| public short getShort(String key, short defaultValue) |
| { |
| return this.getCurrentConfig().getShort(key, defaultValue); |
| } |
| |
| @Override |
| public Short getShort(String key, Short defaultValue) |
| { |
| return this.getCurrentConfig().getShort(key, defaultValue); |
| } |
| |
| @Override |
| public short getShort(String key) |
| { |
| return this.getCurrentConfig().getShort(key); |
| } |
| |
| @Override |
| public String getString(String key, String defaultValue) |
| { |
| return this.getCurrentConfig().getString(key, defaultValue); |
| } |
| |
| @Override |
| public String getString(String key) |
| { |
| return this.getCurrentConfig().getString(key); |
| } |
| |
| @Override |
| public String[] getStringArray(String key) |
| { |
| return this.getCurrentConfig().getStringArray(key); |
| } |
| |
| @Override |
| protected boolean isEmptyInternal() |
| { |
| return this.getCurrentConfig().isEmpty(); |
| } |
| |
| @Override |
| protected int sizeInternal() |
| { |
| return this.getCurrentConfig().size(); |
| } |
| |
| @Override |
| protected void setPropertyInternal(String key, Object value) |
| { |
| if (configs != null) |
| { |
| this.getCurrentConfig().setProperty(key, value); |
| } |
| } |
| |
| @Override |
| public Configuration subset(String prefix) |
| { |
| return this.getCurrentConfig().subset(prefix); |
| } |
| |
| @Override |
| public ExpressionEngine getExpressionEngine() |
| { |
| return super.getExpressionEngine(); |
| } |
| |
| @Override |
| public void setExpressionEngine(ExpressionEngine expressionEngine) |
| { |
| super.setExpressionEngine(expressionEngine); |
| } |
| |
| @Override |
| protected void addNodesInternal(String key, Collection<? extends ImmutableNode> nodes) |
| { |
| this.getCurrentConfig().addNodes(key, nodes); |
| } |
| |
| @Override |
| public HierarchicalConfiguration<ImmutableNode> configurationAt(String key, boolean supportUpdates) |
| { |
| return this.getCurrentConfig().configurationAt(key, supportUpdates); |
| } |
| |
| @Override |
| public HierarchicalConfiguration<ImmutableNode> configurationAt(String key) |
| { |
| return this.getCurrentConfig().configurationAt(key); |
| } |
| |
| @Override |
| public List<HierarchicalConfiguration<ImmutableNode>> configurationsAt(String key) |
| { |
| return this.getCurrentConfig().configurationsAt(key); |
| } |
| |
| @Override |
| protected Object clearTreeInternal(String key) |
| { |
| this.getCurrentConfig().clearTree(key); |
| return Collections.emptyList(); |
| } |
| |
| @Override |
| protected int getMaxIndexInternal(String key) |
| { |
| return this.getCurrentConfig().getMaxIndex(key); |
| } |
| |
| @Override |
| public Configuration interpolatedConfiguration() |
| { |
| return this.getCurrentConfig().interpolatedConfiguration(); |
| } |
| |
| |
| /** |
| * Returns the configuration source, in which the specified key is defined. |
| * This method will determine the configuration node that is identified by |
| * the given key. The following constellations are possible: |
| * <ul> |
| * <li>If no node object is found for this key, <b>null</b> is returned.</li> |
| * <li>If the key maps to multiple nodes belonging to different |
| * configuration sources, a {@code IllegalArgumentException} is |
| * thrown (in this case no unique source can be determined).</li> |
| * <li>If exactly one node is found for the key, the (child) configuration |
| * object, to which the node belongs is determined and returned.</li> |
| * <li>For keys that have been added directly to this combined |
| * configuration and that do not belong to the namespaces defined by |
| * existing child configurations this configuration will be returned.</li> |
| * </ul> |
| * |
| * @param key the key of a configuration property |
| * @return the configuration, to which this property belongs or <b>null</b> |
| * if the key cannot be resolved |
| * @throws IllegalArgumentException if the key maps to multiple properties |
| * and the source cannot be determined, or if the key is <b>null</b> |
| */ |
| @Override |
| public Configuration getSource(String key) |
| { |
| if (key == null) |
| { |
| throw new IllegalArgumentException("Key must not be null!"); |
| } |
| return getCurrentConfig().getSource(key); |
| } |
| |
| @Override |
| public void clearEventListeners() |
| { |
| for (CombinedConfiguration cc : configs.values()) |
| { |
| cc.clearEventListeners(); |
| } |
| super.clearEventListeners(); |
| } |
| |
| @Override |
| public <T extends Event> void addEventListener(EventType<T> eventType, |
| EventListener<? super T> listener) |
| { |
| for (CombinedConfiguration cc : configs.values()) |
| { |
| cc.addEventListener(eventType, listener); |
| } |
| super.addEventListener(eventType, listener); |
| } |
| |
| @Override |
| public <T extends Event> boolean removeEventListener( |
| EventType<T> eventType, EventListener<? super T> listener) |
| { |
| for (CombinedConfiguration cc : configs.values()) |
| { |
| cc.removeEventListener(eventType, listener); |
| } |
| return super.removeEventListener(eventType, listener); |
| } |
| |
| @Override |
| public void clearErrorListeners() |
| { |
| for (CombinedConfiguration cc : configs.values()) |
| { |
| cc.clearErrorListeners(); |
| } |
| super.clearErrorListeners(); |
| } |
| |
| /** |
| * Returns a copy of this object. This implementation performs a deep clone, |
| * i.e. all contained configurations will be cloned, too. For this to work, |
| * all contained configurations must be cloneable. Registered event |
| * listeners won't be cloned. The clone will use the same node combiner than |
| * the original. |
| * |
| * @return the copied object |
| */ |
| @Override |
| public Object clone() |
| { |
| return super.clone(); |
| } |
| |
| /** |
| * Invalidates the current combined configuration. This means that the next time a |
| * property is accessed the combined node structure must be re-constructed. |
| * Invalidation of a combined configuration also means that an event of type |
| * {@code 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). |
| */ |
| @Override |
| public void invalidate() |
| { |
| getCurrentConfig().invalidate(); |
| } |
| |
| public void invalidateAll() |
| { |
| for (CombinedConfiguration cc : configs.values()) |
| { |
| cc.invalidate(); |
| } |
| } |
| |
| /** |
| * {@inheritDoc} This implementation ensures that the current configuration |
| * is initialized. The lock counter is increased. |
| */ |
| @Override |
| protected void beginRead(boolean optimize) |
| { |
| CurrentConfigHolder cch = ensureCurrentConfiguration(); |
| cch.incrementLockCount(); |
| if (!optimize && cch.getCurrentConfiguration() == null) |
| { |
| // delegate to beginWrite() which creates the child configuration |
| beginWrite(false); |
| endWrite(); |
| } |
| |
| // This actually uses our own synchronizer |
| cch.getCurrentConfiguration().beginRead(optimize); |
| } |
| |
| /** |
| * {@inheritDoc} This implementation ensures that the current configuration |
| * is initialized. If necessary, a new child configuration instance is |
| * created. |
| */ |
| @Override |
| protected void beginWrite(boolean optimize) |
| { |
| CurrentConfigHolder cch = ensureCurrentConfiguration(); |
| cch.incrementLockCount(); |
| |
| super.beginWrite(optimize); |
| if (!optimize && cch.getCurrentConfiguration() == null) |
| { |
| cch.setCurrentConfiguration(createChildConfiguration()); |
| configs.put(cch.getKey(), cch.getCurrentConfiguration()); |
| initChildConfiguration(cch.getCurrentConfiguration()); |
| } |
| } |
| |
| /** |
| * {@inheritDoc} This implementation clears the current configuration if |
| * necessary. |
| */ |
| @Override |
| protected void endRead() |
| { |
| CURRENT_CONFIG.get().getCurrentConfiguration().endRead(); |
| releaseLock(); |
| } |
| |
| /** |
| * {@inheritDoc} This implementation clears the current configuration if |
| * necessary. |
| */ |
| @Override |
| protected void endWrite() |
| { |
| super.endWrite(); |
| releaseLock(); |
| } |
| |
| /** |
| * Decrements the lock count of the current configuration holder. If it |
| * reaches 0, the current configuration is removed. (It is then reevaluated |
| * when the next operation starts.) |
| */ |
| private void releaseLock() |
| { |
| CurrentConfigHolder cch = CURRENT_CONFIG.get(); |
| assert cch != null : "No current configuration!"; |
| if (cch.decrementLockCountAndCheckRelease()) |
| { |
| CURRENT_CONFIG.remove(); |
| } |
| } |
| |
| /** |
| * Returns the current configuration. This configuration was initialized at |
| * the beginning of an operation and stored in a thread-local variable. Some |
| * methods of this class call this method directly without requesting a lock |
| * before. To deal with this, we always request an additional read lock. |
| * |
| * @return the current configuration |
| */ |
| private CombinedConfiguration getCurrentConfig() |
| { |
| CombinedConfiguration config; |
| String key; |
| beginRead(false); |
| try |
| { |
| config = CURRENT_CONFIG.get().getCurrentConfiguration(); |
| key = CURRENT_CONFIG.get().getKey(); |
| } |
| finally |
| { |
| endRead(); |
| } |
| |
| if (getLogger().isDebugEnabled()) |
| { |
| getLogger().debug("Returning config for " + key + ": " + config); |
| } |
| return config; |
| } |
| |
| /** |
| * Creates a new, uninitialized child configuration. |
| * |
| * @return the new child configuration |
| */ |
| private CombinedConfiguration createChildConfiguration() |
| { |
| return new CombinedConfiguration(getNodeCombiner()); |
| } |
| |
| /** |
| * Initializes a newly created child configuration. This method copies a |
| * bunch of settings from this instance to the child configuration. |
| * |
| * @param config the child configuration to be initialized |
| */ |
| private void initChildConfiguration(CombinedConfiguration config) |
| { |
| if (loggerName != null) |
| { |
| config.setLogger(new ConfigurationLogger(loggerName)); |
| } |
| config.setExpressionEngine(this.getExpressionEngine()); |
| config.setConversionExpressionEngine(getConversionExpressionEngine()); |
| config.setListDelimiterHandler(getListDelimiterHandler()); |
| copyEventListeners(config); |
| for (ConfigData data : configurations) |
| { |
| config.addConfiguration(data.getConfiguration(), data.getName(), |
| data.getAt()); |
| } |
| config.setSynchronizer(getSynchronizer()); |
| } |
| |
| /** |
| * Creates a {@code ConfigurationInterpolator} instance for performing local |
| * variable substitutions. This implementation returns an object which |
| * shares the prefix lookups from this configuration's |
| * {@code ConfigurationInterpolator}, but does not define any other lookups. |
| * |
| * @return the {@code ConfigurationInterpolator} |
| */ |
| private ConfigurationInterpolator initLocalInterpolator() |
| { |
| return new ConfigurationInterpolator() |
| { |
| @Override |
| protected Lookup fetchLookupForPrefix(String prefix) |
| { |
| return ConfigurationInterpolator |
| .nullSafeLookup(getInterpolator().getLookups().get( |
| prefix)); |
| } |
| }; |
| } |
| |
| /** |
| * Checks whether the current configuration is set. If not, a |
| * {@code CurrentConfigHolder} is now created and initialized, and |
| * associated with the current thread. The member for the current |
| * configuration is undefined if for the current key no configuration exists |
| * yet. |
| * |
| * @return the {@code CurrentConfigHolder} instance for the current thread |
| */ |
| private CurrentConfigHolder ensureCurrentConfiguration() |
| { |
| CurrentConfigHolder cch = CURRENT_CONFIG.get(); |
| if (cch == null) |
| { |
| String key = String.valueOf(localSubst.interpolate(keyPattern)); |
| cch = new CurrentConfigHolder(key); |
| cch.setCurrentConfiguration(configs.get(key)); |
| CURRENT_CONFIG.set(cch); |
| } |
| return cch; |
| } |
| |
| /** |
| * Internal class that identifies each Configuration. |
| */ |
| static class ConfigData |
| { |
| /** Stores a reference to the configuration. */ |
| private final Configuration configuration; |
| |
| /** Stores the name under which the configuration is stored. */ |
| private final String name; |
| |
| /** Stores the at string.*/ |
| private final String at; |
| |
| /** |
| * Creates a new instance of {@code ConfigData} and initializes |
| * it. |
| * |
| * @param config the configuration |
| * @param n the name |
| * @param at the at position |
| */ |
| public ConfigData(Configuration config, String n, String at) |
| { |
| configuration = config; |
| name = n; |
| this.at = at; |
| } |
| |
| /** |
| * Returns the stored configuration. |
| * |
| * @return the configuration |
| */ |
| public Configuration getConfiguration() |
| { |
| return configuration; |
| } |
| |
| /** |
| * Returns the configuration's name. |
| * |
| * @return the name |
| */ |
| public String getName() |
| { |
| return name; |
| } |
| |
| /** |
| * Returns the at position of this configuration. |
| * |
| * @return the at position |
| */ |
| public String getAt() |
| { |
| return at; |
| } |
| |
| } |
| |
| /** |
| * A simple data class holding information about the current configuration |
| * while an operation for a thread is processed. |
| */ |
| private static class CurrentConfigHolder |
| { |
| /** Stores the current configuration of the current thread. */ |
| private CombinedConfiguration currentConfiguration; |
| |
| /** |
| * Stores the key of the configuration evaluated for the current thread |
| * at the beginning of an operation. |
| */ |
| private final String key; |
| |
| /** A counter for reentrant locks. */ |
| private int lockCount; |
| |
| /** |
| * Creates a new instance of {@code CurrentConfigHolder} and initializes |
| * it with the key for the current configuration. |
| * |
| * @param curKey the current key |
| */ |
| public CurrentConfigHolder(String curKey) |
| { |
| key = curKey; |
| } |
| |
| /** |
| * Returns the current configuration. |
| * |
| * @return the current configuration |
| */ |
| public CombinedConfiguration getCurrentConfiguration() |
| { |
| return currentConfiguration; |
| } |
| |
| /** |
| * Sets the current configuration. |
| * |
| * @param currentConfiguration the current configuration |
| */ |
| public void setCurrentConfiguration( |
| CombinedConfiguration currentConfiguration) |
| { |
| this.currentConfiguration = currentConfiguration; |
| } |
| |
| /** |
| * Returns the current key. |
| * |
| * @return the current key |
| */ |
| public String getKey() |
| { |
| return key; |
| } |
| |
| /** |
| * Increments the lock counter. |
| */ |
| public void incrementLockCount() |
| { |
| lockCount++; |
| } |
| |
| /** |
| * Decrements the lock counter and checks whether it has reached 0. In |
| * this cause, the operation is complete, and the lock can be released. |
| * |
| * @return <b>true</b> if the lock count reaches 0, <b>false</b> |
| * otherwise |
| */ |
| public boolean decrementLockCountAndCheckRelease() |
| { |
| return --lockCount == 0; |
| } |
| } |
| } |