Author: oheger Date: Sat Dec 29 20:16:40 2012 New Revision: 1426821 URL: http://svn.apache.org/viewvc?rev=1426821&view=rev Log: CombinedConfigurationBuilder now creates child builders at a later stage. This makes it possible to enable enhanced interpolation in the definition configuration.
Modified: commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/builder/combined/CombinedConfigurationBuilder.java commons/proper/configuration/trunk/src/test/java/org/apache/commons/configuration/builder/combined/TestCombinedConfigurationBuilder.java commons/proper/configuration/trunk/src/test/resources/testInterpolation.properties commons/proper/configuration/trunk/src/test/resources/testInterpolationBuilder.xml Modified: commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/builder/combined/CombinedConfigurationBuilder.java URL: http://svn.apache.org/viewvc/commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/builder/combined/CombinedConfigurationBuilder.java?rev=1426821&r1=1426820&r2=1426821&view=diff ============================================================================== --- commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/builder/combined/CombinedConfigurationBuilder.java (original) +++ commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/builder/combined/CombinedConfigurationBuilder.java Sat Dec 29 20:16:40 2012 @@ -567,20 +567,33 @@ public class CombinedConfigurationBuilde } /** + * <p> * Returns the configuration builder with the given name. With this method a * builder of a child configuration which was given a name in the * configuration definition file can be accessed directly. + * </p> + * <p> + * <strong>Important note:</strong> This method only returns a meaningful + * result after the result configuration has been created by calling + * {@code getConfiguration()}. If called before, always an exception is + * thrown. + * </p> * * @param name the name of the builder in question * @return the child configuration builder with this name - * @throws ConfigurationException if an error occurs setting up the - * definition configuration or no builder with this name exists + * @throws ConfigurationException if information about named builders is not + * yet available or no builder with this name exists */ public synchronized ConfigurationBuilder<? extends Configuration> getNamedBuilder( String name) throws ConfigurationException { + if (sourceData == null) + { + throw new ConfigurationException("Information about child builders" + + " has not been setup yet! Call getConfiguration() first."); + } ConfigurationBuilder<? extends Configuration> builder = - getSourceData().getNamedBuilder(name); + sourceData.getNamedBuilder(name); if (builder == null) { throw new ConfigurationException("Builder cannot be resolved: " @@ -590,21 +603,33 @@ public class CombinedConfigurationBuilde } /** + * <p> * Returns a set with the names of all child configuration builders. A tag * defining a configuration source in the configuration definition file can * have the {@code config-name} attribute. If this attribute is present, the * corresponding builder is assigned this name and can be directly accessed * through the {@link #getNamedBuilder(String)} method. This method returns * a collection with all available builder names. + * </p> + * <p> + * <strong>Important note:</strong> This method only returns a meaningful + * result after the result configuration has been created by calling + * {@code getConfiguration()}. If called before, always an empty set is + * returned. + * </p> * - * @return a collection with the names of all builders - * @throws ConfigurationException if an error occurs setting up the - * definition configuration + * @return a set with the names of all builders */ public synchronized Set<String> builderNames() - throws ConfigurationException { - return Collections.unmodifiableSet(getSourceData().builderNames()); + if (sourceData == null) + { + return Collections.emptySet(); + } + else + { + return Collections.unmodifiableSet(sourceData.builderNames()); + } } /** @@ -749,13 +774,13 @@ public class CombinedConfigurationBuilde setUpParentInterpolator(currentConfiguration, config); ConfigurationSourceData data = getSourceData(); - createAndAddConfigurations(result, data.getOverrideBuilders(), data); - if (!data.getUnionBuilders().isEmpty()) + data.createAndAddConfigurations(result, data.getOverrideSources()); + if (!data.getUnionSources().isEmpty()) { CombinedConfiguration addConfig = createAdditionalsConfiguration(result); result.addConfiguration(addConfig, ADDITIONAL_NAME); initNodeCombinerListNodes(addConfig, config, KEY_ADDITIONAL_LIST); - createAndAddConfigurations(addConfig, data.getUnionBuilders(), data); + data.createAndAddConfigurations(addConfig, data.getUnionSources()); } currentConfiguration = null; } @@ -1281,40 +1306,6 @@ public class CombinedConfigurationBuilde } /** - * Queries the current {@code Configuration} objects from the given builders - * and adds them to the specified combined configuration. - * - * @param cc the resulting combined configuration - * @param builders the collection with configuration builders - * @param srcData the data object for configuration sources - * @throws ConfigurationException if an error occurs - */ - private static void createAndAddConfigurations(CombinedConfiguration cc, - Collection<ConfigurationBuilder<? extends Configuration>> builders, - ConfigurationSourceData srcData) throws ConfigurationException - { - for (ConfigurationBuilder<? extends Configuration> builder : builders) - { - ConfigurationDeclaration decl = srcData.getDeclaration(builder); - assert decl != null : "Cannot resolve builder!"; - try - { - cc.addConfiguration( - (AbstractConfiguration) builder.getConfiguration(), - decl.getName(), decl.getAt()); - } - catch (ConfigurationException cex) - { - // ignore exceptions for optional configurations - if (!decl.isOptional()) - { - throw cex; - } - } - } - } - - /** * Creates the map with the default configuration builder providers. * * @return the map with default providers @@ -1342,23 +1333,14 @@ public class CombinedConfigurationBuilde private class ConfigurationSourceData { /** A list with all builders for override configurations. */ - private final Collection<ConfigurationBuilder<? extends Configuration>> overrideBuilders; + private final Collection<SubnodeConfiguration> overrideBuilders; /** A list with all builders for union configurations. */ - private final Collection<ConfigurationBuilder<? extends Configuration>> unionBuilders; - - /** A list with all sub builders (override plus union). */ - private final Collection<ConfigurationBuilder<? extends Configuration>> allBuilders; + private final Collection<SubnodeConfiguration> unionBuilders; /** A map for direct access to a builder by its name. */ private final Map<String, ConfigurationBuilder<? extends Configuration>> namedBuilders; - /** - * A map for retrieving the bean declarations associated with - * configuration builders. - */ - private final Map<ConfigurationBuilder<? extends Configuration>, ConfigurationDeclaration> declarations; - /** A listener for reacting on changes of sub builders. */ private BuilderListener changeListener; @@ -1368,15 +1350,11 @@ public class CombinedConfigurationBuilde public ConfigurationSourceData() { overrideBuilders = - new LinkedList<ConfigurationBuilder<? extends Configuration>>(); + new LinkedList<SubnodeConfiguration>(); unionBuilders = - new LinkedList<ConfigurationBuilder<? extends Configuration>>(); - allBuilders = - new LinkedList<ConfigurationBuilder<? extends Configuration>>(); + new LinkedList<SubnodeConfiguration>(); namedBuilders = new HashMap<String, ConfigurationBuilder<? extends Configuration>>(); - declarations = - new HashMap<ConfigurationBuilder<? extends Configuration>, ConfigurationDeclaration>(); } /** @@ -1388,16 +1366,35 @@ public class CombinedConfigurationBuilde public void initFromDefinitionConfiguration( HierarchicalConfiguration config) throws ConfigurationException { - createBuilders(overrideBuilders, - fetchTopLevelOverrideConfigs(config)); - createBuilders(overrideBuilders, - config.childConfigurationsAt(KEY_OVERRIDE)); - createBuilders(unionBuilders, - config.childConfigurationsAt(KEY_UNION)); - - allBuilders.addAll(overrideBuilders); - allBuilders.addAll(unionBuilders); - registerChangeListener(); + overrideBuilders.addAll(fetchTopLevelOverrideConfigs(config)); + overrideBuilders.addAll(config.childConfigurationsAt(KEY_OVERRIDE)); + unionBuilders.addAll(config.childConfigurationsAt(KEY_UNION)); + } + + /** + * Processes the declaration of configuration builder providers, creates + * the corresponding builder, obtains configurations, and adds them to + * the specified result configuration. + * + * @param ccResult the result configuration + * @param srcDecl the collection with the declarations of configuration + * sources to process + * @throws ConfigurationException if an error occurs + */ + public void createAndAddConfigurations(CombinedConfiguration ccResult, + Collection<SubnodeConfiguration> srcDecl) + throws ConfigurationException + { + createBuilderChangeListener(); + for (HierarchicalConfiguration src : srcDecl) + { + ConfigurationDeclaration decl = + new ConfigurationDeclaration( + CombinedConfigurationBuilder.this, src); + ConfigurationBuilder<? extends Configuration> builder = + createConfigurationBuilder(src, decl); + addChildConfiguration(ccResult, decl, builder); + } } /** @@ -1406,30 +1403,31 @@ public class CombinedConfigurationBuilde */ public void cleanUp() { - for (ConfigurationBuilder<?> b : allBuilders) + for (ConfigurationBuilder<?> b : namedBuilders.values()) { b.removeBuilderListener(changeListener); } + namedBuilders.clear(); } /** - * Returns a collection with all configuration builders defined in the - * override section. + * Returns a collection with all configuration source declarations + * defined in the override section. * * @return the override configuration builders */ - public Collection<ConfigurationBuilder<? extends Configuration>> getOverrideBuilders() + public Collection<SubnodeConfiguration> getOverrideSources() { return overrideBuilders; } /** - * Returns a collection with all configuration builders defined in the - * union section. + * Returns a collection with all configuration source declarations + * defined in the union section. * * @return the union configuration builders */ - public Collection<ConfigurationBuilder<? extends Configuration>> getUnionBuilders() + public Collection<SubnodeConfiguration> getUnionSources() { return unionBuilders; } @@ -1459,25 +1457,72 @@ public class CombinedConfigurationBuilde } /** - * Returns the {@code ConfigurationDeclaration} associated with the - * specified builder. If the builder is unknown, result is <b>null</b>. + * Creates a configuration builder based on a source declaration in the + * definition configuration. * - * @param builder the builder in question - * @return the {@code ConfigurationDeclaration} for this builder or - * <b>null</b> + * @param src the sub configuration for the current configuration source + * @param decl the current {@code ConfigurationDeclaration} + * @return the newly created bulder + * @throws ConfigurationException if an error occurs */ - public ConfigurationDeclaration getDeclaration( - ConfigurationBuilder<?> builder) + private ConfigurationBuilder<? extends Configuration> createConfigurationBuilder( + HierarchicalConfiguration src, ConfigurationDeclaration decl) + throws ConfigurationException { - return declarations.get(builder); + ConfigurationBuilderProvider provider = + providerForTag(src.getRootElementName()); + if (provider == null) + { + throw new ConfigurationException( + "Unsupported configuration source: " + + src.getRootElementName()); + } + + ConfigurationBuilder<? extends Configuration> builder = + provider.getConfigurationBuilder(decl); + if (decl.getName() != null) + { + namedBuilders.put(decl.getName(), builder); + } + builder.addBuilderListener(changeListener); + return builder; + } + + /** + * Creates a new configuration using the specified builder and adds it + * to the resulting combined configuration. + * + * @param ccResult the resulting combined configuration + * @param decl the current {@code ConfigurationDeclaration} + * @param builder the configuration builder + * @throws ConfigurationException if an error occurs + */ + private void addChildConfiguration(CombinedConfiguration ccResult, + ConfigurationDeclaration decl, + ConfigurationBuilder<? extends Configuration> builder) + throws ConfigurationException + { + try + { + ccResult.addConfiguration( + (AbstractConfiguration) builder.getConfiguration(), + decl.getName(), decl.getAt()); + } + catch (ConfigurationException cex) + { + // ignore exceptions for optional configurations + if (!decl.isOptional()) + { + throw cex; + } + } } /** - * Registers a change listener at all sub builders. Whenever one of the - * sub builders is reset, the combined configuration managed by this - * builder has to be reset, too. + * Creates a listener for builder change events. This listener is + * registered at all builders for child configurations. */ - private void registerChangeListener() + private void createBuilderChangeListener() { changeListener = new BuilderListener() { @@ -1487,11 +1532,6 @@ public class CombinedConfigurationBuilde resetResult(); } }; - - for (ConfigurationBuilder<?> b : allBuilders) - { - b.addBuilderListener(changeListener); - } } /** @@ -1525,43 +1565,5 @@ public class CombinedConfigurationBuilde } return configs; } - - /** - * Creates configuration builder objects from the given configurations - * for configuration sources. - * - * @param builders the collection with builders to be filled - * @param sources the definitions for the single configuration sources - * @throws ConfigurationException if an error occurs - */ - private void createBuilders( - Collection<ConfigurationBuilder<? extends Configuration>> builders, - Collection<? extends HierarchicalConfiguration> sources) - throws ConfigurationException - { - for (HierarchicalConfiguration src : sources) - { - ConfigurationBuilderProvider provider = - providerForTag(src.getRootElementName()); - if (provider == null) - { - throw new ConfigurationException( - "Unsupported configuration source: " - + src.getRootElementName()); - } - - ConfigurationDeclaration decl = - new ConfigurationDeclaration( - CombinedConfigurationBuilder.this, src); - ConfigurationBuilder<? extends Configuration> builder = - provider.getConfigurationBuilder(decl); - builders.add(builder); - declarations.put(builder, decl); - if (decl.getName() != null) - { - namedBuilders.put(decl.getName(), builder); - } - } - } } } Modified: commons/proper/configuration/trunk/src/test/java/org/apache/commons/configuration/builder/combined/TestCombinedConfigurationBuilder.java URL: http://svn.apache.org/viewvc/commons/proper/configuration/trunk/src/test/java/org/apache/commons/configuration/builder/combined/TestCombinedConfigurationBuilder.java?rev=1426821&r1=1426820&r2=1426821&view=diff ============================================================================== --- commons/proper/configuration/trunk/src/test/java/org/apache/commons/configuration/builder/combined/TestCombinedConfigurationBuilder.java (original) +++ commons/proper/configuration/trunk/src/test/java/org/apache/commons/configuration/builder/combined/TestCombinedConfigurationBuilder.java Sat Dec 29 20:16:40 2012 @@ -299,6 +299,19 @@ public class TestCombinedConfigurationBu } /** + * Tests the behavior of builderNames() before the result configuration has + * been created. + */ + @Test + public void testBuilderNamesBeforeConfigurationAccess() + { + assertTrue("Got builders (1)", factory.builderNames().isEmpty()); + factory.configure(new FileBasedBuilderParametersImpl() + .setFile(TEST_FILE)); + assertTrue("Got builders (2)", factory.builderNames().isEmpty()); + } + + /** * Tests whether the names of sub builders can be queried. */ @Test @@ -306,6 +319,7 @@ public class TestCombinedConfigurationBu { factory.configure(new FileBasedBuilderParametersImpl() .setFile(TEST_FILE)); + factory.getConfiguration(); Set<String> names = factory.builderNames(); List<String> expected = Arrays.asList("props", "xml"); assertEquals("Wrong number of named builders", expected.size(), @@ -321,8 +335,9 @@ public class TestCombinedConfigurationBu { factory.configure(new FileBasedBuilderParametersImpl() .setFile(TEST_FILE)); + factory.getConfiguration(); Set<String> names = factory.builderNames(); - names.clear(); + names.add(BUILDER_NAME); } /** @@ -333,6 +348,7 @@ public class TestCombinedConfigurationBu { factory.configure(new FileBasedBuilderParametersImpl() .setFile(TEST_FILE)); + factory.getConfiguration(); ConfigurationBuilder<? extends Configuration> propBuilder = factory.getNamedBuilder("props"); assertTrue("Wrong builder class", @@ -350,6 +366,20 @@ public class TestCombinedConfigurationBu { factory.configure(new FileBasedBuilderParametersImpl() .setFile(TEST_FILE)); + factory.getConfiguration(); + factory.getNamedBuilder("nonExistingBuilder"); + } + + /** + * Tries to query a named builder before the result configuration has been + * created. + */ + @Test(expected = ConfigurationException.class) + public void testGetNamedBuilderBeforeConfigurationAccess() + throws ConfigurationException + { + factory.configure(new FileBasedBuilderParametersImpl() + .setFile(TEST_FILE)); factory.getNamedBuilder("nonExistingBuilder"); } @@ -406,6 +436,7 @@ public class TestCombinedConfigurationBu Map<String, Object> attrs = new HashMap<String, Object>(); attrs.put("config-reload", Boolean.TRUE); prepareSubBuilderTest(attrs); + factory.getConfiguration(); assertTrue( "Not a reloading builder", factory.getNamedBuilder(BUILDER_NAME) instanceof ReloadingFileBasedConfigurationBuilder); @@ -438,6 +469,7 @@ public class TestCombinedConfigurationBu { Map<String, Object> attrs = new HashMap<String, Object>(); prepareSubBuilderTest(attrs); + factory.getConfiguration(); BasicConfigurationBuilder<?> subBuilder = (BasicConfigurationBuilder<?>) factory .getNamedBuilder(BUILDER_NAME); Modified: commons/proper/configuration/trunk/src/test/resources/testInterpolation.properties URL: http://svn.apache.org/viewvc/commons/proper/configuration/trunk/src/test/resources/testInterpolation.properties?rev=1426821&r1=1426820&r2=1426821&view=diff ============================================================================== --- commons/proper/configuration/trunk/src/test/resources/testInterpolation.properties (original) +++ commons/proper/configuration/trunk/src/test/resources/testInterpolation.properties Sat Dec 29 20:16:40 2012 @@ -17,6 +17,7 @@ # configuration sources created by a DefaultConfigurationBuilder. # This properties configuration defines a property which is referenced by # another configuration source. -# $Id:$ +# $Id$ myvar=abc +testXmlFile=testInterpolation.xml Modified: commons/proper/configuration/trunk/src/test/resources/testInterpolationBuilder.xml URL: http://svn.apache.org/viewvc/commons/proper/configuration/trunk/src/test/resources/testInterpolationBuilder.xml?rev=1426821&r1=1426820&r2=1426821&view=diff ============================================================================== --- commons/proper/configuration/trunk/src/test/resources/testInterpolationBuilder.xml (original) +++ commons/proper/configuration/trunk/src/test/resources/testInterpolationBuilder.xml Sat Dec 29 20:16:40 2012 @@ -20,12 +20,12 @@ configuration sources created by a DefaultConfigurationBuilder. This configuration definition file for a DefaultConfigurationBuilder references multiple sources which contain variable references. - $Id:$ + $Id$ --> <configuration> <system/> <properties fileName="testInterpolation.properties"/> - <xml fileName="testInterpolation.xml" config-name="test"> + <xml fileName="${testXmlFile}" config-name="test"> <expressionEngine config-class="org.apache.commons.configuration.tree.xpath.XPathExpressionEngine"/> </xml> </configuration>