< prev index next >
src/java.logging/share/classes/java/util/logging/LogManager.java
Print this page
@@ -31,12 +31,12 @@
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;
/**
* There is a single global LogManager object that is used to
* maintain a set of shared state about Loggers and log services.
@@ -178,14 +178,19 @@
// 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_UNINITIALIZED = 2,
+ STATE_SHUTDOWN = 3; // 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 {
@@ -247,11 +252,11 @@
});
}
// This private class is used as a shutdown hook.
// It does a "reset" to close all open handlers.
- private class Cleaner extends ManagedLocalsThread {
+ private class Cleaner extends Thread {
private Cleaner() {
/* Set context class loader to null in order to avoid
* keeping a strong reference to an application classloader.
*/
@@ -262,20 +267,24 @@
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.
+ configurationLock.lock();
+ // temporarily block any ongoing logging requests until reset finishes
+ // by setting globalHandlersState to STATE_INITIALIZING
+ globalHandlersState = STATE_INITIALIZING;
+ try {
+ // Do a reset to close all active handlers which leaves state at
+ // STATE_INITIALIZING...
reset();
+ } finally {
+ // set globalHandlersState to a final STATE_SHUTDOWN so
+ // that no attempts will be made to initialize them again.
+ globalHandlersState = STATE_SHUTDOWN;
+ configurationLock.unlock();
+ }
}
}
/**
@@ -1312,48 +1321,78 @@
* 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();
+
// Since we are doing a reset we no longer want to initialize
// the global handlers, if they haven't been initialized yet.
- initializedGlobalHandlers = true;
+ // When globalHandlersState == STATE_INITIALIZING it means
+ // we have been called from readConfiguration that is going to set
+ // final state itself. Otherwise we set is to STATE_INITIALIZED.
+ if (globalHandlersState != STATE_INITIALIZING) {
+ 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 +1445,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_SHUTDOWN) {
+ // already in terminal state: don't even bother to read the
+ // configuration
+ return;
+ }
+
+ // change state to STATE_INITIALIZING so that reset() doesn't change it to
+ // STATE_INITIALIZED just yet...
+ globalHandlersState = STATE_INITIALIZING;
+ try {
+ // reset configuration which leaves globalHandlersState at STATE_INITIALIZING
+ // 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 +1496,31 @@
}
// 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;
+ // Note: Marking global handlers as not initialized must be done
+ // before unlocking.
+ 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 +1643,41 @@
}
// 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_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).
+ // So in either case we need to acquire 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 +1768,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 >