Index: src/site/xdoc/manual/plugins.xml
===================================================================
--- src/site/xdoc/manual/plugins.xml (revision 1617189)
+++ src/site/xdoc/manual/plugins.xml (working copy)
@@ -38,15 +38,26 @@
In Log4j 2 a plugin is declared by adding a @Plugin annotation to the class declaration. During
initialization the Configuration will invoke the PluginManager to load the built-in Log4j plugins
- as well as any custom plugins. The PluginManager locates plugins by looking in two places:
+ as well as any custom plugins. The PluginManager locates plugins by looking in four places:
- Serialized plugin listing files on the classpath. These files are generated automatically during
the build (more details below).
+ - A comma-separated list of packages specified by the
log4j.plugin.packages
+ system property.
+ - Packages passed to the static
PluginManager.addPackages method (before Log4j configuration
+ occurs).
- The packages declared in your log4j2
configuration file.
+ If multiple Plugins specify the same (case-insensitive) name, then the load order above
+ determines which one will be used. For example, to override the File plugin which is provided
+ by the built-in FileAppender class, you would need to place your plugin in a JAR file in the
+ CLASSPATH ahead oflog4j-core.jar. This is not recommended; plugin name collisions will cause a
+ warning to be emitted.
+
+
Serialized plugin listing files are generated by an annotation processor contained in the
log4j-core artifact which will automatically scan your code for Log4j 2 plugins and output a metadata
file in your processed classes. There is nothing extra that needs to be done to enable this; the Java
@@ -132,6 +143,13 @@
in the same fashion as a LogEventPatternConverter. These Converters are typically used by the
RollingFileAppender to construct the name of the file to log to.
+
+ If multiple Converters specify the same ConverterKeys, then the load order above determines
+ which one will be used. For example, to override the %date converter which is provided by the
+ built-in DatePatternConverter class, you would need to place your plugin in a JAR file in the
+ CLASSPATH ahead oflog4j-core.jar. This is not recommended; pattern ConverterKeys collisions
+ will cause a warning to be emitted. Try to use unique ConverterKeys for your custom pattern converters.
+
Index: log4j-core/src/test/java/org/apache/logging/log4j/core/config/plugins/processor/PluginProcessorTest.java
===================================================================
--- log4j-core/src/test/java/org/apache/logging/log4j/core/config/plugins/processor/PluginProcessorTest.java (revision 1617189)
+++ log4j-core/src/test/java/org/apache/logging/log4j/core/config/plugins/processor/PluginProcessorTest.java (working copy)
@@ -19,7 +19,7 @@
import java.net.URL;
import java.util.Enumeration;
-import java.util.concurrent.ConcurrentMap;
+import java.util.Map;
import org.apache.logging.log4j.core.config.plugins.Plugin;
import org.apache.logging.log4j.core.config.plugins.PluginAliases;
@@ -47,7 +47,7 @@
@Test
public void testTestCategoryFound() throws Exception {
assertNotNull("No plugin annotation on FakePlugin.", p);
- final ConcurrentMap testCategory = pluginCache.getCategory(p.category());
+ final Map testCategory = pluginCache.getCategory(p.category());
assertNotEquals("No plugins were found.", 0, pluginCache.size());
assertNotNull("The category '" + p.category() + "' was not found.", testCategory);
assertFalse(testCategory.isEmpty());
Index: log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/processor/PluginCache.java
===================================================================
--- log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/processor/PluginCache.java (revision 1617189)
+++ log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/processor/PluginCache.java (working copy)
@@ -25,25 +25,33 @@
import java.io.OutputStream;
import java.net.URL;
import java.util.Enumeration;
+import java.util.LinkedHashMap;
import java.util.Map;
-import java.util.concurrent.ConcurrentMap;
-import org.apache.logging.log4j.core.config.plugins.util.PluginRegistry;
-
/**
*
*/
public class PluginCache {
- private final transient PluginRegistry pluginCategories = new PluginRegistry();
+ private final Map> categories =
+ new LinkedHashMap>();
+ public Map> getAllCategories() {
+ return categories;
+ }
+
/**
* Gets or creates a category of plugins.
*
* @param category name of category to look up.
* @return plugin mapping of names to plugin entries.
*/
- public ConcurrentMap getCategory(final String category) {
- return pluginCategories.getCategory(category);
+ public Map getCategory(final String category) {
+ final String key = category.toLowerCase();
+ Map result = categories.get(key);
+ if (result == null) {
+ categories.put(key, result = new LinkedHashMap());
+ }
+ return result;
}
/**
@@ -55,8 +63,10 @@
public void writeCache(final OutputStream os) throws IOException {
final DataOutputStream out = new DataOutputStream(new BufferedOutputStream(os));
try {
- out.writeInt(pluginCategories.getCategoryCount());
- for (final Map.Entry> category : pluginCategories.getCategories()) {
+ // 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> category : categories.entrySet()) {
out.writeUTF(category.getKey());
final Map m = category.getValue();
out.writeInt(m.size());
@@ -81,7 +91,7 @@
* @throws IOException
*/
public void loadCacheFiles(final Enumeration resources) throws IOException {
- pluginCategories.clear();
+ categories.clear();
while (resources.hasMoreElements()) {
final URL url = resources.nextElement();
final DataInputStream in = new DataInputStream(new BufferedInputStream(url.openStream()));
@@ -89,7 +99,7 @@
final int count = in.readInt();
for (int i = 0; i < count; i++) {
final String category = in.readUTF();
- final ConcurrentMap m = pluginCategories.getCategory(category);
+ final Map m = getCategory(category);
final int entries = in.readInt();
for (int j = 0; j < entries; j++) {
final PluginEntry entry = new PluginEntry();
@@ -99,7 +109,9 @@
entry.setPrintable(in.readBoolean());
entry.setDefer(in.readBoolean());
entry.setCategory(category);
- m.putIfAbsent(entry.getKey(), entry);
+ if (!m.containsKey(entry.getKey())) {
+ m.put(entry.getKey(), entry);
+ }
}
}
} finally {
@@ -114,6 +126,6 @@
* @return number of plugin categories in cache.
*/
public int size() {
- return pluginCategories.getCategoryCount();
+ return categories.size();
}
}
Index: log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/processor/PluginProcessor.java
===================================================================
--- log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/processor/PluginProcessor.java (revision 1617189)
+++ log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/processor/PluginProcessor.java (working copy)
@@ -22,8 +22,8 @@
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
+import java.util.Map;
import java.util.Set;
-import java.util.concurrent.ConcurrentMap;
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.RoundEnvironment;
@@ -92,7 +92,7 @@
for (final Element element : elements) {
final Plugin plugin = element.getAnnotation(Plugin.class);
final PluginEntry entry = element.accept(pluginVisitor, plugin);
- final ConcurrentMap category = pluginCache.getCategory(entry.getCategory());
+ final Map category = pluginCache.getCategory(entry.getCategory());
category.put(entry.getKey(), entry);
final Collection entries = element.accept(pluginAliasesVisitor, plugin);
for (final PluginEntry pluginEntry : entries) {
Index: log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/osgi/Activator.java
===================================================================
--- log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/osgi/Activator.java (revision 1617189)
+++ log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/osgi/Activator.java (working copy)
@@ -20,7 +20,7 @@
import java.util.concurrent.atomic.AtomicReference;
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.PluginRegistry;
import org.apache.logging.log4j.core.util.BundleResourceLoader;
import org.apache.logging.log4j.status.StatusLogger;
import org.osgi.framework.Bundle;
@@ -59,13 +59,18 @@
private static void scanBundleForPlugins(final Bundle bundle) {
LOGGER.trace("Scanning bundle [{}] for plugins.", bundle.getSymbolicName());
- PluginManager.loadPlugins(new BundleResourceLoader(bundle));
+ PluginRegistry.INSTANCE.loadFromBundle(bundle.getBundleId(), new BundleResourceLoader(bundle));
}
@Override
public void stop(final BundleContext context) throws Exception {
// not much can be done that isn't already automated by the framework
this.context.compareAndSet(context, null);
+
+ // TODO not sure if we should uninstall Bundle plugins, and if so will this do the trick
+ for (final Bundle bundle : context.getBundles()) {
+ PluginRegistry.INSTANCE.clearBundlePlugins(bundle.getBundleId());
+ }
}
@Override
Index: log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/util/PluginType.java
===================================================================
--- log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/util/PluginType.java (revision 1617189)
+++ log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/util/PluginType.java (working copy)
@@ -17,7 +17,7 @@
package org.apache.logging.log4j.core.config.plugins.util;
-import java.io.Serializable;
+import org.apache.logging.log4j.core.config.plugins.processor.PluginEntry;
/**
* Plugin Descriptor. This is a memento object for Plugin annotations paired to their annotated classes.
@@ -25,20 +25,16 @@
* @param The plug-in class, which can be any kind of class.
* @see org.apache.logging.log4j.core.config.plugins.Plugin
*/
-public class PluginType implements Serializable {
+public class PluginType {
- private static final long serialVersionUID = 4743255148794846612L;
-
+ private final PluginEntry pluginEntry;
private final Class pluginClass;
private final String elementName;
- private final boolean printObject;
- private final boolean deferChildren;
- public PluginType(final Class clazz, final String name, final boolean printObj, final boolean deferChildren) {
- this.pluginClass = clazz;
- this.elementName = name;
- this.printObject = printObj;
- this.deferChildren = deferChildren;
+ public PluginType(final PluginEntry pluginEntry, final Class pluginClass, final String elementName) {
+ this.pluginEntry = pluginEntry;
+ this.pluginClass = pluginClass;
+ this.elementName = elementName;
}
public Class getPluginClass() {
@@ -49,17 +45,30 @@
return this.elementName;
}
+ public String getKey() {
+ return this.pluginEntry.getKey();
+ }
+
public boolean isObjectPrintable() {
- return this.printObject;
+ return this.pluginEntry.isPrintable();
}
public boolean isDeferChildren() {
- return this.deferChildren;
+ return this.pluginEntry.isDefer();
}
+ public String getCategory() {
+ return this.pluginEntry.getCategory();
+ }
+
@Override
public String toString() {
- return "PluginType [pluginClass=" + this.pluginClass + ", elementName=" + this.elementName + ", printObject="
- + this.printObject + ", deferChildren=" + this.deferChildren + "]";
+ return "PluginType [pluginClass=" + pluginClass +
+ ", key=" + pluginEntry.getKey() +
+ ", elementName=" + pluginEntry.getName() +
+ ", isObjectPrintable=" + pluginEntry.isPrintable() +
+ ", isDeferChildren==" + pluginEntry.isDefer() +
+ ", category=" + pluginEntry.getCategory() +
+ "]";
}
}
Index: log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/util/PluginRegistry.java
===================================================================
--- log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/util/PluginRegistry.java (revision 1617189)
+++ log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/util/PluginRegistry.java (working copy)
@@ -17,86 +17,248 @@
package org.apache.logging.log4j.core.config.plugins.util;
-import java.io.Serializable;
-import java.util.Map;
-import java.util.Set;
+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.ClassLoaderResourceLoader;
+import org.apache.logging.log4j.core.util.Loader;
+import org.apache.logging.log4j.core.util.ResourceLoader;
+import org.apache.logging.log4j.status.StatusLogger;
+
+import java.io.*;
+import java.net.URI;
+import java.net.URL;
+import java.text.DecimalFormat;
+import java.util.*;
+import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
/**
- * Registry for PluginType maps partitioned by category names.
- *
- * @param plugin information object such as PluginType or PluginEntry.
+ * Registry singleton for PluginType maps partitioned by source type and then by category names.
*/
-public class PluginRegistry {
- private final ConcurrentMap> categories =
- new ConcurrentHashMap>();
+public class PluginRegistry {
+ public static final PluginRegistry INSTANCE = new PluginRegistry();
+ private final Logger LOGGER = StatusLogger.getLogger();
+
+ /** Contains plugins found in Log4j2Plugins.dat cache files in the main CLASSPATH. */
+ private final AtomicReference