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