< prev index next >
src/java.logging/share/classes/java/util/logging/LogManager.java
Print this page
@@ -31,10 +31,11 @@
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,14 +179,21 @@
// 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 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,19 +270,20 @@
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;
- }
+ // 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.
+ // Do a reset to close all active handlers which transitions state
+ // to STATE_SHUTDOWN.
reset();
}
}
@@ -1312,48 +1321,84 @@
* the caller does not have LoggingPermission("control").
*/
public void reset() throws SecurityException {
checkPermission();
+
List<CloseOnReset> persistent;
- synchronized (this) {
+
+ // 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.
- initializedGlobalHandlers = true;
+ 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);
}
}
}
- persistent.clear();
- }
- // Private method to reset an individual target logger.
- private void resetLogger(Logger logger) {
- // Close all the Logger's handlers.
+ 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,14 +1451,32 @@
* 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();
- // Load the properties
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,19 +1502,28 @@
}
// 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;
+ 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,24 +1646,46 @@
}
// 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) {
+ private void initializeGlobalHandlers() {
+ int state = globalHandlersState;
+ if (state == STATE_INITIALIZED ||
+ state == STATE_SHUTTING_DOWN ||
+ state == STATE_SHUTDOWN) {
+ // Nothing to do: return.
return;
}
- initializedGlobalHandlers = true;
-
- if (deathImminent) {
- // Aaargh...
- // The VM is shutting down and our exit hook has been called.
- // Avoid allocating global handlers.
- 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,11 +1776,11 @@
}
// Private method to be called when the configuration has
// changed to apply any level settings to any pre-existing loggers.
- synchronized private void setLevelsOnExistingLoggers() {
+ 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 >