You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@logging.apache.org by rg...@apache.org on 2019/06/15 21:01:01 UTC

[logging-log4j2] 01/07: LOG4J2-2621 - Initial commit

This is an automated email from the ASF dual-hosted git repository.

rgoers pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/logging-log4j2.git

commit 8076a70a9e33db48fd68699c42a5262c4cb92fe0
Author: Ralph Goers <rg...@apache.org>
AuthorDate: Sun Jun 2 13:18:36 2019 -0700

    LOG4J2-2621 - Initial commit
---
 .../org/apache/logging/log4j}/util/Assert.java     |   0
 .../logging/log4j/util/InternalException.java      |  56 ++++
 .../org/apache/logging/log4j}/util/NameUtil.java   |   0
 .../apache/logging/log4j}/util/ReflectionUtil.java |   6 +-
 .../log4j/junit/AbstractExternalFileCleaner.java   |   0
 .../org/apache/logging/log4j/junit/CleanFiles.java |   0
 .../apache/logging/log4j/junit/CleanFolders.java   |   0
 .../log4j/junit/URLStreamHandlerFactoryRule.java   |   0
 .../org/apache/logging/log4j}/util/AssertTest.java |   2 +-
 .../config/plugins/convert/Base64Converter.java    |  79 -----
 ...TypeConverters.java => CoreTypeConverters.java} |   7 +-
 .../ValidatingPluginWithGenericBuilderTest.java    |  10 +-
 log4j-plugins-java9/pom.xml                        | 157 +++++++++
 log4j-plugins-java9/src/assembly/java9.xml         |  40 +++
 .../src/main/java/module-info.java                 |  15 +-
 .../org/apache/logging/log4j/plugins/Dummy.java    |   9 +-
 .../logging/log4j/plugins/convert/Dummy.java       |   9 +-
 .../log4j/plugins/processor/PluginService.java     |   9 +-
 .../apache/logging/log4j/plugins/util/Dummy.java   |   9 +-
 .../logging/log4j/plugins/validation/Dummy.java    |   9 +-
 .../logging/log4j/plugins/visitors/Dummy.java      |   9 +-
 log4j-plugins/pom.xml                              | 360 +++++++++++++++++++++
 .../org/apache/logging/log4j/plugins}/Node.java    |   6 +-
 .../org/apache/logging/log4j}/plugins/Plugin.java  |   6 +-
 .../logging/log4j}/plugins/PluginAliases.java      |   4 +-
 .../logging/log4j}/plugins/PluginAttribute.java    |   8 +-
 .../log4j}/plugins/PluginBuilderAttribute.java     |   8 +-
 .../log4j}/plugins/PluginBuilderFactory.java       |   2 +-
 .../logging/log4j}/plugins/PluginElement.java      |   6 +-
 .../logging/log4j}/plugins/PluginFactory.java      |   2 +-
 .../apache/logging/log4j}/plugins/PluginNode.java  |   6 +-
 .../apache/logging/log4j}/plugins/PluginValue.java |   6 +-
 .../log4j}/plugins/PluginVisitorStrategy.java      |   8 +-
 .../log4j}/plugins/convert/EnumConverter.java      |   2 +-
 .../log4j}/plugins/convert/HexConverter.java       |   2 +-
 .../log4j}/plugins/convert/TypeConverter.java      |   2 +-
 .../plugins/convert/TypeConverterRegistry.java     |  23 +-
 .../log4j}/plugins/convert/TypeConverters.java     |  47 +--
 .../log4j/plugins/convert}/package-info.java       |   7 +-
 .../logging/log4j/plugins/osgi/Activator.java      | 103 ++++++
 .../logging/log4j/plugins/osgi}/package-info.java  |   6 +-
 .../logging/log4j/plugins}/package-info.java       |   6 +-
 .../log4j}/plugins/processor/PluginCache.java      |  30 +-
 .../log4j}/plugins/processor/PluginEntry.java      |  14 +-
 .../log4j}/plugins/processor/PluginProcessor.java  | 157 +++++++--
 .../log4j/plugins/processor/PluginService.java     |  56 ++++
 .../log4j}/plugins/processor/package-info.java     |   2 +-
 .../logging/log4j/plugins}/util/Builder.java       |   4 +-
 .../logging/log4j}/plugins/util/PluginManager.java |   8 +-
 .../log4j}/plugins/util/PluginRegistry.java        |  81 +++--
 .../logging/log4j}/plugins/util/PluginType.java    |   6 +-
 .../logging/log4j}/plugins/util/ResolverUtil.java  |  29 +-
 .../logging/log4j/plugins/util/TypeUtil.java       | 217 +++++++++++++
 .../logging/log4j/plugins/util}/package-info.java  |   2 +-
 .../log4j}/plugins/validation/Constraint.java      |   9 +-
 .../plugins/validation/ConstraintValidator.java    |   2 +-
 .../plugins/validation/ConstraintValidators.java   |   8 +-
 .../plugins/validation/constraints/Required.java   |  12 +-
 .../plugins/validation/constraints/ValidHost.java  |   6 +-
 .../plugins/validation/constraints/ValidPort.java  |  12 +-
 .../validation/constraints/package-info.java       |   2 +-
 .../log4j}/plugins/validation/package-info.java    |   2 +-
 .../validation/validators/RequiredValidator.java   |  14 +-
 .../validation/validators/ValidHostValidator.java  |   6 +-
 .../validation/validators/ValidPortValidator.java  |   8 +-
 .../validation/validators/package-info.java        |   2 +-
 .../plugins/visitors/AbstractPluginVisitor.java    |  34 +-
 .../plugins/visitors/PluginAttributeVisitor.java   |  27 +-
 .../visitors/PluginBuilderAttributeVisitor.java    |  23 +-
 .../plugins/visitors/PluginElementVisitor.java     |  17 +-
 .../log4j}/plugins/visitors/PluginNodeVisitor.java |  14 +-
 .../plugins/visitors/PluginValueVisitor.java       |  18 +-
 .../log4j}/plugins/visitors/PluginVisitor.java     |  36 +--
 .../log4j}/plugins/visitors/PluginVisitors.java    |  12 +-
 .../log4j/plugins/visitors}/package-info.java      |   9 +-
 .../services/javax.annotation.processing.Processor |   2 +-
 .../plugins/convert/TypeConverterRegistryTest.java |   4 +-
 .../log4j}/plugins/processor/FakePlugin.java       |   6 +-
 .../plugins/processor/PluginProcessorTest.java     |  26 +-
 .../util/ResolverUtilCustomProtocolTest.java       |  30 +-
 .../log4j}/plugins/util/ResolverUtilTest.java      |  76 +++--
 .../AbstractPluginWithGenericBuilder.java          |   6 +-
 .../log4j}/plugins/validation/HostAndPort.java     |  14 +-
 .../PluginWithGenericSubclassFoo1Builder.java      |  12 +-
 .../plugins/validation/ValidatingPlugin.java       |  16 +-
 .../ValidatingPluginWithGenericBuilder.java        |  17 +-
 .../ValidatingPluginWithTypedBuilder.java          |  16 +-
 .../resources/customplugin/FixedString.java.source |  37 ++-
 .../log4j+config+with+plus+characters.xml          |  31 ++
 .../log4j+config+with+plus+characters.xml          |  31 ++
 90 files changed, 1655 insertions(+), 594 deletions(-)

diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/util/Assert.java b/log4j-api/src/main/java/org/apache/logging/log4j/util/Assert.java
similarity index 100%
rename from log4j-core/src/main/java/org/apache/logging/log4j/core/util/Assert.java
rename to log4j-api/src/main/java/org/apache/logging/log4j/util/Assert.java
diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/util/InternalException.java b/log4j-api/src/main/java/org/apache/logging/log4j/util/InternalException.java
new file mode 100644
index 0000000..8c433db
--- /dev/null
+++ b/log4j-api/src/main/java/org/apache/logging/log4j/util/InternalException.java
@@ -0,0 +1,56 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j;
+
+/**
+ * Exception thrown when an error occurs while logging.  In most cases exceptions will be handled
+ * within Log4j but certain Appenders may be configured to allow exceptions to propagate to the
+ * application. This is a RuntimeException so that the exception may be thrown in those cases without
+ * requiring all Logger methods be contained with try/catch blocks.
+ */
+public class LoggingException extends RuntimeException {
+
+    private static final long serialVersionUID = 6366395965071580537L;
+
+    /**
+     * Construct an exception with a message.
+     *
+     * @param message The reason for the exception
+     */
+    public LoggingException(final String message) {
+        super(message);
+    }
+
+    /**
+     * Construct an exception with a message and underlying cause.
+     *
+     * @param message The reason for the exception
+     * @param cause The underlying cause of the exception
+     */
+    public LoggingException(final String message, final Throwable cause) {
+        super(message, cause);
+    }
+
+    /**
+     * Construct an exception with an underlying cause.
+     *
+     * @param cause The underlying cause of the exception
+     */
+    public LoggingException(final Throwable cause) {
+        super(cause);
+    }
+}
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/util/NameUtil.java b/log4j-api/src/main/java/org/apache/logging/log4j/util/NameUtil.java
similarity index 100%
rename from log4j-core/src/main/java/org/apache/logging/log4j/core/util/NameUtil.java
rename to log4j-api/src/main/java/org/apache/logging/log4j/util/NameUtil.java
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/util/ReflectionUtil.java b/log4j-api/src/main/java/org/apache/logging/log4j/util/ReflectionUtil.java
similarity index 98%
rename from log4j-core/src/main/java/org/apache/logging/log4j/core/util/ReflectionUtil.java
rename to log4j-api/src/main/java/org/apache/logging/log4j/util/ReflectionUtil.java
index ffee439..f00e64a 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/util/ReflectionUtil.java
+++ b/log4j-api/src/main/java/org/apache/logging/log4j/util/ReflectionUtil.java
@@ -193,8 +193,10 @@ public final class ReflectionUtil {
         } catch (final IllegalAccessException e) {
             throw new IllegalStateException(e);
         } catch (final InvocationTargetException e) {
-            Throwables.rethrow(e.getCause());
-            throw new InternalError("Unreachable");
+            if (e.getCause() instanceof RuntimeException) {
+                throw (RuntimeException) e.getCause();
+            }
+            throw new
         }
     }
 }
diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/junit/AbstractExternalFileCleaner.java b/log4j-api/src/test/java/org/apache/logging/log4j/junit/AbstractExternalFileCleaner.java
similarity index 100%
rename from log4j-core/src/test/java/org/apache/logging/log4j/junit/AbstractExternalFileCleaner.java
rename to log4j-api/src/test/java/org/apache/logging/log4j/junit/AbstractExternalFileCleaner.java
diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/junit/CleanFiles.java b/log4j-api/src/test/java/org/apache/logging/log4j/junit/CleanFiles.java
similarity index 100%
rename from log4j-core/src/test/java/org/apache/logging/log4j/junit/CleanFiles.java
rename to log4j-api/src/test/java/org/apache/logging/log4j/junit/CleanFiles.java
diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/junit/CleanFolders.java b/log4j-api/src/test/java/org/apache/logging/log4j/junit/CleanFolders.java
similarity index 100%
rename from log4j-core/src/test/java/org/apache/logging/log4j/junit/CleanFolders.java
rename to log4j-api/src/test/java/org/apache/logging/log4j/junit/CleanFolders.java
diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/junit/URLStreamHandlerFactoryRule.java b/log4j-api/src/test/java/org/apache/logging/log4j/junit/URLStreamHandlerFactoryRule.java
similarity index 100%
rename from log4j-core/src/test/java/org/apache/logging/log4j/junit/URLStreamHandlerFactoryRule.java
rename to log4j-api/src/test/java/org/apache/logging/log4j/junit/URLStreamHandlerFactoryRule.java
diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/core/util/AssertTest.java b/log4j-api/src/test/java/org/apache/logging/log4j/util/AssertTest.java
similarity index 99%
rename from log4j-core/src/test/java/org/apache/logging/log4j/core/util/AssertTest.java
rename to log4j-api/src/test/java/org/apache/logging/log4j/util/AssertTest.java
index 242c41e..7e46036 100644
--- a/log4j-core/src/test/java/org/apache/logging/log4j/core/util/AssertTest.java
+++ b/log4j-api/src/test/java/org/apache/logging/log4j/util/AssertTest.java
@@ -65,4 +65,4 @@ public class AssertTest {
         assertEquals(isEmpty, Assert.isEmpty(value));
     }
 
-}
\ No newline at end of file
+}
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/convert/Base64Converter.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/convert/Base64Converter.java
deleted file mode 100644
index f4e421f..0000000
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/convert/Base64Converter.java
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache license, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the license for the specific language governing permissions and
- * limitations under the license.
- */
-package org.apache.logging.log4j.core.config.plugins.convert;
-
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-
-import org.apache.logging.log4j.Logger;
-import org.apache.logging.log4j.status.StatusLogger;
-import org.apache.logging.log4j.util.LoaderUtil;
-
-/**
- * @Since 2.9
- */
-public class Base64Converter {
-
-    private static final Logger LOGGER = StatusLogger.getLogger();
-    private static Method method = null;
-    private static Object decoder = null;
-
-    static {
-        try {
-            // Base64 is available in Java 8 and up.
-            Class<?> clazz = LoaderUtil.loadClass("java.util.Base64");
-            final Method getDecoder = clazz.getMethod("getDecoder", (Class[]) null);
-            decoder = getDecoder.invoke(null, (Object[]) null);
-            clazz = decoder.getClass();
-            method = clazz.getMethod("decode", String.class);
-        } catch (final ClassNotFoundException ex) {
-
-        } catch (final NoSuchMethodException ex) {
-
-        } catch (final IllegalAccessException ex) {
-
-        } catch (final InvocationTargetException ex) {
-
-        }
-        if (method == null) {
-            try {
-                // DatatypeConverter is not in the default module in Java 9.
-                final Class<?> clazz = LoaderUtil.loadClass("javax.xml.bind.DatatypeConverter");
-                method = clazz.getMethod("parseBase64Binary", String.class);
-            } catch (final ClassNotFoundException ex) {
-                LOGGER.error("No Base64 Converter is available");
-            } catch (final NoSuchMethodException ex) {
-
-            }
-        }
-    }
-
-    public static byte[] parseBase64Binary(final String encoded) {
-        if (method == null) {
-            LOGGER.error("No base64 converter");
-        } else {
-            try {
-                return (byte[]) method.invoke(decoder, encoded);
-            } catch (final IllegalAccessException ex) {
-                LOGGER.error("Error decoding string - " + ex.getMessage());
-            } catch (final InvocationTargetException ex) {
-                LOGGER.error("Error decoding string - " + ex.getMessage());
-            }
-        }
-        return new byte[0];
-    }
-}
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/convert/TypeConverters.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/convert/CoreTypeConverters.java
similarity index 98%
copy from log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/convert/TypeConverters.java
copy to log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/convert/CoreTypeConverters.java
index dc833f0..00e6da7 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/convert/TypeConverters.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/convert/CoreTypeConverters.java
@@ -30,13 +30,14 @@ import java.nio.file.Path;
 import java.nio.file.Paths;
 import java.security.Provider;
 import java.security.Security;
+import java.util.Base64;
 import java.util.UUID;
 import java.util.regex.Pattern;
 
 import org.apache.logging.log4j.Level;
 import org.apache.logging.log4j.Logger;
 import org.apache.logging.log4j.core.appender.rolling.action.Duration;
-import org.apache.logging.log4j.core.config.plugins.Plugin;
+import org.apache.logging.log4j.plugins.Plugin;
 import org.apache.logging.log4j.core.util.CronExpression;
 import org.apache.logging.log4j.status.StatusLogger;
 import org.apache.logging.log4j.util.LoaderUtil;
