--- a/jdk/src/share/classes/java/util/logging/LogManager.java Mon Sep 09 11:08:20 2013 +0800
+++ b/jdk/src/share/classes/java/util/logging/LogManager.java Mon Sep 09 13:59:51 2013 +0200
@@ -144,7 +144,7 @@
public class LogManager {
// The global LogManager object
- private static LogManager manager;
+ private static final LogManager manager;
private Properties props = new Properties();
private final static Level defaultLevel = Level.INFO;
@@ -156,8 +156,10 @@
// LoggerContext for system loggers and user loggers
private final LoggerContext systemContext = new SystemLoggerContext();
private final LoggerContext userContext = new LoggerContext();
- private Logger rootLogger;
-
+ // non final field - make it volatile to make sure that other threads
+ // will see the new value once ensureLogManagerInitialized() has finished
+ // executing.
+ private volatile Logger rootLogger;
// 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)
@@ -169,58 +171,35 @@
private boolean deathImminent;
static {
- AccessController.doPrivileged(new PrivilegedAction<Object>() {
- public Object run() {
- String cname = null;
- try {
- cname = System.getProperty("java.util.logging.manager");
- if (cname != null) {
- try {
- Class<?> clz = ClassLoader.getSystemClassLoader().loadClass(cname);
- manager = (LogManager) clz.newInstance();
- } catch (ClassNotFoundException ex) {
- Class<?> clz = Thread.currentThread().getContextClassLoader().loadClass(cname);
- manager = (LogManager) clz.newInstance();
- }
+ manager = AccessController.doPrivileged(new PrivilegedAction<LogManager>() {
+ @Override
+ public LogManager run() {
+ LogManager mgr = null;
+ String cname = null;
+ try {
+ cname = System.getProperty("java.util.logging.manager");
+ if (cname != null) {
+ try {
+ Class<?> clz = ClassLoader.getSystemClassLoader()
+ .loadClass(cname);
+ mgr = (LogManager) clz.newInstance();
+ } catch (ClassNotFoundException ex) {
+ Class<?> clz = Thread.currentThread()
+ .getContextClassLoader().loadClass(cname);
+ mgr = (LogManager) clz.newInstance();
}
- } catch (Exception ex) {
- System.err.println("Could not load Logmanager \"" + cname + "\"");
- ex.printStackTrace();
}
- if (manager == null) {
- manager = new LogManager();
- }
+ } catch (Exception ex) {
+ System.err.println("Could not load Logmanager \"" + cname + "\"");
+ ex.printStackTrace();
+ }
+ if (mgr == null) {
+ mgr = new LogManager();
+ }
+ return mgr;
- // Create and retain Logger for the root of the namespace.
- manager.rootLogger = manager.new RootLogger();
- // since by design the global manager's userContext and
- // systemContext don't have their requiresDefaultLoggers
- // flag set - we make sure to add the root logger to
- // the global manager's default contexts here.
- manager.addLogger(manager.rootLogger);
- manager.systemContext.addLocalLogger(manager.rootLogger, false);
- manager.userContext.addLocalLogger(manager.rootLogger, false);
-
- // Adding the global Logger. Doing so in the Logger.<clinit>
- // would deadlock with the LogManager.<clinit>.
- // Do not call Logger.getGlobal() here as this might trigger
- // the deadlock too.
- @SuppressWarnings("deprecation")
- final Logger global = Logger.global;
- global.setLogManager(manager);
-
- // Make sure the global logger will be registered in the
- // global manager's default contexts.
- manager.addLogger(global);
- manager.systemContext.addLocalLogger(global, false);
- manager.userContext.addLocalLogger(global, false);
-
- // We don't call readConfiguration() here, as we may be running
- // very early in the JVM startup sequence. Instead readConfiguration
- // will be called lazily in getLogManager().
- return null;
- }
- });
+ }
+ });
}
@@ -235,6 +214,7 @@
this.setContextClassLoader(null);
}
+ @Override
public void run() {
// This is to ensure the LogManager.<clinit> is completed
// before synchronized block. Otherwise deadlocks are possible.
@@ -271,12 +251,103 @@
}
/**
+ * Lazy initialization: if this instance of manager is the global
+ * manager then this method will read the initial configuration and
+ * add the root logger and global logger by calling addLogger().
+ *
+ * Note that it is subtly different from what we do in LoggerContext.
+ * In LoggerContext we're patching up the logger context tree in order to add
+ * the root and global logger *to the context tree*.
+ *
+ * For this to work, addLogger() must have already have been called
+ * once on the LogManager instance for the default logger being
+ * added.
+ *
+ * This is why ensureLogManagerInitialized() needs to be called before
+ * any logger is added to any logger context.
+ *
+ */
+ private boolean initializedCalled = false;
+ private volatile boolean initializationDone = false;
+ final void ensureLogManagerInitialized() {
+ final LogManager owner = this;
+ if (initializationDone || owner != manager) {
+ // we don't want to do this twice, and we don't want to do
+ // this on private manager instances.
+ return;
+ }
+
+ // Maybe another thread has called ensureLogManagerInitialized()
+ // before us and is still executing it. If so we will block until
+ // the log manager has finished initialized, then acquire the monitor,
+ // notice that initializationDone is now true and return.
+ // Otherwise - we have come here first! We will acquire the monitor,
+ // see that initializationDone is still false, and perform the
+ // initialization.
+ //
+ synchronized(this) {
+ // If initializedCalled is true it means that we're already in
+ // the process of initializing the LogManager in this thread.
+ // There has been a recursive call to ensureLogManagerInitialized().
+ final boolean isRecursiveInitialization = (initializedCalled == true);
+
+ assert initializedCalled || !initializationDone
+ : "Initialization can't be done if initialized has not been called!";
+
+ if (isRecursiveInitialization || initializationDone) {
+ // If isRecursiveInitialization is true it means that we're
+ // already in the process of initializing the LogManager in
+ // this thread. There has been a recursive call to
+ // ensureLogManagerInitialized(). We should not proceed as
+ // it would lead to infinite recursion.
+ //
+ // If initializationDone is true then it means the manager
+ // has finished initializing; just return: we're done.
+ return;
+ }
+ // Calling addLogger below will in turn call requiresDefaultLogger()
+ // which will call ensureLogManagerInitialized().
+ // We use initializedCalled to break the recursion.
+ initializedCalled = true;
+ try {
+ AccessController.doPrivileged(new PrivilegedAction<Object>() {
+ @Override
+ public Object run() {
+ assert rootLogger == null;
+ assert initializedCalled && !initializationDone;
+
+ // Read configuration.
+ owner.readPrimordialConfiguration();
+
+ // Create and retain Logger for the root of the namespace.
+ owner.rootLogger = owner.new RootLogger();
+ owner.addLogger(owner.rootLogger);
+
+ // Adding the global Logger.
+ // Do not call Logger.getGlobal() here as this might trigger
+ // subtle inter-dependency issues.
+ @SuppressWarnings("deprecation")
+ final Logger global = Logger.global;
+
+ // Make sure the global logger will be registered in the
+ // global manager
+ owner.addLogger(global);
+ return null;
+ }
+ });
+ } finally {
+ initializationDone = true;
+ }
+ }
+ }
+
+ /**
* Returns the global LogManager object.
* @return the global LogManager object
*/
public static LogManager getLogManager() {
if (manager != null) {
- manager.readPrimordialConfiguration();
+ manager.ensureLogManagerInitialized();
}
return manager;
}
@@ -295,6 +366,7 @@
try {
AccessController.doPrivileged(new PrivilegedExceptionAction<Void>() {
+ @Override
public Void run() throws Exception {
readConfiguration();
@@ -304,8 +376,7 @@
}
});
} catch (Exception ex) {
- // System.err.println("Can't read logging configuration:");
- // ex.printStackTrace();
+ assert false : "Exception raised while reading logging configuration: " + ex;
}
}
}
@@ -392,7 +463,7 @@
}
// LoggerContext maps from AppContext
- private static WeakHashMap<Object, LoggerContext> contextsMap = null;
+ private WeakHashMap<Object, LoggerContext> contextsMap = null;
// Returns the LoggerContext for the user code (i.e. application or AppContext).
// Loggers are isolated from each AppContext.
@@ -414,10 +485,7 @@
context = contextsMap.get(ecx);
if (context == null) {
// Create a new LoggerContext for the applet.
- // The new logger context has its requiresDefaultLoggers
- // flag set to true - so that these loggers will be
- // lazily added when the context is firt accessed.
- context = new LoggerContext(true);
+ context = new LoggerContext();
contextsMap.put(ecx, context);
}
}
@@ -427,9 +495,14 @@
return context != null ? context : userContext;
}
+ // The system context.
+ final LoggerContext getSystemContext() {
+ return systemContext;
+ }
+
private List<LoggerContext> contexts() {
List<LoggerContext> cxs = new ArrayList<>();
- cxs.add(systemContext);
+ cxs.add(getSystemContext());
cxs.add(getUserContext());
return cxs;
}
@@ -450,7 +523,7 @@
Logger result = getLogger(name);
if (result == null) {
// only allocate the new logger once
- Logger newLogger = new Logger(name, resourceBundleName, caller);
+ Logger newLogger = new Logger(name, resourceBundleName, caller, this);
do {
if (addLogger(newLogger)) {
// We successfully added the new Logger that we
@@ -477,7 +550,7 @@
Logger demandSystemLogger(String name, String resourceBundleName) {
// Add a system logger in the system context's namespace
- final Logger sysLogger = systemContext.demandLogger(name, resourceBundleName);
+ final Logger sysLogger = getSystemContext().demandLogger(name, resourceBundleName);
// Add the system logger to the LogManager's namespace if not exist
// so that there is only one single logger of the given name.
@@ -501,6 +574,7 @@
// if logger already exists but handlers not set
final Logger l = logger;
AccessController.doPrivileged(new PrivilegedAction<Void>() {
+ @Override
public Void run() {
for (Handler hdl : l.getHandlers()) {
sysLogger.addHandler(hdl);
@@ -519,24 +593,52 @@
// doesn't exist in the user context, it'll also be added to the user context.
// The user context is queried by the user code and all other loggers are
// added in the user context.
- static class LoggerContext {
+ class LoggerContext {
// Table of named Loggers that maps names to Loggers.
private final Hashtable<String,LoggerWeakRef> namedLoggers = new Hashtable<>();
// Tree of named Loggers
private final LogNode root;
- private final boolean requiresDefaultLoggers;
private LoggerContext() {
- this(false);
+ this.root = new LogNode(null, this);
+ }
+
+
+ // Tells whether default loggers are required in this context.
+ // If true, the default loggers will be lazily added.
+ final boolean requiresDefaultLoggers() {
+ final boolean requiresDefaultLoggers = (getOwner() == manager);
+ if (requiresDefaultLoggers) {
+ getOwner().ensureLogManagerInitialized();
+ }
+ return requiresDefaultLoggers;
}
- private LoggerContext(boolean requiresDefaultLoggers) {
- this.root = new LogNode(null, this);
- this.requiresDefaultLoggers = requiresDefaultLoggers;
+
+ // This context's LogManager.
+ final LogManager getOwner() {
+ return LogManager.this;
+ }
+
+ // This context owner's root logger, which if not null, and if
+ // the context requires default loggers, will be added to the context
+ // logger's tree.
+ final Logger getRootLogger() {
+ return getOwner().rootLogger;
+ }
+
+ // The global logger, which if not null, and if
+ // the context requires default loggers, will be added to the context
+ // logger's tree.
+ final Logger getGlobalLogger() {
+ @SuppressWarnings("deprecated") // avoids initialization cycles.
+ final Logger global = Logger.global;
+ return global;
}
Logger demandLogger(String name, String resourceBundleName) {
// a LogManager subclass may have its own implementation to add and
// get a Logger. So delegate to the LogManager to do the work.
- return manager.demandLogger(name, resourceBundleName, null);
+ final LogManager owner = getOwner();
+ return owner.demandLogger(name, resourceBundleName, null);
}
@@ -548,10 +650,10 @@
// or getLoggerNames()
//
private void ensureInitialized() {
- if (requiresDefaultLoggers) {
+ if (requiresDefaultLoggers()) {
// Ensure that the root and global loggers are set.
- ensureDefaultLogger(manager.rootLogger);
- ensureDefaultLogger(Logger.global);
+ ensureDefaultLogger(getRootLogger());
+ ensureDefaultLogger(getGlobalLogger());
}
}
@@ -580,13 +682,13 @@
// before adding 'logger'.
//
private void ensureAllDefaultLoggers(Logger logger) {
- if (requiresDefaultLoggers) {
+ if (requiresDefaultLoggers()) {
final String name = logger.getName();
if (!name.isEmpty()) {
- ensureDefaultLogger(manager.rootLogger);
- }
- if (!Logger.GLOBAL_LOGGER_NAME.equals(name)) {
- ensureDefaultLogger(Logger.global);
+ ensureDefaultLogger(getRootLogger());
+ if (!Logger.GLOBAL_LOGGER_NAME.equals(name)) {
+ ensureDefaultLogger(getGlobalLogger());
+ }
}
}
}
@@ -598,8 +700,8 @@
// This check is simple sanity: we do not want that this
// method be called for anything else than Logger.global
// or owner.rootLogger.
- if (!requiresDefaultLoggers || logger == null
- || logger != Logger.global && logger != manager.rootLogger) {
+ if (!requiresDefaultLoggers() || logger == null
+ || logger != Logger.global && logger != LogManager.this.rootLogger) {
// the case where we have a non null logger which is neither
// Logger.global nor manager.rootLogger indicates a serious
@@ -625,7 +727,7 @@
boolean addLocalLogger(Logger logger) {
// no need to add default loggers if it's not required
- return addLocalLogger(logger, requiresDefaultLoggers);
+ return addLocalLogger(logger, requiresDefaultLoggers());
}
// Add a logger to this context. This method will only set its level
@@ -663,11 +765,13 @@
// We're adding a new logger.
// Note that we are creating a weak reference here.
- ref = manager.new LoggerWeakRef(logger);
+ final LogManager owner = getOwner();
+ logger.setLogManager(owner);
+ ref = owner.new LoggerWeakRef(logger);
namedLoggers.put(name, ref);
// Apply any initial level defined for the new logger.
- Level level = manager.getLevelProperty(name + ".level", null);
+ Level level = owner.getLevelProperty(name + ".level", null);
if (level != null) {
doSetLevel(logger, level);
}
@@ -719,10 +823,12 @@
// If logger.getUseParentHandlers() returns 'true' and any of the logger's
// parents have levels or handlers defined, make sure they are instantiated.
private void processParentHandlers(final Logger logger, final String name) {
+ final LogManager owner = getOwner();
AccessController.doPrivileged(new PrivilegedAction<Void>() {
+ @Override
public Void run() {
- if (logger != manager.rootLogger) {
- boolean useParent = manager.getBooleanProperty(name + ".useParentHandlers", true);
+ if (logger != owner.rootLogger) {
+ boolean useParent = owner.getBooleanProperty(name + ".useParentHandlers", true);
if (!useParent) {
logger.setUseParentHandlers(false);
}
@@ -738,8 +844,8 @@
break;
}
String pname = name.substring(0, ix2);
- if (manager.getProperty(pname + ".level") != null ||
- manager.getProperty(pname + ".handlers") != null) {
+ if (owner.getProperty(pname + ".level") != null ||
+ owner.getProperty(pname + ".handlers") != null) {
// This pname has a level/handlers definition.
// Make sure it exists.
demandLogger(pname, null);
@@ -779,16 +885,17 @@
}
}
- static class SystemLoggerContext extends LoggerContext {
+ final class SystemLoggerContext extends LoggerContext {
// Add a system logger in the system context's namespace as well as
// in the LogManager's namespace if not exist so that there is only
// one single logger of the given name. System loggers are visible
// to applications unless a logger of the same name has been added.
+ @Override
Logger demandLogger(String name, String resourceBundleName) {
Logger result = findLogger(name);
if (result == null) {
// only allocate the new system logger once
- Logger newLogger = new Logger(name, resourceBundleName);
+ Logger newLogger = new Logger(name, resourceBundleName, null, getOwner());
do {
if (addLocalLogger(newLogger)) {
// We successfully added the new Logger that we
@@ -822,6 +929,7 @@
final String handlersPropertyName)
{
AccessController.doPrivileged(new PrivilegedAction<Object>() {
+ @Override
public Object run() {
String names[] = parseClassNames(handlersPropertyName);
for (int i = 0; i < names.length; i++) {
@@ -1014,6 +1122,7 @@
// There is a security manager. Raise privilege before
// calling setLevel.
AccessController.doPrivileged(new PrivilegedAction<Object>() {
+ @Override
public Object run() {
logger.setLevel(level);
return null;
@@ -1032,6 +1141,7 @@
// There is a security manager. Raise privilege before
// calling setParent.
AccessController.doPrivileged(new PrivilegedAction<Object>() {
+ @Override
public Object run() {
logger.setParent(parent);
return null;
@@ -1129,14 +1239,9 @@
f = new File(f, "logging.properties");
fname = f.getCanonicalPath();
}
- InputStream in = new FileInputStream(fname);
- BufferedInputStream bin = new BufferedInputStream(in);
- try {
+ try (final InputStream in = new FileInputStream(fname)) {
+ final BufferedInputStream bin = new BufferedInputStream(in);
readConfiguration(bin);
- } finally {
- if (in != null) {
- in.close();
- }
}
}
@@ -1201,7 +1306,7 @@
}
hands = hands.trim();
int ix = 0;
- Vector<String> result = new Vector<>();
+ final List<String> result = new ArrayList<>();
while (ix < hands.length()) {
int end = ix;
while (end < hands.length()) {
@@ -1471,28 +1576,35 @@
// We use a subclass of Logger for the root logger, so
// that we only instantiate the global handlers when they
// are first needed.
- private class RootLogger extends Logger {
+ private final class RootLogger extends Logger {
private RootLogger() {
- super("", null);
+ // We do not call the protected Logger two args constructor here,
+ // to avoid calling LogManager.getLogManager() from within the
+ // RootLogger constructor.
+ super("", null, null, LogManager.this);
setLevel(defaultLevel);
}
+ @Override
public void log(LogRecord record) {
// Make sure that the global handlers have been instantiated.
initializeGlobalHandlers();
super.log(record);
}
+ @Override
public void addHandler(Handler h) {
initializeGlobalHandlers();
super.addHandler(h);
}
+ @Override
public void removeHandler(Handler h) {
initializeGlobalHandlers();
super.removeHandler(h);
}
+ @Override
public Handler[] getHandlers() {
initializeGlobalHandlers();
return super.getHandlers();