001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements. See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache license, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License. You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the license for the specific language governing permissions and
015 * limitations under the license.
016 */
017package org.apache.logging.log4j.web;
018
019import java.util.concurrent.locks.Lock;
020import java.util.concurrent.locks.ReentrantLock;
021import javax.servlet.ServletContext;
022
023import org.apache.logging.log4j.LogManager;
024import org.apache.logging.log4j.core.LoggerContext;
025import org.apache.logging.log4j.core.impl.ContextAnchor;
026
027/**
028 * Convenience methods for retrieving the {@link org.apache.logging.log4j.core.LoggerContext} associated with a
029 * particular ServletContext. These methods are most particularly useful for asynchronous servlets where the
030 * Thread Context ClassLoader (TCCL) is potentially different from the TCCL used by the
031 * Servlet container that bootstrapped Log4j.
032 *
033 * @since 2.0.1
034 */
035public final class WebLoggerContextUtils {
036    private WebLoggerContextUtils() {
037    }
038
039    private static final Lock WEB_SUPPORT_LOOKUP = new ReentrantLock();
040
041    /**
042     * Finds the main {@link org.apache.logging.log4j.core.LoggerContext} configured for the given ServletContext.
043     *
044     * @param servletContext the ServletContext to locate a LoggerContext for
045     * @return the LoggerContext for the given ServletContext
046     * @since 2.0.1
047     */
048    public static LoggerContext getWebLoggerContext(final ServletContext servletContext) {
049        return (LoggerContext) servletContext.getAttribute(Log4jWebSupport.CONTEXT_ATTRIBUTE);
050    }
051
052    /**
053     * Finds the main {@link org.apache.logging.log4j.core.LoggerContext} configured for the given ServletContext.
054     *
055     * @param servletContext the ServletContext to locate a LoggerContext for
056     * @return the LoggerContext for the given ServletContext or {@code null} if none was set
057     * @throws java.lang.IllegalStateException if no LoggerContext could be found on the given ServletContext
058     * @since 2.0.1
059     */
060    public static LoggerContext getRequiredWebLoggerContext(final ServletContext servletContext) {
061        final LoggerContext loggerContext = getWebLoggerContext(servletContext);
062        if (loggerContext == null) {
063            throw new IllegalStateException(
064                "No LoggerContext found in ServletContext attribute " + Log4jWebSupport.CONTEXT_ATTRIBUTE);
065        }
066        return loggerContext;
067    }
068
069    /**
070     * Finds or initializes the {@link org.apache.logging.log4j.web.Log4jWebLifeCycle} singleton for the given
071     * ServletContext.
072     *
073     * @param servletContext the ServletContext to get the Log4jWebLifeCycle for
074     * @return the Log4jWebLifeCycle for the given ServletContext
075     * @since 2.0.1
076     */
077    public static Log4jWebLifeCycle getWebLifeCycle(final ServletContext servletContext) {
078        WEB_SUPPORT_LOOKUP.lock();
079        try {
080            Log4jWebLifeCycle webLifeCycle = (Log4jWebLifeCycle) servletContext.getAttribute(
081                Log4jWebSupport.SUPPORT_ATTRIBUTE);
082            if (webLifeCycle == null) {
083                webLifeCycle = Log4jWebInitializerImpl.initialize(servletContext);
084            }
085            return webLifeCycle;
086        } finally {
087            WEB_SUPPORT_LOOKUP.unlock();
088        }
089    }
090
091    /**
092     * Wraps a Runnable instance by setting its thread context {@link org.apache.logging.log4j.core.LoggerContext}
093     * before execution and clearing it after execution.
094     *
095     * @param servletContext the ServletContext to locate a LoggerContext for
096     * @param runnable       the Runnable to wrap execution for
097     * @return a wrapped Runnable
098     * @since 2.0.1
099     */
100    public static Runnable wrapExecutionContext(final ServletContext servletContext, final Runnable runnable) {
101        return new Runnable() {
102            @Override
103            public void run() {
104                final Log4jWebSupport webSupport = getWebLifeCycle(servletContext);
105                webSupport.setLoggerContext();
106                try {
107                    runnable.run();
108                } finally {
109                    webSupport.clearLoggerContext();
110                }
111            }
112        };
113    }
114
115    /**
116     * Gets the current {@link ServletContext} if it has already been assigned to a LoggerContext's external context.
117     *
118     * @return the current ServletContext attached to a LoggerContext or {@code null} if none could be found
119     * @since 2.1
120     */
121    public static ServletContext getServletContext() {
122        org.apache.logging.log4j.spi.LoggerContext lc = ContextAnchor.THREAD_CONTEXT.get();
123        if (lc == null) {
124            lc = LogManager.getContext(false);
125        }
126        return lc == null ? null :
127            lc.getExternalContext() instanceof ServletContext ? (ServletContext) lc.getExternalContext() : null;
128    }
129}