001/**
002 * Logback: the reliable, generic, fast and flexible logging framework.
003 * Copyright (C) 1999-2015, QOS.ch. All rights reserved.
004 *
005 * This program and the accompanying materials are dual-licensed under
006 * either the terms of the Eclipse Public License v1.0 as published by
007 * the Eclipse Foundation
008 *
009 *   or (per the licensee's choosing)
010 *
011 * under the terms of the GNU Lesser General Public License version 2.1
012 * as published by the Free Software Foundation.
013 */
014package ch.qos.logback.core;
015
016import static ch.qos.logback.core.CoreConstants.CONTEXT_NAME_KEY;
017import static ch.qos.logback.core.CoreConstants.FA_FILENAME_COLLISION_MAP;
018import static ch.qos.logback.core.CoreConstants.HOSTNAME_KEY;
019import static ch.qos.logback.core.CoreConstants.RFA_FILENAME_PATTERN_COLLISION_MAP;
020
021import java.util.ArrayList;
022import java.util.HashMap;
023import java.util.List;
024import java.util.Map;
025import java.util.concurrent.ConcurrentHashMap;
026import java.util.concurrent.ExecutorService;
027import java.util.concurrent.ScheduledExecutorService;
028import java.util.concurrent.ScheduledFuture;
029
030import ch.qos.logback.core.rolling.helper.FileNamePattern;
031import ch.qos.logback.core.spi.LifeCycle;
032import ch.qos.logback.core.spi.LogbackLock;
033import ch.qos.logback.core.spi.SequenceNumberGenerator;
034import ch.qos.logback.core.status.InfoStatus;
035import ch.qos.logback.core.status.StatusManager;
036import ch.qos.logback.core.util.ExecutorServiceUtil;
037import ch.qos.logback.core.util.NetworkAddressUtil;
038
039public class ContextBase implements Context, LifeCycle {
040
041    private long birthTime = System.currentTimeMillis();
042
043    private String name;
044    private StatusManager sm = new BasicStatusManager();
045    // TODO propertyMap should be observable so that we can be notified
046    // when it changes so that a new instance of propertyMap can be
047    // serialized. For the time being, we ignore this shortcoming.
048    Map<String, String> propertyMap = new HashMap<String, String>();
049    Map<String, Object> objectMap = new ConcurrentHashMap<>();
050
051    LogbackLock configurationLock = new LogbackLock();
052
053    private ScheduledExecutorService scheduledExecutorService;
054    protected List<ScheduledFuture<?>> scheduledFutures = new ArrayList<ScheduledFuture<?>>(1);
055    private LifeCycleManager lifeCycleManager;
056    private SequenceNumberGenerator sequenceNumberGenerator;
057
058    private boolean started;
059
060    public ContextBase() {
061        initCollisionMaps();
062    }
063
064    public StatusManager getStatusManager() {
065        return sm;
066    }
067
068    /**
069     * Set the {@link StatusManager} for this context. Note that by default this
070     * context is initialized with a {@link BasicStatusManager}. A null value for
071     * the 'statusManager' argument is not allowed.
072     * 
073     * <p>
074     * A malicious attacker can set the status manager to a dummy instance,
075     * disabling internal error reporting.
076     *
077     * @param statusManager the new status manager
078     */
079    public void setStatusManager(StatusManager statusManager) {
080        // this method was added in response to http://jira.qos.ch/browse/LBCORE-35
081        if (statusManager == null) {
082            throw new IllegalArgumentException("null StatusManager not allowed");
083        }
084        this.sm = statusManager;
085    }
086
087    public Map<String, String> getCopyOfPropertyMap() {
088        return new HashMap<String, String>(propertyMap);
089    }
090
091    public void putProperty(String key, String val) {
092        if (HOSTNAME_KEY.equalsIgnoreCase(key)) {
093            putHostnameProperty(val);
094        } else {
095            this.propertyMap.put(key, val);
096        }
097    }
098
099    protected void initCollisionMaps() {
100        putObject(FA_FILENAME_COLLISION_MAP, new HashMap<String, String>());
101        putObject(RFA_FILENAME_PATTERN_COLLISION_MAP, new HashMap<String, FileNamePattern>());
102    }
103
104    /**
105     * Given a key, return the corresponding property value. If invoked with the
106     * special key "CONTEXT_NAME", the name of the context is returned.
107     *
108     * @param key
109     * @return
110     */
111    public String getProperty(String key) {
112        if (CONTEXT_NAME_KEY.equals(key))
113            return getName();
114        if (HOSTNAME_KEY.equalsIgnoreCase(key)) {
115            return lazyGetHostname();
116        }
117
118        return (String) this.propertyMap.get(key);
119    }
120
121    private String lazyGetHostname() {
122        String hostname = (String) this.propertyMap.get(HOSTNAME_KEY);
123        if (hostname == null) {
124            hostname = new NetworkAddressUtil(this).safelyGetLocalHostName();
125            putHostnameProperty(hostname);
126        }
127        return hostname;
128    }
129
130    private void putHostnameProperty(String hostname) {
131        String existingHostname = (String) this.propertyMap.get(HOSTNAME_KEY);
132        if (existingHostname == null) {
133            this.propertyMap.put(CoreConstants.HOSTNAME_KEY, hostname);
134        } else {
135
136        }
137    }
138
139    public Object getObject(String key) {
140        return objectMap.get(key);
141    }
142
143    public void putObject(String key, Object value) {
144        objectMap.put(key, value);
145    }
146
147    public void removeObject(String key) {
148        objectMap.remove(key);
149    }
150
151    public String getName() {
152        return name;
153    }
154
155    @Override
156    public void start() {
157        // We'd like to create the executor service here, but we can't;
158        // ContextBase has not always implemented LifeCycle and there are *many*
159        // uses (mostly in tests) that would need to be modified.
160        started = true;
161    }
162
163    public void stop() {
164        // We don't check "started" here, because the executor service uses
165        // lazy initialization, rather than being created in the start method
166        stopExecutorService();
167
168        started = false;
169    }
170
171    public boolean isStarted() {
172        return started;
173    }
174
175    /**
176     * Clear the internal objectMap and all properties. Removes any registered
177     * shutdown hook.
178     */
179    public void reset() {
180
181        removeShutdownHook();
182        getLifeCycleManager().reset();
183        propertyMap.clear();
184        objectMap.clear();
185    }
186
187    /**
188     * The context name can be set only if it is not already set, or if the current
189     * name is the default context name, namely "default", or if the current name
190     * and the old name are the same.
191     *
192     * @throws IllegalStateException if the context already has a name, other than
193     *                               "default".
194     */
195    public void setName(String name) throws IllegalStateException {
196        if (name != null && name.equals(this.name)) {
197            return; // idempotent naming
198        }
199        if (this.name == null || CoreConstants.DEFAULT_CONTEXT_NAME.equals(this.name)) {
200            this.name = name;
201        } else {
202            throw new IllegalStateException("Context has been already given a name");
203        }
204    }
205
206    public long getBirthTime() {
207        return birthTime;
208    }
209
210    public Object getConfigurationLock() {
211        return configurationLock;
212    }
213
214    @Override
215    /**
216     * @deprecated replaced by getScheduledExecutorService
217     */
218    public synchronized ExecutorService getExecutorService() {
219        return getScheduledExecutorService();
220    }
221
222    @Override
223    public synchronized ScheduledExecutorService getScheduledExecutorService() {
224        if (scheduledExecutorService == null) {
225            scheduledExecutorService = ExecutorServiceUtil.newScheduledExecutorService();
226        }
227        return scheduledExecutorService;
228    }
229
230    private synchronized void stopExecutorService() {
231        if (scheduledExecutorService != null) {
232            ExecutorServiceUtil.shutdown(scheduledExecutorService);
233            scheduledExecutorService = null;
234        }
235    }
236
237    private void removeShutdownHook() {
238        Thread hook = (Thread) getObject(CoreConstants.SHUTDOWN_HOOK_THREAD);
239        if (hook != null) {
240            removeObject(CoreConstants.SHUTDOWN_HOOK_THREAD);
241
242            try {
243                sm.add(new InfoStatus("Removing shutdownHook " + hook, this));
244                Runtime runtime = Runtime.getRuntime();
245                boolean result = runtime.removeShutdownHook(hook);
246                sm.add(new InfoStatus("ShutdownHook removal result: " + result, this));
247            } catch (IllegalStateException e) {
248                // if JVM is already shutting down, ISE is thrown
249                // no need to do anything else
250            }
251        }
252    }
253
254    public void register(LifeCycle component) {
255        getLifeCycleManager().register(component);
256    }
257
258    /**
259     * Gets the life cycle manager for this context.
260     * <p>
261     * The default implementation lazily initializes an instance of
262     * {@link LifeCycleManager}. Subclasses may override to provide a custom manager
263     * implementation, but must take care to return the same manager object for each
264     * call to this method.
265     * <p>
266     * This is exposed primarily to support instrumentation for unit testing.
267     * 
268     * @return manager object
269     */
270    synchronized LifeCycleManager getLifeCycleManager() {
271        if (lifeCycleManager == null) {
272            lifeCycleManager = new LifeCycleManager();
273        }
274        return lifeCycleManager;
275    }
276
277    @Override
278    public String toString() {
279        return name;
280    }
281
282    @Override
283    public void addScheduledFuture(ScheduledFuture<?> scheduledFuture) {
284        scheduledFutures.add(scheduledFuture);
285    }
286
287    /**
288     * @deprecated replaced by getCopyOfScheduledFutures
289     */
290    @Deprecated
291    public List<ScheduledFuture<?>> getScheduledFutures() {
292        return getCopyOfScheduledFutures();
293    }
294
295    public List<ScheduledFuture<?>> getCopyOfScheduledFutures() {
296        return new ArrayList<ScheduledFuture<?>>(scheduledFutures);
297    }
298
299    public SequenceNumberGenerator getSequenceNumberGenerator() {
300        return sequenceNumberGenerator;
301    }
302
303    public void setSequenceNumberGenerator(SequenceNumberGenerator sequenceNumberGenerator) {
304        this.sequenceNumberGenerator = sequenceNumberGenerator;
305    }
306
307}