Repository: logging-log4j2 Updated Branches: refs/heads/master e2f9d5ed4 -> 741be7fc6
[LOG4J2-2015] Allow KeyStoreConfiguration and TrustStoreConfiguration to find files as resources. Project: http://git-wip-us.apache.org/repos/asf/logging-log4j2/repo Commit: http://git-wip-us.apache.org/repos/asf/logging-log4j2/commit/741be7fc Tree: http://git-wip-us.apache.org/repos/asf/logging-log4j2/tree/741be7fc Diff: http://git-wip-us.apache.org/repos/asf/logging-log4j2/diff/741be7fc Branch: refs/heads/master Commit: 741be7fc61529147969afa4bd05cebc6cdac9c91 Parents: e2f9d5e Author: Gary Gregory <[email protected]> Authored: Tue Aug 15 16:52:19 2017 -0600 Committer: Gary Gregory <[email protected]> Committed: Tue Aug 15 16:52:19 2017 -0600 ---------------------------------------------------------------------- ...ecureSocketAppenderConnectPostStartupIT.java | 2 +- .../log4j/core/config/ConfigurationFactory.java | 88 ++------------------ .../log4j/core/config/ConfigurationSource.java | 76 +++++++++++++++++ .../net/ssl/AbstractKeyStoreConfiguration.java | 11 ++- .../core/net/ssl/KeyStoreConfiguration.java | 4 +- .../core/net/ssl/TrustStoreConfiguration.java | 4 +- .../core/net/ssl/SslConfigurationTest.java | 31 +++++-- .../log4j/core/net/ssl/TestConstants.java | 10 ++- src/changes/changes.xml | 3 + 9 files changed, 133 insertions(+), 96 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/741be7fc/log4j-core-its/src/test/java/org/apache/logging/log4j/core/appender/net/SecureSocketAppenderConnectPostStartupIT.java ---------------------------------------------------------------------- diff --git a/log4j-core-its/src/test/java/org/apache/logging/log4j/core/appender/net/SecureSocketAppenderConnectPostStartupIT.java b/log4j-core-its/src/test/java/org/apache/logging/log4j/core/appender/net/SecureSocketAppenderConnectPostStartupIT.java index 18fe5c2..c6dc1cd 100644 --- a/log4j-core-its/src/test/java/org/apache/logging/log4j/core/appender/net/SecureSocketAppenderConnectPostStartupIT.java +++ b/log4j-core-its/src/test/java/org/apache/logging/log4j/core/appender/net/SecureSocketAppenderConnectPostStartupIT.java @@ -55,7 +55,7 @@ public class SecureSocketAppenderConnectPostStartupIT extends AbstractSocketAppe @Before public void initServerSocketFactory() throws StoreConfigurationException { - sslConfiguration = SslConfigurationTest.createTestSslConfiguration(); + sslConfiguration = SslConfigurationTest.createTestSslConfigurationResources(); } @Test http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/741be7fc/log4j-core/src/main/java/org/apache/logging/log4j/core/config/ConfigurationFactory.java ---------------------------------------------------------------------- diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/ConfigurationFactory.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/ConfigurationFactory.java index adefd31..3404a55 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/ConfigurationFactory.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/ConfigurationFactory.java @@ -19,11 +19,7 @@ package org.apache.logging.log4j.core.config; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; -import java.io.IOException; -import java.io.InputStream; -import java.net.MalformedURLException; import java.net.URI; -import java.net.URISyntaxException; import java.net.URL; import java.util.ArrayList; import java.util.Collection; @@ -43,7 +39,6 @@ import org.apache.logging.log4j.core.config.plugins.util.PluginType; import org.apache.logging.log4j.core.lookup.Interpolator; import org.apache.logging.log4j.core.lookup.StrSubstitutor; import org.apache.logging.log4j.core.util.FileUtils; -import org.apache.logging.log4j.core.util.Loader; import org.apache.logging.log4j.core.util.NetUtils; import org.apache.logging.log4j.core.util.ReflectionUtil; import org.apache.logging.log4j.status.StatusLogger; @@ -239,7 +234,7 @@ public abstract class ConfigurationFactory extends ConfigurationBuilderFactory { return null; } if (configLocation != null) { - final ConfigurationSource source = getInputFromUri(configLocation); + final ConfigurationSource source = ConfigurationSource.fromUri(configLocation); if (source != null) { return getConfiguration(loggerContext, source); } @@ -266,7 +261,7 @@ public abstract class ConfigurationFactory extends ConfigurationBuilderFactory { } if (isClassLoaderUri(configLocation)) { final String path = extractClassLoaderUriPath(configLocation); - final ConfigurationSource source = getInputFromResource(path, loader); + final ConfigurationSource source = ConfigurationSource.fromResource(path, loader); if (source != null) { final Configuration configuration = getConfiguration(loggerContext, source); if (configuration != null) { @@ -277,43 +272,7 @@ public abstract class ConfigurationFactory extends ConfigurationBuilderFactory { return getConfiguration(loggerContext, name, configLocation); } - /** - * Loads the configuration from a URI. - * @param configLocation A URI representing the location of the configuration. - * @return The ConfigurationSource for the configuration. - */ - protected ConfigurationSource getInputFromUri(final URI configLocation) { - final File configFile = FileUtils.fileFromUri(configLocation); - if (configFile != null && configFile.exists() && configFile.canRead()) { - try { - return new ConfigurationSource(new FileInputStream(configFile), configFile); - } catch (final FileNotFoundException ex) { - LOGGER.error("Cannot locate file {}", configLocation.getPath(), ex); - } - } - if (isClassLoaderUri(configLocation)) { - final ClassLoader loader = LoaderUtil.getThreadContextClassLoader(); - final String path = extractClassLoaderUriPath(configLocation); - final ConfigurationSource source = getInputFromResource(path, loader); - if (source != null) { - return source; - } - } - if (!configLocation.isAbsolute()) { // LOG4J2-704 avoid confusing error message thrown by uri.toURL() - LOGGER.error("File not found in file system or classpath: {}", configLocation.toString()); - return null; - } - try { - return new ConfigurationSource(configLocation.toURL().openStream(), configLocation.toURL()); - } catch (final MalformedURLException ex) { - LOGGER.error("Invalid URL {}", configLocation.toString(), ex); - } catch (final Exception ex) { - LOGGER.error("Unable to access {}", configLocation.toString(), ex); - } - return null; - } - - private static boolean isClassLoaderUri(final URI uri) { + static boolean isClassLoaderUri(final URI uri) { if (uri == null) { return false; } @@ -321,7 +280,7 @@ public abstract class ConfigurationFactory extends ConfigurationBuilderFactory { return scheme == null || scheme.equals(CLASS_LOADER_SCHEME) || scheme.equals(CLASS_PATH_SCHEME); } - private static String extractClassLoaderUriPath(final URI uri) { + static String extractClassLoaderUriPath(final URI uri) { return uri.getScheme() == null ? uri.getPath() : uri.getSchemeSpecificPart(); } @@ -336,7 +295,7 @@ public abstract class ConfigurationFactory extends ConfigurationBuilderFactory { final URL url = new URL(config); return new ConfigurationSource(url.openStream(), FileUtils.fileFromUri(url.toURI())); } catch (final Exception ex) { - final ConfigurationSource source = getInputFromResource(config, loader); + final ConfigurationSource source = ConfigurationSource.fromResource(config, loader); if (source == null) { try { final File file = new File(config); @@ -351,39 +310,6 @@ public abstract class ConfigurationFactory extends ConfigurationBuilderFactory { } /** - * Retrieves the configuration via the ClassLoader. - * @param resource The resource to load. - * @param loader The default ClassLoader to use. - * @return The ConfigurationSource for the configuration. - */ - protected ConfigurationSource getInputFromResource(final String resource, final ClassLoader loader) { - final URL url = Loader.getResource(resource, loader); - if (url == null) { - return null; - } - InputStream is = null; - try { - is = url.openStream(); - } catch (final IOException ioe) { - LOGGER.catching(Level.DEBUG, ioe); - return null; - } - if (is == null) { - return null; - } - - if (FileUtils.isFile(url)) { - try { - return new ConfigurationSource(is, FileUtils.fileFromUri(url.toURI())); - } catch (final URISyntaxException ex) { - // Just ignore the exception. - LOGGER.catching(Level.DEBUG, ex); - } - } - return new ConfigurationSource(is, url); - } - - /** * Default Factory. */ private static class Factory extends ConfigurationFactory { @@ -473,7 +399,7 @@ public abstract class ConfigurationFactory extends ConfigurationBuilderFactory { private Configuration getConfiguration(final LoggerContext loggerContext, final String configLocationStr) { ConfigurationSource source = null; try { - source = getInputFromUri(NetUtils.toURI(configLocationStr)); + source = ConfigurationSource.fromUri(NetUtils.toURI(configLocationStr)); } catch (final Exception ex) { // Ignore the error and try as a String. LOGGER.catching(Level.DEBUG, ex); @@ -517,7 +443,7 @@ public abstract class ConfigurationFactory extends ConfigurationBuilderFactory { } configName = named ? prefix + name + suffix : prefix + suffix; - final ConfigurationSource source = getInputFromResource(configName, loader); + final ConfigurationSource source = ConfigurationSource.fromResource(configName, loader); if (source != null) { if (!factory.isActive()) { LOGGER.warn("Found configuration file {} for inactive ConfigurationFactory {}", configName, factory.getClass().getName()); http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/741be7fc/log4j-core/src/main/java/org/apache/logging/log4j/core/config/ConfigurationSource.java ---------------------------------------------------------------------- diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/ConfigurationSource.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/ConfigurationSource.java index a3b1580..7848d6a 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/ConfigurationSource.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/ConfigurationSource.java @@ -21,13 +21,20 @@ import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; +import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; +import java.net.MalformedURLException; import java.net.URI; import java.net.URISyntaxException; import java.net.URL; import java.util.Objects; +import org.apache.logging.log4j.Level; +import org.apache.logging.log4j.core.util.FileUtils; +import org.apache.logging.log4j.core.util.Loader; +import org.apache.logging.log4j.util.LoaderUtil; + /** * Represents the source for the logging configuration. */ @@ -209,4 +216,73 @@ public class ConfigurationSource { final int length = data == null ? -1 : data.length; return "stream (" + length + " bytes, unknown location)"; } + + /** + * Loads the configuration from a URI. + * @param configLocation A URI representing the location of the configuration. + * @return The ConfigurationSource for the configuration. + */ + public static ConfigurationSource fromUri(final URI configLocation) { + final File configFile = FileUtils.fileFromUri(configLocation); + if (configFile != null && configFile.exists() && configFile.canRead()) { + try { + return new ConfigurationSource(new FileInputStream(configFile), configFile); + } catch (final FileNotFoundException ex) { + ConfigurationFactory.LOGGER.error("Cannot locate file {}", configLocation.getPath(), ex); + } + } + if (ConfigurationFactory.isClassLoaderUri(configLocation)) { + final ClassLoader loader = LoaderUtil.getThreadContextClassLoader(); + final String path = ConfigurationFactory.extractClassLoaderUriPath(configLocation); + final ConfigurationSource source = fromResource(path, loader); + if (source != null) { + return source; + } + } + if (!configLocation.isAbsolute()) { // LOG4J2-704 avoid confusing error message thrown by uri.toURL() + ConfigurationFactory.LOGGER.error("File not found in file system or classpath: {}", configLocation.toString()); + return null; + } + try { + return new ConfigurationSource(configLocation.toURL().openStream(), configLocation.toURL()); + } catch (final MalformedURLException ex) { + ConfigurationFactory.LOGGER.error("Invalid URL {}", configLocation.toString(), ex); + } catch (final Exception ex) { + ConfigurationFactory.LOGGER.error("Unable to access {}", configLocation.toString(), ex); + } + return null; + } + + /** + * Retrieves the configuration via the ClassLoader. + * @param resource The resource to load. + * @param loader The default ClassLoader to use. + * @return The ConfigurationSource for the configuration. + */ + public static ConfigurationSource fromResource(final String resource, final ClassLoader loader) { + final URL url = Loader.getResource(resource, loader); + if (url == null) { + return null; + } + InputStream is = null; + try { + is = url.openStream(); + } catch (final IOException ioe) { + ConfigurationFactory.LOGGER.catching(Level.DEBUG, ioe); + return null; + } + if (is == null) { + return null; + } + + if (FileUtils.isFile(url)) { + try { + return new ConfigurationSource(is, FileUtils.fileFromUri(url.toURI())); + } catch (final URISyntaxException ex) { + // Just ignore the exception. + ConfigurationFactory.LOGGER.catching(Level.DEBUG, ex); + } + } + return new ConfigurationSource(is, url); + } } http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/741be7fc/log4j-core/src/main/java/org/apache/logging/log4j/core/net/ssl/AbstractKeyStoreConfiguration.java ---------------------------------------------------------------------- diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/net/ssl/AbstractKeyStoreConfiguration.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/net/ssl/AbstractKeyStoreConfiguration.java index b0e3396..6530b21 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/net/ssl/AbstractKeyStoreConfiguration.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/net/ssl/AbstractKeyStoreConfiguration.java @@ -16,14 +16,17 @@ */ package org.apache.logging.log4j.core.net.ssl; -import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; +import java.io.InputStream; import java.security.KeyStore; import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; import java.security.cert.CertificateException; +import org.apache.logging.log4j.core.config.ConfigurationSource; +import org.apache.logging.log4j.core.util.NetUtils; + /** * Configuration of the KeyStore */ @@ -59,7 +62,7 @@ public class AbstractKeyStoreConfiguration extends StoreConfiguration<KeyStore> if (loadLocation == null) { throw new IOException("The location is null"); } - try (final FileInputStream fin = new FileInputStream(loadLocation)) { + try (final InputStream fin = openInputStream(loadLocation)) { final KeyStore ks = KeyStore.getInstance(this.keyStoreType); ks.load(fin, this.getPasswordAsCharArray()); LOGGER.debug("Keystore successfully loaded with params(location={})", loadLocation); @@ -83,6 +86,10 @@ public class AbstractKeyStoreConfiguration extends StoreConfiguration<KeyStore> } } + private InputStream openInputStream(final String filePathOrUri) { + return ConfigurationSource.fromUri(NetUtils.toURI(filePathOrUri)).getInputStream(); + } + public KeyStore getKeyStore() { return this.keyStore; } http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/741be7fc/log4j-core/src/main/java/org/apache/logging/log4j/core/net/ssl/KeyStoreConfiguration.java ---------------------------------------------------------------------- diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/net/ssl/KeyStoreConfiguration.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/net/ssl/KeyStoreConfiguration.java index 4039dc8..facf153 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/net/ssl/KeyStoreConfiguration.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/net/ssl/KeyStoreConfiguration.java @@ -63,7 +63,7 @@ public class KeyStoreConfiguration extends AbstractKeyStoreConfiguration { * Creates a KeyStoreConfiguration. * * @param location - * The location of the KeyStore. + * The location of the KeyStore, a file path, URL or resource. * @param password * The password to access the KeyStore. * @param keyStoreType @@ -89,7 +89,7 @@ public class KeyStoreConfiguration extends AbstractKeyStoreConfiguration { * Creates a KeyStoreConfiguration. * * @param location - * The location of the KeyStore. + * The location of the KeyStore, a file path, URL or resource. * @param password * The password to access the KeyStore. * @param keyStoreType http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/741be7fc/log4j-core/src/main/java/org/apache/logging/log4j/core/net/ssl/TrustStoreConfiguration.java ---------------------------------------------------------------------- diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/net/ssl/TrustStoreConfiguration.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/net/ssl/TrustStoreConfiguration.java index ad02890..58c4d11 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/net/ssl/TrustStoreConfiguration.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/net/ssl/TrustStoreConfiguration.java @@ -56,7 +56,7 @@ public class TrustStoreConfiguration extends AbstractKeyStoreConfiguration { * Creates a KeyStoreConfiguration. * * @param location - * The location of the KeyStore. + * The location of the KeyStore, a file path, URL or resource. * @param password * The password to access the KeyStore. * @param keyStoreType @@ -81,7 +81,7 @@ public class TrustStoreConfiguration extends AbstractKeyStoreConfiguration { * Creates a KeyStoreConfiguration. * * @param location - * The location of the KeyStore. + * The location of the KeyStore, a file path, URL or resource. * @param password * The password to access the KeyStore. * @param keyStoreType http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/741be7fc/log4j-core/src/test/java/org/apache/logging/log4j/core/net/ssl/SslConfigurationTest.java ---------------------------------------------------------------------- diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/core/net/ssl/SslConfigurationTest.java b/log4j-core/src/test/java/org/apache/logging/log4j/core/net/ssl/SslConfigurationTest.java index c56128f..4452496 100644 --- a/log4j-core/src/test/java/org/apache/logging/log4j/core/net/ssl/SslConfigurationTest.java +++ b/log4j-core/src/test/java/org/apache/logging/log4j/core/net/ssl/SslConfigurationTest.java @@ -32,7 +32,15 @@ public class SslConfigurationTest { private static final String TLS_TEST_HOST = "login.yahoo.com"; private static final int TLS_TEST_PORT = 443; - public static SslConfiguration createTestSslConfiguration() throws StoreConfigurationException { + public static SslConfiguration createTestSslConfigurationResources() throws StoreConfigurationException { + final KeyStoreConfiguration ksc = new KeyStoreConfiguration(TestConstants.KEYSTORE_FILE_RESOURCE, + TestConstants.KEYSTORE_PWD, TestConstants.KEYSTORE_TYPE, null); + final TrustStoreConfiguration tsc = new TrustStoreConfiguration(TestConstants.TRUSTSTORE_FILE_RESOURCE, + TestConstants.TRUSTSTORE_PWD, null, null); + return SslConfiguration.createSSLConfiguration(null, ksc, tsc); + } + + public static SslConfiguration createTestSslConfigurationFiles() throws StoreConfigurationException { final KeyStoreConfiguration ksc = new KeyStoreConfiguration(TestConstants.KEYSTORE_FILE, TestConstants.KEYSTORE_PWD, TestConstants.KEYSTORE_TYPE, null); final TrustStoreConfiguration tsc = new TrustStoreConfiguration(TestConstants.TRUSTSTORE_FILE, @@ -41,12 +49,21 @@ public class SslConfigurationTest { } @Test - public void testGettersFromScratch() throws StoreConfigurationException { - Assert.assertNotNull(createTestSslConfiguration().getProtocol()); - Assert.assertNotNull(createTestSslConfiguration().getKeyStoreConfig()); - Assert.assertNotNull(createTestSslConfiguration().getSslContext()); - Assert.assertNotNull(createTestSslConfiguration().getSslSocketFactory()); - Assert.assertNotNull(createTestSslConfiguration().getTrustStoreConfig()); + public void testGettersFromScratchFiles() throws StoreConfigurationException { + Assert.assertNotNull(createTestSslConfigurationFiles().getProtocol()); + Assert.assertNotNull(createTestSslConfigurationFiles().getKeyStoreConfig()); + Assert.assertNotNull(createTestSslConfigurationFiles().getSslContext()); + Assert.assertNotNull(createTestSslConfigurationFiles().getSslSocketFactory()); + Assert.assertNotNull(createTestSslConfigurationFiles().getTrustStoreConfig()); + } + + @Test + public void testGettersFromScratchResources() throws StoreConfigurationException { + Assert.assertNotNull(createTestSslConfigurationResources().getProtocol()); + Assert.assertNotNull(createTestSslConfigurationResources().getKeyStoreConfig()); + Assert.assertNotNull(createTestSslConfigurationResources().getSslContext()); + Assert.assertNotNull(createTestSslConfigurationResources().getSslSocketFactory()); + Assert.assertNotNull(createTestSslConfigurationResources().getTrustStoreConfig()); } @Test http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/741be7fc/log4j-core/src/test/java/org/apache/logging/log4j/core/net/ssl/TestConstants.java ---------------------------------------------------------------------- diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/core/net/ssl/TestConstants.java b/log4j-core/src/test/java/org/apache/logging/log4j/core/net/ssl/TestConstants.java index 81ef41d..1fa8572 100644 --- a/log4j-core/src/test/java/org/apache/logging/log4j/core/net/ssl/TestConstants.java +++ b/log4j-core/src/test/java/org/apache/logging/log4j/core/net/ssl/TestConstants.java @@ -17,14 +17,22 @@ package org.apache.logging.log4j.core.net.ssl; public class TestConstants { - public static final String PATH = "src/test/resources/org/apache/logging/log4j/core/net/ssl/"; + + public static final String SOURCE_FOLDER = "src/test/resources/"; + public static final String RESOURCE_ROOT = "org/apache/logging/log4j/core/net/ssl/"; + + public static final String PATH = SOURCE_FOLDER + RESOURCE_ROOT; public static final String TRUSTSTORE_PATH = PATH; + public static final String TRUSTSTORE_RESOURCE = RESOURCE_ROOT; public static final String TRUSTSTORE_FILE = TRUSTSTORE_PATH + "truststore.jks"; + public static final String TRUSTSTORE_FILE_RESOURCE = TRUSTSTORE_RESOURCE + "truststore.jks"; public static final char[] TRUSTSTORE_PWD = "changeit".toCharArray(); public static final String TRUSTSTORE_TYPE = "JKS"; public static final String KEYSTORE_PATH = PATH; + public static final String KEYSTORE_RESOURCE = RESOURCE_ROOT; public static final String KEYSTORE_FILE = KEYSTORE_PATH + "client.log4j2-keystore.jks"; + public static final String KEYSTORE_FILE_RESOURCE = KEYSTORE_RESOURCE + "client.log4j2-keystore.jks"; public static final char[] KEYSTORE_PWD = "changeit".toCharArray(); public static final String KEYSTORE_TYPE = "JKS"; http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/741be7fc/src/changes/changes.xml ---------------------------------------------------------------------- diff --git a/src/changes/changes.xml b/src/changes/changes.xml index d1b1e13..9ff328a 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -31,6 +31,9 @@ - "remove" - Removed --> <release version="2.9.0" date="2017-MM-DD" description="GA Release 2.9.0"> + <action issue="LOG4J2-2015" dev="ggregory" type="update"> + Allow KeyStoreConfiguration and TrustStoreConfiguration to find files as resources. + </action> <action issue="LOG4J2-2011" dev="rpopma" type="update"> Replace JCommander command line parser with picocli to let users run Log4j2 utility applications without requiring an external dependency. </action>
