This is an automated email from the ASF dual-hosted git repository.
garydgregory 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 c9be15366 Disable include http[s] by default (#633)
c9be15366 is described below
commit c9be15366278781477c7ba643938214e0c7326c7
Author: Gary Gregory <[email protected]>
AuthorDate: Thu Apr 30 16:27:16 2026 -0400
Disable include http[s] by default (#633)
* Allow include error handling to be customized through a listener.
* [CONFIGURATION-756] Allow for custom behavior to handle errors loading
included properties files.
* [CONFIGURATION-756] Allow for custom behavior to handle errors loading
included properties files.
* Javadoc.
* Disable includes of files via http[s] by default.
---------
Co-authored-by: Gary Gregory <[email protected]>
Co-authored-by: Gary Gregory <[email protected]>
---
pom.xml | 5 +
.../configuration2/PropertiesConfiguration.java | 61 ++++-
.../ConfigurationDeniedException.java} | 21 +-
.../io/AbsoluteNameLocationStrategy.java | 34 ++-
.../io/AbstractFileLocationStrategy.java | 273 ++++++++++++++++++++-
.../io/BasePathLocationStrategy.java | 27 +-
.../io/ClasspathLocationStrategy.java | 25 +-
.../io/CombinedLocationStrategy.java | 85 ++++++-
.../configuration2/io/FileLocationStrategy.java | 4 +
.../configuration2/io/FileLocatorUtils.java | 24 +-
.../io/FileSystemLocationStrategy.java | 25 +-
.../io/HomeDirectoryLocationStrategy.java | 87 ++++++-
.../io/ProvidedURLLocationStrategy.java | 34 ++-
.../TestPropertiesConfiguration.java | 160 +++++++++++-
.../io/TestAbstractFileLocationStrategy.java} | 13 +-
.../io/TestProvidedURLLocationStrategy.java | 42 ++++
...clude-load-url-bad-scheme-exception.properties} | 2 +-
... => include-load-url-file-exception.properties} | 2 +-
...ude-load-url-host-unknown-exception.properties} | 0
... => include-load-url-http-exception.properties} | 0
20 files changed, 846 insertions(+), 78 deletions(-)
diff --git a/pom.xml b/pom.xml
index 9e6990746..b7b8db408 100644
--- a/pom.xml
+++ b/pom.xml
@@ -272,6 +272,11 @@
<artifactId>junit-jupiter</artifactId>
<scope>test</scope>
</dependency>
+ <dependency>
+ <groupId>org.junit-pioneer</groupId>
+ <artifactId>junit-pioneer</artifactId>
+ <scope>test</scope>
+ </dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
diff --git
a/src/main/java/org/apache/commons/configuration2/PropertiesConfiguration.java
b/src/main/java/org/apache/commons/configuration2/PropertiesConfiguration.java
index c1ac7c2df..c8e7a762a 100644
---
a/src/main/java/org/apache/commons/configuration2/PropertiesConfiguration.java
+++
b/src/main/java/org/apache/commons/configuration2/PropertiesConfiguration.java
@@ -38,8 +38,11 @@ import java.util.regex.Pattern;
import org.apache.commons.configuration2.convert.ListDelimiterHandler;
import org.apache.commons.configuration2.convert.ValueTransformer;
import org.apache.commons.configuration2.event.ConfigurationEvent;
+import org.apache.commons.configuration2.ex.ConfigurationDeniedException;
import org.apache.commons.configuration2.ex.ConfigurationException;
import org.apache.commons.configuration2.ex.ConfigurationRuntimeException;
+import org.apache.commons.configuration2.io.AbstractFileLocationStrategy;
+import
org.apache.commons.configuration2.io.AbstractFileLocationStrategy.AbstractBuilder;
import org.apache.commons.configuration2.io.FileHandler;
import org.apache.commons.configuration2.io.FileLocator;
import org.apache.commons.configuration2.io.FileLocatorAware;
@@ -159,6 +162,7 @@ import org.apache.commons.text.translate.UnicodeEscaper;
* class, which is responsible for storing the layout of the parsed properties
file (i.e. empty lines, comments, and
* such things). The {@code getLayout()} method can be used to obtain this
layout object. With {@code setLayout()} a new
* layout object can be set. This should be done before a properties file was
loaded.
+ * </p>
* <p>
* Like other {@code Configuration} implementations, this class uses a {@code
Synchronizer} object to control concurrent
* access. By choosing a suitable implementation of the {@code Synchronizer}
interface, an instance can be made
@@ -166,6 +170,7 @@ import org.apache.commons.text.translate.UnicodeEscaper;
* the {@code Synchronizer}. The intended usage is that these properties are
set once at construction time through the
* builder and after that remain constant. If you wish to change such
properties during life time of an instance, you
* have to use the {@code lock()} and {@code unlock()} methods manually to
ensure that other threads see your changes.
+ * </p>
* <p>
* As this class extends {@link AbstractConfiguration}, all basic features
like variable interpolation, list handling,
* or data type conversions are available as well. This is described in the
chapter
@@ -173,8 +178,28 @@ import org.apache.commons.text.translate.UnicodeEscaper;
* and AbstractConfiguration</a> of the user's guide. There is also a separate
chapter dealing with
* <a
href="commons.apache.org/proper/commons-configuration/userguide/howto_properties.html">
Properties files</a> in
* special.
+ * </p>
+ * <p>
+ * As of version 2.15.0, by default, when including files, the only URL
schemes allowed are {@code file} and {@code jar}. To override this default,
+ * you can either use the system property {@code
org.apache.commons.configuration2.io.FileLocationStrategy.schemes} or build a
subclass of
+ * {@link AbstractFileLocationStrategy}.
+ * </p>
+ * <strong>Using System Properties</strong>
+ * <p>
+ * The system property {@code
org.apache.commons.configuration2.io.FileLocationStrategy.schemes} String value
must be a comma-separated list of schemes,
+ * where the default is {@code "file,jar"}, and the complete list is {@code
"file,http,https,jar"}.
+ * </p>
+ * <strong>Using a Builder</strong>
+ * <p>
+ * The root builder for {@link AbstractFileLocationStrategy} is {@link
AbstractBuilder} where you define allowed schemes and hosts through its setter
+ * methods.
+ * </p>
+ * <p>
+ * For example, to programatically enable the shemes "file", "http", "https",
and "jar" for all strategies, see {@link AbstractFileLocationStrategy}.
+ * </p>
*
* @see java.util.Properties#load
+ * @see AbstractFileLocationStrategy
*/
public class PropertiesConfiguration extends BaseConfiguration implements
FileBasedConfiguration, FileLocatorAware {
@@ -1336,7 +1361,17 @@ public class PropertiesConfiguration extends
BaseConfiguration implements FileBa
}
/**
- * Stores the current {@code FileLocator} for a following IO operation.
The {@code FileLocator} is needed to resolve
+ * Gets the file locator.
+ *
+ * @return the file locator.
+ * @since 2.15.0
+ */
+ public FileLocator getLocator() {
+ return locator;
+ }
+
+ /**
+ * Sets the current {@code FileLocator} for a following IO operation. The
{@code FileLocator} is needed to resolve
* include files with relative file names.
*
* @param locator the current {@code FileLocator}
@@ -1377,7 +1412,7 @@ public class PropertiesConfiguration extends
BaseConfiguration implements FileBa
}
/**
- * Helper method for loading an included properties file. This method is
called by {@code load()} when an
+ * Loads an included properties file. This method is called by {@code
load()} when an
* {@code include} property is encountered. It tries to resolve relative
file names based on the current base path. If
* this fails, a resolution based on the location of this properties file
is tried.
*
@@ -1389,21 +1424,23 @@ public class PropertiesConfiguration extends
BaseConfiguration implements FileBa
private void loadIncludeFile(final String fileName, final boolean
optional, final Deque<URL> seenStack) throws ConfigurationException {
if (locator == null) {
throw new ConfigurationException(
- "Load operation not properly initialized! Do not call
read(InputStream) directly, but use a FileHandler to load a configuration.");
+ "Load operation not properly initialized. Do not call
read(InputStream) directly, use a FileHandler to load a configuration.");
}
-
- URL url = locateIncludeFile(locator.getBasePath(), fileName);
- if (url == null) {
- final URL baseURL = locator.getSourceURL();
- if (baseURL != null) {
- url = locateIncludeFile(baseURL.toString(), fileName);
+ URL url = null;
+ try {
+ url = locateIncludeFile(locator.getBasePath(), fileName);
+ if (url == null) {
+ final URL baseURL = locator.getSourceURL();
+ if (baseURL != null) {
+ url = locateIncludeFile(baseURL.toString(), fileName);
+ }
}
+ } catch (ConfigurationDeniedException e) {
+ getIncludeListener().accept(new ConfigurationException(e));
}
-
if (optional && url == null) {
return;
}
-
if (url == null) {
getIncludeListener().accept(new ConfigurationException(new
FileNotFoundException(fileName), "Cannot resolve include file %s", fileName));
} else {
@@ -1432,7 +1469,7 @@ public class PropertiesConfiguration extends
BaseConfiguration implements FileBa
}
/**
- * Tries to obtain the URL of an include file using the specified
(optional) base path and file name.
+ * Locates the URL of an include file using the specified (optional) base
path and file name.
*
* @param basePath the base path
* @param fileName the file name
diff --git
a/src/main/java/org/apache/commons/configuration2/io/AbstractFileLocationStrategy.java
b/src/main/java/org/apache/commons/configuration2/ex/ConfigurationDeniedException.java
similarity index 52%
copy from
src/main/java/org/apache/commons/configuration2/io/AbstractFileLocationStrategy.java
copy to
src/main/java/org/apache/commons/configuration2/ex/ConfigurationDeniedException.java
index 53cbe45ba..69ef3ca25 100644
---
a/src/main/java/org/apache/commons/configuration2/io/AbstractFileLocationStrategy.java
+++
b/src/main/java/org/apache/commons/configuration2/ex/ConfigurationDeniedException.java
@@ -14,11 +14,26 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.commons.configuration2.io;
+
+package org.apache.commons.configuration2.ex;
/**
- * Abstracts services for {@link FileLocationStrategy} implementations.
+ * Thrown when an application only grants specific configurations for elements
like URL schemes and hosts.
+ *
+ * @since 2.15.0
*/
-abstract class AbstractFileLocationStrategy implements FileLocationStrategy {
+public class ConfigurationDeniedException extends
ConfigurationRuntimeException {
+
+ private static final long serialVersionUID = 1L;
+ /**
+ * Constructs a new {@code ConfigurationDeniedException} with specified
detail message using {@link String#format(String,Object...)}.
+ *
+ * @param message the error message.
+ * @param args arguments to the error message.
+ * @see String#format(String,Object...)
+ */
+ public ConfigurationDeniedException(final String message, final Object...
args) {
+ super(message, args);
+ }
}
diff --git
a/src/main/java/org/apache/commons/configuration2/io/AbsoluteNameLocationStrategy.java
b/src/main/java/org/apache/commons/configuration2/io/AbsoluteNameLocationStrategy.java
index 7a1d145b2..ce673c860 100644
---
a/src/main/java/org/apache/commons/configuration2/io/AbsoluteNameLocationStrategy.java
+++
b/src/main/java/org/apache/commons/configuration2/io/AbsoluteNameLocationStrategy.java
@@ -28,33 +28,57 @@ import org.apache.commons.lang3.StringUtils;
* This strategy ignores the URL and the base path stored in the passed in
{@link FileLocator}. It is only triggered by
* absolute names in the locator's {@code fileName} component.
* </p>
+ * <p>
+ * See {@link AbstractFileLocationStrategy} learn how to grant an deny URL
schemes and hosts.
+ * </p>
*
+ * @see AbstractFileLocationStrategy
* @since 2.0
*/
public class AbsoluteNameLocationStrategy extends AbstractFileLocationStrategy
{
/**
- * A singleton instance of this strategy.
+ * Builds new instances of {@link ProvidedURLLocationStrategy}.
+ *
+ * @return a new builder.
+ * @since 2.15.0
*/
- static final AbsoluteNameLocationStrategy INSTANCE = new
AbsoluteNameLocationStrategy();
+ public static StrategyBuilder<AbsoluteNameLocationStrategy> builder() {
+ return new StrategyBuilder<>(AbsoluteNameLocationStrategy::new);
+ }
/**
- * Constructs a new instance.
+ * Constructs a new instance where URL resources are bound by {@link
AbstractFileLocationStrategy.AbstractBuilder}.
+ * <p>
+ * </p>
*/
public AbsoluteNameLocationStrategy() {
// empty
}
/**
- * {@inheritDoc} This implementation constructs a {@code File} object from
the locator's file name (if defined). If this
+ * Constructs a new instance where URL resources are bound by {@link
AbstractFileLocationStrategy.AbstractBuilder}.
+ *
+ * @param builder How to build the instance.
+ * @since 2.15.0
+ */
+ public AbsoluteNameLocationStrategy(final AbstractBuilder<?, ?> builder) {
+ super(builder);
+ }
+
+ /**
+ * {@inheritDoc}
+ * <p>
+ * This implementation constructs a {@code File} object from the locator's
file name (if defined). If this
* results in an absolute file name pointing to an existing file, the
corresponding URL is returned.
+ * </p>
*/
@Override
public URL locate(final FileSystem fileSystem, final FileLocator locator) {
if (StringUtils.isNotEmpty(locator.getFileName())) {
final File file = new File(locator.getFileName());
if (file.isAbsolute() && file.exists()) {
- return FileLocatorUtils.convertFileToURL(file);
+ return check(FileLocatorUtils.convertFileToURL(file));
}
}
return null;
diff --git
a/src/main/java/org/apache/commons/configuration2/io/AbstractFileLocationStrategy.java
b/src/main/java/org/apache/commons/configuration2/io/AbstractFileLocationStrategy.java
index 53cbe45ba..1eb5716a5 100644
---
a/src/main/java/org/apache/commons/configuration2/io/AbstractFileLocationStrategy.java
+++
b/src/main/java/org/apache/commons/configuration2/io/AbstractFileLocationStrategy.java
@@ -14,11 +14,280 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
package org.apache.commons.configuration2.io;
+import java.net.URL;
+import java.util.Collections;
+import java.util.LinkedHashSet;
+import java.util.Objects;
+import java.util.Set;
+import java.util.function.Function;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
+
+import org.apache.commons.configuration2.ex.ConfigurationDeniedException;
+import org.apache.commons.io.build.AbstractSupplier;
+import org.apache.commons.lang3.StringUtils;
+
/**
- * Abstracts services for {@link FileLocationStrategy} implementations.
+ * Abstracts services for FileLocationStrategy implementations.
+ * <p>
+ * Note that some FileLocationStrategy implementation use URLs internally to
encode file locations.
+ * </p>
+ * <p>
+ * As of version 2.15.0, by default, the only URL schemes allowed are {@code
file} and {@code jar}. To override this default, you can either use the system
+ * property {@code
org.apache.commons.configuration2.io.FileLocationStrategy.schemes} or build a
subclass of {@link AbstractFileLocationStrategy}.
+ * </p>
+ * <strong>Using System Properties</strong>
+ * <p>
+ * The system property {@code
org.apache.commons.configuration2.io.FileLocationStrategy.schemes} String value
must be a comma-separated list of schemes,
+ * where the default is {@code "file,jar"}, and the complete list is {@code
"file,http,https,jar"}.
+ * </p>
+ * <strong>Using a Builder</strong>
+ * <p>
+ * The root builder for {@link AbstractFileLocationStrategy} is {@link
AbstractBuilder} where you define allowed schemes and hosts through its setter
+ * methods.
+ * </p>
+ * <p>
+ * For example, to programatically enable the shemes "file", "http", "https",
and "jar" for all strategies, you write:
+ * </p>
+ * <pre>{@code
+ * final PropertiesConfiguration pc = new PropertiesConfiguration();
+ * pc.setIncludeListener(PropertiesConfiguration.NOOP_INCLUDE_LISTENER);
+ * final FileHandler handler = new FileHandler(pc);
+ * final CombinedLocationStrategy.Builder builder = new
CombinedLocationStrategy.Builder()
+ * .setSchemes(new TreeSet<>(Arrays.asList("file", "http",
"https", "jar")));
+ * // @formatter:off
+ * handler.setLocationStrategy(builder.setSubStrategies(Arrays.asList(
+ * new ProvidedURLLocationStrategy(builder),
+ * new FileSystemLocationStrategy(builder),
+ * new AbsoluteNameLocationStrategy(builder),
+ * new BasePathLocationStrategy(builder),
+ * new
HomeDirectoryLocationStrategy.Builder().setEvaluateBasePath(true).getUnchecked(),
+ * new
HomeDirectoryLocationStrategy.Builder().setEvaluateBasePath(false).getUnchecked(),
+ * new ClasspathLocationStrategy(builder)))
+ * .get());
+ * // @formatter:on
+ * handler.setBasePath(TEST_BASE_PATH);
+ *
handler.setFileName("include-load-url-host-unknown-exception.properties");
+ * handler.load();
+ * }</pre>
+ *
+ *
+ * @since 2.15.0
+ * @see FileLocationStrategy
*/
-abstract class AbstractFileLocationStrategy implements FileLocationStrategy {
+public abstract class AbstractFileLocationStrategy implements
FileLocationStrategy {
+
+ /**
+ * Builds new instances for subclasses.
+ * <p>
+ * As of version 2.15.0, by default, the only URL schemes allowed are
{@code file} and {@code jar}. To override this default, you can either use the
system
+ * property {@code
org.apache.commons.configuration2.io.FileLocationStrategy.schemes} or build a
subclass of {@link AbstractFileLocationStrategy}.
+ * </p>
+ * <strong>Using System Properties</strong>
+ * <p>
+ * The system property {@code
org.apache.commons.configuration2.io.FileLocationStrategy.schemes} String value
must be a comma-separated list of schemes,
+ * where the default is {@code "file,jar"}, and the complete list is
{@code "file,http,https,jar"}.
+ * </p>
+ * <strong>Using a Builder</strong>
+ * <p>
+ * The root builder for {@link AbstractFileLocationStrategy} is {@link
AbstractBuilder} where you define allowed schemes and hosts through its setter
+ * methods.
+ * </p>
+ * <p>
+ * See {@link AbstractFileLocationStrategy} learn how to grant an deny URL
schemes and hosts.
+ * </p>
+ *
+ * @param <T> The type of {@link FileLocationStrategy} to build.
+ * @param <B> The builder type.
+ */
+ public abstract static class AbstractBuilder<T extends
FileLocationStrategy, B extends AbstractBuilder<T, B>> extends
AbstractSupplier<T, B> {
+
+ /**
+ * Enabled URL-based hosts, empty means all are enabled. Host are
case-insensitive.
+ */
+ private Set<Pattern> hosts = Collections.emptySet();
+ /**
+ * Enabled URL-based schemes, empty means all are enabled. Schemes are
case-insensitive.
+ */
+ private Set<String> schemes = Collections.emptySet();
+
+ /**
+ * Constructs a new instance for subclasses.
+ */
+ public AbstractBuilder() {
+ // empty
+ }
+
+ Set<Pattern> getHosts() {
+ return hosts;
+ }
+
+ Set<String> getSchemes() {
+ return schemes;
+ }
+
+ /**
+ * Sets enabled URL-based hosts, empty means all are enabled. URL
hosts are case-insensitive.
+ *
+ * @param hosts enabled URL-based hosts.
+ * @return {@code this} instance.
+ */
+ public B setHosts(final Set<Pattern> hosts) {
+ this.hosts = hosts != null ? hosts : Collections.emptySet();
+ return asThis();
+ }
+
+ /**
+ * Sets enabled URL-based hosts, empty means all are enabled. URL
hosts are case-insensitive.
+ *
+ * @param hosts Regular expressions enabled URL-based hosts.
+ * @return {@code this} instance.
+ */
+ public B setHostsRegEx(final Set<String> hosts) {
+ return setHosts(hosts.stream().map(e -> Pattern.compile(e,
Pattern.CASE_INSENSITIVE)).collect(Collectors.toSet()));
+ }
+
+ /**
+ * Sets enabled URL-based schemes, empty means all are enabled. URL
schemes are case-insensitive.
+ *
+ * @param schemes enabled URL-based schemes, the default null means
all schemes are allowed.
+ * @return {@code this} instance.
+ */
+ public B setSchemes(final Set<String> schemes) {
+ this.schemes = schemes != null ? schemes : Collections.emptySet();
+ return asThis();
+ }
+ }
+
+ /**
+ * Builds new instances of T.
+ *
+ * @param <T> The type of {@link FileLocationStrategy} to build.
+ */
+ public static class StrategyBuilder<T extends FileLocationStrategy>
extends AbstractBuilder<T, StrategyBuilder<T>> {
+
+ /**
+ * Either set this or implement get().
+ */
+ private final Function<StrategyBuilder<T>, T> function;
+
+ /**
+ * Constructs a new instance for subclasses.
+ *
+ * @param function Builds an instance of T.
+ */
+ public StrategyBuilder(final Function<StrategyBuilder<T>, T> function)
{
+ this.function = Objects.requireNonNull(function, "function");
+ }
+
+ @Override
+ public T get() {
+ return function.apply(asThis());
+ }
+ }
+
+ /**
+ * Default schemes.
+ */
+ private static final String DEFAULT_SCHEMES = "file,jar";
+ /**
+ * The system property key {@code
org.apache.commons.configuration2.io.FileLocationStrategy.schemes}.
+ * <p>
+ * If absent, defaults to {@code "file,jar"}.
+ * </p>
+ * <p>
+ * For complete functionality, use {@code "file,http,https,jar"}.
+ * </p>
+ */
+ private static final String KEY_SCHEMES =
"org.apache.commons.configuration2.io.FileLocationStrategy.schemes";
+
+ private static Set<String> getSchemesProperty() {
+ final Set<String> set = new LinkedHashSet<>();
+ final String[] split = System.getProperty(KEY_SCHEMES,
DEFAULT_SCHEMES).split(",");
+ Collections.addAll(set, split);
+ return set;
+ }
+
+ /**
+ * Enabled URL-based hosts, empty means all are enabled. Host are
case-insensitive.
+ */
+ private final Set<Pattern> hosts;
+ /**
+ * Enabled URL-based schemes, empty means all are enabled. Schemes are
case-insensitive.
+ */
+ private final Set<String> schemes;
+
+ /**
+ * Constructs a new instance where the enabled URL schemes are read the
system property
+ * {@code
"org.apache.commons.configuration2.io.FileLocationStrategy.schemes"}.
+ * <p>
+ * If absent, defaults to {@code "file,jar"}.
+ * </p>
+ * <p>
+ * For complete functionality, use {@code "file,http,https,jar"}.
+ * </p>
+ */
+ AbstractFileLocationStrategy() {
+ this(getSchemesProperty());
+ }
+
+ AbstractFileLocationStrategy(final AbstractBuilder<?, ?> builder) {
+ Objects.requireNonNull(builder, "builder");
+ this.schemes = builder.schemes;
+ this.hosts = builder.hosts != null ? builder.hosts :
Collections.emptySet();
+ }
+
+ AbstractFileLocationStrategy(final Set<String> schemes) {
+ this.schemes = schemes;
+ this.hosts = Collections.emptySet();
+ }
+
+ URL check(final URL url) {
+ if (url != null) {
+ checkScheme("scheme", url, url.getProtocol(), schemes);
+ checkHost("host", url, url.getHost(), hosts);
+ }
+ return url;
+ }
+
+ void checkHost(final String type, final URL url, final String value, final
Set<Pattern> validSet) {
+ if (!validSet.isEmpty() && StringUtils.isNotEmpty(value)) {
+ hosts.stream().filter(p ->
p.matcher(StringUtils.toRootLowerCase(value)).matches()).findFirst()
+ .orElseThrow(() -> new
ConfigurationDeniedException(String.format("URL %s is not enabled: %s; must be
one of %s", type, value, validSet)));
+ }
+ }
+
+ void checkScheme(final String type, final URL url, final String value,
final Set<String> validSet) {
+ if (!validSet.isEmpty() && value != null &&
!validSet.contains(StringUtils.toRootLowerCase(value))) {
+ throw new ConfigurationDeniedException(String.format(
+ "URL %s \"%s\" is not enabled, must be one of %s, override
defaults with the system property \"%s\", complete set:
\"file,http,https,jar\"",
+ type, value, validSet, KEY_SCHEMES));
+ }
+ }
+
+ /**
+ * Gets the enabled hosts.
+ *
+ * @return the enabled hosts.
+ */
+ Set<Pattern> getHosts() {
+ return hosts;
+ }
+
+ /**
+ * Gets the enabled schemes.
+ *
+ * @return the enabled schemes.
+ */
+ Set<String> getSchemes() {
+ return schemes;
+ }
+ @Override
+ public String toString() {
+ return getClass().getSimpleName() + " [schemes=" + schemes + ",
hosts=" + hosts + "]";
+ }
}
diff --git
a/src/main/java/org/apache/commons/configuration2/io/BasePathLocationStrategy.java
b/src/main/java/org/apache/commons/configuration2/io/BasePathLocationStrategy.java
index 22edc02d3..5d8411e96 100644
---
a/src/main/java/org/apache/commons/configuration2/io/BasePathLocationStrategy.java
+++
b/src/main/java/org/apache/commons/configuration2/io/BasePathLocationStrategy.java
@@ -29,23 +29,42 @@ import org.apache.commons.lang3.StringUtils;
* base path (if present) and the file name. If the resulting path points to a
valid file, the corresponding URL is
* returned.
* </p>
+ * <p>
+ * See {@link AbstractFileLocationStrategy} learn how to grant an deny URL
schemes and hosts.
+ * </p>
*
+ * @see AbstractFileLocationStrategy
* @since 2.0
*/
public class BasePathLocationStrategy extends AbstractFileLocationStrategy {
/**
- * A singleton instance of this strategy.
+ * Builds new instances of {@link ProvidedURLLocationStrategy}.
+ *
+ * @return a new builder.
+ * @since 2.15.0
*/
- static final BasePathLocationStrategy INSTANCE = new
BasePathLocationStrategy();
+ public static StrategyBuilder<BasePathLocationStrategy> builder() {
+ return new StrategyBuilder<>(BasePathLocationStrategy::new);
+ }
/**
- * Constructs a new instance.
+ * Constructs a new instance where URL resources are bound by {@link
AbstractFileLocationStrategy.AbstractBuilder}.
*/
public BasePathLocationStrategy() {
// empty
}
+ /**
+ * Constructs a new instance where URL resources are bound by {@link
AbstractFileLocationStrategy.AbstractBuilder}.
+ *
+ * @param builder How to build the instance.
+ * @since 2.15.0
+ */
+ public BasePathLocationStrategy(final AbstractBuilder<?, ?> builder) {
+ super(builder);
+ }
+
/**
* {@inheritDoc} This implementation uses utility methods from {@code
FileLocatorUtils} to generate a {@code File} from
* the locator's base path and file name. If this {@code File} exists, its
URL is returned.
@@ -55,7 +74,7 @@ public class BasePathLocationStrategy extends
AbstractFileLocationStrategy {
if (StringUtils.isNotEmpty(locator.getFileName())) {
final File file =
FileLocatorUtils.constructFile(locator.getBasePath(), locator.getFileName());
if (file.isFile()) {
- return FileLocatorUtils.convertFileToURL(file);
+ return check(FileLocatorUtils.convertFileToURL(file));
}
}
return null;
diff --git
a/src/main/java/org/apache/commons/configuration2/io/ClasspathLocationStrategy.java
b/src/main/java/org/apache/commons/configuration2/io/ClasspathLocationStrategy.java
index f0f588f18..303a47fca 100644
---
a/src/main/java/org/apache/commons/configuration2/io/ClasspathLocationStrategy.java
+++
b/src/main/java/org/apache/commons/configuration2/io/ClasspathLocationStrategy.java
@@ -26,15 +26,24 @@ import org.apache.commons.lang3.StringUtils;
* This strategy implementation ignores the URL and the base path components
of the passed in {@link FileLocator}. It
* tries to look up the file name on both the class path and the system class
path.
* </p>
+ * <p>
+ * See {@link AbstractFileLocationStrategy} learn how to grant an deny URL
schemes and hosts.
+ * </p>
*
+ * @see AbstractFileLocationStrategy
* @since 2.0
*/
public class ClasspathLocationStrategy extends AbstractFileLocationStrategy {
/**
- * A singleton instance of this strategy.
+ * Builds new instances of {@link ProvidedURLLocationStrategy}.
+ *
+ * @return a new builder.
+ * @since 2.15.0
*/
- static final ClasspathLocationStrategy INSTANCE = new
ClasspathLocationStrategy();
+ public static StrategyBuilder<ClasspathLocationStrategy> builder() {
+ return new StrategyBuilder<>(ClasspathLocationStrategy::new);
+ }
/**
* Constructs a new instance.
@@ -43,11 +52,21 @@ public class ClasspathLocationStrategy extends
AbstractFileLocationStrategy {
// empty
}
+ /**
+ * Constructs a new instance.
+ *
+ * @param builder How to build the instance.
+ * @since 2.15.0
+ */
+ public ClasspathLocationStrategy(final AbstractBuilder<?, ?> builder) {
+ super(builder);
+ }
+
/**
* {@inheritDoc} This implementation looks up the locator's file name as a
resource on the class path.
*/
@Override
public URL locate(final FileSystem fileSystem, final FileLocator locator) {
- return StringUtils.isEmpty(locator.getFileName()) ? null :
FileLocatorUtils.getClasspathResource(locator.getFileName());
+ return check(StringUtils.isEmpty(locator.getFileName()) ? null :
FileLocatorUtils.getClasspathResource(locator.getFileName()));
}
}
diff --git
a/src/main/java/org/apache/commons/configuration2/io/CombinedLocationStrategy.java
b/src/main/java/org/apache/commons/configuration2/io/CombinedLocationStrategy.java
index 8838bc835..c1eccf0c0 100644
---
a/src/main/java/org/apache/commons/configuration2/io/CombinedLocationStrategy.java
+++
b/src/main/java/org/apache/commons/configuration2/io/CombinedLocationStrategy.java
@@ -16,10 +16,13 @@
*/
package org.apache.commons.configuration2.io;
+import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
+import java.util.Set;
+import java.util.regex.Pattern;
/**
* A specialized implementation of a {@code FileLocationStrategy} which
encapsulates an arbitrary number of
@@ -37,29 +40,93 @@ import java.util.Collections;
* requirements. Note that the order in which strategies are added to a {@code
CombinedLocationStrategy} matters: sub
* strategies are queried in the same order as they appear in the collection
passed to the constructor.
* </p>
+ * <p>
+ * See {@link AbstractFileLocationStrategy} learn how to grant an deny URL
schemes and hosts.
+ * </p>
*
+ * @see AbstractFileLocationStrategy
* @since 2.0
*/
public class CombinedLocationStrategy extends AbstractFileLocationStrategy {
+ /**
+ * Builds new instances of {@link CombinedLocationStrategy}.
+ *
+ * @since 2.15.0
+ */
+ public static class Builder extends
AbstractBuilder<CombinedLocationStrategy, Builder> {
+
+ /** A collection with all sub strategies managed by this object. */
+ private Collection<? extends FileLocationStrategy> subStrategies;
+
+ @Override
+ public CombinedLocationStrategy get() throws IOException {
+ return new CombinedLocationStrategy(this);
+ }
+
+ /**
+ * Sets the collection with sub strategies.
+ *
+ * @param subStrategies the collection with sub strategies.
+ * @return {@code this} instance.
+ */
+ public Builder setSubStrategies(final Collection<FileLocationStrategy>
subStrategies) {
+ this.subStrategies = subStrategies;
+ return asThis();
+ }
+
+ /**
+ * Propagates properties of the parent builder scheme and host to
subStrategies.
+ *
+ * @return {@code this} instance.
+ */
+ public Builder propagate() {
+ if (subStrategies != null) {
+ subStrategies.forEach(e -> {
+ if (e instanceof AbstractFileLocationStrategy) {
+ AbstractFileLocationStrategy afls =
(AbstractFileLocationStrategy) e;
+ final Set<String> schemes = afls.getSchemes();
+ schemes.clear();
+ schemes.addAll(getSchemes());
+ final Set<Pattern> hosts = afls.getHosts();
+ hosts.clear();
+ hosts.addAll(getHosts());
+ }
+ });
+ }
+ return asThis();
+ }
+
+ }
+
/** A collection with all sub strategies managed by this object. */
private final Collection<FileLocationStrategy> subStrategies;
/**
- * Creates a new instance of {@code CombinedLocationStrategy} and
initializes it with the provided sub strategies. The
- * passed in collection must not be <strong>null</strong> or contain
<strong>null</strong> elements.
+ * Constructs a new instance.
*
- * @param subs the collection with sub strategies
- * @throws IllegalArgumentException if the collection is
<strong>null</strong> or has <strong>null</strong> elements
+ * @param builder How to build the instance.
*/
- public CombinedLocationStrategy(final Collection<? extends
FileLocationStrategy> subs) {
- if (subs == null) {
+ private CombinedLocationStrategy(final Builder builder) {
+ super(builder);
+ if (builder.subStrategies == null) {
throw new IllegalArgumentException("Collection with sub strategies
must not be null.");
}
- subStrategies = Collections.unmodifiableCollection(new
ArrayList<>(subs));
- if (subStrategies.contains(null)) {
+ if (builder.subStrategies.contains(null)) {
throw new IllegalArgumentException("Collection with sub strategies
contains null entry.");
}
+ subStrategies = Collections.unmodifiableCollection(new
ArrayList<>(builder.subStrategies));
+ }
+
+ /**
+ * Creates a new instance of {@code CombinedLocationStrategy} and
initializes it with the provided sub strategies. The
+ * passed in collection must not be <strong>null</strong> or contain
<strong>null</strong> elements.
+ *
+ * @param subs the collection with sub strategies.
+ * @throws IllegalArgumentException if the collection is
<strong>null</strong> or has <strong>null</strong> elements.
+ */
+ public CombinedLocationStrategy(final Collection<FileLocationStrategy>
subs) {
+ this(new Builder().setSubStrategies(subs));
}
/**
@@ -79,7 +146,7 @@ public class CombinedLocationStrategy extends
AbstractFileLocationStrategy {
for (final FileLocationStrategy sub : getSubStrategies()) {
final URL url = sub.locate(fileSystem, locator);
if (url != null) {
- return url;
+ return check(url);
}
}
return null;
diff --git
a/src/main/java/org/apache/commons/configuration2/io/FileLocationStrategy.java
b/src/main/java/org/apache/commons/configuration2/io/FileLocationStrategy.java
index f8b48d5c5..24b6f056b 100644
---
a/src/main/java/org/apache/commons/configuration2/io/FileLocationStrategy.java
+++
b/src/main/java/org/apache/commons/configuration2/io/FileLocationStrategy.java
@@ -36,7 +36,11 @@ import java.net.URL;
* {@code FileLocationStrategy} implementations; so a file can be searched
using multiple strategies until one of them
* is successful.
* </p>
+ * <p>
+ * See {@link AbstractFileLocationStrategy} learn how to grant an deny URL
schemes and hosts.
+ * </p>
*
+ * @see AbstractFileLocationStrategy
* @since 2.0
*/
public interface FileLocationStrategy {
diff --git
a/src/main/java/org/apache/commons/configuration2/io/FileLocatorUtils.java
b/src/main/java/org/apache/commons/configuration2/io/FileLocatorUtils.java
index 46bd4a2ee..e15ccf725 100644
--- a/src/main/java/org/apache/commons/configuration2/io/FileLocatorUtils.java
+++ b/src/main/java/org/apache/commons/configuration2/io/FileLocatorUtils.java
@@ -73,7 +73,9 @@ public final class FileLocatorUtils {
* <li>Otherwise, the strategy gives up and returns <strong>null</strong>
indicating that the file cannot be resolved.</li>
* </ul>
*/
- public static final FileLocationStrategy DEFAULT_LOCATION_STRATEGY =
initDefaultLocationStrategy();
+ // @formatter:off
+ public static final FileLocationStrategy DEFAULT_LOCATION_STRATEGY =
newDefaultLocationStrategy();
+ // @formatter:on
/** Constant for the file URL protocol */
private static final String FILE_SCHEME = "file:";
@@ -420,19 +422,19 @@ public final class FileLocatorUtils {
* of the {@link #DEFAULT_LOCATION_STRATEGY} member field.
*
* @return the default {@code FileLocationStrategy}
+ * @since 2.15.0
*/
- private static FileLocationStrategy initDefaultLocationStrategy() {
+ public static FileLocationStrategy newDefaultLocationStrategy() {
// @formatter:off
- final FileLocationStrategy[] subStrategies = {
- ProvidedURLLocationStrategy.INSTANCE,
- FileSystemLocationStrategy.INSTANCE,
- AbsoluteNameLocationStrategy.INSTANCE,
- BasePathLocationStrategy.INSTANCE,
- new HomeDirectoryLocationStrategy(true),
- new HomeDirectoryLocationStrategy(false),
- ClasspathLocationStrategy.INSTANCE};
+ return new CombinedLocationStrategy(Arrays.asList(
+ new ProvidedURLLocationStrategy(),
+ new FileSystemLocationStrategy(),
+ new AbsoluteNameLocationStrategy(),
+ new BasePathLocationStrategy(),
+ new
HomeDirectoryLocationStrategy.Builder().setEvaluateBasePath(true).getUnchecked(),
+ new
HomeDirectoryLocationStrategy.Builder().setEvaluateBasePath(false).getUnchecked(),
+ new ClasspathLocationStrategy()));
// @formatter:on
- return new CombinedLocationStrategy(Arrays.asList(subStrategies));
}
/**
diff --git
a/src/main/java/org/apache/commons/configuration2/io/FileSystemLocationStrategy.java
b/src/main/java/org/apache/commons/configuration2/io/FileSystemLocationStrategy.java
index 09a3cb503..66c562bb0 100644
---
a/src/main/java/org/apache/commons/configuration2/io/FileSystemLocationStrategy.java
+++
b/src/main/java/org/apache/commons/configuration2/io/FileSystemLocationStrategy.java
@@ -26,15 +26,24 @@ import java.net.URL;
* file name. These properties are passed to the {@code locateFromURL()}
method of {@code FileSystem}. So the burden of
* resolving the file is delegated to the {@code FileSystem}.
* </p>
+ * <p>
+ * See {@link AbstractFileLocationStrategy} learn how to grant an deny URL
schemes and hosts.
+ * </p>
*
+ * @see AbstractFileLocationStrategy
* @since 2.0
*/
public class FileSystemLocationStrategy extends AbstractFileLocationStrategy {
/**
- * A singleton instance of this strategy.
+ * Builds new instances of {@link ProvidedURLLocationStrategy}.
+ *
+ * @return a new builder.
+ * @since 2.15.0
*/
- static final FileSystemLocationStrategy INSTANCE = new
FileSystemLocationStrategy();
+ public static StrategyBuilder<FileSystemLocationStrategy> builder() {
+ return new StrategyBuilder<>(FileSystemLocationStrategy::new);
+ }
/**
* Constructs a new instance.
@@ -43,11 +52,21 @@ public class FileSystemLocationStrategy extends
AbstractFileLocationStrategy {
// empty
}
+ /**
+ * Constructs a new instance.
+ *
+ * @param builder How to build the instance.
+ * @since 2.15.0
+ */
+ public FileSystemLocationStrategy(final AbstractBuilder<?, ?> builder) {
+ super(builder);
+ }
+
/**
* {@inheritDoc} This implementation delegates to the {@code FileSystem}.
*/
@Override
public URL locate(final FileSystem fileSystem, final FileLocator locator) {
- return fileSystem.locateFromURL(locator.getBasePath(),
locator.getFileName());
+ return check(fileSystem.locateFromURL(locator.getBasePath(),
locator.getFileName()));
}
}
diff --git
a/src/main/java/org/apache/commons/configuration2/io/HomeDirectoryLocationStrategy.java
b/src/main/java/org/apache/commons/configuration2/io/HomeDirectoryLocationStrategy.java
index 868357693..85efc9a15 100644
---
a/src/main/java/org/apache/commons/configuration2/io/HomeDirectoryLocationStrategy.java
+++
b/src/main/java/org/apache/commons/configuration2/io/HomeDirectoryLocationStrategy.java
@@ -17,6 +17,7 @@
package org.apache.commons.configuration2.io;
import java.io.File;
+import java.io.IOException;
import java.net.URL;
import org.apache.commons.lang3.StringUtils;
@@ -33,15 +34,61 @@ import org.apache.commons.lang3.SystemProperties;
* </p>
* <p>
* When constructing an instance it can be configured whether the base path
should be taken into account. If this option
- * is set, the base path is appended to the home directory if it is not
<strong>null</strong>. This is useful for instance to
+ * is set, the base path is appended to the home directory if it is not {@code
null}. This is useful for instance to
* select a specific sub directory of the user's home directory. If this
option is set to <strong>false</strong>, the base path is
* always ignored, and only the file name is evaluated.
* </p>
+ * <p>
+ * See {@link AbstractFileLocationStrategy} learn how to grant an deny URL
schemes and hosts.
+ * </p>
+ *
+ * @see AbstractFileLocationStrategy
*/
public class HomeDirectoryLocationStrategy extends
AbstractFileLocationStrategy {
/**
- * Obtains the home directory to be used by a new instance. If a directory
name is provided, it is used. Otherwise, the
+ * Builds new instances of {@link HomeDirectoryLocationStrategy}.
+ *
+ * @since 2.15.0
+ */
+ public static class Builder extends
AbstractBuilder<HomeDirectoryLocationStrategy, Builder> {
+
+ /** The flag whether the base path is to be taken into account. */
+ private boolean evaluateBasePath;
+
+ /** The home directory to be searched for the requested file. */
+ private String homeDirectory;
+
+ @Override
+ public HomeDirectoryLocationStrategy get() throws IOException {
+ return new HomeDirectoryLocationStrategy(this);
+ }
+
+ /**
+ * Sets whether the base path should be evaluated.
+ *
+ * @param evaluateBasePath whether the base path should be evaluated.
+ * @return {@code this} instance..
+ */
+ public Builder setEvaluateBasePath(final boolean evaluateBasePath) {
+ this.evaluateBasePath = evaluateBasePath;
+ return asThis();
+ }
+
+ /**
+ * Sets the path to the home directory (may be {@code null}).
+ *
+ * @param homeDirectory the path to the home directory (may be {@code
null})
+ * @return {@code this} instance..
+ */
+ public Builder setHomeDirectory(final String homeDirectory) {
+ this.homeDirectory = homeDirectory;
+ return asThis();
+ }
+ }
+
+ /**
+ * Gets the home directory to be used by a new instance. If a directory
name is provided, it is used. Otherwise, the
* user's home directory is looked up.
*
* @param homeDir the passed in home directory
@@ -51,12 +98,12 @@ public class HomeDirectoryLocationStrategy extends
AbstractFileLocationStrategy
return homeDir != null ? homeDir : SystemProperties.getUserHome();
}
- /** The home directory to be searched for the requested file. */
- private final String homeDirectory;
-
/** The flag whether the base path is to be taken into account. */
private final boolean evaluateBasePath;
+ /** The home directory to be searched for the requested file. */
+ private final String homeDirectory;
+
/**
* Creates a new instance of {@code HomeDirectoryLocationStrategy} with
default settings. The home directory is set to
* the user's home directory. The base path flag is set to
<strong>false</strong> (which means that the base path is ignored).
@@ -69,21 +116,35 @@ public class HomeDirectoryLocationStrategy extends
AbstractFileLocationStrategy
* Creates a new instance of {@code HomeDirectoryLocationStrategy} and
initializes the base path flag. The home
* directory is set to the user's home directory.
*
- * @param withBasePath a flag whether the base path should be evaluated
+ * @param withBasePath a flag whether the base path should be evaluated.
+ * @deprecated Use {@link Builder#setEvaluateBasePath(boolean)}.
*/
+ @Deprecated
public HomeDirectoryLocationStrategy(final boolean withBasePath) {
- this(null, withBasePath);
+ this(new
Builder().setHomeDirectory(null).setEvaluateBasePath(withBasePath));
+ }
+
+ /**
+ * Constructs a new instance.
+ *
+ * @param builder How to build the instance.
+ */
+ private HomeDirectoryLocationStrategy(final Builder builder) {
+ super(builder);
+ homeDirectory = getHomeDirectory(builder.homeDirectory);
+ evaluateBasePath = builder.evaluateBasePath;
}
/**
* Creates a new instance of {@code HomeDirectoryLocationStrategy} and
initializes it with the specified settings.
*
- * @param homeDir the path to the home directory (can be
<strong>null</strong>)
- * @param withBasePath a flag whether the base path should be evaluated
+ * @param homeDir the path to the home directory (may be {@code null}).
+ * @param withBasePath a flag whether the base path should be evaluated.
+ * @deprecated Use {@link Builder#setHomeDirectory(String)}.
*/
+ @Deprecated
public HomeDirectoryLocationStrategy(final String homeDir, final boolean
withBasePath) {
- homeDirectory = getHomeDirectory(homeDir);
- evaluateBasePath = withBasePath;
+ this(new
Builder().setHomeDirectory(homeDir).setEvaluateBasePath(withBasePath));
}
/**
@@ -102,7 +163,7 @@ public class HomeDirectoryLocationStrategy extends
AbstractFileLocationStrategy
/**
* Gets the home directory. In this directory the strategy searches for
files.
*
- * @return the home directory used by this object
+ * @return the home directory used by this object.
*/
public String getHomeDirectory() {
return homeDirectory;
@@ -128,7 +189,7 @@ public class HomeDirectoryLocationStrategy extends
AbstractFileLocationStrategy
final String basePath = getBasePath(locator);
final File file = FileLocatorUtils.constructFile(basePath,
locator.getFileName());
if (file.isFile()) {
- return FileLocatorUtils.convertFileToURL(file);
+ return check(FileLocatorUtils.convertFileToURL(file));
}
}
return null;
diff --git
a/src/main/java/org/apache/commons/configuration2/io/ProvidedURLLocationStrategy.java
b/src/main/java/org/apache/commons/configuration2/io/ProvidedURLLocationStrategy.java
index 694215c43..a7cfe86cc 100644
---
a/src/main/java/org/apache/commons/configuration2/io/ProvidedURLLocationStrategy.java
+++
b/src/main/java/org/apache/commons/configuration2/io/ProvidedURLLocationStrategy.java
@@ -22,25 +22,51 @@ import java.net.URL;
* A specialized implementation of {@code FileLocationStrategy} which checks
whether a passed in {@link FileLocator}
* already has a defined URL.
* <p>
- * {@code FileLocator} objects that have a URL already reference a file in an
unambiguous way. Therefore, this strategy
+ * {@link FileLocator} objects that have a URL already reference a file in an
unambiguous way. Therefore, this strategy
* just returns the URL of the passed in {@code FileLocator}. It can be used
as a first step of the file resolving
* process. If it fails, more sophisticated attempts for resolving the file
can be made.
* </p>
+ * <p>
+ * See {@link AbstractFileLocationStrategy} learn how to grant an deny URL
schemes and hosts.
+ * </p>
*
+ * @see AbstractFileLocationStrategy
* @since 2.0
*/
public class ProvidedURLLocationStrategy extends AbstractFileLocationStrategy {
/**
- * A singleton instance of this strategy.
+ * Builds new instances of {@link ProvidedURLLocationStrategy}.
+ *
+ * @return a new builder.
+ * @since 2.15.0
+ */
+ public static StrategyBuilder<ProvidedURLLocationStrategy> builder() {
+ return new StrategyBuilder<>(ProvidedURLLocationStrategy::new);
+ }
+
+ /**
+ * Constructs a new instance where URL resources are bound by {@link
AbstractFileLocationStrategy.AbstractBuilder}.
*/
- static final ProvidedURLLocationStrategy INSTANCE = new
ProvidedURLLocationStrategy();
+ public ProvidedURLLocationStrategy() {
+ }
+
+ /**
+ * Constructs a new instance where URL resources are bound by {@link
AbstractFileLocationStrategy.AbstractBuilder}.
+ *
+ * @param builder How to build the instance.
+ * @since 2.15.0
+ */
+ public ProvidedURLLocationStrategy(final AbstractBuilder<?, ?> builder) {
+ super(builder);
+ }
/**
* {@inheritDoc} This implementation just returns the URL stored in the
given {@code FileLocator}.
*/
@Override
public URL locate(final FileSystem fileSystem, final FileLocator locator) {
- return locator.getSourceURL();
+ return check(locator.getSourceURL());
}
+
}
diff --git
a/src/test/java/org/apache/commons/configuration2/TestPropertiesConfiguration.java
b/src/test/java/org/apache/commons/configuration2/TestPropertiesConfiguration.java
index 3f90bda54..22f60b65c 100644
---
a/src/test/java/org/apache/commons/configuration2/TestPropertiesConfiguration.java
+++
b/src/test/java/org/apache/commons/configuration2/TestPropertiesConfiguration.java
@@ -63,6 +63,7 @@ import java.util.List;
import java.util.PriorityQueue;
import java.util.Properties;
import java.util.Set;
+import java.util.TreeSet;
import org.apache.commons.collections.IteratorUtils;
import org.apache.commons.configuration2.SynchronizerTestImpl.Methods;
@@ -76,16 +77,26 @@ import
org.apache.commons.configuration2.convert.DisabledListDelimiterHandler;
import org.apache.commons.configuration2.convert.LegacyListDelimiterHandler;
import org.apache.commons.configuration2.convert.ListDelimiterHandler;
import org.apache.commons.configuration2.event.ConfigurationEvent;
+import org.apache.commons.configuration2.ex.ConfigurationDeniedException;
import org.apache.commons.configuration2.ex.ConfigurationException;
+import org.apache.commons.configuration2.io.AbsoluteNameLocationStrategy;
+import org.apache.commons.configuration2.io.BasePathLocationStrategy;
+import org.apache.commons.configuration2.io.ClasspathLocationStrategy;
+import org.apache.commons.configuration2.io.CombinedLocationStrategy;
import org.apache.commons.configuration2.io.DefaultFileSystem;
import org.apache.commons.configuration2.io.FileHandler;
+import org.apache.commons.configuration2.io.FileLocatorUtils;
import org.apache.commons.configuration2.io.FileSystem;
+import org.apache.commons.configuration2.io.FileSystemLocationStrategy;
+import org.apache.commons.configuration2.io.HomeDirectoryLocationStrategy;
+import org.apache.commons.configuration2.io.ProvidedURLLocationStrategy;
import org.apache.commons.lang3.mutable.MutableObject;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
+import org.junitpioneer.jupiter.SetSystemProperty;
/**
* Test for loading and saving properties files.
@@ -106,6 +117,21 @@ public class TestPropertiesConfiguration {
}
}
+ static class ExceptionList implements
ConfigurationConsumer<ConfigurationException> {
+
+ private final List<ConfigurationException> exceptions = new
ArrayList<>();
+
+ @Override
+ public void accept(final ConfigurationException t) throws
ConfigurationException {
+ exceptions.add(t);
+ }
+
+ List<ConfigurationException> getExceptions() {
+ return exceptions;
+ }
+
+ }
+
/**
* A mock implementation of a HttpURLConnection used for testing saving to
a HTTP server.
*/
@@ -117,8 +143,8 @@ public class TestPropertiesConfiguration {
/** The output file. The output stream will point to this file. */
private final File outputFile;
- protected MockHttpURLConnection(final URL u, final int respCode, final
File outFile) {
- super(u);
+ protected MockHttpURLConnection(final URL url, final int respCode,
final File outFile) {
+ super(url);
returnCode = respCode;
outputFile = outFile;
}
@@ -221,6 +247,13 @@ public class TestPropertiesConfiguration {
}
}
+ /**
+ * All URL schemes.
+ */
+ private static final String ALL_SCHEMES = "file,http,https,jar";
+
+ private static final String KEY_SCHEMES =
"org.apache.commons.configuration2.io.FileLocationStrategy.schemes";
+
/** Constant for a test property name. */
private static final String PROP_NAME = "testProperty";
@@ -315,6 +348,10 @@ public class TestPropertiesConfiguration {
return checkConfig;
}
+ private void reinitLocationStrategy() {
+
conf.initFileLocator(FileLocatorUtils.fileLocator(conf.getLocator()).locationStrategy(FileLocatorUtils.newDefaultLocationStrategy()).create());
+ }
+
/**
* Saves the test configuration to a default output file.
*
@@ -330,7 +367,6 @@ public class TestPropertiesConfiguration {
conf = new PropertiesConfiguration();
conf.setListDelimiterHandler(new LegacyListDelimiterHandler(','));
load(conf, TEST_PROPERTIES);
-
// remove the test save file if it exists
if (TEST_SAVE_PROPERTIES_FILE.exists()) {
assertTrue(TEST_SAVE_PROPERTIES_FILE.delete());
@@ -772,12 +808,115 @@ public class TestPropertiesConfiguration {
}
@Test
- void testIncludeLoadAllOnLoadException() throws Exception {
+ void testIncludeLoadAllOnLoadBadHostException() throws Exception {
+ final PropertiesConfiguration pc = new PropertiesConfiguration();
+ final ExceptionList list = new ExceptionList();
+ pc.setIncludeListener(list);
+ final FileHandler handler = new FileHandler(pc);
+ // @formatter:off
+ final CombinedLocationStrategy.Builder builder = new
CombinedLocationStrategy.Builder()
+ .setSchemes(new
TreeSet<>(Arrays.asList(ALL_SCHEMES.split(","))))
+ .setHostsRegEx(new TreeSet<>(Arrays.asList("GrantThisHost")));
+ handler.setLocationStrategy(builder.setSubStrategies(Arrays.asList(
+ new ProvidedURLLocationStrategy(builder),
+ new FileSystemLocationStrategy(builder),
+ new AbsoluteNameLocationStrategy(builder),
+ new BasePathLocationStrategy(builder),
+ new
HomeDirectoryLocationStrategy.Builder().setEvaluateBasePath(true).getUnchecked(),
+ new
HomeDirectoryLocationStrategy.Builder().setEvaluateBasePath(false).getUnchecked(),
+ new ClasspathLocationStrategy(builder)))
+ .get());
+ // @formatter:on
+ handler.setBasePath(TEST_BASE_PATH);
+
handler.setFileName("include-load-url-host-unknown-exception.properties");
+ handler.load();
+ assertFalse(list.exceptions.isEmpty());
+ assertInstanceOf(ConfigurationDeniedException.class,
list.exceptions.get(0).getCause());
+ assertEquals("valueA", pc.getString("keyA"));
+ }
+
+ @Test
+ @SetSystemProperty(key = KEY_SCHEMES, value = ALL_SCHEMES)
+ void testIncludeLoadAllOnLoadBadUrlException() throws Exception {
final PropertiesConfiguration pc = new PropertiesConfiguration();
pc.setIncludeListener(PropertiesConfiguration.NOOP_INCLUDE_LISTENER);
final FileHandler handler = new FileHandler(pc);
+ // pick up @SetSystemProperty
+
handler.setLocationStrategy(FileLocatorUtils.newDefaultLocationStrategy());
handler.setBasePath(TEST_BASE_PATH);
- handler.setFileName("include-load-exception.properties");
+
handler.setFileName("include-load-url-host-unknown-exception.properties");
+ handler.load();
+ assertEquals("valueA", pc.getString("keyA"));
+ }
+
+ @Test
+ @SetSystemProperty(key = KEY_SCHEMES, value = ALL_SCHEMES)
+ void testIncludeLoadAllOnLoadBadUrlExceptionManualDefault() throws
Exception {
+ final PropertiesConfiguration pc = new PropertiesConfiguration();
+ pc.setIncludeListener(PropertiesConfiguration.NOOP_INCLUDE_LISTENER);
+ final FileHandler handler = new FileHandler(pc);
+ // pick up @SetSystemProperty
+ // @formatter:off
+ handler.setLocationStrategy(new CombinedLocationStrategy(Arrays.asList(
+ new ProvidedURLLocationStrategy(),
+ new FileSystemLocationStrategy(),
+ new AbsoluteNameLocationStrategy(),
+ new BasePathLocationStrategy(),
+ new
HomeDirectoryLocationStrategy.Builder().setEvaluateBasePath(true).getUnchecked(),
+ new
HomeDirectoryLocationStrategy.Builder().setEvaluateBasePath(false).getUnchecked(),
+ new ClasspathLocationStrategy())));
+ // @formatter:on
+ handler.setBasePath(TEST_BASE_PATH);
+
handler.setFileName("include-load-url-host-unknown-exception.properties");
+ handler.load();
+ assertEquals("valueA", pc.getString("keyA"));
+ }
+
+ @Test
+ void testIncludeLoadAllOnLoadBadUrlExceptionManualPropagate() throws
Exception {
+ final PropertiesConfiguration pc = new PropertiesConfiguration();
+ pc.setIncludeListener(PropertiesConfiguration.NOOP_INCLUDE_LISTENER);
+ final FileHandler handler = new FileHandler(pc);
+ final CombinedLocationStrategy.Builder builder = new
CombinedLocationStrategy.Builder()
+ .setSchemes(new
TreeSet<>(Arrays.asList(ALL_SCHEMES.split(","))));
+ // @formatter:off
+ handler.setLocationStrategy(builder.setSubStrategies(Arrays.asList(
+ new ProvidedURLLocationStrategy(builder),
+ new FileSystemLocationStrategy(builder),
+ new AbsoluteNameLocationStrategy(builder),
+ new BasePathLocationStrategy(builder),
+ new
HomeDirectoryLocationStrategy.Builder().setEvaluateBasePath(true).getUnchecked(),
+ new
HomeDirectoryLocationStrategy.Builder().setEvaluateBasePath(false).getUnchecked(),
+ new ClasspathLocationStrategy(builder)))
+ .get());
+ // @formatter:on
+ handler.setBasePath(TEST_BASE_PATH);
+
handler.setFileName("include-load-url-host-unknown-exception.properties");
+ handler.load();
+ assertEquals("valueA", pc.getString("keyA"));
+ }
+
+ @Test
+ void testIncludeLoadAllOnLoadFileException() throws Exception {
+ final PropertiesConfiguration pc = new PropertiesConfiguration();
+ pc.setIncludeListener(PropertiesConfiguration.NOOP_INCLUDE_LISTENER);
+ final FileHandler handler = new FileHandler(pc);
+ handler.setBasePath(TEST_BASE_PATH);
+ handler.setFileName("include-load-url-file-exception.properties");
+ handler.load();
+ assertEquals("valueA", pc.getString("keyA"));
+ }
+
+ @Test
+ @SetSystemProperty(key = KEY_SCHEMES, value = ALL_SCHEMES)
+ void testIncludeLoadAllOnLoadUrlException() throws Exception {
+ final PropertiesConfiguration pc = new PropertiesConfiguration();
+ pc.setIncludeListener(PropertiesConfiguration.NOOP_INCLUDE_LISTENER);
+ final FileHandler handler = new FileHandler(pc);
+ // pick up @SetSystemProperty
+
handler.setLocationStrategy(FileLocatorUtils.newDefaultLocationStrategy());
+ handler.setBasePath(TEST_BASE_PATH);
+ handler.setFileName("include-load-url-http-exception.properties");
handler.load();
assertEquals("valueA", pc.getString("keyA"));
}
@@ -835,6 +974,17 @@ public class TestPropertiesConfiguration {
assertEquals("valueA", pc.getString("keyA"));
}
+ @Test
+ void testIncludeLoadUrlBadScheme() throws Exception {
+ final PropertiesConfiguration pc = new PropertiesConfiguration();
+ pc.setIncludeListener(PropertiesConfiguration.NOOP_INCLUDE_LISTENER);
+ final FileHandler handler = new FileHandler(pc);
+ handler.setBasePath(TEST_BASE_PATH);
+
handler.setFileName("include-load-url-bad-scheme-exception.properties");
+ handler.load();
+ assertEquals("valueA", pc.getString("keyA"));
+ }
+
/**
* Tests initializing a properties configuration from a non existing file.
There was a bug, which caused properties
* getting lost when later save() is called.
diff --git
a/src/main/java/org/apache/commons/configuration2/io/AbstractFileLocationStrategy.java
b/src/test/java/org/apache/commons/configuration2/io/TestAbstractFileLocationStrategy.java
similarity index 71%
copy from
src/main/java/org/apache/commons/configuration2/io/AbstractFileLocationStrategy.java
copy to
src/test/java/org/apache/commons/configuration2/io/TestAbstractFileLocationStrategy.java
index 53cbe45ba..5ceb2e799 100644
---
a/src/main/java/org/apache/commons/configuration2/io/AbstractFileLocationStrategy.java
+++
b/src/test/java/org/apache/commons/configuration2/io/TestAbstractFileLocationStrategy.java
@@ -14,11 +14,20 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
package org.apache.commons.configuration2.io;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+import org.junit.jupiter.api.Test;
+
/**
- * Abstracts services for {@link FileLocationStrategy} implementations.
+ * Tests {@link AbstractFileLocationStrategy}.
*/
-abstract class AbstractFileLocationStrategy implements FileLocationStrategy {
+public class TestAbstractFileLocationStrategy {
+ @Test
+ void testBuilder() {
+ assertThrows(NullPointerException.class, () -> new
AbstractFileLocationStrategy.StrategyBuilder<>(null));
+ }
}
diff --git
a/src/test/java/org/apache/commons/configuration2/io/TestProvidedURLLocationStrategy.java
b/src/test/java/org/apache/commons/configuration2/io/TestProvidedURLLocationStrategy.java
index b3534764e..2002246da 100644
---
a/src/test/java/org/apache/commons/configuration2/io/TestProvidedURLLocationStrategy.java
+++
b/src/test/java/org/apache/commons/configuration2/io/TestProvidedURLLocationStrategy.java
@@ -14,15 +14,22 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
package org.apache.commons.configuration2.io;
+import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertSame;
+import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.Mockito.mock;
import java.net.URL;
+import java.util.HashSet;
+import java.util.Set;
import org.apache.commons.configuration2.ConfigurationAssert;
+import org.apache.commons.configuration2.ex.ConfigurationDeniedException;
+import
org.apache.commons.configuration2.io.AbstractFileLocationStrategy.StrategyBuilder;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
@@ -49,6 +56,16 @@ public class TestProvidedURLLocationStrategy {
assertNull(strategy.locate(fs, locator));
}
+ /**
+ * Tests a failed locate() operation.
+ */
+ @Test
+ void testLocateFailDefaultBuilder() {
+ final FileSystem fs = mock(FileSystem.class);
+ final FileLocator locator =
FileLocatorUtils.fileLocator().basePath("somePath").fileName("someFile.xml").create();
+ assertNull(ProvidedURLLocationStrategy.builder().get().locate(fs,
locator));
+ }
+
/**
* Tests a successful locate() operation.
*/
@@ -59,4 +76,29 @@ public class TestProvidedURLLocationStrategy {
final FileLocator locator =
FileLocatorUtils.fileLocator().sourceURL(url).create();
assertSame(url, strategy.locate(fs, locator));
}
+
+ /**
+ * Tests a successful locate() operation.
+ */
+ @Test
+ void testLocateSuccessDefaultBuilder() {
+ final FileSystem fs = mock(FileSystem.class);
+ final URL url = ConfigurationAssert.getTestURL("test.xml");
+ final FileLocator locator =
FileLocatorUtils.fileLocator().sourceURL(url).create();
+ assertSame(url, ProvidedURLLocationStrategy.builder().get().locate(fs,
locator));
+ }
+
+ @Test
+ void testLocateSchemes() {
+ final FileSystem fs = mock(FileSystem.class);
+ final URL url = ConfigurationAssert.getTestURL("test.xml");
+ final FileLocator locator =
FileLocatorUtils.fileLocator().sourceURL(url).create();
+ final Set<String> schemes = new HashSet<>();
+ final StrategyBuilder<ProvidedURLLocationStrategy> builder =
ProvidedURLLocationStrategy.builder();
+ assertEquals("file", builder.setSchemes(schemes).get().locate(fs,
locator).getProtocol());
+ schemes.add("foo");
+ assertThrows(ConfigurationDeniedException.class, () ->
builder.setSchemes(schemes).get().locate(fs, locator));
+ schemes.add("file");
+ assertSame(url, builder.setSchemes(schemes).get().locate(fs, locator));
+ }
}
diff --git a/src/test/resources/include-load-exception.properties
b/src/test/resources/include-load-url-bad-scheme-exception.properties
similarity index 92%
copy from src/test/resources/include-load-exception.properties
copy to src/test/resources/include-load-url-bad-scheme-exception.properties
index 9faf9e7d9..1beca3e64 100644
--- a/src/test/resources/include-load-exception.properties
+++ b/src/test/resources/include-load-url-bad-scheme-exception.properties
@@ -12,5 +12,5 @@
# 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
+include = BadScheme://this-file-does-not-and-should-not-exist.txt
keyA = valueA
diff --git a/src/test/resources/include-load-exception.properties
b/src/test/resources/include-load-url-file-exception.properties
similarity index 93%
copy from src/test/resources/include-load-exception.properties
copy to src/test/resources/include-load-url-file-exception.properties
index 9faf9e7d9..44136fadd 100644
--- a/src/test/resources/include-load-exception.properties
+++ b/src/test/resources/include-load-url-file-exception.properties
@@ -12,5 +12,5 @@
# 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
+include = file://this-file-does-not-and-should-not-exist.txt
keyA = valueA
diff --git a/src/test/resources/include-load-exception.properties
b/src/test/resources/include-load-url-host-unknown-exception.properties
similarity index 100%
copy from src/test/resources/include-load-exception.properties
copy to src/test/resources/include-load-url-host-unknown-exception.properties
diff --git a/src/test/resources/include-load-exception.properties
b/src/test/resources/include-load-url-http-exception.properties
similarity index 100%
rename from src/test/resources/include-load-exception.properties
rename to src/test/resources/include-load-url-http-exception.properties