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