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;
018    
019    import org.apache.logging.log4j.core.config.Configuration;
020    import org.apache.logging.log4j.core.config.ConfigurationFactory;
021    import org.apache.logging.log4j.core.config.ConfigurationListener;
022    import org.apache.logging.log4j.core.config.DefaultConfiguration;
023    import org.apache.logging.log4j.core.config.NullConfiguration;
024    import org.apache.logging.log4j.core.config.Reconfigurable;
025    import org.apache.logging.log4j.status.StatusLogger;
026    
027    import java.io.File;
028    import java.net.URI;
029    import java.util.concurrent.ConcurrentHashMap;
030    import java.util.concurrent.ConcurrentMap;
031    import java.util.concurrent.locks.Lock;
032    import java.util.concurrent.locks.ReentrantLock;
033    
034    /**
035     * The LoggerContext is the anchor for the logging system. It maintains a list of all the loggers requested by
036     * applications and a reference to the Configuration. The Configuration will contain the configured loggers, appenders,
037     * filters, etc and will be atomically updated whenever a reconfigure occurs.
038     */
039    public class LoggerContext implements org.apache.logging.log4j.spi.LoggerContext, ConfigurationListener, LifeCycle {
040    
041        private static final StatusLogger logger = StatusLogger.getLogger();
042    
043        private final ConcurrentMap<String, Logger> loggers = new ConcurrentHashMap<String, Logger>();
044    
045        /**
046         * The Configuration is volatile to guarantee that initialization of the Configuration has completed before
047         * the reference is updated.
048         */
049        private volatile Configuration config = new DefaultConfiguration();
050    
051        private Object externalContext;
052    
053        private final String name;
054    
055        private final URI configLocation;
056    
057        /**
058         * Status of the LoggerContext.
059         */
060        public enum Status {
061            /** Initialized but not yet started. */
062            INITIALIZED,
063            /** In the process of starting. */
064            STARTING,
065            /** Is active. */
066            STARTED,
067            /** Shutdown is in progress. */
068            STOPPING,
069            /** Has shutdown. */
070            STOPPED
071        }
072    
073        private volatile Status status = Status.INITIALIZED;
074    
075        private final Lock configLock = new ReentrantLock();
076    
077        /**
078         * Constructor taking only a name.
079         * @param name The context name.
080         */
081        public LoggerContext(String name) {
082            this(name, null, (URI) null);
083        }
084    
085        /**
086         * Constructor taking a name and a reference to an external context.
087         * @param name The context name.
088         * @param externalContext The external context.
089         */
090        public LoggerContext(String name, Object externalContext) {
091            this(name, externalContext, (URI) null);
092        }
093    
094        /**
095         * Constructor taking a name, external context and a configuration URI.
096         * @param name The context name.
097         * @param externalContext The external context.
098         * @param configLocn The location of the configuration as a URI.
099         */
100        public LoggerContext(String name, Object externalContext, URI configLocn) {
101            this.name = name;
102            this.externalContext = externalContext;
103            this.configLocation = configLocn;
104        }
105    
106        /**
107         * Constructor taking a name external context and a configuration location String. The location
108         * must be resolvable to a File.
109         * @param name The configuration location.
110         * @param externalContext The external context.
111         * @param configLocn The configuration location.
112         */
113        public LoggerContext(String name, Object externalContext, String configLocn) {
114            this.name = name;
115            this.externalContext = externalContext;
116            if (configLocn != null) {
117                URI uri;
118                try {
119                    uri = new File(configLocn).toURI();
120                } catch (Exception ex) {
121                    uri = null;
122                }
123                configLocation = uri;
124            } else {
125                configLocation = null;
126            }
127        }
128    
129        public void start() {
130            if (configLock.tryLock()) {
131                try {
132                    if (status == Status.INITIALIZED) {
133                        status = Status.STARTING;
134                        reconfigure();
135                        status = Status.STARTED;
136                    }
137                } finally {
138                    configLock.unlock();
139                }
140            }
141        }
142    
143        public void stop() {
144            configLock.lock();
145            try {
146                status = Status.STOPPING;
147                updateLoggers(new NullConfiguration());
148                config.stop();
149                externalContext = null;
150                status = Status.STOPPED;
151            } finally {
152                configLock.unlock();
153            }
154        }
155    
156        /**
157         * Gets the name.
158         *
159         * @return the name.
160         */
161        public String getName() {
162            return name;
163        }
164    
165        public Status getStatus() {
166            return status;
167        }
168    
169        public boolean isStarted() {
170            return status == Status.STARTED;
171        }
172    
173        /**
174         * Set the external context.
175         * @param context The external context.
176         */
177        public void setExternalContext(Object context) {
178            this.externalContext = context;
179        }
180    
181        /**
182         * Returns the external context.
183         * @return The external context.
184         */
185        public Object getExternalContext() {
186            return this.externalContext;
187        }
188    
189        /**
190         * Obtain a Logger from the Context.
191         * @param name The name of the Logger to return.
192         * @return The Logger.
193         */
194        public Logger getLogger(String name) {
195    
196            Logger logger = loggers.get(name);
197            if (logger != null) {
198                return logger;
199            }
200    
201            logger = newInstance(this, name);
202            Logger prev = loggers.putIfAbsent(name, logger);
203            return prev == null ? logger : prev;
204        }
205    
206        /**
207         * Determine if the specified Logger exists.
208         * @param name The Logger name to search for.
209         * @return True if the Logger exists, false otherwise.
210         */
211        public boolean hasLogger(String name) {
212            return loggers.containsKey(name);
213        }
214    
215        /**
216         * Returns the current Configuration. The Configuration will be replaced when a reconfigure occurs.
217         * @return The Configuration.
218         */
219        public Configuration getConfiguration() {
220            return config;
221        }
222    
223        /**
224         * Add a Filter to the Configuration. Filters that are added through the API will be lost
225         * when a reconfigure occurs.
226         * @param filter The Filter to add.
227         */
228        public void addFilter(Filter filter) {
229            config.addFilter(filter);
230        }
231    
232        /**
233         * Removes a Filter from the current Configuration.
234         * @param filter The Filter to remove.
235         */
236        public void removeFiler(Filter filter) {
237            config.removeFilter(filter);
238        }
239    
240        /**
241         * Set the Configuration to be used.
242         * @param config The new Configuration.
243         * @return The previous Configuration.
244         */
245        public synchronized Configuration setConfiguration(Configuration config) {
246            if (config == null) {
247                throw new NullPointerException("No Configuration was provided");
248            }
249            Configuration prev = this.config;
250            config.addListener(this);
251            config.start();
252            this.config = config;
253            updateLoggers();
254            if (prev != null) {
255                prev.removeListener(this);
256                prev.stop();
257            }
258            return prev;
259        }
260    
261        /**
262         *  Reconfigure the context.
263         */
264        public synchronized void reconfigure() {
265            logger.debug("Reconfiguration started for context " + name);
266            Configuration instance = ConfigurationFactory.getInstance().getConfiguration(name, configLocation);
267            setConfiguration(instance);
268            /*instance.start();
269            Configuration old = setConfiguration(instance);
270            updateLoggers();
271            if (old != null) {
272                old.stop();
273            } */
274            logger.debug("Reconfiguration completed");
275        }
276    
277        /**
278         * Cause all Loggers to be updated against the current Configuration.
279         */
280        public void updateLoggers() {
281            updateLoggers(this.config);
282        }
283    
284        /**
285         * Cause all Logger to be updated against the specified Configuration.
286         * @param config The Configuration.
287         */
288        public void updateLoggers(Configuration config) {
289            for (Logger logger : loggers.values()) {
290                logger.updateConfiguration(config);
291            }
292        }
293    
294        /**
295         * Cause a reconfiguration to take place when the underlying configuration file changes.
296         * @param reconfigurable The Configuration that can be reconfigured.
297         */
298        public synchronized void onChange(Reconfigurable reconfigurable) {
299            logger.debug("Reconfiguration started for context " + name);
300            Configuration config = reconfigurable.reconfigure();
301            if (config != null) {
302                setConfiguration(config);
303                logger.debug("Reconfiguration completed");
304            } else {
305                logger.debug("Reconfiguration failed");
306            }
307        }
308    
309    
310        private Logger newInstance(LoggerContext ctx, String name) {
311            return new Logger(ctx, name);
312        }
313    
314    }