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.ConcurrentHashMap; 026import java.util.concurrent.ExecutorService; 027import java.util.concurrent.ScheduledExecutorService; 028import java.util.concurrent.ScheduledFuture; 029 030import ch.qos.logback.core.rolling.helper.FileNamePattern; 031import ch.qos.logback.core.spi.LifeCycle; 032import ch.qos.logback.core.spi.LogbackLock; 033import ch.qos.logback.core.spi.SequenceNumberGenerator; 034import ch.qos.logback.core.status.InfoStatus; 035import ch.qos.logback.core.status.StatusManager; 036import ch.qos.logback.core.util.ExecutorServiceUtil; 037import ch.qos.logback.core.util.NetworkAddressUtil; 038 039public class ContextBase implements Context, LifeCycle { 040 041 private long birthTime = System.currentTimeMillis(); 042 043 private String name; 044 private StatusManager sm = new BasicStatusManager(); 045 // TODO propertyMap should be observable so that we can be notified 046 // when it changes so that a new instance of propertyMap can be 047 // serialized. For the time being, we ignore this shortcoming. 048 Map<String, String> propertyMap = new HashMap<String, String>(); 049 Map<String, Object> objectMap = new ConcurrentHashMap<>(); 050 051 LogbackLock configurationLock = new LogbackLock(); 052 053 private ScheduledExecutorService scheduledExecutorService; 054 protected List<ScheduledFuture<?>> scheduledFutures = new ArrayList<ScheduledFuture<?>>(1); 055 private LifeCycleManager lifeCycleManager; 056 private SequenceNumberGenerator sequenceNumberGenerator; 057 058 private boolean started; 059 060 public ContextBase() { 061 initCollisionMaps(); 062 } 063 064 public StatusManager getStatusManager() { 065 return sm; 066 } 067 068 /** 069 * Set the {@link StatusManager} for this context. Note that by default this 070 * context is initialized with a {@link BasicStatusManager}. A null value for 071 * the 'statusManager' argument is not allowed. 072 * 073 * <p> 074 * A malicious attacker can set the status manager to a dummy instance, 075 * disabling internal error reporting. 076 * 077 * @param statusManager the new status manager 078 */ 079 public void setStatusManager(StatusManager statusManager) { 080 // this method was added in response to http://jira.qos.ch/browse/LBCORE-35 081 if (statusManager == null) { 082 throw new IllegalArgumentException("null StatusManager not allowed"); 083 } 084 this.sm = statusManager; 085 } 086 087 public Map<String, String> getCopyOfPropertyMap() { 088 return new HashMap<String, String>(propertyMap); 089 } 090 091 public void putProperty(String key, String val) { 092 if (HOSTNAME_KEY.equalsIgnoreCase(key)) { 093 putHostnameProperty(val); 094 } else { 095 this.propertyMap.put(key, val); 096 } 097 } 098 099 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}