View Javadoc

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