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