View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements. See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache license, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License. You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the license for the specific language governing permissions and
15   * limitations under the license.
16   */
17  package org.apache.logging.log4j.core.selector;
18  
19  import org.apache.logging.log4j.core.LoggerContext;
20  import org.apache.logging.log4j.core.helpers.Constants;
21  import org.apache.logging.log4j.core.impl.ContextAnchor;
22  import org.apache.logging.log4j.status.StatusLogger;
23  
24  import javax.naming.Context;
25  import javax.naming.InitialContext;
26  import javax.naming.NameNotFoundException;
27  import javax.naming.NamingException;
28  import java.util.ArrayList;
29  import java.util.Collections;
30  import java.util.List;
31  import java.util.Map;
32  import java.util.concurrent.ConcurrentHashMap;
33  import java.util.concurrent.ConcurrentMap;
34  
35  /**
36   * This class can be used to define a
37   * custom logger repository.  It makes use of the fact that in J2EE
38   * environments, each web-application is guaranteed to have its own JNDI
39   * context relative to the <code>java:comp/env</code> context. In EJBs, each
40   * enterprise bean (albeit not each application) has its own context relative
41   * to the <code>java:comp/env</code> context.  An <code>env-entry</code> in a
42   * deployment descriptor provides the information to the JNDI context.  Once the
43   * <code>env-entry</code> is set, a repository selector can query the JNDI
44   * application context to look up the value of the entry. The logging context of
45   * the web-application will depend on the value the env-entry.  The JNDI context
46   *  which is looked up by this class is
47   * <code>java:comp/env/log4j/context-name</code>.
48   *
49   * <p>Here is an example of an <code>env-entry<code>:
50   * <blockquote>
51   * <pre>
52   * &lt;env-entry&gt;
53   *   &lt;description&gt;JNDI logging context name for this app&lt;/description&gt;
54   *   &lt;env-entry-name&gt;log4j/context-name&lt;/env-entry-name&gt;
55   *   &lt;env-entry-value&gt;aDistinctiveLoggingContextName&lt;/env-entry-value&gt;
56   *   &lt;env-entry-type&gt;java.lang.String&lt;/env-entry-type&gt;
57   * &lt;/env-entry&gt;
58   * </pre>
59   * </blockquote>
60   * </p>
61   *
62   * <p><em>If multiple applications use the same logging context name, then they
63   * will share the same logging context.</em>
64   * </p>
65   *
66   *<p>You can also specify the URL for this context's configuration resource.
67   * This repository selector (ContextJNDISelector) will use this resource
68   * to automatically configure the log4j repository.
69   *</p>
70   ** <blockquote>
71   * <pre>
72   * &lt;env-entry&gt;
73   *   &lt;description&gt;URL for configuring log4j context&lt;/description&gt;
74   *   &lt;env-entry-name&gt;log4j/configuration-resource&lt;/env-entry-name&gt;
75   *   &lt;env-entry-value&gt;urlOfConfigrationResource&lt;/env-entry-value&gt;
76   *   &lt;env-entry-type&gt;java.lang.String&lt;/env-entry-type&gt;
77   * &lt;/env-entry&gt;
78   * </pre>
79   * </blockquote>
80   *
81   * <p>It usually good practice for configuration resources of distinct
82   * applications to have distinct names. However, if this is not possible
83   * Naming
84   * </p>
85   *
86   */
87  public class JNDIContextSelector implements NamedContextSelector {
88  
89      private static final LoggerContext context = new LoggerContext("Default");
90  
91      private static final ConcurrentMap<String, LoggerContext> contextMap =
92          new ConcurrentHashMap<String, LoggerContext>();
93  
94      private static final StatusLogger LOGGER = StatusLogger.getLogger();
95  
96      public LoggerContext getContext(String fqcn, ClassLoader loader, boolean currentContext) {
97  
98          LoggerContext lc = ContextAnchor.THREAD_CONTEXT.get();
99          if (lc != null) {
100             return lc;
101         }
102 
103         String loggingContextName = null;
104 
105         try {
106             Context ctx = new InitialContext();
107             loggingContextName = (String) lookup(ctx, Constants.JNDI_CONTEXT_NAME);
108         } catch (NamingException ne) {
109             LOGGER.error("Unable to lookup " + Constants.JNDI_CONTEXT_NAME, ne);
110         }
111 
112         return loggingContextName == null ? context : locateContext(loggingContextName, null);
113     }
114 
115     public LoggerContext locateContext(String name, String configLocation) {
116         if (name == null) {
117             LOGGER.error("A context name is required to locate a LoggerContext");
118             return null;
119         }
120         if (!contextMap.containsKey(name)) {
121             LoggerContext ctx = new LoggerContext(name, null, configLocation);
122             contextMap.putIfAbsent(name, ctx);
123         }
124         return contextMap.get(name);
125     }
126 
127     public void removeContext(LoggerContext context) {
128 
129         for (Map.Entry<String, LoggerContext> entry : contextMap.entrySet()) {
130             if (entry.getValue().equals(context)) {
131                 contextMap.remove(entry.getKey());
132             }
133         }
134     }
135 
136     public LoggerContext removeContext(String name) {
137         return contextMap.remove(name);
138     }
139 
140     public List<LoggerContext> getLoggerContexts() {
141         List<LoggerContext> list = new ArrayList<LoggerContext>(contextMap.values());
142         return Collections.unmodifiableList(list);
143     }
144 
145 
146     protected static Object lookup(Context ctx, String name) throws NamingException {
147         if (ctx == null) {
148             return null;
149         }
150         try {
151             return ctx.lookup(name);
152         } catch (NameNotFoundException e) {
153             LOGGER.error("Could not find name [" + name + "].");
154             throw e;
155         }
156     }
157 }