@@ -56,6 +57,8 @@ public final class TypeConverters {
      */
     public static final String CATEGORY = "TypeConverter";
 
+    private static final Base64.Decoder decoder = Base64.getDecoder();
+
     /**
      * Parses a {@link String} into a {@link BigDecimal}.
      */
@@ -112,7 +115,7 @@ public final class TypeConverters {
                 bytes = new byte[0];
             } else if (value.startsWith(PREFIX_BASE64)) {
                 final String lexicalXSDBase64Binary = value.substring(PREFIX_BASE64.length());
-                bytes = Base64Converter.parseBase64Binary(lexicalXSDBase64Binary);
+                bytes = decoder.decode(lexicalXSDBase64Binary);
             } else if (value.startsWith(PREFIX_0x)) {
                 final String lexicalXSDHexBinary = value.substring(PREFIX_0x.length());
                 bytes = HexConverter.parseHexBinary(lexicalXSDHexBinary);
diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/core/config/plugins/validation/validators/ValidatingPluginWithGenericBuilderTest.java b/log4j-core/src/test/java/org/apache/logging/log4j/core/config/plugins/validation/validators/ValidatingPluginWithGenericBuilderTest.java
index 8ee5abb..27ac5fe 100644
--- a/log4j-core/src/test/java/org/apache/logging/log4j/core/config/plugins/validation/validators/ValidatingPluginWithGenericBuilderTest.java
+++ b/log4j-core/src/test/java/org/apache/logging/log4j/core/config/plugins/validation/validators/ValidatingPluginWithGenericBuilderTest.java
@@ -20,12 +20,12 @@ import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 
-import org.apache.logging.log4j.core.config.Node;
+import org.apache.logging.log4j.plugins.Node;
 import org.apache.logging.log4j.core.config.NullConfiguration;
-import org.apache.logging.log4j.core.config.plugins.util.PluginBuilder;
-import org.apache.logging.log4j.core.config.plugins.util.PluginManager;
-import org.apache.logging.log4j.core.config.plugins.util.PluginType;
-import org.apache.logging.log4j.core.config.plugins.validation.ValidatingPluginWithGenericBuilder;
+import org.apache.logging.log4j.plugins.util.PluginBuilder;
+import org.apache.logging.log4j.plugins.util.PluginManager;
+import org.apache.logging.log4j.plugins.util.PluginType;
+import org.apache.logging.log4j.plugins.validation.ValidatingPluginWithGenericBuilder;
 import org.junit.Before;
 import org.junit.Test;
 
diff --git a/log4j-plugins-java9/pom.xml b/log4j-plugins-java9/pom.xml
new file mode 100644
index 0000000..49f81c6
--- /dev/null
+++ b/log4j-plugins-java9/pom.xml
@@ -0,0 +1,157 @@
+<?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.
+  -->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <groupId>org.apache.logging.log4j</groupId>
+    <artifactId>log4j</artifactId>
+    <version>3.0.0-SNAPSHOT</version>
+    <relativePath>../</relativePath>
+  </parent>
+  <artifactId>log4j-plugins-java9</artifactId>
+  <packaging>pom</packaging>
+  <name>Apache Log4j Plugins Module support</name>
+  <description>Apache Log4j Plugin Moduels Support</description>
+  <properties>
+    <log4jParentDir>${basedir}/..</log4jParentDir>
+    <docLabel>Log4j Plugins Documentation</docLabel>
+    <projectDir>/plugins</projectDir>
+  </properties>
+  <dependencies>
+    <dependency>
+      <groupId>org.apache.logging.log4j</groupId>
+      <artifactId>log4j-api</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>junit</groupId>
+      <artifactId>junit</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.maven</groupId>
+      <artifactId>maven-core</artifactId>
+      <scope>test</scope>
+    </dependency>
+  </dependencies>
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-toolchains-plugin</artifactId>
+        <version>1.1</version>
+        <executions>
+          <execution>
+            <goals>
+              <goal>toolchain</goal>
+            </goals>
+          </execution>
+        </executions>
+        <configuration>
+          <toolchains>
+            <jdk>
+              <version>[9, )</version>
+            </jdk>
+          </toolchains>
+        </configuration>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-compiler-plugin</artifactId>
+        <executions>
+          <execution>
+            <id>default-compile</id>
+            <phase>compile</phase>
+            <goals>
+              <goal>compile</goal>
+            </goals>
+          </execution>
+          <execution>
+            <id>default-test-compile</id>
+            <phase>test-compile</phase>
+            <goals>
+              <goal>testCompile</goal>
+            </goals>
+          </execution>
+        </executions>
+        <configuration>
+          <source>9</source>
+          <target>9</target>
+          <release>9</release>
+          <proc>none</proc>
+          <!-- disable errorprone -->
+          <compilerId>javac</compilerId>
+        </configuration>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-surefire-plugin</artifactId>
+        <!-- Do not upgrade until https://issues.apache.org/jira/browse/SUREFIRE-720 is fixed -->
+        <version>2.13</version>
+        <executions>
+          <execution>
+            <id>test</id>
+            <phase>test</phase>
+            <goals>
+              <goal>test</goal>
+            </goals>
+          </execution>
+        </executions>
+        <configuration>
+          <systemPropertyVariables>
+            <java.awt.headless>true</java.awt.headless>
+          </systemPropertyVariables>
+          <includes>
+            <include>**/Test*.java</include>
+            <include>**/*Test.java</include>
+          </includes>
+          <excludes>
+            <exclude>**/*FuncTest.java</exclude>
+          </excludes>
+        </configuration>
+      </plugin>
+      <plugin>
+        <artifactId>maven-assembly-plugin</artifactId>
+        <executions>
+          <execution>
+            <id>zip</id>
+            <phase>package</phase>
+            <goals>
+              <goal>single</goal>
+            </goals>
+            <configuration>
+              <finalName>log4j-plugins-java9-${project.version}</finalName>
+              <appendAssemblyId>false</appendAssemblyId>
+              <descriptors>
+                <descriptor>src/assembly/java9.xml</descriptor>
+              </descriptors>
+            </configuration>
+          </execution>
+        </executions>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-deploy-plugin</artifactId>
+        <version>${deploy.plugin.version}</version>
+        <configuration>
+          <skip>true</skip>
+        </configuration>
+      </plugin>
+    </plugins>
+  </build>
+</project>
+
diff --git a/log4j-plugins-java9/src/assembly/java9.xml b/log4j-plugins-java9/src/assembly/java9.xml
new file mode 100644
index 0000000..34649d4
--- /dev/null
+++ b/log4j-plugins-java9/src/assembly/java9.xml
@@ -0,0 +1,40 @@
+<?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.
+-->
+
+<assembly>
+  <id>src</id>
+  <formats>
+    <format>zip</format>
+  </formats>
+  <baseDirectory>/</baseDirectory>
+  <fileSets>
+    <fileSet>
+      <directory>${project.build.outputDirectory}</directory>
+      <outputDirectory>/classes/META-INF/versions/9</outputDirectory>
+      <includes>
+        <include>module-info.class</include>
+      </includes>
+      <excludes>
+        <exclude>**/Dummy.class</exclude>
+        <exclude>**/PluginService.class</exclude>
+      </excludes>
+    </fileSet>
+  </fileSets>
+</assembly>
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/validation/constraints/package-info.java b/log4j-plugins-java9/src/main/java/module-info.java
similarity index 65%
copy from log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/validation/constraints/package-info.java
copy to log4j-plugins-java9/src/main/java/module-info.java
index f22ba49..9802622 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/validation/constraints/package-info.java
+++ b/log4j-plugins-java9/src/main/java/module-info.java
@@ -14,10 +14,13 @@
  * See the license for the specific language governing permissions and
  * limitations under the license.
  */
+module org.apache.logging.log4j.plugins {
+    exports org.apache.logging.log4j.plugins;
+    exports org.apache.logging.log4j.plugins.convert;
+    exports org.apache.logging.log4j.plugins.processor;
+    exports org.apache.logging.log4j.plugins.util;
+    exports org.apache.logging.log4j.plugins.validation;
+    exports org.apache.logging.log4j.plugins.visitors;
 
-/**
- * Validation annotations.
- *
- * @since 2.1
- */
-package org.apache.logging.log4j.core.config.plugins.validation.constraints;
+    uses org.apache.logging.log4j.plugins.processor.PluginService;
+}
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/validation/constraints/package-info.java b/log4j-plugins-java9/src/main/java/org/apache/logging/log4j/plugins/Dummy.java
similarity index 80%
copy from log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/validation/constraints/package-info.java
copy to log4j-plugins-java9/src/main/java/org/apache/logging/log4j/plugins/Dummy.java
index f22ba49..14a90ed 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/validation/constraints/package-info.java
+++ b/log4j-plugins-java9/src/main/java/org/apache/logging/log4j/plugins/Dummy.java
@@ -14,10 +14,11 @@
  * See the license for the specific language governing permissions and
  * limitations under the license.
  */
+package org.apache.logging.log4j.plugins;
 
 /**
- * Validation annotations.
- *
- * @since 2.1
+ * This is a dummy class and is only here to allow module-info.java to compile. It will not
+ * be copied into the log4j-api module.
  */
-package org.apache.logging.log4j.core.config.plugins.validation.constraints;
+public class Dummy {
+}
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/validation/constraints/package-info.java b/log4j-plugins-java9/src/main/java/org/apache/logging/log4j/plugins/convert/Dummy.java
similarity index 79%
copy from log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/validation/constraints/package-info.java
copy to log4j-plugins-java9/src/main/java/org/apache/logging/log4j/plugins/convert/Dummy.java
index f22ba49..10923e8 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/validation/constraints/package-info.java
+++ b/log4j-plugins-java9/src/main/java/org/apache/logging/log4j/plugins/convert/Dummy.java
@@ -14,10 +14,11 @@
  * See the license for the specific language governing permissions and
  * limitations under the license.
  */
+package org.apache.logging.log4j.plugins.convert;
 
 /**
- * Validation annotations.
- *
- * @since 2.1
+ * This is a dummy class and is only here to allow module-info.java to compile. It will not
+ * be copied into the log4j-api module.
  */
-package org.apache.logging.log4j.core.config.plugins.validation.constraints;
+public class Dummy {
+}
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/validation/constraints/package-info.java b/log4j-plugins-java9/src/main/java/org/apache/logging/log4j/plugins/processor/PluginService.java
similarity index 79%
copy from log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/validation/constraints/package-info.java
copy to log4j-plugins-java9/src/main/java/org/apache/logging/log4j/plugins/processor/PluginService.java
index f22ba49..b93ef59 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/validation/constraints/package-info.java
+++ b/log4j-plugins-java9/src/main/java/org/apache/logging/log4j/plugins/processor/PluginService.java
@@ -14,10 +14,11 @@
  * See the license for the specific language governing permissions and
  * limitations under the license.
  */
+package org.apache.logging.log4j.plugins.processor;
 
 /**
- * Validation annotations.
- *
- * @since 2.1
+ * This is a dummy class and is only here to allow module-info.java to compile. It will not
+ * be copied into the log4j-api module.
  */
-package org.apache.logging.log4j.core.config.plugins.validation.constraints;
+public class PluginService {
+}
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/validation/constraints/package-info.java b/log4j-plugins-java9/src/main/java/org/apache/logging/log4j/plugins/util/Dummy.java
similarity index 80%
copy from log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/validation/constraints/package-info.java
copy to log4j-plugins-java9/src/main/java/org/apache/logging/log4j/plugins/util/Dummy.java
index f22ba49..5940b03 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/validation/constraints/package-info.java
+++ b/log4j-plugins-java9/src/main/java/org/apache/logging/log4j/plugins/util/Dummy.java
@@ -14,10 +14,11 @@
  * See the license for the specific language governing permissions and
  * limitations under the license.
  */
+package org.apache.logging.log4j.plugins.util;
 
 /**
- * Validation annotations.
- *
- * @since 2.1
+ * This is a dummy class and is only here to allow module-info.java to compile. It will not
+ * be copied into the log4j-api module.
  */
-package org.apache.logging.log4j.core.config.plugins.validation.constraints;
+public class Dummy {
+}
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/validation/constraints/package-info.java b/log4j-plugins-java9/src/main/java/org/apache/logging/log4j/plugins/validation/Dummy.java
similarity index 79%
copy from log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/validation/constraints/package-info.java
copy to log4j-plugins-java9/src/main/java/org/apache/logging/log4j/plugins/validation/Dummy.java
index f22ba49..14882b5 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/validation/constraints/package-info.java
+++ b/log4j-plugins-java9/src/main/java/org/apache/logging/log4j/plugins/validation/Dummy.java
@@ -14,10 +14,11 @@
  * See the license for the specific language governing permissions and
  * limitations under the license.
  */
+package org.apache.logging.log4j.plugins.validation;
 
 /**
- * Validation annotations.
- *
- * @since 2.1
+ * This is a dummy class and is only here to allow module-info.java to compile. It will not
+ * be copied into the log4j-api module.
  */
-package org.apache.logging.log4j.core.config.plugins.validation.constraints;
+public class Dummy {
+}
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/validation/constraints/package-info.java b/log4j-plugins-java9/src/main/java/org/apache/logging/log4j/plugins/visitors/Dummy.java
similarity index 79%
copy from log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/validation/constraints/package-info.java
copy to log4j-plugins-java9/src/main/java/org/apache/logging/log4j/plugins/visitors/Dummy.java
index f22ba49..5596338 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/validation/constraints/package-info.java
+++ b/log4j-plugins-java9/src/main/java/org/apache/logging/log4j/plugins/visitors/Dummy.java
@@ -14,10 +14,11 @@
  * See the license for the specific language governing permissions and
  * limitations under the license.
  */
+package org.apache.logging.log4j.plugins.visitors;
 
 /**
- * Validation annotations.
- *
- * @since 2.1
+ * This is a dummy class and is only here to allow module-info.java to compile. It will not
+ * be copied into the log4j-api module.
  */
-package org.apache.logging.log4j.core.config.plugins.validation.constraints;
+public class Dummy {
+}
diff --git a/log4j-plugins/pom.xml b/log4j-plugins/pom.xml
new file mode 100644
index 0000000..3551d51
--- /dev/null
+++ b/log4j-plugins/pom.xml
@@ -0,0 +1,360 @@
+<?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.
+  -->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <groupId>org.apache.logging.log4j</groupId>
+    <artifactId>log4j</artifactId>
+    <version>3.0.0-SNAPSHOT</version>
+    <relativePath>../</relativePath>
+  </parent>
+  <artifactId>log4j-plugins</artifactId>
+  <packaging>jar</packaging>
+  <name>Apache Log4j Plugins</name>
+  <description>Log4j Plugin Support</description>
+  <properties>
+    <log4jParentDir>${basedir}/..</log4jParentDir>
+    <docLabel>Plugin Documentation</docLabel>
+    <projectDir>/plugins</projectDir>
+  </properties>
+  <dependencies>
+    <dependency>
+      <groupId>org.apache.logging.log4j</groupId>
+      <artifactId>log4j-api</artifactId>
+    </dependency>
+    <!-- Classes and resources to be shaded into the core jar -->
+    <dependency>
+      <groupId>org.apache.logging.log4j</groupId>
+      <artifactId>log4j-plugins-java9</artifactId>
+      <scope>provided</scope>
+      <type>zip</type>
+    </dependency>
+    <!-- Used for OSGi bundle support -->
+    <dependency>
+      <groupId>org.osgi</groupId>
+      <artifactId>org.osgi.core</artifactId>
+      <scope>provided</scope>
+    </dependency>
+
+    <!-- TEST DEPENDENCIES -->
+
+    <!-- Pull in useful test classes from API -->
+    <dependency>
+      <groupId>org.apache.logging.log4j</groupId>
+      <artifactId>log4j-api</artifactId>
+      <type>test-jar</type>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>junit</groupId>
+      <artifactId>junit</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.hamcrest</groupId>
+      <artifactId>hamcrest-all</artifactId>
+      <scope>test</scope>
+    </dependency>
+  </dependencies>
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-dependency-plugin</artifactId>
+        <version>3.0.2</version>
+        <executions>
+          <execution>
+            <id>unpack-classes</id>
+            <phase>prepare-package</phase>
+            <goals>
+              <goal>unpack</goal>
+            </goals>
+            <configuration>
+              <artifactItems>
+                <artifactItem>
+                  <groupId>org.apache.logging.log4j</groupId>
+                  <artifactId>log4j-plugins-java9</artifactId>
+                  <version>${project.version}</version>
+                  <type>zip</type>
+                  <overWrite>false</overWrite>
+                </artifactItem>
+              </artifactItems>
+              <includes>**/*.class</includes>
+              <excludes>**/*.java</excludes>
+              <outputDirectory>${project.build.directory}</outputDirectory>
+              <overWriteReleases>false</overWriteReleases>
+              <overWriteSnapshots>true</overWriteSnapshots>
+            </configuration>
+          </execution>
+        </executions>
+      </plugin>
+      <plugin>
+        <artifactId>maven-compiler-plugin</artifactId>
+        <executions>
+          <execution>
+            <!-- disable annotation processing for first pass -->
+            <id>generate-plugins</id>
+            <phase>process-classes</phase>
+            <goals>
+              <goal>compile</goal>
+            </goals>
+            <configuration>
+              <excludes>
+                <exclude>module-info.java</exclude>
+              </excludes>
+            </configuration>
+          </execution>
+          <execution>
+            <!-- disable annotation processing for first pass -->
+            <id>generate-test-plugins</id>
+            <phase>generate-test-sources</phase>
+            <goals>
+              <goal>compile</goal>
+            </goals>
+            <configuration>
+              <excludes>
+                <exclude>module-info.java</exclude>
+              </excludes>
+              <proc>only</proc>
+            </configuration>
+          </execution>
+          <execution>
+            <!-- disable annotation processing for first pass -->
+            <id>default-compile</id>
+            <configuration>
+              <excludes>
+                <exclude>module-info.java</exclude>
+              </excludes>
+              <proc>none</proc>
+            </configuration>
+          </execution>
+        </executions>
+      </plugin>
+      <plugin>
+        <artifactId>maven-surefire-plugin</artifactId>
+        <configuration>
+          <excludedGroups>
+            org.apache.logging.log4j.categories.PerformanceTests
+          </excludedGroups>
+          <systemPropertyVariables>
+            <org.apache.activemq.SERIALIZABLE_PACKAGES>*</org.apache.activemq.SERIALIZABLE_PACKAGES>
+          </systemPropertyVariables>
+        </configuration>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-failsafe-plugin</artifactId>
+        <configuration>
+          <skipTests>true</skipTests>
+        </configuration>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-jar-plugin</artifactId>
+        <executions>
+          <execution>
+            <id>default-jar</id>
+            <goals>
+              <goal>jar</goal>
+            </goals>
+            <configuration combine.self="override">
+              <archive>
+                <manifestFile>${manifestfile}</manifestFile>
+                <manifestEntries>
+                  <Specification-Title>${project.name}</Specification-Title>
+                  <Specification-Version>${project.version}</Specification-Version>
+                  <Specification-Vendor>${project.organization.name}</Specification-Vendor>
+                  <Implementation-Title>${project.name}</Implementation-Title>
+                  <Implementation-Version>${project.version}</Implementation-Version>
+                  <Implementation-Vendor>${project.organization.name}</Implementation-Vendor>
+                  <Implementation-Vendor-Id>org.apache</Implementation-Vendor-Id>
+                  <X-Compile-Source-JDK>${maven.compiler.source}</X-Compile-Source-JDK>
+                  <X-Compile-Target-JDK>${maven.compiler.target}</X-Compile-Target-JDK>
+                  <Automatic-Module-Name>org.apache.logging.log4j.plugins</Automatic-Module-Name>
+                  <Multi-Release>true</Multi-Release>
+                </manifestEntries>
+              </archive>
+            </configuration>
+          </execution>
+          <execution>
+            <id>default</id>
+            <goals>
+              <goal>test-jar</goal>
+            </goals>
+            <configuration>
+              <archive>
+                <manifestFile>${manifestfile}</manifestFile>
+                <manifestEntries>
+                  <Specification-Title>${project.name}</Specification-Title>
+                  <Specification-Version>${project.version}</Specification-Version>
+                  <Specification-Vendor>${project.organization.name}</Specification-Vendor>
+                  <Implementation-Title>${project.name}</Implementation-Title>
+                  <Implementation-Version>${project.version}</Implementation-Version>
+                  <Implementation-Vendor>${project.organization.name}</Implementation-Vendor>
+                  <Implementation-Vendor-Id>org.apache</Implementation-Vendor-Id>
+                  <X-Compile-Source-JDK>${maven.compiler.source}</X-Compile-Source-JDK>
+                  <X-Compile-Target-JDK>${maven.compiler.target}</X-Compile-Target-JDK>
+                </manifestEntries>
+              </archive>
+            </configuration>
+          </execution>
+        </executions>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.felix</groupId>
+        <artifactId>maven-bundle-plugin</artifactId>
+        <configuration>
+          <instructions>
+            <Bundle-SymbolicName>org.apache.logging.log4j.plugins</Bundle-SymbolicName>
+            <!-- TODO: exclude internal classes from export -->
+            <Export-Package>org.apache.logging.log4j.plugins.*</Export-Package>
+            <Import-Package>
+              sun.reflect;resolution:=optional,
+              org.apache.logging.log4j.util,
+              *
+            </Import-Package>
+            <Bundle-Activator>org.apache.logging.log4j.plugins.osgi.Activator</Bundle-Activator>
+          </instructions>
+        </configuration>
+      </plugin>
+    </plugins>
+  </build>
+  <reporting>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-changes-plugin</artifactId>
+        <version>${changes.plugin.version}</version>
+        <reportSets>
+          <reportSet>
+            <reports>
+              <report>changes-report</report>
+            </reports>
+          </reportSet>
+        </reportSets>
+        <configuration>
+          <issueLinkTemplate>%URL%/show_bug.cgi?id=%ISSUE%</issueLinkTemplate>
+          <useJql>true</useJql>
+        </configuration>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-checkstyle-plugin</artifactId>
+        <version>${checkstyle.plugin.version}</version>
+        <configuration>
+          <!--<propertiesLocation>${vfs.parent.dir}/checkstyle.properties</propertiesLocation> -->
+          <configLocation>${log4jParentDir}/checkstyle.xml</configLocation>
+          <suppressionsLocation>${log4jParentDir}/checkstyle-suppressions.xml</suppressionsLocation>
+          <enableRulesSummary>false</enableRulesSummary>
+          <propertyExpansion>basedir=${basedir}</propertyExpansion>
+          <propertyExpansion>licensedir=${log4jParentDir}/checkstyle-header.txt</propertyExpansion>
+        </configuration>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-javadoc-plugin</artifactId>
+        <version>${javadoc.plugin.version}</version>
+        <configuration>
+          <failOnError>false</failOnError>
+          <bottom><![CDATA[<p align="center">Copyright &#169; {inceptionYear}-{currentYear} {organizationName}. All Rights Reserved.<br />
+            Apache Logging, Apache Log4j, Log4j, Apache, the Apache feather logo, the Apache Logging project logo,
+            and the Apache Log4j logo are trademarks of The Apache Software Foundation.</p>]]></bottom>
+          <!-- module link generation is completely broken in the javadoc plugin for a multi-module non-aggregating
+               project -->
+          <additionalparam>${javadoc.opts}</additionalparam>
+          <detectOfflineLinks>false</detectOfflineLinks>
+          <linksource>true</linksource>
+          <links>
+            <link>http://docs.oracle.com/javaee/6/api/</link>
+            <link>http://www.osgi.org/javadoc/r4v43/core/</link>
+            <link>https://commons.apache.org/proper/commons-lang/javadocs/api-release/</link>
+          </links>
+          <groups>
+            <group>
+              <title>Core API</title>
+              <packages>org.apache.logging.log4j.core</packages>
+            </group>
+            <group>
+              <title>Configuration</title>
+              <packages>org.apache.logging.log4j.core.config*:org.apache.logging.log4j.core.selector</packages>
+            </group>
+            <group>
+              <title>Core Plugins</title>
+              <packages>org.apache.logging.log4j.core.appender*:org.apache.logging.log4j.core.filter:org.apache.logging.log4j.core.layout:org.apache.logging.log4j.core.lookup:org.apache.logging.log4j.core.pattern:org.apache.logging.log4j.core.script</packages>
+            </group>
+            <group>
+              <title>Tools</title>
+              <packages>org.apache.logging.log4j.core.net*:org.apache.logging.log4j.core.tools</packages>
+            </group>
+            <group>
+              <title>Internals</title>
+              <packages>org.apache.logging.log4j.core.async:org.apache.logging.log4j.core.impl:org.apache.logging.log4j.core.util*:org.apache.logging.log4j.core.osgi:org.apache.logging.log4j.core.jackson:org.apache.logging.log4j.core.jmx</packages>
+            </group>
+          </groups>
+        </configuration>
+        <reportSets>
+          <reportSet>
+            <id>non-aggregate</id>
+            <reports>
+              <report>javadoc</report>
+            </reports>
+          </reportSet>
+        </reportSets>
+      </plugin>
+      <plugin>
+        <groupId>com.github.spotbugs</groupId>
+        <artifactId>spotbugs-maven-plugin</artifactId>
+        <configuration>
+          <fork>true</fork>
+          <jvmArgs>-Duser.language=en</jvmArgs>
+          <threshold>Normal</threshold>
+          <effort>Default</effort>
+          <excludeFilterFile>${log4jParentDir}/spotbugs-exclude-filter.xml</excludeFilterFile>
+        </configuration>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-jxr-plugin</artifactId>
+        <version>${jxr.plugin.version}</version>
+        <reportSets>
+          <reportSet>
+            <id>non-aggregate</id>
+            <reports>
+              <report>jxr</report>
+            </reports>
+          </reportSet>
+          <reportSet>
+            <id>aggregate</id>
+            <reports>
+              <report>aggregate</report>
+            </reports>
+          </reportSet>
+        </reportSets>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-pmd-plugin</artifactId>
+        <version>${pmd.plugin.version}</version>
+        <configuration>
+          <targetJdk>${maven.compiler.target}</targetJdk>
+        </configuration>
+      </plugin>
+    </plugins>
+  </reporting>
+</project>
+
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/Node.java b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/Node.java
similarity index 97%
rename from log4j-core/src/main/java/org/apache/logging/log4j/core/config/Node.java
rename to log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/Node.java
index c92c904..22fd9bf 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/Node.java
+++ b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/Node.java
@@ -14,15 +14,15 @@
  * See the license for the specific language governing permissions and
  * limitations under the license.
  */
-package org.apache.logging.log4j.core.config;
+package org.apache.logging.log4j.plugins;
+
+import org.apache.logging.log4j.plugins.util.PluginType;
 
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 
-import org.apache.logging.log4j.core.config.plugins.util.PluginType;
-
 /**
  * A Configuration node.
  */
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/Plugin.java b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/Plugin.java
similarity index 97%
rename from log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/Plugin.java
rename to log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/Plugin.java
index 8aaf117..348754f 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/Plugin.java
+++ b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/Plugin.java
@@ -14,7 +14,9 @@
  * See the license for the specific language governing permissions and
  * limitations under the license.
  */
-package org.apache.logging.log4j.core.config.plugins;
+package org.apache.logging.log4j.plugins;
+
+import org.apache.logging.log4j.util.Strings;
 
 import java.lang.annotation.Documented;
 import java.lang.annotation.ElementType;
@@ -22,8 +24,6 @@ import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.lang.annotation.Target;
 
-import org.apache.logging.log4j.util.Strings;
-
 /**
  * Annotation that identifies a Class as a Plugin.
  */
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/PluginAliases.java b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/PluginAliases.java
similarity index 87%
rename from log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/PluginAliases.java
rename to log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/PluginAliases.java
index 7be3dea..4dce103 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/PluginAliases.java
+++ b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/PluginAliases.java
@@ -14,7 +14,7 @@
  * See the license for the specific language governing permissions and
  * limitations under the license.
  */
-package org.apache.logging.log4j.core.config.plugins;
+package org.apache.logging.log4j.plugins;
 
 import java.lang.annotation.Documented;
 import java.lang.annotation.ElementType;
@@ -23,7 +23,7 @@ import java.lang.annotation.RetentionPolicy;
 import java.lang.annotation.Target;
 
 /**
- * Identifies a list of aliases for a {@link Plugin}, {@link PluginAttribute}, or {@link PluginBuilderAttribute}.
+ * Identifies a list of aliases for a Plugin, PluginAttribute, or PluginBuilderAttribute.
  */
 @Documented
 @Retention(RetentionPolicy.RUNTIME)
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/PluginAttribute.java b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/PluginAttribute.java
similarity index 96%
rename from log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/PluginAttribute.java
rename to log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/PluginAttribute.java
index bd88220..b9d4684 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/PluginAttribute.java
+++ b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/PluginAttribute.java
@@ -14,7 +14,10 @@
  * See the license for the specific language governing permissions and
  * limitations under the license.
  */
-package org.apache.logging.log4j.core.config.plugins;
+package org.apache.logging.log4j.plugins;
+
+import org.apache.logging.log4j.plugins.visitors.PluginAttributeVisitor;
+import org.apache.logging.log4j.util.Strings;
 
 import java.lang.annotation.Documented;
 import java.lang.annotation.ElementType;
@@ -22,9 +25,6 @@ import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.lang.annotation.Target;
 
-import org.apache.logging.log4j.core.config.plugins.visitors.PluginAttributeVisitor;
-import org.apache.logging.log4j.util.Strings;
-
 /**
  * Identifies a Plugin Attribute and its default value. Note that only one of the defaultFoo attributes will be
  * used based on the type this annotation is attached to. Thus, for primitive types, the default<i>Type</i>
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/PluginBuilderAttribute.java b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/PluginBuilderAttribute.java
similarity index 92%
rename from log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/PluginBuilderAttribute.java
rename to log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/PluginBuilderAttribute.java
index 675d78f..c7fce2f 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/PluginBuilderAttribute.java
+++ b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/PluginBuilderAttribute.java
@@ -15,7 +15,10 @@
  * limitations under the license.
  */
 
-package org.apache.logging.log4j.core.config.plugins;
+package org.apache.logging.log4j.plugins;
+
+import org.apache.logging.log4j.plugins.visitors.PluginBuilderAttributeVisitor;
+import org.apache.logging.log4j.util.Strings;
 
 import java.lang.annotation.Documented;
 import java.lang.annotation.ElementType;
@@ -23,9 +26,6 @@ import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.lang.annotation.Target;
 
-import org.apache.logging.log4j.core.config.plugins.visitors.PluginBuilderAttributeVisitor;
-import org.apache.logging.log4j.util.Strings;
-
 /**
  * Marks a field as a Plugin Attribute.
  */
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/PluginBuilderFactory.java b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/PluginBuilderFactory.java
similarity index 95%
rename from log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/PluginBuilderFactory.java
rename to log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/PluginBuilderFactory.java
index 4e69262..035b025 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/PluginBuilderFactory.java
+++ b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/PluginBuilderFactory.java
@@ -15,7 +15,7 @@
  * limitations under the license.
  */
 
-package org.apache.logging.log4j.core.config.plugins;
+package org.apache.logging.log4j.plugins;
 
 import java.lang.annotation.Documented;
 import java.lang.annotation.ElementType;
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/PluginElement.java b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/PluginElement.java
similarity index 91%
rename from log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/PluginElement.java
rename to log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/PluginElement.java
index 7ea358b..6cfb6fa 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/PluginElement.java
+++ b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/PluginElement.java
@@ -14,7 +14,9 @@
  * See the license for the specific language governing permissions and
  * limitations under the license.
  */
-package org.apache.logging.log4j.core.config.plugins;
+package org.apache.logging.log4j.plugins;
+
+import org.apache.logging.log4j.plugins.visitors.PluginElementVisitor;
 
 import java.lang.annotation.Documented;
 import java.lang.annotation.ElementType;
@@ -22,8 +24,6 @@ import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.lang.annotation.Target;
 
-import org.apache.logging.log4j.core.config.plugins.visitors.PluginElementVisitor;
-
 /**
  * Identifies a parameter as a Plugin and corresponds with an XML element (or equivalent) in configuration files.
  */
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/PluginFactory.java b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/PluginFactory.java
similarity index 96%
rename from log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/PluginFactory.java
rename to log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/PluginFactory.java
index 1c04106..b071510 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/PluginFactory.java
+++ b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/PluginFactory.java
@@ -14,7 +14,7 @@
  * See the license for the specific language governing permissions and
  * limitations under the license.
  */
-package org.apache.logging.log4j.core.config.plugins;
+package org.apache.logging.log4j.plugins;
 
 import java.lang.annotation.Documented;
 import java.lang.annotation.ElementType;
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/PluginNode.java b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/PluginNode.java
similarity index 90%
rename from log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/PluginNode.java
rename to log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/PluginNode.java
index d60f1b5..b172268 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/PluginNode.java
+++ b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/PluginNode.java
@@ -14,7 +14,9 @@
  * See the license for the specific language governing permissions and
  * limitations under the license.
  */
-package org.apache.logging.log4j.core.config.plugins;
+package org.apache.logging.log4j.plugins;
+
+import org.apache.logging.log4j.plugins.visitors.PluginNodeVisitor;
 
 import java.lang.annotation.Documented;
 import java.lang.annotation.ElementType;
@@ -22,8 +24,6 @@ import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.lang.annotation.Target;
 
-import org.apache.logging.log4j.core.config.plugins.visitors.PluginNodeVisitor;
-
 /**
  * Identifies a Plugin configuration Node.
  */
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/PluginValue.java b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/PluginValue.java
similarity index 91%
rename from log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/PluginValue.java
rename to log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/PluginValue.java
index 9c20cc2..31e5a23 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/PluginValue.java
+++ b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/PluginValue.java
@@ -14,7 +14,9 @@
  * See the license for the specific language governing permissions and
  * limitations under the license.
  */
-package org.apache.logging.log4j.core.config.plugins;
+package org.apache.logging.log4j.plugins;
+
+import org.apache.logging.log4j.plugins.visitors.PluginValueVisitor;
 
 import java.lang.annotation.Documented;
 import java.lang.annotation.ElementType;
@@ -22,8 +24,6 @@ import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.lang.annotation.Target;
 
-import org.apache.logging.log4j.core.config.plugins.visitors.PluginValueVisitor;
-
 /**
  * Identifies a parameter as a value. These correspond with property values generally, but are meant as values to be
  * used as a placeholder value somewhere.
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/PluginVisitorStrategy.java b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/PluginVisitorStrategy.java
similarity index 89%
rename from log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/PluginVisitorStrategy.java
rename to log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/PluginVisitorStrategy.java
index 65fcb76..093cc50 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/PluginVisitorStrategy.java
+++ b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/PluginVisitorStrategy.java
@@ -15,7 +15,9 @@
  * limitations under the license.
  */
 
-package org.apache.logging.log4j.core.config.plugins;
+package org.apache.logging.log4j.plugins;
+
+import org.apache.logging.log4j.plugins.visitors.PluginVisitor;
 
 import java.lang.annotation.Annotation;
 import java.lang.annotation.Documented;
@@ -24,8 +26,6 @@ import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.lang.annotation.Target;
 
-import org.apache.logging.log4j.core.config.plugins.visitors.PluginVisitor;
-
 /**
  * Meta-annotation to denote the class name to use that implements
  * {@link org.apache.logging.log4j.core.config.plugins.visitors.PluginVisitor} for the annotated annotation.
@@ -40,5 +40,5 @@ public @interface PluginVisitorStrategy {
      * for the given annotation. The generic type in {@code PluginVisitor} should match the annotation this annotation
      * is applied to.
      */
-    Class<? extends PluginVisitor<? extends Annotation>> value();
+    Class<? extends PluginVisitor<? extends Annotation, ?>> value();
 }
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/convert/EnumConverter.java b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/convert/EnumConverter.java
similarity index 95%
copy from log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/convert/EnumConverter.java
copy to log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/convert/EnumConverter.java
index 15a162c..fd8012a 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/convert/EnumConverter.java
+++ b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/convert/EnumConverter.java
@@ -14,7 +14,7 @@
  * See the license for the specific language governing permissions and
  * limitations under the license.
  */
-package org.apache.logging.log4j.core.config.plugins.convert;
+package org.apache.logging.log4j.plugins.convert;
 
 import org.apache.logging.log4j.util.EnglishEnums;
 
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/convert/HexConverter.java b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/convert/HexConverter.java
similarity index 95%
rename from log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/convert/HexConverter.java
rename to log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/convert/HexConverter.java
index e629657..f9037b3 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/convert/HexConverter.java
+++ b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/convert/HexConverter.java
@@ -14,7 +14,7 @@
  * See the license for the specific language governing permissions and
  * limitations under the license.
  */
-package org.apache.logging.log4j.core.config.plugins.convert;
+package org.apache.logging.log4j.plugins.convert;
 
 /**
  * Converts Strings to hex. This is used in place of java.xml.bind.DataTypeConverter which is not available by
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/convert/TypeConverter.java b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/convert/TypeConverter.java
similarity index 95%
rename from log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/convert/TypeConverter.java
rename to log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/convert/TypeConverter.java
index e67e213..aaa5b4d 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/convert/TypeConverter.java
+++ b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/convert/TypeConverter.java
@@ -15,7 +15,7 @@
  * limitations under the license.
  */
 
-package org.apache.logging.log4j.core.config.plugins.convert;
+package org.apache.logging.log4j.plugins.convert;
 
 /**
  * Interface for doing automatic String conversion to a specific type.
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/convert/TypeConverterRegistry.java b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/convert/TypeConverterRegistry.java
similarity index 92%
rename from log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/convert/TypeConverterRegistry.java
rename to log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/convert/TypeConverterRegistry.java
index 5088f15..fb5b27e 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/convert/TypeConverterRegistry.java
+++ b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/convert/TypeConverterRegistry.java
@@ -14,7 +14,14 @@
  * See the license for the specific language governing permissions and
  * limitations under the license.
  */
-package org.apache.logging.log4j.core.config.plugins.convert;
+package org.apache.logging.log4j.plugins.convert;
+
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.plugins.util.PluginManager;
+import org.apache.logging.log4j.plugins.util.PluginType;
+import org.apache.logging.log4j.plugins.util.TypeUtil;
+import org.apache.logging.log4j.util.ReflectionUtil;
+import org.apache.logging.log4j.status.StatusLogger;
 
 import java.lang.reflect.ParameterizedType;
 import java.lang.reflect.Type;
@@ -25,13 +32,6 @@ import java.util.UnknownFormatConversionException;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentMap;
 
-import org.apache.logging.log4j.Logger;
-import org.apache.logging.log4j.core.config.plugins.util.PluginManager;
-import org.apache.logging.log4j.core.config.plugins.util.PluginType;
-import org.apache.logging.log4j.core.util.ReflectionUtil;
-import org.apache.logging.log4j.core.util.TypeUtil;
-import org.apache.logging.log4j.status.StatusLogger;
-
 /**
  * Registry for {@link TypeConverter} plugins.
  *
@@ -152,7 +152,12 @@ public class TypeConverterRegistry {
     }
 
     private void registerTypeAlias(final Type knownType, final Type aliasType) {
-        registry.putIfAbsent(aliasType, registry.get(knownType));
+        TypeConverter<?> converter = registry.get(knownType);
+        if (converter != null) {
+            registry.putIfAbsent(aliasType, converter);
+        } else {
+            LOGGER.error("Cannot locate converter for {}", knownType);
+        }
     }
 
 }
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/convert/TypeConverters.java b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/convert/TypeConverters.java
similarity index 92%
rename from log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/convert/TypeConverters.java
rename to log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/convert/TypeConverters.java
index dc833f0..7a71b4a 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/convert/TypeConverters.java
+++ b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/convert/TypeConverters.java
@@ -15,32 +15,27 @@
  * limitations under the license.
  */
 
-package org.apache.logging.log4j.core.config.plugins.convert;
+package org.apache.logging.log4j.plugins.convert;
+
+import org.apache.logging.log4j.Level;
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.plugins.Plugin;
+import org.apache.logging.log4j.status.StatusLogger;
+import org.apache.logging.log4j.util.LoaderUtil;
 
 import java.io.File;
 import java.math.BigDecimal;
 import java.math.BigInteger;
-import java.net.InetAddress;
-import java.net.MalformedURLException;
-import java.net.URI;
-import java.net.URISyntaxException;
-import java.net.URL;
+import java.net.*;
 import java.nio.charset.Charset;
 import java.nio.file.Path;
 import java.nio.file.Paths;
 import java.security.Provider;
 import java.security.Security;
+import java.util.Base64;
 import java.util.UUID;
 import java.util.regex.Pattern;
 
-import org.apache.logging.log4j.Level;
-import org.apache.logging.log4j.Logger;
-import org.apache.logging.log4j.core.appender.rolling.action.Duration;
-import org.apache.logging.log4j.core.config.plugins.Plugin;
-import org.apache.logging.log4j.core.util.CronExpression;
-import org.apache.logging.log4j.status.StatusLogger;
-import org.apache.logging.log4j.util.LoaderUtil;
-
 /**
  * Collection of basic TypeConverter implementations. May be used to register additional TypeConverters or find
  * registered TypeConverters.
@@ -56,6 +51,8 @@ public final class TypeConverters {
      */
     public static final String CATEGORY = "TypeConverter";
 
+    private static final Base64.Decoder decoder = Base64.getDecoder();
+
     /**
      * Parses a {@link String} into a {@link BigDecimal}.
      */
@@ -112,7 +109,7 @@ public final class TypeConverters {
                 bytes = new byte[0];
             } else if (value.startsWith(PREFIX_BASE64)) {
                 final String lexicalXSDBase64Binary = value.substring(PREFIX_BASE64.length());
-                bytes = Base64Converter.parseBase64Binary(lexicalXSDBase64Binary);
+                bytes = decoder.decode(lexicalXSDBase64Binary);
             } else if (value.startsWith(PREFIX_0x)) {
                 final String lexicalXSDHexBinary = value.substring(PREFIX_0x.length());
                 bytes = HexConverter.parseHexBinary(lexicalXSDHexBinary);
@@ -203,14 +200,6 @@ public final class TypeConverters {
         }
     }
 
-    @Plugin(name = "CronExpression", category = CATEGORY)
-    public static class CronExpressionConverter implements TypeConverter<CronExpression> {
-        @Override
-        public CronExpression convert(final String s) throws Exception {
-            return new CronExpression(s);
-        }
-    }
-
     /**
      * Converts a {@link String} into a {@link Double}.
      */
@@ -223,18 +212,6 @@ public final class TypeConverters {
     }
 
     /**
-     * Converts a {@link String} into a {@link Duration}.
-     * @since 2.5
-     */
-    @Plugin(name = "Duration", category = CATEGORY)
-    public static class DurationConverter implements TypeConverter<Duration> {
-        @Override
-        public Duration convert(final String s) {
-            return Duration.parse(s);
-        }
-    }
-
-    /**
      * Converts a {@link String} into a {@link File}.
      */
     @Plugin(name = "File", category = CATEGORY)
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/validation/constraints/package-info.java b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/convert/package-info.java
similarity index 80%
copy from log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/validation/constraints/package-info.java
copy to log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/convert/package-info.java
index f22ba49..958beb4 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/validation/constraints/package-info.java
+++ b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/convert/package-info.java
@@ -16,8 +16,7 @@
  */
 
 /**
- * Validation annotations.
- *
- * @since 2.1
+ * TypeConverter plugins for converter strings into various types. These plugins are used for parsing plugin
+ * attributes in plugin factory methods.
  */
-package org.apache.logging.log4j.core.config.plugins.validation.constraints;
+package org.apache.logging.log4j.plugins.convert;
diff --git a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/osgi/Activator.java b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/osgi/Activator.java
new file mode 100644
index 0000000..518bee7
--- /dev/null
+++ b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/osgi/Activator.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.logging.log4j.plugins.osgi;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.plugins.processor.PluginService;
+import org.apache.logging.log4j.plugins.util.PluginRegistry;
+import org.apache.logging.log4j.spi.Provider;
+import org.apache.logging.log4j.status.StatusLogger;
+import org.apache.logging.log4j.util.PropertiesUtil;
+import org.osgi.framework.*;
+import org.osgi.framework.wiring.BundleWiring;
+
+import java.util.Hashtable;
+import java.util.concurrent.atomic.AtomicReference;
+
+/**
+ * OSGi BundleActivator.
+ */
+public final class Activator implements BundleActivator, SynchronousBundleListener {
+
+    private static final Logger LOGGER = StatusLogger.getLogger();
+
+    private final AtomicReference<BundleContext> contextRef = new AtomicReference<>();
+
+    ServiceRegistration provideRegistration = null;
+
+    @Override
+    public void start(final BundleContext context) throws Exception {
+        final PluginService pluginService = new Log4jProvider();
+        final Hashtable<String, String> props = new Hashtable<>();
+        props.put("APIVersion", "3.0");
+        provideRegistration = context.registerService(pluginService.class.getName(), provider, props);
+        if (this.contextRef.compareAndSet(null, context)) {
+            context.addBundleListener(this);
+            // done after the BundleListener as to not miss any new bundle installs in the interim
+            scanInstalledBundlesForPlugins(context);
+        }
+    }
+
+    private static void scanInstalledBundlesForPlugins(final BundleContext context) {
+        final Bundle[] bundles = context.getBundles();
+        for (final Bundle bundle : bundles) {
+            // TODO: bundle state can change during this
+            scanBundleForPlugins(bundle);
+        }
+    }
+
+    private static void scanBundleForPlugins(final Bundle bundle) {
+        final long bundleId = bundle.getBundleId();
+        // LOG4J2-920: don't scan system bundle for plugins
+        if (bundle.getState() == Bundle.ACTIVE && bundleId != 0) {
+            LOGGER.trace("Scanning bundle [{}, id=%d] for plugins.", bundle.getSymbolicName(), bundleId);
+            PluginRegistry.getInstance().loadFromBundle(bundleId,
+                    bundle.adapt(BundleWiring.class).getClassLoader());
+        }
+    }
+
+    private static void stopBundlePlugins(final Bundle bundle) {
+        LOGGER.trace("Stopping bundle [{}] plugins.", bundle.getSymbolicName());
+        // TODO: plugin lifecycle code
+        PluginRegistry.getInstance().clearBundlePlugins(bundle.getBundleId());
+    }
+
+    @Override
+    public void stop(final BundleContext context) throws Exception {
+        provideRegistration.unregister();
+        this.contextRef.compareAndSet(context, null);
+    }
+
+    @Override
+    public void bundleChanged(final BundleEvent event) {
+        switch (event.getType()) {
+            // FIXME: STARTING instead of STARTED?
+            case BundleEvent.STARTED:
+                scanBundleForPlugins(event.getBundle());
+                break;
+
+            case BundleEvent.STOPPING:
+                stopBundlePlugins(event.getBundle());
+                break;
+
+            default:
+                break;
+        }
+    }
+}
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/validation/constraints/package-info.java b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/osgi/package-info.java
similarity index 87%
copy from log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/validation/constraints/package-info.java
copy to log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/osgi/package-info.java
index f22ba49..37dadd4 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/validation/constraints/package-info.java
+++ b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/osgi/package-info.java
@@ -16,8 +16,6 @@
  */
 
 /**
- * Validation annotations.
- *
- * @since 2.1
+ * Collection of OSGi-specific classes for bundles.
  */
-package org.apache.logging.log4j.core.config.plugins.validation.constraints;
+package org.apache.logging.log4j.plugins.osgi;
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/validation/constraints/package-info.java b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/package-info.java
similarity index 87%
copy from log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/validation/constraints/package-info.java
copy to log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/package-info.java
index f22ba49..b161185 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/validation/constraints/package-info.java
+++ b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/package-info.java
@@ -16,8 +16,6 @@
  */
 
 /**
- * Validation annotations.
- *
- * @since 2.1
+ * Annotations for Log4j 2 plugins.
  */
-package org.apache.logging.log4j.core.config.plugins.validation.constraints;
+package org.apache.logging.log4j.plugins;
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/processor/PluginCache.java b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/processor/PluginCache.java
similarity index 71%
rename from log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/processor/PluginCache.java
rename to log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/processor/PluginCache.java
index 2fd4160..784dece 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/processor/PluginCache.java
+++ b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/processor/PluginCache.java
@@ -15,7 +15,7 @@
  * limitations under the license.
  */
 
-package org.apache.logging.log4j.core.config.plugins.processor;
+package org.apache.logging.log4j.plugins.processor;
 
 import java.io.BufferedInputStream;
 import java.io.BufferedOutputStream;
@@ -60,34 +60,6 @@ public class PluginCache {
     }
 
     /**
-     * Stores the plugin cache to a given OutputStream.
-     *
-     * @param os destination to save cache to.
-     * @throws IOException if an I/O exception occurs.
-     */
-    // NOTE: if this file format is to be changed, the filename should change and this format should still be readable
-    public void writeCache(final OutputStream os) throws IOException {
-        try (final DataOutputStream out = new DataOutputStream(new BufferedOutputStream(os))) {
-            // See PluginManager.readFromCacheFiles for the corresponding decoder. Format may not be changed
-            // without breaking existing Log4j2Plugins.dat files.
-            out.writeInt(categories.size());
-            for (final Map.Entry<String, Map<String, PluginEntry>> category : categories.entrySet()) {
-                out.writeUTF(category.getKey());
-                final Map<String, PluginEntry> m = category.getValue();
-                out.writeInt(m.size());
-                for (final Map.Entry<String, PluginEntry> entry : m.entrySet()) {
-                    final PluginEntry plugin = entry.getValue();
-                    out.writeUTF(plugin.getKey());
-                    out.writeUTF(plugin.getClassName());
-                    out.writeUTF(plugin.getName());
-                    out.writeBoolean(plugin.isPrintable());
-                    out.writeBoolean(plugin.isDefer());
-                }
-            }
-        }
-    }
-
-    /**
      * Loads and merges all the Log4j plugin cache files specified. Usually, this is obtained via a ClassLoader.
      *
      * @param resources URLs to all the desired plugin cache files to load.
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/processor/PluginEntry.java b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/processor/PluginEntry.java
similarity index 85%
rename from log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/processor/PluginEntry.java
rename to log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/processor/PluginEntry.java
index dd43601..bd452d3 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/processor/PluginEntry.java
+++ b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/processor/PluginEntry.java
@@ -15,7 +15,7 @@
  * limitations under the license.
  */
 
-package org.apache.logging.log4j.core.config.plugins.processor;
+package org.apache.logging.log4j.plugins.processor;
 
 import java.io.Serializable;
 
@@ -32,6 +32,18 @@ public class PluginEntry implements Serializable {
     private boolean defer;
     private transient String category;
 
+    public PluginEntry() {
+    }
+
+    public PluginEntry(String key, String className, String name, boolean printable, boolean defer, String category) {
+        this.key = key;
+        this.className = className;
+        this.name = name;
+        this.printable = printable;
+        this.defer = defer;
+        this.category = category;
+    }
+
     public String getKey() {
         return key;
     }
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/processor/PluginProcessor.java b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/processor/PluginProcessor.java
similarity index 54%
rename from log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/processor/PluginProcessor.java
rename to log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/processor/PluginProcessor.java
index 2f3b53f..975d2ab 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/processor/PluginProcessor.java
+++ b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/processor/PluginProcessor.java
@@ -15,38 +15,47 @@
  * limitations under the license.
  */
 
-package org.apache.logging.log4j.core.config.plugins.processor;
+package org.apache.logging.log4j.plugins.processor;
+
+import org.apache.logging.log4j.LoggingException;
+import org.apache.logging.log4j.plugins.Plugin;
+import org.apache.logging.log4j.plugins.PluginAliases;
+import org.apache.logging.log4j.util.Strings;
 
-import java.io.IOException;
-import java.io.OutputStream;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Locale;
-import java.util.Map;
-import java.util.Objects;
-import java.util.Set;
 import javax.annotation.processing.AbstractProcessor;
+import javax.annotation.processing.Messager;
 import javax.annotation.processing.RoundEnvironment;
 import javax.annotation.processing.SupportedAnnotationTypes;
 import javax.lang.model.SourceVersion;
 import javax.lang.model.element.Element;
 import javax.lang.model.element.ElementVisitor;
+import javax.lang.model.element.Name;
 import javax.lang.model.element.TypeElement;
 import javax.lang.model.util.Elements;
 import javax.lang.model.util.SimpleElementVisitor7;
 import javax.tools.Diagnostic.Kind;
 import javax.tools.FileObject;
+import javax.tools.JavaFileObject;
 import javax.tools.StandardLocation;
+import java.io.BufferedWriter;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
 
-import org.apache.logging.log4j.core.config.plugins.Plugin;
-import org.apache.logging.log4j.core.config.plugins.PluginAliases;
-import org.apache.logging.log4j.util.Strings;
+import static java.nio.charset.StandardCharsets.UTF_8;
 
 /**
  * Annotation processor for pre-scanning Log4j 2 plugins.
  */
-@SupportedAnnotationTypes("org.apache.logging.log4j.core.config.plugins.*")
+@SupportedAnnotationTypes({"org.apache.logging.log4j.plugins.*", "org.apache.logging.log4j.core.config.plugins.*"})
 public class PluginProcessor extends AbstractProcessor {
 
     // TODO: this could be made more abstract to allow for compile-time and run-time plugin processing
@@ -57,8 +66,8 @@ public class PluginProcessor extends AbstractProcessor {
      */
     public static final String PLUGIN_CACHE_FILE =
             "META-INF/org/apache/logging/log4j/core/config/plugins/Log4j2Plugins.dat";
-
-    private final PluginCache pluginCache = new PluginCache();
+    private static final String SERVICE_FILE_NAME =
+            "META-INF/services/org.apache.logging.log4j.plugins.processor.PluginService";
 
     @Override
     public SourceVersion getSupportedSourceVersion() {
@@ -67,16 +76,21 @@ public class PluginProcessor extends AbstractProcessor {
 
     @Override
     public boolean process(final Set<? extends TypeElement> annotations, final RoundEnvironment roundEnv) {
-        System.out.println("Processing annotations");
+        Map<String, String> options = processingEnv.getOptions();
+        String packageName = options.get("pluginPackage");
+        Messager messager = processingEnv.getMessager();
+        messager.printMessage(Kind.NOTE, "Processing Log4j annotations");
         try {
             final Set<? extends Element> elements = roundEnv.getElementsAnnotatedWith(Plugin.class);
             if (elements.isEmpty()) {
-                System.out.println("No elements to process");
+                messager.printMessage(Kind.NOTE, "No elements to process");
                 return false;
             }
-            collectPlugins(elements);
-            writeCacheFile(elements.toArray(new Element[elements.size()]));
-            System.out.println("Annotations processed");
+            List<PluginEntry> list = new ArrayList<>();
+            packageName = collectPlugins(packageName, elements, list);
+            writeClassFile(packageName, list);
+            writeServiceFile(packageName);
+            messager.printMessage(Kind.NOTE, "Annotations processed");
             return true;
         } catch (final IOException e) {
             e.printStackTrace();
@@ -93,7 +107,8 @@ public class PluginProcessor extends AbstractProcessor {
         processingEnv.getMessager().printMessage(Kind.ERROR, message);
     }
 
-    private void collectPlugins(final Iterable<? extends Element> elements) {
+    private String collectPlugins(String packageName, final Iterable<? extends Element> elements, List<PluginEntry> list) {
+        boolean calculatePackage = packageName == null;
         final Elements elementUtils = processingEnv.getElementUtils();
         final ElementVisitor<PluginEntry, Plugin> pluginVisitor = new PluginElementVisitor(elementUtils);
         final ElementVisitor<Collection<PluginEntry>, Plugin> pluginAliasesVisitor = new PluginAliasesElementVisitor(
@@ -104,23 +119,93 @@ public class PluginProcessor extends AbstractProcessor {
                 continue;
             }
             final PluginEntry entry = element.accept(pluginVisitor, plugin);
-            final Map<String, PluginEntry> category = pluginCache.getCategory(entry.getCategory());
-            category.put(entry.getKey(), entry);
+            list.add(entry);
+            if (calculatePackage) {
+                packageName = calculatePackage(elementUtils, element, packageName);
+            }
             final Collection<PluginEntry> entries = element.accept(pluginAliasesVisitor, plugin);
             for (final PluginEntry pluginEntry : entries) {
-                category.put(pluginEntry.getKey(), pluginEntry);
+                list.add(pluginEntry);
             }
         }
+        return packageName;
     }
 
-    private void writeCacheFile(final Element... elements) throws IOException {
+    private String calculatePackage(Elements elements, Element element, String packageName) {
+        Name name = elements.getPackageOf(element).getQualifiedName();
+        if (name == null) {
+            return null;
+        }
+        String pkgName = name.toString();
+        if (packageName == null) {
+            return pkgName;
+        }
+        if (pkgName.length() == packageName.length()) {
+            return packageName;
+        }
+        if (pkgName.length() < packageName.length() && packageName.startsWith(pkgName)) {
+            return pkgName;
+        }
+
+        return commonPrefix(pkgName, packageName);
+    }
+
+    private void writeServiceFile(String pkgName) throws IOException {
         final FileObject fileObject = processingEnv.getFiler().createResource(StandardLocation.CLASS_OUTPUT, Strings.EMPTY,
-                PLUGIN_CACHE_FILE, elements);
-        try (final OutputStream out = fileObject.openOutputStream()) {
-            pluginCache.writeCache(out);
+                SERVICE_FILE_NAME);
+        try (final PrintWriter writer = new PrintWriter(new BufferedWriter(new OutputStreamWriter(fileObject.openOutputStream(), UTF_8)))) {
+            writer.println(createFqcn(pkgName));
+        }
+    }
+
+    private void writeClassFile(String pkg, List<PluginEntry> list) {
+        String fqcn = createFqcn(pkg);
+        try (final PrintWriter writer = createSourceFile(fqcn)) {
+            writer.println("package " + pkg + ".plugins;");
+            writer.println("");
+            writer.println("import org.apache.logging.log4j.plugins.processor.PluginEntry;");
+            writer.println("import org.apache.logging.log4j.plugins.processor.PluginService;");
+            writer.println("");
+            writer.println("public class Log4jPlugins extends PluginService {");
+            writer.println("");
+            writer.println("    private static PluginEntry[] entries = new PluginEntry[] {");
+            StringBuilder sb = new StringBuilder();
+            int max = list.size() - 1;
+            for (int i = 0; i < list.size(); ++i) {
+                PluginEntry entry = list.get(i);
+                sb.append("        ").append("new PluginEntry(\"");
+                sb.append(entry.getKey()).append("\", \"");
+                sb.append(entry.getClassName()).append("\", \"");
+                sb.append(entry.getName()).append("\", ");
+                sb.append(entry.isPrintable()).append(", ");
+                sb.append(entry.isDefer()).append(", \"");
+                sb.append(entry.getCategory()).append("\")");
+                if (i < max) {
+                    sb.append(",");
+                }
+                writer.println(sb.toString());
+                sb.setLength(0);
+            }
+            writer.println("    };");
+            writer.println("    @Override");
+            writer.println("    public PluginEntry[] getEntries() { return entries;}");
+            writer.println("}");
         }
     }
 
+    private PrintWriter createSourceFile(String fqcn) {
+        try {
+            JavaFileObject sourceFile = processingEnv.getFiler().createSourceFile(fqcn);
+            return new PrintWriter(sourceFile.openWriter());
+        } catch (IOException e) {
+            throw new LoggingException("Unable to create Plugin Service Class " + fqcn, e);
+        }
+    }
+
+    private String createFqcn(String packageName) {
+        return packageName + ".plugins.Log4jPlugins";
+    }
+
     /**
      * ElementVisitor to scan the Plugin annotation.
      */
@@ -146,6 +231,20 @@ public class PluginProcessor extends AbstractProcessor {
         }
     }
 
+    private String commonPrefix(String str1, String str2) {
+        int minLength = str1.length() < str2.length() ? str1.length() : str2.length();
+        for (int i = 0; i < minLength; i++) {
+            if (str1.charAt(i) != str2.charAt(i)) {
+                if (i > 1 && str1.charAt(i-1) == '.') {
+                    return str1.substring(0, i-1);
+                } else {
+                    return str1.substring(0, i);
+                }
+            }
+        }
+        return str1.substring(0, minLength);
+    }
+
     /**
      * ElementVisitor to scan the PluginAliases annotation.
      */
diff --git a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/processor/PluginService.java b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/processor/PluginService.java
new file mode 100644
index 0000000..5042456
--- /dev/null
+++ b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/processor/PluginService.java
@@ -0,0 +1,56 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.plugins.processor;
+
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+/**
+ * Class Description goes here.
+ */
+public abstract class PluginService {
+
+    private final Map<String, Map<String, PluginEntry>> categories = new LinkedHashMap<>();
+
+    public PluginService() {
+        PluginEntry[] entries = getEntries();
+        for (PluginEntry entry : entries) {
+            String category = entry.getCategory().toLowerCase();
+            if (!categories.containsKey(category)) {
+                categories.put(category, new LinkedHashMap<>());
+            }
+            Map<String, PluginEntry> map = categories.get(category);
+            map.put(entry.getKey(), entry);
+        }
+    }
+
+    public abstract PluginEntry[] getEntries();
+
+    public Map<String, Map<String, PluginEntry>> getCategories() {
+        return Collections.unmodifiableMap(categories);
+    }
+
+    public Map<String, PluginEntry> getCategory(String category) {
+        return Collections.unmodifiableMap(categories.get(category.toLowerCase()));
+    }
+
+    public long size() {
+        return categories.size();
+    }
+
+}
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/processor/package-info.java b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/processor/package-info.java
similarity index 94%
copy from log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/processor/package-info.java
copy to log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/processor/package-info.java
index 4f6ddda..2c296f9 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/processor/package-info.java
+++ b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/processor/package-info.java
@@ -19,4 +19,4 @@
  * Java annotation processor for pre-scanning Log4j 2 plugins. This is provided as an alternative to using the
  * executable {@link org.apache.logging.log4j.core.config.plugins.util.PluginManager} class in your build process.
  */
-package org.apache.logging.log4j.core.config.plugins.processor;
+package org.apache.logging.log4j.plugins.processor;
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/util/Builder.java b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/util/Builder.java
similarity index 93%
rename from log4j-core/src/main/java/org/apache/logging/log4j/core/util/Builder.java
rename to log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/util/Builder.java
index 0935ce8..ec8a07f 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/util/Builder.java
+++ b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/util/Builder.java
@@ -15,7 +15,7 @@
  * limitations under the license.
  */
 
-package org.apache.logging.log4j.core.util;
+package org.apache.logging.log4j.plugins.util;
 
 /**
  * A type of builder that can be used to configure and create a instances using a Java DSL instead of
@@ -25,7 +25,7 @@ package org.apache.logging.log4j.core.util;
  * <p>
  *     When creating <em>plugin</em> builders, it is customary to create the builder class as a public static inner class
  *     called {@code Builder}. For instance, the builder class for
- *     {@link org.apache.logging.log4j.core.layout.PatternLayout PatternLayout} would be
+ *     org.apache.logging.log4j.core.layout.PatternLayout PatternLayout would be
  *     {@code PatternLayout.Builder}.
  * </p>
  *
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/util/PluginManager.java b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/util/PluginManager.java
similarity index 96%
rename from log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/util/PluginManager.java
rename to log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/util/PluginManager.java
index 6f38d0e..b6f02f5 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/util/PluginManager.java
+++ b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/util/PluginManager.java
@@ -15,17 +15,13 @@
  * limitations under the license.
  */
 
-package org.apache.logging.log4j.core.config.plugins.util;
+package org.apache.logging.log4j.plugins.util;
 
 import org.apache.logging.log4j.Logger;
 import org.apache.logging.log4j.status.StatusLogger;
 import org.apache.logging.log4j.util.Strings;
 
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
+import java.util.*;
 import java.util.concurrent.CopyOnWriteArrayList;
 
 /**
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/util/PluginRegistry.java b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/util/PluginRegistry.java
similarity index 81%
rename from log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/util/PluginRegistry.java
rename to log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/util/PluginRegistry.java
index 99fa610..9e8c6e2 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/util/PluginRegistry.java
+++ b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/util/PluginRegistry.java
@@ -15,32 +15,28 @@
  * limitations under the license.
  */
 
-package org.apache.logging.log4j.core.config.plugins.util;
+package org.apache.logging.log4j.plugins.util;
+
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.plugins.Plugin;
+import org.apache.logging.log4j.plugins.PluginAliases;
+import org.apache.logging.log4j.plugins.processor.PluginCache;
+import org.apache.logging.log4j.plugins.processor.PluginEntry;
+import org.apache.logging.log4j.plugins.processor.PluginProcessor;
+import org.apache.logging.log4j.plugins.processor.PluginService;
+import org.apache.logging.log4j.status.StatusLogger;
+import org.apache.logging.log4j.util.LoaderUtil;
+import org.apache.logging.log4j.util.Strings;
 
 import java.io.IOException;
 import java.net.URI;
 import java.net.URL;
 import java.text.DecimalFormat;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Enumeration;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
+import java.util.*;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentMap;
 import java.util.concurrent.atomic.AtomicReference;
 
-import org.apache.logging.log4j.Logger;
-import org.apache.logging.log4j.core.config.plugins.Plugin;
-import org.apache.logging.log4j.core.config.plugins.PluginAliases;
-import org.apache.logging.log4j.core.config.plugins.processor.PluginCache;
-import org.apache.logging.log4j.core.config.plugins.processor.PluginEntry;
-import org.apache.logging.log4j.core.config.plugins.processor.PluginProcessor;
-import org.apache.logging.log4j.core.util.Loader;
-import org.apache.logging.log4j.status.StatusLogger;
-import org.apache.logging.log4j.util.Strings;
-
 /**
  * Registry singleton for PluginType maps partitioned by source type and then by category names.
  */
@@ -116,7 +112,8 @@ public class PluginRegistry {
             // already loaded
             return existing;
         }
-        final Map<String, List<PluginType<?>>> newPluginsByCategory = decodeCacheFiles(Loader.getClassLoader());
+        final Map<String, List<PluginType<?>>> newPluginsByCategory = decodeCacheFiles(LoaderUtil.getClassLoader());
+        loadPlugins(newPluginsByCategory);
 
         // Note multiple threads could be calling this method concurrently. Both will do the work,
         // but only one will be allowed to store the result in the AtomicReference.
@@ -144,6 +141,7 @@ public class PluginRegistry {
             return existing;
         }
         final Map<String, List<PluginType<?>>> newPluginsByCategory = decodeCacheFiles(loader);
+        loadPlugins(loader, newPluginsByCategory);
 
         // Note multiple threads could be calling this method concurrently. Both will do the work,
         // but only one will be allowed to store the result in the outer map.
@@ -155,6 +153,51 @@ public class PluginRegistry {
         return newPluginsByCategory;
     }
 
+    /**
+     * @since 3.0
+     */
+    public void loadPlugins(Map<String, List<PluginType<?>>> map) {
+        for (ClassLoader classLoader : LoaderUtil.getClassLoaders()) {
+            try {
+                loadPlugins(classLoader, map);
+            } catch (Throwable ex) {
+                LOGGER.debug("Unable to retrieve provider from ClassLoader {}", classLoader, ex);
+            }
+        }
+    }
+
+    /**
+     * @since 3.0
+     */
+    public void loadPlugins(ClassLoader classLoader, Map<String, List<PluginType<?>>> map) {
+        final long startTime = System.nanoTime();
+        final ServiceLoader<PluginService> serviceLoader = ServiceLoader.load(PluginService.class, classLoader);
+        int pluginCount = 0;
+        for (final PluginService pluginService : serviceLoader) {
+            PluginEntry[] entries = pluginService.getEntries();
+            for (PluginEntry entry : entries) {
+                try {
+                    final Class<?> clazz = classLoader.loadClass(entry.getClassName());
+                    final PluginType<?> type = new PluginType(entry, clazz, entry.getName());
+                    String category = entry.getCategory().toLowerCase();
+                    if (!map.containsKey(category)) {
+                        map.put(category, new ArrayList<>());
+                    }
+                    List<PluginType<?>> list = map.get(category);
+                    list.add(type);
+                    ++pluginCount;
+                } catch (final ClassNotFoundException e) {
+                    LOGGER.info("Plugin [{}] could not be loaded due to missing classes.", entry.getClassName(), e);
+                }
+            }
+        }
+        final long endTime = System.nanoTime();
+        final DecimalFormat numFormat = new DecimalFormat("#0.000000");
+        final double seconds = (endTime - startTime) * 1e-9;
+        LOGGER.debug("Took {} seconds to load {} plugins from {}",
+                numFormat.format(seconds), pluginCount, classLoader);
+    }
+
     private Map<String, List<PluginType<?>>> decodeCacheFiles(final ClassLoader loader) {
         final long startTime = System.nanoTime();
         final PluginCache cache = new PluginCache();
@@ -214,7 +257,7 @@ public class PluginRegistry {
 
         final long startTime = System.nanoTime();
         final ResolverUtil resolver = new ResolverUtil();
-        final ClassLoader classLoader = Loader.getClassLoader();
+        final ClassLoader classLoader = LoaderUtil.getClassLoader();
         if (classLoader != null) {
             resolver.setClassLoader(classLoader);
         }
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/util/PluginType.java b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/util/PluginType.java
similarity index 92%
rename from log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/util/PluginType.java
rename to log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/util/PluginType.java
index cc6bc66..c213f64 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/util/PluginType.java
+++ b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/util/PluginType.java
@@ -14,16 +14,16 @@
  * See the license for the specific language governing permissions and
  * limitations under the license.
  */
-package org.apache.logging.log4j.core.config.plugins.util;
+package org.apache.logging.log4j.plugins.util;
 
 
-import org.apache.logging.log4j.core.config.plugins.processor.PluginEntry;
+import org.apache.logging.log4j.plugins.processor.PluginEntry;
 
 /**
  * Plugin Descriptor. This is a memento object for Plugin annotations paired to their annotated classes.
  *
  * @param <T> The plug-in class, which can be any kind of class.
- * @see org.apache.logging.log4j.core.config.plugins.Plugin
+ * @see org.apache.logging.log4j.plugins.Plugin
  */
 public class PluginType<T> {
 
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/util/ResolverUtil.java b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/util/ResolverUtil.java
similarity index 97%
rename from log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/util/ResolverUtil.java
rename to log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/util/ResolverUtil.java
index 73b5fc0..a57356c 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/util/ResolverUtil.java
+++ b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/util/ResolverUtil.java
@@ -14,33 +14,24 @@
  * See the license for the specific language governing permissions and
  * limitations under the license.
  */
-package org.apache.logging.log4j.core.config.plugins.util;
+package org.apache.logging.log4j.plugins.util;
 
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.UnsupportedEncodingException;
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.util.LoaderUtil;
+import org.apache.logging.log4j.status.StatusLogger;
+import org.osgi.framework.FrameworkUtil;
+import org.osgi.framework.wiring.BundleWiring;
+
+import java.io.*;
 import java.net.URI;
 import java.net.URISyntaxException;
 import java.net.URL;
 import java.net.URLDecoder;
 import java.nio.charset.StandardCharsets;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Enumeration;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
+import java.util.*;
 import java.util.jar.JarEntry;
 import java.util.jar.JarInputStream;
 
-import org.apache.logging.log4j.Logger;
-import org.apache.logging.log4j.core.util.Loader;
-import org.apache.logging.log4j.status.StatusLogger;
-import org.osgi.framework.FrameworkUtil;
-import org.osgi.framework.wiring.BundleWiring;
-
 /**
  * <p>
  * ResolverUtil is used to locate classes that are available in the/a class path and meet arbitrary conditions. The two
@@ -126,7 +117,7 @@ public class ResolverUtil {
      * @return the ClassLoader that will be used to scan for classes
      */
     public ClassLoader getClassLoader() {
-        return classloader != null ? classloader : (classloader = Loader.getClassLoader(ResolverUtil.class, null));
+        return classloader != null ? classloader : (classloader = LoaderUtil.getClassLoader(ResolverUtil.class, null));
     }
 
     /**
diff --git a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/util/TypeUtil.java b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/util/TypeUtil.java
new file mode 100644
index 0000000..e2bb462
--- /dev/null
+++ b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/util/TypeUtil.java
@@ -0,0 +1,217 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.plugins.util;
+
+import java.lang.reflect.*;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Utility class for working with Java {@link Type}s and derivatives. This class is adapted heavily from the
+ * <a href="http://projects.spring.io/spring-framework/">Spring Framework</a>, specifically the
+ * <a href="http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/util/TypeUtils.html">TypeUtils</a>
+ * class.
+ *
+ * @see Type
+ * @see GenericArrayType
+ * @see ParameterizedType
+ * @see WildcardType
+ * @see Class
+ * @since 2.1
+ */
+public final class TypeUtil {
+    
+    private TypeUtil() {
+    }
+
+    /**
+     * Gets all declared fields for the given class (including superclasses).
+     * 
+     * @param cls the class to examine
+     * @return all declared fields for the given class (including superclasses).
+     * @see Class#getDeclaredFields()
+     */
+    public static List<Field> getAllDeclaredFields(Class<?> cls) {
+        final List<Field> fields = new ArrayList<>();
+        while (cls != null) {
+            for (final Field field : cls.getDeclaredFields()) {
+                fields.add(field);
+            }
+            cls = cls.getSuperclass();
+        }
+        return fields;
+    }
+    /**
+     * Indicates if two {@link Type}s are assignment compatible.
+     *
+     * @param lhs the left hand side to check assignability to
+     * @param rhs the right hand side to check assignability from
+     * @return {@code true} if it is legal to assign a variable of type {@code rhs} to a variable of type {@code lhs}
+     * @see Class#isAssignableFrom(Class)
+     */
+    public static boolean isAssignable(final Type lhs, final Type rhs) {
+        Objects.requireNonNull(lhs, "No left hand side type provided");
+        Objects.requireNonNull(rhs, "No right hand side type provided");
+        if (lhs.equals(rhs)) {
+            return true;
+        }
+        if (Object.class.equals(lhs)) {
+            // everything is assignable to Object
+            return true;
+        }
+        // raw type on left
+        if (lhs instanceof Class<?>) {
+            final Class<?> lhsClass = (Class<?>) lhs;
+            if (rhs instanceof Class<?>) {
+                // no generics involved
+                final Class<?> rhsClass = (Class<?>) rhs;
+                return lhsClass.isAssignableFrom(rhsClass);
+            }
+            if (rhs instanceof ParameterizedType) {
+                // check to see if the parameterized type has the same raw type as the lhs; this is legal
+                final Type rhsRawType = ((ParameterizedType) rhs).getRawType();
+                if (rhsRawType instanceof Class<?>) {
+                    return lhsClass.isAssignableFrom((Class<?>) rhsRawType);
+                }
+            }
+            if (lhsClass.isArray() && rhs instanceof GenericArrayType) {
+                // check for compatible array component types
+                return isAssignable(lhsClass.getComponentType(), ((GenericArrayType) rhs).getGenericComponentType());
+            }
+        }
+        // parameterized type on left
+        if (lhs instanceof ParameterizedType) {
+            final ParameterizedType lhsType = (ParameterizedType) lhs;
+            if (rhs instanceof Class<?>) {
+                final Type lhsRawType = lhsType.getRawType();
+                if (lhsRawType instanceof Class<?>) {
+                    return ((Class<?>) lhsRawType).isAssignableFrom((Class<?>) rhs);
+                }
+            } else if (rhs instanceof ParameterizedType) {
+                final ParameterizedType rhsType = (ParameterizedType) rhs;
+                return isParameterizedAssignable(lhsType, rhsType);
+            }
+        }
+        // generic array type on left
+        if (lhs instanceof GenericArrayType) {
+            final Type lhsComponentType = ((GenericArrayType) lhs).getGenericComponentType();
+            if (rhs instanceof Class<?>) {
+                // raw type on right
+                final Class<?> rhsClass = (Class<?>) rhs;
+                if (rhsClass.isArray()) {
+                    return isAssignable(lhsComponentType, rhsClass.getComponentType());
+                }
+            } else if (rhs instanceof GenericArrayType) {
+                return isAssignable(lhsComponentType, ((GenericArrayType) rhs).getGenericComponentType());
+            }
+        }
+        // wildcard type on left
+        if (lhs instanceof WildcardType) {
+            return isWildcardAssignable((WildcardType) lhs, rhs);
+        }
+        // strange...
+        return false;
+    }
+
+    private static boolean isParameterizedAssignable(final ParameterizedType lhs, final ParameterizedType rhs) {
+        if (lhs.equals(rhs)) {
+            // that was easy
+            return true;
+        }
+        final Type[] lhsTypeArguments = lhs.getActualTypeArguments();
+        final Type[] rhsTypeArguments = rhs.getActualTypeArguments();
+        final int size = lhsTypeArguments.length;
+        if (rhsTypeArguments.length != size) {
+            // clearly incompatible types
+            return false;
+        }
+        for (int i = 0; i < size; i++) {
+            // verify all type arguments are assignable
+            final Type lhsArgument = lhsTypeArguments[i];
+            final Type rhsArgument = rhsTypeArguments[i];
+            if (!lhsArgument.equals(rhsArgument) &&
+                !(lhsArgument instanceof WildcardType &&
+                    isWildcardAssignable((WildcardType) lhsArgument, rhsArgument))) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    private static boolean isWildcardAssignable(final WildcardType lhs, final Type rhs) {
+        final Type[] lhsUpperBounds = getEffectiveUpperBounds(lhs);
+        final Type[] lhsLowerBounds = getEffectiveLowerBounds(lhs);
+        if (rhs instanceof WildcardType) {
+            // oh boy, this scenario requires checking a lot of assignability!
+            final WildcardType rhsType = (WildcardType) rhs;
+            final Type[] rhsUpperBounds = getEffectiveUpperBounds(rhsType);
+            final Type[] rhsLowerBounds = getEffectiveLowerBounds(rhsType);
+            for (final Type lhsUpperBound : lhsUpperBounds) {
+                for (final Type rhsUpperBound : rhsUpperBounds) {
+                    if (!isBoundAssignable(lhsUpperBound, rhsUpperBound)) {
+                        return false;
+                    }
+                }
+                for (final Type rhsLowerBound : rhsLowerBounds) {
+                    if (!isBoundAssignable(lhsUpperBound, rhsLowerBound)) {
+                        return false;
+                    }
+                }
+            }
+            for (final Type lhsLowerBound : lhsLowerBounds) {
+                for (final Type rhsUpperBound : rhsUpperBounds) {
+                    if (!isBoundAssignable(rhsUpperBound, lhsLowerBound)) {
+                        return false;
+                    }
+                }
+                for (final Type rhsLowerBound : rhsLowerBounds) {
+                    if (!isBoundAssignable(rhsLowerBound, lhsLowerBound)) {
+                        return false;
+                    }
+                }
+            }
+        } else {
+            // phew, far less bounds to check
+            for (final Type lhsUpperBound : lhsUpperBounds) {
+                if (!isBoundAssignable(lhsUpperBound, rhs)) {
+                    return false;
+                }
+            }
+            for (final Type lhsLowerBound : lhsLowerBounds) {
+                if (!isBoundAssignable(lhsLowerBound, rhs)) {
+                    return false;
+                }
+            }
+        }
+        return true;
+    }
+
+    private static Type[] getEffectiveUpperBounds(final WildcardType type) {
+        final Type[] upperBounds = type.getUpperBounds();
+        return upperBounds.length == 0 ? new Type[]{Object.class} : upperBounds;
+    }
+
+    private static Type[] getEffectiveLowerBounds(final WildcardType type) {
+        final Type[] lowerBounds = type.getLowerBounds();
+        return lowerBounds.length == 0 ? new Type[]{null} : lowerBounds;
+    }
+
+    private static boolean isBoundAssignable(final Type lhs, final Type rhs) {
+        return (rhs == null) || ((lhs != null) && isAssignable(lhs, rhs));
+    }
+}
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/processor/package-info.java b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/util/package-info.java
similarity index 94%
rename from log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/processor/package-info.java
rename to log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/util/package-info.java
index 4f6ddda..fae7580 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/processor/package-info.java
+++ b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/util/package-info.java
@@ -19,4 +19,4 @@
  * Java annotation processor for pre-scanning Log4j 2 plugins. This is provided as an alternative to using the
  * executable {@link org.apache.logging.log4j.core.config.plugins.util.PluginManager} class in your build process.
  */
-package org.apache.logging.log4j.core.config.plugins.processor;
+package org.apache.logging.log4j.plugins.util;
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/validation/Constraint.java b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/validation/Constraint.java
similarity index 81%
rename from log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/validation/Constraint.java
rename to log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/validation/Constraint.java
index 0ac2223..4586315 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/validation/Constraint.java
+++ b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/validation/Constraint.java
@@ -14,14 +14,9 @@
  * See the license for the specific language governing permissions and
  * limitations under the license.
  */
-package org.apache.logging.log4j.core.config.plugins.validation;
+package org.apache.logging.log4j.plugins.validation;
 
-import java.lang.annotation.Annotation;
-import java.lang.annotation.Documented;
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
+import java.lang.annotation.*;
 
 /**
  * Meta annotation to mark an annotation as a validation constraint. This annotation must specify a
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/validation/ConstraintValidator.java b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/validation/ConstraintValidator.java
similarity index 96%
rename from log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/validation/ConstraintValidator.java
rename to log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/validation/ConstraintValidator.java
index 1d8c0c5..2f638a7 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/validation/ConstraintValidator.java
+++ b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/validation/ConstraintValidator.java
@@ -14,7 +14,7 @@
  * See the license for the specific language governing permissions and
  * limitations under the license.
  */
-package org.apache.logging.log4j.core.config.plugins.validation;
+package org.apache.logging.log4j.plugins.validation;
 
 import java.lang.annotation.Annotation;
 
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/validation/ConstraintValidators.java b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/validation/ConstraintValidators.java
similarity index 93%
rename from log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/validation/ConstraintValidators.java
rename to log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/validation/ConstraintValidators.java
index 374c8ec..6236bb6 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/validation/ConstraintValidators.java
+++ b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/validation/ConstraintValidators.java
@@ -14,7 +14,9 @@
  * See the license for the specific language governing permissions and
  * limitations under the license.
  */
-package org.apache.logging.log4j.core.config.plugins.validation;
+package org.apache.logging.log4j.plugins.validation;
+
+import org.apache.logging.log4j.util.ReflectionUtil;
 
 import java.lang.annotation.Annotation;
 import java.lang.reflect.ParameterizedType;
@@ -22,8 +24,6 @@ import java.lang.reflect.Type;
 import java.util.ArrayList;
 import java.util.Collection;
 
-import org.apache.logging.log4j.core.util.ReflectionUtil;
-
 /**
  * Utility class to locate an appropriate {@link ConstraintValidator} implementation for an annotation.
  *
@@ -36,7 +36,7 @@ public final class ConstraintValidators {
 
     /**
      * Finds all relevant {@link ConstraintValidator} objects from an array of annotations. All validators will be
-     * {@link ConstraintValidator#initialize(java.lang.annotation.Annotation) initialized} before being returned.
+     * {@link ConstraintValidator#initialize(Annotation) initialized} before being returned.
      *
      * @param annotations the annotations to find constraint validators for
      * @return a collection of ConstraintValidators for the given annotations
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/validation/constraints/Required.java b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/validation/constraints/Required.java
similarity index 73%
rename from log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/validation/constraints/Required.java
rename to log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/validation/constraints/Required.java
index e6f3c56..9b8a75d 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/validation/constraints/Required.java
+++ b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/validation/constraints/Required.java
@@ -15,16 +15,12 @@
  * limitations under the license.
  */
 
-package org.apache.logging.log4j.core.config.plugins.validation.constraints;
+package org.apache.logging.log4j.plugins.validation.constraints;
 
-import java.lang.annotation.Documented;
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
+import org.apache.logging.log4j.plugins.validation.Constraint;
+import org.apache.logging.log4j.plugins.validation.validators.RequiredValidator;
 
-import org.apache.logging.log4j.core.config.plugins.validation.Constraint;
-import org.apache.logging.log4j.core.config.plugins.validation.validators.RequiredValidator;
+import java.lang.annotation.*;
 
 /**
  * Marks a plugin builder field or plugin factory parameter as required.
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/validation/constraints/ValidHost.java b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/validation/constraints/ValidHost.java
similarity index 84%
rename from log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/validation/constraints/ValidHost.java
rename to log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/validation/constraints/ValidHost.java
index c652d40..14dd9a8 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/validation/constraints/ValidHost.java
+++ b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/validation/constraints/ValidHost.java
@@ -14,10 +14,10 @@
  * See the license for the specific language governing permissions and
  * limitations under the license.
  */
-package org.apache.logging.log4j.core.config.plugins.validation.constraints;
+package org.apache.logging.log4j.plugins.validation.constraints;
 
-import org.apache.logging.log4j.core.config.plugins.validation.Constraint;
-import org.apache.logging.log4j.core.config.plugins.validation.validators.ValidHostValidator;
+import org.apache.logging.log4j.plugins.validation.Constraint;
+import org.apache.logging.log4j.plugins.validation.validators.ValidHostValidator;
 
 import java.lang.annotation.*;
 import java.net.InetAddress;
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/validation/constraints/ValidPort.java b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/validation/constraints/ValidPort.java
similarity index 74%
rename from log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/validation/constraints/ValidPort.java
rename to log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/validation/constraints/ValidPort.java
index a7c68b1..c4aba16 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/validation/constraints/ValidPort.java
+++ b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/validation/constraints/ValidPort.java
@@ -14,16 +14,12 @@
  * See the license for the specific language governing permissions and
  * limitations under the license.
  */
-package org.apache.logging.log4j.core.config.plugins.validation.constraints;
+package org.apache.logging.log4j.plugins.validation.constraints;
 
-import java.lang.annotation.Documented;
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
+import org.apache.logging.log4j.plugins.validation.Constraint;
+import org.apache.logging.log4j.plugins.validation.validators.ValidPortValidator;
 
-import org.apache.logging.log4j.core.config.plugins.validation.Constraint;
-import org.apache.logging.log4j.core.config.plugins.validation.validators.ValidPortValidator;
+import java.lang.annotation.*;
 
 /**
  * Indicates that a plugin attribute must be a valid port number. A valid port number is an integer between 0 and
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/validation/constraints/package-info.java b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/validation/constraints/package-info.java
similarity index 91%
copy from log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/validation/constraints/package-info.java
copy to log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/validation/constraints/package-info.java
index f22ba49..298cd5a 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/validation/constraints/package-info.java
+++ b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/validation/constraints/package-info.java
@@ -20,4 +20,4 @@
  *
  * @since 2.1
  */
-package org.apache.logging.log4j.core.config.plugins.validation.constraints;
+package org.apache.logging.log4j.plugins.validation.constraints;
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/validation/package-info.java b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/validation/package-info.java
similarity index 93%
rename from log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/validation/package-info.java
rename to log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/validation/package-info.java
index 171b25a..15955cb 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/validation/package-info.java
+++ b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/validation/package-info.java
@@ -20,4 +20,4 @@
  *
  * @since 2.1
  */
-package org.apache.logging.log4j.core.config.plugins.validation;
+package org.apache.logging.log4j.plugins.validation;
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/validation/validators/RequiredValidator.java b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/validation/validators/RequiredValidator.java
similarity index 86%
rename from log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/validation/validators/RequiredValidator.java
rename to log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/validation/validators/RequiredValidator.java
index 98c0a71..9df6d3b 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/validation/validators/RequiredValidator.java
+++ b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/validation/validators/RequiredValidator.java
@@ -14,17 +14,17 @@
  * See the license for the specific language governing permissions and
  * limitations under the license.
  */
-package org.apache.logging.log4j.core.config.plugins.validation.validators;
-
-import java.util.Collection;
-import java.util.Map;
+package org.apache.logging.log4j.plugins.validation.validators;
 
 import org.apache.logging.log4j.Logger;
-import org.apache.logging.log4j.core.config.plugins.validation.ConstraintValidator;
-import org.apache.logging.log4j.core.config.plugins.validation.constraints.Required;
-import org.apache.logging.log4j.core.util.Assert;
+import org.apache.logging.log4j.plugins.validation.ConstraintValidator;
+import org.apache.logging.log4j.plugins.validation.constraints.Required;
+import org.apache.logging.log4j.util.Assert;
 import org.apache.logging.log4j.status.StatusLogger;
 
+import java.util.Collection;
+import java.util.Map;
+
 /**
  * Validator that checks an object for emptiness. Emptiness is defined here as:
  * <ul>
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/validation/validators/ValidHostValidator.java b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/validation/validators/ValidHostValidator.java
similarity index 89%
rename from log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/validation/validators/ValidHostValidator.java
rename to log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/validation/validators/ValidHostValidator.java
index 6c01753..41abbfd 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/validation/validators/ValidHostValidator.java
+++ b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/validation/validators/ValidHostValidator.java
@@ -14,11 +14,11 @@
  * See the license for the specific language governing permissions and
  * limitations under the license.
  */
-package org.apache.logging.log4j.core.config.plugins.validation.validators;
+package org.apache.logging.log4j.plugins.validation.validators;
 
 import org.apache.logging.log4j.Logger;
-import org.apache.logging.log4j.core.config.plugins.validation.ConstraintValidator;
-import org.apache.logging.log4j.core.config.plugins.validation.constraints.ValidHost;
+import org.apache.logging.log4j.plugins.validation.ConstraintValidator;
+import org.apache.logging.log4j.plugins.validation.constraints.ValidHost;
 import org.apache.logging.log4j.status.StatusLogger;
 
 import java.net.InetAddress;
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/validation/validators/ValidPortValidator.java b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/validation/validators/ValidPortValidator.java
similarity index 85%
rename from log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/validation/validators/ValidPortValidator.java
rename to log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/validation/validators/ValidPortValidator.java
index a59742c..27e97f0 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/validation/validators/ValidPortValidator.java
+++ b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/validation/validators/ValidPortValidator.java
@@ -14,12 +14,12 @@
  * See the license for the specific language governing permissions and
  * limitations under the license.
  */
-package org.apache.logging.log4j.core.config.plugins.validation.validators;
+package org.apache.logging.log4j.plugins.validation.validators;
 
 import org.apache.logging.log4j.Logger;
-import org.apache.logging.log4j.core.config.plugins.convert.TypeConverters;
-import org.apache.logging.log4j.core.config.plugins.validation.ConstraintValidator;
-import org.apache.logging.log4j.core.config.plugins.validation.constraints.ValidPort;
+import org.apache.logging.log4j.plugins.validation.ConstraintValidator;
+import org.apache.logging.log4j.plugins.validation.constraints.ValidPort;
+import org.apache.logging.log4j.plugins.convert.TypeConverters;
 import org.apache.logging.log4j.status.StatusLogger;
 
 /**
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/validation/validators/package-info.java b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/validation/validators/package-info.java
similarity index 92%
rename from log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/validation/validators/package-info.java
rename to log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/validation/validators/package-info.java
index a8ac560..cfe2041 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/validation/validators/package-info.java
+++ b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/validation/validators/package-info.java
@@ -20,4 +20,4 @@
  *
  * @since 2.1
  */
-package org.apache.logging.log4j.core.config.plugins.validation.validators;
+package org.apache.logging.log4j.plugins.validation.validators;
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/visitors/AbstractPluginVisitor.java b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/visitors/AbstractPluginVisitor.java
similarity index 84%
rename from log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/visitors/AbstractPluginVisitor.java
rename to log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/visitors/AbstractPluginVisitor.java
index 560cbe3..b98dc09 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/visitors/AbstractPluginVisitor.java
+++ b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/visitors/AbstractPluginVisitor.java
@@ -15,18 +15,18 @@
  * limitations under the license.
  */
 
-package org.apache.logging.log4j.core.config.plugins.visitors;
+package org.apache.logging.log4j.plugins.visitors;
+
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.plugins.convert.TypeConverters;
+import org.apache.logging.log4j.status.StatusLogger;
+import org.apache.logging.log4j.util.Strings;
 
 import java.lang.annotation.Annotation;
 import java.lang.reflect.Member;
 import java.util.Map;
 import java.util.Objects;
-
-import org.apache.logging.log4j.Logger;
-import org.apache.logging.log4j.core.config.plugins.convert.TypeConverters;
-import org.apache.logging.log4j.core.lookup.StrSubstitutor;
-import org.apache.logging.log4j.status.StatusLogger;
-import org.apache.logging.log4j.util.Strings;
+import java.util.function.Function;
 
 /**
  * Base class for PluginVisitor implementations. Provides convenience methods as well as all method implementations
@@ -34,7 +34,7 @@ import org.apache.logging.log4j.util.Strings;
  *
  * @param <A> the Plugin annotation type.
  */
-public abstract class AbstractPluginVisitor<A extends Annotation> implements PluginVisitor<A> {
+public abstract class AbstractPluginVisitor<A extends Annotation, T> implements PluginVisitor<A, T> {
 
     /** Status logger. */
     protected static final Logger LOGGER = StatusLogger.getLogger();
@@ -58,10 +58,6 @@ public abstract class AbstractPluginVisitor<A extends Annotation> implements Plu
     /**
      * 
      */
-    protected StrSubstitutor substitutor;
-    /**
-     * 
-     */
     protected Member member;
 
     /**
@@ -75,7 +71,7 @@ public abstract class AbstractPluginVisitor<A extends Annotation> implements Plu
 
     @SuppressWarnings("unchecked")
     @Override
-    public PluginVisitor<A> setAnnotation(final Annotation anAnnotation) {
+    public PluginVisitor<A, T> setAnnotation(final Annotation anAnnotation) {
         final Annotation a = Objects.requireNonNull(anAnnotation, "No annotation was provided");
         if (this.clazz.isInstance(a)) {
             this.annotation = (A) a;
@@ -84,25 +80,19 @@ public abstract class AbstractPluginVisitor<A extends Annotation> implements Plu
     }
 
     @Override
-    public PluginVisitor<A> setAliases(final String... someAliases) {
+    public PluginVisitor<A, T> setAliases(final String... someAliases) {
         this.aliases = someAliases;
         return this;
     }
 
     @Override
-    public PluginVisitor<A> setConversionType(final Class<?> aConversionType) {
+    public PluginVisitor<A, T> setConversionType(final Class<?> aConversionType) {
         this.conversionType = Objects.requireNonNull(aConversionType, "No conversion type class was provided");
         return this;
     }
 
     @Override
-    public PluginVisitor<A> setStrSubstitutor(final StrSubstitutor aSubstitutor) {
-        this.substitutor = Objects.requireNonNull(aSubstitutor, "No StrSubstitutor was provided");
-        return this;
-    }
-
-    @Override
-    public PluginVisitor<A> setMember(final Member aMember) {
+    public PluginVisitor<A, T> setMember(final Member aMember) {
         this.member = aMember;
         return this;
     }
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/visitors/PluginAttributeVisitor.java b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/visitors/PluginAttributeVisitor.java
similarity index 80%
rename from log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/visitors/PluginAttributeVisitor.java
rename to log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/visitors/PluginAttributeVisitor.java
index f4da42b..fbd28b9 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/visitors/PluginAttributeVisitor.java
+++ b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/visitors/PluginAttributeVisitor.java
@@ -15,40 +15,39 @@
  * limitations under the license.
  */
 
-package org.apache.logging.log4j.core.config.plugins.visitors;
+package org.apache.logging.log4j.plugins.visitors;
 
-import java.util.Map;
-
-import org.apache.logging.log4j.core.LogEvent;
-import org.apache.logging.log4j.core.config.Configuration;
-import org.apache.logging.log4j.core.config.Node;
-import org.apache.logging.log4j.core.config.plugins.PluginAttribute;
-import org.apache.logging.log4j.core.util.NameUtil;
+import org.apache.logging.log4j.plugins.Node;
+import org.apache.logging.log4j.plugins.PluginAttribute;
+import org.apache.logging.log4j.util.NameUtil;
 import org.apache.logging.log4j.util.StringBuilders;
 
+import java.util.Map;
+import java.util.function.Function;
+
 /**
  * PluginVisitor implementation for {@link PluginAttribute}.
  */
-public class PluginAttributeVisitor extends AbstractPluginVisitor<PluginAttribute> {
+public class PluginAttributeVisitor extends AbstractPluginVisitor<PluginAttribute, Object> {
     public PluginAttributeVisitor() {
         super(PluginAttribute.class);
     }
 
     @Override
-    public Object visit(final Configuration configuration, final Node node, final LogEvent event,
+    public Object visit(final Object unused, final Node node, final Function<String, String> substitutor,
                         final StringBuilder log) {
         final String name = this.annotation.value();
         final Map<String, String> attributes = node.getAttributes();
         final String rawValue = removeAttributeValue(attributes, name, this.aliases);
-        final String replacedValue = this.substitutor.replace(event, rawValue);
-        final Object defaultValue = findDefaultValue(event);
+        final String replacedValue = substitutor.apply(rawValue);
+        final Object defaultValue = findDefaultValue(substitutor);
         final Object value = convert(replacedValue, defaultValue);
         final Object debugValue = this.annotation.sensitive() ? NameUtil.md5(value + this.getClass().getName()) : value;
         StringBuilders.appendKeyDqValue(log, name, debugValue);
         return value;
     }
 
-    private Object findDefaultValue(final LogEvent event) {
+    private Object findDefaultValue(Function<String, String> substitutor) {
         if (this.conversionType == int.class || this.conversionType == Integer.class) {
             return this.annotation.defaultInt();
         }
@@ -76,6 +75,6 @@ public class PluginAttributeVisitor extends AbstractPluginVisitor<PluginAttribut
         if (this.conversionType == Class.class) {
             return this.annotation.defaultClass();
         }
-        return this.substitutor.replace(event, this.annotation.defaultString());
+        return substitutor.apply(this.annotation.defaultString());
     }
 }
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/visitors/PluginBuilderAttributeVisitor.java b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/visitors/PluginBuilderAttributeVisitor.java
similarity index 74%
rename from log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/visitors/PluginBuilderAttributeVisitor.java
rename to log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/visitors/PluginBuilderAttributeVisitor.java
index e951456..398ff1c 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/visitors/PluginBuilderAttributeVisitor.java
+++ b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/visitors/PluginBuilderAttributeVisitor.java
@@ -15,38 +15,37 @@
  * limitations under the license.
  */
 
-package org.apache.logging.log4j.core.config.plugins.visitors;
+package org.apache.logging.log4j.plugins.visitors;
 
-import java.util.Map;
-
-import org.apache.logging.log4j.core.LogEvent;
-import org.apache.logging.log4j.core.config.Configuration;
-import org.apache.logging.log4j.core.config.Node;
-import org.apache.logging.log4j.core.config.plugins.PluginBuilderAttribute;
-import org.apache.logging.log4j.core.util.NameUtil;
+import org.apache.logging.log4j.plugins.Node;
+import org.apache.logging.log4j.plugins.PluginBuilderAttribute;
+import org.apache.logging.log4j.util.NameUtil;
 import org.apache.logging.log4j.util.StringBuilders;
 
+import java.util.Map;
+import java.util.function.Function;
+
 /**
  * PluginVisitor for PluginBuilderAttribute. If {@code null} is returned for the
- * {@link #visit(org.apache.logging.log4j.core.config.Configuration, org.apache.logging.log4j.core.config.Node, org.apache.logging.log4j.core.LogEvent, StringBuilder)}
+ * {@link #visit(org.apache.logging.log4j.core.config.Configuration, org.apache.logging.log4j.plugins.Node, org.apache.logging.log4j.core.LogEvent, StringBuilder)}
  * method, then the default value of the field should remain untouched.
  *
  * @see org.apache.logging.log4j.core.config.plugins.util.PluginBuilder
  */
-public class PluginBuilderAttributeVisitor extends AbstractPluginVisitor<PluginBuilderAttribute> {
+public class PluginBuilderAttributeVisitor extends AbstractPluginVisitor<PluginBuilderAttribute, Object> {
 
     public PluginBuilderAttributeVisitor() {
         super(PluginBuilderAttribute.class);
     }
 
     @Override
-    public Object visit(final Configuration configuration, final Node node, final LogEvent event,
+    public Object visit(final Object unused, final Node node, final Function<String, String> substitutor,
                         final StringBuilder log) {
         final String overridden = this.annotation.value();
         final String name = overridden.isEmpty() ? this.member.getName() : overridden;
         final Map<String, String> attributes = node.getAttributes();
         final String rawValue = removeAttributeValue(attributes, name, this.aliases);
-        final String replacedValue = this.substitutor.replace(event, rawValue);
+        final String replacedValue = substitutor.apply(rawValue);
         final Object value = convert(replacedValue, null);
         final Object debugValue = this.annotation.sensitive() ? NameUtil.md5(value + this.getClass().getName()) : value;
         StringBuilders.appendKeyDqValue(log, name, debugValue);
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/visitors/PluginElementVisitor.java b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/visitors/PluginElementVisitor.java
similarity index 90%
rename from log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/visitors/PluginElementVisitor.java
rename to log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/visitors/PluginElementVisitor.java
index 2e6e6ef..f8197f1 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/visitors/PluginElementVisitor.java
+++ b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/visitors/PluginElementVisitor.java
@@ -15,30 +15,29 @@
  * limitations under the license.
  */
 
-package org.apache.logging.log4j.core.config.plugins.visitors;
+package org.apache.logging.log4j.plugins.visitors;
+
+import org.apache.logging.log4j.plugins.Node;
+import org.apache.logging.log4j.plugins.PluginElement;
+import org.apache.logging.log4j.plugins.util.PluginType;
 
 import java.lang.reflect.Array;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.List;
-
-import org.apache.logging.log4j.core.LogEvent;
-import org.apache.logging.log4j.core.config.Configuration;
-import org.apache.logging.log4j.core.config.Node;
-import org.apache.logging.log4j.core.config.plugins.PluginElement;
-import org.apache.logging.log4j.core.config.plugins.util.PluginType;
+import java.util.function.Function;
 
 /**
  * PluginVisitor implementation for {@link PluginElement}. Supports arrays as well as singular values.
  */
-public class PluginElementVisitor extends AbstractPluginVisitor<PluginElement> {
+public class PluginElementVisitor extends AbstractPluginVisitor<PluginElement, Object> {
     public PluginElementVisitor() {
         super(PluginElement.class);
     }
 
     @Override
-    public Object visit(final Configuration configuration, final Node node, final LogEvent event,
+    public Object visit(final Object unused, final Node node, final Function<String, String> substitutor,
                         final StringBuilder log) {
         final String name = this.annotation.value();
         if (this.conversionType.isArray()) {
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/visitors/PluginNodeVisitor.java b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/visitors/PluginNodeVisitor.java
similarity index 77%
rename from log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/visitors/PluginNodeVisitor.java
rename to log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/visitors/PluginNodeVisitor.java
index 7f15392..9438b39 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/visitors/PluginNodeVisitor.java
+++ b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/visitors/PluginNodeVisitor.java
@@ -15,23 +15,23 @@
  * limitations under the license.
  */
 
-package org.apache.logging.log4j.core.config.plugins.visitors;
+package org.apache.logging.log4j.plugins.visitors;
 
-import org.apache.logging.log4j.core.LogEvent;
-import org.apache.logging.log4j.core.config.Configuration;
-import org.apache.logging.log4j.core.config.Node;
-import org.apache.logging.log4j.core.config.plugins.PluginNode;
+import org.apache.logging.log4j.plugins.Node;
+import org.apache.logging.log4j.plugins.PluginNode;
+
+import java.util.function.Function;
 
 /**
  * PluginVisitor implementation for {@link PluginNode}.
  */
-public class PluginNodeVisitor extends AbstractPluginVisitor<PluginNode> {
+public class PluginNodeVisitor extends AbstractPluginVisitor<PluginNode, Object> {
     public PluginNodeVisitor() {
         super(PluginNode.class);
     }
 
     @Override
-    public Object visit(final Configuration configuration, final Node node, final LogEvent event,
+    public Object visit(final Object unused, final Node node, final Function<String, String> substitutor,
                         final StringBuilder log) {
         if (this.conversionType.isInstance(node)) {
             log.append("Node=").append(node.getName());
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/visitors/PluginValueVisitor.java b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/visitors/PluginValueVisitor.java
similarity index 79%
rename from log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/visitors/PluginValueVisitor.java
rename to log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/visitors/PluginValueVisitor.java
index 8544570..2f68f04 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/visitors/PluginValueVisitor.java
+++ b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/visitors/PluginValueVisitor.java
@@ -15,26 +15,26 @@
  * limitations under the license.
  */
 
-package org.apache.logging.log4j.core.config.plugins.visitors;
+package org.apache.logging.log4j.plugins.visitors;
 
-import org.apache.logging.log4j.core.LogEvent;
-import org.apache.logging.log4j.core.config.Configuration;
-import org.apache.logging.log4j.core.config.Node;
-import org.apache.logging.log4j.core.config.plugins.PluginValue;
+import org.apache.logging.log4j.plugins.Node;
+import org.apache.logging.log4j.plugins.PluginValue;
 import org.apache.logging.log4j.util.StringBuilders;
 import org.apache.logging.log4j.util.Strings;
 
+import java.util.function.Function;
+
 /**
  * PluginVisitor implementation for {@link PluginValue}.
  */
-public class PluginValueVisitor extends AbstractPluginVisitor<PluginValue> {
+public class PluginValueVisitor extends AbstractPluginVisitor<PluginValue, Object> {
     public PluginValueVisitor() {
         super(PluginValue.class);
     }
 
     @Override
-    public Object visit(final Configuration configuration, final Node node, final LogEvent event,
-            final StringBuilder log) {
+    public Object visit(final Object unused, final Node node, final Function<String, String> substitutor,
+                        final StringBuilder log) {
         final String name = this.annotation.value();
         final String elementValue = node.getValue();
         final String attributeValue = node.getAttributes().get("value");
@@ -49,7 +49,7 @@ public class PluginValueVisitor extends AbstractPluginVisitor<PluginValue> {
         } else {
             rawValue = removeAttributeValue(node.getAttributes(), "value");
         }
-        final String value = this.substitutor.replace(event, rawValue);
+        final String value = substitutor.apply(rawValue);
         StringBuilders.appendKeyDqValue(log, name, value);
         return value;
     }
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/visitors/PluginVisitor.java b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/visitors/PluginVisitor.java
similarity index 68%
rename from log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/visitors/PluginVisitor.java
rename to log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/visitors/PluginVisitor.java
index 34e2b78..fb7aca4 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/visitors/PluginVisitor.java
+++ b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/visitors/PluginVisitor.java
@@ -15,15 +15,13 @@
  * limitations under the license.
  */
 
-package org.apache.logging.log4j.core.config.plugins.visitors;
+package org.apache.logging.log4j.plugins.visitors;
+
+import org.apache.logging.log4j.plugins.Node;
 
 import java.lang.annotation.Annotation;
 import java.lang.reflect.Member;
-
-import org.apache.logging.log4j.core.LogEvent;
-import org.apache.logging.log4j.core.config.Configuration;
-import org.apache.logging.log4j.core.config.Node;
-import org.apache.logging.log4j.core.lookup.StrSubstitutor;
+import java.util.function.Function;
 
 /**
  * Visitor strategy for parsing data from a {@link Node}, doing any relevant type conversion, and returning a
@@ -31,7 +29,7 @@ import org.apache.logging.log4j.core.lookup.StrSubstitutor;
  *
  * @param <A> the Annotation type.
  */
-public interface PluginVisitor<A extends Annotation> {
+public interface PluginVisitor<A extends Annotation, T> {
 
     /**
      * Sets the Annotation to be used for this. If the given Annotation is not compatible with this class's type, then
@@ -41,7 +39,7 @@ public interface PluginVisitor<A extends Annotation> {
      * @return {@code this}.
      * @throws NullPointerException if the argument is {@code null}.
      */
-    PluginVisitor<A> setAnnotation(Annotation annotation);
+    PluginVisitor<A, T> setAnnotation(Annotation annotation);
 
     /**
      * Sets the list of aliases to use for this visit. No aliases are required, however.
@@ -49,7 +47,7 @@ public interface PluginVisitor<A extends Annotation> {
      * @param aliases the list of aliases to use.
      * @return {@code this}.
      */
-    PluginVisitor<A> setAliases(String... aliases);
+    PluginVisitor<A, T> setAliases(String... aliases);
 
     /**
      * Sets the class to convert the plugin value to on this visit. This should correspond with a class obtained from
@@ -59,17 +57,7 @@ public interface PluginVisitor<A extends Annotation> {
      * @return {@code this}.
      * @throws NullPointerException if the argument is {@code null}.
      */
-    PluginVisitor<A> setConversionType(Class<?> conversionType);
-
-    /**
-     * Sets the StrSubstitutor to use for converting raw strings before type conversion. Generally obtained from a
-     * {@link org.apache.logging.log4j.core.config.Configuration}.
-     *
-     * @param substitutor the StrSubstitutor to use on plugin values.
-     * @return {@code this}.
-     * @throws NullPointerException if the argument is {@code null}.
-     */
-    PluginVisitor<A> setStrSubstitutor(StrSubstitutor substitutor);
+    PluginVisitor<A, T> setConversionType(Class<?> conversionType);
 
     /**
      * Sets the Member that this visitor is being used for injection upon. For instance, this could be the Field
@@ -79,16 +67,16 @@ public interface PluginVisitor<A extends Annotation> {
      * @param member the member this visitor is parsing a value for.
      * @return {@code this}.
      */
-    PluginVisitor<A> setMember(Member member);
+    PluginVisitor<A, T> setMember(Member member);
 
     /**
      * Visits a Node to obtain a value for constructing a Plugin object.
      *
      * @param configuration the current Configuration.
      * @param node          the current Node corresponding to the Plugin object being created.
-     * @param event         the current LogEvent that caused this Plugin object to be made (optional).
-     * @param log           the StringBuilder being used to build a debug message.
+     * @param substitutor   the function to perform String substitutions.
+     * @param log           th e StringBuilder being used to build a debug message.
      * @return the converted value to be used for Plugin creation.
      */
-    Object visit(Configuration configuration, Node node, LogEvent event, StringBuilder log);
+    Object visit(T configuration, Node node, Function<String, String> substitutor, StringBuilder log);
 }
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/visitors/PluginVisitors.java b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/visitors/PluginVisitors.java
similarity index 86%
rename from log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/visitors/PluginVisitors.java
rename to log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/visitors/PluginVisitors.java
index 10ee0df..695d387 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/visitors/PluginVisitors.java
+++ b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/visitors/PluginVisitors.java
@@ -15,14 +15,14 @@
  * limitations under the license.
  */
 
-package org.apache.logging.log4j.core.config.plugins.visitors;
-
-import java.lang.annotation.Annotation;
+package org.apache.logging.log4j.plugins.visitors;
 
 import org.apache.logging.log4j.Logger;
-import org.apache.logging.log4j.core.config.plugins.PluginVisitorStrategy;
+import org.apache.logging.log4j.plugins.PluginVisitorStrategy;
 import org.apache.logging.log4j.status.StatusLogger;
 
+import java.lang.annotation.Annotation;
+
 /**
  * Utility class to locate an appropriate {@link PluginVisitor} implementation for an annotation.
  */
@@ -41,13 +41,13 @@ public final class PluginVisitors {
      * @param annotation the Plugin annotation class to find a PluginVisitor for.
      * @return a PluginVisitor instance if one could be created, or {@code null} otherwise.
      */
-    public static PluginVisitor<? extends Annotation> findVisitor(final Class<? extends Annotation> annotation) {
+    public static <T> PluginVisitor<? extends Annotation, T> findVisitor(final Class<? extends Annotation> annotation) {
         final PluginVisitorStrategy strategy = annotation.getAnnotation(PluginVisitorStrategy.class);
         if (strategy == null) {
             return null;
         }
         try {
-            return strategy.value().newInstance();
+            return (PluginVisitor<? extends Annotation, T>) strategy.value().newInstance();
         } catch (final Exception e) {
             LOGGER.error("Error loading PluginVisitor [{}] for annotation [{}].", strategy.value(), annotation, e);
             return null;
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/validation/constraints/package-info.java b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/visitors/package-info.java
similarity index 67%
rename from log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/validation/constraints/package-info.java
rename to log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/visitors/package-info.java
index f22ba49..0855b16 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/validation/constraints/package-info.java
+++ b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/visitors/package-info.java
@@ -16,8 +16,9 @@
  */
 
 /**
- * Validation annotations.
- *
- * @since 2.1
+ * Visitor classes for extracting values from a Configuration or Node corresponding to a plugin annotation.
+ * Visitor implementations must implement {@link org.apache.logging.log4j.plugins.visitors.PluginVisitor},
+ * and the corresponding annotation must be annotated with
+ * {@link org.apache.logging.log4j.plugins.PluginVisitorStrategy}.
  */
-package org.apache.logging.log4j.core.config.plugins.validation.constraints;
+package org.apache.logging.log4j.plugins.visitors;
diff --git a/log4j-core/src/main/resources/META-INF/services/javax.annotation.processing.Processor b/log4j-plugins/src/main/resources/META-INF/services/javax.annotation.processing.Processor
similarity index 91%
rename from log4j-core/src/main/resources/META-INF/services/javax.annotation.processing.Processor
rename to log4j-plugins/src/main/resources/META-INF/services/javax.annotation.processing.Processor
index bb9dcb9..5d6951a 100644
--- a/log4j-core/src/main/resources/META-INF/services/javax.annotation.processing.Processor
+++ b/log4j-plugins/src/main/resources/META-INF/services/javax.annotation.processing.Processor
@@ -14,4 +14,4 @@
 # See the license for the specific language governing permissions and
 # limitations under the license.
 #
-org.apache.logging.log4j.core.config.plugins.processor.PluginProcessor
+org.apache.logging.log4j.plugins.processor.PluginProcessor
diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/core/config/plugins/convert/TypeConverterRegistryTest.java b/log4j-plugins/src/test/java/org/apache/logging/log4j/plugins/convert/TypeConverterRegistryTest.java
similarity index 97%
rename from log4j-core/src/test/java/org/apache/logging/log4j/core/config/plugins/convert/TypeConverterRegistryTest.java
rename to log4j-plugins/src/test/java/org/apache/logging/log4j/plugins/convert/TypeConverterRegistryTest.java
index f9e757d..6e4b059 100644
--- a/log4j-core/src/test/java/org/apache/logging/log4j/core/config/plugins/convert/TypeConverterRegistryTest.java
+++ b/log4j-plugins/src/test/java/org/apache/logging/log4j/plugins/convert/TypeConverterRegistryTest.java
@@ -14,7 +14,7 @@
  * See the license for the specific language governing permissions and
  * limitations under the license.
  */
-package org.apache.logging.log4j.core.config.plugins.convert;
+package org.apache.logging.log4j.plugins.convert;
 
 import org.junit.Test;
 
@@ -63,7 +63,7 @@ public class TypeConverterRegistryTest {
         // TODO: is there a specific converter this should return?
     }
 
-    public static enum Foo {
+    public enum Foo {
         I, PITY, THE
     }
 
diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/core/config/plugins/processor/FakePlugin.java b/log4j-plugins/src/test/java/org/apache/logging/log4j/plugins/processor/FakePlugin.java
similarity index 84%
rename from log4j-core/src/test/java/org/apache/logging/log4j/core/config/plugins/processor/FakePlugin.java
rename to log4j-plugins/src/test/java/org/apache/logging/log4j/plugins/processor/FakePlugin.java
index f4ceb41..48ea7dc 100644
--- a/log4j-core/src/test/java/org/apache/logging/log4j/core/config/plugins/processor/FakePlugin.java
+++ b/log4j-plugins/src/test/java/org/apache/logging/log4j/plugins/processor/FakePlugin.java
@@ -15,10 +15,10 @@
  * limitations under the license.
  */
 
-package org.apache.logging.log4j.core.config.plugins.processor;
+package org.apache.logging.log4j.plugins.processor;
 
-import org.apache.logging.log4j.core.config.plugins.Plugin;
-import org.apache.logging.log4j.core.config.plugins.PluginAliases;
+import org.apache.logging.log4j.plugins.Plugin;
+import org.apache.logging.log4j.plugins.PluginAliases;
 
 /**
  * Test plugin class for unit tests.
diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/core/config/plugins/processor/PluginProcessorTest.java b/log4j-plugins/src/test/java/org/apache/logging/log4j/plugins/processor/PluginProcessorTest.java
similarity index 75%
rename from log4j-core/src/test/java/org/apache/logging/log4j/core/config/plugins/processor/PluginProcessorTest.java
rename to log4j-plugins/src/test/java/org/apache/logging/log4j/plugins/processor/PluginProcessorTest.java
index 9c37af4..034705c 100644
--- a/log4j-core/src/test/java/org/apache/logging/log4j/core/config/plugins/processor/PluginProcessorTest.java
+++ b/log4j-plugins/src/test/java/org/apache/logging/log4j/plugins/processor/PluginProcessorTest.java
@@ -15,17 +15,19 @@
  * limitations under the license.
  */
 
-package org.apache.logging.log4j.core.config.plugins.processor;
+package org.apache.logging.log4j.plugins.processor;
 
-import org.apache.logging.log4j.core.config.plugins.Plugin;
-import org.apache.logging.log4j.core.config.plugins.PluginAliases;
+import org.apache.logging.log4j.plugins.Plugin;
+import org.apache.logging.log4j.plugins.PluginAliases;
 import org.junit.BeforeClass;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
 
 import java.net.URL;
+import java.util.ArrayList;
 import java.util.Enumeration;
+import java.util.List;
 import java.util.Map;
 
 import static org.junit.Assert.*;
@@ -33,29 +35,29 @@ import static org.junit.Assert.*;
 @RunWith(JUnit4.class)
 public class PluginProcessorTest {
 
-    private static final PluginCache pluginCache = new PluginCache();
+    private static PluginService pluginService;
 
     private final Plugin p = FakePlugin.class.getAnnotation(Plugin.class);
 
     @BeforeClass
     public static void setUpClass() throws Exception {
-        final Enumeration<URL> resources =
-            PluginProcessor.class.getClassLoader().getResources(PluginProcessor.PLUGIN_CACHE_FILE);
-        pluginCache.loadCacheFiles(resources);
+        Class<?> clazz = PluginProcessor.class.getClassLoader().loadClass("org.apache.logging.log4j.plugins.plugins.Log4jPlugins");
+        assertNotNull("Could not locate plugins class", clazz);
+        pluginService = (PluginService) clazz.getDeclaredConstructor().newInstance();;
     }
 
     @Test
     public void testTestCategoryFound() throws Exception {
         assertNotNull("No plugin annotation on FakePlugin.", p);
-        final Map<String, PluginEntry> testCategory = pluginCache.getCategory(p.category());
-        assertNotEquals("No plugins were found.", 0, pluginCache.size());
+        final Map<String, PluginEntry> testCategory = pluginService.getCategory(p.category());
+        assertNotEquals("No plugins were found.", 0, pluginService.size());
         assertNotNull("The category '" + p.category() + "' was not found.", testCategory);
         assertFalse(testCategory.isEmpty());
     }
 
     @Test
     public void testFakePluginFoundWithCorrectInformation() throws Exception {
-        final PluginEntry fake = pluginCache.getCategory(p.category()).get(p.name().toLowerCase());
+        final PluginEntry fake = pluginService.getCategory(p.category()).get(p.name().toLowerCase());
         verifyFakePluginEntry(p.name(), fake);
     }
 
@@ -63,7 +65,7 @@ public class PluginProcessorTest {
     public void testFakePluginAliasesContainSameInformation() throws Exception {
         final PluginAliases aliases = FakePlugin.class.getAnnotation(PluginAliases.class);
         for (final String alias : aliases.value()) {
-            final PluginEntry fake = pluginCache.getCategory(p.category()).get(alias.toLowerCase());
+            final PluginEntry fake = pluginService.getCategory(p.category()).get(alias.toLowerCase());
             verifyFakePluginEntry(alias, fake);
         }
     }
@@ -81,7 +83,7 @@ public class PluginProcessorTest {
     @Test
     public void testNestedPlugin() throws Exception {
         final Plugin p = FakePlugin.Nested.class.getAnnotation(Plugin.class);
-        final PluginEntry nested = pluginCache.getCategory(p.category()).get(p.name().toLowerCase());
+        final PluginEntry nested = pluginService.getCategory(p.category()).get(p.name().toLowerCase());
         assertNotNull(nested);
         assertEquals(p.name().toLowerCase(), nested.getKey());
         assertEquals(FakePlugin.Nested.class.getName(), nested.getClassName());
diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/core/config/plugins/util/ResolverUtilCustomProtocolTest.java b/log4j-plugins/src/test/java/org/apache/logging/log4j/plugins/util/ResolverUtilCustomProtocolTest.java
similarity index 93%
rename from log4j-core/src/test/java/org/apache/logging/log4j/core/config/plugins/util/ResolverUtilCustomProtocolTest.java
rename to log4j-plugins/src/test/java/org/apache/logging/log4j/plugins/util/ResolverUtilCustomProtocolTest.java
index 33e0ee1..d0b35d1 100644
--- a/log4j-core/src/test/java/org/apache/logging/log4j/core/config/plugins/util/ResolverUtilCustomProtocolTest.java
+++ b/log4j-plugins/src/test/java/org/apache/logging/log4j/plugins/util/ResolverUtilCustomProtocolTest.java
@@ -15,27 +15,21 @@
  * limitations under the license.
  */
 
-package org.apache.logging.log4j.core.config.plugins.util;
+package org.apache.logging.log4j.plugins.util;
 
-import static org.junit.Assert.assertEquals;
+import org.apache.logging.log4j.junit.CleanFolders;
+import org.apache.logging.log4j.junit.URLStreamHandlerFactoryRule;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.RuleChain;
 
 import java.io.IOException;
-import java.net.Proxy;
-import java.net.URL;
-import java.net.URLClassLoader;
-import java.net.URLConnection;
-import java.net.URLStreamHandler;
-import java.net.URLStreamHandlerFactory;
+import java.net.*;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.Enumeration;
 
-import org.apache.logging.log4j.core.config.plugins.util.PluginRegistry.PluginTest;
-import org.apache.logging.log4j.junit.CleanFolders;
-import org.apache.logging.log4j.junit.URLStreamHandlerFactoryRule;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.RuleChain;
+import static org.junit.Assert.assertEquals;
 
 /**
  * Tests the ResolverUtil class for custom protocol like bundleresource, vfs, vfszip.
@@ -187,9 +181,9 @@ public class ResolverUtilCustomProtocolTest {
             final ResolverUtil resolverUtil = new ResolverUtil();
             resolverUtil
                     .setClassLoader(new SingleURLClassLoader(new URL("vfs:/" + ResolverUtilTest.WORK_DIR + "/resolverutil3/customplugin3/"), cl));
-            resolverUtil.findInPackage(new PluginTest(), "customplugin3");
+            resolverUtil.findInPackage(new PluginRegistry.PluginTest(), "customplugin3");
             assertEquals("Class not found in packages", 1, resolverUtil.getClasses().size());
-            assertEquals("Unexpected class resolved", cl.loadClass("customplugin3.FixedString3Layout"),
+            assertEquals("Unexpected class resolved", cl.loadClass("customplugin3.FixedString3"),
                     resolverUtil.getClasses().iterator().next());
         }
     }
@@ -200,9 +194,9 @@ public class ResolverUtilCustomProtocolTest {
             final ResolverUtil resolverUtil = new ResolverUtil();
             resolverUtil.setClassLoader(new SingleURLClassLoader(
                     new URL("vfs:/" + ResolverUtilTest.WORK_DIR + "/resolverutil4/customplugin4.jar/customplugin4/"), cl));
-            resolverUtil.findInPackage(new PluginTest(), "customplugin4");
+            resolverUtil.findInPackage(new PluginRegistry.PluginTest(), "customplugin4");
             assertEquals("Class not found in packages", 1, resolverUtil.getClasses().size());
-            assertEquals("Unexpected class resolved", cl.loadClass("customplugin4.FixedString4Layout"),
+            assertEquals("Unexpected class resolved", cl.loadClass("customplugin4.FixedString4"),
                     resolverUtil.getClasses().iterator().next());
         }
     }
diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/core/config/plugins/util/ResolverUtilTest.java b/log4j-plugins/src/test/java/org/apache/logging/log4j/plugins/util/ResolverUtilTest.java
similarity index 82%
rename from log4j-core/src/test/java/org/apache/logging/log4j/core/config/plugins/util/ResolverUtilTest.java
rename to log4j-plugins/src/test/java/org/apache/logging/log4j/plugins/util/ResolverUtilTest.java
index 1c6371b..361fe7b 100644
--- a/log4j-core/src/test/java/org/apache/logging/log4j/core/config/plugins/util/ResolverUtilTest.java
+++ b/log4j-plugins/src/test/java/org/apache/logging/log4j/plugins/util/ResolverUtilTest.java
@@ -15,33 +15,25 @@
  * limitations under the license.
  */
 
-package org.apache.logging.log4j.core.config.plugins.util;
+package org.apache.logging.log4j.plugins.util;
 
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-
-import java.io.File;
-import java.io.IOException;
-import java.io.UnsupportedEncodingException;
-import java.net.MalformedURLException;
-import java.net.URI;
-import java.net.URISyntaxException;
-import java.net.URL;
-import java.net.URLClassLoader;
-import java.nio.file.FileSystem;
-import java.nio.file.FileSystems;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.StandardCopyOption;
-import java.util.HashMap;
-import java.util.Map;
-
-import org.apache.logging.log4j.core.config.plugins.util.PluginRegistry.PluginTest;
 import org.apache.logging.log4j.junit.CleanFolders;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.rules.RuleChain;
 
+import javax.tools.*;
+import java.io.File;
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.net.*;
+import java.nio.file.*;
+import java.util.*;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertNotNull;
+
 /**
  * Tests the ResolverUtil class.
  */
@@ -67,6 +59,7 @@ public class ResolverUtilTest {
     private void testExtractPathFromJarUrlNotDecodedIfFileExists(final String existingFile)
             throws MalformedURLException, UnsupportedEncodingException, URISyntaxException {
         URL url = ResolverUtilTest.class.getResource(existingFile);
+        assertNotNull("No url returned for " + existingFile, url);
         if (!url.getProtocol().equals("jar")) {
             // create fake jar: URL that resolves to existing file
             url = new URL("jar:" + url.toExternalForm() + "!/some/entry");
@@ -152,9 +145,9 @@ public class ResolverUtilTest {
         try (final URLClassLoader cl = compileAndCreateClassLoader("1")) {
             final ResolverUtil resolverUtil = new ResolverUtil();
             resolverUtil.setClassLoader(cl);
-            resolverUtil.findInPackage(new PluginTest(), "customplugin1");
+            resolverUtil.findInPackage(new PluginRegistry.PluginTest(), "customplugin1");
             assertEquals("Class not found in packages", 1, resolverUtil.getClasses().size());
-            assertEquals("Unexpected class resolved", cl.loadClass("customplugin1.FixedString1Layout"),
+            assertEquals("Unexpected class resolved", cl.loadClass("customplugin1.FixedString1"),
                     resolverUtil.getClasses().iterator().next());
         }
     }
@@ -164,9 +157,9 @@ public class ResolverUtilTest {
         try (final URLClassLoader cl = compileJarAndCreateClassLoader("2")) {
             final ResolverUtil resolverUtil = new ResolverUtil();
             resolverUtil.setClassLoader(cl);
-            resolverUtil.findInPackage(new PluginTest(), "customplugin2");
+            resolverUtil.findInPackage(new PluginRegistry.PluginTest(), "customplugin2");
             assertEquals("Class not found in packages", 1, resolverUtil.getClasses().size());
-            assertEquals("Unexpected class resolved", cl.loadClass("customplugin2.FixedString2Layout"),
+            assertEquals("Unexpected class resolved", cl.loadClass("customplugin2.FixedString2"),
                     resolverUtil.getClasses().iterator().next());
         }
     }
@@ -176,7 +169,7 @@ public class ResolverUtilTest {
         final File jarFile = new File(workDir, "customplugin" + suffix + ".jar");
         final URI jarURI = jarFile.toURI();
         createJar(jarURI, workDir, new File(workDir,
-              "customplugin" + suffix + "/FixedString" + suffix + "Layout.class"));
+              "customplugin" + suffix + "/FixedString" + suffix + ".class"));
         return URLClassLoader.newInstance(new URL[] {jarURI.toURL()});
     }
 
@@ -186,9 +179,9 @@ public class ResolverUtilTest {
     }
 
     static File compile(final String suffix) throws IOException {
-        final File orig = new File("target/test-classes/customplugin/FixedStringLayout.java.source");
+        final File orig = new File("target/test-classes/customplugin/FixedString.java.source");
         final File workDir = new File(WORK_DIR, "resolverutil" + suffix);
-        final File f = new File(workDir, "customplugin" + suffix + "/FixedString" + suffix + "Layout.java");
+        final File f = new File(workDir, "customplugin" + suffix + "/FixedString" + suffix + ".java");
         final File parent = f.getParentFile();
         if (!parent.exists()) {
           assertTrue("Create customplugin" + suffix + " folder KO", f.getParentFile().mkdirs());
@@ -199,7 +192,7 @@ public class ResolverUtilTest {
           .replaceAll("customplugin", "customplugin" + suffix);
         Files.write(f.toPath(), content.getBytes());
   
-        PluginManagerPackagesTest.compile(f);
+        compile(f);
         return workDir;
     }
 
@@ -218,4 +211,29 @@ public class ResolverUtilTest {
         } 
     }
 
+    static void compile(final File f) throws IOException {
+        // set up compiler
+        final JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
+        final DiagnosticCollector<JavaFileObject> diagnostics = new DiagnosticCollector<>();
+        final List<String> errors = new ArrayList<>();
+        try (final StandardJavaFileManager fileManager = compiler.getStandardFileManager(diagnostics, null, null)) {
+            final Iterable<? extends JavaFileObject> compilationUnits = fileManager.getJavaFileObjectsFromFiles(Arrays
+                .asList(f));
+
+            // compile generated source
+            // (switch off annotation processing: no need to create Log4j2Plugins.dat)
+            final List<String> options = Arrays.asList("-proc:none");
+            compiler.getTask(null, fileManager, diagnostics, options, null, compilationUnits).call();
+
+            // check we don't have any compilation errors
+            for (final Diagnostic<? extends JavaFileObject> diagnostic : diagnostics.getDiagnostics()) {
+                if (diagnostic.getKind() == Diagnostic.Kind.ERROR) {
+                    errors.add(String.format("Compile error at line %d, column %d: %s%n", diagnostic.getLineNumber(),
+                        diagnostic.getColumnNumber(), diagnostic.getMessage(Locale.getDefault())));
+                }
+            }
+        }
+        assertTrue(errors.toString(), errors.isEmpty());
+    }
+
 }
diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/core/config/plugins/validation/AbstractPluginWithGenericBuilder.java b/log4j-plugins/src/test/java/org/apache/logging/log4j/plugins/validation/AbstractPluginWithGenericBuilder.java
similarity index 87%
rename from log4j-core/src/test/java/org/apache/logging/log4j/core/config/plugins/validation/AbstractPluginWithGenericBuilder.java
rename to log4j-plugins/src/test/java/org/apache/logging/log4j/plugins/validation/AbstractPluginWithGenericBuilder.java
index 5689e29..0e243f2 100644
--- a/log4j-core/src/test/java/org/apache/logging/log4j/core/config/plugins/validation/AbstractPluginWithGenericBuilder.java
+++ b/log4j-plugins/src/test/java/org/apache/logging/log4j/plugins/validation/AbstractPluginWithGenericBuilder.java
@@ -14,10 +14,10 @@
  * See the license for the specific language governing permissions and
  * limitations under the license.
  */
-package org.apache.logging.log4j.core.config.plugins.validation;
+package org.apache.logging.log4j.plugins.validation;
 
-import org.apache.logging.log4j.core.config.plugins.PluginBuilderAttribute;
-import org.apache.logging.log4j.core.config.plugins.validation.constraints.Required;
+import org.apache.logging.log4j.plugins.PluginBuilderAttribute;
+import org.apache.logging.log4j.plugins.validation.constraints.Required;
 
 /**
  *
diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/core/config/plugins/validation/HostAndPort.java b/log4j-plugins/src/test/java/org/apache/logging/log4j/plugins/validation/HostAndPort.java
similarity index 78%
rename from log4j-core/src/test/java/org/apache/logging/log4j/core/config/plugins/validation/HostAndPort.java
rename to log4j-plugins/src/test/java/org/apache/logging/log4j/plugins/validation/HostAndPort.java
index 34123c0..626798e 100644
--- a/log4j-core/src/test/java/org/apache/logging/log4j/core/config/plugins/validation/HostAndPort.java
+++ b/log4j-plugins/src/test/java/org/apache/logging/log4j/plugins/validation/HostAndPort.java
@@ -14,15 +14,15 @@
  * See the license for the specific language governing permissions and
  * limitations under the license.
  */
-package org.apache.logging.log4j.core.config.plugins.validation;
+package org.apache.logging.log4j.plugins.validation;
 
-import java.net.InetSocketAddress;
+import org.apache.logging.log4j.plugins.PluginAttribute;
+import org.apache.logging.log4j.plugins.PluginFactory;
+import org.apache.logging.log4j.plugins.validation.constraints.ValidHost;
+import org.apache.logging.log4j.plugins.validation.constraints.ValidPort;
+import org.apache.logging.log4j.plugins.Plugin;
 
-import org.apache.logging.log4j.core.config.plugins.Plugin;
-import org.apache.logging.log4j.core.config.plugins.PluginAttribute;
-import org.apache.logging.log4j.core.config.plugins.PluginFactory;
-import org.apache.logging.log4j.core.config.plugins.validation.constraints.ValidHost;
-import org.apache.logging.log4j.core.config.plugins.validation.constraints.ValidPort;
+import java.net.InetSocketAddress;
 
 @Plugin(name = "HostAndPort", category = "Test")
 public class HostAndPort {
diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/core/config/plugins/validation/PluginWithGenericSubclassFoo1Builder.java b/log4j-plugins/src/test/java/org/apache/logging/log4j/plugins/validation/PluginWithGenericSubclassFoo1Builder.java
similarity index 81%
rename from log4j-core/src/test/java/org/apache/logging/log4j/core/config/plugins/validation/PluginWithGenericSubclassFoo1Builder.java
rename to log4j-plugins/src/test/java/org/apache/logging/log4j/plugins/validation/PluginWithGenericSubclassFoo1Builder.java
index 3f2b15a..c3fe6c5 100644
--- a/log4j-core/src/test/java/org/apache/logging/log4j/core/config/plugins/validation/PluginWithGenericSubclassFoo1Builder.java
+++ b/log4j-plugins/src/test/java/org/apache/logging/log4j/plugins/validation/PluginWithGenericSubclassFoo1Builder.java
@@ -14,18 +14,18 @@
  * See the license for the specific language governing permissions and
  * limitations under the license.
  */
-package org.apache.logging.log4j.core.config.plugins.validation;
+package org.apache.logging.log4j.plugins.validation;
 
-import org.apache.logging.log4j.core.config.plugins.Plugin;
-import org.apache.logging.log4j.core.config.plugins.PluginBuilderAttribute;
-import org.apache.logging.log4j.core.config.plugins.PluginBuilderFactory;
-import org.apache.logging.log4j.core.config.plugins.validation.constraints.Required;
+import org.apache.logging.log4j.plugins.PluginBuilderAttribute;
+import org.apache.logging.log4j.plugins.PluginBuilderFactory;
+import org.apache.logging.log4j.plugins.validation.constraints.Required;
+import org.apache.logging.log4j.plugins.Plugin;
 
 @Plugin(name = "PluginWithGenericSubclassFoo1Builder", category = "Test")
 public class PluginWithGenericSubclassFoo1Builder extends AbstractPluginWithGenericBuilder {
 
     public static class Builder<B extends Builder<B>> extends AbstractPluginWithGenericBuilder.Builder<B>
-            implements org.apache.logging.log4j.core.util.Builder<PluginWithGenericSubclassFoo1Builder> {
+            implements org.apache.logging.log4j.plugins.util.Builder<PluginWithGenericSubclassFoo1Builder> {
 
         @PluginBuilderFactory
         public static <B extends Builder<B>> B newBuilder() {
diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/core/config/plugins/validation/ValidatingPlugin.java b/log4j-plugins/src/test/java/org/apache/logging/log4j/plugins/validation/ValidatingPlugin.java
similarity index 80%
rename from log4j-core/src/test/java/org/apache/logging/log4j/core/config/plugins/validation/ValidatingPlugin.java
rename to log4j-plugins/src/test/java/org/apache/logging/log4j/plugins/validation/ValidatingPlugin.java
index 95a4209..9caf453 100644
--- a/log4j-core/src/test/java/org/apache/logging/log4j/core/config/plugins/validation/ValidatingPlugin.java
+++ b/log4j-plugins/src/test/java/org/apache/logging/log4j/plugins/validation/ValidatingPlugin.java
@@ -14,15 +14,15 @@
  * See the license for the specific language governing permissions and
  * limitations under the license.
  */
-package org.apache.logging.log4j.core.config.plugins.validation;
+package org.apache.logging.log4j.plugins.validation;
 
-import java.util.Objects;
+import org.apache.logging.log4j.plugins.PluginBuilderAttribute;
+import org.apache.logging.log4j.plugins.PluginBuilderFactory;
+import org.apache.logging.log4j.plugins.PluginFactory;
+import org.apache.logging.log4j.plugins.validation.constraints.Required;
+import org.apache.logging.log4j.plugins.Plugin;
 
-import org.apache.logging.log4j.core.config.plugins.Plugin;
-import org.apache.logging.log4j.core.config.plugins.PluginBuilderAttribute;
-import org.apache.logging.log4j.core.config.plugins.PluginBuilderFactory;
-import org.apache.logging.log4j.core.config.plugins.PluginFactory;
-import org.apache.logging.log4j.core.config.plugins.validation.constraints.Required;
+import java.util.Objects;
 
 /**
  *
@@ -51,7 +51,7 @@ public class ValidatingPlugin {
         return new Builder();
     }
 
-    public static class Builder implements org.apache.logging.log4j.core.util.Builder<ValidatingPlugin> {
+    public static class Builder implements org.apache.logging.log4j.plugins.util.Builder<ValidatingPlugin> {
 
         @PluginBuilderAttribute
         @Required(message = "The name given by the builder is null")
diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/core/config/plugins/validation/ValidatingPluginWithGenericBuilder.java b/log4j-plugins/src/test/java/org/apache/logging/log4j/plugins/validation/ValidatingPluginWithGenericBuilder.java
similarity index 80%
rename from log4j-core/src/test/java/org/apache/logging/log4j/core/config/plugins/validation/ValidatingPluginWithGenericBuilder.java
rename to log4j-plugins/src/test/java/org/apache/logging/log4j/plugins/validation/ValidatingPluginWithGenericBuilder.java
index 81b9d6f..b0bec53 100644
--- a/log4j-core/src/test/java/org/apache/logging/log4j/core/config/plugins/validation/ValidatingPluginWithGenericBuilder.java
+++ b/log4j-plugins/src/test/java/org/apache/logging/log4j/plugins/validation/ValidatingPluginWithGenericBuilder.java
@@ -14,15 +14,16 @@
  * See the license for the specific language governing permissions and
  * limitations under the license.
  */
-package org.apache.logging.log4j.core.config.plugins.validation;
+package org.apache.logging.log4j.plugins.validation;
 
-import java.util.Objects;
+import org.apache.logging.log4j.plugins.Plugin;
+
+import org.apache.logging.log4j.plugins.PluginBuilderAttribute;
+import org.apache.logging.log4j.plugins.PluginBuilderFactory;
+import org.apache.logging.log4j.plugins.PluginFactory;
+import org.apache.logging.log4j.plugins.validation.constraints.Required;
 
-import org.apache.logging.log4j.core.config.plugins.Plugin;
-import org.apache.logging.log4j.core.config.plugins.PluginBuilderAttribute;
-import org.apache.logging.log4j.core.config.plugins.PluginBuilderFactory;
-import org.apache.logging.log4j.core.config.plugins.PluginFactory;
-import org.apache.logging.log4j.core.config.plugins.validation.constraints.Required;
+import java.util.Objects;
 
 /**
  *
@@ -51,7 +52,7 @@ public class ValidatingPluginWithGenericBuilder {
         return new Builder<B>().asBuilder();
     }
 
-    public static class Builder<B extends Builder<B>> implements org.apache.logging.log4j.core.util.Builder<ValidatingPluginWithGenericBuilder> {
+    public static class Builder<B extends Builder<B>> implements org.apache.logging.log4j.plugins.util.Builder<ValidatingPluginWithGenericBuilder> {
 
         @PluginBuilderAttribute
         @Required(message = "The name given by the builder is null")
diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/core/config/plugins/validation/ValidatingPluginWithTypedBuilder.java b/log4j-plugins/src/test/java/org/apache/logging/log4j/plugins/validation/ValidatingPluginWithTypedBuilder.java
similarity index 80%
rename from log4j-core/src/test/java/org/apache/logging/log4j/core/config/plugins/validation/ValidatingPluginWithTypedBuilder.java
rename to log4j-plugins/src/test/java/org/apache/logging/log4j/plugins/validation/ValidatingPluginWithTypedBuilder.java
index 74a6477..256181c 100644
--- a/log4j-core/src/test/java/org/apache/logging/log4j/core/config/plugins/validation/ValidatingPluginWithTypedBuilder.java
+++ b/log4j-plugins/src/test/java/org/apache/logging/log4j/plugins/validation/ValidatingPluginWithTypedBuilder.java
@@ -14,15 +14,15 @@
  * See the license for the specific language governing permissions and
  * limitations under the license.
  */
-package org.apache.logging.log4j.core.config.plugins.validation;
+package org.apache.logging.log4j.plugins.validation;
 
-import java.util.Objects;
+import org.apache.logging.log4j.plugins.PluginBuilderAttribute;
+import org.apache.logging.log4j.plugins.PluginBuilderFactory;
+import org.apache.logging.log4j.plugins.PluginFactory;
+import org.apache.logging.log4j.plugins.validation.constraints.Required;
+import org.apache.logging.log4j.plugins.Plugin;
 
-import org.apache.logging.log4j.core.config.plugins.Plugin;
-import org.apache.logging.log4j.core.config.plugins.PluginBuilderAttribute;
-import org.apache.logging.log4j.core.config.plugins.PluginBuilderFactory;
-import org.apache.logging.log4j.core.config.plugins.PluginFactory;
-import org.apache.logging.log4j.core.config.plugins.validation.constraints.Required;
+import java.util.Objects;
 
 /**
  *
@@ -51,7 +51,7 @@ public class ValidatingPluginWithTypedBuilder {
         return new Builder<>();
     }
 
-    public static class Builder<T> implements org.apache.logging.log4j.core.util.Builder<ValidatingPluginWithTypedBuilder> {
+    public static class Builder<T> implements org.apache.logging.log4j.plugins.util.Builder<ValidatingPluginWithTypedBuilder> {
 
         @PluginBuilderAttribute
         @Required(message = "The name given by the builder is null")
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/convert/EnumConverter.java b/log4j-plugins/src/test/resources/customplugin/FixedString.java.source
similarity index 52%
rename from log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/convert/EnumConverter.java
rename to log4j-plugins/src/test/resources/customplugin/FixedString.java.source
index 15a162c..85a62ec 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/convert/EnumConverter.java
+++ b/log4j-plugins/src/test/resources/customplugin/FixedString.java.source
@@ -14,25 +14,32 @@
  * See the license for the specific language governing permissions and
  * limitations under the license.
  */
-package org.apache.logging.log4j.core.config.plugins.convert;
 
-import org.apache.logging.log4j.util.EnglishEnums;
+package customplugin;
 
-/**
- * Converts a {@link String} into a {@link Enum}. Returns {@code null} for invalid enum names.
- *
- * @param <E> the enum class to parse.
- * @since 2.1 moved from TypeConverters
- */
-public class EnumConverter<E extends Enum<E>> implements TypeConverter<E> {
-    private final Class<E> clazz;
+import java.util.Collections;
+import java.util.Map;
+
+import org.apache.logging.log4j.plugins.Plugin;
+import org.apache.logging.log4j.plugins.PluginAttribute;
+import org.apache.logging.log4j.plugins.PluginFactory;
+
+@Plugin(name = "FixedString", category = "Core", elementType = "plugin", printObject = true)
+public class FixedString  {
+
+    private String fixedString;
+
+    @PluginFactory
+    public static FixedString create(
+            @PluginAttribute("fixedString") final String fixedString) {
+        return new FixedString(fixedString);
+    }
 
-    public EnumConverter(final Class<E> clazz) {
-        this.clazz = clazz;
+    public FixedString(String fixedString) {
+        this.fixedString = fixedString;
     }
 
-    @Override
-    public E convert(final String s) {
-        return EnglishEnums.valueOf(clazz, s);
+    public Map<String, String> getContentFormat() {
+        return Collections.emptyMap();
     }
 }
diff --git a/log4j-plugins/src/test/resources/log4j+config+with+plus+characters.xml b/log4j-plugins/src/test/resources/log4j+config+with+plus+characters.xml
new file mode 100644
index 0000000..b85475a
--- /dev/null
+++ b/log4j-plugins/src/test/resources/log4j+config+with+plus+characters.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements.  See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License.  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+-->
+<Configuration status="OFF" name="XMLConfigTest">
+
+  <Appenders>
+    <List name="List">
+    </List>
+  </Appenders>
+  <Loggers>
+    <Root level="trace">
+      <AppenderRef ref="List"/>
+    </Root>
+  </Loggers>
+
+</Configuration>
\ No newline at end of file
diff --git a/log4j-plugins/src/test/resources/s p a c e s/log4j+config+with+plus+characters.xml b/log4j-plugins/src/test/resources/s p a c e s/log4j+config+with+plus+characters.xml
new file mode 100644
index 0000000..b85475a
--- /dev/null
+++ b/log4j-plugins/src/test/resources/s p a c e s/log4j+config+with+plus+characters.xml	
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements.  See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License.  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+-->
+<Configuration status="OFF" name="XMLConfigTest">
+
+  <Appenders>
+    <List name="List">
+    </List>
+  </Appenders>
+  <Loggers>
+    <Root level="trace">
+      <AppenderRef ref="List"/>
+    </Root>
+  </Loggers>
+
+</Configuration>
\ No newline at end of file