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  import static ch.qos.logback.core.CoreConstants.FA_FILENAME_COLLISION_MAP;
18  import static ch.qos.logback.core.CoreConstants.HOSTNAME_KEY;
19  import static ch.qos.logback.core.CoreConstants.RFA_FILENAME_PATTERN_COLLISION_MAP;
20  
21  import java.util.ArrayList;
22  import java.util.HashMap;
23  import java.util.List;
24  import java.util.Map;
25  import java.util.concurrent.*;
26  
27  import ch.qos.logback.core.rolling.helper.FileNamePattern;
28  import ch.qos.logback.core.spi.ConfigurationEvent;
29  import ch.qos.logback.core.spi.ConfigurationEventListener;
30  import ch.qos.logback.core.spi.LifeCycle;
31  import ch.qos.logback.core.spi.LogbackLock;
32  import ch.qos.logback.core.spi.SequenceNumberGenerator;
33  import ch.qos.logback.core.status.InfoStatus;
34  import ch.qos.logback.core.status.StatusManager;
35  import ch.qos.logback.core.util.ExecutorServiceUtil;
36  import ch.qos.logback.core.util.NetworkAddressUtil;
37  
38  public class ContextBase implements Context, LifeCycle {
39  
40      private long birthTime = System.currentTimeMillis();
41  
42      private String name;
43      private StatusManager sm = new BasicStatusManager();
44      // TODO propertyMap should be observable so that we can be notified
45      // when it changes so that a new instance of propertyMap can be
46      // serialized. For the time being, we ignore this shortcoming.
47      Map<String, String> propertyMap = new HashMap<String, String>();
48      Map<String, Object> objectMap = new ConcurrentHashMap<>();
49  
50      LogbackLock configurationLock = new LogbackLock();
51  
52      final private List<ConfigurationEventListener> configurationEventListenerList = new CopyOnWriteArrayList<>();
53  
54      private ScheduledExecutorService scheduledExecutorService;
55  
56      private ThreadPoolExecutor threadPoolExecutor;
57      private ExecutorService alternateExecutorService;
58  
59  
60      protected List<ScheduledFuture<?>> scheduledFutures = new ArrayList<ScheduledFuture<?>>(1);
61      private LifeCycleManager lifeCycleManager;
62      private SequenceNumberGenerator sequenceNumberGenerator;
63  
64      private boolean started;
65  
66      public ContextBase() {
67          initCollisionMaps();
68      }
69  
70      public StatusManager getStatusManager() {
71          return sm;
72      }
73  
74      /**
75       * Set the {@link StatusManager} for this context. Note that by default this
76       * context is initialized with a {@link BasicStatusManager}. A null value for
77       * the 'statusManager' argument is not allowed.
78       * 
79       * <p>
80       * A malicious attacker can set the status manager to a dummy instance,
81       * disabling internal error reporting.
82       *
83       * @param statusManager the new status manager
84       */
85      public void setStatusManager(StatusManager statusManager) {
86          // this method was added in response to http://jira.qos.ch/browse/LBCORE-35
87          if (statusManager == null) {
88              throw new IllegalArgumentException("null StatusManager not allowed");
89          }
90          this.sm = statusManager;
91      }
92  
93      public Map<String, String> getCopyOfPropertyMap() {
94          return new HashMap<String, String>(propertyMap);
95      }
96  
97      public void putProperty(String key, String val) {
98          if (HOSTNAME_KEY.equalsIgnoreCase(key)) {
99              putHostnameProperty(val);
100         } else {
101             this.propertyMap.put(key, val);
102         }
103     }
104 
105     protected void initCollisionMaps() {
106         putObject(FA_FILENAME_COLLISION_MAP, new HashMap<String, String>());
107         putObject(RFA_FILENAME_PATTERN_COLLISION_MAP, new HashMap<String, FileNamePattern>());
108     }
109 
110     @Override
111     public void addSubstitutionProperty(String key, String value) {
112         if (key == null || value == null) {
113             return;
114         }
115         // values with leading or trailing spaces are bad. We remove them now.
116         value = value.trim();
117         propertyMap.put(key, value);
118     }
119 
120     /**
121      * Given a key, return the corresponding property value. If invoked with the
122      * special key "CONTEXT_NAME", the name of the context is returned.
123      *
124      * @param key
125      * @return
126      */
127     public String getProperty(String key) {
128         if (CONTEXT_NAME_KEY.equals(key))
129             return getName();
130         if (HOSTNAME_KEY.equalsIgnoreCase(key)) {
131             return lazyGetHostname();
132         }
133 
134         return (String) this.propertyMap.get(key);
135     }
136 
137     private String lazyGetHostname() {
138         String hostname = (String) this.propertyMap.get(HOSTNAME_KEY);
139         if (hostname == null) {
140             hostname = new NetworkAddressUtil(this).safelyGetLocalHostName();
141             putHostnameProperty(hostname);
142         }
143         return hostname;
144     }
145 
146     private void putHostnameProperty(String hostname) {
147         String existingHostname = (String) this.propertyMap.get(HOSTNAME_KEY);
148         if (existingHostname == null) {
149             this.propertyMap.put(CoreConstants.HOSTNAME_KEY, hostname);
150         } else {
151 
152         }
153     }
154 
155     public Object getObject(String key) {
156         return objectMap.get(key);
157     }
158 
159     public void putObject(String key, Object value) {
160         objectMap.put(key, value);
161     }
162 
163     public void removeObject(String key) {
164         objectMap.remove(key);
165     }
166 
167     public String getName() {
168         return name;
169     }
170 
171     @Override
172     public void start() {
173         // We'd like to create the executor service here, but we can't;
174         // ContextBase has not always implemented LifeCycle and there are *many*
175         // uses (mostly in tests) that would need to be modified.
176         started = true;
177     }
178 
179     public void stop() {
180         // We don't check "started" here, because the executor services use
181         // lazy initialization, rather than being created in the start method
182         stopExecutorServices();
183 
184         started = false;
185     }
186 
187     public boolean isStarted() {
188         return started;
189     }
190 
191     /**
192      * Clear the internal objectMap and all properties. Removes any registered
193      * shutdown hook.
194      */
195     public void reset() {
196 
197         removeShutdownHook();
198         getLifeCycleManager().reset();
199         propertyMap.clear();
200         objectMap.clear();
201     }
202 
203     /**
204      * The context name can be set only if it is not already set, or if the current
205      * name is the default context name, namely "default", or if the current name
206      * and the old name are the same.
207      *
208      * @throws IllegalStateException if the context already has a name, other than
209      *                               "default".
210      */
211     public void setName(String name) throws IllegalStateException {
212         if (name != null && name.equals(this.name)) {
213             return; // idempotent naming
214         }
215         if (this.name == null || CoreConstants.DEFAULT_CONTEXT_NAME.equals(this.name)) {
216             this.name = name;
217         } else {
218             throw new IllegalStateException("Context has been already given a name");
219         }
220     }
221 
222     public long getBirthTime() {
223         return birthTime;
224     }
225 
226     public Object getConfigurationLock() {
227         return configurationLock;
228     }
229 
230 
231 
232     @Override
233     public synchronized ExecutorService getExecutorService() {
234         if (threadPoolExecutor == null) {
235             threadPoolExecutor = (ThreadPoolExecutor) ExecutorServiceUtil.newThreadPoolExecutor();
236         }
237         return threadPoolExecutor;
238     }
239 
240     @Override
241     public synchronized ExecutorService getAlternateExecutorService() {
242         if(alternateExecutorService == null) {
243             alternateExecutorService = ExecutorServiceUtil.newAlternateThreadPoolExecutor();
244         }
245         return alternateExecutorService;
246     }
247 
248         @Override
249     public synchronized ScheduledExecutorService getScheduledExecutorService() {
250         if (scheduledExecutorService == null) {
251             scheduledExecutorService = ExecutorServiceUtil.newScheduledExecutorService();
252         }
253         return scheduledExecutorService;
254     }
255 
256     private synchronized void stopExecutorServices() {
257         ExecutorServiceUtil.shutdown(scheduledExecutorService);
258         scheduledExecutorService = null;
259 
260         ExecutorServiceUtil.shutdown(threadPoolExecutor);
261         threadPoolExecutor = null;
262     }
263 
264     private void removeShutdownHook() {
265         Thread hook = (Thread) getObject(CoreConstants.SHUTDOWN_HOOK_THREAD);
266         if (hook != null) {
267             removeObject(CoreConstants.SHUTDOWN_HOOK_THREAD);
268 
269             try {
270                 sm.add(new InfoStatus("Removing shutdownHook " + hook, this));
271                 Runtime runtime = Runtime.getRuntime();
272                 boolean result = runtime.removeShutdownHook(hook);
273                 sm.add(new InfoStatus("ShutdownHook removal result: " + result, this));
274             } catch (IllegalStateException e) {
275                 // if JVM is already shutting down, ISE is thrown
276                 // no need to do anything else
277             }
278         }
279     }
280 
281     public void register(LifeCycle component) {
282         getLifeCycleManager().register(component);
283     }
284 
285     /**
286      * Gets the life cycle manager for this context.
287      * <p>
288      * The default implementation lazily initializes an instance of
289      * {@link LifeCycleManager}. Subclasses may override to provide a custom manager
290      * implementation, but must take care to return the same manager object for each
291      * call to this method.
292      * <p>
293      * This is exposed primarily to support instrumentation for unit testing.
294      * 
295      * @return manager object
296      */
297     synchronized LifeCycleManager getLifeCycleManager() {
298         if (lifeCycleManager == null) {
299             lifeCycleManager = new LifeCycleManager();
300         }
301         return lifeCycleManager;
302     }
303 
304     @Override
305     public String toString() {
306         return name;
307     }
308 
309     @Override
310     public void addScheduledFuture(ScheduledFuture<?> scheduledFuture) {
311         scheduledFutures.add(scheduledFuture);
312     }
313 
314     /**
315      * @deprecated replaced by getCopyOfScheduledFutures
316      */
317     @Deprecated
318     public List<ScheduledFuture<?>> getScheduledFutures() {
319         return getCopyOfScheduledFutures();
320     }
321 
322     public List<ScheduledFuture<?>> getCopyOfScheduledFutures() {
323         return new ArrayList<ScheduledFuture<?>>(scheduledFutures);
324     }
325 
326     public SequenceNumberGenerator getSequenceNumberGenerator() {
327         return sequenceNumberGenerator;
328     }
329 
330     public void setSequenceNumberGenerator(SequenceNumberGenerator sequenceNumberGenerator) {
331         this.sequenceNumberGenerator = sequenceNumberGenerator;
332     }
333 
334     @Override
335     public void addConfigurationEventListener(ConfigurationEventListener listener) {
336         configurationEventListenerList.add(listener);
337     }
338 
339     @Override
340     public void fireConfigurationEvent(ConfigurationEvent configurationEvent) {
341         //System.out.println("xxxx fireConfigurationEvent  of "+configurationEvent);
342         configurationEventListenerList.forEach( l -> l.listen(configurationEvent));
343     }
344 }