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.ConcurrentHashMap;
26  import java.util.concurrent.ExecutorService;
27  import java.util.concurrent.ScheduledExecutorService;
28  import java.util.concurrent.ScheduledFuture;
29  
30  import ch.qos.logback.core.rolling.helper.FileNamePattern;
31  import ch.qos.logback.core.spi.LifeCycle;
32  import ch.qos.logback.core.spi.LogbackLock;
33  import ch.qos.logback.core.spi.SequenceNumberGenerator;
34  import ch.qos.logback.core.status.InfoStatus;
35  import ch.qos.logback.core.status.StatusManager;
36  import ch.qos.logback.core.util.ExecutorServiceUtil;
37  import ch.qos.logback.core.util.NetworkAddressUtil;
38  
39  public class ContextBase implements Context, LifeCycle {
40  
41      private long birthTime = System.currentTimeMillis();
42  
43      private String name;
44      private StatusManager sm = new BasicStatusManager();
45      // TODO propertyMap should be observable so that we can be notified
46      // when it changes so that a new instance of propertyMap can be
47      // serialized. For the time being, we ignore this shortcoming.
48      Map<String, String> propertyMap = new HashMap<String, String>();
49      Map<String, Object> objectMap = new ConcurrentHashMap<>();
50  
51      LogbackLock configurationLock = new LogbackLock();
52  
53      private ScheduledExecutorService scheduledExecutorService;
54      protected List<ScheduledFuture<?>> scheduledFutures = new ArrayList<ScheduledFuture<?>>(1);
55      private LifeCycleManager lifeCycleManager;
56      private SequenceNumberGenerator sequenceNumberGenerator;
57  
58      private boolean started;
59  
60      public ContextBase() {
61          initCollisionMaps();
62      }
63  
64      public StatusManager getStatusManager() {
65          return sm;
66      }
67  
68      /**
69       * Set the {@link StatusManager} for this context. Note that by default this
70       * context is initialized with a {@link BasicStatusManager}. A null value for
71       * the 'statusManager' argument is not allowed.
72       * 
73       * <p>
74       * A malicious attacker can set the status manager to a dummy instance,
75       * disabling internal error reporting.
76       *
77       * @param statusManager the new status manager
78       */
79      public void setStatusManager(StatusManager statusManager) {
80          // this method was added in response to http://jira.qos.ch/browse/LBCORE-35
81          if (statusManager == null) {
82              throw new IllegalArgumentException("null StatusManager not allowed");
83          }
84          this.sm = statusManager;
85      }
86  
87      public Map<String, String> getCopyOfPropertyMap() {
88          return new HashMap<String, String>(propertyMap);
89      }
90  
91      public void putProperty(String key, String val) {
92          if (HOSTNAME_KEY.equalsIgnoreCase(key)) {
93              putHostnameProperty(val);
94          } else {
95              this.propertyMap.put(key, val);
96          }
97      }
98  
99      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 }