LogManager.java revision 10967:e336cbd8b15e
12038SN/A/*
217248Siignatyev * Copyright (c) 2000, 2014, Oracle and/or its affiliates. All rights reserved.
32038SN/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
42038SN/A *
52038SN/A * This code is free software; you can redistribute it and/or modify it
62038SN/A * under the terms of the GNU General Public License version 2 only, as
78729Sserb * published by the Free Software Foundation.  Oracle designates this
82038SN/A * particular file as subject to the "Classpath" exception as provided
92038SN/A * by Oracle in the LICENSE file that accompanied this code.
102038SN/A *
112038SN/A * This code is distributed in the hope that it will be useful, but WITHOUT
122038SN/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
132038SN/A * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
142038SN/A * version 2 for more details (a copy is included in the LICENSE file that
152038SN/A * accompanied this code).
162038SN/A *
172038SN/A * You should have received a copy of the GNU General Public License version
182038SN/A * 2 along with this work; if not, write to the Free Software Foundation,
192362SN/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
202362SN/A *
212362SN/A * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
222038SN/A * or visit www.oracle.com if you need additional information or have any
232038SN/A * questions.
242038SN/A */
253793Sjrose
2617248Siignatyev
275688Stwistipackage java.util.logging;
2817248Siignatyev
2917248Siignatyevimport java.io.*;
3017248Siignatyevimport java.util.*;
3117248Siignatyevimport java.security.*;
322038SN/Aimport java.lang.ref.ReferenceQueue;
332038SN/Aimport java.lang.ref.WeakReference;
343793Sjroseimport sun.misc.JavaAWTAccess;
352038SN/Aimport sun.misc.SharedSecrets;
3617248Siignatyev
3717248Siignatyev/**
385688Stwisti * There is a single global LogManager object that is used to
3917248Siignatyev * maintain a set of shared state about Loggers and log services.
4017248Siignatyev * <p>
4117248Siignatyev * This LogManager object:
4217248Siignatyev * <ul>
4317248Siignatyev * <li> Manages a hierarchical namespace of Logger objects.  All
443793Sjrose *      named Loggers are stored in this namespace.
4517248Siignatyev * <li> Manages a set of logging control properties.  These are
4617248Siignatyev *      simple key-value pairs that can be used by Handlers and
4717248Siignatyev *      other logging objects to configure themselves.
4817248Siignatyev * </ul>
4917248Siignatyev * <p>
5017248Siignatyev * The global LogManager object can be retrieved using LogManager.getLogManager().
5117248Siignatyev * The LogManager object is created during class initialization and
5217248Siignatyev * cannot subsequently be changed.
5317248Siignatyev * <p>
5417248Siignatyev * At startup the LogManager class is located using the
5517248Siignatyev * java.util.logging.manager system property.
5617248Siignatyev * <p>
5717248Siignatyev * The LogManager defines two optional system properties that allow control over
5817248Siignatyev * the initial configuration:
5917248Siignatyev * <ul>
6017248Siignatyev * <li>"java.util.logging.config.class"
6117248Siignatyev * <li>"java.util.logging.config.file"
6217248Siignatyev * </ul>
6317248Siignatyev * These two properties may be specified on the command line to the "java"
6417248Siignatyev * command, or as system property definitions passed to JNI_CreateJavaVM.
652038SN/A * <p>
662038SN/A * If the "java.util.logging.config.class" property is set, then the
672038SN/A * property value is treated as a class name.  The given class will be
682038SN/A * loaded, an object will be instantiated, and that object's constructor
692038SN/A * is responsible for reading in the initial configuration.  (That object
702038SN/A * may use other system properties to control its configuration.)  The
712038SN/A * alternate configuration class can use <tt>readConfiguration(InputStream)</tt>
724383Sjrose * to define properties in the LogManager.
732038SN/A * <p>
742431SN/A * If "java.util.logging.config.class" property is <b>not</b> set,
752435SN/A * then the "java.util.logging.config.file" system property can be used
764383Sjrose * to specify a properties file (in java.util.Properties format). The
774383Sjrose * initial logging configuration will be read from this file.
784383Sjrose * <p>
792435SN/A * If neither of these properties is defined then the LogManager uses its
802435SN/A * default configuration. The default configuration is typically loaded from the
812038SN/A * properties file "{@code conf/logging.properties}" in the Java installation
822038SN/A * directory.
832038SN/A * <p>
842038SN/A * The properties for loggers and Handlers will have names starting
855688Stwisti * with the dot-separated name for the handler or logger.
865688Stwisti * <p>
875688Stwisti * The global logging properties may include:
885688Stwisti * <ul>
895688Stwisti * <li>A property "handlers".  This defines a whitespace or comma separated
905688Stwisti * list of class names for handler classes to load and register as
915688Stwisti * handlers on the root Logger (the Logger named "").  Each class
922038SN/A * name must be for a Handler class which has a default constructor.
934383Sjrose * Note that these Handlers may be created lazily, when they are
944383Sjrose * first used.
954383Sjrose *
962038SN/A * <li>A property "&lt;logger&gt;.handlers". This defines a whitespace or
972038SN/A * comma separated list of class names for handlers classes to
982038SN/A * load and register as handlers to the specified logger. Each class
992038SN/A * name must be for a Handler class which has a default constructor.
1002038SN/A * Note that these Handlers may be created lazily, when they are
1012431SN/A * first used.
1022038SN/A *
1032038SN/A * <li>A property "&lt;logger&gt;.useParentHandlers". This defines a boolean
1042038SN/A * value. By default every logger calls its parent in addition to
1052038SN/A * handling the logging message itself, this often result in messages
1062038SN/A * being handled by the root logger as well. When setting this property
1072038SN/A * to false a Handler needs to be configured for this logger otherwise
1082038SN/A * no logging messages are delivered.
1092038SN/A *
1102435SN/A * <li>A property "config".  This property is intended to allow
1112038SN/A * arbitrary configuration code to be run.  The property defines a
1122038SN/A * whitespace or comma separated list of class names.  A new instance will be
1132038SN/A * created for each named class.  The default constructor of each class
1142431SN/A * may execute arbitrary code to update the logging configuration, such as
1152038SN/A * setting logger levels, adding handlers, adding filters, etc.
1162038SN/A * </ul>
1172038SN/A * <p>
1182435SN/A * Note that all classes loaded during LogManager configuration are
1192435SN/A * first searched on the system class path before any user class path.
1202431SN/A * That includes the LogManager class, any config classes, and any
1212038SN/A * handler classes.
1222038SN/A * <p>
1232038SN/A * Loggers are organized into a naming hierarchy based on their
1242038SN/A * dot separated names.  Thus "a.b.c" is a child of "a.b", but
1252038SN/A * "a.b1" and a.b2" are peers.
1262038SN/A * <p>
1272038SN/A * All properties whose names end with ".level" are assumed to define
1282038SN/A * log levels for Loggers.  Thus "foo.level" defines a log level for
1292038SN/A * the logger called "foo" and (recursively) for any of its children
1302431SN/A * in the naming hierarchy.  Log Levels are applied in the order they
1312038SN/A * are defined in the properties file.  Thus level settings for child
1322038SN/A * nodes in the tree should come after settings for their parents.
1332038SN/A * The property name ".level" can be used to set the level for the
1342038SN/A * root of the tree.
1352038SN/A * <p>
1362038SN/A * All methods on the LogManager object are multi-thread safe.
1372038SN/A *
1382038SN/A * @since 1.4
1392038SN/A*/
1402431SN/A
1412038SN/Apublic class LogManager {
1422038SN/A    // The global LogManager object
1432038SN/A    private static final LogManager manager;
1442038SN/A
1452435SN/A    // 'props' is assigned within a lock but accessed without it.
1464383Sjrose    // Declaring it volatile makes sure that another thread will not
1472435SN/A    // be able to see a partially constructed 'props' object.
1482435SN/A    // (seeing a partially constructed 'props' object can result in
1492435SN/A    // NPE being thrown in Hashtable.get(), because it leaves the door
1502435SN/A    // open for props.getProperties() to be called before the construcor
1512038SN/A    // of Hashtable is actually completed).
1522038SN/A    private volatile Properties props = new Properties();
1534935Sjrose    private final static Level defaultLevel = Level.INFO;
1542038SN/A
1552038SN/A    // LoggerContext for system loggers and user loggers
1562038SN/A    private final LoggerContext systemContext = new SystemLoggerContext();
1575688Stwisti    private final LoggerContext userContext = new LoggerContext();
1582038SN/A    // non final field - make it volatile to make sure that other threads
1592038SN/A    // will see the new value once ensureLogManagerInitialized() has finished
1602038SN/A    // executing.
1612038SN/A    private volatile Logger rootLogger;
1622038SN/A    // Have we done the primordial reading of the configuration file?
1632038SN/A    // (Must be done after a suitable amount of java.lang.System
1642038SN/A    // initialization has been done)
1652431SN/A    private volatile boolean readPrimordialConfiguration;
1662038SN/A    // Have we initialized global (root) handlers yet?
1678311Sjrose    // This gets set to false in readConfiguration
1682038SN/A    private boolean initializedGlobalHandlers = true;
1692038SN/A    // True if JVM death is imminent and the exit hook has been called.
1702038SN/A    private boolean deathImminent;
1712038SN/A
1722038SN/A    private final Map<Object, Runnable> listeners =
1732038SN/A            Collections.synchronizedMap(new IdentityHashMap<>());
1742431SN/A
1758311Sjrose    static {
1768311Sjrose        manager = AccessController.doPrivileged(new PrivilegedAction<LogManager>() {
1778311Sjrose            @Override
1788311Sjrose            public LogManager run() {
1798311Sjrose                LogManager mgr = null;
1808311Sjrose                String cname = null;
1818311Sjrose                try {
1828311Sjrose                    cname = System.getProperty("java.util.logging.manager");
1838311Sjrose                    if (cname != null) {
1848311Sjrose                        try {
1858311Sjrose                            Class<?> clz = ClassLoader.getSystemClassLoader()
1868311Sjrose                                    .loadClass(cname);
1878311Sjrose                            mgr = (LogManager) clz.newInstance();
1888311Sjrose                        } catch (ClassNotFoundException ex) {
1898311Sjrose                            Class<?> clz = Thread.currentThread()
1908311Sjrose                                    .getContextClassLoader().loadClass(cname);
1918311Sjrose                            mgr = (LogManager) clz.newInstance();
1928311Sjrose                        }
1938311Sjrose                    }
1942038SN/A                } catch (Exception ex) {
1952038SN/A                    System.err.println("Could not load Logmanager \"" + cname + "\"");
1962038SN/A                    ex.printStackTrace();
1972038SN/A                }
1982038SN/A                if (mgr == null) {
1992038SN/A                    mgr = new LogManager();
2002038SN/A                }
2012038SN/A                return mgr;
2022038SN/A
2032038SN/A            }
2042038SN/A        });
2052038SN/A    }
2064935Sjrose
2072038SN/A
2082038SN/A    // This private class is used as a shutdown hook.
2092038SN/A    // It does a "reset" to close all open handlers.
2102038SN/A    private class Cleaner extends Thread {
2112038SN/A
2122038SN/A        private Cleaner() {
2132038SN/A            /* Set context class loader to null in order to avoid
2142038SN/A             * keeping a strong reference to an application classloader.
2152038SN/A             */
2162038SN/A            this.setContextClassLoader(null);
2172038SN/A        }
2182038SN/A
2192038SN/A        @Override
2202040SN/A        public void run() {
2212040SN/A            // This is to ensure the LogManager.<clinit> is completed
2222040SN/A            // before synchronized block. Otherwise deadlocks are possible.
2232040SN/A            LogManager mgr = manager;
2242038SN/A
2252038SN/A            // If the global handlers haven't been initialized yet, we
2262038SN/A            // don't want to initialize them just so we can close them!
2272431SN/A            synchronized (LogManager.this) {
2282431SN/A                // Note that death is imminent.
2292431SN/A                deathImminent = true;
2302431SN/A                initializedGlobalHandlers = true;
2312431SN/A            }
2322431SN/A
2332431SN/A            // Do a reset to close all active handlers.
2342431SN/A            reset();
2352431SN/A        }
2362431SN/A    }
2372431SN/A
2382431SN/A
2392431SN/A    /**
2402431SN/A     * Protected constructor.  This is protected so that container applications
2412431SN/A     * (such as J2EE containers) can subclass the object.  It is non-public as
2422431SN/A     * it is intended that there only be one LogManager object, whose value is
2432431SN/A     * retrieved by calling LogManager.getLogManager.
2442431SN/A     */
2452431SN/A    protected LogManager() {
2462431SN/A        this(checkSubclassPermissions());
2472431SN/A    }
2482431SN/A
2492431SN/A    private LogManager(Void checked) {
2502431SN/A
2512431SN/A        // Add a shutdown hook to close the global handlers.
2522431SN/A        try {
2532431SN/A            Runtime.getRuntime().addShutdownHook(new Cleaner());
2542431SN/A        } catch (IllegalStateException e) {
2552431SN/A            // If the VM is already shutting down,
2562431SN/A            // We do not need to register shutdownHook.
2572431SN/A        }
2582038SN/A    }
2592431SN/A
2602038SN/A    private static Void checkSubclassPermissions() {
2612038SN/A        final SecurityManager sm = System.getSecurityManager();
2622038SN/A        if (sm != null) {
2633793Sjrose            // These permission will be checked in the LogManager constructor,
2642038SN/A            // in order to register the Cleaner() thread as a shutdown hook.
2652038SN/A            // Check them here to avoid the penalty of constructing the object
2662038SN/A            // etc...
2672038SN/A            sm.checkPermission(new RuntimePermission("shutdownHooks"));
2682038SN/A            sm.checkPermission(new RuntimePermission("setContextClassLoader"));
2693012SN/A        }
2703012SN/A        return null;
2713012SN/A    }
2723012SN/A
2733012SN/A    /**
2743012SN/A     * Lazy initialization: if this instance of manager is the global
2758311Sjrose     * manager then this method will read the initial configuration and
2768311Sjrose     * add the root logger and global logger by calling addLogger().
2778311Sjrose     *
2788311Sjrose     * Note that it is subtly different from what we do in LoggerContext.
2798311Sjrose     * In LoggerContext we're patching up the logger context tree in order to add
2808311Sjrose     * the root and global logger *to the context tree*.
2815685Sjrose     *
2825685Sjrose     * For this to work, addLogger() must have already have been called
2832038SN/A     * once on the LogManager instance for the default logger being
2842431SN/A     * added.
2852038SN/A     *
2862038SN/A     * This is why ensureLogManagerInitialized() needs to be called before
2872038SN/A     * any logger is added to any logger context.
2884935Sjrose     *
2892038SN/A     */
2902038SN/A    private boolean initializedCalled = false;
2912038SN/A    private volatile boolean initializationDone = false;
2922038SN/A    final void ensureLogManagerInitialized() {
2932038SN/A        final LogManager owner = this;
2942038SN/A        if (initializationDone || owner != manager) {
2952038SN/A            // we don't want to do this twice, and we don't want to do
2962038SN/A            // this on private manager instances.
2972038SN/A            return;
2982038SN/A        }
2992038SN/A
3002038SN/A        // Maybe another thread has called ensureLogManagerInitialized()
3012038SN/A        // before us and is still executing it. If so we will block until
3022038SN/A        // the log manager has finished initialized, then acquire the monitor,
3032038SN/A        // notice that initializationDone is now true and return.
3048316Sjrose        // Otherwise - we have come here first! We will acquire the monitor,
3058316Sjrose        // see that initializationDone is still false, and perform the
3068316Sjrose        // initialization.
3072038SN/A        //
3084935Sjrose        synchronized(this) {
3092038SN/A            // If initializedCalled is true it means that we're already in
3102038SN/A            // the process of initializing the LogManager in this thread.
3112038SN/A            // There has been a recursive call to ensureLogManagerInitialized().
3124935Sjrose            final boolean isRecursiveInitialization = (initializedCalled == true);
3132038SN/A
3142038SN/A            assert initializedCalled || !initializationDone
3152038SN/A                    : "Initialization can't be done if initialized has not been called!";
3162038SN/A
3172038SN/A            if (isRecursiveInitialization || initializationDone) {
3182038SN/A                // If isRecursiveInitialization is true it means that we're
3192038SN/A                // already in the process of initializing the LogManager in
3202038SN/A                // this thread. There has been a recursive call to
3212038SN/A                // ensureLogManagerInitialized(). We should not proceed as
3222038SN/A                // it would lead to infinite recursion.
3232038SN/A                //
3242038SN/A                // If initializationDone is true then it means the manager
3252038SN/A                // has finished initializing; just return: we're done.
3262038SN/A                return;
3272038SN/A            }
3282038SN/A            // Calling addLogger below will in turn call requiresDefaultLogger()
3292038SN/A            // which will call ensureLogManagerInitialized().
3302038SN/A            // We use initializedCalled to break the recursion.
3312038SN/A            initializedCalled = true;
3322038SN/A            try {
3332038SN/A                AccessController.doPrivileged(new PrivilegedAction<Object>() {
3342038SN/A                    @Override
3352038SN/A                    public Object run() {
3362038SN/A                        assert rootLogger == null;
3372038SN/A                        assert initializedCalled && !initializationDone;
3382038SN/A
3392038SN/A                        // Read configuration.
3403529SN/A                        owner.readPrimordialConfiguration();
3413529SN/A
3423529SN/A                        // Create and retain Logger for the root of the namespace.
3433529SN/A                        owner.rootLogger = owner.new RootLogger();
3443529SN/A                        owner.addLogger(owner.rootLogger);
3453529SN/A                        if (!owner.rootLogger.isLevelInitialized()) {
3463529SN/A                            owner.rootLogger.setLevel(defaultLevel);
3473529SN/A                        }
3484183Sjrose
3494183Sjrose                        // Adding the global Logger.
3504183Sjrose                        // Do not call Logger.getGlobal() here as this might trigger
3513529SN/A                        // subtle inter-dependency issues.
3523529SN/A                        @SuppressWarnings("deprecation")
3533529SN/A                        final Logger global = Logger.global;
3543529SN/A
3553529SN/A                        // Make sure the global logger will be registered in the
3563529SN/A                        // global manager
3573529SN/A                        owner.addLogger(global);
3583529SN/A                        return null;
3593529SN/A                    }
3603529SN/A                });
3613529SN/A            } finally {
3624935Sjrose                initializationDone = true;
3633529SN/A            }
3643529SN/A        }
3653529SN/A    }
3663529SN/A
3673529SN/A    /**
3683529SN/A     * Returns the global LogManager object.
3694935Sjrose     * @return the global LogManager object
3703529SN/A     */
3713529SN/A    public static LogManager getLogManager() {
3723529SN/A        if (manager != null) {
3733529SN/A            manager.ensureLogManagerInitialized();
3743529SN/A        }
3753529SN/A        return manager;
3763529SN/A    }
3778316Sjrose
3788316Sjrose    private void readPrimordialConfiguration() {
3798316Sjrose        if (!readPrimordialConfiguration) {
3808316Sjrose            synchronized (this) {
3818316Sjrose                if (!readPrimordialConfiguration) {
3823529SN/A                    // If System.in/out/err are null, it's a good
3834935Sjrose                    // indication that we're still in the
3844935Sjrose                    // bootstrapping phase
3853529SN/A                    if (System.out == null) {
3862038SN/A                        return;
3872038SN/A                    }
3882038SN/A                    readPrimordialConfiguration = true;
3892038SN/A
3902038SN/A                    try {
3912038SN/A                        AccessController.doPrivileged(new PrivilegedExceptionAction<Void>() {
3922038SN/A                                @Override
3934935Sjrose                                public Void run() throws Exception {
3942038SN/A                                    readConfiguration();
3952040SN/A
3964251Sjrose                                    // Platform loggers begin to delegate to java.util.logging.Logger
3972038SN/A                                    sun.util.logging.PlatformLogger.redirectPlatformLoggers();
3984383Sjrose                                    return null;
3994383Sjrose                                }
4004383Sjrose                            });
4014383Sjrose                    } catch (Exception ex) {
4024383Sjrose                        assert false : "Exception raised while reading logging configuration: " + ex;
4034383Sjrose                    }
4044383Sjrose                }
4052038SN/A            }
4062038SN/A        }
4072038SN/A    }
4082038SN/A
4092038SN/A    // LoggerContext maps from AppContext
4105688Stwisti    private WeakHashMap<Object, LoggerContext> contextsMap = null;
4115688Stwisti
41213901Salanb    // Returns the LoggerContext for the user code (i.e. application or AppContext).
4132040SN/A    // Loggers are isolated from each AppContext.
4142038SN/A    private LoggerContext getUserContext() {
4152038SN/A        LoggerContext context = null;
4162038SN/A
4172038SN/A        SecurityManager sm = System.getSecurityManager();
4182431SN/A        JavaAWTAccess javaAwtAccess = SharedSecrets.getJavaAWTAccess();
4192038SN/A        if (sm != null && javaAwtAccess != null) {
4204935Sjrose            // for each applet, it has its own LoggerContext isolated from others
4212038SN/A            synchronized (javaAwtAccess) {
4228314Sjrose                // find the AppContext of the applet code
4232038SN/A                // will be null if we are in the main app context.
4242038SN/A                final Object ecx = javaAwtAccess.getAppletContext();
4252038SN/A                if (ecx != null) {
4265688Stwisti                    if (contextsMap == null) {
4272038SN/A                        contextsMap = new WeakHashMap<>();
4282038SN/A                    }
4292038SN/A                    context = contextsMap.get(ecx);
4305688Stwisti                    if (context == null) {
4312038SN/A                        // Create a new LoggerContext for the applet.
4322038SN/A                        context = new LoggerContext();
4332038SN/A                        contextsMap.put(ecx, context);
4342038SN/A                    }
4352038SN/A                }
4362038SN/A            }
4372038SN/A        }
4382038SN/A        // for standalone app, return userContext
4392038SN/A        return context != null ? context : userContext;
4402038SN/A    }
4412038SN/A
4422038SN/A    // The system context.
4432038SN/A    final LoggerContext getSystemContext() {
4442038SN/A        return systemContext;
4452038SN/A    }
4462431SN/A
4474383Sjrose    private List<LoggerContext> contexts() {
4484383Sjrose        List<LoggerContext> cxs = new ArrayList<>();
4494383Sjrose        cxs.add(getSystemContext());
4505688Stwisti        cxs.add(getUserContext());
4515688Stwisti        return cxs;
4525688Stwisti    }
4535688Stwisti
4545688Stwisti    // Find or create a specified logger instance. If a logger has
4555688Stwisti    // already been created with the given name it is returned.
4564383Sjrose    // Otherwise a new logger instance is created and registered
4572431SN/A    // in the LogManager global namespace.
4582038SN/A    // This method will always return a non-null Logger object.
4592431SN/A    // Synchronization is not required here. All synchronization for
4602038SN/A    // adding a new Logger object is handled by addLogger().
4615688Stwisti    //
4625688Stwisti    // This method must delegate to the LogManager implementation to
4635688Stwisti    // add a new Logger or return the one that has been added previously
4645688Stwisti    // as a LogManager subclass may override the addLogger, getLogger,
4652038SN/A    // readConfiguration, and other methods.
4662038SN/A    Logger demandLogger(String name, String resourceBundleName, Class<?> caller) {
4672038SN/A        Logger result = getLogger(name);
4682038SN/A        if (result == null) {
4694935Sjrose            // only allocate the new logger once
4702038SN/A            Logger newLogger = new Logger(name, resourceBundleName, caller, this, false);
4712431SN/A            do {
4722038SN/A                if (addLogger(newLogger)) {
4732038SN/A                    // We successfully added the new Logger that we
4742038SN/A                    // created above so return it without refetching.
47513111Smhaupt                    return newLogger;
4763012SN/A                }
4772038SN/A
4782038SN/A                // We didn't add the new Logger that we created above
4792431SN/A                // because another thread added a Logger with the same
4802431SN/A                // name after our null check above and before our call
4812038SN/A                // to addLogger(). We have to refetch the Logger because
4822038SN/A                // addLogger() returns a boolean instead of the Logger
4835685Sjrose                // reference itself. However, if the thread that created
4842038SN/A                // the other Logger is not holding a strong reference to
4852038SN/A                // the other Logger, then it is possible for the other
4865688Stwisti                // Logger to be GC'ed after we saw it in addLogger() and
4875688Stwisti                // before we can refetch it. If it has been GC'ed then
4885688Stwisti                // we'll just loop around and try again.
4895688Stwisti                result = getLogger(name);
4905688Stwisti            } while (result == null);
4912038SN/A        }
4922038SN/A        return result;
4932431SN/A    }
4942431SN/A
4952431SN/A    Logger demandSystemLogger(String name, String resourceBundleName) {
4962431SN/A        // Add a system logger in the system context's namespace
4972431SN/A        final Logger sysLogger = getSystemContext().demandLogger(name, resourceBundleName);
4982431SN/A
4995688Stwisti        // Add the system logger to the LogManager's namespace if not exist
5005688Stwisti        // so that there is only one single logger of the given name.
5012040SN/A        // System loggers are visible to applications unless a logger of
5025688Stwisti        // the same name has been added.
5032040SN/A        Logger logger;
5042040SN/A        do {
5055688Stwisti            // First attempt to call addLogger instead of getLogger
5062431SN/A            // This would avoid potential bug in custom LogManager.getLogger
5072431SN/A            // implementation that adds a logger if does not exist
5082040SN/A            if (addLogger(sysLogger)) {
5092431SN/A                // successfully added the new system logger
5102431SN/A                logger = sysLogger;
5112431SN/A            } else {
5122431SN/A                logger = getLogger(name);
5132431SN/A            }
5142431SN/A        } while (logger == null);
5152038SN/A
5162038SN/A        // LogManager will set the sysLogger's handlers via LogManager.addLogger method.
5175688Stwisti        if (logger != sysLogger && sysLogger.accessCheckedHandlers().length == 0) {
5185688Stwisti            // if logger already exists but handlers not set
5195688Stwisti            final Logger l = logger;
5205688Stwisti            AccessController.doPrivileged(new PrivilegedAction<Void>() {
5215688Stwisti                @Override
5225688Stwisti                public Void run() {
5235688Stwisti                    for (Handler hdl : l.accessCheckedHandlers()) {
5248318Sjrose                        sysLogger.addHandler(hdl);
52512745Smartin                    }
5268314Sjrose                    return null;
5272038SN/A                }
5282038SN/A            });
52912209Skshefov        }
53012209Skshefov        return sysLogger;
53112209Skshefov    }
53212209Skshefov
5332038SN/A    // LoggerContext maintains the logger namespace per context.
5342038SN/A    // The default LogManager implementation has one system context and user
5352038SN/A    // context.  The system context is used to maintain the namespace for
5362038SN/A    // all system loggers and is queried by the system code.  If a system logger
5372038SN/A    // doesn't exist in the user context, it'll also be added to the user context.
5382038SN/A    // The user context is queried by the user code and all other loggers are
5395688Stwisti    // added in the user context.
5405688Stwisti    class LoggerContext {
5412038SN/A        // Table of named Loggers that maps names to Loggers.
5422038SN/A        private final Hashtable<String,LoggerWeakRef> namedLoggers = new Hashtable<>();
5432038SN/A        // Tree of named Loggers
5442038SN/A        private final LogNode root;
5452038SN/A        private LoggerContext() {
5462038SN/A            this.root = new LogNode(null, this);
5472038SN/A        }
5482038SN/A
5492038SN/A
5502038SN/A        // Tells whether default loggers are required in this context.
5518314Sjrose        // If true, the default loggers will be lazily added.
5528314Sjrose        final boolean requiresDefaultLoggers() {
5535688Stwisti            final boolean requiresDefaultLoggers = (getOwner() == manager);
5542038SN/A            if (requiresDefaultLoggers) {
5552038SN/A                getOwner().ensureLogManagerInitialized();
5562038SN/A            }
5572038SN/A            return requiresDefaultLoggers;
5582038SN/A        }
5592038SN/A
5602038SN/A        // This context's LogManager.
5612038SN/A        final LogManager getOwner() {
5622038SN/A            return LogManager.this;
5632038SN/A        }
5642038SN/A
5652038SN/A        // This context owner's root logger, which if not null, and if
5665688Stwisti        // the context requires default loggers, will be added to the context
5672040SN/A        // logger's tree.
5682038SN/A        final Logger getRootLogger() {
5693011SN/A            return getOwner().rootLogger;
5702038SN/A        }
5712431SN/A
5725688Stwisti        // The global logger, which if not null, and if
5733530SN/A        // the context requires default loggers, will be added to the context
5742038SN/A        // logger's tree.
5758314Sjrose        final Logger getGlobalLogger() {
5768314Sjrose            @SuppressWarnings("deprecation") // avoids initialization cycles.
5778314Sjrose            final Logger global = Logger.global;
5788314Sjrose            return global;
5798314Sjrose        }
5805688Stwisti
5812038SN/A        Logger demandLogger(String name, String resourceBundleName) {
5822431SN/A            // a LogManager subclass may have its own implementation to add and
5832431SN/A            // get a Logger.  So delegate to the LogManager to do the work.
5842038SN/A            final LogManager owner = getOwner();
5852038SN/A            return owner.demandLogger(name, resourceBundleName, null);
5862038SN/A        }
5872038SN/A
5882038SN/A
5895688Stwisti        // Due to subtle deadlock issues getUserContext() no longer
5902038SN/A        // calls addLocalLogger(rootLogger);
5912038SN/A        // Therefore - we need to add the default loggers later on.
5923240SN/A        // Checks that the context is properly initialized
5932038SN/A        // This is necessary before calling e.g. find(name)
5942431SN/A        // or getLoggerNames()
5952431SN/A        //
5962038SN/A        private void ensureInitialized() {
5972038SN/A            if (requiresDefaultLoggers()) {
5988314Sjrose                // Ensure that the root and global loggers are set.
5998314Sjrose                ensureDefaultLogger(getRootLogger());
6008314Sjrose                ensureDefaultLogger(getGlobalLogger());
6018314Sjrose            }
6028314Sjrose        }
6038314Sjrose
6048314Sjrose
6054245Sjrose        synchronized Logger findLogger(String name) {
6064245Sjrose            // ensure that this context is properly initialized before
6073240SN/A            // looking for loggers.
6084245Sjrose            ensureInitialized();
6094245Sjrose            LoggerWeakRef ref = namedLoggers.get(name);
6104245Sjrose            if (ref == null) {
6114245Sjrose                return null;
6124245Sjrose            }
6134245Sjrose            Logger logger = ref.get();
6143240SN/A            if (logger == null) {
6153240SN/A                // Hashtable holds stale weak reference
6163240SN/A                // to a logger which has been GC-ed.
6173240SN/A                ref.dispose();
6182038SN/A            }
6192038SN/A            return logger;
62012209Skshefov        }
62112209Skshefov
62212209Skshefov        // This method is called before adding a logger to the
62312209Skshefov        // context.
6242038SN/A        // 'logger' is the context that will be added.
6252038SN/A        // This method will ensure that the defaults loggers are added
6262038SN/A        // before adding 'logger'.
6272038SN/A        //
6282038SN/A        private void ensureAllDefaultLoggers(Logger logger) {
6292038SN/A            if (requiresDefaultLoggers()) {
6302038SN/A                final String name = logger.getName();
6312038SN/A                if (!name.isEmpty()) {
6322038SN/A                    ensureDefaultLogger(getRootLogger());
6332038SN/A                    if (!Logger.GLOBAL_LOGGER_NAME.equals(name)) {
6345688Stwisti                        ensureDefaultLogger(getGlobalLogger());
6355688Stwisti                    }
6365688Stwisti                }
6372038SN/A            }
6388314Sjrose        }
6398314Sjrose
6405688Stwisti        private void ensureDefaultLogger(Logger logger) {
6415688Stwisti            // Used for lazy addition of root logger and global logger
6422038SN/A            // to a LoggerContext.
6432038SN/A
6442038SN/A            // This check is simple sanity: we do not want that this
6452038SN/A            // method be called for anything else than Logger.global
6462038SN/A            // or owner.rootLogger.
6472038SN/A            if (!requiresDefaultLoggers() || logger == null
6482038SN/A                    || logger != getGlobalLogger() && logger != LogManager.this.rootLogger ) {
6492038SN/A
6502038SN/A                // the case where we have a non null logger which is neither
6512038SN/A                // Logger.global nor manager.rootLogger indicates a serious
6528311Sjrose                // issue - as ensureDefaultLogger should never be called
6538311Sjrose                // with any other loggers than one of these two (or null - if
65412209Skshefov                // e.g manager.rootLogger is not yet initialized)...
65512209Skshefov                assert logger == null;
65612209Skshefov
65712209Skshefov                return;
65815692Smhaupt            }
6598311Sjrose
6608311Sjrose            // Adds the logger if it's not already there.
66113901Salanb            if (!namedLoggers.containsKey(logger.getName())) {
66213901Salanb                // It is important to prevent addLocalLogger to
66313901Salanb                // call ensureAllDefaultLoggers when we're in the process
66413901Salanb                // off adding one of those default loggers - as this would
6658311Sjrose                // immediately cause a stack overflow.
6668311Sjrose                // Therefore we must pass addDefaultLoggersIfNeeded=false,
6678311Sjrose                // even if requiresDefaultLoggers is true.
6688311Sjrose                addLocalLogger(logger, false);
66913901Salanb            }
6708311Sjrose        }
6718311Sjrose
6722038SN/A        boolean addLocalLogger(Logger logger) {
6732038SN/A            // no need to add default loggers if it's not required
6742038SN/A            return addLocalLogger(logger, requiresDefaultLoggers());
6752038SN/A        }
6762038SN/A
6772038SN/A        // Add a logger to this context.  This method will only set its level
6782038SN/A        // and process parent loggers.  It doesn't set its handlers.
6792038SN/A        synchronized boolean addLocalLogger(Logger logger, boolean addDefaultLoggersIfNeeded) {
6802038SN/A            // addDefaultLoggersIfNeeded serves to break recursion when adding
6812038SN/A            // default loggers. If we're adding one of the default loggers
6822038SN/A            // (we're being called from ensureDefaultLogger()) then
6832038SN/A            // addDefaultLoggersIfNeeded will be false: we don't want to
6848311Sjrose            // call ensureAllDefaultLoggers again.
6858311Sjrose            //
6868311Sjrose            // Note: addDefaultLoggersIfNeeded can also be false when
6872038SN/A            //       requiresDefaultLoggers is false - since calling
6882038SN/A            //       ensureAllDefaultLoggers would have no effect in this case.
6892038SN/A            if (addDefaultLoggersIfNeeded) {
6902040SN/A                ensureAllDefaultLoggers(logger);
6912038SN/A            }
6923011SN/A
6932038SN/A            final String name = logger.getName();
6942431SN/A            if (name == null) {
6955688Stwisti                throw new NullPointerException();
6963530SN/A            }
6972038SN/A            LoggerWeakRef ref = namedLoggers.get(name);
6988314Sjrose            if (ref != null) {
6998314Sjrose                if (ref.get() == null) {
7008314Sjrose                    // It's possible that the Logger was GC'ed after a
7018314Sjrose                    // drainLoggerRefQueueBounded() call above so allow
7028314Sjrose                    // a new one to be registered.
7035688Stwisti                    ref.dispose();
7042038SN/A                } else {
7052431SN/A                    // We already have a registered logger with the given name.
7062431SN/A                    return false;
7072038SN/A                }
7082038SN/A            }
7092038SN/A
7102038SN/A            // We're adding a new logger.
7115688Stwisti            // Note that we are creating a weak reference here.
7125688Stwisti            final LogManager owner = getOwner();
7135688Stwisti            logger.setLogManager(owner);
7145688Stwisti            ref = owner.new LoggerWeakRef(logger);
7155688Stwisti            namedLoggers.put(name, ref);
7165688Stwisti
7175688Stwisti            // Apply any initial level defined for the new logger, unless
7185688Stwisti            // the logger's level is already initialized
7195688Stwisti            Level level = owner.getLevelProperty(name + ".level", null);
7202040SN/A            if (level != null && !logger.isLevelInitialized()) {
7212431SN/A                doSetLevel(logger, level);
7223240SN/A            }
7232038SN/A
7245688Stwisti            // instantiation of the handler is done in the LogManager.addLogger
7252038SN/A            // implementation as a handler class may be only visible to LogManager
7268311Sjrose            // subclass for the custom log manager case
7278311Sjrose            processParentHandlers(logger, name);
7288311Sjrose
7298311Sjrose            // Find the new node and its parent.
7308311Sjrose            LogNode node = getNode(name);
7318311Sjrose            node.loggerRef = ref;
7328311Sjrose            Logger parent = null;
7338311Sjrose            LogNode nodep = node.parent;
7348311Sjrose            while (nodep != null) {
7358311Sjrose                LoggerWeakRef nodeRef = nodep.loggerRef;
7368311Sjrose                if (nodeRef != null) {
7378311Sjrose                    parent = nodeRef.get();
7388311Sjrose                    if (parent != null) {
7398311Sjrose                        break;
7408311Sjrose                    }
7412431SN/A                }
7422431SN/A                nodep = nodep.parent;
7432038SN/A            }
7442038SN/A
7452038SN/A            if (parent != null) {
7462038SN/A                doSetParent(logger, parent);
74712209Skshefov            }
74812209Skshefov            // Walk over the children and tell them we are their new parent.
74912209Skshefov            node.walkAndSetParent(logger);
75012209Skshefov            // new LogNode is ready so tell the LoggerWeakRef about it
7512038SN/A            ref.setNode(node);
7522038SN/A            return true;
75313111Smhaupt        }
75413111Smhaupt
75513111Smhaupt        synchronized void removeLoggerRef(String name, LoggerWeakRef ref) {
75613111Smhaupt            namedLoggers.remove(name, ref);
7572431SN/A        }
7582431SN/A
7592431SN/A        synchronized Enumeration<String> getLoggerNames() {
7608314Sjrose            // ensure that this context is properly initialized before
7612431SN/A            // returning logger names.
7628314Sjrose            ensureInitialized();
7632431SN/A            return namedLoggers.keys();
76413111Smhaupt        }
7652431SN/A
7662038SN/A        // If logger.getUseParentHandlers() returns 'true' and any of the logger's
7672038SN/A        // parents have levels or handlers defined, make sure they are instantiated.
7682431SN/A        private void processParentHandlers(final Logger logger, final String name) {
76913111Smhaupt            final LogManager owner = getOwner();
7705688Stwisti            AccessController.doPrivileged(new PrivilegedAction<Void>() {
7715688Stwisti                @Override
7725688Stwisti                public Void run() {
7735688Stwisti                    if (logger != owner.rootLogger) {
7745688Stwisti                        boolean useParent = owner.getBooleanProperty(name + ".useParentHandlers", true);
7755688Stwisti                        if (!useParent) {
7765688Stwisti                            logger.setUseParentHandlers(false);
7775688Stwisti                        }
77813111Smhaupt                    }
77913111Smhaupt                    return null;
78013111Smhaupt                }
78113111Smhaupt            });
78213111Smhaupt
7832038SN/A            int ix = 1;
7842431SN/A            for (;;) {
7852431SN/A                int ix2 = name.indexOf('.', ix);
7862038SN/A                if (ix2 < 0) {
7875688Stwisti                    break;
7882040SN/A                }
7898314Sjrose                String pname = name.substring(0, ix2);
7908314Sjrose                if (owner.getProperty(pname + ".level") != null ||
7918314Sjrose                    owner.getProperty(pname + ".handlers") != null) {
7922038SN/A                    // This pname has a level/handlers definition.
7933011SN/A                    // Make sure it exists.
7942038SN/A                    demandLogger(pname, null);
7952431SN/A                }
7968314Sjrose                ix = ix2+1;
7978314Sjrose            }
7983530SN/A        }
7992038SN/A
8008314Sjrose        // Gets a node in our tree of logger nodes.
8018314Sjrose        // If necessary, create it.
8028314Sjrose        LogNode getNode(String name) {
8038314Sjrose            if (name == null || name.equals("")) {
8048314Sjrose                return root;
8058314Sjrose            }
8068314Sjrose            LogNode node = root;
8075688Stwisti            while (name.length() > 0) {
8082038SN/A                int ix = name.indexOf('.');
8092431SN/A                String head;
8102431SN/A                if (ix > 0) {
8112431SN/A                    head = name.substring(0, ix);
8122431SN/A                    name = name.substring(ix + 1);
8132038SN/A                } else {
8142038SN/A                    head = name;
8152038SN/A                    name = "";
8162431SN/A                }
8172431SN/A                if (node.children == null) {
8182431SN/A                    node.children = new HashMap<>();
8192040SN/A                }
8205688Stwisti                LogNode child = node.children.get(head);
8212038SN/A                if (child == null) {
8222038SN/A                    child = new LogNode(node, this);
8233240SN/A                    node.children.put(head, child);
8242038SN/A                }
8252038SN/A                node = child;
8262038SN/A            }
8272038SN/A            return node;
8284383Sjrose        }
82912209Skshefov    }
83012209Skshefov
83112209Skshefov    final class SystemLoggerContext extends LoggerContext {
83212209Skshefov        // Add a system logger in the system context's namespace as well as
8334383Sjrose        // in the LogManager's namespace if not exist so that there is only
8344383Sjrose        // one single logger of the given name.  System loggers are visible
8354383Sjrose        // to applications unless a logger of the same name has been added.
8364383Sjrose        @Override
8375688Stwisti        Logger demandLogger(String name, String resourceBundleName) {
8385688Stwisti            Logger result = findLogger(name);
8395688Stwisti            if (result == null) {
8405688Stwisti                // only allocate the new system logger once
8414383Sjrose                Logger newLogger = new Logger(name, resourceBundleName, null, getOwner(), true);
8425688Stwisti                do {
8435688Stwisti                    if (addLocalLogger(newLogger)) {
8444383Sjrose                        // We successfully added the new Logger that we
8454383Sjrose                        // created above so return it without refetching.
8464383Sjrose                        result = newLogger;
8474383Sjrose                    } else {
8484383Sjrose                        // We didn't add the new Logger that we created above
8494383Sjrose                        // because another thread added a Logger with the same
8504383Sjrose                        // name after our null check above and before our call
8514383Sjrose                        // to addLogger(). We have to refetch the Logger because
8524383Sjrose                        // addLogger() returns a boolean instead of the Logger
8534383Sjrose                        // reference itself. However, if the thread that created
8544383Sjrose                        // the other Logger is not holding a strong reference to
8554383Sjrose                        // the other Logger, then it is possible for the other
8568314Sjrose                        // Logger to be GC'ed after we saw it in addLogger() and
8574383Sjrose                        // before we can refetch it. If it has been GC'ed then
8584383Sjrose                        // we'll just loop around and try again.
8594383Sjrose                        result = findLogger(name);
8604383Sjrose                    }
8614383Sjrose                } while (result == null);
8624383Sjrose            }
8634383Sjrose            return result;
8644383Sjrose        }
8654383Sjrose    }
8664383Sjrose
8674383Sjrose    // Add new per logger handlers.
8684383Sjrose    // We need to raise privilege here. All our decisions will
8694383Sjrose    // be made based on the logging configuration, which can
8704383Sjrose    // only be modified by trusted code.
8714383Sjrose    private void loadLoggerHandlers(final Logger logger, final String name,
8724383Sjrose                                    final String handlersPropertyName)
8734383Sjrose    {
8744383Sjrose        AccessController.doPrivileged(new PrivilegedAction<Object>() {
8752038SN/A            @Override
87612209Skshefov            public Object run() {
87712209Skshefov                String names[] = parseClassNames(handlersPropertyName);
87812209Skshefov                for (String word : names) {
87912209Skshefov                    try {
8802038SN/A                        Class<?> clz = ClassLoader.getSystemClassLoader().loadClass(word);
8812038SN/A                        Handler hdl = (Handler) clz.newInstance();
8822038SN/A                        // Check if there is a property defining the
8832038SN/A                        // this handler's level.
8842038SN/A                        String levs = getProperty(word + ".level");
8852038SN/A                        if (levs != null) {
8862038SN/A                            Level l = Level.findLevel(levs);
8872038SN/A                            if (l != null) {
8882038SN/A                                hdl.setLevel(l);
8892038SN/A                            } else {
8902038SN/A                                // Probably a bad level. Drop through.
8918314Sjrose                                System.err.println("Can't set level for " + word);
8928314Sjrose                            }
8932038SN/A                        }
8942038SN/A                        // Add this Handler to the logger
8952038SN/A                        logger.addHandler(hdl);
8962038SN/A                    } catch (Exception ex) {
8972038SN/A                        System.err.println("Can't load log handler \"" + word + "\"");
8982038SN/A                        System.err.println("" + ex);
8992038SN/A                        ex.printStackTrace();
9002038SN/A                    }
9012038SN/A                }
9022038SN/A                return null;
9032038SN/A            }
9042038SN/A        });
9052038SN/A    }
9062038SN/A
9072040SN/A
9082038SN/A    // loggerRefQueue holds LoggerWeakRef objects for Logger objects
9092038SN/A    // that have been GC'ed.
9103011SN/A    private final ReferenceQueue<Logger> loggerRefQueue
9112038SN/A        = new ReferenceQueue<>();
9122431SN/A
9135688Stwisti    // Package-level inner class.
9143530SN/A    // Helper class for managing WeakReferences to Logger objects.
9152038SN/A    //
9168314Sjrose    // LogManager.namedLoggers
9178314Sjrose    //     - has weak references to all named Loggers
9188314Sjrose    //     - namedLoggers keeps the LoggerWeakRef objects for the named
9198314Sjrose    //       Loggers around until we can deal with the book keeping for
9208314Sjrose    //       the named Logger that is being GC'ed.
9215688Stwisti    // LogManager.LogNode.loggerRef
9222038SN/A    //     - has a weak reference to a named Logger
9232431SN/A    //     - the LogNode will also keep the LoggerWeakRef objects for
9242038SN/A    //       the named Loggers around; currently LogNodes never go away.
9252038SN/A    // Logger.kids
9262038SN/A    //     - has a weak reference to each direct child Logger; this
9272038SN/A    //       includes anonymous and named Loggers
9282038SN/A    //     - anonymous Loggers are always children of the rootLogger
9292038SN/A    //       which is a strong reference; rootLogger.kids keeps the
9302038SN/A    //       LoggerWeakRef objects for the anonymous Loggers around
9312038SN/A    //       until we can deal with the book keeping.
9323240SN/A    //
9332038SN/A    final class LoggerWeakRef extends WeakReference<Logger> {
9342038SN/A        private String                name;       // for namedLoggers cleanup
9352431SN/A        private LogNode               node;       // for loggerRef cleanup
9362431SN/A        private WeakReference<Logger> parentRef;  // for kids cleanup
9372038SN/A        private boolean disposed = false;         // avoid calling dispose twice
9382038SN/A
9392038SN/A        LoggerWeakRef(Logger logger) {
9402038SN/A            super(logger, loggerRefQueue);
94112209Skshefov
94212209Skshefov            name = logger.getName();  // save for namedLoggers cleanup
94312209Skshefov        }
94412209Skshefov
9452038SN/A        // dispose of this LoggerWeakRef object
9462038SN/A        void dispose() {
9472038SN/A            // Avoid calling dispose twice. When a Logger is gc'ed, its
9485688Stwisti            // LoggerWeakRef will be enqueued.
9492038SN/A            // However, a new logger of the same name may be added (or looked
9502038SN/A            // up) before the queue is drained. When that happens, dispose()
9512038SN/A            // will be called by addLocalLogger() or findLogger().
9522038SN/A            // Later when the queue is drained, dispose() will be called again
9532038SN/A            // for the same LoggerWeakRef. Marking LoggerWeakRef as disposed
9542431SN/A            // avoids processing the data twice (even though the code should
9552431SN/A            // now be reentrant).
9562431SN/A            synchronized(this) {
9572431SN/A                // Note to maintainers:
9582038SN/A                // Be careful not to call any method that tries to acquire
9592038SN/A                // another lock from within this block - as this would surely
9602038SN/A                // lead to deadlocks, given that dispose() can be called by
9612038SN/A                // multiple threads, and from within different synchronized
9622038SN/A                // methods/blocks.
9632038SN/A                if (disposed) return;
9642038SN/A                disposed = true;
9652038SN/A            }
9662038SN/A
9675688Stwisti            final LogNode n = node;
9685688Stwisti            if (n != null) {
9695688Stwisti                // n.loggerRef can only be safely modified from within
9702038SN/A                // a lock on LoggerContext. removeLoggerRef is already
9712038SN/A                // synchronized on LoggerContext so calling
9722038SN/A                // n.context.removeLoggerRef from within this lock is safe.
9732038SN/A                synchronized (n.context) {
9742431SN/A                    // if we have a LogNode, then we were a named Logger
9752038SN/A                    // so clear namedLoggers weak ref to us
9762038SN/A                    n.context.removeLoggerRef(name, this);
9772431SN/A                    name = null;  // clear our ref to the Logger's name
9782431SN/A
9792431SN/A                    // LogNode may have been reused - so only clear
9802431SN/A                    // LogNode.loggerRef if LogNode.loggerRef == this
9812431SN/A                    if (n.loggerRef == this) {
9822431SN/A                        n.loggerRef = null;  // clear LogNode's weak ref to us
9832431SN/A                    }
9842431SN/A                    node = null;            // clear our ref to LogNode
9852038SN/A                }
9865688Stwisti            }
9872040SN/A
9888314Sjrose            if (parentRef != null) {
9898314Sjrose                // this LoggerWeakRef has or had a parent Logger
9908314Sjrose                Logger parent = parentRef.get();
9918314Sjrose                if (parent != null) {
9925688Stwisti                    // the parent Logger is still there so clear the
9932038SN/A                    // parent Logger's weak ref to us
9943011SN/A                    parent.removeChildLogger(this);
9952431SN/A                }
9962431SN/A                parentRef = null;  // clear our weak ref to the parent Logger
9972038SN/A            }
9982431SN/A        }
9992431SN/A
10008314Sjrose        // set the node field to the specified value
10012431SN/A        void setNode(LogNode node) {
10025688Stwisti            this.node = node;
10033530SN/A        }
10042038SN/A
10058314Sjrose        // set the parentRef field to the specified value
10068314Sjrose        void setParentRef(WeakReference<Logger> parentRef) {
10078314Sjrose            this.parentRef = parentRef;
10085688Stwisti        }
10092038SN/A    }
10102431SN/A
10112431SN/A    // Package-level method.
10122431SN/A    // Drain some Logger objects that have been GC'ed.
10132431SN/A    //
10142431SN/A    // drainLoggerRefQueueBounded() is called by addLogger() below
10152431SN/A    // and by Logger.getAnonymousLogger(String) so we'll drain up to
10162038SN/A    // MAX_ITERATIONS GC'ed Loggers for every Logger we add.
10172038SN/A    //
10182038SN/A    // On a WinXP VMware client, a MAX_ITERATIONS value of 400 gives
10192431SN/A    // us about a 50/50 mix in increased weak ref counts versus
10202038SN/A    // decreased weak ref counts in the AnonLoggerWeakRefLeak test.
10212038SN/A    // Here are stats for cleaning up sets of 400 anonymous Loggers:
10222431SN/A    //   - test duration 1 minute
10232038SN/A    //   - sample size of 125 sets of 400
10242040SN/A    //   - average: 1.99 ms
10252431SN/A    //   - minimum: 0.57 ms
10262431SN/A    //   - maximum: 25.3 ms
10272431SN/A    //
10282431SN/A    // The same config gives us a better decreased weak ref count
10292431SN/A    // than increased weak ref count in the LoggerWeakRefLeak test.
10302431SN/A    // Here are stats for cleaning up sets of 400 named Loggers:
10312431SN/A    //   - test duration 2 minutes
10322431SN/A    //   - sample size of 506 sets of 400
10332431SN/A    //   - average: 0.57 ms
10342038SN/A    //   - minimum: 0.02 ms
10352038SN/A    //   - maximum: 10.9 ms
10363240SN/A    //
10372038SN/A    private final static int MAX_ITERATIONS = 400;
10382431SN/A    final void drainLoggerRefQueueBounded() {
10392431SN/A        for (int i = 0; i < MAX_ITERATIONS; i++) {
10402431SN/A            if (loggerRefQueue == null) {
10412431SN/A                // haven't finished loading LogManager yet
10422431SN/A                break;
10432431SN/A            }
10442431SN/A
10452431SN/A            LoggerWeakRef ref = (LoggerWeakRef) loggerRefQueue.poll();
10462431SN/A            if (ref == null) {
10472038SN/A                break;
10482038SN/A            }
10492431SN/A            // a Logger object has been GC'ed so clean it up
10502038SN/A            ref.dispose();
105112209Skshefov        }
105212209Skshefov    }
105312209Skshefov
105412209Skshefov    /**
10552431SN/A     * Add a named logger.  This does nothing and returns false if a logger
10562038SN/A     * with the same name is already registered.
10572431SN/A     * <p>
10582431SN/A     * The Logger factory methods call this method to register each
10592431SN/A     * newly created Logger.
10602431SN/A     * <p>
10612431SN/A     * The application should retain its own reference to the Logger
10622431SN/A     * object to avoid it being garbage collected.  The LogManager
10632431SN/A     * may only retain a weak reference.
10642038SN/A     *
10652038SN/A     * @param   logger the new logger.
10662040SN/A     * @return  true if the argument logger was registered successfully,
10672040SN/A     *          false if a logger of that name already exists.
10682040SN/A     * @exception NullPointerException if the logger name is null.
10692040SN/A     */
10702040SN/A    public boolean addLogger(Logger logger) {
10712040SN/A        final String name = logger.getName();
10722040SN/A        if (name == null) {
10732040SN/A            throw new NullPointerException();
10742040SN/A        }
10752040SN/A        drainLoggerRefQueueBounded();
10762040SN/A        LoggerContext cx = getUserContext();
10772040SN/A        if (cx.addLocalLogger(logger)) {
10782040SN/A            // Do we have a per logger handler too?
10792040SN/A            // Note: this will add a 200ms penalty
10802040SN/A            loadLoggerHandlers(logger, name, name + ".handlers");
10812040SN/A            return true;
10822040SN/A        } else {
10832040SN/A            return false;
10842040SN/A        }
10852040SN/A    }
10862040SN/A
10872040SN/A    // Private method to set a level on a logger.
10882040SN/A    // If necessary, we raise privilege before doing the call.
10892040SN/A    private static void doSetLevel(final Logger logger, final Level level) {
10902040SN/A        SecurityManager sm = System.getSecurityManager();
10914935Sjrose        if (sm == null) {
10922040SN/A            // There is no security manager, so things are easy.
10932040SN/A            logger.setLevel(level);
10942040SN/A            return;
10952040SN/A        }
10962040SN/A        // There is a security manager.  Raise privilege before
10972040SN/A        // calling setLevel.
10982040SN/A        AccessController.doPrivileged(new PrivilegedAction<Object>() {
10992040SN/A            @Override
11002040SN/A            public Object run() {
11012040SN/A                logger.setLevel(level);
11022040SN/A                return null;
11032040SN/A            }});
11042040SN/A    }
11052040SN/A
11062040SN/A    // Private method to set a parent on a logger.
11072040SN/A    // If necessary, we raise privilege before doing the setParent call.
11084183Sjrose    private static void doSetParent(final Logger logger, final Logger parent) {
11092040SN/A        SecurityManager sm = System.getSecurityManager();
11104935Sjrose        if (sm == null) {
11112040SN/A            // There is no security manager, so things are easy.
11122040SN/A            logger.setParent(parent);
11132040SN/A            return;
11142040SN/A        }
11154935Sjrose        // There is a security manager.  Raise privilege before
11162040SN/A        // calling setParent.
11172040SN/A        AccessController.doPrivileged(new PrivilegedAction<Object>() {
11182040SN/A            @Override
11192040SN/A            public Object run() {
11202040SN/A                logger.setParent(parent);
11213530SN/A                return null;
11222040SN/A            }});
11233530SN/A    }
11243530SN/A
11253530SN/A    /**
11262040SN/A     * Method to find a named logger.
11272040SN/A     * <p>
11282040SN/A     * Note that since untrusted code may create loggers with
11293530SN/A     * arbitrary names this method should not be relied on to
11303530SN/A     * find Loggers for security sensitive logging.
11312040SN/A     * It is also important to note that the Logger associated with the
11322040SN/A     * String {@code name} may be garbage collected at any time if there
11332040SN/A     * is no strong reference to the Logger. The caller of this method
11342040SN/A     * must check the return value for null in order to properly handle
11355688Stwisti     * the case where the Logger has been garbage collected.
11362435SN/A     *
11372435SN/A     * @param name name of the logger
11383530SN/A     * @return  matching logger or null if none is found
11392435SN/A     */
11403530SN/A    public Logger getLogger(String name) {
11412435SN/A        return getUserContext().findLogger(name);
11423530SN/A    }
11432435SN/A
11442435SN/A    /**
11452040SN/A     * Get an enumeration of known logger names.
11462038SN/A     * <p>
114712209Skshefov     * Note:  Loggers may be added dynamically as new classes are loaded.
114812209Skshefov     * This method only reports on the loggers that are currently registered.
114912209Skshefov     * It is also important to note that this method only returns the name
115012209Skshefov     * of a Logger, not a strong reference to the Logger itself.
11515688Stwisti     * The returned String does nothing to prevent the Logger from being
11522435SN/A     * garbage collected. In particular, if the returned name is passed
11532435SN/A     * to {@code LogManager.getLogger()}, then the caller must check the
11542435SN/A     * return value from {@code LogManager.getLogger()} for null to properly
115512209Skshefov     * handle the case where the Logger has been garbage collected in the
11562435SN/A     * time since its name was returned by this method.
11572435SN/A     *
115812209Skshefov     * @return  enumeration of logger name strings
115912209Skshefov     */
116012209Skshefov    public Enumeration<String> getLoggerNames() {
116112209Skshefov        return getUserContext().getLoggerNames();
11625688Stwisti    }
11632435SN/A
11642435SN/A    /**
11655688Stwisti     * Reinitialize the logging properties and reread the logging configuration.
11662435SN/A     * <p>
116712209Skshefov     * The same rules are used for locating the configuration properties
11682435SN/A     * as are used at startup.  So normally the logging properties will
11692435SN/A     * be re-read from the same file that was used at startup.
117012209Skshefov     * <P>
117112209Skshefov     * Any log level definitions in the new configuration file will be
117212209Skshefov     * applied using Logger.setLevel(), if the target Logger exists.
117312209Skshefov     * <p>
11745688Stwisti     * Any {@linkplain #addConfigurationListener registered configuration
11752435SN/A     * listener} will be invoked after the properties are read.
11763530SN/A     *
11772435SN/A     * @exception  SecurityException  if a security manager exists and if
117812209Skshefov     *             the caller does not have LoggingPermission("control").
11792435SN/A     * @exception  IOException if there are IO problems reading the configuration.
11802038SN/A     */
11812040SN/A    public void readConfiguration() throws IOException, SecurityException {
11823530SN/A        checkPermission();
11833530SN/A
11845685Sjrose        // if a configuration class is specified, load it and use it.
11855685Sjrose        String cname = System.getProperty("java.util.logging.config.class");
11863530SN/A        if (cname != null) {
11873530SN/A            try {
11883530SN/A                // Instantiate the named class.  It is its constructor's
11893530SN/A                // responsibility to initialize the logging configuration, by
11903530SN/A                // calling readConfiguration(InputStream) with a suitable stream.
11913530SN/A                try {
11923530SN/A                    Class<?> clz = ClassLoader.getSystemClassLoader().loadClass(cname);
11933530SN/A                    clz.newInstance();
11942040SN/A                    return;
11952040SN/A                } catch (ClassNotFoundException ex) {
11963530SN/A                    Class<?> clz = Thread.currentThread().getContextClassLoader().loadClass(cname);
11973530SN/A                    clz.newInstance();
11983530SN/A                    return;
11993530SN/A                }
12003530SN/A            } catch (Exception ex) {
12015685Sjrose                System.err.println("Logging configuration class \"" + cname + "\" failed");
12023530SN/A                System.err.println("" + ex);
12034383Sjrose                // keep going and useful config file.
12045685Sjrose            }
12053530SN/A        }
12065688Stwisti
12075685Sjrose        String fname = System.getProperty("java.util.logging.config.file");
12085688Stwisti        if (fname == null) {
12095685Sjrose            fname = System.getProperty("java.home");
12103530SN/A            if (fname == null) {
12113530SN/A                throw new Error("Can't find java.home ??");
12123530SN/A            }
12133530SN/A            File f = new File(fname, "conf");
12143530SN/A            f = new File(f, "logging.properties");
12153530SN/A            fname = f.getCanonicalPath();
12163530SN/A        }
12173530SN/A        try (final InputStream in = new FileInputStream(fname)) {
12183530SN/A            final BufferedInputStream bin = new BufferedInputStream(in);
12193530SN/A            readConfiguration(bin);
12203530SN/A        }
12213530SN/A    }
12223530SN/A
12233530SN/A    /**
12243530SN/A     * Reset the logging configuration.
12253530SN/A     * <p>
12263530SN/A     * For all named loggers, the reset operation removes and closes
12273530SN/A     * all Handlers and (except for the root logger) sets the level
12283530SN/A     * to null.  The root logger's level is set to Level.INFO.
12293530SN/A     *
12303530SN/A     * @exception  SecurityException  if a security manager exists and if
12313530SN/A     *             the caller does not have LoggingPermission("control").
12322435SN/A     */
12333530SN/A
12345685Sjrose    public void reset() throws SecurityException {
12353530SN/A        checkPermission();
12363530SN/A        synchronized (this) {
12373530SN/A            props = new Properties();
12383530SN/A            // Since we are doing a reset we no longer want to initialize
12393530SN/A            // the global handlers, if they haven't been initialized yet.
12403530SN/A            initializedGlobalHandlers = true;
12412040SN/A        }
12423530SN/A        for (LoggerContext cx : contexts()) {
12433530SN/A            Enumeration<String> enum_ = cx.getLoggerNames();
12443530SN/A            while (enum_.hasMoreElements()) {
12455688Stwisti                String name = enum_.nextElement();
12463530SN/A                Logger logger = cx.findLogger(name);
12473530SN/A                if (logger != null) {
12483530SN/A                    resetLogger(logger);
12493530SN/A                }
12503530SN/A            }
12513530SN/A        }
12523530SN/A    }
12533530SN/A
12543530SN/A    // Private method to reset an individual target logger.
12553530SN/A    private void resetLogger(Logger logger) {
12563530SN/A        // Close all the Logger's handlers.
12573530SN/A        Handler[] targets = logger.getHandlers();
12583530SN/A        for (Handler h : targets) {
12593530SN/A            logger.removeHandler(h);
12603530SN/A            try {
12618314Sjrose                h.close();
12628314Sjrose            } catch (Exception ex) {
12638314Sjrose                // Problems closing a handler?  Keep going...
12648314Sjrose            }
12658314Sjrose        }
12665688Stwisti        String name = logger.getName();
12673530SN/A        if (name != null && name.equals("")) {
12683530SN/A            // This is the root logger.
12693530SN/A            logger.setLevel(defaultLevel);
12703530SN/A        } else {
12713530SN/A            logger.setLevel(null);
12725685Sjrose        }
12735685Sjrose    }
12745685Sjrose
12753530SN/A    // get a list of whitespace separated classnames from a property.
12763530SN/A    private String[] parseClassNames(String propertyName) {
12773530SN/A        String hands = getProperty(propertyName);
12782040SN/A        if (hands == null) {
12795688Stwisti            return new String[0];
12802040SN/A        }
12815685Sjrose        hands = hands.trim();
12825685Sjrose        int ix = 0;
12835688Stwisti        final List<String> result = new ArrayList<>();
12845688Stwisti        while (ix < hands.length()) {
12852040SN/A            int end = ix;
12863530SN/A            while (end < hands.length()) {
12873530SN/A                if (Character.isWhitespace(hands.charAt(end))) {
12883530SN/A                    break;
12894251Sjrose                }
12904251Sjrose                if (hands.charAt(end) == ',') {
12913530SN/A                    break;
12923530SN/A                }
12934251Sjrose                end++;
12944251Sjrose            }
12954251Sjrose            String word = hands.substring(ix, end);
12963530SN/A            ix = end+1;
12973530SN/A            word = word.trim();
12983530SN/A            if (word.length() == 0) {
12993530SN/A                continue;
13005685Sjrose            }
13013530SN/A            result.add(word);
13023530SN/A        }
13033530SN/A        return result.toArray(new String[result.size()]);
13045688Stwisti    }
13055688Stwisti
13065688Stwisti    /**
13075688Stwisti     * Reinitialize the logging properties and reread the logging configuration
13085688Stwisti     * from the given stream, which should be in java.util.Properties format.
13095688Stwisti     * Any {@linkplain #addConfigurationListener registered configuration
13105688Stwisti     * listener} will be invoked after the properties are read.
13115688Stwisti     * <p>
13125685Sjrose     * Any log level definitions in the new configuration file will be
13135685Sjrose     * applied using Logger.setLevel(), if the target Logger exists.
13145685Sjrose     *
13155685Sjrose     * @param ins       stream to read properties from
13165688Stwisti     * @exception  SecurityException  if a security manager exists and if
13175688Stwisti     *             the caller does not have LoggingPermission("control").
13185688Stwisti     * @exception  IOException if there are problems reading from the stream.
13195688Stwisti     */
13205688Stwisti    public void readConfiguration(InputStream ins) throws IOException, SecurityException {
13215685Sjrose        checkPermission();
13223530SN/A        reset();
13233530SN/A
13243530SN/A        // Load the properties
13253530SN/A        props.load(ins);
13263530SN/A        // Instantiate new configuration objects.
13273530SN/A        String names[] = parseClassNames("config");
13283530SN/A
13293530SN/A        for (String word : names) {
13303530SN/A            try {
13313530SN/A                Class<?> clz = ClassLoader.getSystemClassLoader().loadClass(word);
13322040SN/A                clz.newInstance();
13333530SN/A            } catch (Exception ex) {
13343530SN/A                System.err.println("Can't load config class \"" + word + "\"");
13353530SN/A                System.err.println("" + ex);
13365688Stwisti                // ex.printStackTrace();
13375688Stwisti            }
13385688Stwisti        }
13395688Stwisti
13405688Stwisti        // Set levels on any pre-existing loggers, based on the new properties.
13415688Stwisti        setLevelsOnExistingLoggers();
13425688Stwisti
13435685Sjrose        try {
13445685Sjrose            invokeConfigurationListeners();
13455685Sjrose        } finally {
13465685Sjrose            // Note that we need to reinitialize global handles when
13475688Stwisti            // they are first referenced.
13485688Stwisti            synchronized (this) {
13495688Stwisti                initializedGlobalHandlers = false;
13505688Stwisti            }
13515688Stwisti        }
13525685Sjrose    }
13533530SN/A
13543530SN/A    /**
13553530SN/A     * Get the value of a logging property.
13563530SN/A     * The method returns null if the property is not found.
13573530SN/A     * @param name      property name
13582040SN/A     * @return          property value
13593530SN/A     */
13603530SN/A    public String getProperty(String name) {
13613530SN/A        return props.getProperty(name);
13625685Sjrose    }
13635685Sjrose
13645685Sjrose    // Package private method to get a String property.
13655685Sjrose    // If the property is not defined we return the given
13665685Sjrose    // default value.
13675685Sjrose    String getStringProperty(String name, String defaultValue) {
13685685Sjrose        String val = getProperty(name);
13695685Sjrose        if (val == null) {
13702038SN/A            return defaultValue;
13712038SN/A        }
13722040SN/A        return val.trim();
13732038SN/A    }
137412209Skshefov
137512209Skshefov    // Package private method to get an integer property.
137612209Skshefov    // If the property is not defined or cannot be parsed
137712209Skshefov    // we return the given default value.
13785688Stwisti    int getIntProperty(String name, int defaultValue) {
13792435SN/A        String val = getProperty(name);
13802435SN/A        if (val == null) {
13812435SN/A            return defaultValue;
138212209Skshefov        }
13832435SN/A        try {
13842435SN/A            return Integer.parseInt(val.trim());
138512209Skshefov        } catch (Exception ex) {
138612209Skshefov            return defaultValue;
138712209Skshefov        }
138812209Skshefov    }
13895688Stwisti
13902435SN/A    // Package private method to get a long property.
13912435SN/A    // If the property is not defined or cannot be parsed
13925688Stwisti    // we return the given default value.
13932435SN/A    long getLongProperty(String name, long defaultValue) {
139412209Skshefov        String val = getProperty(name);
13952435SN/A        if (val == null) {
13962435SN/A            return defaultValue;
139712209Skshefov        }
139812209Skshefov        try {
139912209Skshefov            return Long.parseLong(val.trim());
140012209Skshefov        } catch (Exception ex) {
14015688Stwisti            return defaultValue;
14022435SN/A        }
14033530SN/A    }
14042435SN/A
140512209Skshefov    // Package private method to get a boolean property.
14062435SN/A    // If the property is not defined or cannot be parsed
14072038SN/A    // we return the given default value.
14082038SN/A    boolean getBooleanProperty(String name, boolean defaultValue) {
14092040SN/A        String val = getProperty(name);
14103530SN/A        if (val == null) {
14113530SN/A            return defaultValue;
14125685Sjrose        }
14135685Sjrose        val = val.toLowerCase();
14143530SN/A        if (val.equals("true") || val.equals("1")) {
14153530SN/A            return true;
14163530SN/A        } else if (val.equals("false") || val.equals("0")) {
14173530SN/A            return false;
14183530SN/A        }
14192040SN/A        return defaultValue;
14202040SN/A    }
14213530SN/A
14223530SN/A    // Package private method to get a Level property.
14233530SN/A    // If the property is not defined or cannot be parsed
14242038SN/A    // we return the given default value.
14252038SN/A    Level getLevelProperty(String name, Level defaultValue) {
14262040SN/A        String val = getProperty(name);
14272038SN/A        if (val == null) {
142812209Skshefov            return defaultValue;
142912209Skshefov        }
143012209Skshefov        Level l = Level.findLevel(val.trim());
143112209Skshefov        return l != null ? l : defaultValue;
14325688Stwisti    }
14332038SN/A
14342431SN/A    // Package private method to get a filter property.
14352040SN/A    // We return an instance of the class named by the "name"
14362040SN/A    // property. If the property is not defined or has problems
14372040SN/A    // we return the defaultValue.
14382040SN/A    Filter getFilterProperty(String name, Filter defaultValue) {
143912209Skshefov        String val = getProperty(name);
144012209Skshefov        try {
144112209Skshefov            if (val != null) {
144212209Skshefov                Class<?> clz = ClassLoader.getSystemClassLoader().loadClass(val);
14435688Stwisti                return (Filter) clz.newInstance();
14442040SN/A            }
14452431SN/A        } catch (Exception ex) {
14462431SN/A            // We got one of a variety of exceptions in creating the
14472431SN/A            // class or creating an instance.
14485685Sjrose            // Drop through.
14495685Sjrose        }
14502431SN/A        // We got an exception.  Return the defaultValue.
14515685Sjrose        return defaultValue;
14525685Sjrose    }
14535685Sjrose
14545685Sjrose
14555685Sjrose    // Package private method to get a formatter property.
145612209Skshefov    // We return an instance of the class named by the "name"
145712209Skshefov    // property. If the property is not defined or has problems
145812209Skshefov    // we return the defaultValue.
145912209Skshefov    Formatter getFormatterProperty(String name, Formatter defaultValue) {
14605688Stwisti        String val = getProperty(name);
14615685Sjrose        try {
14625685Sjrose            if (val != null) {
14635685Sjrose                Class<?> clz = ClassLoader.getSystemClassLoader().loadClass(val);
14645685Sjrose                return (Formatter) clz.newInstance();
14655685Sjrose            }
14665685Sjrose        } catch (Exception ex) {
14675685Sjrose            // We got one of a variety of exceptions in creating the
14685685Sjrose            // class or creating an instance.
14692038SN/A            // Drop through.
14702038SN/A        }
14715685Sjrose        // We got an exception.  Return the defaultValue.
14725685Sjrose        return defaultValue;
14735685Sjrose    }
14745685Sjrose
14755685Sjrose    // Private method to load the global handlers.
14765685Sjrose    // We do the real work lazily, when the global handlers
14775685Sjrose    // are first used.
14785685Sjrose    private synchronized void initializeGlobalHandlers() {
14795685Sjrose        if (initializedGlobalHandlers) {
14805685Sjrose            return;
14815685Sjrose        }
14825685Sjrose
14835685Sjrose        initializedGlobalHandlers = true;
14845685Sjrose
14855685Sjrose        if (deathImminent) {
14865685Sjrose            // Aaargh...
14875685Sjrose            // The VM is shutting down and our exit hook has been called.
14885685Sjrose            // Avoid allocating global handlers.
14895685Sjrose            return;
14902040SN/A        }
14912040SN/A        loadLoggerHandlers(rootLogger, null, "handlers");
14925685Sjrose    }
14935685Sjrose
14945685Sjrose    static final Permission controlPermission = new LoggingPermission("control", null);
14955685Sjrose
14965685Sjrose    void checkPermission() {
14975685Sjrose        SecurityManager sm = System.getSecurityManager();
14985685Sjrose        if (sm != null)
14995685Sjrose            sm.checkPermission(controlPermission);
15005685Sjrose    }
15015685Sjrose
15025685Sjrose    /**
15035685Sjrose     * Check that the current context is trusted to modify the logging
15045685Sjrose     * configuration.  This requires LoggingPermission("control").
15055685Sjrose     * <p>
15065685Sjrose     * If the check fails we throw a SecurityException, otherwise
15075685Sjrose     * we return normally.
15085685Sjrose     *
15095685Sjrose     * @exception  SecurityException  if a security manager exists and if
15105685Sjrose     *             the caller does not have LoggingPermission("control").
15115685Sjrose     */
15125685Sjrose    public void checkAccess() throws SecurityException {
15135685Sjrose        checkPermission();
15145685Sjrose    }
15155685Sjrose
15165685Sjrose    // Nested class to represent a node in our tree of named loggers.
15175685Sjrose    private static class LogNode {
15182040SN/A        HashMap<String,LogNode> children;
15192040SN/A        LoggerWeakRef loggerRef;
15202040SN/A        LogNode parent;
15212040SN/A        final LoggerContext context;
15222040SN/A
15232040SN/A        LogNode(LogNode parent, LoggerContext context) {
15242040SN/A            this.parent = parent;
15252431SN/A            this.context = context;
15265685Sjrose        }
15274183Sjrose
15284251Sjrose        // Recursive method to walk the tree below a node and set
15292431SN/A        // a new parent logger.
15302040SN/A        void walkAndSetParent(Logger parent) {
15312040SN/A            if (children == null) {
15325685Sjrose                return;
15332040SN/A            }
15342040SN/A            for (LogNode node : children.values()) {
15352040SN/A                LoggerWeakRef ref = node.loggerRef;
15362040SN/A                Logger logger = (ref == null) ? null : ref.get();
15372040SN/A                if (logger == null) {
15385685Sjrose                    node.walkAndSetParent(parent);
15395685Sjrose                } else {
15405685Sjrose                    doSetParent(logger, parent);
15415685Sjrose                }
15425685Sjrose            }
15435685Sjrose        }
15445685Sjrose    }
15455685Sjrose
15465685Sjrose    // We use a subclass of Logger for the root logger, so
15475685Sjrose    // that we only instantiate the global handlers when they
15485685Sjrose    // are first needed.
15492040SN/A    private final class RootLogger extends Logger {
15502040SN/A        private RootLogger() {
15512040SN/A            // We do not call the protected Logger two args constructor here,
15522431SN/A            // to avoid calling LogManager.getLogManager() from within the
15532431SN/A            // RootLogger constructor.
15542431SN/A            super("", null, null, LogManager.this, true);
15552431SN/A        }
15562431SN/A
15572431SN/A        @Override
15582040SN/A        public void log(LogRecord record) {
15592040SN/A            // Make sure that the global handlers have been instantiated.
15602040SN/A            initializeGlobalHandlers();
15612040SN/A            super.log(record);
15622040SN/A        }
15635685Sjrose
15645685Sjrose        @Override
15655685Sjrose        public void addHandler(Handler h) {
15665685Sjrose            initializeGlobalHandlers();
15675685Sjrose            super.addHandler(h);
15685685Sjrose        }
15695685Sjrose
15705685Sjrose        @Override
15715685Sjrose        public void removeHandler(Handler h) {
15725685Sjrose            initializeGlobalHandlers();
15735685Sjrose            super.removeHandler(h);
15742040SN/A        }
15752040SN/A
15762040SN/A        @Override
15772040SN/A        Handler[] accessCheckedHandlers() {
15785685Sjrose            initializeGlobalHandlers();
15795685Sjrose            return super.accessCheckedHandlers();
15805685Sjrose        }
15815685Sjrose    }
15825685Sjrose
15835685Sjrose
15845685Sjrose    // Private method to be called when the configuration has
15855685Sjrose    // changed to apply any level settings to any pre-existing loggers.
15865685Sjrose    synchronized private void setLevelsOnExistingLoggers() {
15875685Sjrose        Enumeration<?> enum_ = props.propertyNames();
15885685Sjrose        while (enum_.hasMoreElements()) {
15895685Sjrose            String key = (String)enum_.nextElement();
15905685Sjrose            if (!key.endsWith(".level")) {
15915685Sjrose                // Not a level definition.
15925685Sjrose                continue;
15935685Sjrose            }
15945685Sjrose            int ix = key.length() - 6;
15955685Sjrose            String name = key.substring(0, ix);
15962040SN/A            Level level = getLevelProperty(key, null);
15972040SN/A            if (level == null) {
15982040SN/A                System.err.println("Bad level value for property: " + key);
15992040SN/A                continue;
16004935Sjrose            }
16012040SN/A            for (LoggerContext cx : contexts()) {
16022040SN/A                Logger l = cx.findLogger(name);
16032040SN/A                if (l == null) {
16042038SN/A                    continue;
16052038SN/A                }
16062038SN/A                l.setLevel(level);
16072038SN/A            }
16082038SN/A        }
16092038SN/A    }
16102038SN/A
16112038SN/A    // Management Support
16122038SN/A    private static LoggingMXBean loggingMXBean = null;
16132038SN/A    /**
16142038SN/A     * String representation of the
16152038SN/A     * {@link javax.management.ObjectName} for the management interface
16162038SN/A     * for the logging facility.
16172040SN/A     *
16182040SN/A     * @see java.lang.management.PlatformLoggingMXBean
16192038SN/A     * @see java.util.logging.LoggingMXBean
16202038SN/A     *
16212040SN/A     * @since 1.5
16222038SN/A     */
16232038SN/A    public final static String LOGGING_MXBEAN_NAME
16242038SN/A        = "java.util.logging:type=Logging";
16252038SN/A
16262038SN/A    /**
16272038SN/A     * Returns <tt>LoggingMXBean</tt> for managing loggers.
16282038SN/A     * An alternative way to manage loggers is through the
16293011SN/A     * {@link java.lang.management.PlatformLoggingMXBean} interface
16303011SN/A     * that can be obtained by calling:
16314935Sjrose     * <pre>
16323011SN/A     *     PlatformLoggingMXBean logging = {@link java.lang.management.ManagementFactory#getPlatformMXBean(Class)
16333011SN/A     *         ManagementFactory.getPlatformMXBean}(PlatformLoggingMXBean.class);
16342038SN/A     * </pre>
16352038SN/A     *
16362038SN/A     * @return a {@link LoggingMXBean} object.
16372038SN/A     *
16382038SN/A     * @see java.lang.management.PlatformLoggingMXBean
163912209Skshefov     * @since 1.5
164012209Skshefov     */
164112209Skshefov    public static synchronized LoggingMXBean getLoggingMXBean() {
164212209Skshefov        if (loggingMXBean == null) {
16432038SN/A            loggingMXBean =  new Logging();
16442038SN/A        }
16452038SN/A        return loggingMXBean;
16462038SN/A    }
16472038SN/A
16482038SN/A    /**
16492431SN/A     * Adds a configuration listener to be invoked each time the logging
16502431SN/A     * configuration is read.
16512038SN/A     * If the listener is already registered the method does nothing.
16522038SN/A     * <p>
16532038SN/A     * The listener is invoked with privileges that are restricted by the
16544251Sjrose     * calling context of this method.
16552038SN/A     * The order in which the listeners are invoked is unspecified.
16562038SN/A     * <p>
16574251Sjrose     * It is recommended that listeners do not throw errors or exceptions.
16583240SN/A     *
16592038SN/A     * If a listener terminates with an uncaught error or exception then
16602038SN/A     * the first exception will be propagated to the caller of
16612038SN/A     * {@link #readConfiguration()} (or {@link #readConfiguration(java.io.InputStream)})
16622038SN/A     * after all listeners have been invoked.
16632038SN/A     *
16642038SN/A     * @implNote If more than one listener terminates with an uncaught error or
16652038SN/A     * exception, an implementation may record the additional errors or
16662040SN/A     * exceptions as {@linkplain Throwable#addSuppressed(java.lang.Throwable)
16672038SN/A     * suppressed exceptions}.
16682038SN/A     *
16692038SN/A     * @param listener A configuration listener that will be invoked after the
16702038SN/A     *        configuration changed.
16712038SN/A     * @return This LogManager.
16722038SN/A     * @throws SecurityException if a security manager exists and if the
16732038SN/A     * caller does not have LoggingPermission("control").
16742038SN/A     * @throws NullPointerException if the listener is null.
16753240SN/A     *
16762038SN/A     * @since 1.9
16772038SN/A     */
16782038SN/A    public LogManager addConfigurationListener(Runnable listener) {
16792038SN/A        final Runnable r = Objects.requireNonNull(listener);
16802038SN/A        checkPermission();
16812038SN/A        final SecurityManager sm = System.getSecurityManager();
16822038SN/A        final AccessControlContext acc =
16832038SN/A                sm == null ? null : AccessController.getContext();
16842038SN/A        final PrivilegedAction<Void> pa =
16854251Sjrose                acc == null ? null : () -> { r.run() ; return null; };
16865774Sjrose        final Runnable pr =
16872038SN/A                acc == null ? r : () -> AccessController.doPrivileged(pa, acc);
16882038SN/A        // Will do nothing if already registered.
16892431SN/A        listeners.putIfAbsent(r, pr);
16902038SN/A        return this;
16912038SN/A    }
16922038SN/A
16932038SN/A    /**
16942038SN/A     * Removes a previously registered configuration listener.
16952038SN/A     *
16962038SN/A     * Returns silently if the listener is not found.
16973240SN/A     *
16982038SN/A     * @param listener the configuration listener to remove.
16992038SN/A     * @throws NullPointerException if the listener is null.
17002431SN/A     * @throws SecurityException if a security manager exists and if the
17012431SN/A     * caller does not have LoggingPermission("control").
17022038SN/A     *
17032038SN/A     * @since 1.9
17042038SN/A     */
17053528SN/A    public void removeConfigurationListener(Runnable listener) {
170612209Skshefov        final Runnable key = Objects.requireNonNull(listener);
170712209Skshefov        checkPermission();
170812209Skshefov        listeners.remove(key);
170912209Skshefov    }
17105688Stwisti
17115688Stwisti    private void invokeConfigurationListeners() {
17123528SN/A        Throwable t = null;
17133528SN/A
17143528SN/A        // We're using an IdentityHashMap because we want to compare
17153528SN/A        // keys using identity (==).
17164251Sjrose        // We don't want to loop within a block synchronized on 'listeners'
17174251Sjrose        // to avoid invoking listeners from yet another synchronized block.
17184935Sjrose        // So we're taking a snapshot of the values list to avoid the risk of
17194251Sjrose        // ConcurrentModificationException while looping.
17204251Sjrose        //
17213528SN/A        for (Runnable c : listeners.values().toArray(new Runnable[0])) {
17223528SN/A            try {
17233528SN/A                c.run();
172412209Skshefov            } catch (ThreadDeath death) {
17252040SN/A                throw death;
172612209Skshefov            } catch (Error | RuntimeException x) {
172712209Skshefov                if (t == null) t = x;
172812209Skshefov                else t.addSuppressed(x);
172912209Skshefov            }
17302040SN/A        }
17312040SN/A        // Listeners are not supposed to throw exceptions, but if that
17324383Sjrose        // happens, we will rethrow the first error or exception that is raised
17334383Sjrose        // after all listeners have been invoked.
17342040SN/A        if (t instanceof Error) throw (Error)t;
17354251Sjrose        if (t instanceof RuntimeException) throw (RuntimeException)t;
17362040SN/A    }
17372040SN/A
17382431SN/A}
17392040SN/A