This is an automated email from the ASF dual-hosted git repository. ggregory pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/commons-configuration.git
The following commit(s) were added to refs/heads/master by this push: new 8b31196 [CONFIGURATION-756] Allow for custom behavior to handle errors loading included properties files. (#34) 8b31196 is described below commit 8b311965eb0c04508461a3e14169f79a1c065678 Author: Gary Gregory <garydgreg...@users.noreply.github.com> AuthorDate: Thu Sep 12 10:52:13 2019 -0400 [CONFIGURATION-756] Allow for custom behavior to handle errors loading included properties files. (#34) [CONFIGURATION-756] Allow for custom behavior to handle errors loading included properties files. (#34) --- .../configuration2/ConfigurationConsumer.java | 39 +++++++ .../configuration2/PropertiesConfiguration.java | 112 +++++++++++++++++---- .../builder/PropertiesBuilderParametersImpl.java | 15 ++- .../builder/PropertiesBuilderProperties.java | 14 +++ .../builder/fluent/Configurations.java | 44 +++++++- .../TestPropertiesConfiguration.java | 82 ++++++++++++++- .../TestPropertiesBuilderParametersImpl.java | 43 +++++++- .../combined/TestCombinedConfigurationBuilder.java | 2 +- .../builder/fluent/TestConfigurations.java | 105 ++++++++++++++++--- .../builder/fluent/TestParameters.java | 17 +++- .../include-cyclical-reference.properties | 16 +++ .../resources/include-include-not-found.properties | 16 +++ .../resources/include-load-exception.properties | 16 +++ src/test/resources/include-not-found.properties | 16 +++ 14 files changed, 489 insertions(+), 48 deletions(-) diff --git a/src/main/java/org/apache/commons/configuration2/ConfigurationConsumer.java b/src/main/java/org/apache/commons/configuration2/ConfigurationConsumer.java new file mode 100644 index 0000000..3e96418 --- /dev/null +++ b/src/main/java/org/apache/commons/configuration2/ConfigurationConsumer.java @@ -0,0 +1,39 @@ +/* + * 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 org.apache.commons.configuration2.ex.ConfigurationException; + +/** + * A Configuration task that may throw a ConfigurationException. + * + * @param <T> the type of the input to the operation. + * @since 2.6 + */ +@FunctionalInterface +public interface ConfigurationConsumer<T> +{ + + /** + * Performs this operation on the given argument. + * + * @param t the input argument + * @throws ConfigurationException May be thrown while performing a Configuration operation. + */ + void accept(T t) throws ConfigurationException; +} diff --git a/src/main/java/org/apache/commons/configuration2/PropertiesConfiguration.java b/src/main/java/org/apache/commons/configuration2/PropertiesConfiguration.java index 3b86bbe..438bba8 100644 --- a/src/main/java/org/apache/commons/configuration2/PropertiesConfiguration.java +++ b/src/main/java/org/apache/commons/configuration2/PropertiesConfiguration.java @@ -17,6 +17,7 @@ package org.apache.commons.configuration2; +import java.io.FileNotFoundException; import java.io.FilterWriter; import java.io.IOException; import java.io.LineNumberReader; @@ -137,6 +138,10 @@ import org.apache.commons.text.translate.UnicodeEscaper; * they do not replace existing properties with the same key. * * </li> + * <li> + * You can define custom error handling for the special key {@code "include"} + * by using {@link #setIncludeListener(ConfigurationConsumer)}. + * </li> * </ul> * * <p>Here is an example of a valid extended properties file:</p> @@ -194,11 +199,31 @@ import org.apache.commons.text.translate.UnicodeEscaper; * Properties files</a> in special. * * @see java.util.Properties#load - * */ public class PropertiesConfiguration extends BaseConfiguration implements FileBasedConfiguration, FileLocatorAware { + + /** + * Defines default error handling for the special {@code "include"} key by throwing the given exception. + * + * @since 2.6 + */ + public static final ConfigurationConsumer<ConfigurationException> DEFAULT_INCLUDE_LISTENER = e -> + { + throw e; + }; + + /** + * Defines error handling as a noop for the special {@code "include"} key. + * + * @since 2.6 + */ + public static final ConfigurationConsumer<ConfigurationException> NOOP_INCLUDE_LISTENER = e -> + { + // noop + }; + /** * The default encoding (ISO-8859-1 as specified by * http://java.sun.com/j2se/1.5.0/docs/api/java/util/Properties.html) @@ -212,12 +237,6 @@ public class PropertiesConfiguration extends BaseConfiguration static final String DEFAULT_SEPARATOR = " = "; /** - * Constant for the default {@code IOFactory}. This instance is used - * when no specific factory was set. - */ - private static final IOFactory DEFAULT_IO_FACTORY = new DefaultIOFactory(); - - /** * A string with special characters that need to be unescaped when reading * a properties file. {@code java.util.Properties} escapes these characters * when writing out a properties file. @@ -257,6 +276,9 @@ public class PropertiesConfiguration extends BaseConfiguration /** Stores the layout object.*/ private PropertiesConfigurationLayout layout; + /** The include listener for the special {@code "include"} key. */ + private ConfigurationConsumer<ConfigurationException> includeListener; + /** The IOFactory for creating readers and writers.*/ private IOFactory ioFactory; @@ -488,6 +510,17 @@ public class PropertiesConfiguration extends BaseConfiguration } /** + * Gets the current include listener, never null. + * + * @return the current include listener, never null. + * @since 2.6 + */ + public ConfigurationConsumer<ConfigurationException> getIncludeListener() + { + return includeListener != null ? includeListener : PropertiesConfiguration.DEFAULT_INCLUDE_LISTENER; + } + + /** * Returns the {@code IOFactory} to be used for creating readers and * writers when loading or saving this configuration. * @@ -496,7 +529,23 @@ public class PropertiesConfiguration extends BaseConfiguration */ public IOFactory getIOFactory() { - return (ioFactory != null) ? ioFactory : DEFAULT_IO_FACTORY; + return ioFactory != null ? ioFactory : DefaultIOFactory.INSTANCE; + } + + /** + * Sets the current include listener, may not be null. + * + * @param includeListener the current include listener, may not be null. + * @throws IllegalArgumentException if the {@code includeListener} is null. + * @since 2.6 + */ + public void setIncludeListener(final ConfigurationConsumer<ConfigurationException> includeListener) + { + if (includeListener == null) + { + throw new IllegalArgumentException("includeListener must not be null."); + } + this.includeListener = includeListener; } /** @@ -518,7 +567,7 @@ public class PropertiesConfiguration extends BaseConfiguration { if (ioFactory == null) { - throw new IllegalArgumentException("IOFactory must not be null!"); + throw new IllegalArgumentException("IOFactory must not be null."); } this.ioFactory = ioFactory; @@ -1377,6 +1426,11 @@ public class PropertiesConfiguration extends BaseConfiguration */ public static class DefaultIOFactory implements IOFactory { + /** + * The singleton instance. + */ + static final DefaultIOFactory INSTANCE = new DefaultIOFactory(); + @Override public PropertiesReader createPropertiesReader(final Reader in) { @@ -1827,20 +1881,35 @@ public class PropertiesConfiguration extends BaseConfiguration if (url == null) { - throw new ConfigurationException("Cannot resolve include file " - + fileName); - } - - final FileHandler fh = new FileHandler(this); - fh.setFileLocator(locator); - final FileLocator orgLocator = locator; - try - { - fh.load(url); + if (getIncludeListener() != null) + { + getIncludeListener().accept(new ConfigurationException( + "Cannot resolve include file " + fileName, new FileNotFoundException(fileName))); + } } - finally + else { - locator = orgLocator; // reset locator which is changed by load + final FileHandler fh = new FileHandler(this); + fh.setFileLocator(locator); + final FileLocator orgLocator = locator; + try + { + try + { + fh.load(url); + } + catch (ConfigurationException e) + { + if (getIncludeListener() != null) + { + getIncludeListener().accept(e); + } + } + } + finally + { + locator = orgLocator; // reset locator which is changed by load + } } } @@ -1860,4 +1929,5 @@ public class PropertiesConfiguration extends BaseConfiguration .basePath(basePath).fileName(fileName).create(); return FileLocatorUtils.locate(includeLocator); } + } diff --git a/src/main/java/org/apache/commons/configuration2/builder/PropertiesBuilderParametersImpl.java b/src/main/java/org/apache/commons/configuration2/builder/PropertiesBuilderParametersImpl.java index a908ac0..39965a7 100644 --- a/src/main/java/org/apache/commons/configuration2/builder/PropertiesBuilderParametersImpl.java +++ b/src/main/java/org/apache/commons/configuration2/builder/PropertiesBuilderParametersImpl.java @@ -18,8 +18,10 @@ package org.apache.commons.configuration2.builder; import java.util.Map; +import org.apache.commons.configuration2.ConfigurationConsumer; import org.apache.commons.configuration2.PropertiesConfiguration.IOFactory; import org.apache.commons.configuration2.PropertiesConfigurationLayout; +import org.apache.commons.configuration2.ex.ConfigurationException; /** * <p> @@ -44,6 +46,9 @@ public class PropertiesBuilderParametersImpl extends FileBasedBuilderParametersImpl implements PropertiesBuilderProperties<PropertiesBuilderParametersImpl> { + /** The key for the include listener property. */ + private static final String PROP_INCLUDE_LISTENER = "includeListener"; + /** The key for the includes allowed property. */ private static final String PROP_INCLUDES_ALLOWED = "includesAllowed"; @@ -54,6 +59,14 @@ public class PropertiesBuilderParametersImpl extends private static final String PROP_IO_FACTORY = "IOFactory"; @Override + public PropertiesBuilderParametersImpl setIncludeListener( + final ConfigurationConsumer<ConfigurationException> includeListener) + { + storeProperty(PROP_INCLUDE_LISTENER, includeListener); + return this; + } + + @Override public PropertiesBuilderParametersImpl setIncludesAllowed(final boolean f) { storeProperty(PROP_INCLUDES_ALLOWED, Boolean.valueOf(f)); @@ -68,7 +81,7 @@ public class PropertiesBuilderParametersImpl extends public void inheritFrom(final Map<String, ?> source) { super.inheritFrom(source); - copyPropertiesFrom(source, PROP_INCLUDES_ALLOWED, PROP_IO_FACTORY); + copyPropertiesFrom(source, PROP_INCLUDES_ALLOWED, PROP_INCLUDE_LISTENER, PROP_IO_FACTORY); } @Override diff --git a/src/main/java/org/apache/commons/configuration2/builder/PropertiesBuilderProperties.java b/src/main/java/org/apache/commons/configuration2/builder/PropertiesBuilderProperties.java index 939339b..62040ed 100644 --- a/src/main/java/org/apache/commons/configuration2/builder/PropertiesBuilderProperties.java +++ b/src/main/java/org/apache/commons/configuration2/builder/PropertiesBuilderProperties.java @@ -16,8 +16,10 @@ */ package org.apache.commons.configuration2.builder; +import org.apache.commons.configuration2.ConfigurationConsumer; import org.apache.commons.configuration2.PropertiesConfiguration.IOFactory; import org.apache.commons.configuration2.PropertiesConfigurationLayout; +import org.apache.commons.configuration2.ex.ConfigurationException; /** * <p> @@ -39,6 +41,18 @@ import org.apache.commons.configuration2.PropertiesConfigurationLayout; public interface PropertiesBuilderProperties<T> { /** + * Sets the current include listener, may be null. + * + * @param includeListener the current include listener, may be null. + * @return a reference to this object for method chaining + * @since 2.6 + */ + default T setIncludeListener(ConfigurationConsumer<ConfigurationException> includeListener) + { + return (T) this; + } + + /** * Sets a flag whether include files are supported by the properties * configuration object. If set to <b>true</b>, files listed by an include * property are loaded automatically. diff --git a/src/main/java/org/apache/commons/configuration2/builder/fluent/Configurations.java b/src/main/java/org/apache/commons/configuration2/builder/fluent/Configurations.java index 55f6ba5..5c07d71 100644 --- a/src/main/java/org/apache/commons/configuration2/builder/fluent/Configurations.java +++ b/src/main/java/org/apache/commons/configuration2/builder/fluent/Configurations.java @@ -225,6 +225,17 @@ public class Configurations } /** + * Creates a builder for a {@code PropertiesConfiguration}. + * + * @return the newly created {@code FileBasedConfigurationBuilder} + * @since 2.6 + */ + public FileBasedConfigurationBuilder<PropertiesConfiguration> propertiesBuilder() + { + return createFileBasedBuilder(PropertiesConfiguration.class); + } + + /** * Creates a builder for a {@code PropertiesConfiguration} and initializes * it with the given file to be loaded. * @@ -239,6 +250,20 @@ public class Configurations /** * Creates a builder for a {@code PropertiesConfiguration} and initializes + * it with the given parameters to be loaded. + * + * @param parameters the parameters to be loaded + * @return the newly created {@code FileBasedConfigurationBuilder} + * @since 2.6 + */ + public FileBasedConfigurationBuilder<PropertiesConfiguration> propertiesBuilder( + PropertiesBuilderParameters parameters) + { + return propertiesBuilder().configure(parameters); + } + + /** + * Creates a builder for a {@code PropertiesConfiguration} and initializes * it with the given URL to be loaded. * * @param url the URL to be loaded @@ -579,6 +604,21 @@ public class Configurations * specified type. * * @param configClass the configuration class + * @param <T> the type of the configuration to be constructed + * @return the newly created builder + * @since 2.6 + */ + private <T extends FileBasedConfiguration> FileBasedConfigurationBuilder<T> createFileBasedBuilder( + final Class<T> configClass) + { + return new FileBasedConfigurationBuilder<>(configClass); + } + + /** + * Creates a configured builder for a file-based configuration of the + * specified type. + * + * @param configClass the configuration class * @param params the parameters object for configuring the builder * @param <T> the type of the configuration to be constructed * @return the newly created builder @@ -586,8 +626,7 @@ public class Configurations private <T extends FileBasedConfiguration> FileBasedConfigurationBuilder<T> createFileBasedBuilder( final Class<T> configClass, final FileBasedBuilderParameters params) { - return new FileBasedConfigurationBuilder<>(configClass) - .configure(params); + return createFileBasedBuilder(configClass).configure(params); } /** @@ -636,4 +675,5 @@ public class Configurations { return fileParams().setFileName(path); } + } diff --git a/src/test/java/org/apache/commons/configuration2/TestPropertiesConfiguration.java b/src/test/java/org/apache/commons/configuration2/TestPropertiesConfiguration.java index dd63ff5..bd1e532 100644 --- a/src/test/java/org/apache/commons/configuration2/TestPropertiesConfiguration.java +++ b/src/test/java/org/apache/commons/configuration2/TestPropertiesConfiguration.java @@ -17,7 +17,31 @@ package org.apache.commons.configuration2; -import java.io.*; +import static org.hamcrest.CoreMatchers.containsString; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNotSame; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import java.io.BufferedReader; +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.Reader; +import java.io.StringReader; +import java.io.StringWriter; +import java.io.UnsupportedEncodingException; +import java.io.Writer; import java.net.HttpURLConnection; import java.net.URL; import java.net.URLConnection; @@ -50,13 +74,11 @@ import org.apache.commons.configuration2.io.FileHandler; import org.apache.commons.configuration2.io.FileSystem; import org.apache.commons.lang3.mutable.MutableObject; import org.junit.Before; -import org.junit.Test; +import org.junit.Ignore; import org.junit.Rule; +import org.junit.Test; import org.junit.rules.TemporaryFolder; -import static org.hamcrest.CoreMatchers.containsString; -import static org.junit.Assert.*; - /** * Test for loading and saving properties files. * @@ -1088,6 +1110,56 @@ public class TestPropertiesConfiguration } @Test + public void testIncludeLoadAllOnNotFound() throws Exception + { + final PropertiesConfiguration pc = new PropertiesConfiguration(); + pc.setIncludeListener(PropertiesConfiguration.NOOP_INCLUDE_LISTENER); + final FileHandler handler = new FileHandler(pc); + handler.setBasePath(testBasePath); + handler.setFileName("include-not-found.properties"); + handler.load(); + assertEquals("valueA", pc.getString("keyA")); + } + + @Test + public void testIncludeIncludeLoadAllOnNotFound() throws Exception + { + final PropertiesConfiguration pc = new PropertiesConfiguration(); + pc.setIncludeListener(PropertiesConfiguration.NOOP_INCLUDE_LISTENER); + final FileHandler handler = new FileHandler(pc); + handler.setBasePath(testBasePath); + handler.setFileName("include-include-not-found.properties"); + handler.load(); + assertEquals("valueA", pc.getString("keyA")); + assertEquals("valueB", pc.getString("keyB")); + } + + @Test + public void testIncludeLoadAllOnLoadException() throws Exception + { + final PropertiesConfiguration pc = new PropertiesConfiguration(); + pc.setIncludeListener(PropertiesConfiguration.NOOP_INCLUDE_LISTENER); + final FileHandler handler = new FileHandler(pc); + handler.setBasePath(testBasePath); + handler.setFileName("include-load-exception.properties"); + handler.load(); + assertEquals("valueA", pc.getString("keyA")); + } + + @Test + @Ignore("PropertiesConfiguration does NOT detect cyclical references.") + public void testIncludeLoadAllCycliclaReference() throws Exception + { + final PropertiesConfiguration pc = new PropertiesConfiguration(); + pc.setIncludeListener(PropertiesConfiguration.NOOP_INCLUDE_LISTENER); + final FileHandler handler = new FileHandler(pc); + handler.setBasePath(testBasePath); + handler.setFileName("include-cyclical-reference.properties"); + handler.load(); + assertEquals("valueA", pc.getString("keyA")); + } + + @Test public void testLoadViaPropertyWithBasePath2() throws Exception { final PropertiesConfiguration pc = new PropertiesConfiguration(); diff --git a/src/test/java/org/apache/commons/configuration2/builder/TestPropertiesBuilderParametersImpl.java b/src/test/java/org/apache/commons/configuration2/builder/TestPropertiesBuilderParametersImpl.java index 4606338..5815003 100644 --- a/src/test/java/org/apache/commons/configuration2/builder/TestPropertiesBuilderParametersImpl.java +++ b/src/test/java/org/apache/commons/configuration2/builder/TestPropertiesBuilderParametersImpl.java @@ -22,6 +22,7 @@ import static org.junit.Assert.assertSame; import java.util.Map; +import org.apache.commons.configuration2.ConfigurationConsumer; import org.apache.commons.configuration2.PropertiesConfiguration; import org.apache.commons.configuration2.PropertiesConfigurationLayout; import org.apache.commons.configuration2.beanutils.BeanHelper; @@ -70,6 +71,20 @@ public class TestPropertiesBuilderParametersImpl } /** + * Tests whether the include listener can be set. + */ + @Test + public void testSetIncludeListener() + { + final ConfigurationConsumer<ConfigurationException> includeListener = + EasyMock.createMock(ConfigurationConsumer.class); + EasyMock.replay(includeListener); + assertSame("Wrong result", params, params.setIncludeListener(includeListener)); + assertSame("IncludeListener not set", includeListener, + params.getParameters().get("includeListener")); + } + + /** * Tests whether the IO factory can be set. */ @Test @@ -113,9 +128,13 @@ public class TestPropertiesBuilderParametersImpl { final PropertiesConfiguration.IOFactory factory = EasyMock.createMock(PropertiesConfiguration.IOFactory.class); - params.setIOFactory(factory).setIncludesAllowed(false) - .setLayout(new PropertiesConfigurationLayout()); - params.setThrowExceptionOnMissing(true); + final ConfigurationConsumer<ConfigurationException> includeListener = + EasyMock.createMock(ConfigurationConsumer.class); + params.setIOFactory(factory) + .setIncludeListener(includeListener) + .setIncludesAllowed(false) + .setLayout(new PropertiesConfigurationLayout()) + .setThrowExceptionOnMissing(true); final PropertiesBuilderParametersImpl params2 = new PropertiesBuilderParametersImpl(); @@ -123,6 +142,7 @@ public class TestPropertiesBuilderParametersImpl final Map<String, Object> parameters = params2.getParameters(); assertEquals("Exception flag not set", Boolean.TRUE, parameters.get("throwExceptionOnMissing")); + assertEquals("IncludeListener not set", includeListener, parameters.get("includeListener")); assertEquals("IOFactory not set", factory, parameters.get("IOFactory")); assertEquals("Include flag not set", Boolean.FALSE, parameters.get("includesAllowed")); @@ -146,4 +166,21 @@ public class TestPropertiesBuilderParametersImpl final PropertiesConfiguration config = builder.getConfiguration(); assertEquals("Wrong IO factory", factory, config.getIOFactory()); } + + /** + * Tests whether the IncludeListener property can be correctly set. + */ + @Test + public void testSetIncludeListenerProperty() throws ConfigurationException + { + final ConfigurationConsumer<ConfigurationException> includeListener = + PropertiesConfiguration.DEFAULT_INCLUDE_LISTENER; + final ConfigurationBuilder<PropertiesConfiguration> builder = + new FileBasedConfigurationBuilder<>( + PropertiesConfiguration.class) + .configure(params.setIncludeListener(includeListener)); + + final PropertiesConfiguration config = builder.getConfiguration(); + assertEquals("Wrong IncludeListener", includeListener, config.getIncludeListener()); + } } diff --git a/src/test/java/org/apache/commons/configuration2/builder/combined/TestCombinedConfigurationBuilder.java b/src/test/java/org/apache/commons/configuration2/builder/combined/TestCombinedConfigurationBuilder.java index 1017c21..0a59ea6 100644 --- a/src/test/java/org/apache/commons/configuration2/builder/combined/TestCombinedConfigurationBuilder.java +++ b/src/test/java/org/apache/commons/configuration2/builder/combined/TestCombinedConfigurationBuilder.java @@ -704,7 +704,7 @@ public class TestCombinedConfigurationBuilder final CombinedConfiguration cc = builder.getConfiguration(); assertFalse("Configuration is empty", cc.isEmpty()); - // The environment may contain settings with values that + // The environment may contain settings with values that // are altered by interpolation. Disable this for direct access // to the String associated with the environment property name. cc.setInterpolator(null); diff --git a/src/test/java/org/apache/commons/configuration2/builder/fluent/TestConfigurations.java b/src/test/java/org/apache/commons/configuration2/builder/fluent/TestConfigurations.java index 8ac5e71..d42c1a1 100644 --- a/src/test/java/org/apache/commons/configuration2/builder/fluent/TestConfigurations.java +++ b/src/test/java/org/apache/commons/configuration2/builder/fluent/TestConfigurations.java @@ -22,6 +22,7 @@ import static org.junit.Assert.assertSame; import java.io.File; import java.net.URL; +import java.util.Map; import org.apache.commons.configuration2.CombinedConfiguration; import org.apache.commons.configuration2.Configuration; @@ -29,10 +30,12 @@ import org.apache.commons.configuration2.ConfigurationAssert; import org.apache.commons.configuration2.INIConfiguration; import org.apache.commons.configuration2.PropertiesConfiguration; import org.apache.commons.configuration2.XMLConfiguration; +import org.apache.commons.configuration2.builder.BasicConfigurationBuilder; import org.apache.commons.configuration2.builder.FileBasedConfigurationBuilder; import org.apache.commons.configuration2.builder.combined.CombinedConfigurationBuilder; import org.apache.commons.configuration2.ex.ConfigurationException; import org.apache.commons.configuration2.plist.PropertyListConfiguration; +import org.junit.Assert; import org.junit.Test; /** @@ -57,12 +60,12 @@ public class TestConfigurations private static final String TEST_PLIST = "test.plist"; /** - * Generates a full path for the test file with the given name. + * Generates an absolute path for the test file with the given name. * * @param name the name of the test file * @return the full path to this file */ - private static String filePath(final String name) + private static String absolutePath(final String name) { return ConfigurationAssert.getTestFile(name).getAbsolutePath(); } @@ -126,7 +129,7 @@ public class TestConfigurations public void testFileBasedBuilderWithPath() { final Configurations configs = new Configurations(); - final String filePath = filePath(TEST_PROPERTIES); + final String filePath = absolutePath(TEST_PROPERTIES); final FileBasedConfigurationBuilder<PropertiesConfiguration> builder = configs.fileBasedBuilder(PropertiesConfiguration.class, filePath); @@ -179,7 +182,7 @@ public class TestConfigurations final Configurations configs = new Configurations(); final PropertyListConfiguration config = configs.fileBased(PropertyListConfiguration.class, - filePath(TEST_PLIST)); + absolutePath(TEST_PLIST)); checkPList(config); } @@ -256,11 +259,89 @@ public class TestConfigurations { final Configurations configs = new Configurations(); final FileBasedConfigurationBuilder<PropertiesConfiguration> builder = - configs.propertiesBuilder(filePath(TEST_PROPERTIES)); + configs.propertiesBuilder(absolutePath(TEST_PROPERTIES)); checkProperties(builder.getConfiguration()); } /** + * Tests whether a builder for a properties configuration can be created for + * a given file path when an include is not found. + */ + @Test + public void testPropertiesBuilderFromPathIncludeNotFoundFail() throws ConfigurationException + { + final Configurations configs = new Configurations(); + final FileBasedConfigurationBuilder<PropertiesConfiguration> builder = + configs.propertiesBuilder(absolutePath("include-not-found.properties")); + try + { + builder.getConfiguration(); + Assert.fail("Expected ConfigurationException"); + } + catch (ConfigurationException e) { + // ignore + } + } + + /** + * Tests whether a builder for a properties configuration can be created for + * a given file path when an include is not found. + */ + @Test + public void testPropertiesBuilderFromPathIncludeNotFoundPass() throws ConfigurationException + { + final Configurations configs = new Configurations(); + final String absPath = absolutePath("include-not-found.properties"); + final FileBasedConfigurationBuilder<PropertiesConfiguration> builderFail = + configs.propertiesBuilder(absPath); + // Expect failure: + try + { + builderFail.getConfiguration(); + Assert.fail("Expected ConfigurationException"); + } + catch (ConfigurationException e) + { + // Ignore + // e.printStackTrace(); + } + // Expect failure: + try + { + configs.properties(absPath); + } + catch (ConfigurationException e) { + // Ignore + // e.printStackTrace(); + } + { + // Expect success: + // @formatter:off + final Map<String, Object> map = + new Parameters().properties() + .setPath(absPath) + .setIncludeListener(PropertiesConfiguration.NOOP_INCLUDE_LISTENER) + .getParameters(); + // @formatter:on + final BasicConfigurationBuilder<PropertiesConfiguration> builderOk = configs.propertiesBuilder(absPath) + .addParameters(map); + final PropertiesConfiguration configuration = builderOk.getConfiguration(); + assertEquals("valueA", configuration.getString("keyA")); + } + { + // Expect success: + // @formatter:off + final BasicConfigurationBuilder<PropertiesConfiguration> builderOk = configs.propertiesBuilder( + new Parameters().properties() + .setPath(absPath) + .setIncludeListener(PropertiesConfiguration.NOOP_INCLUDE_LISTENER)); + // @formatter:on + final PropertiesConfiguration configuration = builderOk.getConfiguration(); + assertEquals("valueA", configuration.getString("keyA")); + } + } + + /** * Tests whether a properties configuration can be loaded from a file path. */ @Test @@ -268,7 +349,7 @@ public class TestConfigurations { final Configurations configs = new Configurations(); final PropertiesConfiguration config = - configs.properties(filePath(TEST_PROPERTIES)); + configs.properties(absolutePath(TEST_PROPERTIES)); checkProperties(config); } @@ -341,7 +422,7 @@ public class TestConfigurations { final Configurations configs = new Configurations(); final FileBasedConfigurationBuilder<XMLConfiguration> builder = - configs.xmlBuilder(filePath(TEST_XML)); + configs.xmlBuilder(absolutePath(TEST_XML)); checkXML(builder.getConfiguration()); } @@ -352,7 +433,7 @@ public class TestConfigurations public void testXMLFromPath() throws ConfigurationException { final Configurations configs = new Configurations(); - final XMLConfiguration config = configs.xml(filePath(TEST_XML)); + final XMLConfiguration config = configs.xml(absolutePath(TEST_XML)); checkXML(config); } @@ -425,7 +506,7 @@ public class TestConfigurations { final Configurations configs = new Configurations(); final FileBasedConfigurationBuilder<INIConfiguration> builder = - configs.iniBuilder(filePath(TEST_INI)); + configs.iniBuilder(absolutePath(TEST_INI)); checkINI(builder.getConfiguration()); } @@ -436,7 +517,7 @@ public class TestConfigurations public void testINIFromPath() throws ConfigurationException { final Configurations configs = new Configurations(); - final INIConfiguration config = configs.ini(filePath(TEST_INI)); + final INIConfiguration config = configs.ini(absolutePath(TEST_INI)); checkINI(config); } @@ -512,7 +593,7 @@ public class TestConfigurations { final Configurations configs = new Configurations(); final CombinedConfigurationBuilder builder = - configs.combinedBuilder(filePath(TEST_COMBINED)); + configs.combinedBuilder(absolutePath(TEST_COMBINED)); checkCombined(builder.getConfiguration()); } @@ -524,7 +605,7 @@ public class TestConfigurations { final Configurations configs = new Configurations(); final CombinedConfiguration config = - configs.combined(filePath(TEST_COMBINED)); + configs.combined(absolutePath(TEST_COMBINED)); checkCombined(config); } } diff --git a/src/test/java/org/apache/commons/configuration2/builder/fluent/TestParameters.java b/src/test/java/org/apache/commons/configuration2/builder/fluent/TestParameters.java index 50f52b7..03d0785 100644 --- a/src/test/java/org/apache/commons/configuration2/builder/fluent/TestParameters.java +++ b/src/test/java/org/apache/commons/configuration2/builder/fluent/TestParameters.java @@ -25,6 +25,7 @@ import java.util.ArrayList; import java.util.List; import java.util.Map; +import org.apache.commons.configuration2.ConfigurationConsumer; import org.apache.commons.configuration2.PropertiesConfiguration; import org.apache.commons.configuration2.builder.BasicBuilderParameters; import org.apache.commons.configuration2.builder.BasicBuilderProperties; @@ -35,6 +36,7 @@ import org.apache.commons.configuration2.builder.FileBasedBuilderParametersImpl; import org.apache.commons.configuration2.builder.combined.CombinedBuilderParametersImpl; import org.apache.commons.configuration2.builder.combined.MultiFileBuilderParametersImpl; import org.apache.commons.configuration2.convert.ListDelimiterHandler; +import org.apache.commons.configuration2.ex.ConfigurationException; import org.apache.commons.configuration2.tree.ExpressionEngine; import org.easymock.EasyMock; import org.easymock.IAnswer; @@ -272,11 +274,19 @@ public class TestParameters { final PropertiesConfiguration.IOFactory factory = EasyMock.createMock(PropertiesConfiguration.IOFactory.class); + final ConfigurationConsumer<ConfigurationException> includeListener = + EasyMock.createMock(ConfigurationConsumer.class); + // @formatter:off final Map<String, Object> map = - new Parameters().properties().setThrowExceptionOnMissing(true) - .setFileName("test.properties").setIOFactory(factory) - .setListDelimiterHandler(listHandler).setIncludesAllowed(false) + new Parameters().properties() + .setThrowExceptionOnMissing(true) + .setFileName("test.properties") + .setIncludeListener(includeListener) + .setIOFactory(factory) + .setListDelimiterHandler(listHandler) + .setIncludesAllowed(false) .getParameters(); + // @formatter:on checkBasicProperties(map); final FileBasedBuilderParametersImpl fbp = FileBasedBuilderParametersImpl.fromParameters(map); @@ -284,6 +294,7 @@ public class TestParameters .getFileName()); assertEquals("Wrong includes flag", Boolean.FALSE, map.get("includesAllowed")); + assertSame("Wrong include listener", includeListener, map.get("includeListener")); assertSame("Wrong factory", factory, map.get("IOFactory")); } diff --git a/src/test/resources/include-cyclical-reference.properties b/src/test/resources/include-cyclical-reference.properties new file mode 100644 index 0000000..7a5ccf4 --- /dev/null +++ b/src/test/resources/include-cyclical-reference.properties @@ -0,0 +1,16 @@ +# 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. +include = src/test/resources/include-cyclical-reference.properties +keyA = valueA diff --git a/src/test/resources/include-include-not-found.properties b/src/test/resources/include-include-not-found.properties new file mode 100644 index 0000000..d0c400c --- /dev/null +++ b/src/test/resources/include-include-not-found.properties @@ -0,0 +1,16 @@ +# 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. +include = include-not-found.properties +keyB = valueB diff --git a/src/test/resources/include-load-exception.properties b/src/test/resources/include-load-exception.properties new file mode 100644 index 0000000..87334cb --- /dev/null +++ b/src/test/resources/include-load-exception.properties @@ -0,0 +1,16 @@ +# 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. +include = http://this-file-does-not-and-should-not-exist.txt +keyA = valueA diff --git a/src/test/resources/include-not-found.properties b/src/test/resources/include-not-found.properties new file mode 100644 index 0000000..056c086 --- /dev/null +++ b/src/test/resources/include-not-found.properties @@ -0,0 +1,16 @@ +# 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. +include = this-file-does-not-and-should-not-exist.txt +keyA = valueA