/** * Logback: the reliable, generic, fast and flexible logging framework. * Copyright (C) 1999-2015, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under * either the terms of the Eclipse Public License v1.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) * * under the terms of the GNU Lesser General Public License version 2.1 * as published by the Free Software Foundation. */ package ch.qos.logback.core; import static ch.qos.logback.core.CoreConstants.CONTEXT_NAME_KEY; import static ch.qos.logback.core.CoreConstants.HOSTNAME_KEY; import static ch.qos.logback.core.CoreConstants.FA_FILENAME_COLLISION_MAP; import static ch.qos.logback.core.CoreConstants.RFA_FILENAME_PATTERN_COLLISION_MAP; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.ExecutorService; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; import ch.qos.logback.core.rolling.helper.FileNamePattern; import ch.qos.logback.core.spi.LifeCycle; import ch.qos.logback.core.spi.LogbackLock; import ch.qos.logback.core.status.StatusManager; import ch.qos.logback.core.util.ContextUtil; import ch.qos.logback.core.util.ExecutorServiceUtil; public class ContextBase implements Context, LifeCycle { private long birthTime = System.currentTimeMillis(); private String name; private StatusManager sm = new BasicStatusManager(); // TODO propertyMap should be observable so that we can be notified // when it changes so that a new instance of propertyMap can be // serialized. For the time being, we ignore this shortcoming. Map<String, String> propertyMap = new HashMap<String, String>(); Map<String, Object> objectMap = new HashMap<String, Object>(); LogbackLock configurationLock = new LogbackLock(); private ScheduledExecutorService scheduledExecutorService; protected List<ScheduledFuture<?>> scheduledFutures = new ArrayList<ScheduledFuture<?>>(1); private LifeCycleManager lifeCycleManager; private boolean started; public ContextBase() { initCollisionMaps(); } public StatusManager getStatusManager() { return sm; } /** * Set the {@link StatusManager} for this context. Note that by default this * context is initialized with a {@link BasicStatusManager}. A null value for * the 'statusManager' argument is not allowed. * <p/> * <p> A malicious attacker can set the status manager to a dummy instance, * disabling internal error reporting. * * @param statusManager the new status manager */ public void setStatusManager(StatusManager statusManager) { // this method was added in response to http://jira.qos.ch/browse/LBCORE-35 if (statusManager == null) { throw new IllegalArgumentException("null StatusManager not allowed"); } this.sm = statusManager; } public Map<String, String> getCopyOfPropertyMap() { return new HashMap<String, String>(propertyMap); } public void putProperty(String key, String val) { if (HOSTNAME_KEY.equalsIgnoreCase(key)) { putHostnameProperty(val); } else { this.propertyMap.put(key, val); } } protected void initCollisionMaps() { putObject(FA_FILENAME_COLLISION_MAP, new HashMap<String, String>()); putObject(RFA_FILENAME_PATTERN_COLLISION_MAP, new HashMap<String, FileNamePattern>()); } /** * Given a key, return the corresponding property value. If invoked with * the special key "CONTEXT_NAME", the name of the context is returned. * * @param key * @return */ public String getProperty(String key) { if (CONTEXT_NAME_KEY.equals(key)) return getName(); if (HOSTNAME_KEY.equalsIgnoreCase(key)) { return lazyGetHostname(); } return (String) this.propertyMap.get(key); } private String lazyGetHostname() { String hostname = (String) this.propertyMap.get(HOSTNAME_KEY); if (hostname == null) { hostname = new ContextUtil(this).safelyGetLocalHostName(); putHostnameProperty(hostname); } return hostname; } private void putHostnameProperty(String hostname) { String existingHostname = (String) this.propertyMap.get(HOSTNAME_KEY); if (existingHostname == null) { this.propertyMap.put(CoreConstants.HOSTNAME_KEY, hostname); } else { } } public Object getObject(String key) { return objectMap.get(key); } public void putObject(String key, Object value) { objectMap.put(key, value); } public void removeObject(String key) { objectMap.remove(key); } public String getName() { return name; } public void start() { // We'd like to create the executor service here, but we can't; // ContextBase has not always implemented LifeCycle and there are *many* // uses (mostly in tests) that would need to be modified. started = true; } public void stop() { // We don't check "started" here, because the executor service uses // lazy initialization, rather than being created in the start method stopExecutorService(); started = false; } public boolean isStarted() { return started; } /** * Clear the internal objectMap and all properties. Removes registered * shutdown hook */ public void reset() { removeShutdownHook(); getLifeCycleManager().reset(); propertyMap.clear(); objectMap.clear(); } /** * The context name can be set only if it is not already set, or if the * current name is the default context name, namely "default", or if the * current name and the old name are the same. * * @throws IllegalStateException if the context already has a name, other than "default". */ public void setName(String name) throws IllegalStateException { if (name != null && name.equals(this.name)) { return; // idempotent naming } if (this.name == null || CoreConstants.DEFAULT_CONTEXT_NAME.equals(this.name)) { this.name = name; } else { throw new IllegalStateException("Context has been already given a name"); } } public long getBirthTime() { return birthTime; } public Object getConfigurationLock() { return configurationLock; } @Override /** * @deprecated */ public synchronized ExecutorService getExecutorService() { return getScheduledExecutorService(); } @Override public synchronized ScheduledExecutorService getScheduledExecutorService() { if (scheduledExecutorService == null) { scheduledExecutorService = ExecutorServiceUtil.newScheduledExecutorService(); } return scheduledExecutorService; } private synchronized void stopExecutorService() { if (scheduledExecutorService != null) { ExecutorServiceUtil.shutdown(scheduledExecutorService); scheduledExecutorService = null; } } private void removeShutdownHook() { Thread hook = (Thread) getObject(CoreConstants.SHUTDOWN_HOOK_THREAD); if (hook != null) { removeObject(CoreConstants.SHUTDOWN_HOOK_THREAD); try { Runtime.getRuntime().removeShutdownHook(hook); } catch (IllegalStateException e) { // if JVM is already shutting down, ISE is thrown // no need to do anything else } } } public void register(LifeCycle component) { getLifeCycleManager().register(component); } /** * Gets the life cycle manager for this context. * <p> * The default implementation lazily initializes an instance of * {@link LifeCycleManager}. Subclasses may override to provide a custom * manager implementation, but must take care to return the same manager * object for each call to this method. * <p> * This is exposed primarily to support instrumentation for unit testing. * * @return manager object */ synchronized LifeCycleManager getLifeCycleManager() { if (lifeCycleManager == null) { lifeCycleManager = new LifeCycleManager(); } return lifeCycleManager; } @Override public String toString() { return name; } @Override public void addScheduledFuture(ScheduledFuture<?> scheduledFuture) { scheduledFutures.add(scheduledFuture); } public List<ScheduledFuture<?>> getScheduledFutures() { return new ArrayList<ScheduledFuture<?>>(scheduledFutures); } }