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.*; 026import java.util.concurrent.locks.ReentrantLock; 027 028import ch.qos.logback.core.rolling.helper.FileNamePattern; 029import ch.qos.logback.core.spi.ConfigurationEvent; 030import ch.qos.logback.core.spi.ConfigurationEventListener; 031import ch.qos.logback.core.spi.LifeCycle; 032import ch.qos.logback.core.spi.SequenceNumberGenerator; 033import ch.qos.logback.core.status.InfoStatus; 034import ch.qos.logback.core.status.StatusManager; 035import ch.qos.logback.core.util.ExecutorServiceUtil; 036import ch.qos.logback.core.util.NetworkAddressUtil; 037 038public class ContextBase implements Context, LifeCycle { 039 040 private long birthTime = System.currentTimeMillis(); 041 042 private String name; 043 private StatusManager sm = new BasicStatusManager(); 044 // TODO propertyMap should be observable so that we can be notified 045 // when it changes so that a new instance of propertyMap can be 046 // serialized. For the time being, we ignore this shortcoming. 047 Map<String, String> propertyMap = new HashMap<String, String>(); 048 Map<String, Object> objectMap = new ConcurrentHashMap<>(); 049 050 protected ReentrantLock configurationLock = new ReentrantLock(); 051 052 final private List<ConfigurationEventListener> configurationEventListenerList = new CopyOnWriteArrayList<>(); 053 054 private ScheduledExecutorService scheduledExecutorService; 055 056 private ThreadPoolExecutor threadPoolExecutor; 057 private ExecutorService alternateExecutorService; 058 059 060 protected List<ScheduledFuture<?>> scheduledFutures = new ArrayList<ScheduledFuture<?>>(1); 061 private LifeCycleManager lifeCycleManager; 062 private SequenceNumberGenerator sequenceNumberGenerator; 063 064 // 'started' is used to mitigate race conditions in stop() and possibly other methods, hence the volatile qualifier. 065 private volatile boolean started; 066 067 public ContextBase() { 068 initCollisionMaps(); 069 } 070 071 public StatusManager getStatusManager() { 072 return sm; 073 } 074 075 /** 076 * Set the {@link StatusManager} for this context. Note that by default this 077 * context is initialized with a {@link BasicStatusManager}. A null value for 078 * the 'statusManager' argument is not allowed. 079 * 080 * <p> 081 * A malicious attacker can set the status manager to a dummy instance, 082 * disabling internal error reporting. 083 * 084 * @param statusManager the new status manager 085 */ 086 public void setStatusManager(StatusManager statusManager) { 087 // this method was added in response to http://jira.qos.ch/browse/LBCORE-35 088 if (statusManager == null) { 089 throw new IllegalArgumentException("null StatusManager not allowed"); 090 } 091 this.sm = statusManager; 092 } 093 094 public Map<String, String> getCopyOfPropertyMap() { 095 return new HashMap<String, String>(propertyMap); 096 } 097 098 public void putProperty(String key, String val) { 099 if (HOSTNAME_KEY.equalsIgnoreCase(key)) { 100 putHostnameProperty(val); 101 } else { 102 this.propertyMap.put(key, val); 103 } 104 } 105 106 protected void initCollisionMaps() { 107 putObject(FA_FILENAME_COLLISION_MAP, new HashMap<String, String>()); 108 putObject(RFA_FILENAME_PATTERN_COLLISION_MAP, new HashMap<String, FileNamePattern>()); 109 } 110 111 @Override 112 public void addSubstitutionProperty(String key, String value) { 113 if (key == null || value == null) { 114 return; 115 } 116 // values with leading or trailing spaces are bad. We remove them now. 117 value = value.trim(); 118 propertyMap.put(key, value); 119 } 120 121 /** 122 * Given a key, return the corresponding property value. If invoked with the 123 * special key "CONTEXT_NAME", the name of the context is returned. 124 * 125 * @param key 126 * @return 127 */ 128 public String getProperty(String key) { 129 if (CONTEXT_NAME_KEY.equals(key)) 130 return getName(); 131 if (HOSTNAME_KEY.equalsIgnoreCase(key)) { 132 return lazyGetHostname(); 133 } 134 135 return (String) this.propertyMap.get(key); 136 } 137 138 private String lazyGetHostname() { 139 String hostname = (String) this.propertyMap.get(HOSTNAME_KEY); 140 if (hostname == null) { 141 hostname = new NetworkAddressUtil(this).safelyGetLocalHostName(); 142 putHostnameProperty(hostname); 143 } 144 return hostname; 145 } 146 147 private void putHostnameProperty(String hostname) { 148 String existingHostname = (String) this.propertyMap.get(HOSTNAME_KEY); 149 if (existingHostname == null) { 150 this.propertyMap.put(CoreConstants.HOSTNAME_KEY, hostname); 151 } else { 152 153 } 154 } 155 156 public Object getObject(String key) { 157 return objectMap.get(key); 158 } 159 160 public void putObject(String key, Object value) { 161 objectMap.put(key, value); 162 } 163 164 public void removeObject(String key) { 165 objectMap.remove(key); 166 } 167 168 public String getName() { 169 return name; 170 } 171 172 @Override 173 public void start() { 174 // We'd like to create the executor service here, but we can't; 175 // ContextBase has not always implemented LifeCycle and there are *many* 176 // uses (mostly in tests) that would need to be modified. 177 started = true; 178 } 179 180 public void stop() { 181 // We don't check "started" here, because the executor services use 182 // lazy initialization, rather than being created in the start method 183 stopExecutorServices(); 184 185 started = false; 186 } 187 188 public boolean isStarted() { 189 return started; 190 } 191 192 /** 193 * Clear the internal objectMap and all properties. Removes any registered 194 * shutdown hook. 195 */ 196 public void reset() { 197 198 removeShutdownHook(); 199 getLifeCycleManager().reset(); 200 propertyMap.clear(); 201 objectMap.clear(); 202 } 203 204 /** 205 * The context name can be set only if it is not already set, or if the current 206 * name is the default context name, namely "default", or if the current name 207 * and the old name are the same. 208 * 209 * @throws IllegalStateException if the context already has a name, other than 210 * "default". 211 */ 212 public void setName(String name) throws IllegalStateException { 213 if (name != null && name.equals(this.name)) { 214 return; // idempotent naming 215 } 216 if (this.name == null || CoreConstants.DEFAULT_CONTEXT_NAME.equals(this.name)) { 217 this.name = name; 218 } else { 219 throw new IllegalStateException("Context has been already given a name"); 220 } 221 } 222 223 public long getBirthTime() { 224 return birthTime; 225 } 226 227 public ReentrantLock getConfigurationLock() { 228 return configurationLock; 229 } 230 231 232 233 @Override 234 public synchronized ExecutorService getExecutorService() { 235 if (threadPoolExecutor == null) { 236 threadPoolExecutor = (ThreadPoolExecutor) ExecutorServiceUtil.newThreadPoolExecutor(); 237 } 238 return threadPoolExecutor; 239 } 240 241 @Override 242 public synchronized ExecutorService getAlternateExecutorService() { 243 if(alternateExecutorService == null) { 244 alternateExecutorService = ExecutorServiceUtil.newAlternateThreadPoolExecutor(); 245 } 246 return alternateExecutorService; 247 } 248 249 @Override 250 public synchronized ScheduledExecutorService getScheduledExecutorService() { 251 if (scheduledExecutorService == null) { 252 scheduledExecutorService = ExecutorServiceUtil.newScheduledExecutorService(); 253 } 254 return scheduledExecutorService; 255 } 256 257 private synchronized void stopExecutorServices() { 258 ExecutorServiceUtil.shutdown(scheduledExecutorService); 259 scheduledExecutorService = null; 260 261 ExecutorServiceUtil.shutdown(threadPoolExecutor); 262 threadPoolExecutor = null; 263 } 264 265 private void removeShutdownHook() { 266 Thread hook = (Thread) getObject(CoreConstants.SHUTDOWN_HOOK_THREAD); 267 if (hook != null) { 268 removeObject(CoreConstants.SHUTDOWN_HOOK_THREAD); 269 270 try { 271 sm.add(new InfoStatus("Removing shutdownHook " + hook, this)); 272 Runtime runtime = Runtime.getRuntime(); 273 boolean result = runtime.removeShutdownHook(hook); 274 sm.add(new InfoStatus("ShutdownHook removal result: " + result, this)); 275 } catch (IllegalStateException e) { 276 // if JVM is already shutting down, ISE is thrown 277 // no need to do anything else 278 } 279 } 280 } 281 282 public void register(LifeCycle component) { 283 getLifeCycleManager().register(component); 284 } 285 286 /** 287 * Gets the life cycle manager for this context. 288 * <p> 289 * The default implementation lazily initializes an instance of 290 * {@link LifeCycleManager}. Subclasses may override to provide a custom manager 291 * implementation, but must take care to return the same manager object for each 292 * call to this method. 293 * <p> 294 * This is exposed primarily to support instrumentation for unit testing. 295 * 296 * @return manager object 297 */ 298 synchronized LifeCycleManager getLifeCycleManager() { 299 if (lifeCycleManager == null) { 300 lifeCycleManager = new LifeCycleManager(); 301 } 302 return lifeCycleManager; 303 } 304 305 @Override 306 public String toString() { 307 return name; 308 } 309 310 @Override 311 public void addScheduledFuture(ScheduledFuture<?> scheduledFuture) { 312 scheduledFutures.add(scheduledFuture); 313 } 314 315 /** 316 * @deprecated replaced by getCopyOfScheduledFutures 317 */ 318 @Deprecated 319 public List<ScheduledFuture<?>> getScheduledFutures() { 320 return getCopyOfScheduledFutures(); 321 } 322 323 public List<ScheduledFuture<?>> getCopyOfScheduledFutures() { 324 return new ArrayList<ScheduledFuture<?>>(scheduledFutures); 325 } 326 327 public SequenceNumberGenerator getSequenceNumberGenerator() { 328 return sequenceNumberGenerator; 329 } 330 331 public void setSequenceNumberGenerator(SequenceNumberGenerator sequenceNumberGenerator) { 332 this.sequenceNumberGenerator = sequenceNumberGenerator; 333 } 334 335 @Override 336 public void addConfigurationEventListener(ConfigurationEventListener listener) { 337 configurationEventListenerList.add(listener); 338 } 339 340 @Override 341 public void removeConfigurationEventListener(ConfigurationEventListener listener) { 342 configurationEventListenerList.remove(listener); 343 } 344 345 @Override 346 public void fireConfigurationEvent(ConfigurationEvent configurationEvent) { 347 configurationEventListenerList.forEach( l -> l.listen(configurationEvent)); 348 } 349}