/*
 * 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.configuration;

import java.io.File;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Reader;
import java.io.Writer;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.net.URL;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Properties;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.configuration.event.ConfigurationErrorEvent;
import org.apache.commons.configuration.event.ConfigurationErrorListener;
import org.apache.commons.configuration.event.ConfigurationEvent;
import org.apache.commons.configuration.event.ConfigurationListener;
import org.apache.commons.configuration.interpol.ConfigurationInterpolator;
import org.apache.commons.configuration.reloading.ReloadingStrategy;
import org.apache.commons.configuration.resolver.EntityResolverSupport;
import org.apache.commons.configuration.tree.ConfigurationNode;
import org.apache.commons.configuration.tree.ExpressionEngine;
import org.apache.commons.lang.text.StrSubstitutor;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.xml.sax.EntityResolver;
import org.xml.sax.SAXParseException;

This class provides access to multiple configuration files that reside in a location that can be specified by a pattern allowing applications to be multi-tenant. For example, providing a pattern of "file:///opt/config/${product}/${client}/config.xml" will result in "product" and "client" being resolved on every call. The configuration resulting from the resolved pattern will be saved for future access.
Author:Commons Configuration team
Since:1.6
Version:$Id: MultiFileHierarchicalConfiguration.java 1534064 2013-10-21 08:44:33Z henning $
/** * This class provides access to multiple configuration files that reside in a location that * can be specified by a pattern allowing applications to be multi-tenant. For example, * providing a pattern of "file:///opt/config/${product}/${client}/config.xml" will result in * "product" and "client" being resolved on every call. The configuration resulting from the * resolved pattern will be saved for future access. * @since 1.6 * @author <a * href="http://commons.apache.org/configuration/team-list.html">Commons * Configuration team</a> * @version $Id: MultiFileHierarchicalConfiguration.java 1534064 2013-10-21 08:44:33Z henning $ */
public class MultiFileHierarchicalConfiguration extends AbstractHierarchicalFileConfiguration implements ConfigurationListener, ConfigurationErrorListener, EntityResolverSupport {
Prevent recursion while resolving unprefixed properties.
/** * Prevent recursion while resolving unprefixed properties. */
private static ThreadLocal<Boolean> recursive = new ThreadLocal<Boolean>() { @Override protected synchronized Boolean initialValue() { return Boolean.FALSE; } };
Map of configurations
/** Map of configurations */
private final ConcurrentMap<String, XMLConfiguration> configurationsMap = new ConcurrentHashMap<String, XMLConfiguration>();
key pattern for configurationsMap
/** key pattern for configurationsMap */
private String pattern;
True if the constructor has finished
/** True if the constructor has finished */
private boolean init;
Return an empty configuration if loading fails
/** Return an empty configuration if loading fails */
private boolean ignoreException = true;
Capture the schema validation setting
/** Capture the schema validation setting */
private boolean schemaValidation;
Stores a flag whether DTD or Schema validation should be performed.
/** Stores a flag whether DTD or Schema validation should be performed.*/
private boolean validating;
A flag whether attribute splitting is disabled.
/** A flag whether attribute splitting is disabled.*/
private boolean attributeSplittingDisabled;
The Logger name to use
/** The Logger name to use */
private String loggerName = MultiFileHierarchicalConfiguration.class.getName();
The Reloading strategy to use on created configurations
/** The Reloading strategy to use on created configurations */
private ReloadingStrategy fileStrategy;
The EntityResolver
/** The EntityResolver */
private EntityResolver entityResolver;
The internally used helper object for variable substitution.
/** The internally used helper object for variable substitution. */
private StrSubstitutor localSubst = new StrSubstitutor(new ConfigurationInterpolator());
Default Constructor.
/** * Default Constructor. */
public MultiFileHierarchicalConfiguration() { super(); this.init = true; setLogger(LogFactory.getLog(loggerName)); }
Construct the configuration with the specified pattern.
Params:
  • pathPattern – The pattern to use to locate configuration files.
/** * Construct the configuration with the specified pattern. * @param pathPattern The pattern to use to locate configuration files. */
public MultiFileHierarchicalConfiguration(String pathPattern) { super(); this.pattern = pathPattern; this.init = true; setLogger(LogFactory.getLog(loggerName)); } public void setLoggerName(String name) { this.loggerName = name; }
Set the File pattern
Params:
  • pathPattern – The pattern for the path to the configuration.
/** * Set the File pattern * @param pathPattern The pattern for the path to the configuration. */
public void setFilePattern(String pathPattern) { this.pattern = pathPattern; } public boolean isSchemaValidation() { return schemaValidation; } public void setSchemaValidation(boolean schemaValidation) { this.schemaValidation = schemaValidation; } public boolean isValidating() { return validating; } public void setValidating(boolean validating) { this.validating = validating; } public boolean isAttributeSplittingDisabled() { return attributeSplittingDisabled; } public void setAttributeSplittingDisabled(boolean attributeSplittingDisabled) { this.attributeSplittingDisabled = attributeSplittingDisabled; } @Override public ReloadingStrategy getReloadingStrategy() { return fileStrategy; } @Override public void setReloadingStrategy(ReloadingStrategy strategy) { this.fileStrategy = strategy; } public void setEntityResolver(EntityResolver entityResolver) { this.entityResolver = entityResolver; } public EntityResolver getEntityResolver() { return this.entityResolver; }
Set to true if an empty Configuration should be returned when loading fails. If false an exception will be thrown.
Params:
  • ignoreException – The ignore value.
/** * Set to true if an empty Configuration should be returned when loading fails. If * false an exception will be thrown. * @param ignoreException The ignore value. */
public void setIgnoreException(boolean ignoreException) { this.ignoreException = ignoreException; } @Override public void addProperty(String key, Object value) { this.getConfiguration().addProperty(key, value); } @Override public void clear() { this.getConfiguration().clear(); } @Override public void clearProperty(String key) { this.getConfiguration().clearProperty(key); } @Override public boolean containsKey(String key) { return this.getConfiguration().containsKey(key); } @Override public BigDecimal getBigDecimal(String key, BigDecimal defaultValue) { return this.getConfiguration().getBigDecimal(key, defaultValue); } @Override public BigDecimal getBigDecimal(String key) { return this.getConfiguration().getBigDecimal(key); } @Override public BigInteger getBigInteger(String key, BigInteger defaultValue) { return this.getConfiguration().getBigInteger(key, defaultValue); } @Override public BigInteger getBigInteger(String key) { return this.getConfiguration().getBigInteger(key); } @Override public boolean getBoolean(String key, boolean defaultValue) { return this.getConfiguration().getBoolean(key, defaultValue); } @Override public Boolean getBoolean(String key, Boolean defaultValue) { return this.getConfiguration().getBoolean(key, defaultValue); } @Override public boolean getBoolean(String key) { return this.getConfiguration().getBoolean(key); } @Override public byte getByte(String key, byte defaultValue) { return this.getConfiguration().getByte(key, defaultValue); } @Override public Byte getByte(String key, Byte defaultValue) { return this.getConfiguration().getByte(key, defaultValue); } @Override public byte getByte(String key) { return this.getConfiguration().getByte(key); } @Override public double getDouble(String key, double defaultValue) { return this.getConfiguration().getDouble(key, defaultValue); } @Override public Double getDouble(String key, Double defaultValue) { return this.getConfiguration().getDouble(key, defaultValue); } @Override public double getDouble(String key) { return this.getConfiguration().getDouble(key); } @Override public float getFloat(String key, float defaultValue) { return this.getConfiguration().getFloat(key, defaultValue); } @Override public Float getFloat(String key, Float defaultValue) { return this.getConfiguration().getFloat(key, defaultValue); } @Override public float getFloat(String key) { return this.getConfiguration().getFloat(key); } @Override public int getInt(String key, int defaultValue) { return this.getConfiguration().getInt(key, defaultValue); } @Override public int getInt(String key) { return this.getConfiguration().getInt(key); } @Override public Integer getInteger(String key, Integer defaultValue) { return this.getConfiguration().getInteger(key, defaultValue); } @Override public Iterator<String> getKeys() { return this.getConfiguration().getKeys(); } @Override public Iterator<String> getKeys(String prefix) { return this.getConfiguration().getKeys(prefix); } @Override public List<Object> getList(String key, List<?> defaultValue) { return this.getConfiguration().getList(key, defaultValue); } @Override public List<Object> getList(String key) { return this.getConfiguration().getList(key); } @Override public long getLong(String key, long defaultValue) { return this.getConfiguration().getLong(key, defaultValue); } @Override public Long getLong(String key, Long defaultValue) { return this.getConfiguration().getLong(key, defaultValue); } @Override public long getLong(String key) { return this.getConfiguration().getLong(key); } @Override public Properties getProperties(String key) { return this.getConfiguration().getProperties(key); } @Override public Object getProperty(String key) { return this.getConfiguration().getProperty(key); } @Override public short getShort(String key, short defaultValue) { return this.getConfiguration().getShort(key, defaultValue); } @Override public Short getShort(String key, Short defaultValue) { return this.getConfiguration().getShort(key, defaultValue); } @Override public short getShort(String key) { return this.getConfiguration().getShort(key); } @Override public String getString(String key, String defaultValue) { return this.getConfiguration().getString(key, defaultValue); } @Override public String getString(String key) { return this.getConfiguration().getString(key); } @Override public String[] getStringArray(String key) { return this.getConfiguration().getStringArray(key); } @Override public boolean isEmpty() { return this.getConfiguration().isEmpty(); } @Override public void setProperty(String key, Object value) { if (init) { this.getConfiguration().setProperty(key, value); } } @Override public Configuration subset(String prefix) { return this.getConfiguration().subset(prefix); } @Override public Object getReloadLock() { return this.getConfiguration().getReloadLock(); } @Override public Node getRoot() { return this.getConfiguration().getRoot(); } @Override public void setRoot(Node node) { if (init) { this.getConfiguration().setRoot(node); } else { super.setRoot(node); } } @Override public ConfigurationNode getRootNode() { return this.getConfiguration().getRootNode(); } @Override public void setRootNode(ConfigurationNode rootNode) { if (init) { this.getConfiguration().setRootNode(rootNode); } else { super.setRootNode(rootNode); } } @Override public ExpressionEngine getExpressionEngine() { return super.getExpressionEngine(); } @Override public void setExpressionEngine(ExpressionEngine expressionEngine) { super.setExpressionEngine(expressionEngine); } @Override public void addNodes(String key, Collection<? extends ConfigurationNode> nodes) { this.getConfiguration().addNodes(key, nodes); } @Override public SubnodeConfiguration configurationAt(String key, boolean supportUpdates) { return this.getConfiguration().configurationAt(key, supportUpdates); } @Override public SubnodeConfiguration configurationAt(String key) { return this.getConfiguration().configurationAt(key); } @Override public List<HierarchicalConfiguration> configurationsAt(String key) { return this.getConfiguration().configurationsAt(key); } @Override public void clearTree(String key) { this.getConfiguration().clearTree(key); } @Override public int getMaxIndex(String key) { return this.getConfiguration().getMaxIndex(key); } @Override public Configuration interpolatedConfiguration() { return this.getConfiguration().interpolatedConfiguration(); } @Override public void addConfigurationListener(ConfigurationListener l) { super.addConfigurationListener(l); } @Override public boolean removeConfigurationListener(ConfigurationListener l) { return super.removeConfigurationListener(l); } @Override public Collection<ConfigurationListener> getConfigurationListeners() { return super.getConfigurationListeners(); } @Override public void clearConfigurationListeners() { super.clearConfigurationListeners(); } @Override public void addErrorListener(ConfigurationErrorListener l) { super.addErrorListener(l); } @Override public boolean removeErrorListener(ConfigurationErrorListener l) { return super.removeErrorListener(l); } @Override public void clearErrorListeners() { super.clearErrorListeners(); } @Override public Collection<ConfigurationErrorListener> getErrorListeners() { return super.getErrorListeners(); } public void save(Writer writer) throws ConfigurationException { if (init) { this.getConfiguration().save(writer); } } public void load(Reader reader) throws ConfigurationException { if (init) { this.getConfiguration().load(reader); } } @Override public void load() throws ConfigurationException { this.getConfiguration(); } @Override public void load(String fileName) throws ConfigurationException { this.getConfiguration().load(fileName); } @Override public void load(File file) throws ConfigurationException { this.getConfiguration().load(file); } @Override public void load(URL url) throws ConfigurationException { this.getConfiguration().load(url); } @Override public void load(InputStream in) throws ConfigurationException { this.getConfiguration().load(in); } @Override public void load(InputStream in, String encoding) throws ConfigurationException { this.getConfiguration().load(in, encoding); } @Override public void save() throws ConfigurationException { this.getConfiguration().save(); } @Override public void save(String fileName) throws ConfigurationException { this.getConfiguration().save(fileName); } @Override public void save(File file) throws ConfigurationException { this.getConfiguration().save(file); } @Override public void save(URL url) throws ConfigurationException { this.getConfiguration().save(url); } @Override public void save(OutputStream out) throws ConfigurationException { this.getConfiguration().save(out); } @Override public void save(OutputStream out, String encoding) throws ConfigurationException { this.getConfiguration().save(out, encoding); } @Override public void configurationChanged(ConfigurationEvent event) { if (event.getSource() instanceof XMLConfiguration) { for (ConfigurationListener listener : getConfigurationListeners()) { listener.configurationChanged(event); } } } @Override public void configurationError(ConfigurationErrorEvent event) { if (event.getSource() instanceof XMLConfiguration) { for (ConfigurationErrorListener listener : getErrorListeners()) { listener.configurationError(event); } } if (event.getType() == AbstractFileConfiguration.EVENT_RELOAD) { if (isThrowable(event.getCause())) { throw new ConfigurationRuntimeException(event.getCause()); } } } /* * Don't allow resolveContainerStore to be called recursively. * @param key The key to resolve. * @return The value of the key. */ @Override protected Object resolveContainerStore(String key) { if (recursive.get().booleanValue()) { return null; } recursive.set(Boolean.TRUE); try { return super.resolveContainerStore(key); } finally { recursive.set(Boolean.FALSE); } }
Remove the current Configuration.
/** * Remove the current Configuration. */
public void removeConfiguration() { String path = getSubstitutor().replace(pattern); configurationsMap.remove(path); }
First checks to see if the cache exists, if it does, get the associated Configuration. If not it will load a new Configuration and save it in the cache.
Returns:the Configuration associated with the current value of the path pattern.
/** * First checks to see if the cache exists, if it does, get the associated Configuration. * If not it will load a new Configuration and save it in the cache. * * @return the Configuration associated with the current value of the path pattern. */
private AbstractHierarchicalFileConfiguration getConfiguration() { if (pattern == null) { throw new ConfigurationRuntimeException("File pattern must be defined"); } String path = localSubst.replace(pattern); if (configurationsMap.containsKey(path)) { return configurationsMap.get(path); } if (path.equals(pattern)) { XMLConfiguration configuration = new XMLConfiguration() { @Override public void load() throws ConfigurationException { } @Override public void save() throws ConfigurationException { } }; configurationsMap.putIfAbsent(pattern, configuration); return configuration; } XMLConfiguration configuration = new XMLConfiguration(); if (loggerName != null) { Log log = LogFactory.getLog(loggerName); if (log != null) { configuration.setLogger(log); } } configuration.setBasePath(getBasePath()); configuration.setFileName(path); configuration.setFileSystem(getFileSystem()); configuration.setExpressionEngine(getExpressionEngine()); ReloadingStrategy strategy = createReloadingStrategy(); if (strategy != null) { configuration.setReloadingStrategy(strategy); } configuration.setDelimiterParsingDisabled(isDelimiterParsingDisabled()); configuration.setAttributeSplittingDisabled(isAttributeSplittingDisabled()); configuration.setValidating(validating); configuration.setSchemaValidation(schemaValidation); configuration.setEntityResolver(entityResolver); configuration.setListDelimiter(getListDelimiter()); configuration.addConfigurationListener(this); configuration.addErrorListener(this); try { configuration.load(); } catch (ConfigurationException ce) { if (isThrowable(ce)) { throw new ConfigurationRuntimeException(ce); } } configurationsMap.putIfAbsent(path, configuration); return configurationsMap.get(path); } private boolean isThrowable(Throwable throwable) { if (!ignoreException) { return true; } Throwable cause = throwable.getCause(); while (cause != null && !(cause instanceof SAXParseException)) { cause = cause.getCause(); } return cause != null; }
Clone the FileReloadingStrategy since each file needs its own.
Returns:A new FileReloadingStrategy.
/** * Clone the FileReloadingStrategy since each file needs its own. * @return A new FileReloadingStrategy. */
private ReloadingStrategy createReloadingStrategy() { if (fileStrategy == null) { return null; } try { ReloadingStrategy strategy = (ReloadingStrategy) BeanUtils.cloneBean(fileStrategy); strategy.setConfiguration(null); return strategy; } catch (Exception ex) { return null; } } }