You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@logging.apache.org by rg...@apache.org on 2016/04/24 08:33:11 UTC

logging-log4j2 git commit: LOG4J2-494 working CompositeConfiguration

Repository: logging-log4j2
Updated Branches:
  refs/heads/LOG4j2-494 [created] bb64f4577


LOG4J2-494 working CompositeConfiguration


Project: http://git-wip-us.apache.org/repos/asf/logging-log4j2/repo
Commit: http://git-wip-us.apache.org/repos/asf/logging-log4j2/commit/bb64f457
Tree: http://git-wip-us.apache.org/repos/asf/logging-log4j2/tree/bb64f457
Diff: http://git-wip-us.apache.org/repos/asf/logging-log4j2/diff/bb64f457

Branch: refs/heads/LOG4j2-494
Commit: bb64f457730864b70e3b932c2156c5522245d909
Parents: 4195c7b
Author: Ralph Goers <rg...@nextiva.com>
Authored: Sat Apr 23 23:29:57 2016 -0700
Committer: Ralph Goers <rg...@nextiva.com>
Committed: Sat Apr 23 23:29:57 2016 -0700

----------------------------------------------------------------------
 .../apache/logging/log4j/util/LoaderUtil.java   |   8 +-
 log4j-core/${filename}                          |   0
 .../core/config/AbstractConfiguration.java      |  26 +-
 .../log4j/core/config/ConfigurationFactory.java |  66 +++--
 .../log4j/core/config/ConfigurationSource.java  |  51 +++-
 .../core/config/ConfiguratonFileWatcher.java    |   4 +
 .../logging/log4j/core/config/Configurator.java |  46 +++-
 .../composite/CompositeConfiguration.java       | 168 +++++++++++++
 .../config/composite/DefaultMergeStrategy.java  | 252 +++++++++++++++++++
 .../core/config/composite/MergeStrategy.java    |  33 +++
 .../core/config/composite/package-info.java     |  21 ++
 .../log4j/core/impl/Log4jContextFactory.java    |  43 +++-
 .../logging/log4j/core/util/WatchManager.java   |   9 +
 .../core/config/CompositeConfigurationTest.java | 149 +++++++++++
 .../src/test/resources/log4j-comp-appender.json |  34 +++
 .../src/test/resources/log4j-comp-appender.xml  |  39 +++
 .../src/test/resources/log4j-comp-filter.json   |   9 +
 .../src/test/resources/log4j-comp-filter.xml    |  34 +++
 .../src/test/resources/log4j-comp-logger.json   |  36 +++
 .../src/test/resources/log4j-comp-logger.xml    |  41 +++
 .../test/resources/log4j-comp-properties.json   |  16 ++
 .../test/resources/log4j-comp-properties.xml    |  34 +++
 22 files changed, 1072 insertions(+), 47 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/bb64f457/log4j-api/src/main/java/org/apache/logging/log4j/util/LoaderUtil.java
----------------------------------------------------------------------
diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/util/LoaderUtil.java b/log4j-api/src/main/java/org/apache/logging/log4j/util/LoaderUtil.java
index 096d945..c2d55a2 100644
--- a/log4j-api/src/main/java/org/apache/logging/log4j/util/LoaderUtil.java
+++ b/log4j-api/src/main/java/org/apache/logging/log4j/util/LoaderUtil.java
@@ -28,7 +28,7 @@ import java.util.Objects;
 
 /**
  * <em>Consider this class private.</em> Utility class for ClassLoaders.
- * 
+ *
  * @see ClassLoader
  * @see RuntimePermission
  * @see Thread#getContextClassLoader()
@@ -135,14 +135,14 @@ public final class LoaderUtil {
      * @throws InvocationTargetException if there was an exception whilst constructing the class
      * @since 2.1
      */
-    public static Object newInstanceOf(final String className) throws ClassNotFoundException, IllegalAccessException,
+    public static <T> T newInstanceOf(final String className) throws ClassNotFoundException, IllegalAccessException,
             InstantiationException, NoSuchMethodException, InvocationTargetException {
         final Class<?> clazz = loadClass(className);
         try {
-            return clazz.getConstructor().newInstance();
+            return (T) clazz.getConstructor().newInstance();
         } catch (final NoSuchMethodException ignored) {
             // FIXME: looking at the code for Class.newInstance(), this seems to do the same thing as above
-            return clazz.newInstance();
+            return (T) clazz.newInstance();
         }
     }
 

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/bb64f457/log4j-core/${filename}
----------------------------------------------------------------------
diff --git a/log4j-core/${filename} b/log4j-core/${filename}
new file mode 100644
index 0000000..e69de29

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/bb64f457/log4j-core/src/main/java/org/apache/logging/log4j/core/config/AbstractConfiguration.java
----------------------------------------------------------------------
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/AbstractConfiguration.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/AbstractConfiguration.java
index 00cd397..316381a 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/AbstractConfiguration.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/AbstractConfiguration.java
@@ -98,6 +98,11 @@ public abstract class AbstractConfiguration extends AbstractFilterable implement
     protected boolean isShutdownHookEnabled = true;
 
     /**
+     * The Script manager.
+     */
+    protected ScriptManager scriptManager;
+
+    /**
      * The Advertiser which exposes appender configurations to external systems.
      */
     private Advertiser advertiser = new DefaultAdvertiser();
@@ -113,7 +118,6 @@ public abstract class AbstractConfiguration extends AbstractFilterable implement
     private LoggerConfig root = new LoggerConfig();
     private final ConcurrentMap<String, Object> componentMap = new ConcurrentHashMap<>();
     private final ConfigurationSource configurationSource;
-    private ScriptManager scriptManager;
     private final ConfigurationScheduler configurationScheduler = new ConfigurationScheduler();
     private final WatchManager watchManager = new WatchManager(configurationScheduler);
     private AsyncLoggerConfigDisruptor asyncLoggerConfigDisruptor;
@@ -151,6 +155,18 @@ public abstract class AbstractConfiguration extends AbstractFilterable implement
         return scriptManager;
     }
 
+    public void setScriptManager(ScriptManager scriptManager) {
+        this.scriptManager = scriptManager;
+    }
+
+    public PluginManager getPluginManager() {
+        return pluginManager;
+    }
+
+    public void setPluginManager(PluginManager pluginManager) {
+        this.pluginManager = pluginManager;
+    }
+
     @Override
     public WatchManager getWatchManager() {
         return watchManager;
@@ -161,7 +177,11 @@ public abstract class AbstractConfiguration extends AbstractFilterable implement
         return configurationScheduler;
     }
 
