From 0eee3adccf92bfb47f13e321cf28ac40fc888e90 Mon Sep 17 00:00:00 2001 From: Matt Sicker Date: Sun, 26 Jan 2014 17:26:26 -0600 Subject: [PATCH 2/2] Refactor class loader usage in LogManager. --- .../java/org/apache/logging/log4j/LogManager.java | 119 +++------------ .../log4j/spi/LoggerContextFactoryContext.java | 161 +++++++++++++++++++++ log4j-osgi/api-osgi/pom.xml | 121 ++++++++++++++++ .../org/apache/logging/log4j/osgi/Activator.java | 47 ++++++ log4j-osgi/pom.xml | 1 + 5 files changed, 354 insertions(+), 95 deletions(-) create mode 100644 log4j-api/src/main/java/org/apache/logging/log4j/spi/LoggerContextFactoryContext.java create mode 100644 log4j-osgi/api-osgi/pom.xml create mode 100644 log4j-osgi/api-osgi/src/main/java/org/apache/logging/log4j/osgi/Activator.java 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 bc75df5..7f8fb9f 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 @@ -17,32 +17,23 @@ package org.apache.logging.log4j; import java.net.URI; -import java.util.Iterator; -import java.util.Map; -import java.util.SortedMap; -import java.util.TreeMap; import org.apache.logging.log4j.message.MessageFactory; import org.apache.logging.log4j.message.StringFormatterMessageFactory; -import org.apache.logging.log4j.simple.SimpleLoggerContextFactory; import org.apache.logging.log4j.spi.LoggerContext; import org.apache.logging.log4j.spi.LoggerContextFactory; -import org.apache.logging.log4j.spi.Provider; -import org.apache.logging.log4j.status.StatusLogger; -import org.apache.logging.log4j.util.PropertiesUtil; +import org.apache.logging.log4j.spi.LoggerContextFactoryContext; import org.apache.logging.log4j.util.ProviderUtil; /** * The anchor point for the logging system. + * + * @see org.apache.logging.log4j.Logger + * @see org.apache.logging.log4j.spi.LoggerContext + * @see org.apache.logging.log4j.spi.LoggerContextFactory */ public class LogManager { - private static LoggerContextFactory factory; - - private static final String FACTORY_PROPERTY_NAME = "log4j2.loggerContextFactory"; - - private static final Logger LOGGER = StatusLogger.getLogger(); - /** * The name of the root Logger. */ @@ -54,71 +45,7 @@ public class LogManager { */ static { // Shortcut binding to force a specific logging implementation. - final PropertiesUtil managerProps = PropertiesUtil.getProperties(); - final String factoryClass = managerProps.getStringProperty(FACTORY_PROPERTY_NAME); - final ClassLoader cl = ProviderUtil.findClassLoader(); - if (factoryClass != null) { - try { - final Class clazz = cl.loadClass(factoryClass); - if (LoggerContextFactory.class.isAssignableFrom(clazz)) { - factory = (LoggerContextFactory) clazz.newInstance(); - } - } catch (final ClassNotFoundException cnfe) { - LOGGER.error("Unable to locate configured LoggerContextFactory {}", factoryClass); - } catch (final Exception ex) { - LOGGER.error("Unable to create configured LoggerContextFactory {}", factoryClass, ex); - } - } - - if (factory == null) { - final SortedMap factories = new TreeMap(); - - if (ProviderUtil.hasProviders()) { - final Iterator providers = ProviderUtil.getProviders(); - while (providers.hasNext()) { - final Provider provider = providers.next(); - final String className = provider.getClassName(); - if (className != null) { - try { - final Class clazz = cl.loadClass(className); - if (LoggerContextFactory.class.isAssignableFrom(clazz)) { - factories.put(provider.getPriority(), (LoggerContextFactory) clazz.newInstance()); - } else { - LOGGER.error(className + " does not implement " + LoggerContextFactory.class.getName()); - } - } catch (final ClassNotFoundException cnfe) { - LOGGER.error("Unable to locate class " + className + " specified in " + - provider.getURL().toString(), cnfe); - } catch (final IllegalAccessException iae) { - LOGGER.error("Unable to create class " + className + " specified in " + - provider.getURL().toString(), iae); - } catch (final Exception e) { - LOGGER.error("Unable to create class " + className + " specified in " + - provider.getURL().toString(), e); - e.printStackTrace(); - } - } - } - - if (factories.size() == 0) { - LOGGER.error("Unable to locate a logging implementation, using SimpleLogger"); - factory = new SimpleLoggerContextFactory(); - } else { - final StringBuilder sb = new StringBuilder("Multiple logging implementations found: \n"); - for (final Map.Entry entry : factories.entrySet()) { - sb.append("Factory: ").append(entry.getValue().getClass().getName()); - sb.append(", Weighting: ").append(entry.getKey()).append("\n"); - } - factory = factories.get(factories.lastKey()); - sb.append("Using factory: ").append(factory.getClass().getName()); - LOGGER.warn(sb.toString()); - - } - } else { - LOGGER.error("Unable to locate a logging implementation, using SimpleLogger"); - factory = new SimpleLoggerContextFactory(); - } - } + LoggerContextFactoryContext.loadDefaultFactory(ProviderUtil.findClassLoader()); } /** @@ -133,13 +60,13 @@ public class LogManager { /** * Returns the current LoggerContext. - *

+ *

* WARNING - The LoggerContext returned by this method may not be the LoggerContext used to create a Logger * for the calling class. * @return The current LoggerContext. */ public static LoggerContext getContext() { - return factory.getContext(LogManager.class.getName(), null, null, true); + return getFactory().getContext(LogManager.class.getName(), null, null, true); } /** @@ -152,7 +79,7 @@ public class LogManager { * @return a LoggerContext. */ public static LoggerContext getContext(final boolean currentContext) { - return factory.getContext(LogManager.class.getName(), null, null, currentContext, null, null); + return getFactory().getContext(LogManager.class.getName(), null, null, currentContext, null, null); } /** @@ -167,7 +94,7 @@ public class LogManager { * @return a LoggerContext. */ public static LoggerContext getContext(final ClassLoader loader, final boolean currentContext) { - return factory.getContext(LogManager.class.getName(), loader, null, currentContext); + return getFactory().getContext(LogManager.class.getName(), loader, null, currentContext); } /** @@ -184,7 +111,7 @@ public class LogManager { */ public static LoggerContext getContext(final ClassLoader loader, final boolean currentContext, final Object externalContext) { - return factory.getContext(LogManager.class.getName(), loader, externalContext, currentContext); + return getFactory().getContext(LogManager.class.getName(), loader, externalContext, currentContext); } /** @@ -201,7 +128,7 @@ public class LogManager { */ public static LoggerContext getContext(final ClassLoader loader, final boolean currentContext, final URI configLocation) { - return factory.getContext(LogManager.class.getName(), loader, null, currentContext, configLocation, null); + return getFactory().getContext(LogManager.class.getName(), loader, null, currentContext, configLocation, null); } @@ -219,7 +146,7 @@ public class LogManager { */ public static LoggerContext getContext(final ClassLoader loader, final boolean currentContext, final Object externalContext, final URI configLocation) { - return factory.getContext(LogManager.class.getName(), loader, externalContext, currentContext, configLocation, + return getFactory().getContext(LogManager.class.getName(), loader, externalContext, currentContext, configLocation, null); } @@ -240,7 +167,7 @@ public class LogManager { public static LoggerContext getContext(final ClassLoader loader, final boolean currentContext, final Object externalContext, final URI configLocation, final String name) { - return factory.getContext(LogManager.class.getName(), loader, externalContext, currentContext, configLocation, + return getFactory().getContext(LogManager.class.getName(), loader, externalContext, currentContext, configLocation, name); } @@ -254,7 +181,7 @@ public class LogManager { * @return a LoggerContext. */ protected static LoggerContext getContext(final String fqcn, final boolean currentContext) { - return factory.getContext(fqcn, null, null, currentContext); + return getFactory().getContext(fqcn, null, null, currentContext); } /** @@ -270,15 +197,16 @@ public class LogManager { */ protected static LoggerContext getContext(final String fqcn, final ClassLoader loader, final boolean currentContext) { - return factory.getContext(fqcn, loader, null, currentContext); + return getFactory().getContext(fqcn, loader, null, currentContext); } /** - * Returns the LoggerContextFactory. + * Returns the current LoggerContextFactory. * @return The LoggerContextFactory. + * @see org.apache.logging.log4j.spi.LoggerContextFactoryContext */ public static LoggerContextFactory getFactory() { - return factory; + return LoggerContextFactoryContext.getFactory(); } /** @@ -441,7 +369,7 @@ public class LogManager { */ public static Logger getLogger(final String name) { final String actualName = name != null ? name : getClassName(2); - return factory.getContext(LogManager.class.getName(), null, null, false).getLogger(actualName); + return getFactory().getContext(LogManager.class.getName(), null, null, false).getLogger(actualName); } /** @@ -454,7 +382,7 @@ public class LogManager { */ public static Logger getLogger(final String name, final MessageFactory messageFactory) { final String actualName = name != null ? name : getClassName(2); - return factory.getContext(LogManager.class.getName(), null, null, false).getLogger(actualName, messageFactory); + return getFactory().getContext(LogManager.class.getName(), null, null, false).getLogger(actualName, messageFactory); } /** @@ -465,7 +393,7 @@ public class LogManager { * @return The Logger. */ protected static Logger getLogger(final String fqcn, final String name) { - return factory.getContext(fqcn, null, null, false).getLogger(name); + return getFactory().getContext(fqcn, null, null, false).getLogger(name); } /** @@ -478,7 +406,8 @@ public class LogManager { } /** - * Prevents instantiation + * Prevents instantiation. Normally, such a method would be private in order to make this a utility class, but + * this class is extensible for use by different implementations. */ protected LogManager() { } diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/spi/LoggerContextFactoryContext.java b/log4j-api/src/main/java/org/apache/logging/log4j/spi/LoggerContextFactoryContext.java new file mode 100644 index 0000000..3bea3a7 --- /dev/null +++ b/log4j-api/src/main/java/org/apache/logging/log4j/spi/LoggerContextFactoryContext.java @@ -0,0 +1,161 @@ +/* + * 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.spi; + +import java.util.Iterator; +import java.util.Map; +import java.util.SortedMap; +import java.util.TreeMap; + +import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.simple.SimpleLoggerContextFactory; +import org.apache.logging.log4j.status.StatusLogger; +import org.apache.logging.log4j.util.PropertiesUtil; +import org.apache.logging.log4j.util.ProviderUtil; + +/** + * Anchor point for the Log4J LoggerContextFactory instance being used. This class provides the central location for + * accessing and changing the main LoggerContextFactory. In more normal environments, this class can be simply used in + * the static initialization of LogManager. However, in a more exotic environment such as in OSGi bundles, class + * loading becomes more complicated. Thus, this class also provides a central location to specify the correct class + * loader to use to load the LoggerContextFactory instance that everything is loaded from. + */ +public final class LoggerContextFactoryContext { + + /** + * Prevents instantiation and extending this class. + */ + private LoggerContextFactoryContext() {} + + private static final Logger LOGGER = StatusLogger.getLogger(); + + private static final String FACTORY_PROPERTY_NAME = "log4j2.loggerContextFactory"; + + private static LoggerContextFactory factory; + + /** + * Modifies the current LoggerContextFactory instance. + * @param factory instance of factory to use for constructing LoggerContext and Logger objects + */ + public static void setFactory(final LoggerContextFactory factory) { + LoggerContextFactoryContext.factory = factory; + } + + /** + * Gets the current LoggerContextFactory instance. + * @return current logger context factory. + */ + public static LoggerContextFactory getFactory() { + return factory; + } + + /** + * Does the same as {@link #loadDefaultFactory(ClassLoader, boolean)} with {@code overwrite = false}. + * + * @param loader class loader to use to load the configured LoggerContextFactory + */ + public static void loadDefaultFactory(final ClassLoader loader) { + loadDefaultFactory(loader, false); + } + + /** + * Loads the last configured LoggerContextFactory. This is specified by either the system property + * {@value org.apache.logging.log4j.spi.LoggerContextFactoryContext#FACTORY_PROPERTY_NAME} or by the resource + * {@value ProviderUtil#PROVIDER_RESOURCE}. Falls back to {@link org.apache.logging.log4j.simple.SimpleLoggerContextFactory} + * if no suitable logger context factory could be found. + *

+ * Note that if a specific Log4J implementation was desired, but SimpleLogger is being loaded, you may have an issue + * with your class path, bundle configuration, or are simply passing the wrong ClassLoader to this method. The + * default implementation simply tries to find an appropriate class loader automatically using such tricks like + * using {@link Thread#getContextClassLoader()} or using another already-loaded class's class loader. These tricks + * won't work in a modular environment such as OSGi, so such environments should provide the appropriate class + * loader in order to properly load the logger context factory. + * + * @param loader class loader to use to load the configured LoggerContextFactory + * @param overwrite whether or not to overwrite the current LoggerContextFactory if it's non-{@code null} + */ + public static void loadDefaultFactory(final ClassLoader loader, final boolean overwrite) { + if (factory != null && !overwrite) { + LOGGER.debug("Logger context factory already initialized."); + return; + } + + final PropertiesUtil managerProps = PropertiesUtil.getProperties(); + final String factoryClass = managerProps.getStringProperty(FACTORY_PROPERTY_NAME); + if (factoryClass != null) { + try { + final Class clazz = loader.loadClass(factoryClass); + if (LoggerContextFactory.class.isAssignableFrom(clazz)) { + factory = (LoggerContextFactory) clazz.newInstance(); + } + } catch (final ClassNotFoundException cnfe) { + LOGGER.error("Unable to locate configured LoggerContextFactory {}", factoryClass); + } catch (final Exception ex) { + LOGGER.error("Unable to create configured LoggerContextFactory {}", factoryClass, ex); + } + } + + if (factory == null) { + final SortedMap factories = new TreeMap(); + + if (ProviderUtil.hasProviders()) { + final Iterator providers = ProviderUtil.getProviders(); + while (providers.hasNext()) { + final Provider provider = providers.next(); + final String className = provider.getClassName(); + if (className != null) { + try { + final Class clazz = loader.loadClass(className); + if (LoggerContextFactory.class.isAssignableFrom(clazz)) { + factories.put(provider.getPriority(), (LoggerContextFactory) clazz.newInstance()); + } else { + LOGGER.error(className + " does not implement " + LoggerContextFactory.class.getName()); + } + } catch (final ClassNotFoundException cnfe) { + LOGGER.error("Unable to locate class " + className + " specified in " + + provider.getURL().toString(), cnfe); + } catch (final IllegalAccessException iae) { + LOGGER.error("Unable to create class " + className + " specified in " + + provider.getURL().toString(), iae); + } catch (final Exception e) { + LOGGER.error("Unable to create class " + className + " specified in " + + provider.getURL().toString(), e); + e.printStackTrace(); + } + } + } + + if (factories.isEmpty()) { + LOGGER.error("Unable to locate a logging implementation, using SimpleLogger"); + factory = new SimpleLoggerContextFactory(); + } else { + final StringBuilder sb = new StringBuilder("Multiple logging implementations found: \n"); + for (final Map.Entry entry : factories.entrySet()) { + sb.append("Factory: ").append(entry.getValue().getClass().getName()); + sb.append(", Weighting: ").append(entry.getKey()).append('\n'); + } + factory = factories.get(factories.lastKey()); + sb.append("Using factory: ").append(factory.getClass().getName()); + LOGGER.warn(sb.toString()); + } + } else { + LOGGER.error("Unable to locate a logging implementation, using SimpleLogger"); + factory = new SimpleLoggerContextFactory(); + } + } + } +} diff --git a/log4j-osgi/api-osgi/pom.xml b/log4j-osgi/api-osgi/pom.xml new file mode 100644 index 0000000..dffba92 --- /dev/null +++ b/log4j-osgi/api-osgi/pom.xml @@ -0,0 +1,121 @@ + + + + 4.0.0 + + org.apache.logging.log4j + log4j + 2.0RC1-SNAPSHOT + ../../ + + org.apache.logging.log4j.osgi + log4j-api-activator-bundle + bundle + Apache Log4J API OSGi Bundle + OSGi activator for using Log4J in a bundle framework. + + ${basedir}/.. + API Documentation + /api + org.apache.logging.log4j.osgi.* + org.apache.logging.log4j.osgi.Activator + + + + org.apache.logging.log4j + log4j-api + + + org.osgi + core + provided + + + + + + maven-antrun-plugin + + + generate-sources + generate-sources + + + + + + + run + + + + + + org.apache.felix + maven-bundle-plugin + true + + + + true + + false + + + <_nouses>true + + <_removeheaders>JAVA_1_3_HOME,JAVA_1_4_HOME,JAVA_1_5_HOME,JAVA_1_6_HOME,JAVA_1_7_HOME + ${osgi.symbolicName} + ${osgi.export} + ${osgi.private} + ${osgi.import} + ${osgi.dynamicImport} + ${project.url} + ${osgi.activator} + org.apache.logging.log4j-api;bundle-version=${project.version} + + + + + generate-sources + + cleanVersions + + + + ${project.version} + + + + + bundle-manifest + process-classes + + manifest + + + + + + + + diff --git a/log4j-osgi/api-osgi/src/main/java/org/apache/logging/log4j/osgi/Activator.java b/log4j-osgi/api-osgi/src/main/java/org/apache/logging/log4j/osgi/Activator.java new file mode 100644 index 0000000..edf4414 --- /dev/null +++ b/log4j-osgi/api-osgi/src/main/java/org/apache/logging/log4j/osgi/Activator.java @@ -0,0 +1,47 @@ +/* + * 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.osgi; + +import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.spi.LoggerContextFactoryContext; +import org.apache.logging.log4j.status.StatusLogger; +import org.osgi.framework.BundleActivator; +import org.osgi.framework.BundleContext; + +/** + * OSGi bundle activator for Log4J. This activator loads the configured LoggerContextFactory using the appropriate + * class loader provided by the bundle framework. + */ +public class Activator implements BundleActivator { + + // TODO: this would be better implemented using a declarative service or blueprint + + private static final Logger LOGGER = StatusLogger.getLogger(); + + @Override + public void start(final BundleContext context) throws Exception { + LOGGER.info("Bundle starting. Loading logger context factory."); + final ClassLoader cl = this.getClass().getClassLoader(); + LoggerContextFactoryContext.loadDefaultFactory(cl, true); + } + + @Override + public void stop(BundleContext context) throws Exception { + LOGGER.info("Bundle stopping."); + } + +} diff --git a/log4j-osgi/pom.xml b/log4j-osgi/pom.xml index 540ffc8..825ff15 100644 --- a/log4j-osgi/pom.xml +++ b/log4j-osgi/pom.xml @@ -46,6 +46,7 @@ core-osgi-nosql-couch core-osgi-nosql-mongo core-osgi-reduced + api-osgi -- 1.8.5.3