View Javadoc
1   /**
2    * Logback: the reliable, generic, fast and flexible logging framework.
3    * Copyright (C) 1999-2015, QOS.ch. All rights reserved.
4    *
5    * This program and the accompanying materials are dual-licensed under
6    * either the terms of the Eclipse Public License v1.0 as published by
7    * the Eclipse Foundation
8    *
9    *   or (per the licensee's choosing)
10   *
11   * under the terms of the GNU Lesser General Public License version 2.1
12   * as published by the Free Software Foundation.
13   */
14  package ch.qos.logback.core;
15  
16  import static ch.qos.logback.core.CoreConstants.CONTEXT_NAME_KEY;
17  
18  import java.util.HashMap;
19  import java.util.Map;
20  import java.util.concurrent.ExecutorService;
21  
22  import ch.qos.logback.core.spi.LifeCycle;
23  import ch.qos.logback.core.spi.LogbackLock;
24  import ch.qos.logback.core.status.StatusManager;
25  import ch.qos.logback.core.util.ExecutorServiceUtil;
26  
27  public class ContextBase implements Context, LifeCycle {
28  
29    private long birthTime = System.currentTimeMillis();
30  
31    private String name;
32    private StatusManager sm = new BasicStatusManager();
33    // TODO propertyMap should be observable so that we can be notified
34    // when it changes so that a new instance of propertyMap can be
35    // serialized. For the time being, we ignore this shortcoming.
36    Map<String, String> propertyMap = new HashMap<String, String>();
37    Map<String, Object> objectMap = new HashMap<String, Object>();
38  
39    LogbackLock configurationLock = new LogbackLock();
40  
41    private volatile ExecutorService executorService;
42    private LifeCycleManager lifeCycleManager;
43    private boolean started;
44    
45    public StatusManager getStatusManager() {
46      return sm;
47    }
48  
49    /**
50     * Set the {@link StatusManager} for this context. Note that by default this
51     * context is initialized with a {@link BasicStatusManager}. A null value for
52     * the 'statusManager' argument is not allowed.
53     * <p/>
54     * <p> A malicious attacker can set the status manager to a dummy instance,
55     * disabling internal error reporting.
56     *
57     * @param statusManager the new status manager
58     */
59    public void setStatusManager(StatusManager statusManager) {
60      // this method was added in response to http://jira.qos.ch/browse/LBCORE-35
61      if (statusManager == null) {
62        throw new IllegalArgumentException("null StatusManager not allowed");
63      }
64      this.sm = statusManager;
65    }
66  
67    public Map<String, String> getCopyOfPropertyMap() {
68      return new HashMap<String, String>(propertyMap);
69    }
70  
71    public void putProperty(String key, String val) {
72      this.propertyMap.put(key, val);
73    }
74  
75    /**
76     * Given a key, return the corresponding property value. If invoked with
77     * the special key "CONTEXT_NAME", the name of the context is returned.
78     *
79     * @param key
80     * @return
81     */
82    public String getProperty(String key) {
83      if (CONTEXT_NAME_KEY.equals(key))
84        return getName();
85  
86      return (String) this.propertyMap.get(key);
87    }
88  
89    public Object getObject(String key) {
90      return objectMap.get(key);
91    }
92  
93    public void putObject(String key, Object value) {
94      objectMap.put(key, value);
95    }
96  
97    public void removeObject(String key) {
98      objectMap.remove(key);
99    }
100 
101 
102   public String getName() {
103     return name;
104   }
105 
106   public void start() {
107     // We'd like to create the executor service here, but we can't;
108     // ContextBase has not always implemented LifeCycle and there are *many*
109     // uses (mostly in tests) that would need to be modified.
110     started = true;
111   }
112   
113   public void stop() {
114     // We don't check "started" here, because the executor service uses
115     // lazy initialization, rather than being created in the start method
116     stopExecutorService();
117     started = false;
118   }
119 
120   public boolean isStarted() {
121     return started;
122   }
123 
124   /**
125    * Clear the internal objectMap and all properties. Removes registered
126    * shutdown hook
127    */
128   public void reset() {
129 	  removeShutdownHook();
130     getLifeCycleManager().reset();
131     propertyMap.clear();
132     objectMap.clear();
133   }
134 
135   /**
136    * The context name can be set only if it is not already set, or if the
137    * current name is the default context name, namely "default", or if the
138    * current name and the old name are the same.
139    *
140    * @throws IllegalStateException if the context already has a name, other than "default".
141    */
142   public void setName(String name) throws IllegalStateException {
143     if (name != null && name.equals(this.name)) {
144       return; // idempotent naming
145     }
146     if (this.name == null
147             || CoreConstants.DEFAULT_CONTEXT_NAME.equals(this.name)) {
148       this.name = name;
149     } else {
150       throw new IllegalStateException("Context has been already given a name");
151     }
152   }
153 
154   public long getBirthTime() {
155     return birthTime;
156   }
157 
158   public Object getConfigurationLock() {
159     return configurationLock;
160   }
161 
162   public ExecutorService getExecutorService() {
163     if (executorService == null) {
164       synchronized (this) {
165         if (executorService == null) {
166           executorService = ExecutorServiceUtil.newExecutorService();
167         }
168       }
169     }
170     return executorService; 
171   }
172 
173   private synchronized void stopExecutorService() {
174     if (executorService != null) {
175       ExecutorServiceUtil.shutdown(executorService);
176       executorService = null;
177     }
178   }
179   
180   private void removeShutdownHook() {
181     Thread hook = (Thread)getObject(CoreConstants.SHUTDOWN_HOOK_THREAD);
182     if(hook != null) {
183       removeObject(CoreConstants.SHUTDOWN_HOOK_THREAD);
184       try {
185         Runtime.getRuntime().removeShutdownHook(hook);
186       } catch(IllegalStateException e) {
187         //if JVM is already shutting down, ISE is thrown
188         //no need to do anything else
189       }
190     }
191   }
192   
193   public void register(LifeCycle component) {
194     getLifeCycleManager().register(component);
195   }
196 
197   /**
198    * Gets the life cycle manager for this context.
199    * <p>
200    * The default implementation lazily initializes an instance of
201    * {@link LifeCycleManager}.  Subclasses may override to provide a custom 
202    * manager implementation, but must take care to return the same manager
203    * object for each call to this method.
204    * <p>
205    * This is exposed primarily to support instrumentation for unit testing.
206    * 
207    * @return manager object 
208    */
209   synchronized LifeCycleManager getLifeCycleManager() {
210     if (lifeCycleManager == null) {
211       lifeCycleManager = new LifeCycleManager();
212     }
213     return lifeCycleManager;
214   }
215   
216   @Override
217   public String toString() {
218     return name;
219   }
220 
221 }