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