| /* |
| * 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.BaseEventSource; |
| 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.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 |
| */ |
| public class DynamicCombinedConfiguration extends CombinedConfiguration { |
| /** |
| * 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(final Configuration config, final String n, final String at) { |
| configuration = config; |
| name = n; |
| this.at = at; |
| } |
| |
| /** |
| * Gets the at position of this configuration. |
| * |
| * @return the at position |
| */ |
| public String getAt() { |
| return at; |
| } |
| |
| /** |
| * Gets the stored configuration. |
| * |
| * @return the configuration |
| */ |
| public Configuration getConfiguration() { |
| return configuration; |
| } |
| |
| /** |
| * Gets the configuration's name. |
| * |
| * @return the name |
| */ |
| public String getName() { |
| return name; |
| } |
| |
| } |
| |
| /** |
| * A simple data class holding information about the current configuration while an operation for a thread is processed. |
| */ |
| private static final 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(final String curKey) { |
| key = curKey; |
| } |
| |
| /** |
| * 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; |
| } |
| |
| /** |
| * Gets the current configuration. |
| * |
| * @return the current configuration |
| */ |
| public CombinedConfiguration getCurrentConfiguration() { |
| return currentConfiguration; |
| } |
| |
| /** |
| * Gets the current key. |
| * |
| * @return the current key |
| */ |
| public String getKey() { |
| return key; |
| } |
| |
| /** |
| * Increments the lock counter. |
| */ |
| public void incrementLockCount() { |
| lockCount++; |
| } |
| |
| /** |
| * Sets the current configuration. |
| * |
| * @param currentConfiguration the current configuration |
| */ |
| public void setCurrentConfiguration(final CombinedConfiguration currentConfiguration) { |
| this.currentConfiguration = currentConfiguration; |
| } |
| } |
| |
| /** |
| * 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<>(); |
| |
| /** The CombinedConfigurations */ |
| private final ConcurrentMap<String, CombinedConfiguration> configs = new ConcurrentHashMap<>(); |
| |
| /** Stores a list with the contained configurations. */ |
| private final List<ConfigData> configurations = new ArrayList<>(); |
| |
| /** Stores a map with the named configurations. */ |
| private final Map<String, Configuration> namedConfigurations = new HashMap<>(); |
| |
| /** 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} that uses a union combiner. |
| * |
| * @see org.apache.commons.configuration2.tree.UnionCombiner |
| */ |
| public DynamicCombinedConfiguration() { |
| initLogger(new ConfigurationLogger(DynamicCombinedConfiguration.class)); |
| localSubst = initLocalInterpolator(); |
| } |
| |
| /** |
| * 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(final NodeCombiner comb) { |
| setNodeCombiner(comb); |
| initLogger(new ConfigurationLogger(DynamicCombinedConfiguration.class)); |
| localSubst = initLocalInterpolator(); |
| } |
| |
| /** |
| * 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(final Configuration config, final String name, final String at) { |
| beginWrite(true); |
| try { |
| final 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(); |
| } |
| } |
| |
| @Override |
| public <T extends Event> void addEventListener(final EventType<T> eventType, final EventListener<? super T> listener) { |
| configs.values().forEach(cc -> cc.addEventListener(eventType, listener)); |
| super.addEventListener(eventType, listener); |
| } |
| |
| @Override |
| protected void addNodesInternal(final String key, final Collection<? extends ImmutableNode> nodes) { |
| this.getCurrentConfig().addNodes(key, nodes); |
| } |
| |
| @Override |
| protected void addPropertyInternal(final String key, final Object value) { |
| this.getCurrentConfig().addProperty(key, value); |
| } |
| |
| /** |
| * {@inheritDoc} This implementation ensures that the current configuration is initialized. The lock counter is |
| * increased. |
| */ |
| @Override |
| protected void beginRead(final boolean optimize) { |
| final 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(final boolean optimize) { |
| final 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()); |
| } |
| } |
| |
| @Override |
| public void clearErrorListeners() { |
| configs.values().forEach(BaseEventSource::clearErrorListeners); |
| super.clearErrorListeners(); |
| } |
| |
| @Override |
| public void clearEventListeners() { |
| configs.values().forEach(CombinedConfiguration::clearEventListeners); |
| super.clearEventListeners(); |
| } |
| |
| @Override |
| protected void clearInternal() { |
| this.getCurrentConfig().clear(); |
| } |
| |
| @Override |
| protected void clearPropertyDirect(final String key) { |
| this.getCurrentConfig().clearProperty(key); |
| } |
| |
| @Override |
| protected Object clearTreeInternal(final String key) { |
| this.getCurrentConfig().clearTree(key); |
| return Collections.emptyList(); |
| } |
| |
| @Override |
| public HierarchicalConfiguration<ImmutableNode> configurationAt(final String key) { |
| return this.getCurrentConfig().configurationAt(key); |
| } |
| |
| @Override |
| public HierarchicalConfiguration<ImmutableNode> configurationAt(final String key, final boolean supportUpdates) { |
| return this.getCurrentConfig().configurationAt(key, supportUpdates); |
| } |
| |
| @Override |
| public List<HierarchicalConfiguration<ImmutableNode>> configurationsAt(final String key) { |
| return this.getCurrentConfig().configurationsAt(key); |
| } |
| |
| @Override |
| protected boolean containsKeyInternal(final String key) { |
| return this.getCurrentConfig().containsKey(key); |
| } |
| |
| /** |
| * Creates a new, uninitialized child configuration. |
| * |
| * @return the new child configuration |
| */ |
| private CombinedConfiguration createChildConfiguration() { |
| return new CombinedConfiguration(getNodeCombiner()); |
| } |
| |
| /** |
| * {@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(); |
| } |
| |
| /** |
| * 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) { |
| final String key = String.valueOf(localSubst.interpolate(keyPattern)); |
| cch = new CurrentConfigHolder(key); |
| cch.setCurrentConfiguration(configs.get(key)); |
| CURRENT_CONFIG.set(cch); |
| } |
| return cch; |
| } |
| |
| @Override |
| public BigDecimal getBigDecimal(final String key) { |
| return this.getCurrentConfig().getBigDecimal(key); |
| } |
| |
| @Override |
| public BigDecimal getBigDecimal(final String key, final BigDecimal defaultValue) { |
| return this.getCurrentConfig().getBigDecimal(key, defaultValue); |
| } |
| |
| @Override |
| public BigInteger getBigInteger(final String key) { |
| return this.getCurrentConfig().getBigInteger(key); |
| } |
| |
| @Override |
| public BigInteger getBigInteger(final String key, final BigInteger defaultValue) { |
| return this.getCurrentConfig().getBigInteger(key, defaultValue); |
| } |
| |
| @Override |
| public boolean getBoolean(final String key) { |
| return this.getCurrentConfig().getBoolean(key); |
| } |
| |
| @Override |
| public boolean getBoolean(final String key, final boolean defaultValue) { |
| return this.getCurrentConfig().getBoolean(key, defaultValue); |
| } |
| |
| @Override |
| public Boolean getBoolean(final String key, final Boolean defaultValue) { |
| return this.getCurrentConfig().getBoolean(key, defaultValue); |
| } |
| |
| @Override |
| public byte getByte(final String key) { |
| return this.getCurrentConfig().getByte(key); |
| } |
| |
| @Override |
| public byte getByte(final String key, final byte defaultValue) { |
| return this.getCurrentConfig().getByte(key, defaultValue); |
| } |
| |
| @Override |
| public Byte getByte(final String key, final Byte defaultValue) { |
| return this.getCurrentConfig().getByte(key, defaultValue); |
| } |
| |
| /** |
| * Gets 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(final int index) { |
| beginRead(false); |
| try { |
| final ConfigData cd = configurations.get(index); |
| return cd.getConfiguration(); |
| } finally { |
| endRead(); |
| } |
| } |
| |
| /** |
| * Gets 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(final String name) { |
| beginRead(false); |
| try { |
| return namedConfigurations.get(name); |
| } finally { |
| endRead(); |
| } |
| } |
| |
| /** |
| * Gets 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(); |
| } |
| } |
| |
| /** |
| * Gets 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; |
| } |
| |
| @Override |
| public double getDouble(final String key) { |
| return this.getCurrentConfig().getDouble(key); |
| } |
| |
| @Override |
| public double getDouble(final String key, final double defaultValue) { |
| return this.getCurrentConfig().getDouble(key, defaultValue); |
| } |
| |
| @Override |
| public Double getDouble(final String key, final Double defaultValue) { |
| return this.getCurrentConfig().getDouble(key, defaultValue); |
| } |
| |
| @Override |
| public float getFloat(final String key) { |
| return this.getCurrentConfig().getFloat(key); |
| } |
| |
| @Override |
| public float getFloat(final String key, final float defaultValue) { |
| return this.getCurrentConfig().getFloat(key, defaultValue); |
| } |
| |
| @Override |
| public Float getFloat(final String key, final Float defaultValue) { |
| return this.getCurrentConfig().getFloat(key, defaultValue); |
| } |
| |
| @Override |
| public int getInt(final String key) { |
| return this.getCurrentConfig().getInt(key); |
| } |
| |
| @Override |
| public int getInt(final String key, final int defaultValue) { |
| return this.getCurrentConfig().getInt(key, defaultValue); |
| } |
| |
| @Override |
| public Integer getInteger(final String key, final Integer defaultValue) { |
| return this.getCurrentConfig().getInteger(key, defaultValue); |
| } |
| |
| public String getKeyPattern() { |
| return this.keyPattern; |
| } |
| |
| @Override |
| protected Iterator<String> getKeysInternal() { |
| return this.getCurrentConfig().getKeys(); |
| } |
| |
| @Override |
| protected Iterator<String> getKeysInternal(final String prefix) { |
| return this.getCurrentConfig().getKeys(prefix); |
| } |
| |
| @Override |
| public List<Object> getList(final String key) { |
| return this.getCurrentConfig().getList(key); |
| } |
| |
| @Override |
| public List<Object> getList(final String key, final List<?> defaultValue) { |
| return this.getCurrentConfig().getList(key, defaultValue); |
| } |
| |
| @Override |
| public long getLong(final String key) { |
| return this.getCurrentConfig().getLong(key); |
| } |
| |
| @Override |
| public long getLong(final String key, final long defaultValue) { |
| return this.getCurrentConfig().getLong(key, defaultValue); |
| } |
| |
| @Override |
| public Long getLong(final String key, final Long defaultValue) { |
| return this.getCurrentConfig().getLong(key, defaultValue); |
| } |
| |
| @Override |
| protected int getMaxIndexInternal(final String key) { |
| return this.getCurrentConfig().getMaxIndex(key); |
| } |
| |
| /** |
| * Gets the node combiner that is used for creating the combined node structure. |
| * |
| * @return the node combiner |
| */ |
| @Override |
| public NodeCombiner getNodeCombiner() { |
| return nodeCombiner; |
| } |
| |
| /** |
| * Gets 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(); |
| } |
| } |
| |
| @Override |
| public Properties getProperties(final String key) { |
| return this.getCurrentConfig().getProperties(key); |
| } |
| |
| @Override |
| protected Object getPropertyInternal(final String key) { |
| return this.getCurrentConfig().getProperty(key); |
| } |
| |
| @Override |
| public short getShort(final String key) { |
| return this.getCurrentConfig().getShort(key); |
| } |
| |
| @Override |
| public short getShort(final String key, final short defaultValue) { |
| return this.getCurrentConfig().getShort(key, defaultValue); |
| } |
| |
| @Override |
| public Short getShort(final String key, final Short defaultValue) { |
| return this.getCurrentConfig().getShort(key, defaultValue); |
| } |
| |
| /** |
| * Gets 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(final String key) { |
| if (key == null) { |
| throw new IllegalArgumentException("Key must not be null!"); |
| } |
| return getCurrentConfig().getSource(key); |
| } |
| |
| @Override |
| public String getString(final String key) { |
| return this.getCurrentConfig().getString(key); |
| } |
| |
| @Override |
| public String getString(final String key, final String defaultValue) { |
| return this.getCurrentConfig().getString(key, defaultValue); |
| } |
| |
| @Override |
| public String[] getStringArray(final String key) { |
| return this.getCurrentConfig().getStringArray(key); |
| } |
| |
| /** |
| * 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(final CombinedConfiguration config) { |
| if (loggerName != null) { |
| config.setLogger(new ConfigurationLogger(loggerName)); |
| } |
| config.setExpressionEngine(this.getExpressionEngine()); |
| config.setConversionExpressionEngine(getConversionExpressionEngine()); |
| config.setListDelimiterHandler(getListDelimiterHandler()); |
| copyEventListeners(config); |
| configurations.forEach(data -> 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(final String prefix) { |
| return ConfigurationInterpolator.nullSafeLookup(getInterpolator().getLookups().get(prefix)); |
| } |
| }; |
| } |
| |
| @Override |
| public Configuration interpolatedConfiguration() { |
| return this.getCurrentConfig().interpolatedConfiguration(); |
| } |
| |
| /** |
| * 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() { |
| configs.values().forEach(CombinedConfiguration::invalidate); |
| } |
| |
| @Override |
| protected boolean isEmptyInternal() { |
| return this.getCurrentConfig().isEmpty(); |
| } |
| |
| /** |
| * 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() { |
| final CurrentConfigHolder cch = CURRENT_CONFIG.get(); |
| assert cch != null : "No current configuration!"; |
| if (cch.decrementLockCountAndCheckRelease()) { |
| CURRENT_CONFIG.remove(); |
| } |
| } |
| |
| /** |
| * 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(final 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 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(final String name) { |
| final Configuration conf = getConfiguration(name); |
| if (conf != null) { |
| removeConfiguration(conf); |
| } |
| return conf; |
| } |
| |
| /** |
| * Removes the configuration at the specified index. |
| * |
| * @param index the index |
| * @return the removed configuration |
| */ |
| @Override |
| public Configuration removeConfigurationAt(final int index) { |
| beginWrite(false); |
| try { |
| final ConfigData cd = configurations.remove(index); |
| if (cd.getName() != null) { |
| namedConfigurations.remove(cd.getName()); |
| } |
| return cd.getConfiguration(); |
| } finally { |
| endWrite(); |
| } |
| } |
| |
| @Override |
| public <T extends Event> boolean removeEventListener(final EventType<T> eventType, final EventListener<? super T> listener) { |
| configs.values().forEach(cc -> cc.removeEventListener(eventType, listener)); |
| return super.removeEventListener(eventType, listener); |
| } |
| |
| public void setKeyPattern(final String pattern) { |
| this.keyPattern = pattern; |
| } |
| |
| /** |
| * Sets the name of the Logger to use on each CombinedConfiguration. |
| * |
| * @param name The Logger name. |
| */ |
| public void setLoggerName(final String name) { |
| this.loggerName = name; |
| } |
| |
| /** |
| * 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(final NodeCombiner nodeCombiner) { |
| if (nodeCombiner == null) { |
| throw new IllegalArgumentException("Node combiner must not be null!"); |
| } |
| this.nodeCombiner = nodeCombiner; |
| invalidateAll(); |
| } |
| |
| @Override |
| protected void setPropertyInternal(final String key, final Object value) { |
| this.getCurrentConfig().setProperty(key, value); |
| } |
| |
| @Override |
| protected int sizeInternal() { |
| return this.getCurrentConfig().size(); |
| } |
| |
| @Override |
| public Configuration subset(final String prefix) { |
| return this.getCurrentConfig().subset(prefix); |
| } |
| } |