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.NamingException;
    27
    28  import org.apache.logging.log4j.core.LoggerContext;
    29  import org.apache.logging.log4j.core.impl.ContextAnchor;
    30  import org.apache.logging.log4j.core.net.JndiManager;
    31  import org.apache.logging.log4j.core.util.Constants;
    32  import org.apache.logging.log4j.status.StatusLogger;
    33
    34  /**
    35   * This class can be used to define a custom logger repository. It makes use of the fact that in J2EE environments, each
    36   * web-application is guaranteed to have its own JNDI context relative to the <code>java:comp/env</code> context. In
    37   * EJBs, each enterprise bean (albeit not each application) has its own context relative to the
    38   * <code>java:comp/env</code> context. An <code>env-entry</code> in a deployment descriptor provides the information to
    39   * the JNDI context. Once the <code>env-entry</code> is set, a repository selector can query the JNDI application
    40   * context to look up the value of the entry. The logging context of the web-application will depend on the value the
    41   * env-entry. The JNDI context which is looked up by this class is <code>java:comp/env/log4j/context-name</code>.
    42   *
    43   * <p>
    44   * Here is an example of an <code>env-entry</code>:
    45   * </p>
    46   * <blockquote>
    47   *
    48   * <pre>
    49   * &lt;env-entry&gt;
    50   *   &lt;description&gt;JNDI logging context name for this app&lt;/description&gt;
    51   *   &lt;env-entry-name&gt;log4j/context-name&lt;/env-entry-name&gt;
    52   *   &lt;env-entry-value&gt;aDistinctiveLoggingContextName&lt;/env-entry-value&gt;
    53   *   &lt;env-entry-type&gt;java.lang.String&lt;/env-entry-type&gt;
    54   * &lt;/env-entry&gt;
    55   * </pre>
    56   *
    57   * </blockquote>
    58   *
    59   * <p>
    60   * <em>If multiple applications use the same logging context name, then they
    61   * will share the same logging context.</em>
    62   * </p>
    63   *
    64   * <p>
    65   * You can also specify the URL for this context's configuration resource. This repository selector
    66   * (ContextJNDISelector) will use this resource to automatically configure the log4j repository.
    67   * </p>
    68   ** <blockquote>
    69   *
    70   * <pre>
    71   * &lt;env-entry&gt;
    72   *   &lt;description&gt;URL for configuring log4j context&lt;/description&gt;
    73   *   &lt;env-entry-name&gt;log4j/configuration-resource&lt;/env-entry-name&gt;
    74   *   &lt;env-entry-value&gt;urlOfConfigurationResource&lt;/env-entry-value&gt;
    75   *   &lt;env-entry-type&gt;java.lang.String&lt;/env-entry-type&gt;
    76   * &lt;/env-entry&gt;
    77   * </pre>
    78   *
    79   * </blockquote>
    80   *
    81   * <p>
    82   * It usually good practice for configuration resources of distinct applications to have distinct names. However, if
    83   * this is not possible Naming
    84   * </p>
    85   */
    86  public class JndiContextSelector implements NamedContextSelector {
    87
    88      private static final LoggerContext CONTEXT = new LoggerContext("Default");
    89
    90      private static final ConcurrentMap<String, LoggerContext> CONTEXT_MAP =
    91          new ConcurrentHashMap<String, LoggerContext>();
    92
    93      private static final StatusLogger LOGGER = StatusLogger.getLogger();
    94
    95      @Override
    96      public LoggerContext getContext(final String fqcn, final ClassLoader loader, final boolean currentContext) {
    97          return getContext(fqcn, loader, currentContext, null);
    98      }
    99
    100     @Override
    101     public LoggerContext getContext(final String fqcn, final ClassLoader loader, final boolean currentContext,
    102                                     final URI configLocation) {
    103
    104         final LoggerContext lc = ContextAnchor.THREAD_CONTEXT.get();
    105         if (lc != null) {
    106             return lc;
    107         }
    108
    109         String loggingContextName = null;
    110
    111         final JndiManager jndiManager = JndiManager.getDefaultManager();
    112         try {
    113             loggingContextName = jndiManager.lookup(Constants.JNDI_CONTEXT_NAME);
    114         } catch (final NamingException ne) {
    115             LOGGER.error("Unable to lookup {}", Constants.JNDI_CONTEXT_NAME, ne);
    116         } finally {
    117             jndiManager.release();
    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 }