-	@Override
+    public Node getRootNode() {
+        return rootNode;
+    }
+
+    @Override
 	public AsyncLoggerConfigDelegate getAsyncLoggerConfigDelegate() {
 	    // lazily instantiate only when requested by AsyncLoggers:
 	    // loading AsyncLoggerConfigDisruptor requires LMAX Disruptor jar on classpath
@@ -357,7 +377,7 @@ public abstract class AbstractConfiguration extends AbstractFilterable implement
         return isShutdownHookEnabled;
     }
 
-    protected void setup() {
+    public void setup() {
     }
 
     protected Level getDefaultStatus() {

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/bb64f457/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 8f3d764..41f3571 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
@@ -36,6 +36,7 @@ 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;
@@ -392,32 +393,21 @@ public abstract class ConfigurationFactory extends ConfigurationBuilderFactory {
                 final String configLocationStr = this.substitutor.replace(PropertiesUtil.getProperties()
                         .getStringProperty(CONFIGURATION_FILE_PROPERTY));
                 if (configLocationStr != null) {
-                    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;
-                                        }
-                                    }
-                                }
+                    String[] sources = configLocationStr.split(",");
+                    if (sources.length > 1) {
+                        List<AbstractConfiguration> configs = new ArrayList<>();
+                        for (String sourceLocation : sources) {
+                            Configuration config = getConfiguration(sourceLocation);
+                            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);
                 } else {
                     for (final ConfigurationFactory factory : getFactories()) {
                         final String[] types = factory.getSupportedTypes();
@@ -468,6 +458,36 @@ public abstract class ConfigurationFactory extends ConfigurationBuilderFactory {
             return new DefaultConfiguration();
         }
 
+        private Configuration getConfiguration(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();

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/bb64f457/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 edb2d75..f1627bb 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
@@ -23,6 +23,8 @@ import java.io.File;
 import java.io.FileInputStream;
 import java.io.IOException;
 import java.io.InputStream;
+import java.net.URI;
+import java.net.URISyntaxException;
 import java.net.URL;
 import java.util.Objects;
 
@@ -44,7 +46,7 @@ public class ConfigurationSource {
     /**
      * Constructs a new {@code ConfigurationSource} with the specified input stream that originated from the specified
      * file.
-     * 
+     *
      * @param stream the input stream
      * @param file the file where the input stream originated
      */
@@ -59,7 +61,7 @@ public class ConfigurationSource {
     /**
      * Constructs a new {@code ConfigurationSource} with the specified input stream that originated from the specified
      * url.
-     * 
+     *
      * @param stream the input stream
      * @param url the URL where the input stream originated
      */
@@ -74,7 +76,7 @@ public class ConfigurationSource {
     /**
      * Constructs a new {@code ConfigurationSource} with the specified input stream. Since the stream is the only source
      * of data, this constructor makes a copy of the stream contents.
-     * 
+     *
      * @param stream the input stream
      * @throws IOException if an exception occurred reading from the specified stream
      */
@@ -92,7 +94,7 @@ public class ConfigurationSource {
 
     /**
      * Returns the contents of the specified {@code InputStream} as a byte array.
-     * 
+     *
      * @param inputStream the stream to read
      * @return the contents of the specified stream
      * @throws IOException if a problem occurred reading from the stream
@@ -113,7 +115,7 @@ public class ConfigurationSource {
     /**
      * Returns the file configuration source, or {@code null} if this configuration source is based on an URL or has
      * neither a file nor an URL.
-     * 
+     *
      * @return the configuration source file, or {@code null}
      */
     public File getFile() {
@@ -123,7 +125,7 @@ public class ConfigurationSource {
     /**
      * Returns the configuration source URL, or {@code null} if this configuration source is based on a file or has
      * neither a file nor an URL.
-     * 
+     *
      * @return the configuration source URL, or {@code null}
      */
     public URL getURL() {
@@ -131,9 +133,40 @@ public class ConfigurationSource {
     }
 
     /**
+     * Returns a URI representing the configuration resource or null if it cannot be determined.
+     * @return The URI.
+     */
+    public URI getURI() {
+        URI sourceURI = null;
+        if (url != null) {
+            try {
+                sourceURI = url.toURI();
+            } catch (URISyntaxException ex) {
+                    /* Ignore the exception */
+            }
+        }
+        if (sourceURI == null && file != null) {
+            sourceURI = file.toURI();
+        }
+        if (sourceURI == null && location != null) {
+            try {
+                sourceURI = new URI(location);
+            } catch (URISyntaxException ex) {
+                // Assume the scheme was missing.
+                try {
+                    sourceURI = new URI("file://" + location);
+                } catch (URISyntaxException uriEx) {
+                    /* Ignore the exception */
+                }
+            }
+        }
+        return sourceURI;
+    }
+
+    /**
      * Returns a string describing the configuration source file or URL, or {@code null} if this configuration source
      * has neither a file nor an URL.
-     * 
+     *
      * @return a string describing the configuration source file or URL, or {@code null}
      */
     public String getLocation() {
@@ -142,7 +175,7 @@ public class ConfigurationSource {
 
     /**
      * Returns the input stream that this configuration source was constructed with.
-     * 
+     *
      * @return the input stream that this configuration source was constructed with.
      */
     public InputStream getInputStream() {
@@ -151,7 +184,7 @@ public class ConfigurationSource {
 
     /**
      * Returns a new {@code ConfigurationSource} whose input stream is reset to the beginning.
-     * 
+     *
      * @return a new {@code ConfigurationSource}
      * @throws IOException if a problem occurred while opening the new input stream
      */

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/bb64f457/log4j-core/src/main/java/org/apache/logging/log4j/core/config/ConfiguratonFileWatcher.java
----------------------------------------------------------------------
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/ConfiguratonFileWatcher.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/ConfiguratonFileWatcher.java
index e268c54..82d06ab 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/ConfiguratonFileWatcher.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/ConfiguratonFileWatcher.java
@@ -35,6 +35,10 @@ public class ConfiguratonFileWatcher implements FileWatcher {
         this.listeners = listeners;
     }
 
+    public List<ConfigurationListener> getListeners() {
+        return listeners;
+    }
+
 
     @Override
     public void fileModified(File file) {

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/bb64f457/log4j-core/src/main/java/org/apache/logging/log4j/core/config/Configurator.java
----------------------------------------------------------------------
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/Configurator.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/Configurator.java
index fe6a5d5..f0e462e 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/Configurator.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/Configurator.java
@@ -17,6 +17,8 @@
 package org.apache.logging.log4j.core.config;
 
 import java.net.URI;
+import java.util.ArrayList;
+import java.util.List;
 import java.util.Map;
 
 import org.apache.logging.log4j.Level;
@@ -109,8 +111,24 @@ public final class Configurator {
      */
     public static LoggerContext initialize(final String name, final ClassLoader loader, final String configLocation,
             final Object externalContext) {
-        final URI uri = Strings.isBlank(configLocation) ? null : NetUtils.toURI(configLocation);
-        return initialize(name, loader, uri, externalContext);
+        if (Strings.isBlank(configLocation)) {
+            return initialize(name, loader, (URI) null, externalContext);
+        }
+        if (configLocation.contains(",")) {
+            String[] parts = configLocation.split(",");
+            String scheme = null;
+            List<URI> uris = new ArrayList<>(parts.length);
+            for (String part : parts) {
+                URI uri = NetUtils.toURI(scheme != null ? scheme + ":" + part : part);
+                if (scheme == null && uri.getScheme() != null) {
+                    scheme = uri.getScheme();
+                }
+                uris.add(uri);
+            }
+            return initialize(name, loader, uris, externalContext);
+        } else {
+            return initialize(name, loader, NetUtils.toURI(configLocation), externalContext);
+        }
     }
 
     /**
@@ -146,6 +164,20 @@ public final class Configurator {
         return null;
     }
 
+    public static LoggerContext initialize(final String name, final ClassLoader loader, final List<URI> configLocations,
+            final Object externalContext) {
+        try {
+            final Log4jContextFactory factory = getFactory();
+            return factory == null ?
+                    null :
+                    factory.getContext(FQCN, loader, externalContext, false, configLocations, name);
+        } catch (final Exception ex) {
+            LOGGER.error("There was a problem initializing the LoggerContext [{}] using configurations at [{}].", name,
+                    configLocations, ex);
+        }
+        return null;
+    }
+
     /**
      * Initializes the Logging Context.
      * @param name The Context name.
@@ -202,7 +234,7 @@ public final class Configurator {
     public static void setAllLevels(final String parentLogger, final Level level) {
         // 1) get logger config
         // 2) if exact match, use it, if not, create it.
-        // 3) set level on logger config 
+        // 3) set level on logger config
         // 4) update child logger configs with level
         // 5) update loggers
         final LoggerContext loggerContext = LoggerContext.getContext(false);
@@ -225,10 +257,10 @@ public final class Configurator {
         }
         return set;
     }
-    
+
     /**
      * Sets logger levels.
-     * 
+     *
      * @param levelMap
      *            a levelMap where keys are level names and values are new
      *            Levels.
@@ -249,7 +281,7 @@ public final class Configurator {
 
     /**
      * Sets a logger's level.
-     * 
+     *
      * @param loggerName
      *            the logger name
      * @param level
@@ -283,7 +315,7 @@ public final class Configurator {
 
     /**
      * Sets the root logger's level.
-     * 
+     *
      * @param level
      *            the new level
      */

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/bb64f457/log4j-core/src/main/java/org/apache/logging/log4j/core/config/composite/CompositeConfiguration.java
----------------------------------------------------------------------
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/composite/CompositeConfiguration.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/composite/CompositeConfiguration.java
new file mode 100644
index 0000000..8b416b3
--- /dev/null
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/composite/CompositeConfiguration.java
@@ -0,0 +1,168 @@
+/*
+ * 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.composite;
+
+import java.io.File;
+import java.lang.reflect.InvocationTargetException;
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.logging.log4j.Level;
+import org.apache.logging.log4j.core.config.AbstractConfiguration;
+import org.apache.logging.log4j.core.config.Configuration;
+import org.apache.logging.log4j.core.config.ConfigurationFactory;
+import org.apache.logging.log4j.core.config.ConfigurationSource;
+import org.apache.logging.log4j.core.config.ConfiguratonFileWatcher;
+import org.apache.logging.log4j.core.config.Node;
+import org.apache.logging.log4j.core.config.Reconfigurable;
+import org.apache.logging.log4j.core.config.plugins.util.ResolverUtil;
+import org.apache.logging.log4j.core.config.status.StatusConfiguration;
+import org.apache.logging.log4j.core.util.FileWatcher;
+import org.apache.logging.log4j.core.util.Patterns;
+import org.apache.logging.log4j.core.util.WatchManager;
+import org.apache.logging.log4j.util.LoaderUtil;
+import org.apache.logging.log4j.util.PropertiesUtil;
+
+/**
+ * A Composite Configuration.
+ */
+public class CompositeConfiguration extends AbstractConfiguration implements Reconfigurable {
+
+    /**
+     * Allow the ConfigurationFactory class to be specified as a system property.
+     */
+    public static final String MERGE_STRATEGY_PROPERTY = "log4j.mergeStrategy";
+
+    private static final String[] VERBOSE_CLASSES = new String[] {ResolverUtil.class.getName()};
+
+    private List<? extends AbstractConfiguration> configurations;
+
+    private MergeStrategy mergeStrategy;
+
+    /**
+     * Construct the ComponsiteConfiguration.
+     *
+     * @param configurations The List of Configurations to merge.
+     */
+    public CompositeConfiguration(List<? extends AbstractConfiguration> configurations) {
+        super(ConfigurationSource.NULL_SOURCE);
+        this.configurations = configurations;
+        String mergeStrategyClassName = PropertiesUtil.getProperties().getStringProperty(MERGE_STRATEGY_PROPERTY,
+                DefaultMergeStrategy.class.getName());
+        try {
+            mergeStrategy = LoaderUtil.newInstanceOf(mergeStrategyClassName);
+        } catch (ClassNotFoundException | IllegalAccessException | NoSuchMethodException | InvocationTargetException |
+                InstantiationException ex) {
+            mergeStrategy = new DefaultMergeStrategy();
+        }
+    }
+
+    @Override
+    public void setup() {
+        AbstractConfiguration targetConfiguration = configurations.get(0);
+        staffChildConfiguration(targetConfiguration);
+        WatchManager watchManager = targetConfiguration.getWatchManager();
+        rootNode = targetConfiguration.getRootNode();
+        for (AbstractConfiguration sourceConfiguration : configurations.subList(1, configurations.size())) {
+            staffChildConfiguration(sourceConfiguration);
+            Node sourceRoot = sourceConfiguration.getRootNode();
+            mergeStrategy.mergConfigurations(rootNode, sourceRoot, getPluginManager());
+            if (LOGGER.isEnabled(Level.ALL)) {
+                StringBuilder sb = new StringBuilder();
+                printNodes("", rootNode, sb);
+                System.out.println(sb.toString());
+            }
+            int monitorInterval = sourceConfiguration.getWatchManager().getIntervalSeconds();
+            if (monitorInterval > 0) {
+                if (monitorInterval < watchManager.getIntervalSeconds()) {
+                    watchManager.setIntervalSeconds(monitorInterval);
+                }
+                WatchManager sourceWatchManager = sourceConfiguration.getWatchManager();
+                Map<File, FileWatcher> watchers = sourceWatchManager.getWatchers();
+                FileWatcher fileWatcher = new ConfiguratonFileWatcher(this, listeners);
+                for (Map.Entry<File, FileWatcher> entry : watchers.entrySet()) {
+                    if (entry.getValue() instanceof ConfiguratonFileWatcher) {
+                        watchManager.watchFile(entry.getKey(), fileWatcher);
+                    }
+                }
+            }
+        }
+        final StatusConfiguration statusConfig = new StatusConfiguration().withVerboseClasses(VERBOSE_CLASSES)
+                .withStatus(getDefaultStatus());
+        for (final Map.Entry<String, String> entry : rootNode.getAttributes().entrySet()) {
+            final String key = entry.getKey();
+            final String value = getStrSubstitutor().replace(entry.getValue());
+            if ("status".equalsIgnoreCase(key)) {
+                statusConfig.withStatus(value);
+            } else if ("dest".equalsIgnoreCase(key)) {
+                statusConfig.withDestination(value);
+            } else if ("shutdownHook".equalsIgnoreCase(key)) {
+                isShutdownHookEnabled = !"disable".equalsIgnoreCase(value);
+            } else if ("verbose".equalsIgnoreCase(key)) {
+                statusConfig.withVerbosity(value);
+            } else if ("packages".equalsIgnoreCase(key)) {
+                pluginPackages.addAll(Arrays.asList(value.split(Patterns.COMMA_SEPARATOR)));
+            } else if ("name".equalsIgnoreCase(key)) {
+                setName(value);
+            }
+        }
+        statusConfig.initialize();
+    }
+
+    @Override
+    public Configuration reconfigure() {
+        List<AbstractConfiguration> configs = new ArrayList<>();
+        ConfigurationFactory factory = ConfigurationFactory.getInstance();
+        for (AbstractConfiguration config : configurations) {
+            ConfigurationSource source = config.getConfigurationSource();
+            URI sourceURI = source.getURI();
+            Configuration currentConfig;
+            if (sourceURI != null) {
+                LOGGER.warn("Unable to determine URI for configuration {}, changes to it will be ignored",
+                        config.getName());
+                currentConfig = factory.getConfiguration(config.getName(), sourceURI);
+                if (currentConfig == null) {
+                    LOGGER.warn("Unable to reload configuration {}, changes to it will be ignored", config.getName());
+                    currentConfig = config;
+                }
+            } else {
+                currentConfig = config;
+            }
+            configs.add((AbstractConfiguration) currentConfig);
+
+        }
+
+        return new CompositeConfiguration(configs);
+    }
+
+    private void staffChildConfiguration(AbstractConfiguration childConfiguration) {
+        childConfiguration.setPluginManager(pluginManager);
+        childConfiguration.setScriptManager(scriptManager);
+        childConfiguration.setup();
+    }
+
+    private void printNodes(String indent, Node node, StringBuilder sb) {
+        sb.append(indent).append(node.getName()).append(" type: ").append(node.getType()).append("\n");
+        sb.append(indent).append(node.getAttributes().toString()).append("\n");
+        for (Node child : node.getChildren()) {
+            printNodes(indent + "  ", child, sb);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/bb64f457/log4j-core/src/main/java/org/apache/logging/log4j/core/config/composite/DefaultMergeStrategy.java
----------------------------------------------------------------------
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/composite/DefaultMergeStrategy.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/composite/DefaultMergeStrategy.java
new file mode 100644
index 0000000..7b123a0
--- /dev/null
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/composite/DefaultMergeStrategy.java
@@ -0,0 +1,252 @@
+/*
+ * 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.composite;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.logging.log4j.Level;
+import org.apache.logging.log4j.core.Filter;
+import org.apache.logging.log4j.core.config.Node;
+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.filter.CompositeFilter;
+
+/**
+ * The default merge strategy for composite configurations.
+ * <p>
+ * The default merge strategy performs the merge according to the following rules:
+ * <ol>
+ * <li>Aggregates the global configuration attributes with those in later configurations replacing those in previous
+ * configurations with the exception that the highest status level and the lowest monitorInterval greater than 0 will
+ * be used.</li>
+ * <li>Properties from all configurations are aggregated. Duplicate properties replace those in previous
+ * configurations.</li>
+ * <li>Filters are aggregated under a CompositeFilter if more than one Filter is defined. Since Filters are not named
+ * duplicates may be present.</li>
+ * <li>Scripts and ScriptFile references are aggregated. Duplicate definiations replace those in previous
+ * configurations.</li>
+ * <li>Appenders are aggregated. Appenders with the same name are replaced by those in later configurations, including
+ * all of the Appender's subcomponents.</li>
+ * <li>Loggers are all aggregated. Logger attributes are individually merged with duplicates being replaced by those
+ * in later configurations. Appender references on a Logger are aggregated with duplicates being replaced by those in
+ * later configurations. Filters on a Logger are aggregated under a CompositeFilter if more than one Filter is defined.
+ * Since Filters are not named duplicates may be present. Filters under Appender references included or discarded
+ * depending on whether their parent Appender reference is kept or discarded.</li>
+ * </ol>
+ */
+public class DefaultMergeStrategy implements MergeStrategy {
+
+    private static final String APPENDERS = "appenders";
+    private static final String PROPERTIES = "properties";
+    private static final String LOGGERS = "loggers";
+    private static final String SCRIPTS = "scripts";
+    private static final String FILTERS = "filters";
+    private static final String STATUS = "status";
+    private static final String NAME = "name";
+    private static final String REF = "ref";
+
+    /**
+     * Merge the source Configuration into the target Configuration.
+     *
+     * @param target        The target node to merge into.
+     * @param source        The source node.
+     * @param pluginManager The PluginManager.
+     */
+    public void mergConfigurations(Node target, Node source, PluginManager pluginManager) {
+        for (Map.Entry<String, String> attribute : source.getAttributes().entrySet()) {
+            boolean isFound = false;
+            for (Map.Entry<String, String> targetAttribute : target.getAttributes().entrySet()) {
+                if (targetAttribute.getKey().equalsIgnoreCase(attribute.getKey())) {
+                    if (attribute.getKey().equalsIgnoreCase(STATUS)) {
+                        Level targetLevel = Level.getLevel(targetAttribute.getValue());
+                        Level sourceLevel = Level.getLevel(attribute.getValue());
+                        if (targetLevel != null && sourceLevel != null) {
+                            if (sourceLevel.isLessSpecificThan(targetLevel)) {
+                                targetAttribute.setValue(attribute.getValue());
+                            }
+                        }
+                    } else {
+                        if (attribute.getKey().equalsIgnoreCase("monitorInterval")) {
+                            int sourceInterval = Integer.parseInt(attribute.getValue());
+                            int targetInterval = Integer.parseInt(targetAttribute.getValue());
+                            if (targetInterval == 0 || sourceInterval < targetInterval) {
+                                targetAttribute.setValue(attribute.getValue());
+                            }
+                        } else {
+                            targetAttribute.setValue(attribute.getValue());
+                        }
+                    }
+                    isFound = true;
+                }
+            }
+            if (!isFound) {
+                target.getAttributes().put(attribute.getKey(), attribute.getValue());
+            }
+        }
+        for (Node sourceChildNode : source.getChildren()) {
+            boolean isFilter = isFilterNode(sourceChildNode);
+            boolean isMerged = false;
+            for (Node targetChildNode : target.getChildren()) {
+                if (isFilter) {
+                    if (isFilterNode(targetChildNode)) {
+                        updateFilterNode(target, targetChildNode, sourceChildNode, pluginManager);
+                        isMerged = true;
+                        break;
+                    } else {
+                        continue;
+                    }
+                }
+
+                if (!targetChildNode.getName().equalsIgnoreCase(sourceChildNode.getName())) {
+                    continue;
+                }
+
+                switch (targetChildNode.getName().toLowerCase()) {
+                    case PROPERTIES:
+                    case SCRIPTS:
+                    case APPENDERS: {
+                        for (Node node : sourceChildNode.getChildren()) {
+                            for (Node targetNode : targetChildNode.getChildren()) {
+                                if (targetNode.getAttributes().get(NAME).equals(node.getAttributes().get(NAME))) {
+                                    targetChildNode.getChildren().remove(targetNode);
+                                    break;
+                                }
+                            }
+                            targetChildNode.getChildren().add(node);
+                        }
+                        isMerged = true;
+                        break;
+                    }
+                    case LOGGERS: {
+                        Map<String, Node> targetLoggers = new HashMap<>();
+                        for (Node node : targetChildNode.getChildren()) {
+                            targetLoggers.put(node.getName(), node);
+                        }
+                        for (Node node : sourceChildNode.getChildren()) {
+                            Node targetNode = getLoggerNode(targetChildNode, node.getAttributes().get(NAME));
+                            Node loggerNode = new Node(targetChildNode, node.getName(), node.getType());
+                            if (targetNode != null) {
+                                for (Node sourceLoggerChild : node.getChildren()) {
+                                    if (isFilterNode(sourceLoggerChild)) {
+                                        boolean foundFilter = false;
+                                        for (Node targetChild : targetNode.getChildren()) {
+                                            if (isFilterNode(targetChild)) {
+                                                updateFilterNode(loggerNode, targetChild, sourceLoggerChild,
+                                                        pluginManager);
+                                                foundFilter = true;
+                                                break;
+                                            }
+                                        }
+                                        if (!foundFilter) {
+                                            Node childNode = new Node(loggerNode, sourceLoggerChild.getName(),
+                                                    sourceLoggerChild.getType());
+                                            targetNode.getChildren().add(childNode);
+                                        }
+                                    } else {
+                                        Node childNode = new Node(loggerNode, sourceLoggerChild.getName(),
+                                                sourceLoggerChild.getType());
+                                        childNode.getAttributes().putAll(sourceLoggerChild.getAttributes());
+                                        if (childNode.getName().equalsIgnoreCase("AppenderRef")) {
+                                            for (Node targetChild : targetNode.getChildren()) {
+                                                if (isSameReference(targetChild, childNode)) {
+                                                    targetNode.getChildren().remove(targetChild);
+                                                    break;
+                                                }
+                                            }
+                                        } else {
+                                            for (Node targetChild : targetNode.getChildren()) {
+                                                if (isSameName(targetChild, childNode)) {
+                                                    targetNode.getChildren().remove(targetChild);
+                                                    break;
+                                                }
+                                            }
+                                        }
+
+                                        targetNode.getChildren().add(childNode);
+                                    }
+                                }
+                            } else {
+                                loggerNode.getAttributes().putAll(node.getAttributes());
+                                loggerNode.getChildren().addAll(node.getChildren());
+                                targetChildNode.getChildren().add(loggerNode);
+                            }
+                        }
+                        isMerged = true;
+                        break;
+                    }
+                    default: {
+                        targetChildNode.getChildren().addAll(sourceChildNode.getChildren());
+                        isMerged = true;
+                        break;
+                    }
+
+                }
+            }
+            if (!isMerged) {
+                target.getChildren().add(sourceChildNode);
+            }
+        }
+    }
+
+    private Node getLoggerNode(Node parentNode, String name) {
+        for (Node node : parentNode.getChildren()) {
+            String nodeName = node.getAttributes().get(NAME);
+            if (name == null && nodeName == null) {
+                return node;
+            }
+            if (nodeName != null && nodeName.equals(name)) {
+                return node;
+            }
+        }
+        return null;
+    }
+
+    private void updateFilterNode(Node target, Node targetChildNode, Node sourceChildNode,
+            PluginManager pluginManager) {
+        if (CompositeFilter.class.isAssignableFrom(targetChildNode.getType().getPluginClass())) {
+            Node node = new Node(targetChildNode, sourceChildNode.getName(), sourceChildNode.getType());
+            node.getChildren().addAll(sourceChildNode.getChildren());
+            node.getAttributes().putAll(sourceChildNode.getAttributes());
+            targetChildNode.getChildren().add(node);
+        } else {
+            PluginType pluginType = pluginManager.getPluginType(FILTERS);
+            Node filtersNode = new Node(targetChildNode, FILTERS, pluginType);
+            Node node = new Node(filtersNode, sourceChildNode.getName(), sourceChildNode.getType());
+            node.getAttributes().putAll(sourceChildNode.getAttributes());
+            List<Node> children = filtersNode.getChildren();
+            children.add(targetChildNode);
+            children.add(node);
+            List<Node> nodes = target.getChildren();
+            nodes.remove(targetChildNode);
+            nodes.add(filtersNode);
+        }
+    }
+
+    private boolean isFilterNode(Node node) {
+        return Filter.class.isAssignableFrom(node.getType().getPluginClass());
+    }
+
+    private boolean isSameName(Node node1, Node node2) {
+        return node1.getAttributes().get(NAME).toLowerCase().equals(node2.getAttributes().get(NAME).toLowerCase());
+    }
+
+    private boolean isSameReference(Node node1, Node node2) {
+        return node1.getAttributes().get(REF).toLowerCase().equals(node2.getAttributes().get(REF).toLowerCase());
+    }
+}

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/bb64f457/log4j-core/src/main/java/org/apache/logging/log4j/core/config/composite/MergeStrategy.java
----------------------------------------------------------------------
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/composite/MergeStrategy.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/composite/MergeStrategy.java
new file mode 100644
index 0000000..130a6f0
--- /dev/null
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/composite/MergeStrategy.java
@@ -0,0 +1,33 @@
+/*
+ * 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.composite;
+
+import org.apache.logging.log4j.core.config.Node;
+import org.apache.logging.log4j.core.config.plugins.util.PluginManager;
+
+/**
+ * Merges two configurations together
+ */
+public interface MergeStrategy {
+
+    /**
+     * Merge the soure node tree into the target node tree.
+     * @param target The target Node tree.
+     * @param source The source Node tree.
+     */
+    void mergConfigurations(Node target, Node source, PluginManager pluginManager);
+}

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/bb64f457/log4j-core/src/main/java/org/apache/logging/log4j/core/config/composite/package-info.java
----------------------------------------------------------------------
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/composite/package-info.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/composite/package-info.java
new file mode 100644
index 0000000..fd920fa
--- /dev/null
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/composite/package-info.java
@@ -0,0 +1,21 @@
+/*
+ * 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.
+ */
+
+/**
+ * Support for composite configurations.
+ */
+package org.apache.logging.log4j.core.config.composite;
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/bb64f457/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/Log4jContextFactory.java
----------------------------------------------------------------------
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/Log4jContextFactory.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/Log4jContextFactory.java
index c65a64f..b504838 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/Log4jContextFactory.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/Log4jContextFactory.java
@@ -17,10 +17,14 @@
 package org.apache.logging.log4j.core.impl;
 
 import java.net.URI;
+import java.util.ArrayList;
+import java.util.List;
 import java.util.Objects;
 
 import org.apache.logging.log4j.core.LifeCycle;
 import org.apache.logging.log4j.core.LoggerContext;
+import org.apache.logging.log4j.core.config.AbstractConfiguration;
+import org.apache.logging.log4j.core.config.composite.CompositeConfiguration;
 import org.apache.logging.log4j.core.config.Configuration;
 import org.apache.logging.log4j.core.config.ConfigurationFactory;
 import org.apache.logging.log4j.core.config.ConfigurationSource;
@@ -241,6 +245,43 @@ public class Log4jContextFactory implements LoggerContextFactory, ShutdownCallba
         return ctx;
     }
 
+    public LoggerContext getContext(final String fqcn, final ClassLoader loader, final Object externalContext,
+            final boolean currentContext, final List<URI> configLocations, final String name) {
+        final LoggerContext ctx = selector
+                .getContext(fqcn, loader, currentContext, null/*this probably needs to change*/);
+        if (externalContext != null && ctx.getExternalContext() == null) {
+            ctx.setExternalContext(externalContext);
+        }
+        if (name != null) {
+            ctx.setName(name);
+        }
+        if (ctx.getState() == LifeCycle.State.INITIALIZED) {
+            if ((configLocations != null && !configLocations.isEmpty())) {
+                ContextAnchor.THREAD_CONTEXT.set(ctx);
+                List<AbstractConfiguration> configurations = new ArrayList<>(configLocations.size());
+                for (URI configLocation : configLocations) {
+                    Configuration currentReadConfiguration = ConfigurationFactory.getInstance()
+                            .getConfiguration(name, configLocation);
+                    if (currentReadConfiguration instanceof AbstractConfiguration) {
+                        configurations.add((AbstractConfiguration) currentReadConfiguration);
+                    } else {
+                        LOGGER.error(
+                                "Found configuration {}, which is not an AbstractConfiguration and can't be handled by CompositeConfiguration",
+                                configLocation);
+                    }
+                }
+                CompositeConfiguration compositeConfiguration = new CompositeConfiguration(configurations);
+                LOGGER.debug("Starting LoggerContext[name={}] from configurations at {}", ctx.getName(),
+                        configLocations);
+                ctx.start(compositeConfiguration);
+                ContextAnchor.THREAD_CONTEXT.remove();
+            } else {
+                ctx.start();
+            }
+        }
+        return ctx;
+    }
+
     /**
      * Returns the ContextSelector.
      * @return The ContextSelector.
@@ -251,7 +292,7 @@ public class Log4jContextFactory implements LoggerContextFactory, ShutdownCallba
 
 	/**
 	 * Returns the ShutdownCallbackRegistry
-	 * 
+	 *
 	 * @return the ShutdownCallbackRegistry
 	 * @since 2.4
 	 */

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/bb64f457/log4j-core/src/main/java/org/apache/logging/log4j/core/util/WatchManager.java
----------------------------------------------------------------------
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/util/WatchManager.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/util/WatchManager.java
index 3750462..146bff7 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/util/WatchManager.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/util/WatchManager.java
@@ -17,6 +17,7 @@
 package org.apache.logging.log4j.core.util;
 
 import java.io.File;
+import java.util.HashMap;
 import java.util.Map;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentMap;
@@ -78,6 +79,14 @@ public class WatchManager extends AbstractLifeCycle {
 
     }
 
+    public Map<File, FileWatcher> getWatchers() {
+        Map<File, FileWatcher> map = new HashMap<>();
+        for (Map.Entry<File, FileMonitor> entry : watchers.entrySet()) {
+            map.put(entry.getKey(), entry.getValue().fileWatcher);
+        }
+        return map;
+    }
+
     private class WatchWorker implements Runnable {
 
         @Override

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/bb64f457/log4j-core/src/test/java/org/apache/logging/log4j/core/config/CompositeConfigurationTest.java
----------------------------------------------------------------------
diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/core/config/CompositeConfigurationTest.java b/log4j-core/src/test/java/org/apache/logging/log4j/core/config/CompositeConfigurationTest.java
new file mode 100644
index 0000000..f17c941
--- /dev/null
+++ b/log4j-core/src/test/java/org/apache/logging/log4j/core/config/CompositeConfigurationTest.java
@@ -0,0 +1,149 @@
+package org.apache.logging.log4j.core.config;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.util.Map;
+
+import org.apache.logging.log4j.core.Appender;
+import org.apache.logging.log4j.core.appender.ConsoleAppender;
+import org.apache.logging.log4j.core.appender.FileAppender;
+import org.apache.logging.log4j.core.appender.RollingFileAppender;
+import org.apache.logging.log4j.core.config.composite.CompositeConfiguration;
+import org.apache.logging.log4j.core.filter.CompositeFilter;
+import org.apache.logging.log4j.core.filter.ThresholdFilter;
+import org.apache.logging.log4j.junit.LoggerContextRule;
+import org.junit.Test;
+import org.junit.runner.Description;
+import org.junit.runners.model.Statement;
+
+public class CompositeConfigurationTest
+{
+
+    @Test
+    public void compositeConfigurationUsed()
+    {
+        final LoggerContextRule lcr =
+            new LoggerContextRule( "classpath:log4j-comp-appender.xml,log4j-comp-appender.json" );
+        Statement test = new Statement()
+        {
+            @Override
+            public void evaluate()
+                throws Throwable
+            {
+                assertTrue( lcr.getConfiguration() instanceof CompositeConfiguration);
+            }
+        };
+        runTest( lcr, test );
+    }
+
+    @Test
+    public void compositeProperties()
+    {
+        final LoggerContextRule lcr =
+            new LoggerContextRule( "classpath:log4j-comp-properties.xml,log4j-comp-properties.json" );
+        Statement test = new Statement()
+        {
+            @Override
+            public void evaluate()
+                throws Throwable
+            {
+                CompositeConfiguration config = (CompositeConfiguration) lcr.getConfiguration();
+                assertEquals( "json", config.getStrSubstitutor().replace( "${propertyShared}" ) );
+                assertEquals( "xml", config.getStrSubstitutor().replace( "${propertyXml}" ) );
+                assertEquals( "json", config.getStrSubstitutor().replace( "${propertyJson}" ) );
+            }
+        };
+        runTest( lcr, test );
+    }
+
+    @Test
+    public void compositeAppenders()
+    {
+        final LoggerContextRule lcr =
+            new LoggerContextRule( "classpath:log4j-comp-appender.xml,log4j-comp-appender.json" );
+        Statement test = new Statement()
+        {
+            @Override
+            public void evaluate()
+                throws Throwable
+            {
+                CompositeConfiguration config = (CompositeConfiguration) lcr.getConfiguration();
+                Map<String, Appender> appender = config.getAppenders();
+                assertEquals( 3, appender.size() );
+                assertTrue( appender.get( "STDOUT" ) instanceof ConsoleAppender );
+                assertTrue( appender.get( "File" ) instanceof FileAppender );
+                assertTrue( appender.get( "Override" ) instanceof RollingFileAppender );
+            }
+        };
+        runTest( lcr, test );
+    }
+
+    @Test
+    public void compositeLogger()
+    {
+        final LoggerContextRule lcr = new LoggerContextRule( "classpath:log4j-comp-logger.xml,log4j-comp-logger.json" );
+        Statement test = new Statement()
+        {
+            @Override
+            public void evaluate()
+                throws Throwable
+            {
+                CompositeConfiguration config = (CompositeConfiguration) lcr.getConfiguration();
+                Map<String, Appender> appendersMap = config.getLogger( "cat1" ).getAppenders();
+                assertEquals("Expected 2 Appender references for cat1 but got " + appendersMap.size(), 2,
+                        appendersMap.size() );
+                assertTrue( appendersMap.get( "STDOUT" ) instanceof ConsoleAppender );
+
+                appendersMap = config.getLogger( "cat2" ).getAppenders();
+                assertEquals("Expected 1 Appender reference for cat2 but got " + appendersMap.size(), 1,
+                        appendersMap.size() );
+                assertTrue( appendersMap.get( "File" ) instanceof FileAppender );
+
+                appendersMap = config.getLogger( "cat3" ).getAppenders();
+                assertEquals("Expected 1 Appender reference for cat3 but got " + appendersMap.size(), 1,
+                        appendersMap.size() );
+                assertTrue( appendersMap.get( "File" ) instanceof FileAppender );
+
+                appendersMap = config.getRootLogger().getAppenders();
+                assertEquals("Expected 2 Appender references for the root logger but got " + appendersMap.size(), 2,
+                        appendersMap.size() );
+                assertTrue( appendersMap.get( "File" ) instanceof FileAppender );
+                assertTrue( appendersMap.get( "STDOUT" ) instanceof ConsoleAppender );
+            }
+        };
+        runTest( lcr, test );
+    }
+
+    @Test
+    public void overrideFilter()
+    {
+        final LoggerContextRule lcr = new LoggerContextRule( "classpath:log4j-comp-filter.xml,log4j-comp-filter.json" );
+        Statement test = new Statement()
+        {
+            @Override
+            public void evaluate()
+                throws Throwable
+            {
+                CompositeConfiguration config = (CompositeConfiguration) lcr.getConfiguration();
+                assertTrue( config.getFilter() instanceof CompositeFilter);
+                CompositeFilter filter = (CompositeFilter) config.getFilter();
+                assertTrue( filter.getFiltersArray().length == 2);
+            }
+        };
+        runTest( lcr, test );
+    }
+
+    private void runTest( LoggerContextRule rule, Statement statement )
+    {
+        try
+        {
+            rule.apply( statement, Description.createTestDescription( getClass(),
+                Thread.currentThread().getStackTrace()[1].getMethodName() ) ).evaluate();
+        }
+        catch ( Throwable throwable )
+        {
+            throw new RuntimeException( throwable );
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/bb64f457/log4j-core/src/test/resources/log4j-comp-appender.json
----------------------------------------------------------------------
diff --git a/log4j-core/src/test/resources/log4j-comp-appender.json b/log4j-core/src/test/resources/log4j-comp-appender.json
new file mode 100644
index 0000000..1f0b02d
--- /dev/null
+++ b/log4j-core/src/test/resources/log4j-comp-appender.json
@@ -0,0 +1,34 @@
+{
+    "Configuration" : {
+        "status": "warn",
+        "name": "YAMLConfigTest",
+        "appenders": {
+            "Console": {
+                "name": "STDOUT",
+                "PatternLayout": {
+                    "Pattern": "%m%n"
+                }
+            },
+            "RollingFile": {
+                "name": "Override",
+                "fileName": "target/log4j-appender-override.log",
+                "filePattern": "target/log4j-appender-override-$${date:yyyy-MM}.log",
+                "PatternLayout": {
+                    "Pattern": "%m%n"
+                },
+                "SizeBasedTriggeringPolicy": {
+                    "size": "500"
+                }
+            }
+        },
+        "Loggers" : {
+            "Root" : {
+                "level" : "error",
+                "AppenderRef" : {
+                    "ref" : "STDOUT"
+                }
+
+            }
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/bb64f457/log4j-core/src/test/resources/log4j-comp-appender.xml
----------------------------------------------------------------------
diff --git a/log4j-core/src/test/resources/log4j-comp-appender.xml b/log4j-core/src/test/resources/log4j-comp-appender.xml
new file mode 100644
index 0000000..7cd71a2
--- /dev/null
+++ b/log4j-core/src/test/resources/log4j-comp-appender.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+
+-->
+<Configuration status="ERROR" name="XMLConfigTest">
+    <Appenders>
+        <File name="File" fileName="target/log4j-comp-appender.log" bufferedIO="false">
+            <PatternLayout>
+                <Pattern>%d %p %C{1.} [%t] %m%n</Pattern>
+            </PatternLayout>
+        </File>
+        <File name="Override" fileName="target/log4j-comp-appender.log" bufferedIO="false">
+            <PatternLayout>
+                <Pattern>%d %p %C{1.} [%t] %m%n</Pattern>
+            </PatternLayout>
+        </File>
+    </Appenders>
+
+    <Loggers>
+        <Root level="ERROR">
+            <AppenderRef ref="File"/>
+        </Root>
+    </Loggers>
+
+</Configuration>

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/bb64f457/log4j-core/src/test/resources/log4j-comp-filter.json
----------------------------------------------------------------------
diff --git a/log4j-core/src/test/resources/log4j-comp-filter.json b/log4j-core/src/test/resources/log4j-comp-filter.json
new file mode 100644
index 0000000..c010298
--- /dev/null
+++ b/log4j-core/src/test/resources/log4j-comp-filter.json
@@ -0,0 +1,9 @@
+{
+    "Configuration" : {
+        "status": "warn",
+        "name": "YAMLConfigTest",
+        "thresholdFilter" : {
+            "level": "debug"
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/bb64f457/log4j-core/src/test/resources/log4j-comp-filter.xml
----------------------------------------------------------------------
diff --git a/log4j-core/src/test/resources/log4j-comp-filter.xml b/log4j-core/src/test/resources/log4j-comp-filter.xml
new file mode 100644
index 0000000..553d448
--- /dev/null
+++ b/log4j-core/src/test/resources/log4j-comp-filter.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+
+-->
+<Configuration status="ERROR" name="XMLConfigTest">
+  <BurstFilter level="INFO" rate="16" maxBurst="100"/>
+
+  <Appenders>
+    <Console name="STDOUT">
+      <PatternLayout pattern="%m%n"/>
+    </Console>
+  </Appenders>
+
+  <Loggers>
+    <Root level="error">
+      <AppenderRef ref="STDOUT"/>
+    </Root>
+  </Loggers>
+
+</Configuration>

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/bb64f457/log4j-core/src/test/resources/log4j-comp-logger.json
----------------------------------------------------------------------
diff --git a/log4j-core/src/test/resources/log4j-comp-logger.json b/log4j-core/src/test/resources/log4j-comp-logger.json
new file mode 100644
index 0000000..b2b08d7
--- /dev/null
+++ b/log4j-core/src/test/resources/log4j-comp-logger.json
@@ -0,0 +1,36 @@
+{
+    "Configuration" : {
+        "status": "warn",
+        "name": "LoggerConfigTest",
+        "Loggers" : {
+            "logger" : [
+                {
+                    "name" : "cat1",
+                    "level" : "debug",
+                    "additivity" : false,
+                    "AppenderRef" : {
+                        "ref" : "STDOUT"
+                    }
+                },
+                {
+                    "name" : "cat2",
+                    "level" : "debug",
+                    "additivity" : false,
+                    "AppenderRef" : {
+                        "ref" : "File"
+                    }
+
+                }
+            ],
+            "Root" : {
+                "level" : "error",
+                "AppenderRef" : [{
+                    "ref" : "STDOUT"
+                },
+                {
+                    "ref" : "File"
+                }]
+            }
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/bb64f457/log4j-core/src/test/resources/log4j-comp-logger.xml
----------------------------------------------------------------------
diff --git a/log4j-core/src/test/resources/log4j-comp-logger.xml b/log4j-core/src/test/resources/log4j-comp-logger.xml
new file mode 100644
index 0000000..0934e94
--- /dev/null
+++ b/log4j-core/src/test/resources/log4j-comp-logger.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+
+-->
+<Configuration status="ERROR" name="LoggerConfigTest">
+    <Appenders>
+        <Console name="STDOUT">
+            <PatternLayout pattern="%m%n"/>
+        </Console>
+        <File name="File" fileName="${filename}" bufferedIO="false">
+            <PatternLayout>
+                <Pattern>%d %p %C{1.} [%t] %m%n</Pattern>
+            </PatternLayout>
+        </File>
+    </Appenders>
+
+    <Loggers>
+        <Logger name="cat1" level="debug" additivity="false">
+            <AppenderRef ref="File"/>
+        </Logger>
+
+        <Logger name="cat3" level="debug" additivity="false">
+            <AppenderRef ref="File"/>
+        </Logger>
+    </Loggers>
+
+</Configuration>

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/bb64f457/log4j-core/src/test/resources/log4j-comp-properties.json
----------------------------------------------------------------------
diff --git a/log4j-core/src/test/resources/log4j-comp-properties.json b/log4j-core/src/test/resources/log4j-comp-properties.json
new file mode 100644
index 0000000..83c2d09
--- /dev/null
+++ b/log4j-core/src/test/resources/log4j-comp-properties.json
@@ -0,0 +1,16 @@
+{
+    "Configuration" : {
+        "status": "warn",
+        "name": "YAMLConfigTest",
+
+        "properties" : {
+            "property" : [{
+                "name" : "propertyShared",
+                "value": "json"
+            },{
+                "name" : "propertyJson",
+                "value": "json"
+            }]
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/bb64f457/log4j-core/src/test/resources/log4j-comp-properties.xml
----------------------------------------------------------------------
diff --git a/log4j-core/src/test/resources/log4j-comp-properties.xml b/log4j-core/src/test/resources/log4j-comp-properties.xml
new file mode 100644
index 0000000..6fa9c6b
--- /dev/null
+++ b/log4j-core/src/test/resources/log4j-comp-properties.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+
+-->
+<Configuration status="OFF" name="XMLConfigTest">
+  <Properties>
+    <Property name="propertyShared">xml</Property>
+    <Property name="propertyXml">xml</Property>
+  </Properties>
+  <Appenders>
+    <Console name="STDOUT">
+      <PatternLayout pattern="%m%n"/>
+    </Console>
+  </Appenders>
+  <Loggers>
+    <Root level="error">
+      <AppenderRef ref="STDOUT"/>
+    </Root>
+  </Loggers>
+</Configuration>