--- old/src/share/classes/java/util/logging/LogManager.java 2013-09-05 16:36:01.000000000 +0200 +++ new/src/share/classes/java/util/logging/LogManager.java 2013-09-05 16:36:01.000000000 +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,7 +156,9 @@ // LoggerContext for system loggers and user loggers private final LoggerContext systemContext = new SystemLoggerContext(); private final LoggerContext userContext = new LoggerContext(); - private Logger rootLogger; + private volatile Logger rootLogger; // non final field - make it volatile to + // make sure that other threads will see the new value once + // ensureLogManagerInitialized() has finished executing. // Have we done the primordial reading of the configuration file? // (Must be done after a suitable amount of java.lang.System @@ -169,58 +171,35 @@ private boolean deathImminent; static { - AccessController.doPrivileged(new PrivilegedAction() { - 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() { + @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(); } - - // 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. - // would deadlock with the LogManager.. - // 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; + } catch (Exception ex) { + System.err.println("Could not load Logmanager \"" + cname + "\""); + ex.printStackTrace(); } - }); + if (mgr == null) { + mgr = new LogManager(); + } + return mgr; + + } + }); } @@ -235,6 +214,7 @@ this.setContextClassLoader(null); } + @Override public void run() { // This is to ensure the LogManager. is completed // before synchronized block. Otherwise deadlocks are possible. @@ -271,12 +251,95 @@ } /** + * 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) { + assert initializedCalled || !initializationDone + : "Initialization can't be done if initialized has not been called!"; + if (initializedCalled || initializationDone) { + // If initializationCalled is true and initializationDone is + // false it means that we're in the process of initializing + // the LogManager in this thread. 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() { + @Override + public Object run() { + assert rootLogger == null; + assert initializedCalled && !initializationDone; + // Read configuration. This was previously triggered + // by the new RootLogger() constructor - but no longer. + 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 +358,7 @@ try { AccessController.doPrivileged(new PrivilegedExceptionAction() { + @Override public Void run() throws Exception { readConfiguration(); @@ -304,8 +368,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 +455,7 @@ } // LoggerContext maps from AppContext - private static WeakHashMap contextsMap = null; + private WeakHashMap contextsMap = null; // Returns the LoggerContext for the user code (i.e. application or AppContext). // Loggers are isolated from each AppContext. @@ -414,10 +477,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 +487,14 @@ return context != null ? context : userContext; } + // The system context. + final LoggerContext getSystemContext() { + return systemContext; + } + private List contexts() { List cxs = new ArrayList<>(); - cxs.add(systemContext); + cxs.add(getSystemContext()); cxs.add(getUserContext()); return cxs; } @@ -450,7 +515,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 +542,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 +566,7 @@ // if logger already exists but handlers not set final Logger l = logger; AccessController.doPrivileged(new PrivilegedAction() { + @Override public Void run() { for (Handler hdl : l.getHandlers()) { sysLogger.addHandler(hdl); @@ -519,24 +585,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 namedLoggers = new Hashtable<>(); // Tree of named Loggers private final LogNode root; - private final boolean requiresDefaultLoggers; private LoggerContext() { - this(false); - } - private LoggerContext(boolean requiresDefaultLoggers) { this.root = new LogNode(null, this); - this.requiresDefaultLoggers = requiresDefaultLoggers; + } + + + // 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; + } + + // 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 +642,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 +674,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 +692,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 +719,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 +757,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 +815,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() { + @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 +836,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 +877,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 +921,7 @@ final String handlersPropertyName) { AccessController.doPrivileged(new PrivilegedAction() { + @Override public Object run() { String names[] = parseClassNames(handlersPropertyName); for (int i = 0; i < names.length; i++) { @@ -1014,6 +1114,7 @@ // There is a security manager. Raise privilege before // calling setLevel. AccessController.doPrivileged(new PrivilegedAction() { + @Override public Object run() { logger.setLevel(level); return null; @@ -1032,6 +1133,7 @@ // There is a security manager. Raise privilege before // calling setParent. AccessController.doPrivileged(new PrivilegedAction() { + @Override public Object run() { logger.setParent(parent); return null; @@ -1129,15 +1231,10 @@ 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 +1298,7 @@ } hands = hands.trim(); int ix = 0; - Vector result = new Vector<>(); + final List result = new ArrayList<>(); while (ix < hands.length()) { int end = ix; while (end < hands.length()) { @@ -1471,28 +1568,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();