View Javadoc

1   /**
2    * Logback: the reliable, generic, fast and flexible logging framework.
3    * 
4    * Copyright (C) 1999-2006, QOS.ch
5    * 
6    * This library is free software, you can redistribute it and/or modify it under
7    * the terms of the GNU Lesser General Public License as published by the Free
8    * Software Foundation.
9    */
10  package ch.qos.logback.classic.selector;
11  
12  import static ch.qos.logback.classic.ClassicGlobal.JNDI_CONFIGURATION_RESOURCE;
13  import static ch.qos.logback.classic.ClassicGlobal.JNDI_CONTEXT_NAME;
14  
15  import java.net.URL;
16  import java.util.ArrayList;
17  import java.util.Collections;
18  import java.util.HashMap;
19  import java.util.List;
20  import java.util.Map;
21  
22  import javax.naming.Context;
23  import javax.naming.NamingException;
24  
25  import ch.qos.logback.classic.LoggerContext;
26  import ch.qos.logback.classic.joran.JoranConfigurator;
27  import ch.qos.logback.classic.util.ContextInitializer;
28  import ch.qos.logback.classic.util.JNDIUtil;
29  import ch.qos.logback.core.joran.spi.JoranException;
30  import ch.qos.logback.core.status.InfoStatus;
31  import ch.qos.logback.core.status.StatusManager;
32  import ch.qos.logback.core.status.WarnStatus;
33  import ch.qos.logback.core.util.Loader;
34  import ch.qos.logback.core.util.StatusPrinter;
35  
36  /**
37   * A class that allows the LoggerFactory to access an environment-based
38   * LoggerContext.
39   * 
40   * To add in catalina.sh
41   * 
42   * JAVA_OPTS="$JAVA_OPTS "-Dlogback.ContextSelector=JNDI""
43   * 
44   * @author Ceki Gülcü
45   * @author Sébastien Pennec
46   */
47  public class ContextJNDISelector implements ContextSelector {
48  
49    private final Map<String, LoggerContext> synchronizedContextMap;
50    private final LoggerContext defaultContext;
51  
52    private static final ThreadLocal<LoggerContext> threadLocal = new ThreadLocal<LoggerContext>();
53  
54    public ContextJNDISelector(LoggerContext context) {
55      synchronizedContextMap = Collections
56          .synchronizedMap(new HashMap<String, LoggerContext>());
57      defaultContext = context;
58    }
59  
60    public LoggerContext getDefaultLoggerContext() {
61      return defaultContext;
62    }
63  
64    public LoggerContext detachLoggerContext(String loggerContextName) {
65      return synchronizedContextMap.remove(loggerContextName);
66    }
67  
68    public LoggerContext getLoggerContext() {
69      String contextName = null;
70      Context ctx = null;
71  
72      // First check if ThreadLocal has been set already
73      LoggerContext lc = threadLocal.get();
74      if (lc != null) {
75        return lc;
76      }
77  
78      try {
79        // We first try to find the name of our
80        // environment's LoggerContext
81        ctx = JNDIUtil.getInitialContext();
82        contextName = (String) JNDIUtil.lookup(ctx, JNDI_CONTEXT_NAME);
83      } catch (NamingException ne) {
84        // We can't log here
85      }
86  
87      if (contextName == null) {
88        // We return the default context
89        return defaultContext;
90      } else {
91        // Let's see if we already know such a context
92        LoggerContext loggerContext = synchronizedContextMap.get(contextName);
93  
94        if (loggerContext == null) {
95          // We have to create a new LoggerContext
96          loggerContext = new LoggerContext();
97          loggerContext.setName(contextName);
98          synchronizedContextMap.put(contextName, loggerContext);
99          URL url = findConfigFileURL(ctx, loggerContext);
100         if (url != null) {
101           configureLoggerContextByURL(loggerContext, url);
102         } else {
103           try {
104             new ContextInitializer(loggerContext).autoConfig();
105           } catch (JoranException je) {
106           }
107         }
108         StatusPrinter.printInCaseOfErrorsOrWarnings(loggerContext);
109       }
110       return loggerContext;
111     }
112   }
113 
114   private String conventionalConfigFileName(String contextName) {
115     return "logback-" + contextName + ".xml";
116   }
117 
118   private URL findConfigFileURL(Context ctx, LoggerContext loggerContext) {
119     StatusManager sm = loggerContext.getStatusManager();
120 
121     String jndiEntryForConfigResource = JNDIUtil.lookup(ctx,
122         JNDI_CONFIGURATION_RESOURCE);
123     // Do we have a dedicated configuration file?
124     if (jndiEntryForConfigResource != null) {
125       sm.add(new InfoStatus("Searching for [" + jndiEntryForConfigResource
126           + "]", this));
127       URL url = urlByResourceName(sm, jndiEntryForConfigResource);
128       if (url == null) {
129         String msg = "The jndi resource [" + jndiEntryForConfigResource
130             + "] for context [" + loggerContext.getName()
131             + "] does not lead to a valid file";
132         sm.add(new WarnStatus(msg, this));
133       }
134       return url;
135     } else {
136       String resourceByConvention = conventionalConfigFileName(loggerContext
137           .getName());
138       return urlByResourceName(sm, resourceByConvention);
139     }
140   }
141 
142   private URL urlByResourceName(StatusManager sm, String resourceName) {
143     sm.add(new InfoStatus("Searching for [" + resourceName + "]",
144         this));
145     URL url = Loader.getResource(resourceName, Loader.getTCL());
146     if (url != null) {
147       return url;
148     }
149     return Loader.getResourceBySelfClassLoader(resourceName);
150   }
151 
152   private void configureLoggerContextByURL(LoggerContext context, URL url) {
153     try {
154       JoranConfigurator configurator = new JoranConfigurator();
155       context.reset();
156       configurator.setContext(context);
157       configurator.doConfigure(url);
158     } catch (JoranException e) {
159     }
160     StatusPrinter.printInCaseOfErrorsOrWarnings(context);
161   }
162 
163   public List<String> getContextNames() {
164     List<String> list = new ArrayList<String>();
165     list.addAll(synchronizedContextMap.keySet());
166     return list;
167   }
168 
169   public LoggerContext getLoggerContext(String name) {
170     return synchronizedContextMap.get(name);
171   }
172 
173   /**
174    * Returns the number of managed contexts Used for testing purposes
175    * 
176    * @return the number of managed contexts
177    */
178   public int getCount() {
179     return synchronizedContextMap.size();
180   }
181 
182   /**
183    * These methods are used by the LoggerContextFilter.
184    * 
185    * They provide a way to tell the selector which context to use, thus saving
186    * the cost of a JNDI call at each new request.
187    * 
188    * @param context
189    */
190   public void setLocalContext(LoggerContext context) {
191     threadLocal.set(context);
192   }
193 
194   public void removeLocalContext() {
195     threadLocal.remove();
196   }
197 
198 }