Merge branch 'master' of https://github.com/apache/logging-log4j2 into LOG4J2-2707
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/bridge/AppenderWrapper.java b/log4j-1.2-api/src/main/java/org/apache/log4j/bridge/AppenderWrapper.java
index f06f909..eb39fe7 100644
--- a/log4j-1.2-api/src/main/java/org/apache/log4j/bridge/AppenderWrapper.java
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/bridge/AppenderWrapper.java
@@ -18,16 +18,26 @@
 
 import org.apache.log4j.Appender;
 import org.apache.log4j.Layout;
+import org.apache.log4j.helpers.AppenderAttachableImpl;
+import org.apache.log4j.spi.AppenderAttachable;
 import org.apache.log4j.spi.ErrorHandler;
 import org.apache.log4j.spi.Filter;
 import org.apache.log4j.spi.LoggingEvent;
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.core.filter.AbstractFilterable;
+import org.apache.logging.log4j.status.StatusLogger;
+
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.List;
 
 /**
  * Holds a Log4j 2 Appender in an empty Log4j 1 Appender so it can be extracted when constructing the configuration.
  * Allows a Log4j 1 Appender to reference a Log4j 2 Appender.
  */
-public class AppenderWrapper implements Appender {
+public class AppenderWrapper extends AppenderAttachableImpl implements Appender {
 
+    private static final Logger LOGGER = StatusLogger.getLogger();
     private final org.apache.logging.log4j.core.Appender appender;
 
     public AppenderWrapper(org.apache.logging.log4j.core.Appender appender) {
@@ -40,6 +50,15 @@
 
     @Override
     public void addFilter(Filter newFilter) {
+        if (appender instanceof AbstractFilterable) {
+            if (newFilter instanceof FilterWrapper) {
+                ((AbstractFilterable) appender).addFilter(((FilterWrapper) newFilter).getFilter());
+            } else {
+                ((AbstractFilterable) appender).addFilter(new FilterAdapter(newFilter));
+            }
+        } else {
+            LOGGER.warn("Unable to add filter to appender {}, it does not support filters", appender.getName());
+        }
     }
 
     @Override
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/AbstractBuilder.java b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/AbstractBuilder.java
new file mode 100644
index 0000000..0448ed2
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/AbstractBuilder.java
@@ -0,0 +1,120 @@
+/*
+ * 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.log4j.builders;
+
+import org.apache.log4j.bridge.FilterAdapter;
+import org.apache.log4j.bridge.FilterWrapper;
+import org.apache.log4j.helpers.OptionConverter;
+import org.apache.log4j.spi.Filter;
+import org.apache.logging.log4j.Level;
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.core.filter.CompositeFilter;
+import org.apache.logging.log4j.core.filter.ThresholdFilter;
+import org.apache.logging.log4j.status.StatusLogger;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Properties;
+
+/**
+ * Base class for Log4j 1 component builders.
+ */
+public abstract class AbstractBuilder {
+
+    private static Logger LOGGER = StatusLogger.getLogger();
+    protected static final String FILE_PARAM = "File";
+    protected static final String APPEND_PARAM = "Append";
+    protected static final String BUFFERED_IO_PARAM = "BufferedIO";
+    protected static final String BUFFER_SIZE_PARAM = "BufferSize";
+    protected static final String MAX_SIZE_PARAM = "MaxFileSize";
+    protected static final String MAX_BACKUP_INDEX = "MaxBackupIndex";
+    protected static final String RELATIVE = "RELATIVE";
+
+    private final String prefix;
+    private final Properties props;
+
+    public AbstractBuilder() {
+        this.prefix = null;
+        this.props = new Properties();
+    }
+
+    public AbstractBuilder(String prefix, Properties props) {
+        this.prefix = prefix + ".";
+        this.props = props;
+    }
+
+    public String getProperty(String key) {
+        return props.getProperty(prefix + key);
+    }
+
+    public String getProperty(String key, String defaultValue) {
+        return props.getProperty(prefix + key, defaultValue);
+    }
+
+    public boolean getBooleanProperty(String key) {
+        return Boolean.parseBoolean(props.getProperty(prefix + key, Boolean.FALSE.toString()));
+    }
+
+    public int getIntegerProperty(String key, int defaultValue) {
+        String value = props.getProperty(key);
+        try {
+            if (value != null) {
+                return Integer.parseInt(value);
+            }
+        } catch (Exception ex) {
+            LOGGER.warn("Error converting value {} of {} to an integer: {}", value, key, ex.getMessage());
+        }
+        return defaultValue;
+    }
+
+    public Properties getProperties() {
+        return props;
+    }
+
+
+    protected org.apache.logging.log4j.core.Filter buildFilters(String level, Filter filter) {
+        if (level != null && filter != null) {
+            List<org.apache.logging.log4j.core.Filter> filterList = new ArrayList<>();
+            org.apache.logging.log4j.core.Filter thresholdFilter =
+                    ThresholdFilter.createFilter(OptionConverter.convertLevel(level, Level.TRACE),
+                            org.apache.logging.log4j.core.Filter.Result.NEUTRAL,
+                            org.apache.logging.log4j.core.Filter.Result.DENY);
+            filterList.add(thresholdFilter);
+            Filter f = filter;
+            while (f != null) {
+                if (filter instanceof FilterWrapper) {
+                    filterList.add(((FilterWrapper) f).getFilter());
+                } else {
+                    filterList.add(new FilterAdapter(f));
+                }
+                f = f.next;
+            }
+            return CompositeFilter.createFilters(filterList.toArray(new org.apache.logging.log4j.core.Filter[0]));
+        } else if (level != null) {
+            return ThresholdFilter.createFilter(OptionConverter.convertLevel(level, Level.TRACE),
+                    org.apache.logging.log4j.core.Filter.Result.NEUTRAL,
+                    org.apache.logging.log4j.core.Filter.Result.DENY);
+        } else if (filter != null) {
+            if (filter instanceof FilterWrapper) {
+                return ((FilterWrapper) filter).getFilter();
+            } else {
+                return new FilterAdapter(filter);
+            }
+        }
+        return null;
+    }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/BuilderManager.java b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/BuilderManager.java
index 9a6b7b8..d6465f0 100644
--- a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/BuilderManager.java
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/BuilderManager.java
@@ -21,8 +21,9 @@
 import org.apache.log4j.builders.appender.AppenderBuilder;
 import org.apache.log4j.builders.filter.FilterBuilder;
 import org.apache.log4j.builders.layout.LayoutBuilder;
+import org.apache.log4j.config.PropertiesConfiguration;
 import org.apache.log4j.spi.Filter;
-import org.apache.log4j.xml.XmlConfigurationFactory;
+import org.apache.log4j.xml.XmlConfiguration;
 import org.apache.logging.log4j.Logger;
 import org.apache.logging.log4j.plugins.util.PluginManager;
 import org.apache.logging.log4j.plugins.util.PluginType;
@@ -30,8 +31,10 @@
 import org.apache.logging.log4j.util.LoaderUtil;
 import org.w3c.dom.Element;
 
+import java.lang.reflect.Constructor;
 import java.lang.reflect.InvocationTargetException;
 import java.util.Map;
+import java.util.Properties;
 
 /**
  *
@@ -41,6 +44,7 @@
     public static final String CATEGORY = "Log4j Builder";
     private static final Logger LOGGER = StatusLogger.getLogger();
     private final Map<String, PluginType<?>> plugins;
+    private static Class<?>[] constructorParams = new Class[] { String.class, Properties.class};
 
     public BuilderManager() {
         final PluginManager manager = new PluginManager(CATEGORY);
@@ -48,13 +52,13 @@
         plugins = manager.getPlugins();
     }
 
-    public Appender parseAppender(String className, Element appenderElement, XmlConfigurationFactory factory) {
+    public Appender parseAppender(String className, Element appenderElement, XmlConfiguration config) {
         PluginType<?> plugin = plugins.get(className.toLowerCase());
         if (plugin != null) {
             try {
                 @SuppressWarnings("unchecked")
                 AppenderBuilder builder = (AppenderBuilder) LoaderUtil.newInstanceOf(plugin.getPluginClass());
-                return builder.parseAppender(appenderElement, factory);
+                return builder.parseAppender(appenderElement, config);
             } catch (InstantiationException | IllegalAccessException | InvocationTargetException ex) {
                 LOGGER.warn("Unable to load plugin: {} due to: {}", plugin.getKey(), ex.getMessage());
             }
@@ -62,13 +66,25 @@
         return null;
     }
 
-    public Filter parseFilter(String className, Element filterElement, XmlConfigurationFactory factory) {
+    public Appender parseAppender(String name, String className, String prefix, String layoutPrefix,
+            String filterPrefix, Properties props, PropertiesConfiguration config) {
+        PluginType<?> plugin = plugins.get(className.toLowerCase());
+        if (plugin != null) {
+            AppenderBuilder builder = createBuilder(plugin, prefix, props);
+            if (builder != null) {
+                return builder.parseAppender(name, layoutPrefix, filterPrefix, props, config);
+            }
+        }
+        return null;
+    }
+
+    public Filter parseFilter(String className, Element filterElement, XmlConfiguration config) {
         PluginType<?> plugin = plugins.get(className.toLowerCase());
         if (plugin != null) {
             try {
                 @SuppressWarnings("unchecked")
                 FilterBuilder builder = (FilterBuilder) LoaderUtil.newInstanceOf(plugin.getPluginClass());
-                return builder.parseFilter(filterElement, factory);
+                return builder.parseFilter(filterElement, config);
             } catch (InstantiationException | IllegalAccessException | InvocationTargetException ex) {
                 LOGGER.warn("Unable to load plugin: {} due to: {}", plugin.getKey(), ex.getMessage());
             }
@@ -76,19 +92,58 @@
         return null;
     }
 
+    public Filter parseFilter(String className, String filterPrefix, Properties props, PropertiesConfiguration config) {
+        PluginType<?> plugin = plugins.get(className.toLowerCase());
+        if (plugin != null) {
+            FilterBuilder builder = createBuilder(plugin, filterPrefix, props);
+            if (builder != null) {
+                return builder.parseFilter(config);
+            }
+        }
+        return null;
+    }
 
-    public Layout parseLayout(String className, Element layoutElement, XmlConfigurationFactory factory) {
+    public Layout parseLayout(String className, Element layoutElement, XmlConfiguration config) {
         PluginType<?> plugin = plugins.get(className.toLowerCase());
         if (plugin != null) {
             try {
                 @SuppressWarnings("unchecked")
                 LayoutBuilder builder = (LayoutBuilder) LoaderUtil.newInstanceOf(plugin.getPluginClass());
-                return builder.parseLayout(layoutElement, factory);
+                return builder.parseLayout(layoutElement, config);
             } catch (InstantiationException | IllegalAccessException | InvocationTargetException ex) {
                 LOGGER.warn("Unable to load plugin: {} due to: {}", plugin.getKey(), ex.getMessage());
             }
         }
         return null;
     }
+    public Layout parseLayout(String className, String layoutPrefix, Properties props, PropertiesConfiguration config) {
+        PluginType<?> plugin = plugins.get(className.toLowerCase());
+        if (plugin != null) {
+            LayoutBuilder builder = createBuilder(plugin, layoutPrefix, props);
+            if (builder != null) {
+                return builder.parseLayout(config);
+            }
+        }
+        return null;
+    }
+
+    private <T extends AbstractBuilder> T createBuilder(PluginType<?> plugin, String prefix, Properties props) {
+        try {
+            Class<?> clazz = plugin.getPluginClass();
+            if (AbstractBuilder.class.isAssignableFrom(clazz)) {
+                @SuppressWarnings("unchecked")
+                Constructor<T> constructor =
+                        (Constructor<T>) clazz.getConstructor(constructorParams);
+                return constructor.newInstance(prefix, props);
+            } else {
+                @SuppressWarnings("unchecked")
+                T builder = (T) LoaderUtil.newInstanceOf(clazz);
+                return builder;
+            }
+        } catch (NoSuchMethodException | InstantiationException | IllegalAccessException | InvocationTargetException ex) {
+            LOGGER.warn("Unable to load plugin: {} due to: {}", plugin.getKey(), ex.getMessage());
+            return null;
+        }
+    }
 
 }
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/AppenderBuilder.java b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/AppenderBuilder.java
index 2b72efd..0572b86 100644
--- a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/AppenderBuilder.java
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/AppenderBuilder.java
@@ -17,17 +17,20 @@
 package org.apache.log4j.builders.appender;
 
 import org.apache.log4j.Appender;
-import org.apache.log4j.xml.XmlConfigurationFactory;
-import org.apache.logging.log4j.core.config.ConfigurationFactory;
+import org.apache.log4j.config.PropertiesConfiguration;
+import org.apache.log4j.xml.XmlConfiguration;
 import org.w3c.dom.Element;
 
-import java.util.function.BiFunction;
+import java.util.Properties;
 
 /**
  * Define an Appender Builder.
  */
 public interface AppenderBuilder {
 
-    Appender parseAppender(Element element, XmlConfigurationFactory factory);
+    Appender parseAppender(Element element, XmlConfiguration configuration);
+
+    Appender parseAppender(String name, String layoutPrefix, String filterPrefix, Properties props,
+            PropertiesConfiguration configuration);
 
 }
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/AsyncAppenderBuilder.java b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/AsyncAppenderBuilder.java
new file mode 100644
index 0000000..86969ed
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/AsyncAppenderBuilder.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.log4j.builders.appender;
+
+import org.apache.log4j.Appender;
+import org.apache.log4j.bridge.AppenderWrapper;
+import org.apache.log4j.builders.AbstractBuilder;
+import org.apache.log4j.builders.BooleanHolder;
+import org.apache.log4j.builders.Holder;
+import org.apache.log4j.config.Log4j1Configuration;
+import org.apache.log4j.config.PropertiesConfiguration;
+import org.apache.log4j.helpers.OptionConverter;
+import org.apache.log4j.xml.XmlConfiguration;
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.core.appender.AsyncAppender;
+import org.apache.logging.log4j.core.config.AppenderRef;
+import org.apache.logging.log4j.plugins.Plugin;
+import org.apache.logging.log4j.status.StatusLogger;
+import org.w3c.dom.Element;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Properties;
+
+import static org.apache.log4j.builders.BuilderManager.CATEGORY;
+import static org.apache.log4j.xml.XmlConfiguration.NAME_ATTR;
+import static org.apache.log4j.xml.XmlConfiguration.PARAM_TAG;
+import static org.apache.log4j.xml.XmlConfiguration.VALUE_ATTR;
+import static org.apache.log4j.xml.XmlConfiguration.forEachElement;
+import static org.apache.log4j.config.Log4j1Configuration.APPENDER_REF_TAG;
+import static org.apache.log4j.config.Log4j1Configuration.THRESHOLD_PARAM;
+
+
+/**
+ * Build an Asynch Appender
+ */
+@Plugin(name = "org.apache.log4j.AsyncAppender", category = CATEGORY)
+public class AsyncAppenderBuilder extends AbstractBuilder implements AppenderBuilder {
+
+    private static final Logger LOGGER = StatusLogger.getLogger();
+    private static final String BLOCKING_PARAM = "Blocking";
+    private static final String INCLUDE_LOCATION_PARAM = "IncludeLocation";
+
+    public AsyncAppenderBuilder() {
+    }
+
+    public AsyncAppenderBuilder(String prefix, Properties props) {
+        super(prefix, props);
+    }
+
+    @Override
+    public Appender parseAppender(final Element appenderElement, final XmlConfiguration config) {
+        String name = appenderElement.getAttribute(NAME_ATTR);
+        Holder<List<String>> appenderRefs = new Holder<>(new ArrayList<>());
+        Holder<Boolean> blocking = new BooleanHolder();
+        Holder<Boolean> includeLocation = new BooleanHolder();
+        Holder<String> level = new Holder<>("trace");
+        Holder<Integer> bufferSize = new Holder<>(1024);
+        forEachElement(appenderElement.getChildNodes(), (currentElement) -> {
+            switch (currentElement.getTagName()) {
+                case APPENDER_REF_TAG:
+                    Appender appender = config.findAppenderByReference(currentElement);
+                    if (appender != null) {
+                        appenderRefs.get().add(appender.getName());
+                    }
+                    break;
+                case PARAM_TAG: {
+                    switch (currentElement.getAttribute(NAME_ATTR)) {
+                        case BUFFER_SIZE_PARAM: {
+                            String value = currentElement.getAttribute(VALUE_ATTR);
+                            if (value == null) {
+                                LOGGER.warn("No value supplied for BufferSize parameter. Defaulting to 1024.");
+                            } else {
+                                bufferSize.set(Integer.parseInt(value));
+                            }
+                            break;
+                        }
+                        case BLOCKING_PARAM: {
+                            String value = currentElement.getAttribute(VALUE_ATTR);
+                            if (value == null) {
+                                LOGGER.warn("No value supplied for Blocking parameter. Defaulting to false.");
+                            } else {
+                                blocking.set(Boolean.parseBoolean(value));
+                            }
+                            break;
+                        }
+                        case INCLUDE_LOCATION_PARAM: {
+                            String value = currentElement.getAttribute(VALUE_ATTR);
+                            if (value == null) {
+                                LOGGER.warn("No value supplied for IncludeLocation parameter. Defaulting to false.");
+                            } else {
+                                includeLocation.set(Boolean.parseBoolean(value));
+                            }
+                            break;
+                        }
+                        case THRESHOLD_PARAM: {
+                            String value = currentElement.getAttribute(VALUE_ATTR);
+                            if (value == null) {
+                                LOGGER.warn("No value supplied for Threshold parameter, ignoring.");
+                            } else {
+                                level.set(value);
+                            }
+                            break;
+                        }
+                    }
+                    break;
+                }
+            }
+        });
+        return createAppender(name, level.get(), appenderRefs.get().toArray(new String[0]), blocking.get(),
+                bufferSize.get(), includeLocation.get(), config);
+    }
+
+    @Override
+    public Appender parseAppender(final String name, final String layoutPrefix,
+            final String filterPrefix, final Properties props, final PropertiesConfiguration configuration) {
+        String appenderRef = getProperty(APPENDER_REF_TAG);
+        boolean blocking = getBooleanProperty(BLOCKING_PARAM);
+        boolean includeLocation = getBooleanProperty(INCLUDE_LOCATION_PARAM);
+        String level = getProperty(THRESHOLD_PARAM);
+        int bufferSize = getIntegerProperty(BUFFER_SIZE_PARAM, 1024);
+        if (appenderRef == null) {
+            LOGGER.warn("No appender references configured for AsyncAppender {}", name);
+            return null;
+        }
+        Appender appender = configuration.parseAppender(props, appenderRef);
+        if (appender == null) {
+            LOGGER.warn("Cannot locate Appender {}", appenderRef);
+            return null;
+        }
+        return createAppender(name, level, new String[] {appenderRef}, blocking, bufferSize, includeLocation,
+                configuration);
+    }
+
+    private <T extends Log4j1Configuration> Appender createAppender(String name, String level,
+            String[] appenderRefs, boolean blocking, int bufferSize, boolean includeLocation,
+            T configuration) {
+        org.apache.logging.log4j.Level logLevel = OptionConverter.convertLevel(level,
+                org.apache.logging.log4j.Level.TRACE);
+        AppenderRef[] refs = new AppenderRef[appenderRefs.length];
+        int index = 0;
+        for (String appenderRef : appenderRefs) {
+            refs[index++] = AppenderRef.createAppenderRef(appenderRef, logLevel, null);
+        }
+        return new AppenderWrapper(AsyncAppender.newBuilder()
+                .setName(name)
+                .setAppenderRefs(refs)
+                .setBlocking(blocking)
+                .setBufferSize(bufferSize)
+                .setIncludeLocation(includeLocation)
+                .setConfiguration(configuration)
+                .build());
+    }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/ConsoleAppenderBuilder.java b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/ConsoleAppenderBuilder.java
index d9b7905..423fab7 100644
--- a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/ConsoleAppenderBuilder.java
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/ConsoleAppenderBuilder.java
@@ -19,93 +19,143 @@
 import org.apache.log4j.Appender;
 import org.apache.log4j.Layout;
 import org.apache.log4j.bridge.AppenderWrapper;
-import org.apache.log4j.bridge.FilterAdapter;
-import org.apache.log4j.bridge.FilterWrapper;
 import org.apache.log4j.bridge.LayoutAdapter;
 import org.apache.log4j.bridge.LayoutWrapper;
+import org.apache.log4j.builders.AbstractBuilder;
 import org.apache.log4j.builders.Holder;
+import org.apache.log4j.config.Log4j1Configuration;
+import org.apache.log4j.config.PropertiesConfiguration;
 import org.apache.log4j.spi.Filter;
-import org.apache.log4j.xml.XmlConfigurationFactory;
+import org.apache.log4j.xml.XmlConfiguration;
 import org.apache.logging.log4j.Logger;
 import org.apache.logging.log4j.core.appender.ConsoleAppender;
 import org.apache.logging.log4j.plugins.Plugin;
 import org.apache.logging.log4j.status.StatusLogger;
 import org.w3c.dom.Element;
 
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Properties;
+
 import static org.apache.log4j.builders.BuilderManager.CATEGORY;
-import static org.apache.log4j.xml.XmlConfigurationFactory.*;
+import static org.apache.log4j.config.Log4j1Configuration.THRESHOLD_PARAM;
+import static org.apache.log4j.xml.XmlConfiguration.FILTER_TAG;
+import static org.apache.log4j.xml.XmlConfiguration.LAYOUT_TAG;
+import static org.apache.log4j.xml.XmlConfiguration.NAME_ATTR;
+import static org.apache.log4j.xml.XmlConfiguration.PARAM_TAG;
+import static org.apache.log4j.xml.XmlConfiguration.VALUE_ATTR;
+import static org.apache.log4j.xml.XmlConfiguration.forEachElement;
 
 /**
  * Build a Console Appender
  */
 @Plugin(name = "org.apache.log4j.ConsoleAppender", category = CATEGORY)
-public class ConsoleAppenderBuilder implements AppenderBuilder {
+public class ConsoleAppenderBuilder extends AbstractBuilder implements AppenderBuilder {
     private static final String SYSTEM_OUT = "System.out";
     private static final String SYSTEM_ERR = "System.err";
     private static final String TARGET = "target";
 
     private static final Logger LOGGER = StatusLogger.getLogger();
 
+    public ConsoleAppenderBuilder() {
+    }
+
+    public ConsoleAppenderBuilder(String prefix, Properties props) {
+        super(prefix, props);
+    }
+
     @Override
-    public Appender parseAppender(Element appenderElement, XmlConfigurationFactory factory) {
-        String name = appenderElement.getAttribute(XmlConfigurationFactory.NAME_ATTR);
+    public Appender parseAppender(final Element appenderElement, final XmlConfiguration config) {
+        String name = appenderElement.getAttribute(NAME_ATTR);
         Holder<String> target = new Holder<>(SYSTEM_OUT);
         Holder<Layout> layout = new Holder<>();
-        Holder<Filter> filter = new Holder<>();
+        Holder<List<Filter>> filters = new Holder<>(new ArrayList<>());
+        Holder<String> level = new Holder<>();
         forEachElement(appenderElement.getChildNodes(), (currentElement) -> {
             switch (currentElement.getTagName()) {
                 case LAYOUT_TAG:
-                    layout.set(factory.parseLayout(currentElement));
+                    layout.set(config.parseLayout(currentElement));
                     break;
                 case FILTER_TAG:
-                    filter.set(factory.parseFilters(currentElement));
+                    filters.get().add(config.parseFilters(currentElement));
                     break;
                 case PARAM_TAG: {
-                    if (currentElement.getAttribute(NAME_ATTR).equalsIgnoreCase(TARGET)) {
-                        String value = currentElement.getAttribute(VALUE_ATTR);
-                        if (value == null) {
-                            LOGGER.warn("No value supplied for target parameter. Defaulting to System.out.");
-                        } else {
-                            switch (value) {
-                                case SYSTEM_OUT:
-                                    target.set(SYSTEM_OUT);
-                                    break;
-                                case SYSTEM_ERR:
-                                    target.set(SYSTEM_ERR);
-                                    break;
-                                default:
-                                    LOGGER.warn("Invalid value \"{}\" for target parameter. Using default of System.out",
-                                            value);
+                    switch (currentElement.getAttribute(NAME_ATTR)) {
+                        case TARGET: {
+                            String value = currentElement.getAttribute(VALUE_ATTR);
+                            if (value == null) {
+                                LOGGER.warn("No value supplied for target parameter. Defaulting to System.out.");
+                            } else {
+                                switch (value) {
+                                    case SYSTEM_OUT:
+                                        target.set(SYSTEM_OUT);
+                                        break;
+                                    case SYSTEM_ERR:
+                                        target.set(SYSTEM_ERR);
+                                        break;
+                                    default:
+                                        LOGGER.warn("Invalid value \"{}\" for target parameter. Using default of System.out",
+                                                value);
+                                }
                             }
+                            break;
+                        }
+                        case THRESHOLD_PARAM: {
+                            String value = currentElement.getAttribute(VALUE_ATTR);
+                            if (value == null) {
+                                LOGGER.warn("No value supplied for Threshold parameter, ignoring.");
+                            } else {
+                                level.set(value);
+                            }
+                            break;
                         }
                     }
                     break;
                 }
             }
         });
-        org.apache.logging.log4j.core.Layout<?> consoleLayout = null;
-        org.apache.logging.log4j.core.Filter consoleFilter = null;
-
-        if (layout.get() instanceof LayoutWrapper) {
-            consoleLayout = ((LayoutWrapper) layout.get()).getLayout();
-        } else if (layout.get() != null) {
-            consoleLayout = new LayoutAdapter(layout.get());
-        }
-        if (filter.get() != null) {
-            if (filter.get() instanceof FilterWrapper) {
-                consoleFilter = ((FilterWrapper) filter.get()).getFilter();
+        Filter head = null;
+        Filter current = null;
+        for (Filter f : filters.get()) {
+            if (head == null) {
+                head = f;
+                current = f;
             } else {
-                consoleFilter = new FilterAdapter(filter.get());
+                current.next = f;
+                current = f;
             }
         }
-        ConsoleAppender.Target consoleTarget = SYSTEM_ERR.equals(target.get())
+        return createAppender(name, layout.get(), head, level.get(), target.get(), config);
+    }
+
+    @Override
+    public Appender parseAppender(final String name, final String layoutPrefix,
+            final String filterPrefix, final Properties props, final PropertiesConfiguration configuration) {
+        Layout layout = configuration.parseLayout(layoutPrefix, name, props);
+        Filter filter = configuration.parseAppenderFilters(props, filterPrefix, name);
+        String level = getProperty(THRESHOLD_PARAM);
+        String target = getProperty(TARGET);
+        return createAppender(name, layout, filter, level, target, configuration);
+    }
+
+    private <T extends Log4j1Configuration> Appender createAppender(String name, Layout layout, Filter filter,
+            String level, String target, T configuration) {
+        org.apache.logging.log4j.core.Layout<?> consoleLayout = null;
+
+        if (layout instanceof LayoutWrapper) {
+            consoleLayout = ((LayoutWrapper) layout).getLayout();
+        } else if (layout != null) {
+            consoleLayout = new LayoutAdapter(layout);
+        }
+        org.apache.logging.log4j.core.Filter consoleFilter = buildFilters(level, filter);
+        ConsoleAppender.Target consoleTarget = SYSTEM_ERR.equals(target)
                 ? ConsoleAppender.Target.SYSTEM_ERR : ConsoleAppender.Target.SYSTEM_OUT;
         return new AppenderWrapper(ConsoleAppender.newBuilder()
                 .setName(name)
                 .setTarget(consoleTarget)
                 .setLayout(consoleLayout)
                 .setFilter(consoleFilter)
-                .setConfiguration(factory.getConfiguration())
+                .setConfiguration(configuration)
                 .build());
     }
 }
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/DailyRollingFileAppenderBuilder.java b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/DailyRollingFileAppenderBuilder.java
index e37d385..1dd1885 100644
--- a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/DailyRollingFileAppenderBuilder.java
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/DailyRollingFileAppenderBuilder.java
@@ -19,14 +19,15 @@
 import org.apache.log4j.Appender;
 import org.apache.log4j.Layout;
 import org.apache.log4j.bridge.AppenderWrapper;
-import org.apache.log4j.bridge.FilterAdapter;
-import org.apache.log4j.bridge.FilterWrapper;
 import org.apache.log4j.bridge.LayoutAdapter;
 import org.apache.log4j.bridge.LayoutWrapper;
+import org.apache.log4j.builders.AbstractBuilder;
 import org.apache.log4j.builders.BooleanHolder;
 import org.apache.log4j.builders.Holder;
+import org.apache.log4j.config.Log4j1Configuration;
+import org.apache.log4j.config.PropertiesConfiguration;
 import org.apache.log4j.spi.Filter;
-import org.apache.log4j.xml.XmlConfigurationFactory;
+import org.apache.log4j.xml.XmlConfiguration;
 import org.apache.logging.log4j.Logger;
 import org.apache.logging.log4j.core.appender.RollingFileAppender;
 import org.apache.logging.log4j.core.appender.rolling.DefaultRolloverStrategy;
@@ -37,23 +38,41 @@
 import org.apache.logging.log4j.status.StatusLogger;
 import org.w3c.dom.Element;
 
+import java.util.Properties;
+
 import static org.apache.log4j.builders.BuilderManager.CATEGORY;
-import static org.apache.log4j.xml.XmlConfigurationFactory.*;
+import static org.apache.log4j.config.Log4j1Configuration.THRESHOLD_PARAM;
+import static org.apache.log4j.xml.XmlConfiguration.FILTER_TAG;
+import static org.apache.log4j.xml.XmlConfiguration.LAYOUT_TAG;
+import static org.apache.log4j.xml.XmlConfiguration.NAME_ATTR;
+import static org.apache.log4j.xml.XmlConfiguration.PARAM_TAG;
+import static org.apache.log4j.xml.XmlConfiguration.VALUE_ATTR;
+import static org.apache.log4j.xml.XmlConfiguration.forEachElement;
+
 
 /**
  * Build a Daily Rolling File Appender
  */
 @Plugin(name = "org.apache.log4j.DailyRollingFileAppender", category = CATEGORY)
-public class DailyRollingFileAppenderBuilder implements AppenderBuilder {
+public class DailyRollingFileAppenderBuilder extends AbstractBuilder implements AppenderBuilder {
 
     private static final Logger LOGGER = StatusLogger.getLogger();
 
+    public DailyRollingFileAppenderBuilder() {
+    }
+
+    public DailyRollingFileAppenderBuilder(String prefix, Properties props) {
+        super(prefix, props);
+    }
+
+
     @Override
-    public Appender parseAppender(Element appenderElement, XmlConfigurationFactory factory) {
+    public Appender parseAppender(final Element appenderElement, final XmlConfiguration config) {
         String name = appenderElement.getAttribute(NAME_ATTR);
         Holder<Layout> layout = new Holder<>();
         Holder<Filter> filter = new Holder<>();
         Holder<String> fileName = new Holder<>();
+        Holder<String> level = new Holder<>();
         Holder<Boolean> immediateFlush = new BooleanHolder();
         Holder<Boolean> append = new BooleanHolder();
         Holder<Boolean> bufferedIo = new BooleanHolder();
@@ -61,13 +80,13 @@
         forEachElement(appenderElement.getChildNodes(), (currentElement) -> {
             switch (currentElement.getTagName()) {
                 case LAYOUT_TAG:
-                    layout.set(factory.parseLayout(currentElement));
+                    layout.set(config.parseLayout(currentElement));
                     break;
                 case FILTER_TAG:
-                    filter.set(factory.parseFilters(currentElement));
+                    filter.set(config.parseFilters(currentElement));
                     break;
                 case PARAM_TAG: {
-                    switch (currentElement.getAttribute(NAME_ATTR).toLowerCase()) {
+                    switch (currentElement.getAttribute(NAME_ATTR)) {
                         case FILE_PARAM:
                             fileName.set(currentElement.getAttribute(VALUE_ATTR));
                             break;
@@ -89,7 +108,7 @@
                             }
                             break;
                         }
-                        case BUFFER_SIZE_PARAM:
+                        case BUFFER_SIZE_PARAM: {
                             String size = currentElement.getAttribute(VALUE_ATTR);
                             if (size != null) {
                                 bufferSize.set(Integer.parseInt(size));
@@ -97,46 +116,72 @@
                                 LOGGER.warn("No value provide for bufferSize parameter");
                             }
                             break;
+                        }
+                        case THRESHOLD_PARAM: {
+                            String value = currentElement.getAttribute(VALUE_ATTR);
+                            if (value == null) {
+                                LOGGER.warn("No value supplied for Threshold parameter, ignoring.");
+                            } else {
+                                level.set(value);
+                            }
+                            break;
+                        }
                     }
                     break;
                 }
             }
         });
+        return createAppender(name, layout.get(), filter.get(), fileName.get(), append.get(), immediateFlush.get(),
+                level.get(), bufferedIo.get(), bufferSize.get(), config);
+    }
+
+    @Override
+    public Appender parseAppender(final String name, final String layoutPrefix, final String filterPrefix,
+            final Properties props, final PropertiesConfiguration configuration) {
+        Layout layout = configuration.parseLayout(layoutPrefix, name, props);
+        Filter filter = configuration.parseAppenderFilters(props, filterPrefix, name);
+        String fileName = getProperty(FILE_PARAM);
+        String level = getProperty(THRESHOLD_PARAM);
+        boolean append = getBooleanProperty(APPEND_PARAM);
+        boolean immediateFlush = false;
+        boolean bufferedIo = getBooleanProperty(BUFFERED_IO_PARAM);
+        int bufferSize = Integer.parseInt(getProperty(BUFFER_SIZE_PARAM, "8192"));
+        return createAppender(name, layout, filter, fileName, append, immediateFlush,
+                level, bufferedIo, bufferSize, configuration);
+    }
+
+    private <T extends Log4j1Configuration> Appender createAppender(final String name, final Layout layout,
+            final Filter filter, final String fileName, final boolean append, boolean immediateFlush,
+            final String level, final boolean bufferedIo, final int bufferSize, final T configuration) {
 
         org.apache.logging.log4j.core.Layout<?> fileLayout = null;
-        org.apache.logging.log4j.core.Filter fileFilter = null;
-        if (bufferedIo.get()) {
-            immediateFlush.set(Boolean.TRUE);
+        if (bufferedIo) {
+            immediateFlush = true;
         }
-        if (layout.get() instanceof LayoutWrapper) {
-            fileLayout = ((LayoutWrapper) layout.get()).getLayout();
-        } else if (layout.get() != null) {
-            fileLayout = new LayoutAdapter(layout.get());
+        if (layout instanceof LayoutWrapper) {
+            fileLayout = ((LayoutWrapper) layout).getLayout();
+        } else if (layout != null) {
+            fileLayout = new LayoutAdapter(layout);
         }
-        if (filter.get() != null) {
-            if (filter.get() instanceof FilterWrapper) {
-                fileFilter = ((FilterWrapper) filter.get()).getFilter();
-            } else {
-                fileFilter = new FilterAdapter(filter.get());
-            }
-        }
-        if (fileName.get() == null) {
+        org.apache.logging.log4j.core.Filter fileFilter = buildFilters(level, filter);
+        if (fileName == null) {
             LOGGER.warn("Unable to create File Appender, no file name provided");
             return null;
         }
-        String filePattern = fileName.get() +"%d{yyy-MM-dd}";
+        String filePattern = fileName +"%d{yyy-MM-dd}";
         TriggeringPolicy policy = TimeBasedTriggeringPolicy.newBuilder().setModulate(true).build();
         RolloverStrategy strategy = DefaultRolloverStrategy.newBuilder()
-                .setConfig(factory.getConfiguration())
+                .setConfig(configuration)
                 .setMax(Integer.toString(Integer.MAX_VALUE))
                 .build();
         return new AppenderWrapper(RollingFileAppender.newBuilder()
                 .setName(name)
-                .setConfiguration(factory.getConfiguration())
+                .setConfiguration(configuration)
                 .setLayout(fileLayout)
                 .setFilter(fileFilter)
-                .setFileName(fileName.get())
-                .setImmediateFlush(immediateFlush.get())
+                .setFileName(fileName)
+                .setBufferSize(bufferSize)
+                .setImmediateFlush(immediateFlush)
                 .setFilePattern(filePattern)
                 .setPolicy(policy)
                 .setStrategy(strategy)
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/FileAppenderBuilder.java b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/FileAppenderBuilder.java
index 732581e..9d108c2 100644
--- a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/FileAppenderBuilder.java
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/FileAppenderBuilder.java
@@ -19,37 +19,54 @@
 import org.apache.log4j.Appender;
 import org.apache.log4j.Layout;
 import org.apache.log4j.bridge.AppenderWrapper;
-import org.apache.log4j.bridge.FilterAdapter;
-import org.apache.log4j.bridge.FilterWrapper;
 import org.apache.log4j.bridge.LayoutAdapter;
 import org.apache.log4j.bridge.LayoutWrapper;
+import org.apache.log4j.builders.AbstractBuilder;
 import org.apache.log4j.builders.BooleanHolder;
 import org.apache.log4j.builders.Holder;
+import org.apache.log4j.config.Log4j1Configuration;
+import org.apache.log4j.config.PropertiesConfiguration;
 import org.apache.log4j.spi.Filter;
-import org.apache.log4j.xml.XmlConfigurationFactory;
+import org.apache.log4j.xml.XmlConfiguration;
 import org.apache.logging.log4j.Logger;
 import org.apache.logging.log4j.core.appender.FileAppender;
 import org.apache.logging.log4j.plugins.Plugin;
 import org.apache.logging.log4j.status.StatusLogger;
 import org.w3c.dom.Element;
 
+import java.util.Properties;
+
 import static org.apache.log4j.builders.BuilderManager.CATEGORY;
-import static org.apache.log4j.xml.XmlConfigurationFactory.*;
+import static org.apache.log4j.config.Log4j1Configuration.THRESHOLD_PARAM;
+import static org.apache.log4j.xml.XmlConfiguration.FILTER_TAG;
+import static org.apache.log4j.xml.XmlConfiguration.LAYOUT_TAG;
+import static org.apache.log4j.xml.XmlConfiguration.PARAM_TAG;
+import static org.apache.log4j.xml.XmlConfiguration.forEachElement;
+import static org.apache.log4j.xml.XmlConfiguration.NAME_ATTR;
+import static org.apache.log4j.xml.XmlConfiguration.VALUE_ATTR;
 
 /**
  * Build a File Appender
  */
 @Plugin(name = "org.apache.log4j.FileAppender", category = CATEGORY)
-public class FileAppenderBuilder implements AppenderBuilder {
+public class FileAppenderBuilder extends AbstractBuilder implements AppenderBuilder {
 
     private static final Logger LOGGER = StatusLogger.getLogger();
 
+    public FileAppenderBuilder() {
+    }
+
+    public FileAppenderBuilder(String prefix, Properties props) {
+        super(prefix, props);
+    }
+
     @Override
-    public Appender parseAppender(Element appenderElement, XmlConfigurationFactory factory) {
+    public Appender parseAppender(Element appenderElement, XmlConfiguration config) {
         String name = appenderElement.getAttribute(NAME_ATTR);
         Holder<Layout> layout = new Holder<>();
         Holder<Filter> filter = new Holder<>();
         Holder<String> fileName = new Holder<>();
+        Holder<String> level = new Holder<>();
         Holder<Boolean> immediateFlush = new BooleanHolder();
         Holder<Boolean> append = new BooleanHolder();
         Holder<Boolean> bufferedIo = new BooleanHolder();
@@ -57,13 +74,13 @@
         forEachElement(appenderElement.getChildNodes(), (currentElement) -> {
             switch (currentElement.getTagName()) {
                 case LAYOUT_TAG:
-                    layout.set(factory.parseLayout(currentElement));
+                    layout.set(config.parseLayout(currentElement));
                     break;
                 case FILTER_TAG:
-                    filter.set(factory.parseFilters(currentElement));
+                    filter.set(config.parseFilters(currentElement));
                     break;
                 case PARAM_TAG: {
-                    switch (currentElement.getAttribute(NAME_ATTR).toLowerCase()) {
+                    switch (currentElement.getAttribute(NAME_ATTR)) {
                         case FILE_PARAM:
                             fileName.set(currentElement.getAttribute(VALUE_ATTR));
                             break;
@@ -85,7 +102,7 @@
                             }
                             break;
                         }
-                        case BUFFER_SIZE_PARAM:
+                        case BUFFER_SIZE_PARAM: {
                             String size = currentElement.getAttribute(VALUE_ATTR);
                             if (size != null) {
                                 bufferSize.set(Integer.parseInt(size));
@@ -93,43 +110,69 @@
                                 LOGGER.warn("No value provide for bufferSize parameter");
                             }
                             break;
+                        }
+                        case THRESHOLD_PARAM: {
+                            String value = currentElement.getAttribute(VALUE_ATTR);
+                            if (value == null) {
+                                LOGGER.warn("No value supplied for Threshold parameter, ignoring.");
+                            } else {
+                                level.set(value);
+                            }
+                            break;
+                        }
                     }
                     break;
                 }
             }
         });
 
+        return createAppender(name, config, layout.get(), filter.get(), fileName.get(), level.get(),
+                immediateFlush.get(), append.get(), bufferedIo.get(), bufferSize.get());
+    }
+
+
+    @Override
+    public Appender parseAppender(final String name, final String layoutPrefix, final String filterPrefix,
+            final Properties props, final PropertiesConfiguration configuration) {
+        Layout layout = configuration.parseLayout(layoutPrefix, name, props);
+        Filter filter = configuration.parseAppenderFilters(props, filterPrefix, name);
+        String level = getProperty(THRESHOLD_PARAM);
+        String fileName = getProperty(FILE_PARAM);
+        boolean append = getBooleanProperty(APPEND_PARAM);
+        boolean immediateFlush = false;
+        boolean bufferedIo = getBooleanProperty(BUFFERED_IO_PARAM);
+        int bufferSize = Integer.parseInt(getProperty(BUFFER_SIZE_PARAM, "8192"));
+        return createAppender(name, configuration, layout, filter, fileName, level, immediateFlush,
+                append, bufferedIo, bufferSize);
+    }
+
+    private Appender createAppender(final String name, final Log4j1Configuration configuration, final Layout layout,
+            final Filter filter, final String fileName, String level, boolean immediateFlush, final boolean append,
+            final boolean bufferedIo, final int bufferSize) {
         org.apache.logging.log4j.core.Layout<?> fileLayout = null;
-        org.apache.logging.log4j.core.Filter fileFilter = null;
-        if (bufferedIo.get()) {
-            immediateFlush.set(Boolean.TRUE);
+        if (bufferedIo) {
+            immediateFlush = true;
         }
-        if (layout.get() instanceof LayoutWrapper) {
-            fileLayout = ((LayoutWrapper) layout.get()).getLayout();
-        } else if (layout.get() != null) {
-            fileLayout = new LayoutAdapter(layout.get());
+        if (layout instanceof LayoutWrapper) {
+            fileLayout = ((LayoutWrapper) layout).getLayout();
+        } else if (layout != null) {
+            fileLayout = new LayoutAdapter(layout);
         }
-        if (filter.get() != null) {
-            if (filter.get() instanceof FilterWrapper) {
-                fileFilter = ((FilterWrapper) filter.get()).getFilter();
-            } else {
-                fileFilter = new FilterAdapter(filter.get());
-            }
-        }
-        if (fileName.get() == null) {
+        org.apache.logging.log4j.core.Filter fileFilter = buildFilters(level, filter);
+        if (fileName == null) {
             LOGGER.warn("Unable to create File Appender, no file name provided");
             return null;
         }
         return new AppenderWrapper(FileAppender.newBuilder()
                 .setName(name)
-                .setConfiguration(factory.getConfiguration())
+                .setConfiguration(configuration)
                 .setLayout(fileLayout)
                 .setFilter(fileFilter)
-                .setFileName(fileName.get())
-                .setImmediateFlush(immediateFlush.get())
-                .setAppend(append.get())
-                .setBufferedIo(bufferedIo.get())
-                .setBufferSize(bufferSize.get())
+                .setFileName(fileName)
+                .setImmediateFlush(immediateFlush)
+                .setAppend(append)
+                .setBufferedIo(bufferedIo)
+                .setBufferSize(bufferSize)
                 .build());
     }
 }
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/NullAppenderBuilder.java b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/NullAppenderBuilder.java
index b972d6b..8b69d39 100644
--- a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/NullAppenderBuilder.java
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/NullAppenderBuilder.java
@@ -18,13 +18,16 @@
 
 import org.apache.log4j.Appender;
 import org.apache.log4j.bridge.AppenderWrapper;
-import org.apache.log4j.xml.XmlConfigurationFactory;
+import org.apache.log4j.config.PropertiesConfiguration;
+import org.apache.log4j.xml.XmlConfiguration;
 import org.apache.logging.log4j.Logger;
 import org.apache.logging.log4j.core.appender.NullAppender;
 import org.apache.logging.log4j.plugins.Plugin;
 import org.apache.logging.log4j.status.StatusLogger;
 import org.w3c.dom.Element;
 
+import java.util.Properties;
+
 import static org.apache.log4j.builders.BuilderManager.CATEGORY;
 
 /**
@@ -36,8 +39,15 @@
     private static final Logger LOGGER = StatusLogger.getLogger();
 
     @Override
-    public Appender parseAppender(Element appenderElement, XmlConfigurationFactory factory) {
+    public Appender parseAppender(Element appenderElement, XmlConfiguration config) {
         String name = appenderElement.getAttribute("name");
         return new AppenderWrapper(NullAppender.createAppender(name));
     }
+
+
+    @Override
+    public Appender parseAppender(final String name, final String layoutPrefix,
+            final String filterPrefix, final Properties props, final PropertiesConfiguration configuration) {
+        return new AppenderWrapper(NullAppender.createAppender(name));
+    }
 }
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/RollingFileAppenderBuilder.java b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/RollingFileAppenderBuilder.java
index 81c3901..ccc8099 100644
--- a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/RollingFileAppenderBuilder.java
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/RollingFileAppenderBuilder.java
@@ -19,14 +19,15 @@
 import org.apache.log4j.Appender;
 import org.apache.log4j.Layout;
 import org.apache.log4j.bridge.AppenderWrapper;
-import org.apache.log4j.bridge.FilterAdapter;
-import org.apache.log4j.bridge.FilterWrapper;
 import org.apache.log4j.bridge.LayoutAdapter;
 import org.apache.log4j.bridge.LayoutWrapper;
+import org.apache.log4j.builders.AbstractBuilder;
 import org.apache.log4j.builders.BooleanHolder;
 import org.apache.log4j.builders.Holder;
+import org.apache.log4j.config.Log4j1Configuration;
+import org.apache.log4j.config.PropertiesConfiguration;
 import org.apache.log4j.spi.Filter;
-import org.apache.log4j.xml.XmlConfigurationFactory;
+import org.apache.log4j.xml.XmlConfiguration;
 import org.apache.logging.log4j.Logger;
 import org.apache.logging.log4j.core.appender.RollingFileAppender;
 import org.apache.logging.log4j.core.appender.rolling.CompositeTriggeringPolicy;
@@ -39,19 +40,35 @@
 import org.apache.logging.log4j.status.StatusLogger;
 import org.w3c.dom.Element;
 
+import java.util.Properties;
+
 import static org.apache.log4j.builders.BuilderManager.CATEGORY;
-import static org.apache.log4j.xml.XmlConfigurationFactory.*;
+import static org.apache.log4j.config.Log4j1Configuration.THRESHOLD_PARAM;
+import static org.apache.log4j.xml.XmlConfiguration.FILTER_TAG;
+import static org.apache.log4j.xml.XmlConfiguration.LAYOUT_TAG;
+import static org.apache.log4j.xml.XmlConfiguration.NAME_ATTR;
+import static org.apache.log4j.xml.XmlConfiguration.PARAM_TAG;
+import static org.apache.log4j.xml.XmlConfiguration.VALUE_ATTR;
+import static org.apache.log4j.xml.XmlConfiguration.forEachElement;
+
 
 /**
  * Build a File Appender
  */
 @Plugin(name = "org.apache.log4j.RollingFileAppender", category = CATEGORY)
-public class RollingFileAppenderBuilder implements AppenderBuilder {
+public class RollingFileAppenderBuilder extends AbstractBuilder implements AppenderBuilder {
 
     private static final Logger LOGGER = StatusLogger.getLogger();
 
+    public RollingFileAppenderBuilder() {
+    }
+
+    public RollingFileAppenderBuilder(String prefix, Properties props) {
+        super(prefix, props);
+    }
+
     @Override
-    public Appender parseAppender(Element appenderElement, XmlConfigurationFactory factory) {
+    public Appender parseAppender(Element appenderElement, XmlConfiguration config) {
         String name = appenderElement.getAttribute(NAME_ATTR);
         Holder<Layout> layout = new Holder<>();
         Holder<Filter> filter = new Holder<>();
@@ -62,16 +79,17 @@
         Holder<Integer> bufferSize = new Holder<>(8192);
         Holder<String> maxSize = new Holder<>();
         Holder<String> maxBackups = new Holder<>();
+        Holder<String> level = new Holder<>();
         forEachElement(appenderElement.getChildNodes(), (currentElement) -> {
             switch (currentElement.getTagName()) {
                 case LAYOUT_TAG:
-                    layout.set(factory.parseLayout(currentElement));
+                    layout.set(config.parseLayout(currentElement));
                     break;
                 case FILTER_TAG:
-                    filter.set(factory.parseFilters(currentElement));
+                    filter.set(config.parseFilters(currentElement));
                     break;
                 case PARAM_TAG: {
-                    switch (currentElement.getAttribute(NAME_ATTR).toLowerCase()) {
+                    switch (currentElement.getAttribute(NAME_ATTR)) {
                         case FILE_PARAM:
                             fileName.set(currentElement.getAttribute(VALUE_ATTR));
                             break;
@@ -120,48 +138,73 @@
                             }
                             break;
                         }
+                        case THRESHOLD_PARAM: {
+                            String value = currentElement.getAttribute(VALUE_ATTR);
+                            if (value == null) {
+                                LOGGER.warn("No value supplied for Threshold parameter, ignoring.");
+                            } else {
+                                level.set(value);
+                            }
+                            break;
+                        }
                     }
                     break;
                 }
             }
         });
+        return createAppender(name, config, layout.get(), filter.get(), bufferedIo.get(), immediateFlush.get(),
+                fileName.get(), level.get(), maxSize.get(), maxBackups.get());
+    }
 
+
+    @Override
+    public Appender parseAppender(final String name, final String layoutPrefix,
+            final String filterPrefix, final Properties props, final PropertiesConfiguration configuration) {
+        Layout layout = configuration.parseLayout(layoutPrefix, name, props);
+        Filter filter = configuration.parseAppenderFilters(props, filterPrefix, name);
+        String fileName = getProperty(FILE_PARAM);
+        String level = getProperty(THRESHOLD_PARAM);
+        boolean immediateFlush = false;
+        boolean bufferedIo = getBooleanProperty(BUFFERED_IO_PARAM);
+        String maxSize = getProperty(MAX_SIZE_PARAM);
+        String maxBackups = getProperty(MAX_BACKUP_INDEX);
+        return createAppender(name, configuration, layout, filter, bufferedIo, immediateFlush, fileName, level, maxSize,
+                maxBackups);
+    }
+
+    private Appender createAppender(final String name, final Log4j1Configuration config, final Layout layout,
+            final Filter filter, final boolean bufferedIo, boolean immediateFlush, final String fileName,
+            final String level, final String maxSize, final String maxBackups) {
         org.apache.logging.log4j.core.Layout<?> fileLayout = null;
-        org.apache.logging.log4j.core.Filter fileFilter = null;
-        if (bufferedIo.get()) {
-            immediateFlush.set(Boolean.TRUE);
+        if (bufferedIo) {
+            immediateFlush = true;
         }
-        if (layout.get() instanceof LayoutWrapper) {
-            fileLayout = ((LayoutWrapper) layout.get()).getLayout();
-        } else if (layout.get() != null) {
-            fileLayout = new LayoutAdapter(layout.get());
+        if (layout instanceof LayoutWrapper) {
+            fileLayout = ((LayoutWrapper) layout).getLayout();
+        } else if (layout != null) {
+            fileLayout = new LayoutAdapter(layout);
         }
-        if (filter.get() != null) {
-            if (filter.get() instanceof FilterWrapper) {
-                fileFilter = ((FilterWrapper) filter.get()).getFilter();
-            } else {
-                fileFilter = new FilterAdapter(filter.get());
-            }
-        }
-        if (fileName.get() == null) {
+        org.apache.logging.log4j.core.Filter fileFilter = buildFilters(level, filter);
+        if (fileName == null) {
             LOGGER.warn("Unable to create File Appender, no file name provided");
             return null;
         }
-        String filePattern = fileName.get() +"%d{yyy-MM-dd}";
+        String filePattern = fileName +"%d{yyy-MM-dd}";
         TriggeringPolicy timePolicy = TimeBasedTriggeringPolicy.newBuilder().setModulate(true).build();
-        SizeBasedTriggeringPolicy sizePolicy = SizeBasedTriggeringPolicy.createPolicy(maxSize.get());
+        SizeBasedTriggeringPolicy sizePolicy = SizeBasedTriggeringPolicy.createPolicy(maxSize);
         CompositeTriggeringPolicy policy = CompositeTriggeringPolicy.createPolicy(sizePolicy, timePolicy);
         RolloverStrategy strategy = DefaultRolloverStrategy.newBuilder()
-                .setConfig(factory.getConfiguration())
-                .setMax(maxBackups.get())
+                .setConfig(config)
+                .setMax(maxBackups)
                 .build();
         return new AppenderWrapper(RollingFileAppender.newBuilder()
                 .setName(name)
-                .setConfiguration(factory.getConfiguration())
+                .setConfiguration(config)
                 .setLayout(fileLayout)
                 .setFilter(fileFilter)
-                .setImmediateFlush(immediateFlush.get())
-                .setFileName(fileName.get())
+                .setBufferedIo(bufferedIo)
+                .setImmediateFlush(immediateFlush)
+                .setFileName(fileName)
                 .setFilePattern(filePattern)
                 .setPolicy(policy)
                 .setStrategy(strategy)
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/filter/DenyAllFilterBuilder.java b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/filter/DenyAllFilterBuilder.java
index b449ca0..f15d6e5 100644
--- a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/filter/DenyAllFilterBuilder.java
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/filter/DenyAllFilterBuilder.java
@@ -17,8 +17,9 @@
 package org.apache.log4j.builders.filter;
 
 import org.apache.log4j.bridge.FilterWrapper;
+import org.apache.log4j.config.PropertiesConfiguration;
 import org.apache.log4j.spi.Filter;
-import org.apache.log4j.xml.XmlConfigurationFactory;
+import org.apache.log4j.xml.XmlConfiguration;
 import org.apache.logging.log4j.Logger;
 import org.apache.logging.log4j.plugins.Plugin;
 import org.apache.logging.log4j.core.filter.DenyAllFilter;
@@ -36,7 +37,12 @@
     private static final Logger LOGGER = StatusLogger.getLogger();
 
     @Override
-    public Filter parseFilter(Element filterElement, XmlConfigurationFactory factory) {
+    public Filter parseFilter(Element filterElement, XmlConfiguration config) {
+        return new FilterWrapper(DenyAllFilter.newBuilder().build());
+    }
+
+    @Override
+    public Filter parseFilter(PropertiesConfiguration config) {
         return new FilterWrapper(DenyAllFilter.newBuilder().build());
     }
 }
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/filter/FilterBuilder.java b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/filter/FilterBuilder.java
index fbeaefb..f26e7cb 100644
--- a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/filter/FilterBuilder.java
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/filter/FilterBuilder.java
@@ -16,8 +16,9 @@
  */
 package org.apache.log4j.builders.filter;
 
+import org.apache.log4j.config.PropertiesConfiguration;
 import org.apache.log4j.spi.Filter;
-import org.apache.log4j.xml.XmlConfigurationFactory;
+import org.apache.log4j.xml.XmlConfiguration;
 import org.w3c.dom.Element;
 
 /**
@@ -25,6 +26,8 @@
  */
 public interface FilterBuilder {
 
-    Filter parseFilter(Element element, XmlConfigurationFactory factory);
+    Filter parseFilter(Element element, XmlConfiguration config);
+
+    Filter parseFilter(PropertiesConfiguration config);
 
 }
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/filter/LevelMatchFilterBuilder.java b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/filter/LevelMatchFilterBuilder.java
index 0d13e3f..5b1e435 100644
--- a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/filter/LevelMatchFilterBuilder.java
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/filter/LevelMatchFilterBuilder.java
@@ -17,10 +17,12 @@
 package org.apache.log4j.builders.filter;
 
 import org.apache.log4j.bridge.FilterWrapper;
+import org.apache.log4j.builders.AbstractBuilder;
 import org.apache.log4j.builders.BooleanHolder;
 import org.apache.log4j.builders.Holder;
+import org.apache.log4j.config.PropertiesConfiguration;
 import org.apache.log4j.spi.Filter;
-import org.apache.log4j.xml.XmlConfigurationFactory;
+import org.apache.log4j.xml.XmlConfiguration;
 import org.apache.logging.log4j.Level;
 import org.apache.logging.log4j.Logger;
 import org.apache.logging.log4j.plugins.Plugin;
@@ -28,27 +30,36 @@
 import org.apache.logging.log4j.status.StatusLogger;
 import org.w3c.dom.Element;
 
+import java.util.Properties;
+
 import static org.apache.log4j.builders.BuilderManager.CATEGORY;
-import static org.apache.log4j.xml.XmlConfigurationFactory.*;
-import static org.apache.log4j.xml.XmlConfigurationFactory.VALUE_ATTR;
+import static org.apache.log4j.xml.XmlConfiguration.*;
+import static org.apache.log4j.xml.XmlConfiguration.VALUE_ATTR;
 
 /**
  * Build a Level match failter.
  */
 @Plugin(name = "org.apache.log4j.varia.LevelMatchFilter", category = CATEGORY)
-public class LevelMatchFilterBuilder implements FilterBuilder {
+public class LevelMatchFilterBuilder extends AbstractBuilder implements FilterBuilder {
 
     private static final Logger LOGGER = StatusLogger.getLogger();
-    private static final String LEVEL = "level";
-    private static final String ACCEPT_ON_MATCH = "acceptonmatch";
+    private static final String LEVEL = "LevelToMatch";
+    private static final String ACCEPT_ON_MATCH = "AcceptOnMatch";
+
+    public LevelMatchFilterBuilder() {
+    }
+
+    public LevelMatchFilterBuilder(String prefix, Properties props) {
+        super(prefix, props);
+    }
 
     @Override
-    public Filter parseFilter(Element filterElement, XmlConfigurationFactory factory) {
+    public Filter parseFilter(Element filterElement, XmlConfiguration config) {
         final Holder<String> level = new Holder<>();
         final Holder<Boolean> acceptOnMatch = new BooleanHolder();
         forEachElement(filterElement.getElementsByTagName("param"), (currentElement) -> {
             if (currentElement.getTagName().equals("param")) {
-                switch (currentElement.getAttribute(NAME_ATTR).toLowerCase()) {
+                switch (currentElement.getAttribute(NAME_ATTR)) {
                     case LEVEL:
                         level.set(currentElement.getAttribute(VALUE_ATTR));
                         break;
@@ -58,11 +69,22 @@
                 }
             }
         });
+        return createFilter(level.get(), acceptOnMatch.get());
+    }
+
+    @Override
+    public Filter parseFilter(PropertiesConfiguration config) {
+        String level = getProperty(LEVEL);
+        boolean acceptOnMatch = getBooleanProperty(ACCEPT_ON_MATCH);
+        return createFilter(level, acceptOnMatch);
+    }
+
+    private Filter createFilter(String level, boolean acceptOnMatch) {
         Level lvl = Level.ERROR;
-        if (level.get() != null) {
-            lvl = Level.toLevel(level.get(), Level.ERROR);
+        if (level != null) {
+            lvl = Level.toLevel(level, Level.ERROR);
         }
-        org.apache.logging.log4j.core.Filter.Result onMatch = acceptOnMatch.get() != null && acceptOnMatch.get()
+        org.apache.logging.log4j.core.Filter.Result onMatch = acceptOnMatch
                 ? org.apache.logging.log4j.core.Filter.Result.ACCEPT
                 : org.apache.logging.log4j.core.Filter.Result.DENY;
         return new FilterWrapper(LevelMatchFilter.newBuilder()
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/filter/LevelRangeFilterBuilder.java b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/filter/LevelRangeFilterBuilder.java
index 838aafdc..ca2c70f 100644
--- a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/filter/LevelRangeFilterBuilder.java
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/filter/LevelRangeFilterBuilder.java
@@ -17,10 +17,12 @@
 package org.apache.log4j.builders.filter;
 
 import org.apache.log4j.bridge.FilterWrapper;
+import org.apache.log4j.builders.AbstractBuilder;
 import org.apache.log4j.builders.BooleanHolder;
 import org.apache.log4j.builders.Holder;
+import org.apache.log4j.config.PropertiesConfiguration;
 import org.apache.log4j.spi.Filter;
-import org.apache.log4j.xml.XmlConfigurationFactory;
+import org.apache.log4j.xml.XmlConfiguration;
 import org.apache.logging.log4j.Level;
 import org.apache.logging.log4j.Logger;
 import org.apache.logging.log4j.plugins.Plugin;
@@ -29,28 +31,37 @@
 import org.w3c.dom.Element;
 
 
+import java.util.Properties;
+
 import static org.apache.log4j.builders.BuilderManager.CATEGORY;
-import static org.apache.log4j.xml.XmlConfigurationFactory.*;
+import static org.apache.log4j.xml.XmlConfiguration.*;
 
 /**
  * Build a Level match failter.
  */
 @Plugin(name = "org.apache.log4j.varia.LevelRangeFilter", category = CATEGORY)
-public class LevelRangeFilterBuilder implements FilterBuilder {
+public class LevelRangeFilterBuilder extends AbstractBuilder implements FilterBuilder {
 
     private static final Logger LOGGER = StatusLogger.getLogger();
-    private static final String LEVEL_MAX = "levelmax";
-    private static final String LEVEL_MIN = "levelmin";
-    private static final String ACCEPT_ON_MATCH = "acceptonmatch";
+    private static final String LEVEL_MAX = "LevelMax";
+    private static final String LEVEL_MIN = "LevelMin";
+    private static final String ACCEPT_ON_MATCH = "AcceptOnMatch";
+
+    public LevelRangeFilterBuilder() {
+    }
+
+    public LevelRangeFilterBuilder(String prefix, Properties props) {
+        super(prefix, props);
+    }
 
     @Override
-    public Filter parseFilter(Element filterElement, XmlConfigurationFactory factory) {
+    public Filter parseFilter(Element filterElement, XmlConfiguration config) {
         final Holder<String> levelMax = new Holder<>();
         final Holder<String> levelMin = new Holder<>();
         final Holder<Boolean> acceptOnMatch = new BooleanHolder();
         forEachElement(filterElement.getElementsByTagName("param"), (currentElement) -> {
             if (currentElement.getTagName().equals("param")) {
-                switch (currentElement.getAttribute(NAME_ATTR).toLowerCase()) {
+                switch (currentElement.getAttribute(NAME_ATTR)) {
                     case LEVEL_MAX:
                         levelMax.set(currentElement.getAttribute(VALUE_ATTR));
                         break;
@@ -63,15 +74,27 @@
                 }
             }
         });
+        return createFilter(levelMax.get(), levelMin.get(), acceptOnMatch.get());
+    }
+
+    @Override
+    public Filter parseFilter(PropertiesConfiguration config) {
+        String levelMax = getProperty(LEVEL_MAX);
+        String levelMin = getProperty(LEVEL_MIN);
+        boolean acceptOnMatch = getBooleanProperty(ACCEPT_ON_MATCH);
+        return createFilter(levelMax, levelMin, acceptOnMatch);
+    }
+
+    private Filter createFilter(String levelMax, String levelMin, boolean acceptOnMatch) {
         Level max = Level.FATAL;
         Level min = Level.TRACE;
-        if (levelMax.get() != null) {
-            max = Level.toLevel(levelMax.get(), Level.FATAL);
+        if (levelMax != null) {
+            max = Level.toLevel(levelMax, Level.FATAL);
         }
-        if (levelMin.get() != null) {
-            min = Level.toLevel(levelMin.get(), Level.DEBUG);
+        if (levelMin != null) {
+            min = Level.toLevel(levelMin, Level.DEBUG);
         }
-        org.apache.logging.log4j.core.Filter.Result onMatch = acceptOnMatch.get() != null && acceptOnMatch.get()
+        org.apache.logging.log4j.core.Filter.Result onMatch = acceptOnMatch
                 ? org.apache.logging.log4j.core.Filter.Result.ACCEPT
                 : org.apache.logging.log4j.core.Filter.Result.NEUTRAL;
 
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/filter/StringMatchFilterBuilder.java b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/filter/StringMatchFilterBuilder.java
index a8ec3bf..f81dbdc 100644
--- a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/filter/StringMatchFilterBuilder.java
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/filter/StringMatchFilterBuilder.java
@@ -17,36 +17,40 @@
 package org.apache.log4j.builders.filter;
 
 import org.apache.log4j.bridge.FilterWrapper;
+import org.apache.log4j.builders.AbstractBuilder;
 import org.apache.log4j.builders.BooleanHolder;
 import org.apache.log4j.builders.Holder;
+import org.apache.log4j.config.PropertiesConfiguration;
 import org.apache.log4j.spi.Filter;
-import org.apache.log4j.xml.XmlConfigurationFactory;
+import org.apache.log4j.xml.XmlConfiguration;
 import org.apache.logging.log4j.Logger;
 import org.apache.logging.log4j.plugins.Plugin;
 import org.apache.logging.log4j.core.filter.StringMatchFilter;
 import org.apache.logging.log4j.status.StatusLogger;
 import org.w3c.dom.Element;
 
+import java.util.Properties;
+
 import static org.apache.log4j.builders.BuilderManager.CATEGORY;
-import static org.apache.log4j.xml.XmlConfigurationFactory.*;
+import static org.apache.log4j.xml.XmlConfiguration.*;
 
 /**
  * Build a String match filter.
  */
 @Plugin(name = "org.apache.log4j.varia.StringMatchFilter", category = CATEGORY)
-public class StringMatchFilterBuilder implements FilterBuilder {
+public class StringMatchFilterBuilder extends AbstractBuilder implements FilterBuilder {
 
     private static final Logger LOGGER = StatusLogger.getLogger();
-    private static final String STRING_TO_MATCH = "stringtomatch";
-    private static final String ACCEPT_ON_MATCH = "acceptonmatch";
+    private static final String STRING_TO_MATCH = "StringToMatch";
+    private static final String ACCEPT_ON_MATCH = "AcceptOnMatch";
 
     @Override
-    public Filter parseFilter(Element filterElement, XmlConfigurationFactory factory) {
+    public Filter parseFilter(Element filterElement, XmlConfiguration config) {
         final Holder<Boolean> acceptOnMatch = new BooleanHolder();
         final Holder<String> text = new Holder<>();
         forEachElement(filterElement.getElementsByTagName("param"), (currentElement) -> {
             if (currentElement.getTagName().equals("param")) {
-                switch (currentElement.getAttribute(NAME_ATTR).toLowerCase()) {
+                switch (currentElement.getAttribute(NAME_ATTR)) {
                     case STRING_TO_MATCH:
                         text.set(currentElement.getAttribute(VALUE_ATTR));
                         break;
@@ -57,15 +61,26 @@
                 }
             }
         });
-        if (text.get() == null) {
+        return createFilter(text.get(), acceptOnMatch.get());
+    }
+
+    @Override
+    public Filter parseFilter(PropertiesConfiguration config) {
+        String text = getProperty(STRING_TO_MATCH);
+        boolean acceptOnMatch = getBooleanProperty(ACCEPT_ON_MATCH);
+        return createFilter(text, acceptOnMatch);
+    }
+
+    private Filter createFilter(String text, boolean acceptOnMatch) {
+        if (text == null) {
             LOGGER.warn("No text provided for StringMatchFilter");
             return null;
         }
-        org.apache.logging.log4j.core.Filter.Result onMatch = acceptOnMatch.get() != null && acceptOnMatch.get()
+        org.apache.logging.log4j.core.Filter.Result onMatch = acceptOnMatch
                 ? org.apache.logging.log4j.core.Filter.Result.ACCEPT
                 : org.apache.logging.log4j.core.Filter.Result.DENY;
         return new FilterWrapper(StringMatchFilter.newBuilder()
-                .setMatchString(text.get())
+                .setMatchString(text)
                 .setOnMatch(onMatch)
                 .setOnMismatch(org.apache.logging.log4j.core.Filter.Result.NEUTRAL)
                 .build());
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/layout/HtmlLayoutBuilder.java b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/layout/HtmlLayoutBuilder.java
index 0cbbf70..423350e 100644
--- a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/layout/HtmlLayoutBuilder.java
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/layout/HtmlLayoutBuilder.java
@@ -18,43 +18,68 @@
 
 import org.apache.log4j.Layout;
 import org.apache.log4j.bridge.LayoutWrapper;
+import org.apache.log4j.builders.AbstractBuilder;
 import org.apache.log4j.builders.BooleanHolder;
 import org.apache.log4j.builders.Holder;
-import org.apache.log4j.xml.XmlConfigurationFactory;
+import org.apache.log4j.config.PropertiesConfiguration;
+import org.apache.log4j.xml.XmlConfiguration;
 import org.apache.logging.log4j.Logger;
 import org.apache.logging.log4j.plugins.Plugin;
 import org.apache.logging.log4j.core.layout.HtmlLayout;
 import org.apache.logging.log4j.status.StatusLogger;
 import org.w3c.dom.Element;
 
+import java.util.Properties;
+
 import static org.apache.log4j.builders.BuilderManager.CATEGORY;
-import static org.apache.log4j.xml.XmlConfigurationFactory.*;
+import static org.apache.log4j.xml.XmlConfiguration.*;
 
 /**
  * Build a Pattern Layout
  */
 @Plugin(name = "org.apache.log4j.HTMLLayout", category = CATEGORY)
-public class HtmlLayoutBuilder implements LayoutBuilder {
+public class HtmlLayoutBuilder extends AbstractBuilder implements LayoutBuilder {
 
     private static final Logger LOGGER = StatusLogger.getLogger();
 
+    private static final String TITLE = "Title";
+    private static final String LOCATION_INFO = "LocationInfo";
+
+    public HtmlLayoutBuilder() {
+    }
+
+    public HtmlLayoutBuilder(String prefix, Properties props) {
+        super(prefix, props);
+    }
+
 
     @Override
-    public Layout parseLayout(Element layoutElement, XmlConfigurationFactory factory) {
+    public Layout parseLayout(Element layoutElement, XmlConfiguration config) {
         final Holder<String> title = new Holder<>();
         final Holder<Boolean> locationInfo = new BooleanHolder();
         forEachElement(layoutElement.getElementsByTagName("param"), (currentElement) -> {
-            if (currentElement.getTagName().equals("param")) {
-                if ("title".equalsIgnoreCase(currentElement.getAttribute("name"))) {
+            if (currentElement.getTagName().equals(PARAM_TAG)) {
+                if (TITLE.equalsIgnoreCase(currentElement.getAttribute("name"))) {
                     title.set(currentElement.getAttribute("value"));
-                } else if ("locationInfo".equalsIgnoreCase(currentElement.getAttribute("name"))) {
+                } else if (LOCATION_INFO.equalsIgnoreCase(currentElement.getAttribute("name"))) {
                     locationInfo.set(Boolean.parseBoolean(currentElement.getAttribute("value")));
                 }
             }
         });
+        return createLayout(title.get(), locationInfo.get());
+    }
+
+    @Override
+    public Layout parseLayout(PropertiesConfiguration config) {
+        String title = getProperty(TITLE);
+        boolean locationInfo = getBooleanProperty(LOCATION_INFO);
+        return createLayout(title, locationInfo);
+    }
+
+    private Layout createLayout(String title, boolean locationInfo) {
         return new LayoutWrapper(HtmlLayout.newBuilder()
-                .setTitle(title.get())
-                .setLocationInfo(locationInfo.get())
+                .setTitle(title)
+                .setLocationInfo(locationInfo)
                 .build());
     }
 }
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/layout/LayoutBuilder.java b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/layout/LayoutBuilder.java
index 9dc88f3..4f28284 100644
--- a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/layout/LayoutBuilder.java
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/layout/LayoutBuilder.java
@@ -17,7 +17,8 @@
 package org.apache.log4j.builders.layout;
 
 import org.apache.log4j.Layout;
-import org.apache.log4j.xml.XmlConfigurationFactory;
+import org.apache.log4j.config.PropertiesConfiguration;
+import org.apache.log4j.xml.XmlConfiguration;
 import org.w3c.dom.Element;
 
 /**
@@ -25,6 +26,7 @@
  */
 public interface LayoutBuilder {
 
-    Layout parseLayout(Element element, XmlConfigurationFactory factory);
+    Layout parseLayout(Element element, XmlConfiguration config);
 
+    Layout parseLayout(PropertiesConfiguration config);
 }
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/layout/PatternLayoutBuilder.java b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/layout/PatternLayoutBuilder.java
index e066778..1849768 100644
--- a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/layout/PatternLayoutBuilder.java
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/layout/PatternLayoutBuilder.java
@@ -18,29 +18,43 @@
 
 import org.apache.log4j.Layout;
 import org.apache.log4j.bridge.LayoutWrapper;
-import org.apache.log4j.xml.XmlConfigurationFactory;
+import org.apache.log4j.builders.AbstractBuilder;
+import org.apache.log4j.config.Log4j1Configuration;
+import org.apache.log4j.config.PropertiesConfiguration;
+import org.apache.log4j.xml.XmlConfiguration;
 import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.core.config.plugins.PluginAliases;
 import org.apache.logging.log4j.core.layout.PatternLayout;
 import org.apache.logging.log4j.plugins.Plugin;
-import org.apache.logging.log4j.plugins.PluginAliases;
 import org.apache.logging.log4j.status.StatusLogger;
 import org.w3c.dom.Element;
 import org.w3c.dom.Node;
 import org.w3c.dom.NodeList;
 
+import java.util.Properties;
+
 import static org.apache.log4j.builders.BuilderManager.CATEGORY;
+import static org.apache.log4j.xml.XmlConfiguration.PARAM_TAG;
 
 /**
  * Build a Pattern Layout
  */
 @Plugin(name = "org.apache.log4j.PatternLayout", category = CATEGORY)
 @PluginAliases("org.apache.log4j.EnhancedPatternLayout")
-public class PatternLayoutBuilder implements LayoutBuilder {
+public class PatternLayoutBuilder extends AbstractBuilder implements LayoutBuilder {
 
     private static final Logger LOGGER = StatusLogger.getLogger();
+    private static final String PATTERN = "ConversionPattern";
+
+    public PatternLayoutBuilder() {
+    }
+
+    public PatternLayoutBuilder(String prefix, Properties props) {
+        super(prefix, props);
+    }
 
     @Override
-    public Layout parseLayout(Element layoutElement, XmlConfigurationFactory factory) {
+    public Layout parseLayout(final Element layoutElement, final XmlConfiguration config) {
         NodeList params = layoutElement.getElementsByTagName("param");
         final int length = params.getLength();
         String pattern = null;
@@ -48,30 +62,44 @@
             Node currentNode = params.item(index);
             if (currentNode.getNodeType() == Node.ELEMENT_NODE) {
                 Element currentElement = (Element) currentNode;
-                if (currentElement.getTagName().equals("param")) {
-                    if ("conversionPattern".equalsIgnoreCase(currentElement.getAttribute("name"))) {
-                        pattern = currentElement.getAttribute("value")
-                                // Log4j 2's %x (NDC) is not compatible with Log4j 1's
-                                // %x
-                                // Log4j 1: "foo bar baz"
-                                // Log4j 2: "[foo, bar, baz]"
-                                // Use %ndc to get the Log4j 1 format
-                                .replace("%x", "%ndc")
-
-                                // Log4j 2's %X (MDC) is not compatible with Log4j 1's
-                                // %X
-                                // Log4j 1: "{{foo,bar}{hoo,boo}}"
-                                // Log4j 2: "{foo=bar,hoo=boo}"
-                                // Use %properties to get the Log4j 1 format
-                                .replace("%X", "%properties");
+                if (currentElement.getTagName().equals(PARAM_TAG)) {
+                    if (PATTERN.equalsIgnoreCase(currentElement.getAttribute("name"))) {
+                        pattern = currentElement.getAttribute("value");
                         break;
                     }
                 }
             }
         }
+        return createLayout(pattern, config);
+    }
+
+    @Override
+    public Layout parseLayout(final PropertiesConfiguration config) {
+        String pattern = getProperty(PATTERN);
+        return createLayout(pattern, config);
+    }
+
+    private Layout createLayout(String pattern, final Log4j1Configuration config) {
+        if (pattern == null) {
+            LOGGER.info("No pattern provided for pattern layout, using default pattern");
+            pattern = PatternLayout.DEFAULT_CONVERSION_PATTERN;
+        }
         return new LayoutWrapper(PatternLayout.newBuilder()
-                .setPattern(pattern)
-                .setConfiguration(factory.getConfiguration())
+                .setPattern(pattern
+                        // Log4j 2's %x (NDC) is not compatible with Log4j 1's
+                        // %x
+                        // Log4j 1: "foo bar baz"
+                        // Log4j 2: "[foo, bar, baz]"
+                        // Use %ndc to get the Log4j 1 format
+                        .replace("%x", "%ndc")
+
+                        // Log4j 2's %X (MDC) is not compatible with Log4j 1's
+                        // %X
+                        // Log4j 1: "{{foo,bar}{hoo,boo}}"
+                        // Log4j 2: "{foo=bar,hoo=boo}"
+                        // Use %properties to get the Log4j 1 format
+                        .replace("%X", "%properties"))
+                .setConfiguration(config)
                 .build());
     }
 }
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/layout/SimpleLayoutBuilder.java b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/layout/SimpleLayoutBuilder.java
index 411b2ef..d35beba 100644
--- a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/layout/SimpleLayoutBuilder.java
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/layout/SimpleLayoutBuilder.java
@@ -18,13 +18,16 @@
 
 import org.apache.log4j.Layout;
 import org.apache.log4j.bridge.LayoutWrapper;
-import org.apache.log4j.xml.XmlConfigurationFactory;
+import org.apache.log4j.config.PropertiesConfiguration;
+import org.apache.log4j.xml.XmlConfiguration;
 import org.apache.logging.log4j.Logger;
-import org.apache.logging.log4j.core.layout.PatternLayout;
 import org.apache.logging.log4j.plugins.Plugin;
+import org.apache.logging.log4j.core.layout.PatternLayout;
 import org.apache.logging.log4j.status.StatusLogger;
 import org.w3c.dom.Element;
 
+import java.util.Properties;
+
 import static org.apache.log4j.builders.BuilderManager.CATEGORY;
 
 /**
@@ -36,10 +39,18 @@
     private static final Logger LOGGER = StatusLogger.getLogger();
 
     @Override
-    public Layout parseLayout(Element layoutElement, XmlConfigurationFactory factory) {
+    public Layout parseLayout(Element layoutElement, XmlConfiguration config) {
         return new LayoutWrapper(PatternLayout.newBuilder()
                 .setPattern("%level - %m%n")
-                .setConfiguration(factory.getConfiguration())
+                .setConfiguration(config)
+                .build());
+    }
+
+    @Override
+    public Layout parseLayout(PropertiesConfiguration config) {
+        return new LayoutWrapper(PatternLayout.newBuilder()
+                .setPattern("%level - %m%n")
+                .setConfiguration(config)
                 .build());
     }
 }
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/layout/TTCCLayoutBuilder.java b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/layout/TTCCLayoutBuilder.java
index a2e85de..2641ea2 100644
--- a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/layout/TTCCLayoutBuilder.java
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/layout/TTCCLayoutBuilder.java
@@ -18,42 +18,54 @@
 
 import org.apache.log4j.Layout;
 import org.apache.log4j.bridge.LayoutWrapper;
+import org.apache.log4j.builders.AbstractBuilder;
 import org.apache.log4j.builders.BooleanHolder;
 import org.apache.log4j.builders.Holder;
-import org.apache.log4j.xml.XmlConfigurationFactory;
+import org.apache.log4j.config.Log4j1Configuration;
+import org.apache.log4j.config.PropertiesConfiguration;
+import org.apache.log4j.xml.XmlConfiguration;
 import org.apache.logging.log4j.Logger;
 import org.apache.logging.log4j.core.layout.PatternLayout;
 import org.apache.logging.log4j.plugins.Plugin;
 import org.apache.logging.log4j.status.StatusLogger;
 import org.w3c.dom.Element;
 
+import java.util.Properties;
+
 import static org.apache.log4j.builders.BuilderManager.CATEGORY;
-import static org.apache.log4j.xml.XmlConfigurationFactory.*;
+import static org.apache.log4j.xml.XmlConfiguration.*;
 
 /**
  * Build a Pattern Layout
  */
 @Plugin(name = "org.apache.log4j.TTCCLayout", category = CATEGORY)
-public class TTCCLayoutBuilder implements LayoutBuilder {
+public class TTCCLayoutBuilder extends AbstractBuilder implements LayoutBuilder {
 
     private static final Logger LOGGER = StatusLogger.getLogger();
 
-    private static final String THREAD_PRINTING_PARAM = "threadprinting";
-    private static final String CATEGORY_PREFIXING_PARAM = "categoryprefixing";
-    private static final String CONTEXT_PRINTING_PARAM = "contextprinting";
-    private static final String DATE_FORMAT_PARAM = "dateformat";
-    private static final String TIMEZONE_FORMAT = "timezone";
+    private static final String THREAD_PRINTING_PARAM = "ThreadPrinting";
+    private static final String CATEGORY_PREFIXING_PARAM = "CategoryPrefixing";
+    private static final String CONTEXT_PRINTING_PARAM = "ContextPrinting";
+    private static final String DATE_FORMAT_PARAM = "DateFormat";
+    private static final String TIMEZONE_FORMAT = "TimeZone";
+
+    public TTCCLayoutBuilder() {
+    }
+
+    public TTCCLayoutBuilder(String prefix, Properties props) {
+        super(prefix, props);
+    }
 
     @Override
-    public Layout parseLayout(Element layoutElement, XmlConfigurationFactory factory) {
+    public Layout parseLayout(Element layoutElement, XmlConfiguration config) {
         final Holder<Boolean> threadPrinting = new BooleanHolder();
         final Holder<Boolean> categoryPrefixing = new BooleanHolder();
         final Holder<Boolean> contextPrinting = new BooleanHolder();
         final Holder<String> dateFormat = new Holder<>();
         final Holder<String> timezone = new Holder<>();
         forEachElement(layoutElement.getElementsByTagName("param"), (currentElement) -> {
-            if (currentElement.getTagName().equals("param")) {
-                switch (currentElement.getAttribute(NAME_ATTR).toLowerCase()) {
+            if (currentElement.getTagName().equals(PARAM_TAG)) {
+                switch (currentElement.getAttribute(NAME_ATTR)) {
                     case THREAD_PRINTING_PARAM:
                         threadPrinting.set(Boolean.parseBoolean(currentElement.getAttribute(VALUE_ATTR)));
                         break;
@@ -72,32 +84,50 @@
                 }
             }
         });
+        return createLayout(threadPrinting.get(), categoryPrefixing.get(), contextPrinting.get(),
+                dateFormat.get(), timezone.get(), config);
+    }
+
+    @Override
+    public Layout parseLayout(PropertiesConfiguration config) {
+        boolean threadPrinting = getBooleanProperty(THREAD_PRINTING_PARAM);
+        boolean categoryPrefixing = getBooleanProperty(CATEGORY_PREFIXING_PARAM);
+        boolean contextPrinting = getBooleanProperty(CONTEXT_PRINTING_PARAM);
+        String dateFormat = getProperty(DATE_FORMAT_PARAM);
+        String timezone = getProperty(TIMEZONE_FORMAT);
+
+        return createLayout(threadPrinting, categoryPrefixing, contextPrinting,
+                dateFormat, timezone, config);
+    }
+
+    private Layout createLayout(boolean threadPrinting, boolean categoryPrefixing, boolean contextPrinting,
+            String dateFormat, String timezone, Log4j1Configuration config) {
         StringBuilder sb = new StringBuilder();
-        if (dateFormat.get() != null) {
-            if (RELATIVE.equalsIgnoreCase(dateFormat.get())) {
+        if (dateFormat != null) {
+            if (RELATIVE.equalsIgnoreCase(dateFormat)) {
                 sb.append("%r ");
             } else {
-                sb.append("%d{").append(dateFormat.get()).append("}");
-                if (timezone.get() != null) {
-                    sb.append("{").append(timezone.get()).append("}");
+                sb.append("%d{").append(dateFormat).append("}");
+                if (timezone != null) {
+                    sb.append("{").append(timezone).append("}");
                 }
                 sb.append(" ");
             }
         }
-        if (threadPrinting.get()) {
+        if (threadPrinting) {
             sb.append("[%t] ");
         }
         sb.append("%p ");
-        if (categoryPrefixing.get()) {
+        if (categoryPrefixing) {
             sb.append("%c ");
         }
-        if (contextPrinting.get()) {
+        if (contextPrinting) {
             sb.append("%notEmpty{%ndc }");
         }
         sb.append("- %m%n");
         return new LayoutWrapper(PatternLayout.newBuilder()
                 .setPattern(sb.toString())
-                .setConfiguration(factory.getConfiguration())
+                .setConfiguration(config)
                 .build());
     }
 }
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/config/Log4j1Configuration.java b/log4j-1.2-api/src/main/java/org/apache/log4j/config/Log4j1Configuration.java
index c0717cb..021afa3 100644
--- a/log4j-1.2-api/src/main/java/org/apache/log4j/config/Log4j1Configuration.java
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/config/Log4j1Configuration.java
@@ -23,27 +23,31 @@
 import org.apache.logging.log4j.core.config.Reconfigurable;
 
 /**
- * Class Description goes here.
+ * Base Configuration for Log4j 1.
  */
 public class Log4j1Configuration extends AbstractConfiguration implements Reconfigurable {
 
+    public static final String MONITOR_INTERVAL = "log4j1.monitorInterval";
+    public static final String APPENDER_REF_TAG = "appender-ref";
+    public static final String THRESHOLD_PARAM = "Threshold";
+
+    public static final String INHERITED = "inherited";
+
+    public static final String NULL = "null";
+
     public Log4j1Configuration(final LoggerContext loggerContext, final ConfigurationSource source,
             int monitorIntervalSeconds) {
         super(loggerContext, source);
         initializeWatchers(this, source, monitorIntervalSeconds);
     }
 
-    @Override
-    protected void doConfigure() {
-        super.getScheduler().start();
-
-    }
-
     /**
      * Initialize the configuration.
      */
     @Override
     public void initialize() {
+        getStrSubstitutor().setConfiguration(this);
+        super.getScheduler().start();
         doConfigure();
         setState(State.INITIALIZED);
         LOGGER.debug("Configuration {} initialized", this);
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/config/PropertiesConfiguration.java b/log4j-1.2-api/src/main/java/org/apache/log4j/config/PropertiesConfiguration.java
new file mode 100644
index 0000000..c3e995d
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/config/PropertiesConfiguration.java
@@ -0,0 +1,592 @@
+/*
+ * 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.log4j.config;
+
+import org.apache.log4j.Appender;
+import org.apache.log4j.Layout;
+import org.apache.log4j.LogManager;
+import org.apache.log4j.PatternLayout;
+import org.apache.log4j.bridge.AppenderAdapter;
+import org.apache.log4j.bridge.AppenderWrapper;
+import org.apache.log4j.builders.BuilderManager;
+import org.apache.log4j.helpers.OptionConverter;
+import org.apache.log4j.spi.ErrorHandler;
+import org.apache.log4j.spi.Filter;
+import org.apache.logging.log4j.core.LoggerContext;
+import org.apache.logging.log4j.core.config.ConfigurationSource;
+import org.apache.logging.log4j.core.config.LoggerConfig;
+import org.apache.logging.log4j.core.config.status.StatusConfiguration;
+import org.apache.logging.log4j.core.filter.AbstractFilterable;
+import org.apache.logging.log4j.util.LoaderUtil;
+
+import java.io.InputStream;
+import java.lang.reflect.InvocationTargetException;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.SortedMap;
+import java.util.StringTokenizer;
+import java.util.TreeMap;
+
+/**
+ * Construct a configuration based on Log4j 1 properties.
+ */
+public class PropertiesConfiguration  extends Log4j1Configuration {
+
+    private static final String CATEGORY_PREFIX = "log4j.category.";
+    private static final String LOGGER_PREFIX = "log4j.logger.";
+    private static final String ADDITIVITY_PREFIX = "log4j.additivity.";
+    private static final String ROOT_CATEGORY_PREFIX = "log4j.rootCategory";
+    private static final String ROOT_LOGGER_PREFIX = "log4j.rootLogger";
+    private static final String APPENDER_PREFIX = "log4j.appender.";
+    private static final String LOGGER_REF	= "logger-ref";
+    private static final String ROOT_REF		= "root-ref";
+    private static final String APPENDER_REF_TAG = "appender-ref";
+    public static final long DEFAULT_DELAY = 60000;
+    public static final String DEBUG_KEY="log4j.debug";
+
+    private static final String INTERNAL_ROOT_NAME = "root";
+
+    private final Map<String, Appender> registry;
+
+    private final BuilderManager manager;
+
+    /**
+     * No argument constructor.
+     */
+    public PropertiesConfiguration(final LoggerContext loggerContext, final ConfigurationSource source,
+            int monitorIntervalSeconds) {
+        super(loggerContext, source, monitorIntervalSeconds);
+        registry = new HashMap<>();
+        manager = new BuilderManager();
+    }
+
+    public void doConfigure() {
+        InputStream is = getConfigurationSource().getInputStream();
+        Properties props = new Properties();
+        try {
+            props.load(is);
+        } catch (Exception e) {
+            LOGGER.error("Could not read configuration file [{}].", getConfigurationSource().toString(), e);
+            return;
+        }
+        // If we reach here, then the config file is alright.
+        doConfigure(props);
+    }
+
+    /**
+     * Read configuration from a file. <b>The existing configuration is
+     * not cleared nor reset.</b> If you require a different behavior,
+     * then call {@link  LogManager#resetConfiguration
+     * resetConfiguration} method before calling
+     * <code>doConfigure</code>.
+     *
+     * <p>The configuration file consists of statements in the format
+     * <code>key=value</code>. The syntax of different configuration
+     * elements are discussed below.
+     *
+     * <p>The level value can consist of the string values OFF, FATAL,
+     * ERROR, WARN, INFO, DEBUG, ALL or a <em>custom level</em> value. A
+     * custom level value can be specified in the form
+     * level#classname. By default the repository-wide threshold is set
+     * to the lowest possible value, namely the level <code>ALL</code>.
+     * </p>
+     *
+     *
+     * <h3>Appender configuration</h3>
+     *
+     * <p>Appender configuration syntax is:
+     * <pre>
+     * # For appender named <i>appenderName</i>, set its class.
+     * # Note: The appender name can contain dots.
+     * log4j.appender.appenderName=fully.qualified.name.of.appender.class
+     *
+     * # Set appender specific options.
+     * log4j.appender.appenderName.option1=value1
+     * ...
+     * log4j.appender.appenderName.optionN=valueN
+     * </pre>
+     * <p>
+     * For each named appender you can configure its {@link Layout}. The
+     * syntax for configuring an appender's layout is:
+     * <pre>
+     * log4j.appender.appenderName.layout=fully.qualified.name.of.layout.class
+     * log4j.appender.appenderName.layout.option1=value1
+     * ....
+     * log4j.appender.appenderName.layout.optionN=valueN
+     * </pre>
+     * <p>
+     * The syntax for adding {@link Filter}s to an appender is:
+     * <pre>
+     * log4j.appender.appenderName.filter.ID=fully.qualified.name.of.filter.class
+     * log4j.appender.appenderName.filter.ID.option1=value1
+     * ...
+     * log4j.appender.appenderName.filter.ID.optionN=valueN
+     * </pre>
+     * The first line defines the class name of the filter identified by ID;
+     * subsequent lines with the same ID specify filter option - value
+     * pairs. Multiple filters are added to the appender in the lexicographic
+     * order of IDs.
+     * <p>
+     * The syntax for adding an {@link ErrorHandler} to an appender is:
+     * <pre>
+     * log4j.appender.appenderName.errorhandler=fully.qualified.name.of.errorhandler.class
+     * log4j.appender.appenderName.errorhandler.appender-ref=appenderName
+     * log4j.appender.appenderName.errorhandler.option1=value1
+     * ...
+     * log4j.appender.appenderName.errorhandler.optionN=valueN
+     * </pre>
+     *
+     * <h3>Configuring loggers</h3>
+     *
+     * <p>The syntax for configuring the root logger is:
+     * <pre>
+     * log4j.rootLogger=[level], appenderName, appenderName, ...
+     * </pre>
+     *
+     * <p>This syntax means that an optional <em>level</em> can be
+     * supplied followed by appender names separated by commas.
+     *
+     * <p>The level value can consist of the string values OFF, FATAL,
+     * ERROR, WARN, INFO, DEBUG, ALL or a <em>custom level</em> value. A
+     * custom level value can be specified in the form
+     * <code>level#classname</code>.
+     *
+     * <p>If a level value is specified, then the root level is set
+     * to the corresponding level.  If no level value is specified,
+     * then the root level remains untouched.
+     *
+     * <p>The root logger can be assigned multiple appenders.
+     *
+     * <p>Each <i>appenderName</i> (separated by commas) will be added to
+     * the root logger. The named appender is defined using the
+     * appender syntax defined above.
+     *
+     * <p>For non-root categories the syntax is almost the same:
+     * <pre>
+     * log4j.logger.logger_name=[level|INHERITED|NULL], appenderName, appenderName, ...
+     * </pre>
+     *
+     * <p>The meaning of the optional level value is discussed above
+     * in relation to the root logger. In addition however, the value
+     * INHERITED can be specified meaning that the named logger should
+     * inherit its level from the logger hierarchy.
+     *
+     * <p>If no level value is supplied, then the level of the
+     * named logger remains untouched.
+     *
+     * <p>By default categories inherit their level from the
+     * hierarchy. However, if you set the level of a logger and later
+     * decide that that logger should inherit its level, then you should
+     * specify INHERITED as the value for the level value. NULL is a
+     * synonym for INHERITED.
+     *
+     * <p>Similar to the root logger syntax, each <i>appenderName</i>
+     * (separated by commas) will be attached to the named logger.
+     *
+     * <p>See the <a href="../../../../manual.html#additivity">appender
+     * additivity rule</a> in the user manual for the meaning of the
+     * <code>additivity</code> flag.
+     *
+     *
+     * # Set options for appender named "A1".
+     * # Appender "A1" will be a SyslogAppender
+     * log4j.appender.A1=org.apache.log4j.net.SyslogAppender
+     *
+     * # The syslog daemon resides on www.abc.net
+     * log4j.appender.A1.SyslogHost=www.abc.net
+     *
+     * # A1's layout is a PatternLayout, using the conversion pattern
+     * # <b>%r %-5p %c{2} %M.%L %x - %m\n</b>. Thus, the log output will
+     * # include # the relative time since the start of the application in
+     * # milliseconds, followed by the level of the log request,
+     * # followed by the two rightmost components of the logger name,
+     * # followed by the callers method name, followed by the line number,
+     * # the nested diagnostic context and finally the message itself.
+     * # Refer to the documentation of {@link PatternLayout} for further information
+     * # on the syntax of the ConversionPattern key.
+     * log4j.appender.A1.layout=org.apache.log4j.PatternLayout
+     * log4j.appender.A1.layout.ConversionPattern=%-4r %-5p %c{2} %M.%L %x - %m\n
+     *
+     * # Set options for appender named "A2"
+     * # A2 should be a RollingFileAppender, with maximum file size of 10 MB
+     * # using at most one backup file. A2's layout is TTCC, using the
+     * # ISO8061 date format with context printing enabled.
+     * log4j.appender.A2=org.apache.log4j.RollingFileAppender
+     * log4j.appender.A2.MaxFileSize=10MB
+     * log4j.appender.A2.MaxBackupIndex=1
+     * log4j.appender.A2.layout=org.apache.log4j.TTCCLayout
+     * log4j.appender.A2.layout.ContextPrinting=enabled
+     * log4j.appender.A2.layout.DateFormat=ISO8601
+     *
+     * # Root logger set to DEBUG using the A2 appender defined above.
+     * log4j.rootLogger=DEBUG, A2
+     *
+     * # Logger definitions:
+     * # The SECURITY logger inherits is level from root. However, it's output
+     * # will go to A1 appender defined above. It's additivity is non-cumulative.
+     * log4j.logger.SECURITY=INHERIT, A1
+     * log4j.additivity.SECURITY=false
+     *
+     * # Only warnings or above will be logged for the logger "SECURITY.access".
+     * # Output will go to A1.
+     * log4j.logger.SECURITY.access=WARN
+     *
+     *
+     * # The logger "class.of.the.day" inherits its level from the
+     * # logger hierarchy.  Output will go to the appender's of the root
+     * # logger, A2 in this case.
+     * log4j.logger.class.of.the.day=INHERIT
+     * </pre>
+     *
+     * <p>Refer to the <b>setOption</b> method in each Appender and
+     * Layout for class specific options.
+     *
+     * <p>Use the <code>#</code> or <code>!</code> characters at the
+     * beginning of a line for comments.
+     *
+     * <p>
+     */
+    private void doConfigure(Properties properties) {
+        String status = "error";
+        String value = properties.getProperty(DEBUG_KEY);
+        if (value == null) {
+            value = properties.getProperty("log4j.configDebug");
+            if (value != null) {
+                LOGGER.warn("[log4j.configDebug] is deprecated. Use [log4j.debug] instead.");
+            }
+        }
+
+        if (value != null) {
+            status = OptionConverter.toBoolean(value, false) ? "debug" : "error";
+        }
+
+        final StatusConfiguration statusConfig = new StatusConfiguration().setStatus(status);
+        statusConfig.initialize();
+
+        configureRoot(properties);
+        parseLoggers(properties);
+
+        LOGGER.debug("Finished configuring.");
+    }
+
+    // --------------------------------------------------------------------------
+    // Internal stuff
+    // --------------------------------------------------------------------------
+
+    private void configureRoot(Properties props) {
+        String effectiveFrefix = ROOT_LOGGER_PREFIX;
+        String value = OptionConverter.findAndSubst(ROOT_LOGGER_PREFIX, props);
+
+        if (value == null) {
+            value = OptionConverter.findAndSubst(ROOT_CATEGORY_PREFIX, props);
+            effectiveFrefix = ROOT_CATEGORY_PREFIX;
+        }
+
+        if (value == null) {
+            LOGGER.debug("Could not find root logger information. Is this OK?");
+        } else {
+            LoggerConfig root = getRootLogger();
+            parseLogger(props, root, effectiveFrefix, INTERNAL_ROOT_NAME, value);
+        }
+    }
+
+    /**
+     * Parse non-root elements, such non-root categories and renderers.
+     */
+    private void parseLoggers(Properties props) {
+        Enumeration enumeration = props.propertyNames();
+        while (enumeration.hasMoreElements()) {
+            String key = (String) enumeration.nextElement();
+            if (key.startsWith(CATEGORY_PREFIX) || key.startsWith(LOGGER_PREFIX)) {
+                String loggerName = null;
+                if (key.startsWith(CATEGORY_PREFIX)) {
+                    loggerName = key.substring(CATEGORY_PREFIX.length());
+                } else if (key.startsWith(LOGGER_PREFIX)) {
+                    loggerName = key.substring(LOGGER_PREFIX.length());
+                }
+                String value = OptionConverter.findAndSubst(key, props);
+                LoggerConfig loggerConfig = getLogger(loggerName);
+                if (loggerConfig == null) {
+                    boolean additivity = getAdditivityForLogger(props, loggerName);
+                    loggerConfig = new LoggerConfig(loggerName, org.apache.logging.log4j.Level.ERROR, additivity);
+                    addLogger(loggerName, loggerConfig);
+                }
+                parseLogger(props, loggerConfig, key, loggerName, value);
+            }
+        }
+    }
+
+    /**
+     * Parse the additivity option for a non-root category.
+     */
+    private boolean getAdditivityForLogger(Properties props, String loggerName) {
+        boolean additivity = true;
+        String key = ADDITIVITY_PREFIX + loggerName;
+        String value = OptionConverter.findAndSubst(key, props);
+        LOGGER.debug("Handling {}=[{}]", key, value);
+        // touch additivity only if necessary
+        if ((value != null) && (!value.equals(""))) {
+            additivity = OptionConverter.toBoolean(value, true);
+        }
+        return additivity;
+    }
+
+    /**
+     * This method must work for the root category as well.
+     */
+    private void parseLogger(Properties props, LoggerConfig logger, String optionKey, String loggerName, String value) {
+
+        LOGGER.debug("Parsing for [{}] with value=[{}].", loggerName, value);
+        // We must skip over ',' but not white space
+        StringTokenizer st = new StringTokenizer(value, ",");
+
+        // If value is not in the form ", appender.." or "", then we should set the level of the logger.
+
+        if (!(value.startsWith(",") || value.equals(""))) {
+
+            // just to be on the safe side...
+            if (!st.hasMoreTokens()) {
+                return;
+            }
+
+            String levelStr = st.nextToken();
+            LOGGER.debug("Level token is [{}].", levelStr);
+
+            org.apache.logging.log4j.Level level = levelStr == null ? org.apache.logging.log4j.Level.ERROR :
+                    OptionConverter.convertLevel(levelStr, org.apache.logging.log4j.Level.DEBUG);
+            logger.setLevel(level);
+            LOGGER.debug("Logger {} level set to {}", loggerName, level);
+        }
+
+        Appender appender;
+        String appenderName;
+        while (st.hasMoreTokens()) {
+            appenderName = st.nextToken().trim();
+            if (appenderName == null || appenderName.equals(",")) {
+                continue;
+            }
+            LOGGER.debug("Parsing appender named \"{}\".", appenderName);
+            appender = parseAppender(props, appenderName);
+            if (appender != null) {
+                LOGGER.debug("Adding appender named [{}] to loggerConfig [{}].", appenderName,
+                        logger.getName());
+                logger.addAppender(getAppender(appenderName), null, null);
+            } else {
+                LOGGER.debug("Appender named [{}}] not found.", appenderName);
+            }
+        }
+    }
+
+    public Appender parseAppender(Properties props, String appenderName) {
+        Appender appender = registry.get(appenderName);
+        if ((appender != null)) {
+            LOGGER.debug("Appender \"" + appenderName + "\" was already parsed.");
+            return appender;
+        }
+        // Appender was not previously initialized.
+        final String prefix = APPENDER_PREFIX + appenderName;
+        final String layoutPrefix = prefix + ".layout";
+        final String filterPrefix = APPENDER_PREFIX + appenderName + ".filter.";
+        String className = OptionConverter.findAndSubst(prefix, props);
+        appender = manager.parseAppender(appenderName, className, prefix, layoutPrefix, filterPrefix, props, this);
+        if (appender == null) {
+            appender = buildAppender(appenderName, className, prefix, layoutPrefix, filterPrefix, props);
+        } else {
+            registry.put(appenderName, appender);
+            if (appender instanceof AppenderWrapper) {
+                addAppender(((AppenderWrapper) appender).getAppender());
+            } else {
+                addAppender(new AppenderAdapter(appender).getAdapter());
+            }
+        }
+        return appender;
+    }
+
+    private Appender buildAppender(final String appenderName, final String className, final String prefix,
+            final String layoutPrefix, final String filterPrefix, final Properties props) {
+        Appender appender = newInstanceOf(className, "Appender");
+        if (appender == null) {
+            return null;
+        }
+        appender.setName(appenderName);
+        appender.setLayout(parseLayout(layoutPrefix, appenderName, props));
+        final String errorHandlerPrefix = prefix + ".errorhandler";
+        String errorHandlerClass = OptionConverter.findAndSubst(errorHandlerPrefix, props);
+        if (errorHandlerClass != null) {
+            ErrorHandler eh = parseErrorHandler(props, errorHandlerPrefix, errorHandlerClass, appender);
+            if (eh != null) {
+                appender.setErrorHandler(eh);
+            }
+        }
+        parseAppenderFilters(props, filterPrefix, appenderName);
+        String[] keys = new String[] {
+                layoutPrefix,
+        };
+        addProperties(appender, keys, props, prefix);
+        if (appender instanceof AppenderWrapper) {
+            addAppender(((AppenderWrapper) appender).getAppender());
+        } else {
+            addAppender(new AppenderAdapter(appender).getAdapter());
+        }
+        registry.put(appenderName, appender);
+        return appender;
+    }
+
+    public Layout parseLayout(String layoutPrefix, String appenderName, Properties props) {
+        String layoutClass = OptionConverter.findAndSubst(layoutPrefix, props);
+        if (layoutClass == null) {
+            return null;
+        }
+        Layout layout = manager.parseLayout(layoutClass, layoutPrefix, props, this);
+        if (layout == null) {
+            layout = buildLayout(layoutPrefix, layoutClass, appenderName, props);
+        }
+        return layout;
+    }
+
+    private Layout buildLayout(String layoutPrefix, String className, String appenderName, Properties props) {
+        Layout layout = newInstanceOf(className, "Layout");
+        if (layout == null) {
+            return null;
+        }
+        LOGGER.debug("Parsing layout options for \"{}\".", appenderName);
+        PropertySetter.setProperties(layout, props, layoutPrefix + ".");
+        LOGGER.debug("End of parsing for \"{}\".", appenderName);
+        return layout;
+    }
+
+    public ErrorHandler parseErrorHandler(final Properties props, final String errorHandlerPrefix,
+            final String errorHandlerClass, final Appender appender) {
+        ErrorHandler eh = newInstanceOf(errorHandlerClass, "ErrorHandler");
+        final String[] keys = new String[] {
+                errorHandlerPrefix + "." + ROOT_REF,
+                errorHandlerPrefix + "." + LOGGER_REF,
+                errorHandlerPrefix + "." + APPENDER_REF_TAG
+        };
+        addProperties(eh, keys, props, errorHandlerPrefix);
+        return eh;
+    }
+
+    public void addProperties(final Object obj, final String[] keys, final Properties props, final String prefix) {
+        final Properties edited = new Properties();
+        props.stringPropertyNames().stream().filter((name) -> {
+            if (name.startsWith(prefix)) {
+                for (String key : keys) {
+                    if (name.equals(key)) {
+                        return false;
+                    }
+                }
+                return true;
+            }
+            return false;
+        }).forEach((name) -> edited.put(name, props.getProperty(name)));
+        PropertySetter.setProperties(obj, edited, prefix + ".");
+    }
+
+
+    public Filter parseAppenderFilters(Properties props, String filterPrefix, String appenderName) {
+        // extract filters and filter options from props into a hashtable mapping
+        // the property name defining the filter class to a list of pre-parsed
+        // name-value pairs associated to that filter
+        int fIdx = filterPrefix.length();
+        SortedMap<String, List<NameValue>> filters = new TreeMap<>();
+        Enumeration e = props.keys();
+        String name = "";
+        while (e.hasMoreElements()) {
+            String key = (String) e.nextElement();
+            if (key.startsWith(filterPrefix)) {
+                int dotIdx = key.indexOf('.', fIdx);
+                String filterKey = key;
+                if (dotIdx != -1) {
+                    filterKey = key.substring(0, dotIdx);
+                    name = key.substring(dotIdx + 1);
+                }
+                List<NameValue> filterOpts = filters.computeIfAbsent(filterKey, k -> new ArrayList<>());
+                if (dotIdx != -1) {
+                    String value = OptionConverter.findAndSubst(key, props);
+                    filterOpts.add(new NameValue(name, value));
+                }
+            }
+        }
+
+        Filter head = null;
+        Filter next = null;
+        for (Map.Entry<String, List<NameValue>> entry : filters.entrySet()) {
+            String clazz = props.getProperty(entry.getKey());
+            Filter filter = null;
+            if (clazz != null) {
+                filter = manager.parseFilter(clazz, filterPrefix, props, this);
+                if (filter == null) {
+                    LOGGER.debug("Filter key: [{}] class: [{}] props: {}", entry.getKey(), clazz, entry.getValue());
+                    filter = buildFilter(clazz, appenderName, entry.getValue());
+                }
+            }
+            if (filter != null) {
+                if (head != null) {
+                    head = filter;
+                    next = filter;
+                } else {
+                    next.setNext(filter);
+                    next = filter;
+                }
+            }
+        }
+        return head;
+    }
+
+    private Filter buildFilter(String className, String appenderName, List<NameValue> props) {
+        Filter filter = newInstanceOf(className, "Filter");
+        if (filter != null) {
+            PropertySetter propSetter = new PropertySetter(filter);
+            for (NameValue property : props) {
+                propSetter.setProperty(property.key, property.value);
+            }
+            propSetter.activate();
+        }
+        return filter;
+    }
+
+
+    private static <T> T newInstanceOf(String className, String type) {
+        try {
+            return LoaderUtil.newInstanceOf(className);
+        } catch (ClassNotFoundException | IllegalAccessException | NoSuchMethodException |
+                InstantiationException | InvocationTargetException ex) {
+            LOGGER.error("Unable to create {} {} due to {}:{}", type,  className,
+                    ex.getClass().getSimpleName(), ex.getMessage());
+            return null;
+        }
+    }
+
+    private static class NameValue {
+        String key, value;
+
+        NameValue(String key, String value) {
+            this.key = key;
+            this.value = value;
+        }
+
+        public String toString() {
+            return key + "=" + value;
+        }
+    }
+
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/config/PropertiesConfigurationFactory.java b/log4j-1.2-api/src/main/java/org/apache/log4j/config/PropertiesConfigurationFactory.java
new file mode 100644
index 0000000..465eaa1
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/config/PropertiesConfigurationFactory.java
@@ -0,0 +1,74 @@
+/*
+ * 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.log4j.config;
+
+import org.apache.logging.log4j.core.LoggerContext;
+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.Order;
+import org.apache.logging.log4j.plugins.Plugin;
+import org.apache.logging.log4j.util.PropertiesUtil;
+
+/**
+ * Configures Log4j from a log4j 1 format properties file.
+ */
+@Plugin(name = "Log4j1PropertiesConfigurationFactory", category = ConfigurationFactory.CATEGORY)
+@Order(2)
+public class PropertiesConfigurationFactory extends ConfigurationFactory {
+
+
+    /**
+     * File name prefix for test configurations.
+     */
+    protected static final String TEST_PREFIX = "log4j-test";
+
+    /**
+     * File name prefix for standard configurations.
+     */
+    protected static final String DEFAULT_PREFIX = "log4j";
+
+    @Override
+    protected String[] getSupportedTypes() {
+        if (!PropertiesUtil.getProperties().getBooleanProperty(ConfigurationFactory.LOG4J1_EXPERIMENTAL, Boolean.FALSE)) {
+            return null;
+        }
+        return new String[] {".properties"};
+    }
+
+    @Override
+    public Configuration getConfiguration(LoggerContext loggerContext, ConfigurationSource source) {
+        int interval = PropertiesUtil.getProperties().getIntegerProperty(Log4j1Configuration.MONITOR_INTERVAL, 0);
+        return new PropertiesConfiguration(loggerContext, source, interval);
+    }
+
+    @Override
+    protected String getTestPrefix() {
+        return TEST_PREFIX;
+    }
+
+    @Override
+    protected String getDefaultPrefix() {
+        return DEFAULT_PREFIX;
+    }
+
+    @Override
+    protected String getVersion() {
+        return LOG4J1_VERSION;
+    }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/helpers/AppenderAttachableImpl.java b/log4j-1.2-api/src/main/java/org/apache/log4j/helpers/AppenderAttachableImpl.java
new file mode 100644
index 0000000..fe9530d
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/helpers/AppenderAttachableImpl.java
@@ -0,0 +1,70 @@
+/*
+ * 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.log4j.helpers;
+
+import org.apache.log4j.Appender;
+import org.apache.log4j.spi.AppenderAttachable;
+
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+/**
+ * Allows Classes to attach Appenders.
+ */
+public class AppenderAttachableImpl implements AppenderAttachable {
+
+    private final ConcurrentMap<String, Appender> appenders = new ConcurrentHashMap<>();
+
+    @Override
+    public void addAppender(Appender newAppender) {
+        if (newAppender != null) {
+            appenders.put(newAppender.getName(), newAppender);
+        }
+    }
+
+    @Override
+    public Enumeration getAllAppenders() {
+        return Collections.enumeration(appenders.values());
+    }
+
+    @Override
+    public Appender getAppender(String name) {
+        return appenders.get(name);
+    }
+
+    @Override
+    public boolean isAttached(Appender appender) {
+        return appenders.containsValue(appender);
+    }
+
+    @Override
+    public void removeAllAppenders() {
+        appenders.clear();
+    }
+
+    @Override
+    public void removeAppender(Appender appender) {
+        appenders.remove(appender.getName(), appender);
+    }
+
+    @Override
+    public void removeAppender(String name) {
+        appenders.remove(name);
+    }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/helpers/OptionConverter.java b/log4j-1.2-api/src/main/java/org/apache/log4j/helpers/OptionConverter.java
index 8ee0ea5..e09e26b 100644
--- a/log4j-1.2-api/src/main/java/org/apache/log4j/helpers/OptionConverter.java
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/helpers/OptionConverter.java
@@ -18,7 +18,6 @@
 package org.apache.log4j.helpers;
 
 import org.apache.log4j.Level;
-
 import org.apache.logging.log4j.LogManager;
 import org.apache.logging.log4j.Logger;
 import org.apache.logging.log4j.util.LoaderUtil;
@@ -332,6 +331,51 @@
             }
         }
     }
+
+    public static org.apache.logging.log4j.Level convertLevel(String level,
+            org.apache.logging.log4j.Level defaultLevel) {
+        Level l = toLevel(level, null);
+        return l != null ? convertLevel(l) : defaultLevel;
+    }
+
+    public static  org.apache.logging.log4j.Level convertLevel(Level level) {
+        if (level == null) {
+            return org.apache.logging.log4j.Level.ERROR;
+        }
+        if (level.isGreaterOrEqual(Level.FATAL)) {
+            return org.apache.logging.log4j.Level.FATAL;
+        } else if (level.isGreaterOrEqual(Level.ERROR)) {
+            return org.apache.logging.log4j.Level.ERROR;
+        } else if (level.isGreaterOrEqual(Level.WARN)) {
+            return org.apache.logging.log4j.Level.WARN;
+        } else if (level.isGreaterOrEqual(Level.INFO)) {
+            return org.apache.logging.log4j.Level.INFO;
+        } else if (level.isGreaterOrEqual(Level.DEBUG)) {
+            return org.apache.logging.log4j.Level.DEBUG;
+        } else if (level.isGreaterOrEqual(Level.TRACE)) {
+            return org.apache.logging.log4j.Level.TRACE;
+        }
+        return org.apache.logging.log4j.Level.ALL;
+    }
+
+    /**
+     * Find the value corresponding to <code>key</code> in
+     * <code>props</code>. Then perform variable substitution on the
+     * found value.
+     */
+    public static String findAndSubst(String key, Properties props) {
+        String value = props.getProperty(key);
+        if (value == null) {
+            return null;
+        }
+
+        try {
+            return substVars(value, props);
+        } catch (IllegalArgumentException e) {
+            LOGGER.error("Bad option value [{}].", value, e);
+            return value;
+        }
+    }
     
     private static class CharMap {
         final char key;
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/xml/XmlConfiguration.java b/log4j-1.2-api/src/main/java/org/apache/log4j/xml/XmlConfiguration.java
new file mode 100644
index 0000000..c96c26d
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/xml/XmlConfiguration.java
@@ -0,0 +1,744 @@
+/*
+ * 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.log4j.xml;
+
+import org.apache.log4j.Appender;
+import org.apache.log4j.Layout;
+import org.apache.log4j.Level;
+import org.apache.log4j.bridge.AppenderAdapter;
+import org.apache.log4j.bridge.AppenderWrapper;
+import org.apache.log4j.builders.BuilderManager;
+import org.apache.log4j.config.Log4j1Configuration;
+import org.apache.log4j.config.PropertySetter;
+import org.apache.log4j.helpers.OptionConverter;
+import org.apache.log4j.spi.AppenderAttachable;
+import org.apache.log4j.spi.ErrorHandler;
+import org.apache.log4j.spi.Filter;
+import org.apache.logging.log4j.core.LoggerContext;
+import org.apache.logging.log4j.core.config.ConfigurationSource;
+import org.apache.logging.log4j.core.config.LoggerConfig;
+import org.apache.logging.log4j.core.config.status.StatusConfiguration;
+import org.apache.logging.log4j.status.StatusLogger;
+import org.apache.logging.log4j.util.LoaderUtil;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+import org.xml.sax.SAXParseException;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.FactoryConfigurationError;
+import java.io.IOException;
+import java.io.InterruptedIOException;
+import java.lang.reflect.Method;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Properties;
+import java.util.function.Consumer;
+
+/**
+ * Class Description goes here.
+ */
+public class XmlConfiguration extends Log4j1Configuration {
+
+    private static final org.apache.logging.log4j.Logger LOGGER = StatusLogger.getLogger();
+
+    private static final String CONFIGURATION_TAG = "log4j:configuration";
+    private static final String OLD_CONFIGURATION_TAG = "configuration";
+    private static final String RENDERER_TAG = "renderer";
+    private static final String APPENDER_TAG = "appender";
+    public  static final String PARAM_TAG = "param";
+    public static final String LAYOUT_TAG = "layout";
+    private static final String CATEGORY = "category";
+    private static final String LOGGER_ELEMENT = "logger";
+    private static final String CATEGORY_FACTORY_TAG = "categoryFactory";
+    private static final String LOGGER_FACTORY_TAG = "loggerFactory";
+    public static final String NAME_ATTR = "name";
+    private static final String CLASS_ATTR = "class";
+    public static final String VALUE_ATTR = "value";
+    private static final String ROOT_TAG = "root";
+    private static final String LEVEL_TAG = "level";
+    private static final String PRIORITY_TAG = "priority";
+    public static final String FILTER_TAG = "filter";
+    private static final String ERROR_HANDLER_TAG = "errorHandler";
+    public static final String REF_ATTR = "ref";
+    private static final String ADDITIVITY_ATTR = "additivity";
+    private static final String CONFIG_DEBUG_ATTR = "configDebug";
+    private static final String INTERNAL_DEBUG_ATTR = "debug";
+    private static final String EMPTY_STR = "";
+    private static final Class[] ONE_STRING_PARAM = new Class[]{String.class};
+    private static final String dbfKey = "javax.xml.parsers.DocumentBuilderFactory";
+    private static final String THROWABLE_RENDERER_TAG = "throwableRenderer";
+
+    public static final long DEFAULT_DELAY = 60000;
+    /**
+     * File name prefix for test configurations.
+     */
+    protected static final String TEST_PREFIX = "log4j-test";
+
+    /**
+     * File name prefix for standard configurations.
+     */
+    protected static final String DEFAULT_PREFIX = "log4j";
+
+    private final BuilderManager manager;
+
+    // key: appenderName, value: appender
+    private Map<String, Appender> appenderMap;
+
+    private Properties props = null;
+
+    public XmlConfiguration(final LoggerContext loggerContext, final ConfigurationSource source,
+            int monitorIntervalSeconds) {
+        super(loggerContext, source, monitorIntervalSeconds);
+        appenderMap = new HashMap<>();
+        manager = new BuilderManager();
+    }
+
+    public void addAppenderIfAbsent(Appender appender) {
+        appenderMap.putIfAbsent(appender.getName(), appender);
+    }
+
+    /**
+     * Configure log4j by reading in a log4j.dtd compliant XML
+     * configuration file.
+     */
+    @Override
+    public void doConfigure() throws FactoryConfigurationError {
+        ConfigurationSource source = getConfigurationSource();
+        ParseAction action = new ParseAction() {
+            public Document parse(final DocumentBuilder parser) throws SAXException, IOException {
+                InputSource inputSource = new InputSource(source.getInputStream());
+                inputSource.setSystemId("dummy://log4j.dtd");
+                return parser.parse(inputSource);
+            }
+
+            public String toString() {
+                return getConfigurationSource().getLocation();
+            }
+        };
+        doConfigure(action);
+    }
+
+    private void doConfigure(final ParseAction action) throws FactoryConfigurationError {
+        DocumentBuilderFactory dbf;
+        try {
+            LOGGER.debug("System property is : {}", OptionConverter.getSystemProperty(dbfKey, null));
+            dbf = DocumentBuilderFactory.newInstance();
+            LOGGER.debug("Standard DocumentBuilderFactory search succeded.");
+            LOGGER.debug("DocumentBuilderFactory is: " + dbf.getClass().getName());
+        } catch (FactoryConfigurationError fce) {
+            Exception e = fce.getException();
+            LOGGER.debug("Could not instantiate a DocumentBuilderFactory.", e);
+            throw fce;
+        }
+
+        try {
+            dbf.setValidating(true);
+
+            DocumentBuilder docBuilder = dbf.newDocumentBuilder();
+
+            docBuilder.setErrorHandler(new SAXErrorHandler());
+            docBuilder.setEntityResolver(new Log4jEntityResolver());
+
+            Document doc = action.parse(docBuilder);
+            parse(doc.getDocumentElement());
+        } catch (Exception e) {
+            if (e instanceof InterruptedException || e instanceof InterruptedIOException) {
+                Thread.currentThread().interrupt();
+            }
+            // I know this is miserable...
+            LOGGER.error("Could not parse " + action.toString() + ".", e);
+        }
+    }
+
+    /**
+     * Delegates unrecognized content to created instance if it supports UnrecognizedElementParser.
+     *
+     * @param instance instance, may be null.
+     * @param element  element, may not be null.
+     * @param props    properties
+     * @throws IOException thrown if configuration of owner object should be abandoned.
+     */
+    private void parseUnrecognizedElement(final Object instance, final Element element,
+            final Properties props) throws Exception {
+        boolean recognized = false;
+        if (instance instanceof UnrecognizedElementHandler) {
+            recognized = ((UnrecognizedElementHandler) instance).parseUnrecognizedElement(
+                    element, props);
+        }
+        if (!recognized) {
+            LOGGER.warn("Unrecognized element {}", element.getNodeName());
+        }
+    }
+
+    /**
+     * Delegates unrecognized content to created instance if
+     * it supports UnrecognizedElementParser and catches and
+     * logs any exception.
+     *
+     * @param instance instance, may be null.
+     * @param element  element, may not be null.
+     * @param props    properties
+     * @since 1.2.15
+     */
+    private void quietParseUnrecognizedElement(final Object instance,
+            final Element element,
+            final Properties props) {
+        try {
+            parseUnrecognizedElement(instance, element, props);
+        } catch (Exception ex) {
+            if (ex instanceof InterruptedException || ex instanceof InterruptedIOException) {
+                Thread.currentThread().interrupt();
+            }
+            LOGGER.error("Error in extension content: ", ex);
+        }
+    }
+
+    /**
+     * Substitutes property value for any references in expression.
+     *
+     * @param value value from configuration file, may contain
+     *              literal text, property references or both
+     * @param props properties.
+     * @return evaluated expression, may still contain expressions
+     * if unable to expand.
+     */
+    public String subst(final String value, final Properties props) {
+        try {
+            return OptionConverter.substVars(value, props);
+        } catch (IllegalArgumentException e) {
+            LOGGER.warn("Could not perform variable substitution.", e);
+            return value;
+        }
+    }
+
+    /**
+     * Sets a parameter based from configuration file content.
+     *
+     * @param elem       param element, may not be null.
+     * @param propSetter property setter, may not be null.
+     * @param props      properties
+     * @since 1.2.15
+     */
+    public void setParameter(final Element elem, final PropertySetter propSetter, final Properties props) {
+        String name = subst(elem.getAttribute("name"), props);
+        String value = (elem.getAttribute("value"));
+        value = subst(OptionConverter.convertSpecialChars(value), props);
+        propSetter.setProperty(name, value);
+    }
+
+    /**
+     * Creates an object and processes any nested param elements
+     * but does not call activateOptions.  If the class also supports
+     * UnrecognizedElementParser, the parseUnrecognizedElement method
+     * will be call for any child elements other than param.
+     *
+     * @param element       element, may not be null.
+     * @param props         properties
+     * @param expectedClass interface or class expected to be implemented
+     *                      by created class
+     * @return created class or null.
+     * @throws Exception thrown if the contain object should be abandoned.
+     * @since 1.2.15
+     */
+    public Object parseElement(final Element element, final Properties props,
+            @SuppressWarnings("rawtypes") final Class expectedClass) throws Exception {
+        String clazz = subst(element.getAttribute("class"), props);
+        Object instance = OptionConverter.instantiateByClassName(clazz,
+                expectedClass, null);
+
+        if (instance != null) {
+            PropertySetter propSetter = new PropertySetter(instance);
+            NodeList children = element.getChildNodes();
+            final int length = children.getLength();
+
+            for (int loop = 0; loop < length; loop++) {
+                Node currentNode = children.item(loop);
+                if (currentNode.getNodeType() == Node.ELEMENT_NODE) {
+                    Element currentElement = (Element) currentNode;
+                    String tagName = currentElement.getTagName();
+                    if (tagName.equals("param")) {
+                        setParameter(currentElement, propSetter, props);
+                    } else {
+                        parseUnrecognizedElement(instance, currentElement, props);
+                    }
+                }
+            }
+            return instance;
+        }
+        return null;
+    }
+
+    /**
+     * Used internally to parse appenders by IDREF name.
+     */
+    private Appender findAppenderByName(Document doc, String appenderName) {
+        Appender appender = appenderMap.get(appenderName);
+
+        if (appender != null) {
+            return appender;
+        } else {
+            // Doesn't work on DOM Level 1 :
+            // Element element = doc.getElementById(appenderName);
+
+            // Endre's hack:
+            Element element = null;
+            NodeList list = doc.getElementsByTagName("appender");
+            for (int t = 0; t < list.getLength(); t++) {
+                Node node = list.item(t);
+                NamedNodeMap map = node.getAttributes();
+                Node attrNode = map.getNamedItem("name");
+                if (appenderName.equals(attrNode.getNodeValue())) {
+                    element = (Element) node;
+                    break;
+                }
+            }
+            // Hack finished.
+
+            if (element == null) {
+
+                LOGGER.error("No appender named [{}] could be found.", appenderName);
+                return null;
+            } else {
+                appender = parseAppender(element);
+                if (appender != null) {
+                    appenderMap.put(appenderName, appender);
+                }
+                return appender;
+            }
+        }
+    }
+
+    /**
+     * Used internally to parse appenders by IDREF element.
+     */
+    public Appender findAppenderByReference(Element appenderRef) {
+        String appenderName = subst(appenderRef.getAttribute(REF_ATTR));
+        Document doc = appenderRef.getOwnerDocument();
+        return findAppenderByName(doc, appenderName);
+    }
+
+    /**
+     * Used internally to parse an appender element.
+     */
+    public Appender parseAppender(Element appenderElement) {
+        String className = subst(appenderElement.getAttribute(CLASS_ATTR));
+        LOGGER.debug("Class name: [" + className + ']');
+        Appender appender = manager.parseAppender(className, appenderElement, this);
+        if (appender == null) {
+            appender = buildAppender(className, appenderElement);
+        }
+        return appender;
+    }
+
+    private Appender buildAppender(String className, Element appenderElement) {
+        try {
+            Appender appender = LoaderUtil.newInstanceOf(className);
+            PropertySetter propSetter = new PropertySetter(appender);
+
+            appender.setName(subst(appenderElement.getAttribute(NAME_ATTR)));
+            forEachElement(appenderElement.getChildNodes(), (currentElement) -> {
+                // Parse appender parameters
+                switch (currentElement.getTagName()) {
+                    case PARAM_TAG:
+                        setParameter(currentElement, propSetter);
+                        break;
+                    case LAYOUT_TAG:
+                        appender.setLayout(parseLayout(currentElement));
+                        break;
+                    case FILTER_TAG:
+                        Filter filter = parseFilters(currentElement);
+                        if (filter != null) {
+                            LOGGER.debug("Adding filter of type [{}] to appender named [{}]",
+                                    filter.getClass(), appender.getName());
+                            appender.addFilter(filter);
+                        }
+                        break;
+                    case ERROR_HANDLER_TAG:
+                        parseErrorHandler(currentElement, appender);
+                        break;
+                    case APPENDER_REF_TAG:
+                        String refName = subst(currentElement.getAttribute(REF_ATTR));
+                        if (appender instanceof AppenderAttachable) {
+                            AppenderAttachable aa = (AppenderAttachable) appender;
+                            Appender child = findAppenderByReference(currentElement);
+                            LOGGER.debug("Attaching appender named [{}] to appender named [{}].", refName,
+                                    appender.getName());
+                            aa.addAppender(child);
+                        } else {
+                            LOGGER.error("Requesting attachment of appender named [{}] to appender named [{}}]"
+                                            + "which does not implement org.apache.log4j.spi.AppenderAttachable.",
+                                    refName, appender.getName());
+                        }
+                        break;
+                    default:
+                        try {
+                            parseUnrecognizedElement(appender, currentElement, props);
+                        } catch (Exception ex) {
+                            throw new ConsumerException(ex);
+                        }
+                }
+            });
+            propSetter.activate();
+            return appender;
+        } catch (ConsumerException ex) {
+            Throwable t = ex.getCause();
+            if (t instanceof InterruptedException || t instanceof InterruptedIOException) {
+                Thread.currentThread().interrupt();
+            }
+            LOGGER.error("Could not create an Appender. Reported error follows.", t);
+        } catch (Exception oops) {
+            if (oops instanceof InterruptedException || oops instanceof InterruptedIOException) {
+                Thread.currentThread().interrupt();
+            }
+            LOGGER.error("Could not create an Appender. Reported error follows.", oops);
+        }
+        return null;
+    }
+
+    /**
+     * Used internally to parse an {@link ErrorHandler} element.
+     */
+    private void parseErrorHandler(Element element, Appender appender) {
+        ErrorHandler eh = (ErrorHandler) OptionConverter.instantiateByClassName(
+                subst(element.getAttribute(CLASS_ATTR)),
+                ErrorHandler.class,
+                null);
+
+        if (eh != null) {
+            eh.setAppender(appender);
+
+            PropertySetter propSetter = new PropertySetter(eh);
+            forEachElement(element.getChildNodes(), (currentElement) -> {
+                String tagName = currentElement.getTagName();
+                if (tagName.equals(PARAM_TAG)) {
+                    setParameter(currentElement, propSetter);
+                }
+            });
+            propSetter.activate();
+            appender.setErrorHandler(eh);
+        }
+    }
+
+    /**
+     * Used internally to parse a filter element.
+     */
+    public Filter parseFilters(Element filterElement) {
+        String className = subst(filterElement.getAttribute(CLASS_ATTR));
+        LOGGER.debug("Class name: [" + className + ']');
+        Filter filter = manager.parseFilter(className, filterElement, this);
+        if (filter == null) {
+            PropertySetter propSetter = new PropertySetter(filter);
+            forEachElement(filterElement.getChildNodes(), (currentElement) -> {
+                String tagName = currentElement.getTagName();
+                if (tagName.equals(PARAM_TAG)) {
+                    setParameter(currentElement, propSetter);
+                } else {
+                    quietParseUnrecognizedElement(filter, currentElement, props);
+                }
+            });
+            propSetter.activate();
+        }
+        return filter;
+    }
+
+    /**
+     * Used internally to parse an category element.
+     */
+    private void parseCategory(Element loggerElement) {
+        // Create a new org.apache.log4j.Category object from the <category> element.
+        String catName = subst(loggerElement.getAttribute(NAME_ATTR));
+        boolean additivity = OptionConverter.toBoolean(subst(loggerElement.getAttribute(ADDITIVITY_ATTR)), true);
+        LoggerConfig loggerConfig = getLogger(catName);
+        if (loggerConfig == null) {
+            loggerConfig = new LoggerConfig(catName, org.apache.logging.log4j.Level.ERROR, additivity);
+            addLogger(catName, loggerConfig);
+        } else {
+            loggerConfig.setAdditive(additivity);
+        }
+        parseChildrenOfLoggerElement(loggerElement, loggerConfig, false);
+    }
+
+    /**
+     * Used internally to parse the roor category element.
+     */
+    private void parseRoot(Element rootElement) {
+        LoggerConfig root = getRootLogger();
+        parseChildrenOfLoggerElement(rootElement, root, true);
+    }
+
+    /**
+     * Used internally to parse the children of a LoggerConfig element.
+     */
+    private void parseChildrenOfLoggerElement(Element catElement, LoggerConfig loggerConfig, boolean isRoot) {
+
+        final PropertySetter propSetter = new PropertySetter(loggerConfig);
+        loggerConfig.getAppenderRefs().clear();
+        forEachElement(catElement.getChildNodes(), (currentElement) -> {
+            switch (currentElement.getTagName()) {
+                case APPENDER_REF_TAG: {
+                    Appender appender = findAppenderByReference(currentElement);
+                    String refName = subst(currentElement.getAttribute(REF_ATTR));
+                    if (appender != null) {
+                        LOGGER.debug("Adding appender named [{}] to loggerConfig [{}].", refName,
+                                loggerConfig.getName());
+                        loggerConfig.addAppender(getAppender(refName), null, null);
+                    } else {
+                        LOGGER.debug("Appender named [{}}] not found.", refName);
+                    }
+                    break;
+                }
+                case LEVEL_TAG: case PRIORITY_TAG: {
+                    parseLevel(currentElement, loggerConfig, isRoot);
+                    break;
+                }
+                case PARAM_TAG: {
+                    setParameter(currentElement, propSetter);
+                    break;
+                }
+                default: {
+                    quietParseUnrecognizedElement(loggerConfig, currentElement, props);
+                }
+            }
+        });
+        propSetter.activate();
+    }
+
+    /**
+     * Used internally to parse a layout element.
+     */
+    public Layout parseLayout(Element layoutElement) {
+        String className = subst(layoutElement.getAttribute(CLASS_ATTR));
+        LOGGER.debug("Parsing layout of class: \"{}\"", className);
+        Layout layout = manager.parseLayout(className, layoutElement, this);
+        if (layout == null) {
+            layout = buildLayout(className, layoutElement);
+        }
+        return layout;
+    }
+
+    private Layout buildLayout(String className, Element layout_element) {
+        try {
+            Layout layout = LoaderUtil.newInstanceOf(className);
+            PropertySetter propSetter = new PropertySetter(layout);
+            forEachElement(layout_element.getChildNodes(), (currentElement) -> {
+                String tagName = currentElement.getTagName();
+                if (tagName.equals(PARAM_TAG)) {
+                    setParameter(currentElement, propSetter);
+                } else {
+                    try {
+                        parseUnrecognizedElement(layout, currentElement, props);
+                    } catch (Exception ex) {
+                        throw new ConsumerException(ex);
+                    }
+                }
+            });
+
+            propSetter.activate();
+            return layout;
+        } catch (ConsumerException ce) {
+            Throwable cause = ce.getCause();
+            if (cause instanceof InterruptedException || cause instanceof InterruptedIOException) {
+                Thread.currentThread().interrupt();
+            }
+            LOGGER.error("Could not create the Layout. Reported error follows.", cause);
+        } catch (Exception oops) {
+            if (oops instanceof InterruptedException || oops instanceof InterruptedIOException) {
+                Thread.currentThread().interrupt();
+            }
+            LOGGER.error("Could not create the Layout. Reported error follows.", oops);
+        }
+        return null;
+    }
+
+    /**
+     * Used internally to parse a level  element.
+     */
+    private void parseLevel(Element element, LoggerConfig logger, boolean isRoot) {
+        String catName = logger.getName();
+        if (isRoot) {
+            catName = "root";
+        }
+
+        String priStr = subst(element.getAttribute(VALUE_ATTR));
+        LOGGER.debug("Level value for {} is [{}}].", catName, priStr);
+
+        if (INHERITED.equalsIgnoreCase(priStr) || NULL.equalsIgnoreCase(priStr)) {
+            if (isRoot) {
+                LOGGER.error("Root level cannot be inherited. Ignoring directive.");
+            } else {
+                logger.setLevel(null);
+            }
+        } else {
+            String className = subst(element.getAttribute(CLASS_ATTR));
+            if (EMPTY_STR.equals(className)) {
+                logger.setLevel(OptionConverter.convertLevel(priStr, org.apache.logging.log4j.Level.DEBUG));
+            } else {
+                LOGGER.debug("Desired Level sub-class: [{}]", className);
+                try {
+                    Class<?> clazz = LoaderUtil.loadClass(className);
+                    Method toLevelMethod = clazz.getMethod("toLevel", ONE_STRING_PARAM);
+                    Level pri = (Level) toLevelMethod.invoke(null, new Object[]{priStr});
+                    logger.setLevel(OptionConverter.convertLevel(pri));
+                } catch (Exception oops) {
+                    if (oops instanceof InterruptedException || oops instanceof InterruptedIOException) {
+                        Thread.currentThread().interrupt();
+                    }
+                    LOGGER.error("Could not create level [" + priStr +
+                            "]. Reported error follows.", oops);
+                    return;
+                }
+            }
+        }
+        LOGGER.debug("{} level set to {}", catName,  logger.getLevel());
+    }
+
+    private void setParameter(Element elem, PropertySetter propSetter) {
+        String name = subst(elem.getAttribute(NAME_ATTR));
+        String value = (elem.getAttribute(VALUE_ATTR));
+        value = subst(OptionConverter.convertSpecialChars(value));
+        propSetter.setProperty(name, value);
+    }
+
+    /**
+     * Used internally to configure the log4j framework by parsing a DOM
+     * tree of XML elements based on <a
+     * href="doc-files/log4j.dtd">log4j.dtd</a>.
+     */
+    private void parse(Element element) {
+        String rootElementName = element.getTagName();
+
+        if (!rootElementName.equals(CONFIGURATION_TAG)) {
+            if (rootElementName.equals(OLD_CONFIGURATION_TAG)) {
+                LOGGER.warn("The <" + OLD_CONFIGURATION_TAG +
+                        "> element has been deprecated.");
+                LOGGER.warn("Use the <" + CONFIGURATION_TAG + "> element instead.");
+            } else {
+                LOGGER.error("DOM element is - not a <" + CONFIGURATION_TAG + "> element.");
+                return;
+            }
+        }
+
+
+        String debugAttrib = subst(element.getAttribute(INTERNAL_DEBUG_ATTR));
+
+        LOGGER.debug("debug attribute= \"" + debugAttrib + "\".");
+        // if the log4j.dtd is not specified in the XML file, then the
+        // "debug" attribute is returned as the empty string.
+        String status = "error";
+        if (!debugAttrib.equals("") && !debugAttrib.equals("null")) {
+            status = OptionConverter.toBoolean(debugAttrib, true) ? "debug" : "error";
+
+        } else {
+            LOGGER.debug("Ignoring " + INTERNAL_DEBUG_ATTR + " attribute.");
+        }
+
+        String confDebug = subst(element.getAttribute(CONFIG_DEBUG_ATTR));
+        if (!confDebug.equals("") && !confDebug.equals("null")) {
+            LOGGER.warn("The \"" + CONFIG_DEBUG_ATTR + "\" attribute is deprecated.");
+            LOGGER.warn("Use the \"" + INTERNAL_DEBUG_ATTR + "\" attribute instead.");
+            status = OptionConverter.toBoolean(confDebug, true) ? "debug" : "error";
+        }
+
+        final StatusConfiguration statusConfig = new StatusConfiguration().setStatus(status);
+        statusConfig.initialize();
+
+        forEachElement(element.getChildNodes(), (currentElement) -> {
+            switch (currentElement.getTagName()) {
+                case CATEGORY: case LOGGER_ELEMENT:
+                    parseCategory(currentElement);
+                    break;
+                case ROOT_TAG:
+                    parseRoot(currentElement);
+                    break;
+                case RENDERER_TAG:
+                    LOGGER.warn("Renderers are not supported by Log4j 2 and will be ignored.");
+                    break;
+                case THROWABLE_RENDERER_TAG:
+                    LOGGER.warn("Throwable Renderers are not supported by Log4j 2 and will be ignored.");
+                    break;
+                case CATEGORY_FACTORY_TAG: case LOGGER_FACTORY_TAG:
+                    LOGGER.warn("Log4j 1 Logger factories are not supported by Log4j 2 and will be ignored.");
+                    break;
+                case APPENDER_TAG:
+                    Appender appender = parseAppender(currentElement);
+                    appenderMap.put(appender.getName(), appender);
+                    if (appender instanceof AppenderWrapper) {
+                        addAppender(((AppenderWrapper) appender).getAppender());
+                    } else {
+                        addAppender(new AppenderAdapter(appender).getAdapter());
+                    }
+                    break;
+                default:
+                    quietParseUnrecognizedElement(null, currentElement, props);
+            }
+        });
+    }
+
+    private String subst(final String value) {
+        return getStrSubstitutor().replace(value);
+    }
+
+    public static void forEachElement(NodeList list, Consumer<Element> consumer) {
+        final int length = list.getLength();
+        for (int loop = 0; loop < length; loop++) {
+            Node currentNode = list.item(loop);
+
+            if (currentNode.getNodeType() == Node.ELEMENT_NODE) {
+                Element currentElement = (Element) currentNode;
+                consumer.accept(currentElement);
+            }
+        }
+    }
+
+    private interface ParseAction {
+        Document parse(final DocumentBuilder parser) throws SAXException, IOException;
+    }
+
+    private static class SAXErrorHandler implements org.xml.sax.ErrorHandler {
+        private static final org.apache.logging.log4j.Logger LOGGER = StatusLogger.getLogger();
+
+        public void error(final SAXParseException ex) {
+            emitMessage("Continuable parsing error ", ex);
+        }
+
+        public void fatalError(final SAXParseException ex) {
+            emitMessage("Fatal parsing error ", ex);
+        }
+
+        public void warning(final SAXParseException ex) {
+            emitMessage("Parsing warning ", ex);
+        }
+
+        private static void emitMessage(final String msg, final SAXParseException ex) {
+            LOGGER.warn("{} {} and column {}", msg, ex.getLineNumber(), ex.getColumnNumber());
+            LOGGER.warn(ex.getMessage(), ex.getException());
+        }
+    }
+
+    private static class ConsumerException extends RuntimeException {
+
+        ConsumerException(Exception ex) {
+            super(ex);
+        }
+    }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/xml/XmlConfigurationFactory.java b/log4j-1.2-api/src/main/java/org/apache/log4j/xml/XmlConfigurationFactory.java
index 17235a4..04b596e 100644
--- a/log4j-1.2-api/src/main/java/org/apache/log4j/xml/XmlConfigurationFactory.java
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/xml/XmlConfigurationFactory.java
@@ -17,105 +17,24 @@
 
 package org.apache.log4j.xml;
 
-import org.apache.log4j.Appender;
-import org.apache.log4j.Layout;
-import org.apache.log4j.Level;
-import org.apache.log4j.bridge.AppenderAdapter;
-import org.apache.log4j.bridge.AppenderWrapper;
-import org.apache.log4j.builders.BuilderManager;
 import org.apache.log4j.config.Log4j1Configuration;
-import org.apache.log4j.config.PropertySetter;
-import org.apache.log4j.helpers.OptionConverter;
-import org.apache.log4j.spi.AppenderAttachable;
-import org.apache.log4j.spi.Configurator;
-import org.apache.log4j.spi.ErrorHandler;
-import org.apache.log4j.spi.Filter;
 import org.apache.logging.log4j.core.LoggerContext;
 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.LoggerConfig;
 import org.apache.logging.log4j.core.config.Order;
-import org.apache.logging.log4j.core.config.status.StatusConfiguration;
 import org.apache.logging.log4j.plugins.Plugin;
 import org.apache.logging.log4j.status.StatusLogger;
-import org.apache.logging.log4j.util.LoaderUtil;
-import org.w3c.dom.Document;
-import org.w3c.dom.Element;
-import org.w3c.dom.NamedNodeMap;
-import org.w3c.dom.Node;
-import org.w3c.dom.NodeList;
-import org.xml.sax.InputSource;
-import org.xml.sax.SAXException;
-import org.xml.sax.SAXParseException;
-
-import javax.xml.parsers.DocumentBuilder;
-import javax.xml.parsers.DocumentBuilderFactory;
-import javax.xml.parsers.FactoryConfigurationError;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InterruptedIOException;
-import java.io.Reader;
-import java.lang.reflect.Method;
-import java.net.URL;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Properties;
-import java.util.concurrent.TimeUnit;
-import java.util.function.Consumer;
+import org.apache.logging.log4j.util.PropertiesUtil;
 
 /**
  * Constructs a Configuration usable in Log4j 2 from a Log4j 1 configuration file.
  */
 @Plugin(name = "Log4j1XmlConfigurationFactory", category = ConfigurationFactory.CATEGORY)
 @Order(2)
-public class XmlConfigurationFactory extends ConfigurationFactory implements Configurator {
+public class XmlConfigurationFactory extends ConfigurationFactory {
     private static final org.apache.logging.log4j.Logger LOGGER = StatusLogger.getLogger();
 
-    private static final String CONFIGURATION_TAG = "log4j:configuration";
-    private static final String OLD_CONFIGURATION_TAG = "configuration";
-    private static final String RENDERER_TAG = "renderer";
-    private static final String APPENDER_TAG = "appender";
-    private static final String APPENDER_REF_TAG = "appender-ref";
-    public  static final String PARAM_TAG = "param";
-    public static final String LAYOUT_TAG = "layout";
-    private static final String CATEGORY = "category";
-    private static final String LOGGER_ELEMENT = "logger";
-    private static final String CATEGORY_FACTORY_TAG = "categoryFactory";
-    private static final String LOGGER_FACTORY_TAG = "loggerFactory";
-    public static final String NAME_ATTR = "name";
-    private static final String CLASS_ATTR = "class";
-    public static final String VALUE_ATTR = "value";
-    private static final String ROOT_TAG = "root";
-    private static final String LEVEL_TAG = "level";
-    private static final String PRIORITY_TAG = "priority";
-    public static final String FILTER_TAG = "filter";
-    private static final String ERROR_HANDLER_TAG = "errorHandler";
-    private static final String REF_ATTR = "ref";
-    private static final String ADDITIVITY_ATTR = "additivity";
-    private static final String CONFIG_DEBUG_ATTR = "configDebug";
-    private static final String INTERNAL_DEBUG_ATTR = "debug";
-    private static final String EMPTY_STR = "";
-    private static final Class[] ONE_STRING_PARAM = new Class[]{String.class};
-    private static final String dbfKey = "javax.xml.parsers.DocumentBuilderFactory";
-    private static final String THROWABLE_RENDERER_TAG = "throwableRenderer";
-    private static final String SYSTEM_OUT = "System.out";
-    private static final String SYSTEM_ERR = "System.err";
-    private static final String THREAD_PRINTING_PARAM = "threadprinting";
-    private static final String CATEGORY_PREFIXING_PARAM = "categoryprefixing";
-    private static final String CONTEXT_PRINTING_PARAM = "contextprinting";
-    private static final String DATE_FORMAT_PARAM = "dateformat";
-    private static final String TIMEZONE_FORMAT = "timezone";
-    public static final String FILE_PARAM = "file";
-    public static final String APPEND_PARAM = "append";
-    public static final String BUFFERED_IO_PARAM = "bufferedio";
-    public static final String BUFFER_SIZE_PARAM = "buffersize";
-    public static final String MAX_SIZE_PARAM = "maxfileSize";
-    public static final String MAX_BACKUP_INDEX = "maxbackupindex";
-    public static final String RELATIVE = "RELATIVE";
-    public static final long DEFAULT_DELAY = 60000;
     /**
      * File name prefix for test configurations.
      */
@@ -126,47 +45,18 @@
      */
     protected static final String DEFAULT_PREFIX = "log4j";
 
-    private final BuilderManager manager;
-
-    // key: appenderName, value: appender
-    private Map<String, Appender> appenderBag;
-
-    private Properties props = null;
-
-    private final LoggerContext loggerContext;
-    private Log4j1Configuration configuration;
-
-    /**
-     * No argument constructor.
-     */
-    public XmlConfigurationFactory() {
-        appenderBag = new HashMap<>();
-        loggerContext = (LoggerContext) org.apache.logging.log4j.LogManager.getContext(false);
-        manager = new BuilderManager();
-    }
-
-
-    private XmlConfigurationFactory(ConfigurationSource source, int monitorIntervalSeconds) {
-        appenderBag = new HashMap<>();
-        loggerContext = (LoggerContext) org.apache.logging.log4j.LogManager.getContext(false);
-        configuration = new Log4j1Configuration(loggerContext, source, monitorIntervalSeconds);
-        manager = new BuilderManager();
-    }
-
     @Override
     protected String[] getSupportedTypes() {
+        if (!PropertiesUtil.getProperties().getBooleanProperty(ConfigurationFactory.LOG4J1_EXPERIMENTAL, Boolean.FALSE)) {
+            return null;
+        }
         return new String[] {".xml"};
     }
 
     @Override
     public Configuration getConfiguration(LoggerContext loggerContext, ConfigurationSource source) {
-        configuration = new Log4j1Configuration(loggerContext, source, 0);
-        doConfigure();
-        return configuration;
-    }
-
-    public Configuration getConfiguration() {
-        return configuration;
+        int interval = PropertiesUtil.getProperties().getIntegerProperty(Log4j1Configuration.MONITOR_INTERVAL, 0);
+        return new XmlConfiguration(loggerContext, source, interval);
     }
 
     @Override
@@ -179,731 +69,8 @@
         return DEFAULT_PREFIX;
     }
 
-    /**
-     * Delegates unrecognized content to created instance if
-     * it supports UnrecognizedElementParser.
-     *
-     * @param instance instance, may be null.
-     * @param element  element, may not be null.
-     * @param props    properties
-     * @throws IOException thrown if configuration of owner object
-     *                     should be abandoned.
-     * @since 1.2.15
-     */
-    private static void parseUnrecognizedElement(final Object instance, final Element element,
-            final Properties props) throws Exception {
-        boolean recognized = false;
-        if (instance instanceof UnrecognizedElementHandler) {
-            recognized = ((UnrecognizedElementHandler) instance).parseUnrecognizedElement(
-                    element, props);
-        }
-        if (!recognized) {
-            LOGGER.warn("Unrecognized element {}", element.getNodeName());
-        }
-    }
-
-    /**
-     * Delegates unrecognized content to created instance if
-     * it supports UnrecognizedElementParser and catches and
-     * logs any exception.
-     *
-     * @param instance instance, may be null.
-     * @param element  element, may not be null.
-     * @param props    properties
-     * @since 1.2.15
-     */
-    private static void quietParseUnrecognizedElement(final Object instance,
-            final Element element,
-            final Properties props) {
-        try {
-            parseUnrecognizedElement(instance, element, props);
-        } catch (Exception ex) {
-            if (ex instanceof InterruptedException || ex instanceof InterruptedIOException) {
-                Thread.currentThread().interrupt();
-            }
-            LOGGER.error("Error in extension content: ", ex);
-        }
-    }
-
-    /**
-     * Like {@link #configureAndWatch(String, long)} except that the
-     * default delay is used.
-     *
-     * @param configFilename A log4j configuration file in XML format.
-     */
-    public static void configureAndWatch(final String configFilename) {
-        configureAndWatch(configFilename, DEFAULT_DELAY);
-    }
-
-    /**
-     * Read the configuration file <code>configFilename</code> if it
-     * exists. Moreover, a thread will be created that will periodically
-     * check if <code>configFilename</code> has been created or
-     * modified. The period is determined by the <code>delay</code>
-     * argument. If a change or file creation is detected, then
-     * <code>configFilename</code> is read to configure log4j.
-     *
-     * @param configFilename A log4j configuration file in XML format.
-     * @param delay          The delay in milliseconds to wait between each check.
-     */
-    public static void configureAndWatch(final String configFilename, final long delay) {
-        try {
-            File file = new File(configFilename);
-            InputStream is = new FileInputStream(file);
-            ConfigurationSource source = new ConfigurationSource(is, file);
-            int seconds = (int) TimeUnit.MILLISECONDS.toSeconds(delay);
-            XmlConfigurationFactory factory = new XmlConfigurationFactory(source, seconds);
-            factory.doConfigure();
-            org.apache.logging.log4j.core.config.Configurator.reconfigure(factory.getConfiguration());
-
-        } catch (IOException ioe) {
-            LOGGER.error("Unable to process configuration file {} due to {}", configFilename, ioe.getMessage());
-        }
-    }
-
-    /**
-     * A static version of doConfigure(String).
-     */
-    public static void configure(final String filename) throws FactoryConfigurationError {
-        configureAndWatch(filename, 0);
-    }
-
-    /**
-     * A static version of doConfigure(URL).
-     */
-    public static void configure(final URL url) throws FactoryConfigurationError {
-        try {
-            InputStream is = url.openStream();
-            ConfigurationSource source = new ConfigurationSource(is, url);
-            XmlConfigurationFactory factory = new XmlConfigurationFactory(source, 0);
-            factory.doConfigure();
-            org.apache.logging.log4j.core.config.Configurator.reconfigure(factory.getConfiguration());
-        } catch (IOException ioe) {
-            LOGGER.error("Unable to process configuration {} due to {}", url.toString(), ioe.getMessage());
-        }
-    }
-
-    /**
-     * Substitutes property value for any references in expression.
-     *
-     * @param value value from configuration file, may contain
-     *              literal text, property references or both
-     * @param props properties.
-     * @return evaluated expression, may still contain expressions
-     * if unable to expand.
-     */
-    public static String subst(final String value, final Properties props) {
-        try {
-            return OptionConverter.substVars(value, props);
-        } catch (IllegalArgumentException e) {
-            LOGGER.warn("Could not perform variable substitution.", e);
-            return value;
-        }
-    }
-
-    /**
-     * Sets a parameter based from configuration file content.
-     *
-     * @param elem       param element, may not be null.
-     * @param propSetter property setter, may not be null.
-     * @param props      properties
-     * @since 1.2.15
-     */
-    public static void setParameter(final Element elem, final PropertySetter propSetter, final Properties props) {
-        String name = subst(elem.getAttribute("name"), props);
-        String value = (elem.getAttribute("value"));
-        value = subst(OptionConverter.convertSpecialChars(value), props);
-        propSetter.setProperty(name, value);
-    }
-
-    /**
-     * Creates an object and processes any nested param elements
-     * but does not call activateOptions.  If the class also supports
-     * UnrecognizedElementParser, the parseUnrecognizedElement method
-     * will be call for any child elements other than param.
-     *
-     * @param element       element, may not be null.
-     * @param props         properties
-     * @param expectedClass interface or class expected to be implemented
-     *                      by created class
-     * @return created class or null.
-     * @throws Exception thrown if the contain object should be abandoned.
-     * @since 1.2.15
-     */
-    public static Object parseElement(final Element element, final Properties props,
-            @SuppressWarnings("rawtypes") final Class expectedClass) throws Exception {
-        String clazz = subst(element.getAttribute("class"), props);
-        Object instance = OptionConverter.instantiateByClassName(clazz,
-                expectedClass, null);
-
-        if (instance != null) {
-            PropertySetter propSetter = new PropertySetter(instance);
-            NodeList children = element.getChildNodes();
-            final int length = children.getLength();
-
-            for (int loop = 0; loop < length; loop++) {
-                Node currentNode = children.item(loop);
-                if (currentNode.getNodeType() == Node.ELEMENT_NODE) {
-                    Element currentElement = (Element) currentNode;
-                    String tagName = currentElement.getTagName();
-                    if (tagName.equals("param")) {
-                        setParameter(currentElement, propSetter, props);
-                    } else {
-                        parseUnrecognizedElement(instance, currentElement, props);
-                    }
-                }
-            }
-            return instance;
-        }
-        return null;
-    }
-
-    /**
-     * Used internally to parse appenders by IDREF name.
-     */
-    private Appender findAppenderByName(Document doc, String appenderName) {
-        Appender appender = appenderBag.get(appenderName);
-
-        if (appender != null) {
-            return appender;
-        } else {
-            // Doesn't work on DOM Level 1 :
-            // Element element = doc.getElementById(appenderName);
-
-            // Endre's hack:
-            Element element = null;
-            NodeList list = doc.getElementsByTagName("appender");
-            for (int t = 0; t < list.getLength(); t++) {
-                Node node = list.item(t);
-                NamedNodeMap map = node.getAttributes();
-                Node attrNode = map.getNamedItem("name");
-                if (appenderName.equals(attrNode.getNodeValue())) {
-                    element = (Element) node;
-                    break;
-                }
-            }
-            // Hack finished.
-
-            if (element == null) {
-
-                LOGGER.error("No appender named [{}] could be found.", appenderName);
-                return null;
-            } else {
-                appender = parseAppender(element);
-                if (appender != null) {
-                    appenderBag.put(appenderName, appender);
-                }
-                return appender;
-            }
-        }
-    }
-
-    /**
-     * Used internally to parse appenders by IDREF element.
-     */
-    private Appender findAppenderByReference(Element appenderRef) {
-        String appenderName = subst(appenderRef.getAttribute(REF_ATTR));
-        Document doc = appenderRef.getOwnerDocument();
-        return findAppenderByName(doc, appenderName);
-    }
-
-    /**
-     * Used internally to parse an appender element.
-     */
-    private Appender parseAppender(Element appenderElement) {
-        String className = subst(appenderElement.getAttribute(CLASS_ATTR));
-        LOGGER.debug("Class name: [" + className + ']');
-        Appender appender = manager.parseAppender(className, appenderElement, this);
-        if (appender == null) {
-            appender = buildAppender(className, appenderElement);
-        }
-        return appender;
-    }
-
-    private Appender buildAppender(String className, Element appenderElement) {
-            try {
-                Appender appender = LoaderUtil.newInstanceOf(className);
-                PropertySetter propSetter = new PropertySetter(appender);
-
-                appender.setName(subst(appenderElement.getAttribute(NAME_ATTR)));
-                forEachElement(appenderElement.getChildNodes(), (currentElement) -> {
-                    // Parse appender parameters
-                    switch (currentElement.getTagName()) {
-                        case PARAM_TAG:
-                            setParameter(currentElement, propSetter);
-                            break;
-                        case LAYOUT_TAG:
-                            appender.setLayout(parseLayout(currentElement));
-                            break;
-                        case FILTER_TAG:
-                            Filter filter = parseFilters(currentElement);
-                            if (filter != null) {
-                                LOGGER.debug("Adding filter of type [{}] to appender named [{}]",
-                                        filter.getClass(), appender.getName());
-                                appender.addFilter(filter);
-                            }
-                            break;
-                        case ERROR_HANDLER_TAG:
-                            parseErrorHandler(currentElement, appender);
-                            break;
-                        case APPENDER_REF_TAG:
-                            String refName = subst(currentElement.getAttribute(REF_ATTR));
-                            if (appender instanceof AppenderAttachable) {
-                                AppenderAttachable aa = (AppenderAttachable) appender;
-                                Appender child = findAppenderByReference(currentElement);
-                                LOGGER.debug("Attaching appender named [{}] to appender named [{}].", refName,
-                                        appender.getName());
-                                aa.addAppender(child);
-                            } else {
-                                LOGGER.error("Requesting attachment of appender named [{}] to appender named [{}}]"
-                                                + "which does not implement org.apache.log4j.spi.AppenderAttachable.",
-                                        refName, appender.getName());
-                            }
-                            break;
-                        default:
-                            try {
-                                parseUnrecognizedElement(appender, currentElement, props);
-                            } catch (Exception ex) {
-                                throw new ConsumerException(ex);
-                            }
-                    }
-                });
-                propSetter.activate();
-                return appender;
-            } catch (ConsumerException ex) {
-                Throwable t = ex.getCause();
-                if (t instanceof InterruptedException || t instanceof InterruptedIOException) {
-                    Thread.currentThread().interrupt();
-                }
-                LOGGER.error("Could not create an Appender. Reported error follows.", t);
-            } catch (Exception oops) {
-                if (oops instanceof InterruptedException || oops instanceof InterruptedIOException) {
-                    Thread.currentThread().interrupt();
-                }
-                LOGGER.error("Could not create an Appender. Reported error follows.", oops);
-            }
-            return null;
-        }
-
-    /**
-     * Used internally to parse an {@link ErrorHandler} element.
-     */
-    private void parseErrorHandler(Element element, Appender appender) {
-        ErrorHandler eh = (ErrorHandler) OptionConverter.instantiateByClassName(
-                subst(element.getAttribute(CLASS_ATTR)),
-                ErrorHandler.class,
-                null);
-
-        if (eh != null) {
-            eh.setAppender(appender);
-
-            PropertySetter propSetter = new PropertySetter(eh);
-            forEachElement(element.getChildNodes(), (currentElement) -> {
-                String tagName = currentElement.getTagName();
-                if (tagName.equals(PARAM_TAG)) {
-                    setParameter(currentElement, propSetter);
-                }
-            });
-            propSetter.activate();
-            appender.setErrorHandler(eh);
-        }
-    }
-
-    /**
-     * Used internally to parse a filter element.
-     */
-    public Filter parseFilters(Element filterElement) {
-        String className = subst(filterElement.getAttribute(CLASS_ATTR));
-        LOGGER.debug("Class name: [" + className + ']');
-        Filter filter = manager.parseFilter(className, filterElement, this);
-        if (filter == null) {
-            PropertySetter propSetter = new PropertySetter(filter);
-            forEachElement(filterElement.getChildNodes(), (currentElement) -> {
-                String tagName = currentElement.getTagName();
-                if (tagName.equals(PARAM_TAG)) {
-                    setParameter(currentElement, propSetter);
-                } else {
-                    quietParseUnrecognizedElement(filter, currentElement, props);
-                }
-            });
-            propSetter.activate();
-        }
-        return filter;
-    }
-
-    /**
-     * Used internally to parse an category element.
-     */
-    private void parseCategory(Element loggerElement) {
-        // Create a new org.apache.log4j.Category object from the <category> element.
-        String catName = subst(loggerElement.getAttribute(NAME_ATTR));
-        boolean additivity = OptionConverter.toBoolean(subst(loggerElement.getAttribute(ADDITIVITY_ATTR)), true);
-        LoggerConfig loggerConfig = configuration.getLogger(catName);
-        if (loggerConfig == null) {
-            loggerConfig = new LoggerConfig(catName, org.apache.logging.log4j.Level.ERROR, additivity);
-            configuration.addLogger(catName, loggerConfig);
-        } else {
-            loggerConfig.setAdditive(additivity);
-        }
-        parseChildrenOfLoggerElement(loggerElement, loggerConfig, false);
-    }
-
-    /**
-     * Used internally to parse the roor category element.
-     */
-    private void parseRoot(Element rootElement) {
-        LoggerConfig root = configuration.getRootLogger();
-        parseChildrenOfLoggerElement(rootElement, root, true);
-    }
-
-    /**
-     * Used internally to parse the children of a LoggerConfig element.
-     */
-    private void parseChildrenOfLoggerElement(Element catElement, LoggerConfig loggerConfig, boolean isRoot) {
-
-        final PropertySetter propSetter = new PropertySetter(loggerConfig);
-        loggerConfig.getAppenderRefs().clear();
-        forEachElement(catElement.getChildNodes(), (currentElement) -> {
-            switch (currentElement.getTagName()) {
-                case APPENDER_REF_TAG: {
-                    Appender appender = findAppenderByReference(currentElement);
-                    String refName = subst(currentElement.getAttribute(REF_ATTR));
-                    if (appender != null) {
-                        LOGGER.debug("Adding appender named [{}] to loggerConfig [{}].", refName,
-                                loggerConfig.getName());
-                        loggerConfig.addAppender(configuration.getAppender(refName), null, null);
-                    } else {
-                        LOGGER.debug("Appender named [{}}] not found.", refName);
-                    }
-                    break;
-                }
-                case LEVEL_TAG: case PRIORITY_TAG: {
-                    parseLevel(currentElement, loggerConfig, isRoot);
-                    break;
-                }
-                case PARAM_TAG: {
-                    setParameter(currentElement, propSetter);
-                    break;
-                }
-                default: {
-                    quietParseUnrecognizedElement(loggerConfig, currentElement, props);
-                }
-            }
-        });
-        propSetter.activate();
-    }
-
-    /**
-     * Used internally to parse a layout element.
-     */
-    public Layout parseLayout(Element layoutElement) {
-        String className = subst(layoutElement.getAttribute(CLASS_ATTR));
-        LOGGER.debug("Parsing layout of class: \"{}\"", className);
-        Layout layout = manager.parseLayout(className, layoutElement, this);
-        if (layout == null) {
-            layout = buildLayout(className, layoutElement);
-        }
-        return layout;
-    }
-
-    private Layout buildLayout(String className, Element layout_element) {
-        try {
-            Layout layout = LoaderUtil.newInstanceOf(className);
-            PropertySetter propSetter = new PropertySetter(layout);
-            forEachElement(layout_element.getChildNodes(), (currentElement) -> {
-                String tagName = currentElement.getTagName();
-                if (tagName.equals(PARAM_TAG)) {
-                    setParameter(currentElement, propSetter);
-                } else {
-                    try {
-                        parseUnrecognizedElement(layout, currentElement, props);
-                    } catch (Exception ex) {
-                        throw new ConsumerException(ex);
-                    }
-                }
-            });
-
-            propSetter.activate();
-            return layout;
-        } catch (ConsumerException ce) {
-            Throwable cause = ce.getCause();
-            if (cause instanceof InterruptedException || cause instanceof InterruptedIOException) {
-                Thread.currentThread().interrupt();
-            }
-            LOGGER.error("Could not create the Layout. Reported error follows.", cause);
-        } catch (Exception oops) {
-            if (oops instanceof InterruptedException || oops instanceof InterruptedIOException) {
-                Thread.currentThread().interrupt();
-            }
-            LOGGER.error("Could not create the Layout. Reported error follows.", oops);
-        }
-        return null;
-    }
-
-    /**
-     * Used internally to parse a level  element.
-     */
-    private void parseLevel(Element element, LoggerConfig logger, boolean isRoot) {
-        String catName = logger.getName();
-        if (isRoot) {
-            catName = "root";
-        }
-
-        String priStr = subst(element.getAttribute(VALUE_ATTR));
-        LOGGER.debug("Level value for {} is [{}}].", catName, priStr);
-
-        if (INHERITED.equalsIgnoreCase(priStr) || NULL.equalsIgnoreCase(priStr)) {
-            if (isRoot) {
-                LOGGER.error("Root level cannot be inherited. Ignoring directive.");
-            } else {
-                logger.setLevel(null);
-            }
-        } else {
-            String className = subst(element.getAttribute(CLASS_ATTR));
-            if (EMPTY_STR.equals(className)) {
-                logger.setLevel(convertLevel(OptionConverter.toLevel(priStr, Level.DEBUG)));
-            } else {
-                LOGGER.debug("Desired Level sub-class: [{}]", className);
-                try {
-                    Class<?> clazz = LoaderUtil.loadClass(className);
-                    Method toLevelMethod = clazz.getMethod("toLevel", ONE_STRING_PARAM);
-                    Level pri = (Level) toLevelMethod.invoke(null, new Object[]{priStr});
-                    logger.setLevel(convertLevel(pri));
-                } catch (Exception oops) {
-                    if (oops instanceof InterruptedException || oops instanceof InterruptedIOException) {
-                        Thread.currentThread().interrupt();
-                    }
-                    LOGGER.error("Could not create level [" + priStr +
-                            "]. Reported error follows.", oops);
-                    return;
-                }
-            }
-        }
-        LOGGER.debug("{} level set to {}", catName,  logger.getLevel());
-    }
-
-    private void setParameter(Element elem, PropertySetter propSetter) {
-        String name = subst(elem.getAttribute(NAME_ATTR));
-        String value = (elem.getAttribute(VALUE_ATTR));
-        value = subst(OptionConverter.convertSpecialChars(value));
-        propSetter.setProperty(name, value);
-    }
-
-    /**
-     * Configure log4j by reading in a log4j.dtd compliant XML
-     * configuration file.
-     */
-    private void doConfigure() throws FactoryConfigurationError {
-        ConfigurationSource source = configuration.getConfigurationSource();
-        ParseAction action = new ParseAction() {
-            public Document parse(final DocumentBuilder parser) throws SAXException, IOException {
-                InputSource inputSource = new InputSource(source.getInputStream());
-                inputSource.setSystemId("dummy://log4j.dtd");
-                return parser.parse(inputSource);
-            }
-
-            public String toString() {
-                return configuration.getConfigurationSource().getLocation();
-            }
-        };
-        doConfigure(action);
-    }
-
-    private void doConfigure(final ParseAction action) throws FactoryConfigurationError {
-        DocumentBuilderFactory dbf;
-        try {
-            LOGGER.debug("System property is : {}", OptionConverter.getSystemProperty(dbfKey, null));
-            dbf = DocumentBuilderFactory.newInstance();
-            LOGGER.debug("Standard DocumentBuilderFactory search succeded.");
-            LOGGER.debug("DocumentBuilderFactory is: " + dbf.getClass().getName());
-        } catch (FactoryConfigurationError fce) {
-            Exception e = fce.getException();
-            LOGGER.debug("Could not instantiate a DocumentBuilderFactory.", e);
-            throw fce;
-        }
-
-        try {
-            dbf.setValidating(true);
-
-            DocumentBuilder docBuilder = dbf.newDocumentBuilder();
-
-            docBuilder.setErrorHandler(new SAXErrorHandler());
-            docBuilder.setEntityResolver(new Log4jEntityResolver());
-
-            Document doc = action.parse(docBuilder);
-            parse(doc.getDocumentElement());
-        } catch (Exception e) {
-            if (e instanceof InterruptedException || e instanceof InterruptedIOException) {
-                Thread.currentThread().interrupt();
-            }
-            // I know this is miserable...
-            LOGGER.error("Could not parse " + action.toString() + ".", e);
-        }
-    }
-
     @Override
-    public void doConfigure(InputStream inputStream, LoggerContext loggerContext) {
-        try {
-            ConfigurationSource source = new ConfigurationSource(inputStream);
-            configuration = new Log4j1Configuration(loggerContext, source, 0);
-            doConfigure();
-        } catch (IOException ioe) {
-            LOGGER.error("Unable to process configuration due to {}",  ioe.getMessage());
-        }
-    }
-
-    @Override
-    public void doConfigure(URL url, LoggerContext loggerContext) {
-        try {
-            ConfigurationSource source = new ConfigurationSource(url.openStream(), url);
-            configuration = new Log4j1Configuration(loggerContext, source, 0);
-            doConfigure();
-        } catch (IOException ioe) {
-            LOGGER.error("Unable to process configuration due to {}",  ioe.getMessage());
-        }
-    }
-
-    /**
-     * Used internally to configure the log4j framework by parsing a DOM
-     * tree of XML elements based on <a
-     * href="doc-files/log4j.dtd">log4j.dtd</a>.
-     */
-    private void parse(Element element) {
-        String rootElementName = element.getTagName();
-
-        if (!rootElementName.equals(CONFIGURATION_TAG)) {
-            if (rootElementName.equals(OLD_CONFIGURATION_TAG)) {
-                LOGGER.warn("The <" + OLD_CONFIGURATION_TAG +
-                        "> element has been deprecated.");
-                LOGGER.warn("Use the <" + CONFIGURATION_TAG + "> element instead.");
-            } else {
-                LOGGER.error("DOM element is - not a <" + CONFIGURATION_TAG + "> element.");
-                return;
-            }
-        }
-
-
-        String debugAttrib = subst(element.getAttribute(INTERNAL_DEBUG_ATTR));
-
-        LOGGER.debug("debug attribute= \"" + debugAttrib + "\".");
-        // if the log4j.dtd is not specified in the XML file, then the
-        // "debug" attribute is returned as the empty string.
-        String status = "error";
-        if (!debugAttrib.equals("") && !debugAttrib.equals("null")) {
-            status = OptionConverter.toBoolean(debugAttrib, true) ? "debug" : "error";
-
-        } else {
-            LOGGER.debug("Ignoring " + INTERNAL_DEBUG_ATTR + " attribute.");
-        }
-
-        String confDebug = subst(element.getAttribute(CONFIG_DEBUG_ATTR));
-        if (!confDebug.equals("") && !confDebug.equals("null")) {
-            LOGGER.warn("The \"" + CONFIG_DEBUG_ATTR + "\" attribute is deprecated.");
-            LOGGER.warn("Use the \"" + INTERNAL_DEBUG_ATTR + "\" attribute instead.");
-            status = OptionConverter.toBoolean(confDebug, true) ? "debug" : "error";
-        }
-
-        final StatusConfiguration statusConfig = new StatusConfiguration().setStatus(status);
-        statusConfig.initialize();
-
-        forEachElement(element.getChildNodes(), (currentElement) -> {
-            switch (currentElement.getTagName()) {
-                case CATEGORY: case LOGGER_ELEMENT:
-                    parseCategory(currentElement);
-                    break;
-                case ROOT_TAG:
-                    parseRoot(currentElement);
-                    break;
-                case RENDERER_TAG:
-                    LOGGER.warn("Renderers are not supported by Log4j 2 and will be ignored.");
-                    break;
-                case THROWABLE_RENDERER_TAG:
-                    LOGGER.warn("Throwable Renderers are not supported by Log4j 2 and will be ignored.");
-                    break;
-                case CATEGORY_FACTORY_TAG: case LOGGER_FACTORY_TAG:
-                    LOGGER.warn("Log4j 1 Logger factories are not supported by Log4j 2 and will be ignored.");
-                    break;
-                case APPENDER_TAG:
-                    Appender appender = parseAppender(currentElement);
-                    appenderBag.put(appender.getName(), appender);
-                    if (appender instanceof AppenderWrapper) {
-                        configuration.addAppender(((AppenderWrapper) appender).getAppender());
-                    } else {
-                        configuration.addAppender(new AppenderAdapter(appender).getAdapter());
-                    }
-                    break;
-                default:
-                    quietParseUnrecognizedElement(null, currentElement, props);
-            }
-        });
-    }
-
-    private org.apache.logging.log4j.Level convertLevel(Level level) {
-        if (level == null) {
-            return org.apache.logging.log4j.Level.ERROR;
-        }
-        if (level.isGreaterOrEqual(Level.FATAL)) {
-            return org.apache.logging.log4j.Level.FATAL;
-        } else if (level.isGreaterOrEqual(Level.ERROR)) {
-            return org.apache.logging.log4j.Level.ERROR;
-        } else if (level.isGreaterOrEqual(Level.WARN)) {
-            return org.apache.logging.log4j.Level.WARN;
-        } else if (level.isGreaterOrEqual(Level.INFO)) {
-            return org.apache.logging.log4j.Level.INFO;
-        } else if (level.isGreaterOrEqual(Level.DEBUG)) {
-            return org.apache.logging.log4j.Level.DEBUG;
-        } else if (level.isGreaterOrEqual(Level.TRACE)) {
-            return org.apache.logging.log4j.Level.TRACE;
-        }
-        return org.apache.logging.log4j.Level.ALL;
-    }
-
-    private String subst(final String value) {
-        return configuration.getStrSubstitutor().replace(value);
-    }
-
-    public static void forEachElement(NodeList list, Consumer<Element> consumer) {
-        final int length = list.getLength();
-        for (int loop = 0; loop < length; loop++) {
-            Node currentNode = list.item(loop);
-
-            if (currentNode.getNodeType() == Node.ELEMENT_NODE) {
-                Element currentElement = (Element) currentNode;
-                consumer.accept(currentElement);
-            }
-        }
-    }
-
-    private interface ParseAction {
-        Document parse(final DocumentBuilder parser) throws SAXException, IOException;
-    }
-
-    private static class SAXErrorHandler implements org.xml.sax.ErrorHandler {
-        private static final org.apache.logging.log4j.Logger LOGGER = StatusLogger.getLogger();
-
-        public void error(final SAXParseException ex) {
-            emitMessage("Continuable parsing error ", ex);
-        }
-
-        public void fatalError(final SAXParseException ex) {
-            emitMessage("Fatal parsing error ", ex);
-        }
-
-        public void warning(final SAXParseException ex) {
-            emitMessage("Parsing warning ", ex);
-        }
-
-        private static void emitMessage(final String msg, final SAXParseException ex) {
-            LOGGER.warn("{} {} and column {}", msg, ex.getLineNumber(), ex.getColumnNumber());
-            LOGGER.warn(ex.getMessage(), ex.getException());
-        }
-    }
-
-    private static class ConsumerException extends RuntimeException {
-
-        ConsumerException(Exception ex) {
-            super(ex);
-        }
+    protected String getVersion() {
+        return LOG4J1_VERSION;
     }
 }
-
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/config/AsyncAppenderTest.java b/log4j-1.2-api/src/test/java/org/apache/log4j/config/AsyncAppenderTest.java
new file mode 100644
index 0000000..bac1338
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/config/AsyncAppenderTest.java
@@ -0,0 +1,103 @@
+/*
+ * 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.log4j.config;
+
+import org.apache.log4j.ListAppender;
+import org.apache.log4j.LogManager;
+import org.apache.log4j.Logger;
+import org.apache.log4j.bridge.AppenderAdapter;
+import org.apache.log4j.spi.LoggingEvent;
+import org.apache.log4j.xml.XmlConfigurationFactory;
+import org.apache.logging.log4j.core.Appender;
+import org.apache.logging.log4j.core.LoggerContext;
+import org.apache.logging.log4j.core.config.Configuration;
+import org.apache.logging.log4j.core.config.ConfigurationSource;
+import org.apache.logging.log4j.core.config.Configurator;
+import org.apache.logging.log4j.spi.LoggerContextFactory;
+import org.junit.Test;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.InputStream;
+import java.util.List;
+import java.util.Map;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Test configuration from XML.
+ */
+public class AsyncAppenderTest {
+
+    @Test
+    public void testAsyncXml() throws Exception {
+        LoggerContext loggerContext = configure("target/test-classes/log4j1-async.xml");
+        Logger logger = LogManager.getLogger("test");
+        logger.debug("This is a test of the root logger");
+        Thread.sleep(50);
+        Configuration configuration = loggerContext.getConfiguration();
+        Map<String, Appender> appenders = configuration.getAppenders();
+        ListAppender messageAppender = null;
+        for (Map.Entry<String, Appender> entry : appenders.entrySet()) {
+            if (entry.getKey().equals("list")) {
+                messageAppender = (ListAppender) ((AppenderAdapter.Adapter) entry.getValue()).getAppender();
+            }
+        }
+        assertNotNull("No Message Appender", messageAppender);
+        List<String> messages = messageAppender.getMessages();
+        assertTrue("No messages", messages != null && messages.size() > 0);
+    }
+
+    @Test
+    public void testAsyncProperties() throws Exception {
+        LoggerContext loggerContext = configure("target/test-classes/log4j1-async.properties");
+        Logger logger = LogManager.getLogger("test");
+        logger.debug("This is a test of the root logger");
+        Thread.sleep(50);
+        Configuration configuration = loggerContext.getConfiguration();
+        Map<String, Appender> appenders = configuration.getAppenders();
+        ListAppender messageAppender = null;
+        for (Map.Entry<String, Appender> entry : appenders.entrySet()) {
+            if (entry.getKey().equals("list")) {
+                messageAppender = (ListAppender) ((AppenderAdapter.Adapter) entry.getValue()).getAppender();
+            }
+        }
+        assertNotNull("No Message Appender", messageAppender);
+        List<String> messages = messageAppender.getMessages();
+        assertTrue("No messages", messages != null && messages.size() > 0);
+    }
+
+
+    private LoggerContext configure(String configLocation) throws Exception {
+        File file = new File(configLocation);
+        InputStream is = new FileInputStream(file);
+        ConfigurationSource source = new ConfigurationSource(is, file);
+        LoggerContextFactory factory = org.apache.logging.log4j.LogManager.getFactory();
+        LoggerContext context = (LoggerContext) org.apache.logging.log4j.LogManager.getContext(false);
+        Configuration configuration;
+        if (configLocation.endsWith(".xml")) {
+            configuration = new XmlConfigurationFactory().getConfiguration(context, source);
+        } else {
+            configuration = new PropertiesConfigurationFactory().getConfiguration(context, source);
+        }
+        assertNotNull("No configuration created", configuration);
+        Configurator.reconfigure(configuration);
+        return context;
+    }
+
+}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/config/AutoConfigTest.java b/log4j-1.2-api/src/test/java/org/apache/log4j/config/AutoConfigTest.java
index 764f612..5b4412d 100644
--- a/log4j-1.2-api/src/test/java/org/apache/log4j/config/AutoConfigTest.java
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/config/AutoConfigTest.java
@@ -21,10 +21,11 @@
 import org.apache.log4j.Logger;
 import org.apache.log4j.bridge.AppenderAdapter;
 import org.apache.log4j.spi.LoggingEvent;
-import org.apache.log4j.xml.XmlConfigurationFactory;
 import org.apache.logging.log4j.core.Appender;
 import org.apache.logging.log4j.core.config.Configuration;
+import org.apache.logging.log4j.core.config.ConfigurationFactory;
 import org.apache.logging.log4j.spi.LoggerContext;
+import org.junit.BeforeClass;
 import org.junit.Test;
 
 import java.io.File;
@@ -39,6 +40,11 @@
  */
 public class AutoConfigTest {
 
+    @BeforeClass
+    public static void beforeClass() {
+        System.setProperty(ConfigurationFactory.LOG4J1_EXPERIMENTAL, "true");
+    }
+
     @Test
     public void testListAppender() {
         Logger logger = LogManager.getLogger("test");
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/config/PropertiesConfigurationFactoryTest.java b/log4j-1.2-api/src/test/java/org/apache/log4j/config/PropertiesConfigurationFactoryTest.java
new file mode 100644
index 0000000..de81b63
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/config/PropertiesConfigurationFactoryTest.java
@@ -0,0 +1,52 @@
+/*
+ * 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.log4j.config;
+
+import org.apache.log4j.LogManager;
+import org.apache.log4j.Logger;
+import org.apache.logging.log4j.core.config.ConfigurationFactory;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import java.io.File;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Test configuration from Properties.
+ */
+public class PropertiesConfigurationFactoryTest {
+
+    @BeforeClass
+    public static void beforeClass() {
+        System.setProperty(ConfigurationFactory.LOG4J1_CONFIGURATION_FILE_PROPERTY, "target/test-classes/log4j1-file.properties");
+    }
+
+    @Test
+    public void testProperties() throws Exception {
+        Logger logger = LogManager.getLogger("test");
+        logger.debug("This is a test of the root logger");
+        File file = new File("target/temp.A1");
+        assertTrue("File A1 was not created", file.exists());
+        assertTrue("File A1 is empty", file.length() > 0);
+        file = new File("target/temp.A2");
+        assertTrue("File A2 was not created", file.exists());
+        assertTrue("File A2 is empty", file.length() > 0);
+    }
+
+}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/config/PropertiesConfigurationTest.java b/log4j-1.2-api/src/test/java/org/apache/log4j/config/PropertiesConfigurationTest.java
new file mode 100644
index 0000000..72ff293
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/config/PropertiesConfigurationTest.java
@@ -0,0 +1,93 @@
+/*
+ * 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.log4j.config;
+
+import org.apache.log4j.ListAppender;
+import org.apache.log4j.LogManager;
+import org.apache.log4j.Logger;
+import org.apache.log4j.bridge.AppenderAdapter;
+import org.apache.log4j.spi.LoggingEvent;
+import org.apache.logging.log4j.core.Appender;
+import org.apache.logging.log4j.core.LoggerContext;
+import org.apache.logging.log4j.core.config.Configuration;
+import org.apache.logging.log4j.core.config.ConfigurationSource;
+import org.apache.logging.log4j.core.config.Configurator;
+import org.junit.Test;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.InputStream;
+import java.util.List;
+import java.util.Map;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Test configuration from Properties.
+ */
+public class PropertiesConfigurationTest {
+
+    @Test
+    public void testProperties() throws Exception {
+        configure("target/test-classes/log4j1-file.properties");
+        Logger logger = LogManager.getLogger("test");
+        logger.debug("This is a test of the root logger");
+        File file = new File("target/temp.A1");
+        assertTrue("File A1 was not created", file.exists());
+        assertTrue("File A1 is empty", file.length() > 0);
+        file = new File("target/temp.A2");
+        assertTrue("File A2 was not created", file.exists());
+        assertTrue("File A2 is empty", file.length() > 0);
+    }
+
+    @Test
+    public void testListAppender() throws Exception {
+        LoggerContext loggerContext = configure("target/test-classes/log4j1-list.properties");
+        Logger logger = LogManager.getLogger("test");
+        logger.debug("This is a test of the root logger");
+        Configuration configuration = loggerContext.getConfiguration();
+        Map<String, Appender> appenders = configuration.getAppenders();
+        ListAppender eventAppender = null;
+        ListAppender messageAppender = null;
+        for (Map.Entry<String, Appender> entry : appenders.entrySet()) {
+            if (entry.getKey().equals("list")) {
+                messageAppender = (ListAppender) ((AppenderAdapter.Adapter) entry.getValue()).getAppender();
+            } else if (entry.getKey().equals("events")) {
+                eventAppender = (ListAppender) ((AppenderAdapter.Adapter) entry.getValue()).getAppender();
+            }
+        }
+        assertNotNull("No Event Appender", eventAppender);
+        assertNotNull("No Message Appender", messageAppender);
+        List<LoggingEvent> events = eventAppender.getEvents();
+        assertTrue("No events", events != null && events.size() > 0);
+        List<String> messages = messageAppender.getMessages();
+        assertTrue("No messages", messages != null && messages.size() > 0);
+    }
+
+    private LoggerContext configure(String configLocation) throws Exception {
+        File file = new File(configLocation);
+        InputStream is = new FileInputStream(file);
+        ConfigurationSource source = new ConfigurationSource(is, file);
+        LoggerContext context = (LoggerContext) org.apache.logging.log4j.LogManager.getContext(false);
+        Configuration configuration = new PropertiesConfigurationFactory().getConfiguration(context, source);
+        assertNotNull("No configuration created", configuration);
+        Configurator.reconfigure(configuration);
+        return context;
+    }
+
+}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/config/XmlConfigurationFactoryTest.java b/log4j-1.2-api/src/test/java/org/apache/log4j/config/XmlConfigurationFactoryTest.java
index d522a18..65b8d47 100644
--- a/log4j-1.2-api/src/test/java/org/apache/log4j/config/XmlConfigurationFactoryTest.java
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/config/XmlConfigurationFactoryTest.java
@@ -16,23 +16,18 @@
  */
 package org.apache.log4j.config;
 
-import org.apache.log4j.ListAppender;
 import org.apache.log4j.LogManager;
 import org.apache.log4j.Logger;
-import org.apache.log4j.bridge.AppenderAdapter;
-import org.apache.log4j.spi.LoggingEvent;
-import org.apache.log4j.xml.DOMConfigurator;
-import org.apache.log4j.xml.XmlConfigurationFactory;
-import org.apache.logging.log4j.core.Appender;
-import org.apache.logging.log4j.core.config.Configuration;
-import org.apache.logging.log4j.spi.LoggerContext;
+import org.apache.logging.log4j.core.config.ConfigurationFactory;
+import org.junit.BeforeClass;
 import org.junit.Test;
 
 import java.io.File;
+import java.io.FileInputStream;
+import java.io.InputStream;
 import java.util.List;
 import java.util.Map;
 
-import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 
@@ -41,9 +36,12 @@
  */
 public class XmlConfigurationFactoryTest {
 
+    @BeforeClass
+    public static void beforeClass() {
+        System.setProperty(ConfigurationFactory.LOG4J1_CONFIGURATION_FILE_PROPERTY, "target/test-classes/log4j1-file.xml");
+    }
     @Test
     public void testXML() throws Exception {
-        XmlConfigurationFactory.configure("target/test-classes/log4j1-file.xml");
         Logger logger = LogManager.getLogger("test");
         logger.debug("This is a test of the root logger");
         File file = new File("target/temp.A1");
@@ -54,29 +52,4 @@
         assertTrue("File A2 is empty", file.length() > 0);
     }
 
-    @Test
-    public void testListAppender() {
-        XmlConfigurationFactory.configure("target/test-classes/log4j1-list.xml");
-        Logger logger = LogManager.getLogger("test");
-        logger.debug("This is a test of the root logger");
-        LoggerContext loggerContext = org.apache.logging.log4j.LogManager.getContext(false);
-        Configuration configuration = ((org.apache.logging.log4j.core.LoggerContext) loggerContext).getConfiguration();
-        Map<String, Appender> appenders = configuration.getAppenders();
-        ListAppender eventAppender = null;
-        ListAppender messageAppender = null;
-        for (Map.Entry<String, Appender> entry : appenders.entrySet()) {
-            if (entry.getKey().equals("list")) {
-                messageAppender = (ListAppender) ((AppenderAdapter.Adapter) entry.getValue()).getAppender();
-            } else if (entry.getKey().equals("events")) {
-                eventAppender = (ListAppender) ((AppenderAdapter.Adapter) entry.getValue()).getAppender();
-            }
-        }
-        assertNotNull("No Event Appender", eventAppender);
-        assertNotNull("No Message Appender", messageAppender);
-        List<LoggingEvent> events = eventAppender.getEvents();
-        assertTrue("No events", events != null && events.size() > 0);
-        List<String> messages = messageAppender.getMessages();
-        assertTrue("No messages", messages != null && messages.size() > 0);
-    }
-
 }
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/config/XmlConfigurationTest.java b/log4j-1.2-api/src/test/java/org/apache/log4j/config/XmlConfigurationTest.java
new file mode 100644
index 0000000..c4cc360
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/config/XmlConfigurationTest.java
@@ -0,0 +1,97 @@
+/*
+ * 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.log4j.config;
+
+import org.apache.log4j.ListAppender;
+import org.apache.log4j.LogManager;
+import org.apache.log4j.Logger;
+import org.apache.log4j.bridge.AppenderAdapter;
+import org.apache.log4j.spi.LoggingEvent;
+import org.apache.log4j.xml.XmlConfigurationFactory;
+import org.apache.logging.log4j.core.Appender;
+import org.apache.logging.log4j.core.config.Configuration;
+import org.apache.logging.log4j.core.config.ConfigurationSource;
+import org.apache.logging.log4j.core.config.Configurator;
+import org.apache.logging.log4j.core.LoggerContext;
+import org.apache.logging.log4j.spi.LoggerContextFactory;
+import org.junit.Test;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.InputStream;
+import java.util.List;
+import java.util.Map;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Test configuration from XML.
+ */
+public class XmlConfigurationTest {
+
+    @Test
+    public void testXML() throws Exception {
+        configure("target/test-classes/log4j1-file.xml");
+        Logger logger = LogManager.getLogger("test");
+        logger.debug("This is a test of the root logger");
+        File file = new File("target/temp.A1");
+        assertTrue("File A1 was not created", file.exists());
+        assertTrue("File A1 is empty", file.length() > 0);
+        file = new File("target/temp.A2");
+        assertTrue("File A2 was not created", file.exists());
+        assertTrue("File A2 is empty", file.length() > 0);
+    }
+
+    @Test
+    public void testListAppender() throws Exception {
+        LoggerContext loggerContext = configure("target/test-classes/log4j1-list.xml");
+        Logger logger = LogManager.getLogger("test");
+        logger.debug("This is a test of the root logger");
+        Configuration configuration = loggerContext.getConfiguration();
+        Map<String, Appender> appenders = configuration.getAppenders();
+        ListAppender eventAppender = null;
+        ListAppender messageAppender = null;
+        for (Map.Entry<String, Appender> entry : appenders.entrySet()) {
+            if (entry.getKey().equals("list")) {
+                messageAppender = (ListAppender) ((AppenderAdapter.Adapter) entry.getValue()).getAppender();
+            } else if (entry.getKey().equals("events")) {
+                eventAppender = (ListAppender) ((AppenderAdapter.Adapter) entry.getValue()).getAppender();
+            }
+        }
+        assertNotNull("No Event Appender", eventAppender);
+        assertNotNull("No Message Appender", messageAppender);
+        List<LoggingEvent> events = eventAppender.getEvents();
+        assertTrue("No events", events != null && events.size() > 0);
+        List<String> messages = messageAppender.getMessages();
+        assertTrue("No messages", messages != null && messages.size() > 0);
+    }
+
+    private LoggerContext configure(String configLocation) throws Exception {
+        File file = new File(configLocation);
+        InputStream is = new FileInputStream(file);
+        ConfigurationSource source = new ConfigurationSource(is, file);
+        LoggerContextFactory factory = org.apache.logging.log4j.LogManager.getFactory();
+        LoggerContext context = (LoggerContext) org.apache.logging.log4j.LogManager.getContext(false);
+        Configuration configuration = new XmlConfigurationFactory().getConfiguration(context, source);
+        assertNotNull("No configuration created", configuration);
+        Configurator.reconfigure(configuration);
+        return context;
+    }
+
+}
diff --git a/log4j-1.2-api/src/test/resources/log4j1-async.properties b/log4j-1.2-api/src/test/resources/log4j1-async.properties
new file mode 100644
index 0000000..8e80b46
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-async.properties
@@ -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.
+
+log4j.appender.list=org.apache.log4j.ListAppender
+log4j.appender.list.layout=org.apache.log4j.PatternLayout
+log4j.appender.list.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n
+log4j.appender.async=org.apache.log4j.AsyncAppender
+log4j.appender.async.appender-ref=list
+log4j.rootLogger=trace, async
\ No newline at end of file
diff --git a/log4j-1.2-api/src/test/resources/log4j1-async.xml b/log4j-1.2-api/src/test/resources/log4j1-async.xml
new file mode 100644
index 0000000..a0cb7f6
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-async.xml
@@ -0,0 +1,35 @@
+<?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.
+-->
+<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
+
+<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
+  <appender name="list" class="org.apache.log4j.ListAppender">
+    <layout class="org.apache.log4j.PatternLayout">
+      <param name="ConversionPattern" value="%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n" />
+    </layout>
+  </appender>
+  <appender name="async" class="org.apache.log4j.AsyncAppender">
+    <appender-ref ref="list"/>
+  </appender>
+
+  <root>
+    <priority value ="trace" />
+    <appender-ref ref="async" />
+  </root>
+
+</log4j:configuration>
\ No newline at end of file
diff --git a/log4j-1.2-api/src/test/resources/log4j1-file.properties b/log4j-1.2-api/src/test/resources/log4j1-file.properties
new file mode 100644
index 0000000..ee870df
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-file.properties
@@ -0,0 +1,31 @@
+# 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.
+
+log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
+log4j.appender.CONSOLE.Target=System.out
+log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
+log4j.appender.CONSOLE.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n
+log4j.appender.A1=org.apache.log4j.FileAppender
+log4j.appender.A1.File=target/temp.A1
+log4j.appender.A1.Append=false
+log4j.appender.A1.layout=org.apache.log4j.PatternLayout
+log4j.appender.A1.layout.ConversionPattern=%-5p %c{2} - %m%n
+log4j.appender.A2=org.apache.log4j.FileAppender
+log4j.appender.A2.File=target/temp.A2
+log4j.appender.A2.Append=false
+log4j.appender.A2.layout=org.apache.log4j.TTCCLayout
+log4j.appender.A2.layout.DateFormat=ISO8601
+log4j.logger.org.apache.log4j.xml=trace, A1
+log4j.rootLogger=trace, A1, A2
\ No newline at end of file
diff --git a/log4j-1.2-api/src/test/resources/log4j1-list.properties b/log4j-1.2-api/src/test/resources/log4j1-list.properties
new file mode 100644
index 0000000..43d6208
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-list.properties
@@ -0,0 +1,20 @@
+# 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.
+
+log4j.appender.list=org.apache.log4j.ListAppender
+log4j.appender.list.layout=org.apache.log4j.PatternLayout
+log4j.appender.list.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n
+log4j.appender.events=org.apache.log4j.ListAppender
+log4j.rootLogger=trace, list, events
\ No newline at end of file
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 17cbbed..6757f04 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
@@ -45,10 +45,10 @@
 import org.apache.logging.log4j.core.util.NetUtils;
 import org.apache.logging.log4j.plugins.util.PluginManager;
 import org.apache.logging.log4j.plugins.util.PluginType;
-import org.apache.logging.log4j.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.ReflectionUtil;
 import org.apache.logging.log4j.util.Strings;
 
 /**
@@ -91,6 +91,10 @@
      */
     public static final String CONFIGURATION_FILE_PROPERTY = "log4j.configurationFile";
 
+    public static final String LOG4J1_CONFIGURATION_FILE_PROPERTY = "log4j.configuration";
+
+    public static final String LOG4J1_EXPERIMENTAL = "log4j1.compatibility";
+
     public static final String AUTHORIZATION_PROVIDER = "log4j2.authorizationProvider";
 
     /**
@@ -116,6 +120,9 @@
      */
     protected static final String DEFAULT_PREFIX = "log4j2";
 
+    protected static final String LOG4J1_VERSION = "1";
+    protected static final String LOG4J2_VERSION = "2";
+
     /**
      * The name of the classloader URI scheme.
      */
@@ -137,7 +144,7 @@
     private static final String HTTPS = "https";
     private static final String HTTP = "http";
 
-    private static AuthorizationProvider authorizationProvider = null;
+    private static volatile AuthorizationProvider authorizationProvider = null;
 
     /**
      * Returns the ConfigurationFactory.
@@ -175,7 +182,6 @@
                     //noinspection NonThreadSafeLazyInitialization
                     factories = Collections.unmodifiableList(list);
                     authorizationProvider = authorizationProvider(props);
-
                 }
             } finally {
                 LOCK.unlock();
@@ -207,7 +213,6 @@
         return provider;
     }
 
-
     public static AuthorizationProvider getAuthorizationProvider() {
         return authorizationProvider;
     }
@@ -265,6 +270,10 @@
         return DEFAULT_PREFIX;
     }
 
+    protected String getVersion() {
+        return LOG4J2_VERSION;
+    }
+
     protected boolean isActive() {
         return true;
     }
@@ -399,6 +408,13 @@
                         return new CompositeConfiguration(configs);
                     }
                     return getConfiguration(loggerContext, configLocationStr);
+                } else {
+                    final String log4j1ConfigStr = this.substitutor.replace(PropertiesUtil.getProperties()
+                            .getStringProperty(LOG4J1_CONFIGURATION_FILE_PROPERTY));
+                    if (log4j1ConfigStr != null) {
+                        System.setProperty(LOG4J1_EXPERIMENTAL, "true");
+                        return getConfiguration(LOG4J1_VERSION, loggerContext, log4j1ConfigStr);
+                    }
                 }
                 for (final ConfigurationFactory factory : getFactories()) {
                     final String[] types = factory.getSupportedTypes();
@@ -444,7 +460,7 @@
             if (config != null) {
                 return config;
             }
-            LOGGER.error("No Log4j 2 configuration file found. " +
+            LOGGER.warn("No Log4j 2 configuration file found. " +
                     "Using default configuration (logging only errors to the console), " +
                     "or user programmatically provided configurations. " +
                     "Set system property 'log4j2.debug' " +
@@ -454,6 +470,11 @@
         }
 
         private Configuration getConfiguration(final LoggerContext loggerContext, final String configLocationStr) {
+            return getConfiguration(null, loggerContext, configLocationStr);
+        }
+
+        private Configuration getConfiguration(String requiredVersion, final LoggerContext loggerContext,
+                final String configLocationStr) {
             ConfigurationSource source = null;
             try {
                 source = ConfigurationSource.fromUri(NetUtils.toURI(configLocationStr));
@@ -467,6 +488,9 @@
             }
             if (source != null) {
                 for (final ConfigurationFactory factory : getFactories()) {
+                    if (requiredVersion != null && !factory.getVersion().equals(requiredVersion)) {
+                        continue;
+                    }
                     final String[] types = factory.getSupportedTypes();
                     if (types != null) {
                         for (final String type : types) {
diff --git a/log4j-layout-jackson-xml/src/main/java/org/apache/logging/log4j/jackson/xml/builders/layout/XmlLayoutBuilder.java b/log4j-layout-jackson-xml/src/main/java/org/apache/logging/log4j/jackson/xml/builders/layout/XmlLayoutBuilder.java
index 3175ed2..28f0474 100644
--- a/log4j-layout-jackson-xml/src/main/java/org/apache/logging/log4j/jackson/xml/builders/layout/XmlLayoutBuilder.java
+++ b/log4j-layout-jackson-xml/src/main/java/org/apache/logging/log4j/jackson/xml/builders/layout/XmlLayoutBuilder.java
@@ -18,40 +18,68 @@
 
 import org.apache.log4j.Layout;
 import org.apache.log4j.bridge.LayoutWrapper;
+import org.apache.log4j.builders.AbstractBuilder;
 import org.apache.log4j.builders.BooleanHolder;
-import org.apache.log4j.builders.BuilderManager;
 import org.apache.log4j.builders.Holder;
 import org.apache.log4j.builders.layout.LayoutBuilder;
-import org.apache.log4j.xml.XmlConfigurationFactory;
+import org.apache.log4j.config.PropertiesConfiguration;
+import org.apache.log4j.xml.XmlConfiguration;
 import org.apache.logging.log4j.Logger;
 import org.apache.logging.log4j.jackson.xml.layout.XmlLayout;
 import org.apache.logging.log4j.plugins.Plugin;
 import org.apache.logging.log4j.status.StatusLogger;
 import org.w3c.dom.Element;
 
+import java.util.Properties;
+
+import static org.apache.log4j.builders.BuilderManager.CATEGORY;
+import static org.apache.log4j.xml.XmlConfiguration.PARAM_TAG;
+import static org.apache.log4j.xml.XmlConfiguration.forEachElement;
+
 /**
- * Build a Pattern Layout
+ * Build an XML Layout
  */
-@Plugin(name = "org.apache.log4j.xml.XMLLayout", category = BuilderManager.CATEGORY)
-public class XmlLayoutBuilder implements LayoutBuilder {
+@Plugin(name = "org.apache.log4j.xml.XMLLayout", category = CATEGORY)
+public class XmlLayoutBuilder extends AbstractBuilder implements LayoutBuilder {
 
     private static final Logger LOGGER = StatusLogger.getLogger();
 
+    private static final String LOCATION_INFO = "LocationInfo";
+    private static final String PROPERTIES = "Properties";
+
+    public XmlLayoutBuilder() {
+    }
+
+    public XmlLayoutBuilder(String prefix, Properties props) {
+        super(prefix, props);
+    }
+
 
     @Override
-    public Layout parseLayout(Element layoutElement, XmlConfigurationFactory factory) {
+    public Layout parseLayout(Element layoutElement, XmlConfiguration config) {
         final Holder<Boolean> properties = new BooleanHolder();
         final Holder<Boolean> locationInfo = new BooleanHolder();
-        XmlConfigurationFactory.forEachElement(layoutElement.getElementsByTagName("param"), (currentElement) -> {
-            if ("properties".equalsIgnoreCase(currentElement.getAttribute("name"))) {
+        forEachElement(layoutElement.getElementsByTagName(PARAM_TAG), (currentElement) -> {
+            if (PROPERTIES.equalsIgnoreCase(currentElement.getAttribute("name"))) {
                 properties.set(Boolean.parseBoolean(currentElement.getAttribute("value")));
-            } else if ("locationInfo".equalsIgnoreCase(currentElement.getAttribute("name"))) {
+            } else if (LOCATION_INFO.equalsIgnoreCase(currentElement.getAttribute("name"))) {
                 locationInfo.set(Boolean.parseBoolean(currentElement.getAttribute("value")));
             }
         });
+        return createLayout(properties.get(), locationInfo.get());
+    }
+
+    @Override
+    public Layout parseLayout(PropertiesConfiguration config) {
+        boolean properties = getBooleanProperty(PROPERTIES);
+        boolean locationInfo = getBooleanProperty(LOCATION_INFO);
+        return createLayout(properties, locationInfo);
+    }
+
+    private Layout createLayout(boolean properties, boolean locationInfo) {
         return new LayoutWrapper(XmlLayout.newBuilder()
-                .setLocationInfo(locationInfo.get())
-                .setProperties(properties.get())
+                .setLocationInfo(locationInfo)
+                .setProperties(properties)
                 .build());
     }
 }
diff --git a/src/site/asciidoc/index.adoc b/src/site/asciidoc/index.adoc
index ed6a03c..c2ef444 100644
--- a/src/site/asciidoc/index.adoc
+++ b/src/site/asciidoc/index.adoc
@@ -111,8 +111,9 @@
 === Cloud Enabled
 
 Version 2.12.0 introduces support for accessing Docker container information via a Lookup and for accessing
-and updating the Log4j configuration through Spring Cloud Configuration. See [Logging in the Cloud](manual/cloud.html)
-for details.
+and updating the Log4j configuration through Spring Cloud Configuration. This support was enhanced in
+version 2.13.0 to add support for accessing Spring Boot properties as well as Kubernetes information.
+See [Logging in the Cloud](manual/cloud.html) for details.
 
 == Documentation
 
diff --git a/src/site/markdown/manual/cloud.md b/src/site/markdown/manual/cloud.md
index 0dd0d17..69020fd 100644
--- a/src/site/markdown/manual/cloud.md
+++ b/src/site/markdown/manual/cloud.md
@@ -224,6 +224,15 @@
 Further information regarding integration of the log4j-spring-cloud-config-client can be found at 
 [Log4j Spring Cloud Config Client](../log4j-spring-cloud-config/log4j-spring-cloud-config-client/index.html).
 
+## Integration with Spring Boot
+
+Log4j integrates with Spring Boot in 2 ways:
+
+1. A Spring Lookup can be used to access the Spring application configuration from Log4j configuration files.
+2. Log4j will access the Spring configuration when it is trying to resolve log4j system properties.
+
+Both of these require that the log4j-spring-cloud-client jar is included in the application.
+
 ## Integration with Docker
 
 Applications within a Docker container that log using a Docker logging driver can include special 
diff --git a/src/site/markdown/manual/compatibility.md b/src/site/markdown/manual/compatibility.md
new file mode 100644
index 0000000..df0d728
--- /dev/null
+++ b/src/site/markdown/manual/compatibility.md
@@ -0,0 +1,44 @@
+<!-- vim: set syn=markdown : -->
+<!--
+ 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.
+-->
+
+# Log4j 2 Compatibility with Log4j 1
+
+## API Compatibility
+
+Log4j 2 provides support for the Log4j 1 logging methods by providing alternate implementations 
+of the classes containing those methods. These classes may be found in the log4j-1.2-api jar 
+distributed with the project. All calls to perform logging will result in the data passed to the logging methods
+to be forwarded to the Log4j2 API where they can be processed by implementations of the Log4j 2 API. 
+
+## Configuration Compatibility
+
+Log4j 2 provides experimental support for Log4j 1 configuration files. Configuration of the Appenders, Layouts 
+and Filters that were provided in the Log4j 1 distribution will be redirected to their Log4j 2 counterparts. 
+This means that although the while the behavior of these components will be similar they may not be exactly the
+same. For example, the XML generated by the XMLLayout may not exactly match the XML generated by the Log4j 1
+XMLLayout. 
+
+In addition, Log4j 2 supports custom Log4j 1 Appenders, Filters, and Layouts with some constraints. Since the 
+original Log4j 1 components are not present in Log4j 2 custom components that extend them will fail. 
+
+As support for Log4j 1 is an experimental feature one of the following steps must be taken to enable it:
+
+1. Set the system property "log4j1.compatibility" to a value of "true". Log4j 2 will then add log4j.properties,
+log4j-test.properties, log4j.xml and log4j-test.xml to the configuration files it searches for on the class path.
+1. Set the Log4j 1 system property "log4j.configuration" to the location of the log4j 1 configuration file. The 
+files must have a file extension of either ".properties" or ".xml".
\ No newline at end of file
diff --git a/src/site/site.xml b/src/site/site.xml
index 74b047e..bd86e7f 100644
--- a/src/site/site.xml
+++ b/src/site/site.xml
@@ -86,6 +86,7 @@
     <menu name="Manual" inherit="top" img="img/glyphicons/book.png">
       <item name="Introduction" href="/manual/index.html"/>
       <item name="Architecture" href="/manual/architecture.html"/>
+      <item name="Log4j 1.x Compatibility" href="manual/compatibility.html"/>
       <item name="Log4j 1.x Migration" href="manual/migration.html"/>
 
       <item name="Java API" href="/manual/api.html" collapse="true">