
Author: seb Date: Thu Jan 25 19:25:31 2007 New Revision: 1284 Added: logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/selector/ logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/selector/ContextJNDISelector.java logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/selector/ContextSelector.java logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/selector/DefaultContextSelector.java logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/selector/servlet/ logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/selector/servlet/ContextDetachingSCL.java logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/util/JNDIUtil.java Modified: logback/trunk/logback-classic/pom.xml logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/ClassicGlobal.java logback/trunk/logback-classic/src/main/java/org/slf4j/LoggerFactory.java logback/trunk/logback-core/src/main/java/ch/qos/logback/core/util/Loader.java Log: first commit of a ContextJNDISelector Modified: logback/trunk/logback-classic/pom.xml ============================================================================== --- logback/trunk/logback-classic/pom.xml (original) +++ logback/trunk/logback-classic/pom.xml Thu Jan 25 19:25:31 2007 @@ -85,6 +85,13 @@ <optional>true</optional> </dependency> + <dependency> + <groupId>javax.servlet</groupId> + <artifactId>servlet-api</artifactId> + <scope>compile</scope> + <optional>true</optional> + </dependency> + </dependencies> <build> Modified: logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/ClassicGlobal.java ============================================================================== --- logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/ClassicGlobal.java (original) +++ logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/ClassicGlobal.java Thu Jan 25 19:25:31 2007 @@ -14,4 +14,8 @@ static public final String CAUSED_BY = "Caused by: "; static public final char DOT = '.'; static public final String USER_MDC_KEY = "user"; + + public static final String LOGBACK_CONTEXT_SELECTOR = "logback.ContextSelector"; + public static String JNDI_CONFIGURATION_RESOURCE = "java:comp/env/logback/configuration-resource"; + public static String JNDI_CONTEXT_NAME = "java:comp/env/logback/context-name"; } Added: logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/selector/ContextJNDISelector.java ============================================================================== --- (empty file) +++ logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/selector/ContextJNDISelector.java Thu Jan 25 19:25:31 2007 @@ -0,0 +1,112 @@ +package ch.qos.logback.classic.selector; + +import static ch.qos.logback.classic.ClassicGlobal.JNDI_CONFIGURATION_RESOURCE; +import static ch.qos.logback.classic.ClassicGlobal.JNDI_CONTEXT_NAME; + +import java.net.URL; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +import javax.naming.Context; +import javax.naming.NamingException; + +import org.slf4j.Logger; + +import ch.qos.logback.classic.LoggerContext; +import ch.qos.logback.classic.joran.JoranConfigurator; +import ch.qos.logback.classic.util.ContextInitializer; +import ch.qos.logback.classic.util.JNDIUtil; +import ch.qos.logback.core.joran.spi.JoranException; +import ch.qos.logback.core.util.Loader; +import ch.qos.logback.core.util.StatusPrinter; + +/** + * A class that allows the LoggerFactory to access an environment-based + * LoggerContext. + * + * To add in catalina.sh + * + * JAVA_OPTS="$JAVA_OPTS "-Dlogback.ContextSelector=JNDI"" + * + * @author Ceki Gülcü + * @author Sébastien Pennec + */ +public class ContextJNDISelector implements ContextSelector { + + private final Map<String, LoggerContext> contextMap; + private final LoggerContext defaultContext; + + public ContextJNDISelector(LoggerContext context) { + contextMap = Collections + .synchronizedMap(new HashMap<String, LoggerContext>()); + defaultContext = context; + } + + public LoggerContext getLoggerContext() { + String contextName = null; + Context ctx = null; + + try { + // We first try to find the name of our + // environment's LoggerContext + ctx = JNDIUtil.getInitialContext(); + contextName = (String) JNDIUtil.lookup(ctx, JNDI_CONTEXT_NAME); + } catch (NamingException ne) { + // We can't log here + } + + if (contextName == null) { + // We return the default context + return defaultContext; + } else { + // Let's see if we already know such a context + LoggerContext loggerContext = contextMap.get(contextName); + + if (loggerContext == null) { + // We have to create a new LoggerContext + loggerContext = new LoggerContext(); + loggerContext.setName(contextName); + contextMap.put(contextName, loggerContext); + + // Do we have a dedicated configuration file? + String configFilePath = JNDIUtil.lookup(ctx, + JNDI_CONFIGURATION_RESOURCE); + if (configFilePath != null) { + configureLoggerContextByResource(loggerContext, configFilePath); + } else { + ContextInitializer.autoConfig(loggerContext); + } + } + return loggerContext; + } + } + + public LoggerContext getDefaultLoggerContext() { + return defaultContext; + } + + public LoggerContext detachLoggerContext(String loggerContextName) { + return contextMap.remove(loggerContextName); + } + + private void configureLoggerContextByResource(LoggerContext context, + String configFilePath) { + URL url = Loader.getResourceByTCL(configFilePath); + if (url != null) { + try { + JoranConfigurator configurator = new JoranConfigurator(); + context.shutdownAndReset(); + configurator.setContext(context); + configurator.doConfigure(url); + } catch (JoranException e) { + StatusPrinter.print(context); + } + } else { + Logger logger = defaultContext.getLogger(LoggerContext.ROOT_NAME); + logger.warn("The provided URL for context" + context.getName() + + " does not lead to a valid file"); + } + } + +} Added: logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/selector/ContextSelector.java ============================================================================== --- (empty file) +++ logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/selector/ContextSelector.java Thu Jan 25 19:25:31 2007 @@ -0,0 +1,22 @@ +package ch.qos.logback.classic.selector; + +import ch.qos.logback.classic.LoggerContext; + +/** + * An interface that provides access to different contexts. + * + * It is used by the LoggerFactory to access the context + * it will use to retrieve loggers. + * + * @author Ceki Gülcü + * @author Sébastien Pennec + */ +public interface ContextSelector { + + public LoggerContext getLoggerContext(); + + public LoggerContext getDefaultLoggerContext(); + + public LoggerContext detachLoggerContext(String loggerContextName); + +} Added: logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/selector/DefaultContextSelector.java ============================================================================== --- (empty file) +++ logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/selector/DefaultContextSelector.java Thu Jan 25 19:25:31 2007 @@ -0,0 +1,24 @@ +package ch.qos.logback.classic.selector; + +import ch.qos.logback.classic.LoggerContext; + +public class DefaultContextSelector implements ContextSelector { + + private LoggerContext context; + + public DefaultContextSelector(LoggerContext context) { + this.context = context; + } + + public LoggerContext getLoggerContext() { + return getDefaultLoggerContext(); + } + + public LoggerContext getDefaultLoggerContext() { + return context; + } + + public LoggerContext detachLoggerContext(String loggerContextName) { + return context; + } +} Added: logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/selector/servlet/ContextDetachingSCL.java ============================================================================== --- (empty file) +++ logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/selector/servlet/ContextDetachingSCL.java Thu Jan 25 19:25:31 2007 @@ -0,0 +1,47 @@ +package ch.qos.logback.classic.selector.servlet; + +import static ch.qos.logback.classic.ClassicGlobal.JNDI_CONTEXT_NAME; + +import javax.naming.Context; +import javax.naming.NamingException; +import javax.servlet.ServletContextEvent; +import javax.servlet.ServletContextListener; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import ch.qos.logback.classic.LoggerContext; +import ch.qos.logback.classic.selector.ContextSelector; +import ch.qos.logback.classic.util.JNDIUtil; + +public class ContextDetachingSCL implements ServletContextListener { + + public void contextDestroyed(ServletContextEvent arg0) { + String loggerContextName = null; + + try { + Context ctx = JNDIUtil.getInitialContext(); + loggerContextName = (String) JNDIUtil.lookup(ctx, JNDI_CONTEXT_NAME); + } catch (NamingException ne) { + } + + if (loggerContextName != null) { + System.out.println("About to detach context named " + loggerContextName); + + ContextSelector selector = LoggerFactory.getContextSelector(); + LoggerContext context = selector.detachLoggerContext(loggerContextName); + if (context != null) { + Logger logger = context.getLogger(LoggerContext.ROOT_NAME); + logger.warn("Shutting down context " + loggerContextName); + context.shutdownAndReset(); + } else { + System.out.println("No context named " + loggerContextName + " was found."); + } + } + } + + public void contextInitialized(ServletContextEvent arg0) { + // do nothing + } + +} Added: logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/util/JNDIUtil.java ============================================================================== --- (empty file) +++ logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/util/JNDIUtil.java Thu Jan 25 19:25:31 2007 @@ -0,0 +1,30 @@ +package ch.qos.logback.classic.util; + +import javax.naming.Context; +import javax.naming.InitialContext; +import javax.naming.NamingException; + +/** + * A simple utility class to create and use a JNDI Context. + * + * @author Ceki Gülcü + * @author Sébastien Pennec + */ + +public class JNDIUtil { + + public static Context getInitialContext() throws NamingException { + return new InitialContext(); + } + + public static String lookup(Context ctx, String name) { + if (ctx == null) { + return null; + } + try { + return (String) ctx.lookup(name); + } catch (NamingException e) { + return null; + } + } +} \ No newline at end of file Modified: logback/trunk/logback-classic/src/main/java/org/slf4j/LoggerFactory.java ============================================================================== --- logback/trunk/logback-classic/src/main/java/org/slf4j/LoggerFactory.java (original) +++ logback/trunk/logback-classic/src/main/java/org/slf4j/LoggerFactory.java Thu Jan 25 19:25:31 2007 @@ -34,8 +34,13 @@ import org.slf4j.impl.Util; +import ch.qos.logback.classic.ClassicGlobal; import ch.qos.logback.classic.LoggerContext; +import ch.qos.logback.classic.selector.ContextJNDISelector; +import ch.qos.logback.classic.selector.ContextSelector; +import ch.qos.logback.classic.selector.DefaultContextSelector; import ch.qos.logback.classic.util.ContextInitializer; +import ch.qos.logback.core.util.OptionHelper; /** * The <code>LoggerFactory</code> is a utility class producing Loggers for @@ -55,7 +60,9 @@ */ public final class LoggerFactory { - static LoggerContext loggerContext; + static LoggerContext defaultLoggerContext; + + private static ContextSelector contextSelector; // private constructor prevents instantiation private LoggerFactory() { @@ -63,9 +70,19 @@ static { try { - loggerContext = new LoggerContext(); - loggerContext.setName("default"); - ContextInitializer.autoConfig(loggerContext); + //let's configure a default context + defaultLoggerContext = new LoggerContext(); + defaultLoggerContext.setName("default"); + ContextInitializer.autoConfig(defaultLoggerContext); + + //See if a special context selector is needed + String contextSelectorStr = OptionHelper.getSystemProperty(ClassicGlobal.LOGBACK_CONTEXT_SELECTOR, null); + if (contextSelectorStr == null) { + contextSelector = new DefaultContextSelector(defaultLoggerContext); + } else if (contextSelectorStr.equals("JNDI")) { + //if jndi is specified, let's use the appropriate class + contextSelector = new ContextJNDISelector(defaultLoggerContext); + } } catch (Exception e) { // we should never get here Util.reportFailure("Failed to instantiate logger [" + LoggerContext.class @@ -82,7 +99,7 @@ * @return logger */ public static Logger getLogger(String name) { - return loggerContext.getLogger(name); + return contextSelector.getLoggerContext().getLogger(name); } /** @@ -94,7 +111,7 @@ * @return logger */ public static Logger getLogger(Class clazz) { - return loggerContext.getLogger(clazz.getName()); + return contextSelector.getLoggerContext().getLogger(clazz.getName()); } /** @@ -106,6 +123,15 @@ * @return the ILoggerFactory instance in use */ public static ILoggerFactory getILoggerFactory() { - return loggerContext; + return contextSelector.getLoggerContext(); + } + + /** + * Return the {@link ContextSelector} instance in use. + * + * @return the ContextSelector instance in use + */ + public static ContextSelector getContextSelector() { + return contextSelector; } } Modified: logback/trunk/logback-core/src/main/java/ch/qos/logback/core/util/Loader.java ============================================================================== --- logback/trunk/logback-core/src/main/java/ch/qos/logback/core/util/Loader.java (original) +++ logback/trunk/logback-core/src/main/java/ch/qos/logback/core/util/Loader.java Thu Jan 25 19:25:31 2007 @@ -64,9 +64,12 @@ } catch (Throwable t) { return null; } - } + public static URL getResourceByTCL(String resource) { + return getResource(resource, getTCL()); + } + /** * Get the Thread Context Loader which is a JDK 1.2 feature. If we are running * under JDK 1.1 or anything else goes wrong the method returns