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 from system property {@link Constants#LOG4J_CONTEXT_SELECTOR}.
045         */
046        public Log4jContextFactory() {
047            this(createContextSelector());
048        }
049    
050        /**
051         * Initializes this factory's ContextSelector with the specified selector.
052         * @param selector the selector to use
053         */
054        public Log4jContextFactory(final ContextSelector selector) {
055            this.selector = selector;
056        }
057    
058        private static ContextSelector createContextSelector() {
059            final String sel = PropertiesUtil.getProperties().getStringProperty(Constants.LOG4J_CONTEXT_SELECTOR);
060            if (sel != null) {
061                try {
062                    return Loader.newCheckedInstanceOf(sel, ContextSelector.class);
063                } catch (final Exception ex) {
064                    LOGGER.error("Unable to create context {}", sel, ex);
065                }
066            }
067            return new ClassLoaderContextSelector();
068        }
069    
070        /**
071         * Loads the LoggerContext using the ContextSelector.
072         * @param fqcn The fully qualified class name of the caller.
073         * @param loader The ClassLoader to use or null.
074         * @param currentContext If true returns the current Context, if false returns the Context appropriate
075         * for the caller if a more appropriate Context can be determined.
076         * @param externalContext An external context (such as a ServletContext) to be associated with the LoggerContext.
077         * @return The LoggerContext.
078         */
079        @Override
080        public LoggerContext getContext(final String fqcn, final ClassLoader loader, final Object externalContext,
081                                        final boolean currentContext) {
082            final LoggerContext ctx = selector.getContext(fqcn, loader, currentContext);
083            ctx.setExternalContext(externalContext);
084            if (ctx.getState() == LifeCycle.State.INITIALIZED) {
085                ctx.start();
086            }
087            return ctx;
088        }
089    
090        /**
091         * Loads the LoggerContext using the ContextSelector.
092         * @param fqcn The fully qualified class name of the caller.
093         * @param loader The ClassLoader to use or null.
094         * @param externalContext An external context (such as a ServletContext) to be associated with the LoggerContext.
095         * @param currentContext If true returns the current Context, if false returns the Context appropriate
096         * for the caller if a more appropriate Context can be determined.
097         * @param source The configuration source.
098         * @return The LoggerContext.
099         */
100        public LoggerContext getContext(final String fqcn, final ClassLoader loader, final Object externalContext,
101                                        final boolean currentContext, final ConfigurationSource source) {
102            final LoggerContext ctx = selector.getContext(fqcn, loader, currentContext, null);
103            if (externalContext != null && ctx.getExternalContext() == null) {
104                ctx.setExternalContext(externalContext);
105            }
106            if (ctx.getState() == LifeCycle.State.INITIALIZED) {
107                if (source != null) {
108                    ContextAnchor.THREAD_CONTEXT.set(ctx);
109                    final Configuration config = ConfigurationFactory.getInstance().getConfiguration(source);
110                    LOGGER.debug("Starting LoggerContext[name={}] from configuration {}", ctx.getName(), source);
111                    ctx.start(config);
112                    ContextAnchor.THREAD_CONTEXT.remove();
113                } else {
114                    ctx.start();
115                }
116            }
117            return ctx;
118        }
119    
120        /**
121         * Loads the LoggerContext using the ContextSelector.
122         * @param fqcn The fully qualified class name of the caller.
123         * @param loader The ClassLoader to use or null.
124         * @param externalContext An external context (such as a ServletContext) to be associated with the LoggerContext.
125         * @param currentContext If true returns the current Context, if false returns the Context appropriate
126         * for the caller if a more appropriate Context can be determined.
127         * @param configLocation The location of the configuration for the LoggerContext.
128         * @return The LoggerContext.
129         */
130        @Override
131        public LoggerContext getContext(final String fqcn, final ClassLoader loader, final Object externalContext,
132                                        final boolean currentContext, final URI configLocation, final String name) {
133            final LoggerContext ctx = selector.getContext(fqcn, loader, currentContext, configLocation);
134            if (externalContext != null && ctx.getExternalContext() == null) {
135                ctx.setExternalContext(externalContext);
136            }
137            if (ctx.getState() == LifeCycle.State.INITIALIZED) {
138                if (configLocation != null || name != null) {
139                    ContextAnchor.THREAD_CONTEXT.set(ctx);
140                    final Configuration config = ConfigurationFactory.getInstance().getConfiguration(name, configLocation);
141                    LOGGER.debug("Starting LoggerContext[name={}] from configuration at {}", ctx.getName(), configLocation);
142                    ctx.start(config);
143                    ContextAnchor.THREAD_CONTEXT.remove();
144                } else {
145                    ctx.start();
146                }
147            }
148            return ctx;
149        }
150    
151        /**
152         * Returns the ContextSelector.
153         * @return The ContextSelector.
154         */
155        public ContextSelector getSelector() {
156            return selector;
157        }
158    
159        /**
160         * Removes knowledge of a LoggerContext.
161         *
162         * @param context The context to remove.
163         */
164        @Override
165        public void removeContext(final org.apache.logging.log4j.spi.LoggerContext context) {
166            if (context instanceof LoggerContext) {
167                selector.removeContext((LoggerContext) context);
168            }
169        }
170    }