Repository: logging-log4j2 Updated Branches: refs/heads/master e706a22da -> 67b4ba5e6
Javadoc: Use the active voice. Project: http://git-wip-us.apache.org/repos/asf/logging-log4j2/repo Commit: http://git-wip-us.apache.org/repos/asf/logging-log4j2/commit/67b4ba5e Tree: http://git-wip-us.apache.org/repos/asf/logging-log4j2/tree/67b4ba5e Diff: http://git-wip-us.apache.org/repos/asf/logging-log4j2/diff/67b4ba5e Branch: refs/heads/master Commit: 67b4ba5e6e8649cfc446e7b064d6bfc615f942f1 Parents: e706a22 Author: Gary Gregory <[email protected]> Authored: Sun Aug 21 22:10:33 2016 -0700 Committer: Gary Gregory <[email protected]> Committed: Sun Aug 21 22:10:33 2016 -0700 ---------------------------------------------------------------------- .../log4j/core/config/ConfigurationFactory.java | 1101 +++++++++--------- 1 file changed, 551 insertions(+), 550 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/67b4ba5e/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 a5c54ac..c1a3684 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 @@ -1,550 +1,551 @@ -/* - * 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.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; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.concurrent.locks.Lock; -import java.util.concurrent.locks.ReentrantLock; - -import org.apache.logging.log4j.Level; -import org.apache.logging.log4j.Logger; -import org.apache.logging.log4j.core.config.builder.api.ConfigurationBuilderFactory; -import org.apache.logging.log4j.core.config.composite.CompositeConfiguration; -import org.apache.logging.log4j.core.config.plugins.util.PluginManager; -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; -import org.apache.logging.log4j.util.LoaderUtil; -import org.apache.logging.log4j.util.PropertiesUtil; -import org.apache.logging.log4j.util.Strings; - -/** - * Factory class for parsed {@link Configuration} objects from a configuration file. - * ConfigurationFactory allows the configuration implementation to be - * dynamically chosen in 1 of 3 ways: - * <ol> - * <li>A system property named "log4j.configurationFactory" can be set with the - * name of the ConfigurationFactory to be used.</li> - * <li> - * {@linkplain #setConfigurationFactory(ConfigurationFactory)} can be called - * with the instance of the ConfigurationFactory to be used. This must be called - * before any other calls to Log4j.</li> - * <li> - * A ConfigurationFactory implementation can be added to the classpath and configured as a plugin in the - * {@link #CATEGORY ConfigurationFactory} category. The {@link Order} annotation should be used to configure the - * factory to be the first one inspected. See - * {@linkplain org.apache.logging.log4j.core.config.xml.XmlConfigurationFactory} for an example.</li> - * </ol> - * - * If the ConfigurationFactory that was added returns null on a call to - * getConfiguration then any other ConfigurationFactories found as plugins will - * be called in their respective order. DefaultConfiguration is always called - * last if no configuration has been returned. - */ -public abstract class ConfigurationFactory extends ConfigurationBuilderFactory { - /** - * Allow the ConfigurationFactory class to be specified as a system property. - */ - public static final String CONFIGURATION_FACTORY_PROPERTY = "log4j.configurationFactory"; - - /** - * Allow the location of the configuration file to be specified as a system property. - */ - public static final String CONFIGURATION_FILE_PROPERTY = "log4j.configurationFile"; - - /** - * Plugin category used to inject a ConfigurationFactory {@link org.apache.logging.log4j.core.config.plugins.Plugin} - * class. - * - * @since 2.1 - */ - public static final String CATEGORY = "ConfigurationFactory"; - - /** - * Allow subclasses access to the status logger without creating another instance. - */ - protected static final Logger LOGGER = StatusLogger.getLogger(); - - /** - * File name prefix for test configurations. - */ - protected static final String TEST_PREFIX = "log4j2-test"; - - /** - * File name prefix for standard configurations. - */ - protected static final String DEFAULT_PREFIX = "log4j2"; - - /** - * The name of the classloader URI scheme. - */ - private static final String CLASS_LOADER_SCHEME = "classloader"; - - /** - * The name of the classpath URI scheme, synonymous with the classloader URI scheme. - */ - private static final String CLASS_PATH_SCHEME = "classpath"; - - private static volatile List<ConfigurationFactory> factories = null; - - private static ConfigurationFactory configFactory = new Factory(); - - protected final StrSubstitutor substitutor = new StrSubstitutor(new Interpolator()); - - private static final Lock LOCK = new ReentrantLock(); - - /** - * Returns the ConfigurationFactory. - * @return the ConfigurationFactory. - */ - public static ConfigurationFactory getInstance() { - // volatile works in Java 1.6+, so double-checked locking also works properly - //noinspection DoubleCheckedLocking - if (factories == null) { - LOCK.lock(); - try { - if (factories == null) { - final List<ConfigurationFactory> list = new ArrayList<>(); - final String factoryClass = PropertiesUtil.getProperties().getStringProperty(CONFIGURATION_FACTORY_PROPERTY); - if (factoryClass != null) { - addFactory(list, factoryClass); - } - final PluginManager manager = new PluginManager(CATEGORY); - manager.collectPlugins(); - final Map<String, PluginType<?>> plugins = manager.getPlugins(); - final List<Class<? extends ConfigurationFactory>> ordered = new ArrayList<>(plugins.size()); - for (final PluginType<?> type : plugins.values()) { - try { - ordered.add(type.getPluginClass().asSubclass(ConfigurationFactory.class)); - } catch (final Exception ex) { - LOGGER.warn("Unable to add class {}", type.getPluginClass(), ex); - } - } - Collections.sort(ordered, OrderComparator.getInstance()); - for (final Class<? extends ConfigurationFactory> clazz : ordered) { - addFactory(list, clazz); - } - // see above comments about double-checked locking - //noinspection NonThreadSafeLazyInitialization - factories = Collections.unmodifiableList(list); - } - } finally { - LOCK.unlock(); - } - } - - LOGGER.debug("Using configurationFactory {}", configFactory); - return configFactory; - } - - private static void addFactory(final Collection<ConfigurationFactory> list, final String factoryClass) { - try { - addFactory(list, LoaderUtil.loadClass(factoryClass).asSubclass(ConfigurationFactory.class)); - } catch (final Exception ex) { - LOGGER.error("Unable to load class {}", factoryClass, ex); - } - } - - private static void addFactory(final Collection<ConfigurationFactory> list, - final Class<? extends ConfigurationFactory> factoryClass) { - try { - list.add(ReflectionUtil.instantiate(factoryClass)); - } catch (final Exception ex) { - LOGGER.error("Unable to create instance of {}", factoryClass.getName(), ex); - } - } - - /** - * Set the configuration factory. This method is not intended for general use and may not be thread safe. - * @param factory the ConfigurationFactory. - */ - public static void setConfigurationFactory(final ConfigurationFactory factory) { - configFactory = factory; - } - - /** - * Reset the ConfigurationFactory to the default. This method is not intended for general use and may - * not be thread safe. - */ - public static void resetConfigurationFactory() { - configFactory = new Factory(); - } - - /** - * Remove the ConfigurationFactory. This method is not intended for general use and may not be thread safe. - * @param factory The factory to remove. - */ - public static void removeConfigurationFactory(final ConfigurationFactory factory) { - if (configFactory == factory) { - configFactory = new Factory(); - } - } - - protected abstract String[] getSupportedTypes(); - - protected boolean isActive() { - return true; - } - - public abstract Configuration getConfiguration(ConfigurationSource source); - - /** - * Returns the Configuration. - * @param name The configuration name. - * @param configLocation The configuration location. - * @return The Configuration. - */ - public Configuration getConfiguration(final String name, final URI configLocation) { - if (!isActive()) { - return null; - } - if (configLocation != null) { - final ConfigurationSource source = getInputFromUri(configLocation); - if (source != null) { - return getConfiguration(source); - } - } - return null; - } - - /** - * Returns the Configuration obtained using a given ClassLoader. - * - * @param name The configuration name. - * @param configLocation A URI representing the location of the configuration. - * @param loader The default ClassLoader to use. If this is {@code null}, then the - * {@linkplain LoaderUtil#getThreadContextClassLoader() default ClassLoader} will be used. - * @return The Configuration. - * @since 2.1 - */ - public Configuration getConfiguration(final String name, final URI configLocation, final ClassLoader loader) { - if (!isActive()) { - return null; - } - if (loader == null) { - return getConfiguration(name, configLocation); - } - if (isClassLoaderUri(configLocation)) { - final String path = extractClassLoaderUriPath(configLocation); - final ConfigurationSource source = getInputFromResource(path, loader); - if (source != null) { - final Configuration configuration = getConfiguration(source); - if (configuration != null) { - return configuration; - } - } - } - return getConfiguration(name, configLocation); - } - - /** - * Load 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) { - if (uri == null) { - return false; - } - final String scheme = uri.getScheme(); - return scheme == null || scheme.equals(CLASS_LOADER_SCHEME) || scheme.equals(CLASS_PATH_SCHEME); - } - - private static String extractClassLoaderUriPath(final URI uri) { - return uri.getScheme() == null ? uri.getPath() : uri.getSchemeSpecificPart(); - } - - /** - * Load the configuration from the location represented by the String. - * @param config The configuration location. - * @param loader The default ClassLoader to use. - * @return The InputSource to use to read the configuration. - */ - protected ConfigurationSource getInputFromString(final String config, final ClassLoader loader) { - try { - 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); - if (source == null) { - try { - final File file = new File(config); - return new ConfigurationSource(new FileInputStream(file), file); - } catch (final FileNotFoundException fnfe) { - // Ignore the exception - LOGGER.catching(Level.DEBUG, fnfe); - } - } - return source; - } - } - - /** - * Retrieve 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 { - - /** - * Default Factory Constructor. - * @param name The configuration name. - * @param configLocation The configuration location. - * @return The Configuration. - */ - @Override - public Configuration getConfiguration(final String name, final URI configLocation) { - - if (configLocation == null) { - final String configLocationStr = this.substitutor.replace(PropertiesUtil.getProperties() - .getStringProperty(CONFIGURATION_FILE_PROPERTY)); - if (configLocationStr != null) { - final String[] sources = configLocationStr.split(","); - if (sources.length > 1) { - final List<AbstractConfiguration> configs = new ArrayList<>(); - for (final String sourceLocation : sources) { - final Configuration config = getConfiguration(sourceLocation.trim()); - if (config != null && config instanceof AbstractConfiguration) { - configs.add((AbstractConfiguration) config); - } else { - LOGGER.error("Failed to created configuration at {}", sourceLocation); - return null; - } - } - return new CompositeConfiguration(configs); - } - return getConfiguration(configLocationStr); - } - for (final ConfigurationFactory factory : getFactories()) { - final String[] types = factory.getSupportedTypes(); - if (types != null) { - for (final String type : types) { - if (type.equals("*")) { - final Configuration config = factory.getConfiguration(name, configLocation); - if (config != null) { - return config; - } - } - } - } - } - } else { - // configLocation != null - final String configLocationStr = configLocation.toString(); - for (final ConfigurationFactory factory : getFactories()) { - final String[] types = factory.getSupportedTypes(); - if (types != null) { - for (final String type : types) { - if (type.equals("*") || configLocationStr.endsWith(type)) { - final Configuration config = factory.getConfiguration(name, configLocation); - if (config != null) { - return config; - } - } - } - } - } - } - - Configuration config = getConfiguration(true, name); - if (config == null) { - config = getConfiguration(true, null); - if (config == null) { - config = getConfiguration(false, name); - if (config == null) { - config = getConfiguration(false, null); - } - } - } - if (config != null) { - return config; - } - LOGGER.error("No log4j2 configuration file found. Using default configuration: logging only errors to the console."); - return new DefaultConfiguration(); - } - - private Configuration getConfiguration(final String configLocationStr) { - ConfigurationSource source = null; - try { - source = getInputFromUri(NetUtils.toURI(configLocationStr)); - } catch (final Exception ex) { - // Ignore the error and try as a String. - LOGGER.catching(Level.DEBUG, ex); - } - if (source == null) { - final ClassLoader loader = LoaderUtil.getThreadContextClassLoader(); - source = getInputFromString(configLocationStr, loader); - } - if (source != null) { - for (final ConfigurationFactory factory : getFactories()) { - final String[] types = factory.getSupportedTypes(); - if (types != null) { - for (final String type : types) { - if (type.equals("*") || configLocationStr.endsWith(type)) { - final Configuration config = factory.getConfiguration(source); - if (config != null) { - return config; - } - } - } - } - } - } - return null; - } - - private Configuration getConfiguration(final boolean isTest, final String name) { - final boolean named = Strings.isNotEmpty(name); - final ClassLoader loader = LoaderUtil.getThreadContextClassLoader(); - for (final ConfigurationFactory factory : getFactories()) { - String configName; - final String prefix = isTest ? TEST_PREFIX : DEFAULT_PREFIX; - final String [] types = factory.getSupportedTypes(); - if (types == null) { - continue; - } - - for (final String suffix : types) { - if (suffix.equals("*")) { - continue; - } - configName = named ? prefix + name + suffix : prefix + suffix; - - final ConfigurationSource source = getInputFromResource(configName, loader); - if (source != null) { - return factory.getConfiguration(source); - } - } - } - return null; - } - - @Override - public String[] getSupportedTypes() { - return null; - } - - @Override - public Configuration getConfiguration(final ConfigurationSource source) { - if (source != null) { - final String config = source.getLocation(); - for (final ConfigurationFactory factory : getFactories()) { - final String[] types = factory.getSupportedTypes(); - if (types != null) { - for (final String type : types) { - if (type.equals("*") || config != null && config.endsWith(type)) { - final Configuration c = factory.getConfiguration(source); - if (c != null) { - LOGGER.debug("Loaded configuration from {}", source); - return c; - } - LOGGER.error("Cannot determine the ConfigurationFactory to use for {}", config); - return null; - } - } - } - } - } - LOGGER.error("Cannot process configuration, input source is null"); - return null; - } - } - - static List<ConfigurationFactory> getFactories() { - return factories; - } -} +/* + * 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.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; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; + +import org.apache.logging.log4j.Level; +import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.core.config.builder.api.ConfigurationBuilderFactory; +import org.apache.logging.log4j.core.config.composite.CompositeConfiguration; +import org.apache.logging.log4j.core.config.plugins.util.PluginManager; +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; +import org.apache.logging.log4j.util.LoaderUtil; +import org.apache.logging.log4j.util.PropertiesUtil; +import org.apache.logging.log4j.util.Strings; + +/** + * Factory class for parsed {@link Configuration} objects from a configuration file. + * ConfigurationFactory allows the configuration implementation to be + * dynamically chosen in 1 of 3 ways: + * <ol> + * <li>A system property named "log4j.configurationFactory" can be set with the + * name of the ConfigurationFactory to be used.</li> + * <li> + * {@linkplain #setConfigurationFactory(ConfigurationFactory)} can be called + * with the instance of the ConfigurationFactory to be used. This must be called + * before any other calls to Log4j.</li> + * <li> + * A ConfigurationFactory implementation can be added to the classpath and configured as a plugin in the + * {@link #CATEGORY ConfigurationFactory} category. The {@link Order} annotation should be used to configure the + * factory to be the first one inspected. See + * {@linkplain org.apache.logging.log4j.core.config.xml.XmlConfigurationFactory} for an example.</li> + * </ol> + * + * If the ConfigurationFactory that was added returns null on a call to + * getConfiguration then any other ConfigurationFactories found as plugins will + * be called in their respective order. DefaultConfiguration is always called + * last if no configuration has been returned. + */ +public abstract class ConfigurationFactory extends ConfigurationBuilderFactory { + + /** + * Allows the ConfigurationFactory class to be specified as a system property. + */ + public static final String CONFIGURATION_FACTORY_PROPERTY = "log4j.configurationFactory"; + + /** + * Allows the location of the configuration file to be specified as a system property. + */ + public static final String CONFIGURATION_FILE_PROPERTY = "log4j.configurationFile"; + + /** + * Plugin category used to inject a ConfigurationFactory {@link org.apache.logging.log4j.core.config.plugins.Plugin} + * class. + * + * @since 2.1 + */ + public static final String CATEGORY = "ConfigurationFactory"; + + /** + * Allows subclasses access to the status logger without creating another instance. + */ + protected static final Logger LOGGER = StatusLogger.getLogger(); + + /** + * File name prefix for test configurations. + */ + protected static final String TEST_PREFIX = "log4j2-test"; + + /** + * File name prefix for standard configurations. + */ + protected static final String DEFAULT_PREFIX = "log4j2"; + + /** + * The name of the classloader URI scheme. + */ + private static final String CLASS_LOADER_SCHEME = "classloader"; + + /** + * The name of the classpath URI scheme, synonymous with the classloader URI scheme. + */ + private static final String CLASS_PATH_SCHEME = "classpath"; + + private static volatile List<ConfigurationFactory> factories = null; + + private static ConfigurationFactory configFactory = new Factory(); + + protected final StrSubstitutor substitutor = new StrSubstitutor(new Interpolator()); + + private static final Lock LOCK = new ReentrantLock(); + + /** + * Returns the ConfigurationFactory. + * @return the ConfigurationFactory. + */ + public static ConfigurationFactory getInstance() { + // volatile works in Java 1.6+, so double-checked locking also works properly + //noinspection DoubleCheckedLocking + if (factories == null) { + LOCK.lock(); + try { + if (factories == null) { + final List<ConfigurationFactory> list = new ArrayList<>(); + final String factoryClass = PropertiesUtil.getProperties().getStringProperty(CONFIGURATION_FACTORY_PROPERTY); + if (factoryClass != null) { + addFactory(list, factoryClass); + } + final PluginManager manager = new PluginManager(CATEGORY); + manager.collectPlugins(); + final Map<String, PluginType<?>> plugins = manager.getPlugins(); + final List<Class<? extends ConfigurationFactory>> ordered = new ArrayList<>(plugins.size()); + for (final PluginType<?> type : plugins.values()) { + try { + ordered.add(type.getPluginClass().asSubclass(ConfigurationFactory.class)); + } catch (final Exception ex) { + LOGGER.warn("Unable to add class {}", type.getPluginClass(), ex); + } + } + Collections.sort(ordered, OrderComparator.getInstance()); + for (final Class<? extends ConfigurationFactory> clazz : ordered) { + addFactory(list, clazz); + } + // see above comments about double-checked locking + //noinspection NonThreadSafeLazyInitialization + factories = Collections.unmodifiableList(list); + } + } finally { + LOCK.unlock(); + } + } + + LOGGER.debug("Using configurationFactory {}", configFactory); + return configFactory; + } + + private static void addFactory(final Collection<ConfigurationFactory> list, final String factoryClass) { + try { + addFactory(list, LoaderUtil.loadClass(factoryClass).asSubclass(ConfigurationFactory.class)); + } catch (final Exception ex) { + LOGGER.error("Unable to load class {}", factoryClass, ex); + } + } + + private static void addFactory(final Collection<ConfigurationFactory> list, + final Class<? extends ConfigurationFactory> factoryClass) { + try { + list.add(ReflectionUtil.instantiate(factoryClass)); + } catch (final Exception ex) { + LOGGER.error("Unable to create instance of {}", factoryClass.getName(), ex); + } + } + + /** + * Sets the configuration factory. This method is not intended for general use and may not be thread safe. + * @param factory the ConfigurationFactory. + */ + public static void setConfigurationFactory(final ConfigurationFactory factory) { + configFactory = factory; + } + + /** + * Resets the ConfigurationFactory to the default. This method is not intended for general use and may + * not be thread safe. + */ + public static void resetConfigurationFactory() { + configFactory = new Factory(); + } + + /** + * Removes the ConfigurationFactory. This method is not intended for general use and may not be thread safe. + * @param factory The factory to remove. + */ + public static void removeConfigurationFactory(final ConfigurationFactory factory) { + if (configFactory == factory) { + configFactory = new Factory(); + } + } + + protected abstract String[] getSupportedTypes(); + + protected boolean isActive() { + return true; + } + + public abstract Configuration getConfiguration(ConfigurationSource source); + + /** + * Returns the Configuration. + * @param name The configuration name. + * @param configLocation The configuration location. + * @return The Configuration. + */ + public Configuration getConfiguration(final String name, final URI configLocation) { + if (!isActive()) { + return null; + } + if (configLocation != null) { + final ConfigurationSource source = getInputFromUri(configLocation); + if (source != null) { + return getConfiguration(source); + } + } + return null; + } + + /** + * Returns the Configuration obtained using a given ClassLoader. + * + * @param name The configuration name. + * @param configLocation A URI representing the location of the configuration. + * @param loader The default ClassLoader to use. If this is {@code null}, then the + * {@linkplain LoaderUtil#getThreadContextClassLoader() default ClassLoader} will be used. + * @return The Configuration. + * @since 2.1 + */ + public Configuration getConfiguration(final String name, final URI configLocation, final ClassLoader loader) { + if (!isActive()) { + return null; + } + if (loader == null) { + return getConfiguration(name, configLocation); + } + if (isClassLoaderUri(configLocation)) { + final String path = extractClassLoaderUriPath(configLocation); + final ConfigurationSource source = getInputFromResource(path, loader); + if (source != null) { + final Configuration configuration = getConfiguration(source); + if (configuration != null) { + return configuration; + } + } + } + return getConfiguration(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) { + if (uri == null) { + return false; + } + final String scheme = uri.getScheme(); + return scheme == null || scheme.equals(CLASS_LOADER_SCHEME) || scheme.equals(CLASS_PATH_SCHEME); + } + + private static String extractClassLoaderUriPath(final URI uri) { + return uri.getScheme() == null ? uri.getPath() : uri.getSchemeSpecificPart(); + } + + /** + * Loads the configuration from the location represented by the String. + * @param config The configuration location. + * @param loader The default ClassLoader to use. + * @return The InputSource to use to read the configuration. + */ + protected ConfigurationSource getInputFromString(final String config, final ClassLoader loader) { + try { + 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); + if (source == null) { + try { + final File file = new File(config); + return new ConfigurationSource(new FileInputStream(file), file); + } catch (final FileNotFoundException fnfe) { + // Ignore the exception + LOGGER.catching(Level.DEBUG, fnfe); + } + } + return source; + } + } + + /** + * 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 { + + /** + * Default Factory Constructor. + * @param name The configuration name. + * @param configLocation The configuration location. + * @return The Configuration. + */ + @Override + public Configuration getConfiguration(final String name, final URI configLocation) { + + if (configLocation == null) { + final String configLocationStr = this.substitutor.replace(PropertiesUtil.getProperties() + .getStringProperty(CONFIGURATION_FILE_PROPERTY)); + if (configLocationStr != null) { + final String[] sources = configLocationStr.split(","); + if (sources.length > 1) { + final List<AbstractConfiguration> configs = new ArrayList<>(); + for (final String sourceLocation : sources) { + final Configuration config = getConfiguration(sourceLocation.trim()); + if (config != null && config instanceof AbstractConfiguration) { + configs.add((AbstractConfiguration) config); + } else { + LOGGER.error("Failed to created configuration at {}", sourceLocation); + return null; + } + } + return new CompositeConfiguration(configs); + } + return getConfiguration(configLocationStr); + } + for (final ConfigurationFactory factory : getFactories()) { + final String[] types = factory.getSupportedTypes(); + if (types != null) { + for (final String type : types) { + if (type.equals("*")) { + final Configuration config = factory.getConfiguration(name, configLocation); + if (config != null) { + return config; + } + } + } + } + } + } else { + // configLocation != null + final String configLocationStr = configLocation.toString(); + for (final ConfigurationFactory factory : getFactories()) { + final String[] types = factory.getSupportedTypes(); + if (types != null) { + for (final String type : types) { + if (type.equals("*") || configLocationStr.endsWith(type)) { + final Configuration config = factory.getConfiguration(name, configLocation); + if (config != null) { + return config; + } + } + } + } + } + } + + Configuration config = getConfiguration(true, name); + if (config == null) { + config = getConfiguration(true, null); + if (config == null) { + config = getConfiguration(false, name); + if (config == null) { + config = getConfiguration(false, null); + } + } + } + if (config != null) { + return config; + } + LOGGER.error("No log4j2 configuration file found. Using default configuration: logging only errors to the console."); + return new DefaultConfiguration(); + } + + private Configuration getConfiguration(final String configLocationStr) { + ConfigurationSource source = null; + try { + source = getInputFromUri(NetUtils.toURI(configLocationStr)); + } catch (final Exception ex) { + // Ignore the error and try as a String. + LOGGER.catching(Level.DEBUG, ex); + } + if (source == null) { + final ClassLoader loader = LoaderUtil.getThreadContextClassLoader(); + source = getInputFromString(configLocationStr, loader); + } + if (source != null) { + for (final ConfigurationFactory factory : getFactories()) { + final String[] types = factory.getSupportedTypes(); + if (types != null) { + for (final String type : types) { + if (type.equals("*") || configLocationStr.endsWith(type)) { + final Configuration config = factory.getConfiguration(source); + if (config != null) { + return config; + } + } + } + } + } + } + return null; + } + + private Configuration getConfiguration(final boolean isTest, final String name) { + final boolean named = Strings.isNotEmpty(name); + final ClassLoader loader = LoaderUtil.getThreadContextClassLoader(); + for (final ConfigurationFactory factory : getFactories()) { + String configName; + final String prefix = isTest ? TEST_PREFIX : DEFAULT_PREFIX; + final String [] types = factory.getSupportedTypes(); + if (types == null) { + continue; + } + + for (final String suffix : types) { + if (suffix.equals("*")) { + continue; + } + configName = named ? prefix + name + suffix : prefix + suffix; + + final ConfigurationSource source = getInputFromResource(configName, loader); + if (source != null) { + return factory.getConfiguration(source); + } + } + } + return null; + } + + @Override + public String[] getSupportedTypes() { + return null; + } + + @Override + public Configuration getConfiguration(final ConfigurationSource source) { + if (source != null) { + final String config = source.getLocation(); + for (final ConfigurationFactory factory : getFactories()) { + final String[] types = factory.getSupportedTypes(); + if (types != null) { + for (final String type : types) { + if (type.equals("*") || config != null && config.endsWith(type)) { + final Configuration c = factory.getConfiguration(source); + if (c != null) { + LOGGER.debug("Loaded configuration from {}", source); + return c; + } + LOGGER.error("Cannot determine the ConfigurationFactory to use for {}", config); + return null; + } + } + } + } + } + LOGGER.error("Cannot process configuration, input source is null"); + return null; + } + } + + static List<ConfigurationFactory> getFactories() { + return factories; + } +}
