< prev index next >

src/java.logging/share/classes/java/util/logging/LogManager.java

Print this page

        

*** 31,40 **** --- 31,41 ---- import java.security.*; import java.lang.ref.ReferenceQueue; import java.lang.ref.WeakReference; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CopyOnWriteArrayList; + import java.util.concurrent.locks.ReentrantLock; import sun.misc.JavaAWTAccess; import sun.misc.ManagedLocalsThread; import sun.misc.SharedSecrets; /**
*** 178,191 **** // Have we done the primordial reading of the configuration file? // (Must be done after a suitable amount of java.lang.System // initialization has been done) private volatile boolean readPrimordialConfiguration; // Have we initialized global (root) handlers yet? ! // This gets set to false in readConfiguration ! private boolean initializedGlobalHandlers = true; ! // True if JVM death is imminent and the exit hook has been called. ! private boolean deathImminent; // This list contains the loggers for which some handlers have been // explicitly configured in the configuration file. // It prevents these loggers from being arbitrarily garbage collected. private static final class CloseOnReset { --- 179,199 ---- // Have we done the primordial reading of the configuration file? // (Must be done after a suitable amount of java.lang.System // initialization has been done) private volatile boolean readPrimordialConfiguration; // Have we initialized global (root) handlers yet? ! // This gets set to STATE_UNINITIALIZED in readConfiguration ! private static final int ! STATE_INITIALIZED = 0, // initial state ! STATE_INITIALIZING = 1, ! STATE_READING_CONFIG = 2, ! STATE_UNINITIALIZED = 3, ! STATE_SHUTTING_DOWN = 4, ! STATE_SHUTDOWN = 5; // terminal state ! private volatile int globalHandlersState; // = STATE_INITIALIZED; ! // A concurrency lock for reset(), readConfiguration() and Cleaner. ! private final ReentrantLock configurationLock = new ReentrantLock(); // This list contains the loggers for which some handlers have been // explicitly configured in the configuration file. // It prevents these loggers from being arbitrarily garbage collected. private static final class CloseOnReset {
*** 262,280 **** public void run() { // This is to ensure the LogManager.<clinit> is completed // before synchronized block. Otherwise deadlocks are possible. LogManager mgr = manager; ! // If the global handlers haven't been initialized yet, we ! // don't want to initialize them just so we can close them! ! synchronized (LogManager.this) { ! // Note that death is imminent. ! deathImminent = true; ! initializedGlobalHandlers = true; ! } ! // Do a reset to close all active handlers. reset(); } } --- 270,289 ---- public void run() { // This is to ensure the LogManager.<clinit> is completed // before synchronized block. Otherwise deadlocks are possible. LogManager mgr = manager; ! // set globalHandlersState to STATE_SHUTTING_DOWN atomically so that ! // no attempts are made to (re)initialize the handlers or (re)read ! // the configuration again. This is also a signal for reset() to ! // change the state to terminal STATE_SHUTDOWN. ! configurationLock.lock(); ! globalHandlersState = STATE_SHUTTING_DOWN; ! configurationLock.unlock(); ! // Do a reset to close all active handlers which transitions state ! // to STATE_SHUTDOWN. reset(); } }
*** 1312,1359 **** * the caller does not have LoggingPermission("control"). */ public void reset() throws SecurityException { checkPermission(); List<CloseOnReset> persistent; ! synchronized (this) { props = new Properties(); // make sure we keep the loggers persistent until reset is done. // Those are the loggers for which we previously created a // handler from the configuration, and we need to prevent them // from being gc'ed until those handlers are closed. persistent = new ArrayList<>(closeOnResetLoggers); closeOnResetLoggers.clear(); // Since we are doing a reset we no longer want to initialize // the global handlers, if they haven't been initialized yet. ! initializedGlobalHandlers = true; } for (LoggerContext cx : contexts()) { Enumeration<String> enum_ = cx.getLoggerNames(); while (enum_.hasMoreElements()) { String name = enum_.nextElement(); Logger logger = cx.findLogger(name); if (logger != null) { resetLogger(logger); } } } - persistent.clear(); - } ! // Private method to reset an individual target logger. ! private void resetLogger(Logger logger) { ! // Close all the Logger's handlers. Handler[] targets = logger.getHandlers(); for (Handler h : targets) { logger.removeHandler(h); try { h.close(); } catch (Exception ex) { // Problems closing a handler? Keep going... } } String name = logger.getName(); if (name != null && name.equals("")) { // This is the root logger. logger.setLevel(defaultLevel); } else { --- 1321,1404 ---- * the caller does not have LoggingPermission("control"). */ public void reset() throws SecurityException { checkPermission(); + List<CloseOnReset> persistent; ! ! // We don't want reset() and readConfiguration() ! // to run in parallel ! configurationLock.lock(); ! try { ! if (globalHandlersState == STATE_SHUTDOWN) { ! // already in terminal state ! return; ! } ! // install new empty properties props = new Properties(); // make sure we keep the loggers persistent until reset is done. // Those are the loggers for which we previously created a // handler from the configuration, and we need to prevent them // from being gc'ed until those handlers are closed. persistent = new ArrayList<>(closeOnResetLoggers); closeOnResetLoggers.clear(); + + if (globalHandlersState == STATE_SHUTTING_DOWN) { + // if reset has been called from shutdown-hook (Cleaner), + // then transition state to terminal STATE_SHUTDOWN. + globalHandlersState = STATE_SHUTDOWN; + } else if (globalHandlersState == STATE_READING_CONFIG) { + // if reset has been called from readConfiguration() which + // already holds the lock then the state will be changed by + // readConfiguration() depending on the outcome. + } else { + // ...user calling reset()... // Since we are doing a reset we no longer want to initialize // the global handlers, if they haven't been initialized yet. ! globalHandlersState = STATE_INITIALIZED; } + for (LoggerContext cx : contexts()) { + resetLoggerContext(cx); + } + + persistent.clear(); + } finally { + configurationLock.unlock(); + } + } + + private void resetLoggerContext(LoggerContext cx) { Enumeration<String> enum_ = cx.getLoggerNames(); while (enum_.hasMoreElements()) { String name = enum_.nextElement(); Logger logger = cx.findLogger(name); if (logger != null) { resetLogger(logger); } } } ! private void closeHandlers(Logger logger) { Handler[] targets = logger.getHandlers(); for (Handler h : targets) { logger.removeHandler(h); try { h.close(); } catch (Exception ex) { // Problems closing a handler? Keep going... } } + } + + // Private method to reset an individual target logger. + private void resetLogger(Logger logger) { + // Close all the Logger handlers. + closeHandlers(logger); + + // Reset Logger level String name = logger.getName(); if (name != null && name.equals("")) { // This is the root logger. logger.setLevel(defaultLevel); } else {
*** 1406,1419 **** * the caller does not have LoggingPermission("control"). * @exception IOException if there are problems reading from the stream. */ public void readConfiguration(InputStream ins) throws IOException, SecurityException { checkPermission(); reset(); - // Load the properties try { props.load(ins); } catch (IllegalArgumentException x) { // props.load may throw an IllegalArgumentException if the stream // contains malformed Unicode escape sequences. // We wrap that in an IOException as readConfiguration is --- 1451,1482 ---- * the caller does not have LoggingPermission("control"). * @exception IOException if there are problems reading from the stream. */ public void readConfiguration(InputStream ins) throws IOException, SecurityException { checkPermission(); + + // We don't want reset() and readConfiguration() to run + // in parallel. + configurationLock.lock(); + try { + if (globalHandlersState == STATE_SHUTTING_DOWN || + globalHandlersState == STATE_SHUTDOWN) { + // shutting down or already in terminal state: don't even bother + // to read the configuration + return; + } + + // change state to STATE_READING_CONFIG to signal reset() to not change it + globalHandlersState = STATE_READING_CONFIG; + try { + // reset configuration which leaves globalHandlersState at STATE_READING_CONFIG + // so that while reading configuration, any ongoing logging requests block and + // wait for the outcome (see the end of this try statement) reset(); try { + // Load the properties props.load(ins); } catch (IllegalArgumentException x) { // props.load may throw an IllegalArgumentException if the stream // contains malformed Unicode escape sequences. // We wrap that in an IOException as readConfiguration is
*** 1439,1457 **** } // Set levels on any pre-existing loggers, based on the new properties. setLevelsOnExistingLoggers(); - try { - invokeConfigurationListeners(); - } finally { // Note that we need to reinitialize global handles when // they are first referenced. ! synchronized (this) { ! initializedGlobalHandlers = false; } } } /** * Get the value of a logging property. * The method returns null if the property is not found. --- 1502,1529 ---- } // Set levels on any pre-existing loggers, based on the new properties. setLevelsOnExistingLoggers(); // Note that we need to reinitialize global handles when // they are first referenced. ! globalHandlersState = STATE_UNINITIALIZED; ! } catch (Throwable t) { ! // If there were any trouble, then set state to STATE_INITIALIZED ! // so that no global handlers reinitialization is performed on not fully ! // initialized configuration. ! globalHandlersState = STATE_INITIALIZED; ! // re-throw ! throw t; } + } finally { + configurationLock.unlock(); } + + // should be called out of lock to avoid dead-lock situations + // when user code is involved + invokeConfigurationListeners(); } /** * Get the value of a logging property. * The method returns null if the property is not found.
*** 1574,1597 **** } // Private method to load the global handlers. // We do the real work lazily, when the global handlers // are first used. ! private synchronized void initializeGlobalHandlers() { ! if (initializedGlobalHandlers) { return; } ! initializedGlobalHandlers = true; ! ! if (deathImminent) { ! // Aaargh... ! // The VM is shutting down and our exit hook has been called. ! // Avoid allocating global handlers. ! return; } loadLoggerHandlers(rootLogger, null, "handlers"); } static final Permission controlPermission = new LoggingPermission("control", null); void checkPermission() { --- 1646,1691 ---- } // Private method to load the global handlers. // We do the real work lazily, when the global handlers // are first used. ! private void initializeGlobalHandlers() { ! int state = globalHandlersState; ! if (state == STATE_INITIALIZED || ! state == STATE_SHUTTING_DOWN || ! state == STATE_SHUTDOWN) { ! // Nothing to do: return. return; } ! // If we have not initialized global handlers yet (or need to ! // reinitialize them), lets do it now (this case is indicated by ! // globalHandlersState == STATE_UNINITIALIZED). ! // If we are in the process of initializing global handlers we ! // also need to lock & wait (this case is indicated by ! // globalHandlersState == STATE_INITIALIZING). ! // If we are in the process of reading configuration we also need to ! // wait to see what the outcome will be (this case ! // is indicated by globalHandlersState == STATE_READING_CONFIG) ! // So in either case we need to wait for the lock. ! configurationLock.lock(); ! try { ! if (globalHandlersState != STATE_UNINITIALIZED) { ! return; // recursive call or nothing to do } + // set globalHandlersState to STATE_INITIALIZING first to avoid + // getting an infinite recursion when loadLoggerHandlers(...) + // is going to call addHandler(...) + globalHandlersState = STATE_INITIALIZING; + try { loadLoggerHandlers(rootLogger, null, "handlers"); + } finally { + globalHandlersState = STATE_INITIALIZED; + } + } finally { + configurationLock.unlock(); + } } static final Permission controlPermission = new LoggingPermission("control", null); void checkPermission() {
*** 1682,1692 **** } // Private method to be called when the configuration has // changed to apply any level settings to any pre-existing loggers. ! synchronized private void setLevelsOnExistingLoggers() { Enumeration<?> enum_ = props.propertyNames(); while (enum_.hasMoreElements()) { String key = (String)enum_.nextElement(); if (!key.endsWith(".level")) { // Not a level definition. --- 1776,1786 ---- } // Private method to be called when the configuration has // changed to apply any level settings to any pre-existing loggers. ! private void setLevelsOnExistingLoggers() { Enumeration<?> enum_ = props.propertyNames(); while (enum_.hasMoreElements()) { String key = (String)enum_.nextElement(); if (!key.endsWith(".level")) { // Not a level definition.
< prev index next >