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 java.net.URI;
20  import java.util.ArrayList;
21  import java.util.Collections;
22  import java.util.List;
23  import java.util.Map;
24  import java.util.concurrent.ConcurrentHashMap;
25  import java.util.concurrent.ConcurrentMap;
26  
27  import javax.naming.Context;
28  import javax.naming.InitialContext;
29  import javax.naming.NameNotFoundException;
30  import javax.naming.NamingException;
31  
32  import org.apache.logging.log4j.core.LoggerContext;
33  import org.apache.logging.log4j.core.impl.ContextAnchor;
34  import org.apache.logging.log4j.core.util.Constants;
35  import org.apache.logging.log4j.core.util.JndiCloser;
36  import org.apache.logging.log4j.status.StatusLogger;
37  
38  /**
39   * This class can be used to define a
40   * custom logger repository.  It makes use of the fact that in J2EE
41   * environments, each web-application is guaranteed to have its own JNDI
42   * context relative to the <code>java:comp/env</code> context. In EJBs, each
43   * enterprise bean (albeit not each application) has its own context relative
44   * to the <code>java:comp/env</code> context.  An <code>env-entry</code> in a
45   * deployment descriptor provides the information to the JNDI context.  Once the
46   * <code>env-entry</code> is set, a repository selector can query the JNDI
47   * application context to look up the value of the entry. The logging context of
48   * the web-application will depend on the value the env-entry.  The JNDI context
49   *  which is looked up by this class is
50   * <code>java:comp/env/log4j/context-name</code>.
51   *
52   * <p>Here is an example of an <code>env-entry<code>:
53   * <blockquote>
54   * <pre>
55   * &lt;env-entry&gt;
56   *   &lt;description&gt;JNDI logging context name for this app&lt;/description&gt;
57   *   &lt;env-entry-name&gt;log4j/context-name&lt;/env-entry-name&gt;
58   *   &lt;env-entry-value&gt;aDistinctiveLoggingContextName&lt;/env-entry-value&gt;
59   *   &lt;env-entry-type&gt;java.lang.String&lt;/env-entry-type&gt;
60   * &lt;/env-entry&gt;
61   * </pre>
62   * </blockquote>
63   * </p>
64   *
65   * <p><em>If multiple applications use the same logging context name, then they
66   * will share the same logging context.</em>
67   * </p>
68   *
69   *<p>You can also specify the URL for this context's configuration resource.
70   * This repository selector (ContextJNDISelector) will use this resource
71   * to automatically configure the log4j repository.
72   *</p>
73   ** <blockquote>
74   * <pre>
75   * &lt;env-entry&gt;
76   *   &lt;description&gt;URL for configuring log4j context&lt;/description&gt;
77   *   &lt;env-entry-name&gt;log4j/configuration-resource&lt;/env-entry-name&gt;
78   *   &lt;env-entry-value&gt;urlOfConfigrationResource&lt;/env-entry-value&gt;
79   *   &lt;env-entry-type&gt;java.lang.String&lt;/env-entry-type&gt;
80   * &lt;/env-entry&gt;
81   * </pre>
82   * </blockquote>
83   *
84   * <p>It usually good practice for configuration resources of distinct
85   * applications to have distinct names. However, if this is not possible
86   * Naming
87   * </p>
88   *
89   */
90  public class JndiContextSelector implements NamedContextSelector {
91  
92      private static final LoggerContext CONTEXT = new LoggerContext("Default");
93  
94      private static final ConcurrentMap<String, LoggerContext> CONTEXT_MAP =
95          new ConcurrentHashMap<String, LoggerContext>();
96  
97      private static final StatusLogger LOGGER = StatusLogger.getLogger();
98  
99      @Override
100     public LoggerContext getContext(final String fqcn, final ClassLoader loader, final boolean currentContext) {
101         return getContext(fqcn, loader, currentContext, null);
102     }
103 
104     @Override
105     public LoggerContext getContext(final String fqcn, final ClassLoader loader, final boolean currentContext,
106                                     final URI configLocation) {
107 
108         final LoggerContext lc = ContextAnchor.THREAD_CONTEXT.get();
109         if (lc != null) {
110             return lc;
111         }
112 
113         String loggingContextName = null;
114 
115         Context ctx = null;
116         try {
117             ctx = new InitialContext();
118             loggingContextName = (String) lookup(ctx, Constants.JNDI_CONTEXT_NAME);
119         } catch (final NamingException ne) {
120             LOGGER.error("Unable to lookup " + Constants.JNDI_CONTEXT_NAME, ne);
121         } finally {
122             JndiCloser.closeSilently(ctx);
123         }
124 
125         return loggingContextName == null ? CONTEXT : locateContext(loggingContextName, null, configLocation);
126     }
127 
128     @Override
129     public LoggerContext locateContext(final String name, final Object externalContext, final URI configLocation) {
130         if (name == null) {
131             LOGGER.error("A context name is required to locate a LoggerContext");
132             return null;
133         }
134         if (!CONTEXT_MAP.containsKey(name)) {
135             final LoggerContext ctx = new LoggerContext(name, externalContext, configLocation);
136             CONTEXT_MAP.putIfAbsent(name, ctx);
137         }
138         return CONTEXT_MAP.get(name);
139     }
140 
141     @Override
142     public void removeContext(final LoggerContext context) {
143 
144         for (final Map.Entry<String, LoggerContext> entry : CONTEXT_MAP.entrySet()) {
145             if (entry.getValue().equals(context)) {
146                 CONTEXT_MAP.remove(entry.getKey());
147             }
148         }
149     }
150 
151     @Override
152     public LoggerContext removeContext(final String name) {
153         return CONTEXT_MAP.remove(name);
154     }
155 
156     @Override
157     public List<LoggerContext> getLoggerContexts() {
158         final List<LoggerContext> list = new ArrayList<LoggerContext>(CONTEXT_MAP.values());
159         return Collections.unmodifiableList(list);
160     }
161 
162 
163     protected static Object lookup(final Context ctx, final String name) throws NamingException {
164         if (ctx == null) {
165             return null;
166         }
167         try {
168             return ctx.lookup(name);
169         } catch (final NameNotFoundException e) {
170             LOGGER.error("Could not find name [" + name + "].");
171             throw e;
172         }
173     }
174 }