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/09 17:50:07 UTC
[logging-log4j2] branch LOG4J2-2621 updated: LOG4J2-2621 - OSGi
tests pass
This is an automated email from the ASF dual-hosted git repository.
rgoers pushed a commit to branch LOG4J2-2621
in repository https://gitbox.apache.org/repos/asf/logging-log4j2.git
The following commit(s) were added to refs/heads/LOG4J2-2621 by this push:
new b0eb3f9 LOG4J2-2621 - OSGi tests pass
b0eb3f9 is described below
commit b0eb3f950105ebb8389dbc0e87bb4e51a5396910
Author: Ralph Goers <rg...@apache.org>
AuthorDate: Sun Jun 9 10:49:54 2019 -0700
LOG4J2-2621 - OSGi tests pass
---
.../java/org/apache/logging/log4j/LogManager.java | 22 ++++-
.../logging/log4j/spi/LoggerContextFactory.java | 33 ++++++++
log4j-core/pom.xml | 6 ++
.../log4j/core/impl/Log4jContextFactory.java | 22 +++++
.../apache/logging/log4j/core/osgi/Activator.java | 62 +++------------
.../log4j/core/osgi/BundleContextSelector.java | 77 ++++++++++++++++++
.../log4j/core/selector/BasicContextSelector.java | 11 +++
.../core/selector/ClassLoaderContextSelector.java | 47 +++++++++++
.../log4j/core/selector/ContextSelector.java | 31 ++++++++
.../log4j/core/selector/JndiContextSelector.java | 36 ++++++++-
.../org/apache/logging/log4j/core/util/Loader.java | 56 ++++++++-----
.../log4j/osgi/tests/AbstractLoadBundleTest.java | 67 +++++++++++-----
log4j-plugins/pom.xml | 5 +-
.../logging/log4j/plugins/osgi/Activator.java | 93 ++++++++++++++++++----
.../log4j/plugins/processor/PluginService.java | 27 ++++---
.../logging/log4j/plugins/util/PluginRegistry.java | 19 +++++
.../logging/log4j/plugins/util/PluginType.java | 4 +
.../plugins/processor/PluginProcessorTest.java | 26 +++++-
18 files changed, 516 insertions(+), 128 deletions(-)
diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/LogManager.java b/log4j-api/src/main/java/org/apache/logging/log4j/LogManager.java
index ecff443..2da1e84 100644
--- a/log4j-api/src/main/java/org/apache/logging/log4j/LogManager.java
+++ b/log4j-api/src/main/java/org/apache/logging/log4j/LogManager.java
@@ -387,7 +387,27 @@ public class LogManager {
* @since 2.6
*/
public static void shutdown(final boolean currentContext) {
- shutdown(getContext(currentContext));
+ factory.shutdown(FQCN, null, currentContext, false);
+ }
+
+ /**
+ * Shutdown the logging system if the logging system supports it.
+ * This is equivalent to calling {@code LogManager.shutdown(LogManager.getContext(currentContext))}.
+ *
+ * This call is synchronous and will block until shut down is complete.
+ * This may include flushing pending log events over network connections.
+ *
+ * @param currentContext if true a default LoggerContext (may not be the LoggerContext used to create a Logger
+ * for the calling class) will be used.
+ * If false the LoggerContext appropriate for the caller of this method is used. For
+ * example, in a web application if the caller is a class in WEB-INF/lib then one LoggerContext may be
+ * used and if the caller is a class in the container's classpath then a different LoggerContext may
+ * be used.
+ * @param allContexts if true all LoggerContexts that can be located will be shutdown.
+ * @since 3.0
+ */
+ public static void shutdown(final boolean currentContext, final boolean allContexts) {
+ factory.shutdown(FQCN, null, currentContext, allContexts);
}
/**
diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/spi/LoggerContextFactory.java b/log4j-api/src/main/java/org/apache/logging/log4j/spi/LoggerContextFactory.java
index 235ad0c..14aaa37 100644
--- a/log4j-api/src/main/java/org/apache/logging/log4j/spi/LoggerContextFactory.java
+++ b/log4j-api/src/main/java/org/apache/logging/log4j/spi/LoggerContextFactory.java
@@ -17,6 +17,7 @@
package org.apache.logging.log4j.spi;
import java.net.URI;
+import java.util.concurrent.TimeUnit;
/**
* Implemented by factories that create {@link LoggerContext} objects.
@@ -24,6 +25,38 @@ import java.net.URI;
public interface LoggerContextFactory {
/**
+ * Shuts down the LoggerContext.
+ * @param fqcn The fully qualified class name of the caller.
+ * @param loader The ClassLoader to use or null.
+ * @param currentContext If true shuts down the current Context, if false shuts down the Context appropriate
+ * for the caller if a more appropriate Context can be determined.
+ * @param allContexts if true all LoggerContexts that can be located will be shutdown.
+ * @return true if a LoggerContext has been installed, false otherwise.
+ * @since 3.0
+ */
+ default void shutdown(String fqcn, ClassLoader loader, boolean currentContext, boolean allContexts) {
+ if (hasContext(fqcn, loader, currentContext)) {
+ LoggerContext ctx = getContext(fqcn, loader, null, currentContext);
+ if (ctx instanceof Terminable) {
+ ((Terminable) ctx).terminate();
+ }
+ }
+ }
+
+ /**
+ * Checks to see if a LoggerContext is installed. The default implementation returns false.
+ * @param fqcn The fully qualified class name of the caller.
+ * @param loader The ClassLoader to use or null.
+ * @param currentContext If true returns the current Context, if false returns the Context appropriate
+ * for the caller if a more appropriate Context can be determined.
+ * @return true if a LoggerContext has been installed, false otherwise.
+ * @since 3.0
+ */
+ default boolean hasContext(String fqcn, ClassLoader loader, boolean currentContext) {
+ return false;
+ }
+
+ /**
* Creates a {@link LoggerContext}.
*
* @param fqcn The fully qualified class name of the caller.
diff --git a/log4j-core/pom.xml b/log4j-core/pom.xml
index fcabc0b..4ac43c1 100644
--- a/log4j-core/pom.xml
+++ b/log4j-core/pom.xml
@@ -444,6 +444,12 @@
<Import-Package>
sun.reflect;resolution:=optional,
org.apache.logging.log4j.util,
+ org.apache.logging.log4j.plugins,
+ org.apache.logging.log4j.plugins.convert,
+ org.apache.logging.log4j.plugins.processor,
+ org.apache.logging.log4j.plugins.util,
+ org.apache.logging.log4j.plugins.validation,
+ org.apache.logging.log4j.plugins.visitors,
*
</Import-Package>
<Bundle-Activator>org.apache.logging.log4j.core.osgi.Activator</Bundle-Activator>
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/Log4jContextFactory.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/Log4jContextFactory.java
index 42bd52a..e00eb29 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/Log4jContextFactory.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/Log4jContextFactory.java
@@ -36,6 +36,7 @@ import org.apache.logging.log4j.core.util.DefaultShutdownCallbackRegistry;
import org.apache.logging.log4j.core.util.Loader;
import org.apache.logging.log4j.core.util.ShutdownCallbackRegistry;
import org.apache.logging.log4j.spi.LoggerContextFactory;
+import org.apache.logging.log4j.spi.Terminable;
import org.apache.logging.log4j.status.StatusLogger;
import org.apache.logging.log4j.util.LoaderUtil;
import org.apache.logging.log4j.util.PropertiesUtil;
@@ -284,6 +285,27 @@ public class Log4jContextFactory implements LoggerContextFactory, ShutdownCallba
return ctx;
}
+ @Override
+ public void shutdown(String fqcn, ClassLoader loader, boolean currentContext, boolean allContexts) {
+ if (selector.hasContext(fqcn, loader, currentContext)) {
+ selector.shutdown(fqcn, loader, currentContext, allContexts);
+ }
+ }
+
+ /**
+ * Checks to see if a LoggerContext is installed.
+ * @param fqcn The fully qualified class name of the caller.
+ * @param loader The ClassLoader to use or null.
+ * @param currentContext If true returns the current Context, if false returns the Context appropriate
+ * for the caller if a more appropriate Context can be determined.
+ * @return true if a LoggerContext has been installed, false otherwise.
+ * @since 3.0
+ */
+ @Override
+ public boolean hasContext(String fqcn, ClassLoader loader, boolean currentContext) {
+ return selector.hasContext(fqcn, loader, currentContext);
+ }
+
/**
* Returns the ContextSelector.
* @return The ContextSelector.
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/osgi/Activator.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/osgi/Activator.java
index afbefa5..4671f63 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/osgi/Activator.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/osgi/Activator.java
@@ -22,33 +22,33 @@ import java.util.concurrent.atomic.AtomicReference;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
-import org.apache.logging.log4j.plugins.util.PluginRegistry;
import org.apache.logging.log4j.core.impl.Log4jProvider;
+import org.apache.logging.log4j.core.plugins.Log4jPlugins;
import org.apache.logging.log4j.core.util.Constants;
+import org.apache.logging.log4j.plugins.processor.PluginService;
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.Bundle;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
-import org.osgi.framework.BundleEvent;
import org.osgi.framework.ServiceRegistration;
-import org.osgi.framework.SynchronousBundleListener;
-import org.osgi.framework.wiring.BundleWiring;
/**
* OSGi BundleActivator.
*/
-public final class Activator implements BundleActivator, SynchronousBundleListener {
+public final class Activator implements BundleActivator {
private static final Logger LOGGER = StatusLogger.getLogger();
private final AtomicReference<BundleContext> contextRef = new AtomicReference<>();
ServiceRegistration provideRegistration = null;
+ ServiceRegistration pluginRegistration = null;
@Override
public void start(final BundleContext context) throws Exception {
+ pluginRegistration = context.registerService(PluginService.class.getName(), new Log4jPlugins(),
+ new Hashtable<>());
final Provider provider = new Log4jProvider();
final Hashtable<String, String> props = new Hashtable<>();
props.put("APIVersion", "2.60");
@@ -57,58 +57,14 @@ public final class Activator implements BundleActivator, SynchronousBundleListen
if (PropertiesUtil.getProperties().getStringProperty(Constants.LOG4J_CONTEXT_SELECTOR) == null) {
System.setProperty(Constants.LOG4J_CONTEXT_SELECTOR, BundleContextSelector.class.getName());
}
- 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());
+ contextRef.compareAndSet(null, context);
}
@Override
public void stop(final BundleContext context) throws Exception {
provideRegistration.unregister();
+ pluginRegistration.unregister();
this.contextRef.compareAndSet(context, null);
- LogManager.shutdown();
- }
-
- @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;
- }
+ LogManager.shutdown(false, true);
}
}
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/osgi/BundleContextSelector.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/osgi/BundleContextSelector.java
index 5e22922..7cb433f 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/osgi/BundleContextSelector.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/osgi/BundleContextSelector.java
@@ -19,6 +19,7 @@ package org.apache.logging.log4j.core.osgi;
import java.lang.ref.WeakReference;
import java.net.URI;
import java.util.Objects;
+import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import org.apache.logging.log4j.core.LoggerContext;
@@ -39,6 +40,76 @@ import org.osgi.framework.FrameworkUtil;
public class BundleContextSelector extends ClassLoaderContextSelector {
@Override
+ public void shutdown(final String fqcn, final ClassLoader loader, final boolean currentContext,
+ final boolean allContexts) {
+ LoggerContext ctx = null;
+ Bundle bundle = null;
+ if (currentContext) {
+ ctx = ContextAnchor.THREAD_CONTEXT.get();
+ ContextAnchor.THREAD_CONTEXT.remove();
+ }
+ if (ctx == null && loader instanceof BundleReference) {
+ bundle = ((BundleReference) loader).getBundle();
+ ctx = getLoggerContext(bundle);
+ removeLoggerContext(ctx);
+ }
+ if (ctx == null) {
+ final Class<?> callerClass = StackLocatorUtil.getCallerClass(fqcn);
+ if (callerClass != null) {
+ bundle = FrameworkUtil.getBundle(callerClass);
+ ctx = getLoggerContext(FrameworkUtil.getBundle(callerClass));
+ removeLoggerContext(ctx);
+ }
+ }
+ if (ctx == null) {
+ ctx = ContextAnchor.THREAD_CONTEXT.get();
+ ContextAnchor.THREAD_CONTEXT.remove();
+ }
+ if (ctx != null) {
+ ctx.stop(DEFAULT_STOP_TIMEOUT, TimeUnit.MILLISECONDS);
+ }
+ if (bundle != null && allContexts) {
+ final Bundle[] bundles = bundle.getBundleContext().getBundles();
+ for (final Bundle bdl : bundles) {
+ ctx = getLoggerContext(bdl);
+ if (ctx != null) {
+ ctx.stop(DEFAULT_STOP_TIMEOUT, TimeUnit.MILLISECONDS);
+ }
+ }
+ }
+
+
+ }
+
+ private LoggerContext getLoggerContext(final Bundle bundle) {
+ final String name = Objects.requireNonNull(bundle, "No Bundle provided").getSymbolicName();
+ final AtomicReference<WeakReference<LoggerContext>> ref = CONTEXT_MAP.get(name);
+ if (ref != null && ref.get() != null) {
+ return ref.get().get();
+ }
+ return null;
+ }
+
+ private void removeLoggerContext(LoggerContext context) {
+ CONTEXT_MAP.remove(context.getName());
+ }
+
+ @Override
+ public boolean hasContext(final String fqcn, final ClassLoader loader, final boolean currentContext) {
+ if (currentContext && ContextAnchor.THREAD_CONTEXT.get() != null) {
+ return true;
+ }
+ if (loader instanceof BundleReference) {
+ return hasContext(((BundleReference) loader).getBundle());
+ }
+ final Class<?> callerClass = StackLocatorUtil.getCallerClass(fqcn);
+ if (callerClass != null) {
+ return hasContext(FrameworkUtil.getBundle(callerClass));
+ }
+ return ContextAnchor.THREAD_CONTEXT.get() != null;
+ }
+
+ @Override
public LoggerContext getContext(final String fqcn, final ClassLoader loader, final boolean currentContext,
final URI configLocation) {
if (currentContext) {
@@ -60,6 +131,12 @@ public class BundleContextSelector extends ClassLoaderContextSelector {
return lc == null ? getDefault() : lc;
}
+ private static boolean hasContext(final Bundle bundle) {
+ final String name = Objects.requireNonNull(bundle, "No Bundle provided").getSymbolicName();
+ final AtomicReference<WeakReference<LoggerContext>> ref = CONTEXT_MAP.get(name);
+ return ref != null && ref.get() != null;
+ }
+
private static LoggerContext locateContext(final Bundle bundle, final URI configLocation) {
final String name = Objects.requireNonNull(bundle, "No Bundle provided").getSymbolicName();
final AtomicReference<WeakReference<LoggerContext>> ref = CONTEXT_MAP.get(name);
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/selector/BasicContextSelector.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/selector/BasicContextSelector.java
index 502084d..1006a60 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/selector/BasicContextSelector.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/selector/BasicContextSelector.java
@@ -20,6 +20,7 @@ import java.net.URI;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
+import java.util.concurrent.TimeUnit;
import org.apache.logging.log4j.core.LoggerContext;
import org.apache.logging.log4j.core.impl.ContextAnchor;
@@ -32,6 +33,16 @@ public class BasicContextSelector implements ContextSelector {
private static final LoggerContext CONTEXT = new LoggerContext("Default");
@Override
+ public void shutdown(String fqcn, ClassLoader loader, boolean currentContext, boolean allContexts) {
+ ContextAnchor.THREAD_CONTEXT.get().stop(DEFAULT_STOP_TIMEOUT, TimeUnit.MILLISECONDS);
+ }
+
+ @Override
+ public boolean hasContext(String fqcn, ClassLoader loader, boolean currentContext) {
+ return ContextAnchor.THREAD_CONTEXT.get() != null;
+ }
+
+ @Override
public LoggerContext getContext(final String fqcn, final ClassLoader loader, final boolean currentContext) {
final LoggerContext ctx = ContextAnchor.THREAD_CONTEXT.get();
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/selector/ClassLoaderContextSelector.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/selector/ClassLoaderContextSelector.java
index 8ffe5a5..3204e7e 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/selector/ClassLoaderContextSelector.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/selector/ClassLoaderContextSelector.java
@@ -25,6 +25,7 @@ import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import org.apache.logging.log4j.core.LoggerContext;
@@ -53,6 +54,52 @@ public class ClassLoaderContextSelector implements ContextSelector {
new ConcurrentHashMap<>();
@Override
+ public void shutdown(final String fqcn, final ClassLoader loader, final boolean currentContext,
+ final boolean allContexts) {
+ LoggerContext ctx = null;
+ if (currentContext) {
+ ctx = ContextAnchor.THREAD_CONTEXT.get();
+ } else if (loader != null) {
+ ctx = findContext(loader);
+ } else {
+ final Class<?> clazz = StackLocatorUtil.getCallerClass(fqcn);
+ if (clazz != null) {
+ ctx = findContext(clazz.getClassLoader());
+ }
+ ctx = ContextAnchor.THREAD_CONTEXT.get();
+ }
+ if (ctx != null) {
+ ctx.stop(DEFAULT_STOP_TIMEOUT, TimeUnit.MILLISECONDS);
+ }
+ }
+
+ @Override
+ public boolean hasContext(final String fqcn, final ClassLoader loader, final boolean currentContext) {
+ if (currentContext) {
+ return ContextAnchor.THREAD_CONTEXT.get() != null;
+ } else if (loader != null) {
+ return findContext(loader) != null;
+ } else {
+ final Class<?> clazz = StackLocatorUtil.getCallerClass(fqcn);
+ if (clazz != null) {
+ return findContext(clazz.getClassLoader()) != null;
+ }
+ return ContextAnchor.THREAD_CONTEXT.get() != null;
+ }
+ }
+
+ private LoggerContext findContext(ClassLoader loaderOrNull) {
+ final ClassLoader loader = loaderOrNull != null ? loaderOrNull : ClassLoader.getSystemClassLoader();
+ final String name = toContextMapKey(loader);
+ AtomicReference<WeakReference<LoggerContext>> ref = CONTEXT_MAP.get(name);
+ if (ref != null) {
+ final WeakReference<LoggerContext> weakRef = ref.get();
+ return weakRef.get();
+ }
+ return null;
+ }
+
+ @Override
public LoggerContext getContext(final String fqcn, final ClassLoader loader, final boolean currentContext) {
return getContext(fqcn, loader, currentContext, null);
}
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/selector/ContextSelector.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/selector/ContextSelector.java
index 65c4dd7..41f7d39 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/selector/ContextSelector.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/selector/ContextSelector.java
@@ -18,6 +18,7 @@ package org.apache.logging.log4j.core.selector;
import java.net.URI;
import java.util.List;
+import java.util.concurrent.TimeUnit;
import org.apache.logging.log4j.core.LoggerContext;
@@ -26,6 +27,36 @@ import org.apache.logging.log4j.core.LoggerContext;
*/
public interface ContextSelector {
+ long DEFAULT_STOP_TIMEOUT = 50;
+
+ /**
+ * Shuts down the LoggerContext.
+ * @param fqcn The fully qualified class name of the caller.
+ * @param loader The ClassLoader to use or null.
+ * @param currentContext If true returns the current Context, if false returns the Context appropriate
+ * @param allContexts if true all LoggerContexts that can be located will be shutdown.
+ * @since 3.0
+ */
+ default void shutdown(final String fqcn, final ClassLoader loader, final boolean currentContext,
+ final boolean allContexts) {
+ if (hasContext(fqcn, loader, currentContext)) {
+ getContext(fqcn, loader, currentContext).stop(DEFAULT_STOP_TIMEOUT, TimeUnit.MILLISECONDS);
+ }
+ }
+
+ /**
+ * Checks to see if a LoggerContext is installed. The default implementation returns false.
+ * @param fqcn The fully qualified class name of the caller.
+ * @param loader The ClassLoader to use or null.
+ * @param currentContext If true returns the current Context, if false returns the Context appropriate
+ * for the caller if a more appropriate Context can be determined.
+ * @return true if a LoggerContext has been installed, false otherwise.
+ * @since 3.0
+ */
+ default boolean hasContext(String fqcn, ClassLoader loader, boolean currentContext) {
+ return false;
+ }
+
/**
* Returns the LoggerContext.
* @param fqcn The fully qualified class name of the caller.
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/selector/JndiContextSelector.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/selector/JndiContextSelector.java
index b790eaf..67c32ae 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/selector/JndiContextSelector.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/selector/JndiContextSelector.java
@@ -23,6 +23,7 @@ import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.TimeUnit;
import javax.naming.NamingException;
import org.apache.logging.log4j.core.LoggerContext;
@@ -93,6 +94,32 @@ public class JndiContextSelector implements NamedContextSelector {
private static final StatusLogger LOGGER = StatusLogger.getLogger();
@Override
+ public void shutdown(String fqcn, ClassLoader loader, boolean currentContext, boolean allContexts) {
+ LoggerContext ctx = ContextAnchor.THREAD_CONTEXT.get();
+ if (ctx == null) {
+ String loggingContextName = getContextName();
+ if (loggingContextName != null) {
+ ctx = CONTEXT_MAP.get(loggingContextName);
+ }
+ }
+ if (ctx != null) {
+ ctx.stop(DEFAULT_STOP_TIMEOUT, TimeUnit.MILLISECONDS);
+ }
+ }
+
+ @Override
+ public boolean hasContext(String fqcn, ClassLoader loader, boolean currentContext) {
+ if (ContextAnchor.THREAD_CONTEXT.get() != null) {
+ return true;
+ }
+ String loggingContextName = getContextName();
+ if (loggingContextName == null) {
+ return false;
+ }
+ return CONTEXT_MAP.containsKey(loggingContextName);
+ }
+
+ @Override
public LoggerContext getContext(final String fqcn, final ClassLoader loader, final boolean currentContext) {
return getContext(fqcn, loader, currentContext, null);
}
@@ -106,6 +133,12 @@ public class JndiContextSelector implements NamedContextSelector {
return lc;
}
+ String loggingContextName = getContextName();
+
+ return loggingContextName == null ? CONTEXT : locateContext(loggingContextName, null, configLocation);
+ }
+
+ private String getContextName() {
String loggingContextName = null;
try (final JndiManager jndiManager = JndiManager.getDefaultManager()) {
@@ -113,8 +146,7 @@ public class JndiContextSelector implements NamedContextSelector {
} catch (final NamingException ne) {
LOGGER.error("Unable to lookup {}", Constants.JNDI_CONTEXT_NAME, ne);
}
-
- return loggingContextName == null ? CONTEXT : locateContext(loggingContextName, null, configLocation);
+ return loggingContextName;
}
@Override
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/util/Loader.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/util/Loader.java
index 009116d..86932f6 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/util/Loader.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/util/Loader.java
@@ -34,6 +34,9 @@ public final class Loader {
private static final String TSTR = "Caught Exception while in Loader.getResource. This may be innocuous.";
+ final static Boolean ignoreTccl =
+ Boolean.valueOf(PropertiesUtil.getProperties().getStringProperty(LoaderUtil.IGNORE_TCCL_PROPERTY, null));
+
private Loader() {
}
@@ -42,7 +45,7 @@ public final class Loader {
* @return the ClassLoader.
*/
public static ClassLoader getClassLoader() {
- return LoaderUtil.getClassLoader(LoaderUtil.class, null);
+ return Loader.getClassLoader(Loader.class, null);
}
// TODO: this method could use some explanation
@@ -241,21 +244,12 @@ public final class Loader {
* @throws InstantiationException if there was an exception whilst instantiating the class
* @throws NoSuchMethodException if there isn't a no-args constructor on the class
* @throws InvocationTargetException if there was an exception whilst constructing the class
+ * @since 2.1
*/
@SuppressWarnings("unchecked")
- public static <T> T newInstanceOf(final String className)
- throws ClassNotFoundException,
- IllegalAccessException,
- InstantiationException,
- NoSuchMethodException,
- InvocationTargetException {
- ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
- try {
- Thread.currentThread().setContextClassLoader(getClassLoader());
- return LoaderUtil.newInstanceOf(className);
- } finally {
- Thread.currentThread().setContextClassLoader(contextClassLoader);
- }
+ public static <T> T newInstanceOf(final String className) throws ClassNotFoundException, IllegalAccessException,
+ InstantiationException, NoSuchMethodException, InvocationTargetException {
+ return newInstanceOf((Class<T>) loadClass(className));
}
/**
@@ -281,7 +275,7 @@ public final class Loader {
ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
try {
Thread.currentThread().setContextClassLoader(getClassLoader());
- return LoaderUtil.newCheckedInstanceOf(className, clazz);
+ return clazz.cast(newInstanceOf(className));
} finally {
Thread.currentThread().setContextClassLoader(contextClassLoader);
}
@@ -315,6 +309,26 @@ public final class Loader {
}
/**
+ * Loads and instantiates a Class using the default constructor.
+ *
+ * @param clazz The class.
+ * @return new instance of the class.
+ * @throws IllegalAccessException if the class can't be instantiated through a public constructor
+ * @throws InstantiationException if there was an exception whilst instantiating the class
+ * @throws InvocationTargetException if there was an exception whilst constructing the class
+ * @since 2.7
+ */
+ public static <T> T newInstanceOf(final Class<T> clazz)
+ throws InstantiationException, IllegalAccessException, InvocationTargetException {
+ try {
+ return clazz.getConstructor().newInstance();
+ } catch (final NoSuchMethodException ignored) {
+ // FIXME: looking at the code for Class.newInstance(), this seems to do the same thing as above
+ return clazz.newInstance();
+ }
+ }
+
+ /**
* Determines if a named Class can be loaded or not.
*
* @param className The class name.
@@ -343,13 +357,13 @@ public final class Loader {
* @throws ClassNotFoundException if the specified class name could not be found
*/
public static Class<?> loadClass(final String className) throws ClassNotFoundException {
-
- ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
+ if (ignoreTccl) {
+ return Class.forName(className);
+ }
try {
- Thread.currentThread().setContextClassLoader(getClassLoader());
- return LoaderUtil.loadClass(className);
- } finally {
- Thread.currentThread().setContextClassLoader(contextClassLoader);
+ return getClassLoader().loadClass(className);
+ } catch (final Throwable ignored) {
+ return Class.forName(className);
}
}
}
diff --git a/log4j-osgi/src/test/java/org/apache/logging/log4j/osgi/tests/AbstractLoadBundleTest.java b/log4j-osgi/src/test/java/org/apache/logging/log4j/osgi/tests/AbstractLoadBundleTest.java
index cd7ba25..3d09f6b 100644
--- a/log4j-osgi/src/test/java/org/apache/logging/log4j/osgi/tests/AbstractLoadBundleTest.java
+++ b/log4j-osgi/src/test/java/org/apache/logging/log4j/osgi/tests/AbstractLoadBundleTest.java
@@ -37,6 +37,11 @@ public abstract class AbstractLoadBundleTest extends AbstractOsgiTest {
return getBundleContext().installBundle(apiPath.toUri().toString());
}
+ private Bundle getPluginsBundle() throws BundleException {
+ final Path apiPath = getHere().resolveSibling("log4j-plugins").resolve("target").resolve(getBundleTestInfo().buildJarFileName("log4j-plugins"));
+ return getBundleContext().installBundle(apiPath.toUri().toString());
+ }
+
private Bundle getCoreBundle() throws BundleException {
final Path corePath = getHere().resolveSibling("log4j-core").resolve("target").resolve(getBundleTestInfo().buildJarFileName("log4j-core"));
@@ -93,21 +98,24 @@ public abstract class AbstractLoadBundleTest extends AbstractOsgiTest {
return oldStream;
}
- private void start(final Bundle api, final Bundle core, final Bundle dummy) throws BundleException {
+ private void start(final Bundle api, final Bundle plugins, final Bundle core, final Bundle dummy) throws BundleException {
api.start();
+ plugins.start();
core.start();
dummy.start();
}
- private void stop(final Bundle api, final Bundle core, final Bundle dummy) throws BundleException {
+ private void stop(final Bundle api, final Bundle plugins, final Bundle core, final Bundle dummy) throws BundleException {
dummy.stop();
core.stop();
+ plugins.stop();
api.stop();
}
- private void uninstall(final Bundle api, final Bundle core, final Bundle dummy) throws BundleException {
+ private void uninstall(final Bundle api, final Bundle plugins, final Bundle core, final Bundle dummy) throws BundleException {
dummy.uninstall();
core.uninstall();
+ plugins.uninstall();
api.uninstall();
}
@@ -118,39 +126,51 @@ public abstract class AbstractLoadBundleTest extends AbstractOsgiTest {
public void testApiCoreStartStopStartStop() throws BundleException {
final Bundle api = getApiBundle();
+ final Bundle plugins = getPluginsBundle();
final Bundle core = getCoreBundle();
Assert.assertEquals("api is not in INSTALLED state", Bundle.INSTALLED, api.getState());
+ Assert.assertEquals("plugins is not in INSTALLED state", Bundle.INSTALLED, plugins.getState());
Assert.assertEquals("core is not in INSTALLED state", Bundle.INSTALLED, core.getState());
api.start();
+ plugins.start();
core.start();
- Assert.assertEquals("api is not in ACTIVE state", Bundle.ACTIVE, api.getState());
+ Assert.assertEquals("api is not in ACTIVE state", Bundle.ACTIVE, api.getState());
+ Assert.assertEquals("plugins is not in ACTIVE state", Bundle.ACTIVE, plugins.getState());
Assert.assertEquals("core is not in ACTIVE state", Bundle.ACTIVE, core.getState());
core.stop();
+ plugins.stop();
api.stop();
Assert.assertEquals("api is not in RESOLVED state", Bundle.RESOLVED, api.getState());
+ Assert.assertEquals("plugins is not in RESOLVED state", Bundle.RESOLVED, plugins.getState());
Assert.assertEquals("core is not in RESOLVED state", Bundle.RESOLVED, core.getState());
-
+
api.start();
+ plugins.start();
core.start();
-
- Assert.assertEquals("api is not in ACTIVE state", Bundle.ACTIVE, api.getState());
- Assert.assertEquals("core is not in ACTIVE state", Bundle.ACTIVE, core.getState());
-
+
+ Assert.assertEquals("api is not in ACTIVE state", Bundle.ACTIVE, api.getState());
+ Assert.assertEquals("plugins is not in ACTIVE state", Bundle.ACTIVE, plugins.getState());
+ Assert.assertEquals("core is not in ACTIVE state", Bundle.ACTIVE, core.getState());
+
core.stop();
+ plugins.stop();
api.stop();
-
+
Assert.assertEquals("api is not in RESOLVED state", Bundle.RESOLVED, api.getState());
+ Assert.assertEquals("plugins is not in RESOLVED state", Bundle.RESOLVED, plugins.getState());
Assert.assertEquals("core is not in RESOLVED state", Bundle.RESOLVED, core.getState());
core.uninstall();
+ plugins.uninstall();
api.uninstall();
Assert.assertEquals("api is not in UNINSTALLED state", Bundle.UNINSTALLED, api.getState());
+ Assert.assertEquals("plugins is not in UNINSTALLED state", Bundle.UNINSTALLED, plugins.getState());
Assert.assertEquals("core is not in UNINSTALLED state", Bundle.UNINSTALLED, core.getState());
}
@@ -161,9 +181,11 @@ public abstract class AbstractLoadBundleTest extends AbstractOsgiTest {
public void testClassNotFoundErrorLogger() throws BundleException {
final Bundle api = getApiBundle();
+ final Bundle plugins = getPluginsBundle();
final Bundle core = getCoreBundle();
api.start();
+ plugins.start();
// fails if LOG4J2-1637 is not fixed
try {
core.start();
@@ -187,9 +209,11 @@ public abstract class AbstractLoadBundleTest extends AbstractOsgiTest {
}
core.stop();
+ plugins.stop();
api.stop();
core.uninstall();
+ plugins.uninstall();
api.uninstall();
}
@@ -200,10 +224,11 @@ public abstract class AbstractLoadBundleTest extends AbstractOsgiTest {
public void testLoadingOfConfigurableCoreClasses() throws BundleException, ReflectiveOperationException {
final Bundle api = getApiBundle();
+ final Bundle plugins = getPluginsBundle();
final Bundle core = getCoreBundle();
final Bundle dummy = getDummyBundle();
- start(api, core, dummy);
+ start(api, plugins, core, dummy);
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
final PrintStream logStream = new PrintStream(baos);
@@ -218,8 +243,8 @@ public abstract class AbstractLoadBundleTest extends AbstractOsgiTest {
final boolean result = baos.toString().contains("BundleContextSelector cannot be found");
Assert.assertFalse("Core class BundleContextSelector cannot be loaded in OSGI setup", result);
- stop(api, core, dummy);
- uninstall(api, core, dummy);
+ stop(api, plugins, core, dummy);
+ uninstall(api, plugins, core, dummy);
}
/**
@@ -229,10 +254,11 @@ public abstract class AbstractLoadBundleTest extends AbstractOsgiTest {
public void testSimpleLogInAnOsgiContext() throws BundleException, ReflectiveOperationException {
final Bundle api = getApiBundle();
+ final Bundle plugins = getPluginsBundle();
final Bundle core = getCoreBundle();
final Bundle dummy = getDummyBundle();
- start(api, core, dummy);
+ start(api, plugins, core, dummy);
final PrintStream bakStream = System.out;
try {
@@ -244,14 +270,15 @@ public abstract class AbstractLoadBundleTest extends AbstractOsgiTest {
final String result = baos.toString().substring(
12).trim(); // remove the instant then the spaces at start and end, that are non constant
- Assert.assertEquals("[main] ERROR org.apache.logging.log4j.configuration.CustomConfiguration - Test OK",
- result);
+ String expected = "[main] ERROR org.apache.logging.log4j.configuration.CustomConfiguration - Test OK";
+ Assert.assertTrue("Incorrect string. Expected string ends woth: " + expected + " Actual: " + result,
+ result.endsWith(expected));
} finally {
System.setOut(bakStream);
}
- stop(api, core, dummy);
- uninstall(api, core, dummy);
+ stop(api, plugins, core, dummy);
+ uninstall(api, plugins, core, dummy);
}
@@ -263,10 +290,12 @@ public abstract class AbstractLoadBundleTest extends AbstractOsgiTest {
public void testLog4J12Fragement() throws BundleException, ReflectiveOperationException {
final Bundle api = getApiBundle();
+ final Bundle plugins = getPluginsBundle();
final Bundle core = getCoreBundle();
final Bundle compat = get12ApiBundle();
api.start();
+ plugins.start();
core.start();
final Class<?> coreClassFromCore = core.loadClass("org.apache.logging.log4j.core.Core");
@@ -279,7 +308,7 @@ public abstract class AbstractLoadBundleTest extends AbstractOsgiTest {
core.stop();
api.stop();
- uninstall(api, core, compat);
+ uninstall(api, plugins, core, compat);
}
}
diff --git a/log4j-plugins/pom.xml b/log4j-plugins/pom.xml
index 3551d51..5008c07 100644
--- a/log4j-plugins/pom.xml
+++ b/log4j-plugins/pom.xml
@@ -224,9 +224,10 @@
<!-- 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,
+ org.apache.logging.log4j.status,
org.apache.logging.log4j.util,
- *
+ org.osgi.framework.*
</Import-Package>
<Bundle-Activator>org.apache.logging.log4j.plugins.osgi.Activator</Bundle-Activator>
</instructions>
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
index 06fb124..2364aa9 100644
--- 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
@@ -17,16 +17,16 @@
package org.apache.logging.log4j.plugins.osgi;
-import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Level;
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.security.Permission;
+import java.util.Collection;
import java.util.Hashtable;
import java.util.concurrent.atomic.AtomicReference;
@@ -37,21 +37,77 @@ public final class Activator implements BundleActivator, SynchronousBundleListen
private static final Logger LOGGER = StatusLogger.getLogger();
- private final AtomicReference<BundleContext> contextRef = new AtomicReference<>();
+ private static final SecurityManager SECURITY_MANAGER = System.getSecurityManager();
- ServiceRegistration provideRegistration = null;
+ private final AtomicReference<BundleContext> contextRef = new AtomicReference<>();
@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);
- } */
+ public void start(final BundleContext bundleContext) throws Exception {
+ loadPlugins(bundleContext);
+ bundleContext.addBundleListener(this);
+ final Bundle[] bundles = bundleContext.getBundles();
+ for (final Bundle bundle : bundles) {
+ loadPlugins(bundle);
+ }
+ scanInstalledBundlesForPlugins(bundleContext);
+ this.contextRef.compareAndSet(null, bundleContext);
+ }
+
+ private void loadPlugins(final BundleContext bundleContext) {
+ try {
+ final Collection<ServiceReference<PluginService>> serviceReferences = bundleContext.getServiceReferences(PluginService.class, null);
+ for (final ServiceReference<PluginService> serviceReference : serviceReferences) {
+ final PluginService pluginService = bundleContext.getService(serviceReference);
+ PluginRegistry.getInstance().loadFromBundle(pluginService.getCategories(), bundleContext.getBundle().getBundleId());
+ }
+ } catch (final InvalidSyntaxException ex) {
+ LOGGER.error("Error accessing Plugins", ex);
+ }
+ }
+
+ private void loadPlugins(final Bundle bundle) {
+ if (bundle.getState() == Bundle.UNINSTALLED) {
+ return;
+ }
+ try {
+ checkPermission(new AdminPermission(bundle, AdminPermission.RESOURCE));
+ checkPermission(new AdaptPermission(BundleWiring.class.getName(), bundle, AdaptPermission.ADAPT));
+ final BundleContext bundleContext = bundle.getBundleContext();
+ if (bundleContext == null) {
+ LOGGER.debug("Bundle {} has no context (state={}), skipping loading plugins", bundle.getSymbolicName(), toStateString(bundle.getState()));
+ } else {
+ loadPlugins(bundleContext);
+ }
+ } catch (final SecurityException e) {
+ LOGGER.debug("Cannot access bundle [{}] contents. Ignoring.", bundle.getSymbolicName(), e);
+ } catch (final Exception e) {
+ LOGGER.warn("Problem checking bundle {} for Log4j 2 provider.", bundle.getSymbolicName(), e);
+ }
+ }
+
+ private static void checkPermission(final Permission permission) {
+ if (SECURITY_MANAGER != null) {
+ SECURITY_MANAGER.checkPermission(permission);
+ }
+ }
+
+ private String toStateString(final int state) {
+ switch (state) {
+ case Bundle.UNINSTALLED:
+ return "UNINSTALLED";
+ case Bundle.INSTALLED:
+ return "INSTALLED";
+ case Bundle.RESOLVED:
+ return "RESOLVED";
+ case Bundle.STARTING:
+ return "STARTING";
+ case Bundle.STOPPING:
+ return "STOPPING";
+ case Bundle.ACTIVE:
+ return "ACTIVE";
+ default:
+ return Integer.toString(state);
+ }
}
private static void scanInstalledBundlesForPlugins(final BundleContext context) {
@@ -80,7 +136,11 @@ public final class Activator implements BundleActivator, SynchronousBundleListen
@Override
public void stop(final BundleContext context) throws Exception {
- provideRegistration.unregister();
+ final Bundle[] bundles = context.getBundles();
+ for (final Bundle bundle : bundles) {
+ stopBundlePlugins(bundle);
+ }
+ stopBundlePlugins(context.getBundle());
this.contextRef.compareAndSet(context, null);
}
@@ -89,6 +149,7 @@ public final class Activator implements BundleActivator, SynchronousBundleListen
switch (event.getType()) {
// FIXME: STARTING instead of STARTED?
case BundleEvent.STARTED:
+ loadPlugins(event.getBundle());
scanBundleForPlugins(event.getBundle());
break;
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
index 5042456..ae9d094 100644
--- 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
@@ -16,37 +16,44 @@
*/
package org.apache.logging.log4j.plugins.processor;
-import java.util.Collections;
-import java.util.LinkedHashMap;
-import java.util.Map;
+import org.apache.logging.log4j.plugins.util.PluginType;
+
+import java.util.*;
/**
* Class Description goes here.
*/
public abstract class PluginService {
- private final Map<String, Map<String, PluginEntry>> categories = new LinkedHashMap<>();
+ private final Map<String, List<PluginType<?>>> 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<>());
+ categories.put(category, new LinkedList<>());
+ }
+ try {
+ Class<?> clazz = this.getClass().getClassLoader().loadClass(entry.getClassName());
+ List<PluginType<?>> list = categories.get(category);
+ PluginType<?> type = new PluginType<>(entry, clazz, entry.getName());
+ list.add(type);
+ } catch (ClassNotFoundException ex) {
+ throw new IllegalStateException("No class named " + entry.getClassName() +
+ " located for element " + entry.getName(), ex);
}
- Map<String, PluginEntry> map = categories.get(category);
- map.put(entry.getKey(), entry);
}
}
public abstract PluginEntry[] getEntries();
- public Map<String, Map<String, PluginEntry>> getCategories() {
+ public Map<String, List<PluginType<?>>> getCategories() {
return Collections.unmodifiableMap(categories);
}
- public Map<String, PluginEntry> getCategory(String category) {
- return Collections.unmodifiableMap(categories.get(category.toLowerCase()));
+ public List<PluginType<?>> getCategory(String category) {
+ return Collections.unmodifiableList(categories.get(category.toLowerCase()));
}
public long size() {
diff --git a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/util/PluginRegistry.java b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/util/PluginRegistry.java
index 9e8c6e2..a759cf3 100644
--- a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/util/PluginRegistry.java
+++ b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/util/PluginRegistry.java
@@ -36,6 +36,8 @@ import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicReference;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
/**
* Registry singleton for PluginType maps partitioned by source type and then by category names.
@@ -46,6 +48,7 @@ public class PluginRegistry {
private static volatile PluginRegistry INSTANCE;
private static final Object INSTANCE_LOCK = new Object();
+ protected static final Lock STARTUP_LOCK = new ReentrantLock();
/**
* Contains plugins found in Log4j2Plugins.dat cache files in the main CLASSPATH.
@@ -154,6 +157,22 @@ public class PluginRegistry {
}
/**
+ * Loads all the plugins in a Bundle.
+ * @param categories All the categories in the bundle.
+ * @since 3.0
+ */
+ public void loadFromBundle(Map<String, List<PluginType<?>>> categories, Long bundleId) {
+ pluginsByCategoryByBundleId.put(bundleId, categories);
+ for (Map.Entry<String, List<PluginType<?>>> entry: categories.entrySet()) {
+ if (!categories.containsKey(entry.getKey())) {
+
+ categories.put(entry.getKey(), new LinkedList<>());
+ }
+ categories.get(entry.getKey()).addAll(entry.getValue());
+ }
+ }
+
+ /**
* @since 3.0
*/
public void loadPlugins(Map<String, List<PluginType<?>>> map) {
diff --git a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/util/PluginType.java b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/util/PluginType.java
index c213f64..cd3fa49 100644
--- a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/util/PluginType.java
+++ b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/util/PluginType.java
@@ -40,6 +40,10 @@ public class PluginType<T> {
this.elementName = elementName;
}
+ public PluginEntry getPluginEntry() {
+ return this.pluginEntry;
+ }
+
public Class<T> getPluginClass() {
return this.pluginClass;
}
diff --git a/log4j-plugins/src/test/java/org/apache/logging/log4j/plugins/processor/PluginProcessorTest.java b/log4j-plugins/src/test/java/org/apache/logging/log4j/plugins/processor/PluginProcessorTest.java
index 034705c..b3bd425 100644
--- a/log4j-plugins/src/test/java/org/apache/logging/log4j/plugins/processor/PluginProcessorTest.java
+++ b/log4j-plugins/src/test/java/org/apache/logging/log4j/plugins/processor/PluginProcessorTest.java
@@ -19,6 +19,7 @@ package org.apache.logging.log4j.plugins.processor;
import org.apache.logging.log4j.plugins.Plugin;
import org.apache.logging.log4j.plugins.PluginAliases;
+import org.apache.logging.log4j.plugins.util.PluginType;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -49,7 +50,7 @@ public class PluginProcessorTest {
@Test
public void testTestCategoryFound() throws Exception {
assertNotNull("No plugin annotation on FakePlugin.", p);
- final Map<String, PluginEntry> testCategory = pluginService.getCategory(p.category());
+ final List<PluginType<?>> 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());
@@ -57,7 +58,10 @@ public class PluginProcessorTest {
@Test
public void testFakePluginFoundWithCorrectInformation() throws Exception {
- final PluginEntry fake = pluginService.getCategory(p.category()).get(p.name().toLowerCase());
+ final List<PluginType<?>> list = pluginService.getCategory(p.category());
+ assertNotNull(list);
+ final PluginEntry fake = getEntry(list, p.name());
+ assertNotNull(fake);
verifyFakePluginEntry(p.name(), fake);
}
@@ -65,7 +69,10 @@ 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 = pluginService.getCategory(p.category()).get(alias.toLowerCase());
+ final List<PluginType<?>> list = pluginService.getCategory(p.category());
+ assertNotNull(list);
+ final PluginEntry fake = getEntry(list, alias);
+ assertNotNull(fake);
verifyFakePluginEntry(alias, fake);
}
}
@@ -83,7 +90,9 @@ public class PluginProcessorTest {
@Test
public void testNestedPlugin() throws Exception {
final Plugin p = FakePlugin.Nested.class.getAnnotation(Plugin.class);
- final PluginEntry nested = pluginService.getCategory(p.category()).get(p.name().toLowerCase());
+ final List<PluginType<?>> list = pluginService.getCategory(p.category());
+ assertNotNull(list);
+ final PluginEntry nested = getEntry(list, p.name());
assertNotNull(nested);
assertEquals(p.name().toLowerCase(), nested.getKey());
assertEquals(FakePlugin.Nested.class.getName(), nested.getClassName());
@@ -92,4 +101,13 @@ public class PluginProcessorTest {
assertEquals(p.printObject(), nested.isPrintable());
assertEquals(p.deferChildren(), nested.isDefer());
}
+
+ private PluginEntry getEntry(List<PluginType<?>> list, String name) {
+ for (PluginType<?> type : list) {
+ if (type.getPluginEntry().getName().equalsIgnoreCase(name)) {
+ return type.getPluginEntry();
+ }
+ }
+ return null;
+ }
}