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.core.impl;
018
019import java.net.URI;
020
021import org.apache.logging.log4j.core.LoggerContext;
022import org.apache.logging.log4j.core.config.Configuration;
023import org.apache.logging.log4j.core.config.ConfigurationFactory;
024import org.apache.logging.log4j.core.helpers.Constants;
025import org.apache.logging.log4j.core.helpers.Loader;
026import org.apache.logging.log4j.core.selector.ClassLoaderContextSelector;
027import org.apache.logging.log4j.core.selector.ContextSelector;
028import org.apache.logging.log4j.spi.LoggerContextFactory;
029import org.apache.logging.log4j.status.StatusLogger;
030import org.apache.logging.log4j.util.PropertiesUtil;
031
032/**
033 * Factory to locate a ContextSelector and then load a LoggerContext.
034 */
035public class Log4jContextFactory implements LoggerContextFactory {
036
037    private static final StatusLogger LOGGER = StatusLogger.getLogger();
038
039    private ContextSelector selector;
040
041    /**
042     * Constructor that initializes the ContextSelector.
043     */
044    public Log4jContextFactory() {
045        final String sel = PropertiesUtil.getProperties().getStringProperty(Constants.LOG4J_CONTEXT_SELECTOR);
046        if (sel != null) {
047            try {
048                final Class<?> clazz = Loader.loadClass(sel);
049                if (clazz != null && ContextSelector.class.isAssignableFrom(clazz)) {
050                    selector = (ContextSelector) clazz.newInstance();
051                }
052            } catch (final Exception ex) {
053                LOGGER.error("Unable to create context " + sel, ex);
054            }
055        }
056        if (selector == null) {
057            selector = new ClassLoaderContextSelector();
058        }
059    }
060
061    /**
062     * Returns the ContextSelector.
063     * @return The ContextSelector.
064     */
065    public ContextSelector getSelector() {
066        return selector;
067    }
068
069    /**
070     * Load the LoggerContext using the ContextSelector.
071     * @param fqcn The fully qualified class name of the caller.
072     * @param loader The ClassLoader to use or null.
073     * @param currentContext If true returns the current Context, if false returns the Context appropriate
074     * for the caller if a more appropriate Context can be determined.
075     * @param externalContext An external context (such as a ServletContext) to be associated with the LoggerContext.
076     * @return The LoggerContext.
077     */
078    @Override
079    public LoggerContext getContext(final String fqcn, final ClassLoader loader, final Object externalContext,
080                                    final boolean currentContext) {
081        final LoggerContext ctx = selector.getContext(fqcn, loader, currentContext);
082        ctx.setExternalContext(externalContext);
083        if (ctx.getStatus() == LoggerContext.Status.INITIALIZED) {
084            ctx.start();
085        }
086        return ctx;
087    }
088
089    /**
090     * Load the LoggerContext using the ContextSelector.
091     * @param fqcn The fully qualified class name of the caller.
092     * @param loader The ClassLoader to use or null.
093     * @param externalContext An external context (such as a ServletContext) to be associated with the LoggerContext.
094     * @param currentContext If true returns the current Context, if false returns the Context appropriate
095     * for the caller if a more appropriate Context can be determined.
096     * @param configLocation The location of the configuration for the LoggerContext.
097     * @return The LoggerContext.
098     */
099    @Override
100    public LoggerContext getContext(final String fqcn, final ClassLoader loader, final Object externalContext,
101                                    final boolean currentContext, final URI configLocation, final String name) {
102        final LoggerContext ctx = selector.getContext(fqcn, loader, currentContext, configLocation);
103        if (externalContext != null && ctx.getExternalContext() == null) {
104            ctx.setExternalContext(externalContext);
105        }
106        if (ctx.getStatus() == LoggerContext.Status.INITIALIZED) {
107            if (configLocation != null || name != null) {
108                ContextAnchor.THREAD_CONTEXT.set(ctx);
109                final Configuration config = ConfigurationFactory.getInstance().getConfiguration(name, configLocation);
110                ctx.start(config);
111                ContextAnchor.THREAD_CONTEXT.remove();
112            } else {
113                ctx.start();
114            }
115        }
116        return ctx;
117    }
118
119    /**
120     * Load the LoggerContext using the ContextSelector.
121     * @param fqcn The fully qualified class name of the caller.
122     * @param loader The ClassLoader to use or null.
123     * @param externalContext An external context (such as a ServletContext) to be associated with the LoggerContext.
124     * @param currentContext If true returns the current Context, if false returns the Context appropriate
125     * for the caller if a more appropriate Context can be determined.
126     * @param source The configuration source.
127     * @return The LoggerContext.
128     */
129    public LoggerContext getContext(final String fqcn, final ClassLoader loader, final Object externalContext,
130                                    final boolean currentContext, final ConfigurationFactory.ConfigurationSource source) {
131        final LoggerContext ctx = selector.getContext(fqcn, loader, currentContext, null);
132        if (externalContext != null && ctx.getExternalContext() == null) {
133            ctx.setExternalContext(externalContext);
134        }
135        if (ctx.getStatus() == LoggerContext.Status.INITIALIZED) {
136            if (source != null) {
137                ContextAnchor.THREAD_CONTEXT.set(ctx);
138                final Configuration config = ConfigurationFactory.getInstance().getConfiguration(source);
139                ctx.start(config);
140                ContextAnchor.THREAD_CONTEXT.remove();
141            } else {
142                ctx.start();
143            }
144        }
145        return ctx;
146    }
147
148
149
150    /**
151     * Removes knowledge of a LoggerContext.
152     *
153     * @param context The context to remove.
154     */
155    @Override
156    public void removeContext(final org.apache.logging.log4j.spi.LoggerContext context) {
157        if (context instanceof LoggerContext) {
158            selector.removeContext((LoggerContext) context);
159        }
160    }
161}