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.*; 026 027import ch.qos.logback.core.rolling.helper.FileNamePattern; 028import ch.qos.logback.core.spi.ConfigurationEvent; 029import ch.qos.logback.core.spi.ConfigurationEventListener; 030import ch.qos.logback.core.spi.LifeCycle; 031import ch.qos.logback.core.spi.LogbackLock; 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 LogbackLock configurationLock = new LogbackLock(); 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 private boolean started; 065 066 public ContextBase() { 067 initCollisionMaps(); 068 } 069 070 public StatusManager getStatusManager() { 071 return sm; 072 } 073 074 /** 075 * Set the {@link StatusManager} for this context. Note that by default this 076 * context is initialized with a {@link BasicStatusManager}. A null value for 077 * the 'statusManager' argument is not allowed. 078 * 079 * <p> 080 * A malicious attacker can set the status manager to a dummy instance, 081 * disabling internal error reporting. 082 * 083 * @param statusManager the new status manager 084 */ 085 public void setStatusManager(StatusManager statusManager) { 086 // this method was added in response to http://jira.qos.ch/browse/LBCORE-35 087 if (statusManager == null) { 088 throw new IllegalArgumentException("null StatusManager not allowed"); 089 } 090 this.sm = statusManager; 091 } 092 093 public Map<String, String> getCopyOfPropertyMap() { 094 return new HashMap<String, String>(propertyMap); 095 } 096 097 public void putProperty(String key, String val) { 098 if (HOSTNAME_KEY.equalsIgnoreCase(key)) { 099 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}