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

Print this page

        

@@ -142,11 +142,11 @@
  * @since 1.4
 */
 
 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;
 
     // The map of the registered listeners. The map value is the registration

@@ -154,11 +154,13 @@
     private final Map<Object,Integer> listenerMap = new HashMap<>();
 
     // 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
     // initialization has been done)
     private volatile boolean readPrimordialConfiguration;

@@ -167,60 +169,37 @@
     private boolean initializedGlobalHandlers = true;
     // True if JVM death is imminent and the exit hook has been called.
     private boolean deathImminent;
 
     static {
-        AccessController.doPrivileged(new PrivilegedAction<Object>() {
-                public Object run() {
+        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);
-                                manager = (LogManager) clz.newInstance();
+                            Class<?> clz = ClassLoader.getSystemClassLoader()
+                                    .loadClass(cname);
+                            mgr = (LogManager) clz.newInstance();
                             } catch (ClassNotFoundException ex) {
-                                Class<?> clz = Thread.currentThread().getContextClassLoader().loadClass(cname);
-                                manager = (LogManager) clz.newInstance();
+                            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();
+                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;
                 }
             });
     }
 
 

@@ -233,10 +212,11 @@
              * keeping a strong reference to an application classloader.
              */
             this.setContextClassLoader(null);
         }
 
+        @Override
         public void run() {
             // This is to ensure the LogManager.<clinit> is completed
             // before synchronized block. Otherwise deadlocks are possible.
             LogManager mgr = manager;
 

@@ -269,16 +249,99 @@
             // We do not need to register shutdownHook.
         }
     }
 
     /**
+     * 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<Object>() {
+                    @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;
     }
 
     private void readPrimordialConfiguration() {

@@ -293,21 +356,21 @@
                     }
                     readPrimordialConfiguration = true;
 
                     try {
                         AccessController.doPrivileged(new PrivilegedExceptionAction<Void>() {
+                                @Override
                                 public Void run() throws Exception {
                                     readConfiguration();
 
                                     // Platform loggers begin to delegate to java.util.logging.Logger
                                     sun.util.logging.PlatformLogger.redirectPlatformLoggers();
                                     return null;
                                 }
                             });
                     } catch (Exception ex) {
-                        // System.err.println("Can't read logging configuration:");
-                        // ex.printStackTrace();
+                        assert false : "Exception raised while reading logging configuration: " + ex;
                     }
                 }
             }
         }
     }

@@ -390,11 +453,11 @@
             }
         }
     }
 
     // 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.
     private LoggerContext getUserContext() {
         LoggerContext context = null;

@@ -412,26 +475,28 @@
                         contextsMap = new WeakHashMap<>();
                     }
                     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);
                     }
                 }
             }
         }
         // for standalone app, return userContext
         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;
     }
 
     // Find or create a specified logger instance. If a logger has

@@ -448,11 +513,11 @@
     // readConfiguration, and other methods.
     Logger demandLogger(String name, String resourceBundleName, Class<?> caller) {
         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
                     // created above so return it without refetching.
                     return newLogger;

@@ -475,11 +540,11 @@
         return result;
     }
 
     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.
         // System loggers are visible to applications unless a logger of
         // the same name has been added.

@@ -499,10 +564,11 @@
         // LogManager will set the sysLogger's handlers via LogManager.addLogger method.
         if (logger != sysLogger && sysLogger.getHandlers().length == 0) {
             // 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);
                     }
                     return null;

@@ -517,28 +583,56 @@
     // context.  The system context is used to maintain the namespace for
     // all system loggers and is queried by the system code.  If a system logger
     // 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);
-        }
-        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);
         }
 
 
         // Due to subtle deadlock issues getUserContext() no longer
         // calls addLocalLogger(rootLogger);

@@ -546,14 +640,14 @@
         // Checks that the context is properly initialized
         // This is necessary before calling e.g. find(name)
         // 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());
             }
         }
 
 
         synchronized Logger findLogger(String name) {

@@ -578,17 +672,17 @@
         // 'logger' is the context that will be added.
         // This method will ensure that the defaults loggers are added
         // before adding 'logger'.
         //
         private void ensureAllDefaultLoggers(Logger logger) {
-            if (requiresDefaultLoggers) {
+            if (requiresDefaultLoggers()) {
                 final String name = logger.getName();
                 if (!name.isEmpty()) {
-                    ensureDefaultLogger(manager.rootLogger);
-                }
+                    ensureDefaultLogger(getRootLogger());
                 if (!Logger.GLOBAL_LOGGER_NAME.equals(name)) {
-                    ensureDefaultLogger(Logger.global);
+                        ensureDefaultLogger(getGlobalLogger());
+                    }
                 }
             }
         }
 
         private void ensureDefaultLogger(Logger logger) {

@@ -596,12 +690,12 @@
             // to a LoggerContext.
 
             // 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
                 // issue - as ensureDefaultLogger should never be called
                 // with any other loggers than one of these two (or null - if

@@ -623,11 +717,11 @@
             }
         }
 
         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
         // and process parent loggers.  It doesn't set its handlers.
         synchronized boolean addLocalLogger(Logger logger, boolean addDefaultLoggersIfNeeded) {

@@ -661,15 +755,17 @@
                 }
             }
 
             // 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);
             }
 
             // instantiation of the handler is done in the LogManager.addLogger

@@ -717,14 +813,16 @@
         }
 
         // 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);
                         }
                     }
                     return null;

@@ -736,12 +834,12 @@
                 int ix2 = name.indexOf(".", ix);
                 if (ix2 < 0) {
                     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);
                 }
                 ix = ix2+1;

@@ -777,20 +875,21 @@
             }
             return node;
         }
     }
 
-    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
                         // created above so return it without refetching.
                         result = newLogger;

@@ -820,10 +919,11 @@
     // only be modified by trusted code.
     private void loadLoggerHandlers(final Logger logger, final String name,
                                     final String handlersPropertyName)
     {
         AccessController.doPrivileged(new PrivilegedAction<Object>() {
+            @Override
             public Object run() {
                 String names[] = parseClassNames(handlersPropertyName);
                 for (int i = 0; i < names.length; i++) {
                     String word = names[i];
                     try {

@@ -1012,10 +1112,11 @@
             return;
         }
         // There is a security manager.  Raise privilege before
         // calling setLevel.
         AccessController.doPrivileged(new PrivilegedAction<Object>() {
+            @Override
             public Object run() {
                 logger.setLevel(level);
                 return null;
             }});
     }

@@ -1030,10 +1131,11 @@
             return;
         }
         // There is a security manager.  Raise privilege before
         // calling setParent.
         AccessController.doPrivileged(new PrivilegedAction<Object>() {
+            @Override
             public Object run() {
                 logger.setParent(parent);
                 return null;
             }});
     }

@@ -1127,18 +1229,13 @@
             }
             File f = new File(fname, "lib");
             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();
-            }
         }
     }
 
     /**
      * Reset the logging configuration.

@@ -1199,11 +1296,11 @@
         if (hands == null) {
             return new String[0];
         }
         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()) {
                 if (Character.isWhitespace(hands.charAt(end))) {
                     break;

@@ -1469,32 +1566,39 @@
     }
 
     // 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();
         }
     }