       1 /*
       2  * Copyright (c) 2000, 2020, Oracle and/or its affiliates. All rights reserved.
       3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
       4  *
       5  * This code is free software; you can redistribute it and/or modify it
       6  * under the terms of the GNU General Public License version 2 only, as
       7  * published by the Free Software Foundation.  Oracle designates this
       8  * particular file as subject to the "Classpath" exception as provided
       9  * by Oracle in the LICENSE file that accompanied this code.
      10  *
      11  * This code is distributed in the hope that it will be useful, but WITHOUT
      12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
      13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
      14  * version 2 for more details (a copy is included in the LICENSE file that
      15  * accompanied this code).
      16  *
      17  * You should have received a copy of the GNU General Public License version
      18  * 2 along with this work; if not, write to the Free Software Foundation,
      19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
      20  *
      21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
      22  * or visit www.oracle.com if you need additional information or have any
      23  * questions.
      24  */
      25
      26 package java.util.logging;
      27
      28 import java.io.*;
      29 import java.util.*;
      30 import java.security.*;
      31 import java.lang.ref.ReferenceQueue;
      32 import java.lang.ref.WeakReference;
      33 import java.util.concurrent.ConcurrentHashMap;
      34 import java.nio.file.Paths;
      35 import java.util.concurrent.CopyOnWriteArrayList;
      36 import java.util.concurrent.locks.ReentrantLock;
      37 import java.util.function.BiFunction;
      38 import java.util.function.Function;
      39 import java.util.function.Predicate;
      40 import java.util.stream.Collectors;
      41 import java.util.stream.Stream;
      42 import jdk.internal.access.JavaAWTAccess;
      43 import jdk.internal.access.SharedSecrets;
      44 import sun.util.logging.internal.LoggingProviderImpl;
      45 import static jdk.internal.logger.DefaultLoggerFinder.isSystem;
      46
      47 /**
      48  * There is a single global LogManager object that is used to
      49  * maintain a set of shared state about Loggers and log services.
      50  * <p>
      51  * This LogManager object:
      52  * <ul>
      53  * <li> Manages a hierarchical namespace of Logger objects.  All
      54  *      named Loggers are stored in this namespace.
      55  * <li> Manages a set of logging control properties.  These are
      56  *      simple key-value pairs that can be used by Handlers and
      57  *      other logging objects to configure themselves.
      58  * </ul>
      59  * <p>
      60  * The global LogManager object can be retrieved using LogManager.getLogManager().
      61  * The LogManager object is created during class initialization and
      62  * cannot subsequently be changed.
      63  * <p>
      64  * At startup the LogManager class is located using the
      65  * java.util.logging.manager system property.
      66  *
      67  * <h2>LogManager Configuration</h2>
      68  *
      69  * A LogManager initializes the logging configuration via
      70  * the {@link #readConfiguration()} method during LogManager initialization.
      71  * By default, LogManager default configuration is used.
      72  * The logging configuration read by LogManager must be in the
      73  * {@linkplain Properties properties file} format.
      74  * <p>
      75  * The LogManager defines two optional system properties that allow control over
      76  * the initial configuration, as specified in the {@link #readConfiguration()}
      77  * method:
      78  * <ul>
      79  * <li>{@systemProperty java.util.logging.config.class}
      80  * <li>{@systemProperty java.util.logging.config.file}
      81  * </ul>
      82  * <p>
      83  * These two system properties may be specified on the command line to the "java"
      84  * command, or as system property definitions passed to JNI_CreateJavaVM.
      85  * <p>
      86  * The {@linkplain Properties properties} for loggers and Handlers will have
      87  * names starting with the dot-separated name for the handler or logger.<br>
      88  * The global logging properties may include:
      89  * <ul>
      90  * <li>A property "handlers".  This defines a whitespace or comma separated
      91  * list of class names for handler classes to load and register as
      92  * handlers on the root Logger (the Logger named "").  Each class
      93  * name must be for a Handler class which has a default constructor.
      94  * Note that these Handlers may be created lazily, when they are
      95  * first used.
      96  *
      97  * <li>A property "&lt;logger&gt;.handlers". This defines a whitespace or
      98  * comma separated list of class names for handlers classes to
      99  * load and register as handlers to the specified logger. Each class
     100  * name must be for a Handler class which has a default constructor.
     101  * Note that these Handlers may be created lazily, when they are
     102  * first used.
     103  *
     104  * <li>A property "&lt;logger&gt;.handlers.ensureCloseOnReset". This defines a
     105  * a boolean value. If "&lt;logger&gt;.handlers" is not defined or is empty,
     106  * this property is ignored. Otherwise it defaults to {@code true}. When the
     107  * value is {@code true}, the handlers associated with the logger are guaranteed
     108  * to be closed on {@linkplain #reset} and shutdown. This can be turned off
     109  * by explicitly setting "&lt;logger&gt;.handlers.ensureCloseOnReset=false" in
     110  * the configuration. Note that turning this property off causes the risk of
     111  * introducing a resource leak, as the logger may get garbage collected before
     112  * {@code reset()} is called, thus preventing its handlers from being closed
     113  * on {@code reset()}. In that case it is the responsibility of the application
     114  * to ensure that the handlers are closed before the logger is garbage
     115  * collected.
     116  *
     117  * <li>A property "&lt;logger&gt;.useParentHandlers". This defines a boolean
     118  * value. By default every logger calls its parent in addition to
     119  * handling the logging message itself, this often result in messages
     120  * being handled by the root logger as well. When setting this property
     121  * to false a Handler needs to be configured for this logger otherwise
     122  * no logging messages are delivered.
     123  *
     124  * <li>A property "config".  This property is intended to allow
     125  * arbitrary configuration code to be run.  The property defines a
     126  * whitespace or comma separated list of class names.  A new instance will be
     127  * created for each named class.  The default constructor of each class
     128  * may execute arbitrary code to update the logging configuration, such as
     129  * setting logger levels, adding handlers, adding filters, etc.
     130  * </ul>
     131  * <p>
     132  * Note that all classes loaded during LogManager configuration are
     133  * first searched on the system class path before any user class path.
     134  * That includes the LogManager class, any config classes, and any
     135  * handler classes.
     136  * <p>
     137  * Loggers are organized into a naming hierarchy based on their
     138  * dot separated names.  Thus "a.b.c" is a child of "a.b", but
     139  * "a.b1" and a.b2" are peers.
     140  * <p>
     141  * All properties whose names end with ".level" are assumed to define
     142  * log levels for Loggers.  Thus "foo.level" defines a log level for
     143  * the logger called "foo" and (recursively) for any of its children
     144  * in the naming hierarchy.  Log Levels are applied in the order they
     145  * are defined in the properties file.  Thus level settings for child
     146  * nodes in the tree should come after settings for their parents.
     147  * The property name ".level" can be used to set the level for the
     148  * root of the tree.
     149  * <p>
     150  * All methods on the LogManager object are multi-thread safe.
     151  *
     152  * @since 1.4
     153 */
     154
     155 public class LogManager {
     156     // The global LogManager object
     157     private static final LogManager manager;
     158
     159     // 'props' is assigned within a lock but accessed without it.
     160     // Declaring it volatile makes sure that another thread will not
     161     // be able to see a partially constructed 'props' object.
     162     // (seeing a partially constructed 'props' object can result in
     163     // NPE being thrown in Hashtable.get(), because it leaves the door
     164     // open for props.getProperties() to be called before the construcor
     165     // of Hashtable is actually completed).
     166     private volatile Properties props = new Properties();
     167     private static final Level defaultLevel = Level.INFO;
     168
     169     // LoggerContext for system loggers and user loggers
     170     private final LoggerContext systemContext = new SystemLoggerContext();
     171     private final LoggerContext userContext = new LoggerContext();
     172     // non final field - make it volatile to make sure that other threads
     173     // will see the new value once ensureLogManagerInitialized() has finished
     174     // executing.
     175     private volatile Logger rootLogger;
     176     // Have we done the primordial reading of the configuration file?
     177     // (Must be done after a suitable amount of java.lang.System
     178     // initialization has been done)
     179     private volatile boolean readPrimordialConfiguration;
     180     // Have we initialized global (root) handlers yet?
     181     // This gets set to STATE_UNINITIALIZED in readConfiguration
     182     private static final int
     183             STATE_INITIALIZED = 0, // initial state
     184             STATE_INITIALIZING = 1,
     185             STATE_READING_CONFIG = 2,
     186             STATE_UNINITIALIZED = 3,
     187             STATE_SHUTDOWN = 4;    // terminal state
     188     private volatile int globalHandlersState; // = STATE_INITIALIZED;
     189     // A concurrency lock for reset(), readConfiguration() and Cleaner.
     190     private final ReentrantLock configurationLock = new ReentrantLock();
     191
     192     // This list contains the loggers for which some handlers have been
     193     // explicitly configured in the configuration file.
     194     // It prevents these loggers from being arbitrarily garbage collected.
     195     private static final class CloseOnReset {
     196         private final Logger logger;
     197         private CloseOnReset(Logger ref) {
     198             this.logger = Objects.requireNonNull(ref);
     199         }
     200         @Override
     201         public boolean equals(Object other) {
     202             return (other instanceof CloseOnReset) && ((CloseOnReset)other).logger == logger;
     203         }
     204         @Override
     205         public int hashCode() {
     206             return System.identityHashCode(logger);
     207         }
     208         public Logger get() {
     209             return logger;
     210         }
     211         public static CloseOnReset create(Logger logger) {
     212             return new CloseOnReset(logger);
     213         }
     214     }
     215     private final CopyOnWriteArrayList<CloseOnReset> closeOnResetLoggers =
     216             new CopyOnWriteArrayList<>();
     217
     218
     219     private final Map<Object, Runnable> listeners =
     220             Collections.synchronizedMap(new IdentityHashMap<>());
     221
     222     static {
     223         manager = AccessController.doPrivileged(new PrivilegedAction<LogManager>() {
     224             @Override
     225             public LogManager run() {
     226                 LogManager mgr = null;
     227                 String cname = null;
     228                 try {
     229                     cname = System.getProperty("java.util.logging.manager");
     230                     if (cname != null) {
     231                         try {
     232                             @SuppressWarnings("deprecation")
     233                             Object tmp = ClassLoader.getSystemClassLoader()
     234                                 .loadClass(cname).newInstance();
     235                             mgr = (LogManager) tmp;
     236                         } catch (ClassNotFoundException ex) {
     237                             @SuppressWarnings("deprecation")
     238                             Object tmp = Thread.currentThread()
     239                                 .getContextClassLoader().loadClass(cname).newInstance();
     240                             mgr = (LogManager) tmp;
     241                         }
     242                     }
     243                 } catch (Exception ex) {
     244                     System.err.println("Could not load Logmanager \"" + cname + "\"");
     245                     ex.printStackTrace();
     246                 }
     247                 if (mgr == null) {
     248                     mgr = new LogManager();
     249                 }
     250                 return mgr;
     251
     252             }
     253         });
     254     }
     255
     256     // This private class is used as a shutdown hook.
     257     // It does a "reset" to close all open handlers.
     258     private class Cleaner extends Thread {
     259
     260         private Cleaner() {
     261             super(null, null, "Logging-Cleaner", 0, false);
     262             /* Set context class loader to null in order to avoid
     263              * keeping a strong reference to an application classloader.
     264              */
     265             this.setContextClassLoader(null);
     266         }
     267
     268         @Override
     269         public void run() {
     270             // This is to ensure the LogManager.<clinit> is completed
     271             // before synchronized block. Otherwise deadlocks are possible.
     272             LogManager mgr = manager;
     273
     274             // set globalHandlersState to STATE_SHUTDOWN atomically so that
     275             // no attempts are made to (re)initialize the handlers or (re)read
     276             // the configuration again. This is terminal state.
     277             configurationLock.lock();
     278             globalHandlersState = STATE_SHUTDOWN;
     279             configurationLock.unlock();
     280
     281             // Do a reset to close all active handlers.
     282             reset();
     283         }
     284     }
     285
     286
     287     /**
     288      * Protected constructor.  This is protected so that container applications
     289      * (such as J2EE containers) can subclass the object.  It is non-public as
     290      * it is intended that there only be one LogManager object, whose value is
     291      * retrieved by calling LogManager.getLogManager.
     292      */
     293     protected LogManager() {
     294         this(checkSubclassPermissions());
     295     }
     296
     297     private LogManager(Void checked) {
     298
     299         // Add a shutdown hook to close the global handlers.
     300         try {
     301             Runtime.getRuntime().addShutdownHook(new Cleaner());
     302         } catch (IllegalStateException e) {
     303             // If the VM is already shutting down,
     304             // We do not need to register shutdownHook.
     305         }
     306     }
     307
     308     private static Void checkSubclassPermissions() {
     309         final SecurityManager sm = System.getSecurityManager();
     310         if (sm != null) {
     311             // These permission will be checked in the LogManager constructor,
     312             // in order to register the Cleaner() thread as a shutdown hook.
     313             // Check them here to avoid the penalty of constructing the object
     314             // etc...
     315             sm.checkPermission(new RuntimePermission("shutdownHooks"));
     316             sm.checkPermission(new RuntimePermission("setContextClassLoader"));
     317         }
     318         return null;
     319     }
     320
     321     /**
     322      * Lazy initialization: if this instance of manager is the global
     323      * manager then this method will read the initial configuration and
     324      * add the root logger and global logger by calling addLogger().
     325      *
     326      * Note that it is subtly different from what we do in LoggerContext.
     327      * In LoggerContext we're patching up the logger context tree in order to add
     328      * the root and global logger *to the context tree*.
     329      *
     330      * For this to work, addLogger() must have already have been called
     331      * once on the LogManager instance for the default logger being
     332      * added.
     333      *
     334      * This is why ensureLogManagerInitialized() needs to be called before
     335      * any logger is added to any logger context.
     336      *
     337      */
     338     private boolean initializedCalled = false;
     339     private volatile boolean initializationDone = false;
     340     final void ensureLogManagerInitialized() {
     341         final LogManager owner = this;
     342         if (initializationDone || owner != manager) {
     343             // we don't want to do this twice, and we don't want to do
     344             // this on private manager instances.
     345             return;
     346         }
     347
     348         // Maybe another thread has called ensureLogManagerInitialized()
     349         // before us and is still executing it. If so we will block until
     350         // the log manager has finished initialized, then acquire the monitor,
     351         // notice that initializationDone is now true and return.
     352         // Otherwise - we have come here first! We will acquire the monitor,
     353         // see that initializationDone is still false, and perform the
     354         // initialization.
     355         //
     356         configurationLock.lock();
     357         try {
     358             // If initializedCalled is true it means that we're already in
     359             // the process of initializing the LogManager in this thread.
     360             // There has been a recursive call to ensureLogManagerInitialized().
     361             final boolean isRecursiveInitialization = (initializedCalled == true);
     362
     363             assert initializedCalled || !initializationDone
     364                     : "Initialization can't be done if initialized has not been called!";
     365
     366             if (isRecursiveInitialization || initializationDone) {
     367                 // If isRecursiveInitialization is true it means that we're
     368                 // already in the process of initializing the LogManager in
     369                 // this thread. There has been a recursive call to
     370                 // ensureLogManagerInitialized(). We should not proceed as
     371                 // it would lead to infinite recursion.
     372                 //
     373                 // If initializationDone is true then it means the manager
     374                 // has finished initializing; just return: we're done.
     375                 return;
     376             }
     377             // Calling addLogger below will in turn call requiresDefaultLogger()
     378             // which will call ensureLogManagerInitialized().
     379             // We use initializedCalled to break the recursion.
     380             initializedCalled = true;
     381             try {
     382                 AccessController.doPrivileged(new PrivilegedAction<Object>() {
     383                     @Override
     384                     public Object run() {
     385                         assert rootLogger == null;
     386                         assert initializedCalled && !initializationDone;
     387
     388                         // create root logger before reading primordial
     389                         // configuration - to ensure that it will be added
     390                         // before the global logger, and not after.
     391                         final Logger root = owner.rootLogger = owner.new RootLogger();
     392
     393                         // Read configuration.
     394                         owner.readPrimordialConfiguration();
     395
     396                         // Create and retain Logger for the root of the namespace.
     397                         owner.addLogger(root);
     398
     399                         // Initialize level if not yet initialized
     400                         if (!root.isLevelInitialized()) {
     401                             root.setLevel(defaultLevel);
     402                         }
     403
     404                         // Adding the global Logger.
     405                         // Do not call Logger.getGlobal() here as this might trigger
     406                         // subtle inter-dependency issues.
     407                         @SuppressWarnings("deprecation")
     408                         final Logger global = Logger.global;
     409
     410                         // Make sure the global logger will be registered in the
     411                         // global manager
     412                         owner.addLogger(global);
     413                         return null;
     414                     }
     415                 });
     416             } finally {
     417                 initializationDone = true;
     418             }
     419         } finally {
     420             configurationLock.unlock();
     421         }
     422     }
     423
     424     /**
     425      * Returns the global LogManager object.
     426      * @return the global LogManager object
     427      */
     428     public static LogManager getLogManager() {
     429         if (manager != null) {
     430             manager.ensureLogManagerInitialized();
     431         }
     432         return manager;
     433     }
     434
     435     private void readPrimordialConfiguration() { // must be called while holding configurationLock
     436         if (!readPrimordialConfiguration) {
     437             // If System.in/out/err are null, it's a good
     438             // indication that we're still in the
     439             // bootstrapping phase
     440             if (System.out == null) {
     441                 return;
     442             }
     443             readPrimordialConfiguration = true;
     444             try {
     445                 readConfiguration();
     446
     447                 // Platform loggers begin to delegate to java.util.logging.Logger
     448                 jdk.internal.logger.BootstrapLogger.redirectTemporaryLoggers();
     449
     450             } catch (Exception ex) {
     451                 assert false : "Exception raised while reading logging configuration: " + ex;
     452             }
     453         }
     454     }
     455
     456     // LoggerContext maps from AppContext
     457     private WeakHashMap<Object, LoggerContext> contextsMap = null;
     458
     459     // Returns the LoggerContext for the user code (i.e. application or AppContext).
     460     // Loggers are isolated from each AppContext.
     461     private LoggerContext getUserContext() {
     462         LoggerContext context = null;
     463
     464         SecurityManager sm = System.getSecurityManager();
     465         JavaAWTAccess javaAwtAccess = SharedSecrets.getJavaAWTAccess();
     466         if (sm != null && javaAwtAccess != null) {
     467             // for each applet, it has its own LoggerContext isolated from others
     468             final Object ecx = javaAwtAccess.getAppletContext();
     469             if (ecx != null) {
     470                 synchronized (javaAwtAccess) {
     471                     // find the AppContext of the applet code
     472                     // will be null if we are in the main app context.
     473                     if (contextsMap == null) {
     474                         contextsMap = new WeakHashMap<>();
     475                     }
     476                     context = contextsMap.get(ecx);
     477                     if (context == null) {
     478                         // Create a new LoggerContext for the applet.
     479                         context = new LoggerContext();
     480                         contextsMap.put(ecx, context);
     481                     }
     482                 }
     483             }
     484         }
     485         // for standalone app, return userContext
     486         return context != null ? context : userContext;
     487     }
     488
     489     // The system context.
     490     final LoggerContext getSystemContext() {
     491         return systemContext;
     492     }
     493
     494     private List<LoggerContext> contexts() {
     495         List<LoggerContext> cxs = new ArrayList<>();
     496         cxs.add(getSystemContext());
     497         cxs.add(getUserContext());
     498         return cxs;
     499     }
     500
     501     // Find or create a specified logger instance. If a logger has
     502     // already been created with the given name it is returned.
     503     // Otherwise a new logger instance is created and registered
     504     // in the LogManager global namespace.
     505     // This method will always return a non-null Logger object.
     506     // Synchronization is not required here. All synchronization for
     507     // adding a new Logger object is handled by addLogger().
     508     //
     509     // This method must delegate to the LogManager implementation to
     510     // add a new Logger or return the one that has been added previously
     511     // as a LogManager subclass may override the addLogger, getLogger,
     512     // readConfiguration, and other methods.
     513     Logger demandLogger(String name, String resourceBundleName, Class<?> caller) {
     514         final Module module = caller == null ? null : caller.getModule();
     515         return demandLogger(name, resourceBundleName, module);
     516     }
     517
     518     Logger demandLogger(String name, String resourceBundleName, Module module) {
     519         Logger result = getLogger(name);
     520         if (result == null) {
     521             // only allocate the new logger once
     522             Logger newLogger = new Logger(name, resourceBundleName,
     523                                           module, this, false);
     524             do {
     525                 if (addLogger(newLogger)) {
     526                     // We successfully added the new Logger that we
     527                     // created above so return it without refetching.
     528                     return newLogger;
     529                 }
     530
     531                 // We didn't add the new Logger that we created above
     532                 // because another thread added a Logger with the same
     533                 // name after our null check above and before our call
     534                 // to addLogger(). We have to refetch the Logger because
     535                 // addLogger() returns a boolean instead of the Logger
     536                 // reference itself. However, if the thread that created
     537                 // the other Logger is not holding a strong reference to
     538                 // the other Logger, then it is possible for the other
     539                 // Logger to be GC'ed after we saw it in addLogger() and
     540                 // before we can refetch it. If it has been GC'ed then
     541                 // we'll just loop around and try again.
     542                 result = getLogger(name);
     543             } while (result == null);
     544         }
     545         return result;
     546     }
     547
     548     Logger demandSystemLogger(String name, String resourceBundleName, Class<?> caller) {
     549         final Module module = caller == null ? null : caller.getModule();
     550         return demandSystemLogger(name, resourceBundleName, module);
     551     }
     552
     553     Logger demandSystemLogger(String name, String resourceBundleName, Module module) {
     554         // Add a system logger in the system context's namespace
     555         final Logger sysLogger = getSystemContext()
     556                 .demandLogger(name, resourceBundleName, module);
     557
     558         // Add the system logger to the LogManager's namespace if not exist
     559         // so that there is only one single logger of the given name.
     560         // System loggers are visible to applications unless a logger of
     561         // the same name has been added.
     562         Logger logger;
     563         do {
     564             // First attempt to call addLogger instead of getLogger
     565             // This would avoid potential bug in custom LogManager.getLogger
     566             // implementation that adds a logger if does not exist
     567             if (addLogger(sysLogger)) {
     568                 // successfully added the new system logger
     569                 logger = sysLogger;
     570             } else {
     571                 logger = getLogger(name);
     572             }
     573         } while (logger == null);
     574
     575         // LogManager will set the sysLogger's handlers via LogManager.addLogger method.
     576         if (logger != sysLogger) {
     577             // if logger already exists we merge the two logger configurations.
     578             final Logger l = logger;
     579             AccessController.doPrivileged(new PrivilegedAction<Void>() {
     580                 @Override
     581                 public Void run() {
     582                     l.mergeWithSystemLogger(sysLogger);
     583                     return null;
     584                 }
     585             });
     586         }
     587         return sysLogger;
     588     }
     589
     590     // LoggerContext maintains the logger namespace per context.
     591     // The default LogManager implementation has one system context and user
     592     // context.  The system context is used to maintain the namespace for
     593     // all system loggers and is queried by the system code.  If a system logger
     594     // doesn't exist in the user context, it'll also be added to the user context.
     595     // The user context is queried by the user code and all other loggers are
     596     // added in the user context.
     597     class LoggerContext {
     598         // Table of named Loggers that maps names to Loggers.
     599         private final ConcurrentHashMap<String,LoggerWeakRef> namedLoggers =
     600                 new ConcurrentHashMap<>();
     601         // Tree of named Loggers
     602         private final LogNode root;
     603         private LoggerContext() {
     604             this.root = new LogNode(null, this);
     605         }
     606
     607
     608         // Tells whether default loggers are required in this context.
     609         // If true, the default loggers will be lazily added.
     610         final boolean requiresDefaultLoggers() {
     611             final boolean requiresDefaultLoggers = (getOwner() == manager);
     612             if (requiresDefaultLoggers) {
     613                 getOwner().ensureLogManagerInitialized();
     614             }
     615             return requiresDefaultLoggers;
     616         }
     617
     618         // This context's LogManager.
     619         final LogManager getOwner() {
     620             return LogManager.this;
     621         }
     622
     623         // This context owner's root logger, which if not null, and if
     624         // the context requires default loggers, will be added to the context
     625         // logger's tree.
     626         final Logger getRootLogger() {
     627             return getOwner().rootLogger;
     628         }
     629
     630         // The global logger, which if not null, and if
     631         // the context requires default loggers, will be added to the context
     632         // logger's tree.
     633         final Logger getGlobalLogger() {
     634             @SuppressWarnings("deprecation") // avoids initialization cycles.
     635             final Logger global = Logger.global;
     636             return global;
     637         }
     638
     639         Logger demandLogger(String name, String resourceBundleName, Module module) {
     640             // a LogManager subclass may have its own implementation to add and
     641             // get a Logger.  So delegate to the LogManager to do the work.
     642             final LogManager owner = getOwner();
     643             return owner.demandLogger(name, resourceBundleName, module);
     644         }
     645
     646
     647         // Due to subtle deadlock issues getUserContext() no longer
     648         // calls addLocalLogger(rootLogger);
     649         // Therefore - we need to add the default loggers later on.
     650         // Checks that the context is properly initialized
     651         // This is necessary before calling e.g. find(name)
     652         // or getLoggerNames()
     653         //
     654         private void ensureInitialized() {
     655             if (requiresDefaultLoggers()) {
     656                 // Ensure that the root and global loggers are set.
     657                 ensureDefaultLogger(getRootLogger());
     658                 ensureDefaultLogger(getGlobalLogger());
     659             }
     660         }
     661
     662
     663         Logger findLogger(String name) {
     664             // Attempt to find logger without locking.
     665             LoggerWeakRef ref = namedLoggers.get(name);
     666             Logger logger = ref == null ? null : ref.get();
     667
     668             // if logger is not null, then we can return it right away.
     669             // if name is "" or "global" and logger is null
     670             // we need to fall through and check that this context is
     671             // initialized.
     672             // if ref is not null and logger is null we also need to
     673             // fall through.
     674             if (logger != null || (ref == null && !name.isEmpty()
     675                     && !name.equals(Logger.GLOBAL_LOGGER_NAME))) {
     676                 return logger;
     677             }
     678
     679             // We either found a stale reference, or we were looking for
     680             // "" or "global" and didn't find them.
     681             // Make sure context is initialized (has the default loggers),
     682             // and look up again, cleaning the stale reference if it hasn't
     683             // been cleaned up in between. All this needs to be done inside
     684             // a synchronized block.
     685             synchronized(this) {
     686                 // ensure that this context is properly initialized before
     687                 // looking for loggers.
     688                 ensureInitialized();
     689                 ref = namedLoggers.get(name);
     690                 if (ref == null) {
     691                     return null;
     692                 }
     693                 logger = ref.get();
     694                 if (logger == null) {
     695                     // The namedLoggers map holds stale weak reference
     696                     // to a logger which has been GC-ed.
     697                     ref.dispose();
     698                 }
     699                 return logger;
     700             }
     701         }
     702
     703         // This method is called before adding a logger to the
     704         // context.
     705         // 'logger' is the context that will be added.
     706         // This method will ensure that the defaults loggers are added
     707         // before adding 'logger'.
     708         //
     709         private void ensureAllDefaultLoggers(Logger logger) {
     710             if (requiresDefaultLoggers()) {
     711                 final String name = logger.getName();
     712                 if (!name.isEmpty()) {
     713                     ensureDefaultLogger(getRootLogger());
     714                     if (!Logger.GLOBAL_LOGGER_NAME.equals(name)) {
     715                         ensureDefaultLogger(getGlobalLogger());
     716                     }
     717                 }
     718             }
     719         }
     720
     721         private void ensureDefaultLogger(Logger logger) {
     722             // Used for lazy addition of root logger and global logger
     723             // to a LoggerContext.
     724
     725             // This check is simple sanity: we do not want that this
     726             // method be called for anything else than Logger.global
     727             // or owner.rootLogger.
     728             if (!requiresDefaultLoggers() || logger == null
     729                     || logger != getGlobalLogger() && logger != LogManager.this.rootLogger ) {
     730
     731                 // the case where we have a non null logger which is neither
     732                 // Logger.global nor manager.rootLogger indicates a serious
     733                 // issue - as ensureDefaultLogger should never be called
     734                 // with any other loggers than one of these two (or null - if
     735                 // e.g manager.rootLogger is not yet initialized)...
     736                 assert logger == null;
     737
     738                 return;
     739             }
     740
     741             // Adds the logger if it's not already there.
     742             if (!namedLoggers.containsKey(logger.getName())) {
     743                 // It is important to prevent addLocalLogger to
     744                 // call ensureAllDefaultLoggers when we're in the process
     745                 // off adding one of those default loggers - as this would
     746                 // immediately cause a stack overflow.
     747                 // Therefore we must pass addDefaultLoggersIfNeeded=false,
     748                 // even if requiresDefaultLoggers is true.
     749                 addLocalLogger(logger, false);
     750             }
     751         }
     752
     753         boolean addLocalLogger(Logger logger) {
     754             // no need to add default loggers if it's not required
     755             return addLocalLogger(logger, requiresDefaultLoggers());
     756         }
     757
     758         // Add a logger to this context.  This method will only set its level
     759         // and process parent loggers.  It doesn't set its handlers.
     760         synchronized boolean addLocalLogger(Logger logger, boolean addDefaultLoggersIfNeeded) {
     761             // addDefaultLoggersIfNeeded serves to break recursion when adding
     762             // default loggers. If we're adding one of the default loggers
     763             // (we're being called from ensureDefaultLogger()) then
     764             // addDefaultLoggersIfNeeded will be false: we don't want to
     765             // call ensureAllDefaultLoggers again.
     766             //
     767             // Note: addDefaultLoggersIfNeeded can also be false when
     768             //       requiresDefaultLoggers is false - since calling
     769             //       ensureAllDefaultLoggers would have no effect in this case.
     770             if (addDefaultLoggersIfNeeded) {
     771                 ensureAllDefaultLoggers(logger);
     772             }
     773
     774             final String name = logger.getName();
     775             if (name == null) {
     776                 throw new NullPointerException();
     777             }
     778             LoggerWeakRef ref = namedLoggers.get(name);
     779             if (ref != null) {
     780                 if (ref.refersTo(null)) {
     781                     // It's possible that the Logger was GC'ed after a
     782                     // drainLoggerRefQueueBounded() call above so allow
     783                     // a new one to be registered.
     784                     ref.dispose();
     785                 } else {
     786                     // We already have a registered logger with the given name.
     787                     return false;
     788                 }
     789             }
     790
     791             // We're adding a new logger.
     792             // Note that we are creating a weak reference here.
     793             final LogManager owner = getOwner();
     794             logger.setLogManager(owner);
     795             ref = owner.new LoggerWeakRef(logger);
     796
     797             // Apply any initial level defined for the new logger, unless
     798             // the logger's level is already initialized
     799             Level level = owner.getLevelProperty(name + ".level", null);
     800             if (level != null && !logger.isLevelInitialized()) {
     801                 doSetLevel(logger, level);
     802             }
     803
     804             // instantiation of the handler is done in the LogManager.addLogger
     805             // implementation as a handler class may be only visible to LogManager
     806             // subclass for the custom log manager case
     807             processParentHandlers(logger, name, VisitedLoggers.NEVER);
     808
     809             // Find the new node and its parent.
     810             LogNode node = getNode(name);
     811             node.loggerRef = ref;
     812             Logger parent = null;
     813             LogNode nodep = node.parent;
     814             while (nodep != null) {
     815                 LoggerWeakRef nodeRef = nodep.loggerRef;
     816                 if (nodeRef != null) {
     817                     parent = nodeRef.get();
     818                     if (parent != null) {
     819                         break;
     820                     }
     821                 }
     822                 nodep = nodep.parent;
     823             }
     824
     825             if (parent != null) {
     826                 doSetParent(logger, parent);
     827             }
     828             // Walk over the children and tell them we are their new parent.
     829             node.walkAndSetParent(logger);
     830             // new LogNode is ready so tell the LoggerWeakRef about it
     831             ref.setNode(node);
     832
     833             // Do not publish 'ref' in namedLoggers before the logger tree
     834             // is fully updated - because the named logger will be visible as
     835             // soon as it is published in namedLoggers (findLogger takes
     836             // benefit of the ConcurrentHashMap implementation of namedLoggers
     837             // to avoid synchronizing on retrieval when that is possible).
     838             namedLoggers.put(name, ref);
     839             return true;
     840         }
     841
     842         void removeLoggerRef(String name, LoggerWeakRef ref) {
     843             namedLoggers.remove(name, ref);
     844         }
     845
     846         synchronized Enumeration<String> getLoggerNames() {
     847             // ensure that this context is properly initialized before
     848             // returning logger names.
     849             ensureInitialized();
     850             return Collections.enumeration(namedLoggers.keySet());
     851         }
     852
     853         // If logger.getUseParentHandlers() returns 'true' and any of the logger's
     854         // parents have levels or handlers defined, make sure they are instantiated.
     855         private void processParentHandlers(final Logger logger, final String name,
     856                Predicate<Logger> visited) {
     857             final LogManager owner = getOwner();
     858             AccessController.doPrivileged(new PrivilegedAction<Void>() {
     859                 @Override
     860                 public Void run() {
     861                     if (logger != owner.rootLogger) {
     862                         boolean useParent = owner.getBooleanProperty(name + ".useParentHandlers", true);
     863                         if (!useParent) {
     864                             logger.setUseParentHandlers(false);
     865                         }
     866                     }
     867                     return null;
     868                 }
     869             });
     870
     871             int ix = 1;
     872             for (;;) {
     873                 int ix2 = name.indexOf('.', ix);
     874                 if (ix2 < 0) {
     875                     break;
     876                 }
     877                 String pname = name.substring(0, ix2);
     878                 if (owner.getProperty(pname + ".level") != null ||
     879                     owner.getProperty(pname + ".handlers") != null) {
     880                     // This pname has a level/handlers definition.
     881                     // Make sure it exists.
     882                     if (visited.test(demandLogger(pname, null, null))) {
     883                         break;
     884                     }
     885                 }
     886                 ix = ix2+1;
     887             }
     888         }
     889
     890         // Gets a node in our tree of logger nodes.
     891         // If necessary, create it.
     892         LogNode getNode(String name) {
     893             if (name == null || name.isEmpty()) {
     894                 return root;
     895             }
     896             LogNode node = root;
     897             while (name.length() > 0) {
     898                 int ix = name.indexOf('.');
     899                 String head;
     900                 if (ix > 0) {
     901                     head = name.substring(0, ix);
     902                     name = name.substring(ix + 1);
     903                 } else {
     904                     head = name;
     905                     name = "";
     906                 }
     907                 if (node.children == null) {
     908                     node.children = new HashMap<>();
     909                 }
     910                 LogNode child = node.children.get(head);
     911                 if (child == null) {
     912                     child = new LogNode(node, this);
     913                     node.children.put(head, child);
     914                 }
     915                 node = child;
     916             }
     917             return node;
     918         }
     919     }
     920
     921     final class SystemLoggerContext extends LoggerContext {
     922         // Add a system logger in the system context's namespace as well as
     923         // in the LogManager's namespace if not exist so that there is only
     924         // one single logger of the given name.  System loggers are visible
     925         // to applications unless a logger of the same name has been added.
     926         @Override
     927         Logger demandLogger(String name, String resourceBundleName,
     928                             Module module) {
     929             Logger result = findLogger(name);
     930             if (result == null) {
     931                 // only allocate the new system logger once
     932                 Logger newLogger = new Logger(name, resourceBundleName,
     933                                               module, getOwner(), true);
     934                 do {
     935                     if (addLocalLogger(newLogger)) {
     936                         // We successfully added the new Logger that we
     937                         // created above so return it without refetching.
     938                         result = newLogger;
     939                     } else {
     940                         // We didn't add the new Logger that we created above
     941                         // because another thread added a Logger with the same
     942                         // name after our null check above and before our call
     943                         // to addLogger(). We have to refetch the Logger because
     944                         // addLogger() returns a boolean instead of the Logger
     945                         // reference itself. However, if the thread that created
     946                         // the other Logger is not holding a strong reference to
     947                         // the other Logger, then it is possible for the other
     948                         // Logger to be GC'ed after we saw it in addLogger() and
     949                         // before we can refetch it. If it has been GC'ed then
     950                         // we'll just loop around and try again.
     951                         result = findLogger(name);
     952                     }
     953                 } while (result == null);
     954             }
     955             return result;
     956         }
     957     }
     958
     959     // Add new per logger handlers.
     960     // We need to raise privilege here. All our decisions will
     961     // be made based on the logging configuration, which can
     962     // only be modified by trusted code.
     963     private void loadLoggerHandlers(final Logger logger, final String name,
     964                                     final String handlersPropertyName)
     965     {
     966         AccessController.doPrivileged(new PrivilegedAction<Void>() {
     967             @Override
     968             public Void run() {
     969                 setLoggerHandlers(logger, name, handlersPropertyName,
     970                     createLoggerHandlers(name, handlersPropertyName));
     971                 return null;
     972             }
     973         });
     974     }
     975
     976     private void setLoggerHandlers(final Logger logger, final String name,
     977                                    final String handlersPropertyName,
     978                                    List<Handler> handlers)
     979     {
     980         final boolean ensureCloseOnReset = ! handlers.isEmpty()
     981                     && getBooleanProperty(handlersPropertyName + ".ensureCloseOnReset",true);
     982         int count = 0;
     983         for (Handler hdl : handlers) {
     984             logger.addHandler(hdl);
     985             if (++count == 1 && ensureCloseOnReset) {
     986                 // add this logger to the closeOnResetLoggers list.
     987                 closeOnResetLoggers.addIfAbsent(CloseOnReset.create(logger));
     988             }
     989         }
     990     }
     991
     992     private List<Handler> createLoggerHandlers(final String name,
     993                                                final String handlersPropertyName)
     994     {
     995         String names[] = parseClassNames(handlersPropertyName);
     996         List<Handler> handlers = new ArrayList<>(names.length);
     997         for (String type : names) {
     998             try {
     999                 @SuppressWarnings("deprecation")
    1000                 Object o = ClassLoader.getSystemClassLoader().loadClass(type).newInstance();
    1001                 Handler hdl = (Handler) o;
    1002                 // Check if there is a property defining the
    1003                 // this handler's level.
    1004                 String levs = getProperty(type + ".level");
    1005                 if (levs != null) {
    1006                     Level l = Level.findLevel(levs);
    1007                     if (l != null) {
    1008                         hdl.setLevel(l);
    1009                     } else {
    1010                         // Probably a bad level. Drop through.
    1011                         System.err.println("Can't set level for " + type);
    1012                     }
    1013                 }
    1014                 // Add this Handler to the logger
    1015                 handlers.add(hdl);
    1016             } catch (Exception ex) {
    1017                 System.err.println("Can't load log handler \"" + type + "\"");
    1018                 System.err.println("" + ex);
    1019                 ex.printStackTrace();
    1020             }
    1021         }
    1022
    1023         return handlers;
    1024     }
    1025
    1026
    1027     // loggerRefQueue holds LoggerWeakRef objects for Logger objects
    1028     // that have been GC'ed.
    1029     private final ReferenceQueue<Logger> loggerRefQueue
    1030         = new ReferenceQueue<>();
    1031
    1032     // Package-level inner class.
    1033     // Helper class for managing WeakReferences to Logger objects.
    1034     //
    1035     // LogManager.namedLoggers
    1036     //     - has weak references to all named Loggers
    1037     //     - namedLoggers keeps the LoggerWeakRef objects for the named
    1038     //       Loggers around until we can deal with the book keeping for
    1039     //       the named Logger that is being GC'ed.
    1040     // LogManager.LogNode.loggerRef
    1041     //     - has a weak reference to a named Logger
    1042     //     - the LogNode will also keep the LoggerWeakRef objects for
    1043     //       the named Loggers around; currently LogNodes never go away.
    1044     // Logger.kids
    1045     //     - has a weak reference to each direct child Logger; this
    1046     //       includes anonymous and named Loggers
    1047     //     - anonymous Loggers are always children of the rootLogger
    1048     //       which is a strong reference; rootLogger.kids keeps the
    1049     //       LoggerWeakRef objects for the anonymous Loggers around
    1050     //       until we can deal with the book keeping.
    1051     //
    1052     final class LoggerWeakRef extends WeakReference<Logger> {
    1053         private String                name;       // for namedLoggers cleanup
    1054         private LogNode               node;       // for loggerRef cleanup
    1055         private WeakReference<Logger> parentRef;  // for kids cleanup
    1056         private boolean disposed = false;         // avoid calling dispose twice
    1057
    1058         LoggerWeakRef(Logger logger) {
    1059             super(logger, loggerRefQueue);
    1060
    1061             name = logger.getName();  // save for namedLoggers cleanup
    1062         }
    1063
    1064         // dispose of this LoggerWeakRef object
    1065         void dispose() {
    1066             // Avoid calling dispose twice. When a Logger is gc'ed, its
    1067             // LoggerWeakRef will be enqueued.
    1068             // However, a new logger of the same name may be added (or looked
    1069             // up) before the queue is drained. When that happens, dispose()
    1070             // will be called by addLocalLogger() or findLogger().
    1071             // Later when the queue is drained, dispose() will be called again
    1072             // for the same LoggerWeakRef. Marking LoggerWeakRef as disposed
    1073             // avoids processing the data twice (even though the code should
    1074             // now be reentrant).
    1075             synchronized(this) {
    1076                 // Note to maintainers:
    1077                 // Be careful not to call any method that tries to acquire
    1078                 // another lock from within this block - as this would surely
    1079                 // lead to deadlocks, given that dispose() can be called by
    1080                 // multiple threads, and from within different synchronized
    1081                 // methods/blocks.
    1082                 if (disposed) return;
    1083                 disposed = true;
    1084             }
    1085
    1086             final LogNode n = node;
    1087             if (n != null) {
    1088                 // n.loggerRef can only be safely modified from within
    1089                 // a lock on LoggerContext. removeLoggerRef is already
    1090                 // synchronized on LoggerContext so calling
    1091                 // n.context.removeLoggerRef from within this lock is safe.
    1092                 synchronized (n.context) {
    1093                     // if we have a LogNode, then we were a named Logger
    1094                     // so clear namedLoggers weak ref to us
    1095                     n.context.removeLoggerRef(name, this);
    1096                     name = null;  // clear our ref to the Logger's name
    1097
    1098                     // LogNode may have been reused - so only clear
    1099                     // LogNode.loggerRef if LogNode.loggerRef == this
    1100                     if (n.loggerRef == this) {
    1101                         n.loggerRef = null;  // clear LogNode's weak ref to us
    1102                     }
    1103                     node = null;            // clear our ref to LogNode
    1104                 }
    1105             }
    1106
    1107             if (parentRef != null) {
    1108                 // this LoggerWeakRef has or had a parent Logger
    1109                 Logger parent = parentRef.get();
    1110                 if (parent != null) {
    1111                     // the parent Logger is still there so clear the
    1112                     // parent Logger's weak ref to us
    1113                     parent.removeChildLogger(this);
    1114                 }
    1115                 parentRef = null;  // clear our weak ref to the parent Logger
    1116             }
    1117         }
    1118
    1119         // set the node field to the specified value
    1120         void setNode(LogNode node) {
    1121             this.node = node;
    1122         }
    1123
    1124         // set the parentRef field to the specified value
    1125         void setParentRef(WeakReference<Logger> parentRef) {
    1126             this.parentRef = parentRef;
    1127         }
    1128     }
    1129
    1130     // Package-level method.
    1131     // Drain some Logger objects that have been GC'ed.
    1132     //
    1133     // drainLoggerRefQueueBounded() is called by addLogger() below
    1134     // and by Logger.getAnonymousLogger(String) so we'll drain up to
    1135     // MAX_ITERATIONS GC'ed Loggers for every Logger we add.
    1136     //
    1137     // On a WinXP VMware client, a MAX_ITERATIONS value of 400 gives
    1138     // us about a 50/50 mix in increased weak ref counts versus
    1139     // decreased weak ref counts in the AnonLoggerWeakRefLeak test.
    1140     // Here are stats for cleaning up sets of 400 anonymous Loggers:
    1141     //   - test duration 1 minute
    1142     //   - sample size of 125 sets of 400
    1143     //   - average: 1.99 ms
    1144     //   - minimum: 0.57 ms
    1145     //   - maximum: 25.3 ms
    1146     //
    1147     // The same config gives us a better decreased weak ref count
    1148     // than increased weak ref count in the LoggerWeakRefLeak test.
    1149     // Here are stats for cleaning up sets of 400 named Loggers:
    1150     //   - test duration 2 minutes
    1151     //   - sample size of 506 sets of 400
    1152     //   - average: 0.57 ms
    1153     //   - minimum: 0.02 ms
    1154     //   - maximum: 10.9 ms
    1155     //
    1156     private static final int MAX_ITERATIONS = 400;
    1157     final void drainLoggerRefQueueBounded() {
    1158         for (int i = 0; i < MAX_ITERATIONS; i++) {
    1159             if (loggerRefQueue == null) {
    1160                 // haven't finished loading LogManager yet
    1161                 break;
    1162             }
    1163
    1164             LoggerWeakRef ref = (LoggerWeakRef) loggerRefQueue.poll();
    1165             if (ref == null) {
    1166                 break;
    1167             }
    1168             // a Logger object has been GC'ed so clean it up
    1169             ref.dispose();
    1170         }
    1171     }
    1172
    1173     /**
    1174      * Add a named logger.  This does nothing and returns false if a logger
    1175      * with the same name is already registered.
    1176      * <p>
    1177      * The Logger factory methods call this method to register each
    1178      * newly created Logger.
    1179      * <p>
    1180      * The application should retain its own reference to the Logger
    1181      * object to avoid it being garbage collected.  The LogManager
    1182      * may only retain a weak reference.
    1183      *
    1184      * @param   logger the new logger.
    1185      * @return  true if the argument logger was registered successfully,
    1186      *          false if a logger of that name already exists.
    1187      * @throws NullPointerException if the logger name is null.
    1188      */
    1189     public boolean addLogger(Logger logger) {
    1190         final String name = logger.getName();
    1191         if (name == null) {
    1192             throw new NullPointerException();
    1193         }
    1194         drainLoggerRefQueueBounded();
    1195         LoggerContext cx = getUserContext();
    1196         if (cx.addLocalLogger(logger) || forceLoadHandlers(logger)) {
    1197             // Do we have a per logger handler too?
    1198             // Note: this will add a 200ms penalty
    1199             loadLoggerHandlers(logger, name, name + ".handlers");
    1200             return true;
    1201         } else {
    1202             return false;
    1203         }
    1204     }
    1205
    1206
    1207     // Checks whether the given logger is a special logger
    1208     // that still requires handler initialization.
    1209     // This method will only return true for the root and
    1210     // global loggers and only if called by the thread that
    1211     // performs initialization of the LogManager, during that
    1212     // initialization. Must only be called by addLogger.
    1213     @SuppressWarnings("deprecation")
    1214     private boolean forceLoadHandlers(Logger logger) {
    1215         // Called just after reading the primordial configuration, in
    1216         // the same thread that reads it.
    1217         // The root and global logger would already be present in the context
    1218         // by this point, but we would not have called loadLoggerHandlers
    1219         // yet.
    1220         return (logger == rootLogger ||  logger == Logger.global)
    1221                 && !initializationDone
    1222                 && initializedCalled
    1223                 && configurationLock.isHeldByCurrentThread();
    1224     }
    1225
    1226     // Private method to set a level on a logger.
    1227     // If necessary, we raise privilege before doing the call.
    1228     private static void doSetLevel(final Logger logger, final Level level) {
    1229         SecurityManager sm = System.getSecurityManager();
    1230         if (sm == null) {
    1231             // There is no security manager, so things are easy.
    1232             logger.setLevel(level);
    1233             return;
    1234         }
    1235         // There is a security manager.  Raise privilege before
    1236         // calling setLevel.
    1237         AccessController.doPrivileged(new PrivilegedAction<Object>() {
    1238             @Override
    1239             public Object run() {
    1240                 logger.setLevel(level);
    1241                 return null;
    1242             }});
    1243     }
    1244
    1245     // Private method to set a parent on a logger.
    1246     // If necessary, we raise privilege before doing the setParent call.
    1247     private static void doSetParent(final Logger logger, final Logger parent) {
    1248         SecurityManager sm = System.getSecurityManager();
    1249         if (sm == null) {
    1250             // There is no security manager, so things are easy.
    1251             logger.setParent(parent);
    1252             return;
    1253         }
    1254         // There is a security manager.  Raise privilege before
    1255         // calling setParent.
    1256         AccessController.doPrivileged(new PrivilegedAction<Object>() {
    1257             @Override
    1258             public Object run() {
    1259                 logger.setParent(parent);
    1260                 return null;
    1261             }});
    1262     }
    1263
    1264     /**
    1265      * Method to find a named logger.
    1266      * <p>
    1267      * Note that since untrusted code may create loggers with
    1268      * arbitrary names this method should not be relied on to
    1269      * find Loggers for security sensitive logging.
    1270      * It is also important to note that the Logger associated with the
    1271      * String {@code name} may be garbage collected at any time if there
    1272      * is no strong reference to the Logger. The caller of this method
    1273      * must check the return value for null in order to properly handle
    1274      * the case where the Logger has been garbage collected.
    1275      *
    1276      * @param name name of the logger
    1277      * @return  matching logger or null if none is found
    1278      */
    1279     public Logger getLogger(String name) {
    1280         return getUserContext().findLogger(name);
    1281     }
    1282
    1283     /**
    1284      * Get an enumeration of known logger names.
    1285      * <p>
    1286      * Note:  Loggers may be added dynamically as new classes are loaded.
    1287      * This method only reports on the loggers that are currently registered.
    1288      * It is also important to note that this method only returns the name
    1289      * of a Logger, not a strong reference to the Logger itself.
    1290      * The returned String does nothing to prevent the Logger from being
    1291      * garbage collected. In particular, if the returned name is passed
    1292      * to {@code LogManager.getLogger()}, then the caller must check the
    1293      * return value from {@code LogManager.getLogger()} for null to properly
    1294      * handle the case where the Logger has been garbage collected in the
    1295      * time since its name was returned by this method.
    1296      *
    1297      * @return  enumeration of logger name strings
    1298      */
    1299     public Enumeration<String> getLoggerNames() {
    1300         return getUserContext().getLoggerNames();
    1301     }
    1302
    1303     /**
    1304      * Reads and initializes the logging configuration.
    1305      * <p>
    1306      * If the "java.util.logging.config.class" system property is set, then the
    1307      * property value is treated as a class name.  The given class will be
    1308      * loaded, an object will be instantiated, and that object's constructor
    1309      * is responsible for reading in the initial configuration.  (That object
    1310      * may use other system properties to control its configuration.)  The
    1311      * alternate configuration class can use {@code readConfiguration(InputStream)}
    1312      * to define properties in the LogManager.
    1313      * <p>
    1314      * If "java.util.logging.config.class" system property is <b>not</b> set,
    1315      * then this method will read the initial configuration from a properties
    1316      * file and calls the {@link #readConfiguration(InputStream)} method to initialize
    1317      * the configuration. The "java.util.logging.config.file" system property can be used
    1318      * to specify the properties file that will be read as the initial configuration;
    1319      * if not set, then the LogManager default configuration is used.
    1320      * The default configuration is typically loaded from the
    1321      * properties file "{@code conf/logging.properties}" in the Java installation
    1322      * directory.
    1323      *
    1324      * <p>
    1325      * Any {@linkplain #addConfigurationListener registered configuration
    1326      * listener} will be invoked after the properties are read.
    1327      *
    1328      * @apiNote This {@code readConfiguration} method should only be used for
    1329      * initializing the configuration during LogManager initialization or
    1330      * used with the "java.util.logging.config.class" property.
    1331      * When this method is called after loggers have been created, and
    1332      * the "java.util.logging.config.class" system property is not set, all
    1333      * existing loggers will be {@linkplain #reset() reset}. Then any
    1334      * existing loggers that have a level property specified in the new
    1335      * configuration stream will be {@linkplain
    1336      * Logger#setLevel(java.util.logging.Level) set} to the specified log level.
    1337      * <p>
    1338      * To properly update the logging configuration, use the
    1339      * {@link #updateConfiguration(java.util.function.Function)} or
    1340      * {@link #updateConfiguration(java.io.InputStream, java.util.function.Function)}
    1341      * methods instead.
    1342      *
    1343      * @throws   SecurityException  if a security manager exists and if
    1344      *              the caller does not have LoggingPermission("control").
    1345      * @throws   IOException if there are IO problems reading the configuration.
    1346      */
    1347     public void readConfiguration() throws IOException, SecurityException {
    1348         checkPermission();
    1349
    1350         // if a configuration class is specified, load it and use it.
    1351         String cname = System.getProperty("java.util.logging.config.class");
    1352         if (cname != null) {
    1353             try {
    1354                 // Instantiate the named class.  It is its constructor's
    1355                 // responsibility to initialize the logging configuration, by
    1356                 // calling readConfiguration(InputStream) with a suitable stream.
    1357                 try {
    1358                     Class<?> clz = ClassLoader.getSystemClassLoader().loadClass(cname);
    1359                     @SuppressWarnings("deprecation")
    1360                     Object witness = clz.newInstance();
    1361                     return;
    1362                 } catch (ClassNotFoundException ex) {
    1363                     Class<?> clz = Thread.currentThread().getContextClassLoader().loadClass(cname);
    1364                     @SuppressWarnings("deprecation")
    1365                     Object witness = clz.newInstance();
    1366                     return;
    1367                 }
    1368             } catch (Exception ex) {
    1369                 System.err.println("Logging configuration class \"" + cname + "\" failed");
    1370                 System.err.println("" + ex);
    1371                 // keep going and useful config file.
    1372             }
    1373         }
    1374
    1375         String fname = getConfigurationFileName();
    1376         try (final InputStream in = new FileInputStream(fname)) {
    1377             final BufferedInputStream bin = new BufferedInputStream(in);
    1378             readConfiguration(bin);
    1379         }
    1380     }
    1381
    1382     String getConfigurationFileName() throws IOException {
    1383         String fname = System.getProperty("java.util.logging.config.file");
    1384         if (fname == null) {
    1385             fname = System.getProperty("java.home");
    1386             if (fname == null) {
    1387                 throw new Error("Can't find java.home ??");
    1388             }
    1389             fname = Paths.get(fname, "conf", "logging.properties")
    1390                     .toAbsolutePath().normalize().toString();
    1391         }
    1392         return fname;
    1393     }
    1394
    1395     /**
    1396      * Reset the logging configuration.
    1397      * <p>
    1398      * For all named loggers, the reset operation removes and closes
    1399      * all Handlers and (except for the root logger) sets the level
    1400      * to {@code null}. The root logger's level is set to {@code Level.INFO}.
    1401      *
    1402      * @apiNote Calling this method also clears the LogManager {@linkplain
    1403      * #getProperty(java.lang.String) properties}. The {@link
    1404      * #updateConfiguration(java.util.function.Function)
    1405      * updateConfiguration(Function)} or
    1406      * {@link #updateConfiguration(java.io.InputStream, java.util.function.Function)
    1407      * updateConfiguration(InputStream, Function)} method can be used to
    1408      * properly update to a new configuration.
    1409      *
    1410      * @throws  SecurityException  if a security manager exists and if
    1411      *             the caller does not have LoggingPermission("control").
    1412      */
    1413
    1414     public void reset() throws SecurityException {
    1415         checkPermission();
    1416
    1417         List<CloseOnReset> persistent;
    1418
    1419         // We don't want reset() and readConfiguration()
    1420         // to run in parallel
    1421         configurationLock.lock();
    1422         try {
    1423             // install new empty properties
    1424             props = new Properties();
    1425             // make sure we keep the loggers persistent until reset is done.
    1426             // Those are the loggers for which we previously created a
    1427             // handler from the configuration, and we need to prevent them
    1428             // from being gc'ed until those handlers are closed.
    1429             persistent = new ArrayList<>(closeOnResetLoggers);
    1430             closeOnResetLoggers.clear();
    1431
    1432             // if reset has been called from shutdown-hook (Cleaner),
    1433             // or if reset has been called from readConfiguration() which
    1434             // already holds the lock and will change the state itself,
    1435             // then do not change state here...
    1436             if (globalHandlersState != STATE_SHUTDOWN &&
    1437                 globalHandlersState != STATE_READING_CONFIG) {
    1438                 // ...else user called reset()...
    1439                 // Since we are doing a reset we no longer want to initialize
    1440                 // the global handlers, if they haven't been initialized yet.
    1441                 globalHandlersState = STATE_INITIALIZED;
    1442             }
    1443
    1444             for (LoggerContext cx : contexts()) {
    1445                 resetLoggerContext(cx);
    1446             }
    1447
    1448             persistent.clear();
    1449         } finally {
    1450             configurationLock.unlock();
    1451         }
    1452     }
    1453
    1454     private void resetLoggerContext(LoggerContext cx) {
    1455         Enumeration<String> enum_ = cx.getLoggerNames();
    1456         while (enum_.hasMoreElements()) {
    1457             String name = enum_.nextElement();
    1458             Logger logger = cx.findLogger(name);
    1459             if (logger != null) {
    1460                 resetLogger(logger);
    1461             }
    1462         }
    1463     }
    1464
    1465     private void closeHandlers(Logger logger) {
    1466         Handler[] targets = logger.getHandlers();
    1467         for (Handler h : targets) {
    1468             logger.removeHandler(h);
    1469             try {
    1470                 h.close();
    1471             } catch (Exception ex) {
    1472                 // Problems closing a handler?  Keep going...
    1473             } catch (Error e) {
    1474                 // ignore Errors while shutting down
    1475                 if (globalHandlersState != STATE_SHUTDOWN) {
    1476                     throw e;
    1477                 }
    1478             }
    1479         }
    1480     }
    1481
    1482     // Private method to reset an individual target logger.
    1483     private void resetLogger(Logger logger) {
    1484         // Close all the Logger handlers.
    1485         closeHandlers(logger);
    1486
    1487         // Reset Logger level
    1488         String name = logger.getName();
    1489         if (name != null && name.isEmpty()) {
    1490             // This is the root logger.
    1491             logger.setLevel(defaultLevel);
    1492         } else {
    1493             logger.setLevel(null);
    1494         }
    1495     }
    1496
    1497     // get a list of whitespace separated classnames from a property.
    1498     private String[] parseClassNames(String propertyName) {
    1499         String hands = getProperty(propertyName);
    1500         if (hands == null) {
    1501             return new String[0];
    1502         }
    1503         hands = hands.trim();
    1504         int ix = 0;
    1505         final List<String> result = new ArrayList<>();
    1506         while (ix < hands.length()) {
    1507             int end = ix;
    1508             while (end < hands.length()) {
    1509                 if (Character.isWhitespace(hands.charAt(end))) {
    1510                     break;
    1511                 }
    1512                 if (hands.charAt(end) == ',') {
    1513                     break;
    1514                 }
    1515                 end++;
    1516             }
    1517             String word = hands.substring(ix, end);
    1518             ix = end+1;
    1519             word = word.trim();
    1520             if (word.length() == 0) {
    1521                 continue;
    1522             }
    1523             result.add(word);
    1524         }
    1525         return result.toArray(new String[result.size()]);
    1526     }
    1527
    1528     /**
    1529      * Reads and initializes the logging configuration from the given input stream.
    1530      *
    1531      * <p>
    1532      * Any {@linkplain #addConfigurationListener registered configuration
    1533      * listener} will be invoked after the properties are read.
    1534      *
    1535      * @apiNote This {@code readConfiguration} method should only be used for
    1536      * initializing the configuration during LogManager initialization or
    1537      * used with the "java.util.logging.config.class" property.
    1538      * When this method is called after loggers have been created, all
    1539      * existing loggers will be {@linkplain #reset() reset}. Then any
    1540      * existing loggers that have a level property specified in the
    1541      * given input stream will be {@linkplain
    1542      * Logger#setLevel(java.util.logging.Level) set} to the specified log level.
    1543      * <p>
    1544      * To properly update the logging configuration, use the
    1545      * {@link #updateConfiguration(java.util.function.Function)} or
    1546      * {@link #updateConfiguration(java.io.InputStream, java.util.function.Function)}
    1547      * method instead.
    1548      *
    1549      * @param ins  stream to read properties from
    1550      * @throws  SecurityException  if a security manager exists and if
    1551      *             the caller does not have LoggingPermission("control").
    1552      * @throws  IOException if there are problems reading from the stream,
    1553      *             or the given stream is not in the
    1554      *             {@linkplain java.util.Properties properties file} format.
    1555      */
    1556     public void readConfiguration(InputStream ins) throws IOException, SecurityException {
    1557         checkPermission();
    1558
    1559         // We don't want reset() and readConfiguration() to run
    1560         // in parallel.
    1561         configurationLock.lock();
    1562         try {
    1563             if (globalHandlersState == STATE_SHUTDOWN) {
    1564                 // already in terminal state: don't even bother
    1565                 // to read the configuration
    1566                 return;
    1567             }
    1568
    1569             // change state to STATE_READING_CONFIG to signal reset() to not change it
    1570             globalHandlersState = STATE_READING_CONFIG;
    1571             try {
    1572                 // reset configuration which leaves globalHandlersState at STATE_READING_CONFIG
    1573                 // so that while reading configuration, any ongoing logging requests block and
    1574                 // wait for the outcome (see the end of this try statement)
    1575                 reset();
    1576
    1577                 try {
    1578                     // Load the properties
    1579                     props.load(ins);
    1580                 } catch (IllegalArgumentException x) {
    1581                     // props.load may throw an IllegalArgumentException if the stream
    1582                     // contains malformed Unicode escape sequences.
    1583                     // We wrap that in an IOException as readConfiguration is
    1584                     // specified to throw IOException if there are problems reading
    1585                     // from the stream.
    1586                     // Note: new IOException(x.getMessage(), x) allow us to get a more
    1587                     // concise error message than new IOException(x);
    1588                     throw new IOException(x.getMessage(), x);
    1589                 }
    1590
    1591                 // Instantiate new configuration objects.
    1592                 String names[] = parseClassNames("config");
    1593
    1594                 for (String word : names) {
    1595                     try {
    1596                         Class<?> clz = ClassLoader.getSystemClassLoader().loadClass(word);
    1597                         @SuppressWarnings("deprecation")
    1598                         Object witness = clz.newInstance();
    1599                     } catch (Exception ex) {
    1600                         System.err.println("Can't load config class \"" + word + "\"");
    1601                         System.err.println("" + ex);
    1602                         // ex.printStackTrace();
    1603                     }
    1604                 }
    1605
    1606                 // Set levels on any pre-existing loggers, based on the new properties.
    1607                 setLevelsOnExistingLoggers();
    1608
    1609                 // Note that we need to reinitialize global handles when
    1610                 // they are first referenced.
    1611                 globalHandlersState = STATE_UNINITIALIZED;
    1612             } catch (Throwable t) {
    1613                 // If there were any trouble, then set state to STATE_INITIALIZED
    1614                 // so that no global handlers reinitialization is performed on not fully
    1615                 // initialized configuration.
    1616                 globalHandlersState = STATE_INITIALIZED;
    1617                 // re-throw
    1618                 throw t;
    1619             }
    1620         } finally {
    1621             configurationLock.unlock();
    1622         }
    1623
    1624         // should be called out of lock to avoid dead-lock situations
    1625         // when user code is involved
    1626         invokeConfigurationListeners();
    1627     }
    1628
    1629     // This enum enumerate the configuration properties that will be
    1630     // updated on existing loggers when the configuration is updated
    1631     // with LogManager.updateConfiguration().
    1632     //
    1633     // Note that this works properly only for the global LogManager - as
    1634     // Handler and its subclasses get their configuration from
    1635     // LogManager.getLogManager().
    1636     //
    1637     static enum ConfigProperty {
    1638         LEVEL(".level"), HANDLERS(".handlers"), USEPARENT(".useParentHandlers");
    1639         final String suffix;
    1640         final int length;
    1641         private ConfigProperty(String suffix) {
    1642             this.suffix = Objects.requireNonNull(suffix);
    1643             length = suffix.length();
    1644         }
    1645
    1646         public boolean handleKey(String key) {
    1647             if (this == HANDLERS && suffix.substring(1).equals(key)) return true;
    1648             if (this == HANDLERS && suffix.equals(key)) return false;
    1649             return key.endsWith(suffix);
    1650         }
    1651         String key(String loggerName) {
    1652             if (this == HANDLERS && (loggerName == null || loggerName.isEmpty())) {
    1653                 return suffix.substring(1);
    1654             }
    1655             return loggerName + suffix;
    1656         }
    1657         String loggerName(String key) {
    1658             assert key.equals(suffix.substring(1)) && this == HANDLERS || key.endsWith(suffix);
    1659             if (this == HANDLERS && suffix.substring(1).equals(key)) return "";
    1660             return key.substring(0, key.length() - length);
    1661         }
    1662
    1663         /**
    1664          * If the property is one that should be updated on existing loggers by
    1665          * updateConfiguration, returns the name of the logger for which the
    1666          * property is configured. Otherwise, returns null.
    1667          * @param property a property key in 'props'
    1668          * @return the name of the logger on which the property is to be set,
    1669          *         if the property is one that should be updated on existing
    1670          *         loggers, {@code null} otherwise.
    1671          */
    1672         static String getLoggerName(String property) {
    1673             for (ConfigProperty p : ConfigProperty.ALL) {
    1674                 if (p.handleKey(property)) {
    1675                     return p.loggerName(property);
    1676                 }
    1677             }
    1678             return null; // Not a property that should be updated.
    1679         }
    1680
    1681         /**
    1682          * Find the ConfigProperty corresponding to the given
    1683          * property key (may find none).
    1684          * @param property a property key in 'props'
    1685          * @return An optional containing a ConfigProperty object,
    1686          *         if the property is one that should be updated on existing
    1687          *         loggers, empty otherwise.
    1688          */
    1689         static Optional<ConfigProperty> find(String property) {
    1690             return ConfigProperty.ALL.stream()
    1691                     .filter(p -> p.handleKey(property))
    1692                     .findFirst();
    1693          }
    1694
    1695         /**
    1696          * Returns true if the given property is one that should be updated
    1697          * on existing loggers.
    1698          * Used to filter property name streams.
    1699          * @param property a property key from the configuration.
    1700          * @return true if this property is of interest for updateConfiguration.
    1701          */
    1702         static boolean matches(String property) {
    1703             return find(property).isPresent();
    1704         }
    1705
    1706         /**
    1707          * Returns true if the new property value is different from the old,
    1708          * and therefore needs to be updated on existing loggers.
    1709          * @param k a property key in the configuration
    1710          * @param previous the old configuration
    1711          * @param next the new configuration
    1712          * @return true if the property is changing value between the two
    1713          *         configurations.
    1714          */
    1715         static boolean needsUpdating(String k, Properties previous, Properties next) {
    1716             final String p = trim(previous.getProperty(k, null));
    1717             final String n = trim(next.getProperty(k, null));
    1718             return ! Objects.equals(p,n);
    1719         }
    1720
    1721         /**
    1722          * Applies the mapping function for the given key to the next
    1723          * configuration.
    1724          * If the mapping function is null then this method does nothing.
    1725          * Otherwise, it calls the mapping function to compute the value
    1726          * that should be associated with {@code key} in the resulting
    1727          * configuration, and applies it to {@code next}.
    1728          * If the mapping function returns {@code null} the key is removed
    1729          * from {@code next}.
    1730          *
    1731          * @param k a property key in the configuration
    1732          * @param previous the old configuration
    1733          * @param next the new configuration (modified by this function)
    1734          * @param mappingFunction the mapping function.
    1735          */
    1736         static void merge(String k, Properties previous, Properties next,
    1737                           BiFunction<String, String, String> mappingFunction) {
    1738             String p = trim(previous.getProperty(k, null));
    1739             String n = trim(next.getProperty(k, null));
    1740             String mapped = trim(mappingFunction.apply(p,n));
    1741             if (!Objects.equals(n, mapped)) {
    1742                 if (mapped == null) {
    1743                     next.remove(k);
    1744                 } else {
    1745                     next.setProperty(k, mapped);
    1746                 }
    1747             }
    1748         }
    1749
    1750         private static final EnumSet<ConfigProperty> ALL =
    1751                 EnumSet.allOf(ConfigProperty.class);
    1752     }
    1753
    1754     // trim the value if not null.
    1755     private static String trim(String value) {
    1756         return value == null ? null : value.trim();
    1757     }
    1758
    1759     /**
    1760      * An object that keep track of loggers we have already visited.
    1761      * Used when updating configuration, to avoid processing the same logger
    1762      * twice.
    1763      */
    1764     static final class VisitedLoggers implements Predicate<Logger> {
    1765         final IdentityHashMap<Logger,Boolean> visited;
    1766         private VisitedLoggers(IdentityHashMap<Logger,Boolean> visited) {
    1767             this.visited = visited;
    1768         }
    1769         VisitedLoggers() {
    1770             this(new IdentityHashMap<>());
    1771         }
    1772         @Override
    1773         public boolean test(Logger logger) {
    1774             return visited != null && visited.put(logger, Boolean.TRUE) != null;
    1775         }
    1776         public void clear() {
    1777             if (visited != null) visited.clear();
    1778         }
    1779
    1780         // An object that considers that no logger has ever been visited.
    1781         // This is used when processParentHandlers is called from
    1782         // LoggerContext.addLocalLogger
    1783         static final VisitedLoggers NEVER = new VisitedLoggers(null);
    1784     }
    1785
    1786
    1787     /**
    1788      * Type of the modification for a given property. One of SAME, ADDED, CHANGED,
    1789      * or REMOVED.
    1790      */
    1791     static enum ModType {
    1792         SAME,    // property had no value in the old and new conf, or had the
    1793                  // same value in both.
    1794         ADDED,   // property had no value in the old conf, but has one in the new.
    1795         CHANGED, // property has a different value in the old conf and the new conf.
    1796         REMOVED; // property has no value in the new conf, but had one in the old.
    1797         static ModType of(String previous, String next) {
    1798             if (previous == null && next != null) {
    1799                 return ADDED;
    1800             }
    1801             if (next == null && previous != null) {
    1802                 return REMOVED;
    1803             }
    1804             if (!Objects.equals(trim(previous), trim(next))) {
    1805                 return CHANGED;
    1806             }
    1807             return SAME;
    1808         }
    1809     }
    1810
    1811     /**
    1812      * Updates the logging configuration.
    1813      * <p>
    1814      * If the "java.util.logging.config.file" system property is set,
    1815      * then the property value specifies the properties file to be read
    1816      * as the new configuration. Otherwise, the LogManager default
    1817      * configuration is used.
    1818      * <br>The default configuration is typically loaded from the
    1819      * properties file "{@code conf/logging.properties}" in the
    1820      * Java installation directory.
    1821      * <p>
    1822      * This method reads the new configuration and calls the {@link
    1823      * #updateConfiguration(java.io.InputStream, java.util.function.Function)
    1824      * updateConfiguration(ins, mapper)} method to
    1825      * update the configuration.
    1826      *
    1827      * @apiNote
    1828      * This method updates the logging configuration from reading
    1829      * a properties file and ignores the "java.util.logging.config.class"
    1830      * system property.  The "java.util.logging.config.class" property is
    1831      * only used by the {@link #readConfiguration()}  method to load a custom
    1832      * configuration class as an initial configuration.
    1833      *
    1834      * @param mapper a functional interface that takes a configuration
    1835      *   key <i>k</i> and returns a function <i>f(o,n)</i> whose returned
    1836      *   value will be applied to the resulting configuration. The
    1837      *   function <i>f</i> may return {@code null} to indicate that the property
    1838      *   <i>k</i> will not be added to the resulting configuration.
    1839      *   <br>
    1840      *   If {@code mapper} is {@code null} then {@code (k) -> ((o, n) -> n)} is
    1841      *   assumed.
    1842      *   <br>
    1843      *   For each <i>k</i>, the mapped function <i>f</i> will
    1844      *   be invoked with the value associated with <i>k</i> in the old
    1845      *   configuration (i.e <i>o</i>) and the value associated with
    1846      *   <i>k</i> in the new configuration (i.e. <i>n</i>).
    1847      *   <br>A {@code null} value for <i>o</i> or <i>n</i> indicates that no
    1848      *   value was present for <i>k</i> in the corresponding configuration.
    1849      *
    1850      * @throws  SecurityException  if a security manager exists and if
    1851      *          the caller does not have LoggingPermission("control"), or
    1852      *          does not have the permissions required to set up the
    1853      *          configuration (e.g. open file specified for FileHandlers
    1854      *          etc...)
    1855      *
    1856      * @throws  NullPointerException  if {@code mapper} returns a {@code null}
    1857      *         function when invoked.
    1858      *
    1859      * @throws  IOException if there are problems reading from the
    1860      *          logging configuration file.
    1861      *
    1862      * @see #updateConfiguration(java.io.InputStream, java.util.function.Function)
    1863      * @since 9
    1864      */
    1865     public void updateConfiguration(Function<String, BiFunction<String,String,String>> mapper)
    1866             throws IOException {
    1867         checkPermission();
    1868         ensureLogManagerInitialized();
    1869         drainLoggerRefQueueBounded();
    1870
    1871         String fname = getConfigurationFileName();
    1872         try (final InputStream in = new FileInputStream(fname)) {
    1873             final BufferedInputStream bin = new BufferedInputStream(in);
    1874             updateConfiguration(bin, mapper);
    1875         }
    1876     }
    1877
    1878     /**
    1879      * Updates the logging configuration.
    1880      * <p>
    1881      * For each configuration key in the {@linkplain
    1882      * #getProperty(java.lang.String) existing configuration} and
    1883      * the given input stream configuration, the given {@code mapper} function
    1884      * is invoked to map from the configuration key to a function,
    1885      * <i>f(o,n)</i>, that takes the old value and new value and returns
    1886      * the resulting value to be applied in the resulting configuration,
    1887      * as specified in the table below.
    1888      * <p>Let <i>k</i> be a configuration key in the old or new configuration,
    1889      * <i>o</i> be the old value (i.e. the value associated
    1890      * with <i>k</i> in the old configuration), <i>n</i> be the
    1891      * new value (i.e. the value associated with <i>k</i> in the new
    1892      * configuration), and <i>f</i> be the function returned
    1893      * by {@code mapper.apply(}<i>k</i>{@code )}: then <i>v = f(o,n)</i> is the
    1894      * resulting value. If <i>v</i> is not {@code null}, then a property
    1895      * <i>k</i> with value <i>v</i> will be added to the resulting configuration.
    1896      * Otherwise, it will be omitted.
    1897      * <br>A {@code null} value may be passed to function
    1898      * <i>f</i> to indicate that the corresponding configuration has no
    1899      * configuration key <i>k</i>.
    1900      * The function <i>f</i> may return {@code null} to indicate that
    1901      * there will be no value associated with <i>k</i> in the resulting
    1902      * configuration.
    1903      * <p>
    1904      * If {@code mapper} is {@code null}, then <i>v</i> will be set to
    1905      * <i>n</i>.
    1906      * <p>
    1907      * LogManager {@linkplain #getProperty(java.lang.String) properties} are
    1908      * updated with the resulting value in the resulting configuration.
    1909      * <p>
    1910      * The registered {@linkplain #addConfigurationListener configuration
    1911      * listeners} will be invoked after the configuration is successfully updated.
    1912      * <br><br>
    1913      * <table class="striped">
    1914      * <caption style="display:none">Updating configuration properties</caption>
    1915      * <thead>
    1916      * <tr>
    1917      * <th scope="col">Property</th>
    1918      * <th scope="col">Resulting Behavior</th>
    1919      * </tr>
    1920      * </thead>
    1921      * <tbody>
    1922      * <tr>
    1923      * <th scope="row" style="vertical-align:top">{@code <logger>.level}</th>
    1924      * <td>
    1925      * <ul>
    1926      *   <li>If the resulting configuration defines a level for a logger and
    1927      *       if the resulting level is different than the level specified in the
    1928      *       the old configuration, or not specified in
    1929      *       the old configuration, then if the logger exists or if children for
    1930      *       that logger exist, the level for that logger will be updated,
    1931      *       and the change propagated to any existing logger children.
    1932      *       This may cause the logger to be created, if necessary.
    1933      *   </li>
    1934      *   <li>If the old configuration defined a level for a logger, and the
    1935      *       resulting configuration doesn't, then this change will not be
    1936      *       propagated to existing loggers, if any.
    1937      *       To completely replace a configuration - the caller should therefore
    1938      *       call {@link #reset() reset} to empty the current configuration,
    1939      *       before calling {@code updateConfiguration}.
    1940      *   </li>
    1941      * </ul>
    1942      * </td>
    1943      * <tr>
    1944      * <th scope="row" style="vertical-align:top">{@code <logger>.useParentHandlers}</th>
    1945      * <td>
    1946      * <ul>
    1947      *   <li>If either the resulting or the old value for the useParentHandlers
    1948      *       property is not null, then if the logger exists or if children for
    1949      *       that logger exist, that logger will be updated to the resulting
    1950      *       value.
    1951      *       The value of the useParentHandlers property is the value specified
    1952      *       in the configuration; if not specified, the default is true.
    1953      *   </li>
    1954      * </ul>
    1955      * </td>
    1956      * </tr>
    1957      * <tr>
    1958      * <th scope="row" style="vertical-align:top">{@code <logger>.handlers}</th>
    1959      * <td>
    1960      * <ul>
    1961      *   <li>If the resulting configuration defines a list of handlers for a
    1962      *       logger, and if the resulting list is different than the list
    1963      *       specified in the old configuration for that logger (that could be
    1964      *       empty), then if the logger exists or its children exist, the
    1965      *       handlers associated with that logger are closed and removed and
    1966      *       the new handlers will be created per the resulting configuration
    1967      *       and added to that logger, creating that logger if necessary.
    1968      *   </li>
    1969      *   <li>If the old configuration defined some handlers for a logger, and
    1970      *       the resulting configuration doesn't, if that logger exists,
    1971      *       its handlers will be removed and closed.
    1972      *   </li>
    1973      *   <li>Changing the list of handlers on an existing logger will cause all
    1974      *       its previous handlers to be removed and closed, regardless of whether
    1975      *       they had been created from the configuration or programmatically.
    1976      *       The old handlers will be replaced by new handlers, if any.
    1977      *   </li>
    1978      * </ul>
    1979      * </td>
    1980      * </tr>
    1981      * <tr>
    1982      * <th scope="row" style="vertical-align:top">{@code <handler-name>.*}</th>
    1983      * <td>
    1984      * <ul>
    1985      *   <li>Properties configured/changed on handler classes will only affect
    1986      *       newly created handlers. If a node is configured with the same list
    1987      *       of handlers in the old and the resulting configuration, then these
    1988      *       handlers will remain unchanged.
    1989      *   </li>
    1990      * </ul>
    1991      * </td>
    1992      * </tr>
    1993      * <tr>
    1994      * <th scope="row" style="vertical-align:top">{@code config} and any other property</th>
    1995      * <td>
    1996      * <ul>
    1997      *   <li>The resulting value for these property will be stored in the
    1998      *   LogManager properties, but {@code updateConfiguration} will not parse
    1999      *   or process their values.
    2000      *   </li>
    2001      * </ul>
    2002      * </td>
    2003      * </tr>
    2004      * </tbody>
    2005      * </table>
    2006      * <p>
    2007      * <em>Example mapper functions:</em>
    2008      * <br><br>
    2009      * <ul>
    2010      * <li>Replace all logging properties with the new configuration:
    2011      * <br><br>{@code     (k) -> ((o, n) -> n)}:
    2012      * <br><br>this is equivalent to passing a null {@code mapper} parameter.
    2013      * </li>
    2014      * <li>Merge the new configuration and old configuration and use the
    2015      * new value if <i>k</i> exists in the new configuration:
    2016      * <br><br>{@code     (k) -> ((o, n) -> n == null ? o : n)}:
    2017      * <br><br>as if merging two collections as follows:
    2018      * {@code result.putAll(oldc); result.putAll(newc)}.<br></li>
    2019      * <li>Merge the new configuration and old configuration and use the old
    2020      * value if <i>k</i> exists in the old configuration:
    2021      * <br><br>{@code     (k) -> ((o, n) -> o == null ? n : o)}:
    2022      * <br><br>as if merging two collections as follows:
    2023      * {@code result.putAll(newc); result.putAll(oldc)}.<br></li>
    2024      * <li>Replace all properties with the new configuration except the handler
    2025      * property to configure Logger's handler that is not root logger:
    2026      * <br>
    2027      * <pre>{@code (k) -> k.endsWith(".handlers")}
    2028      *      {@code     ? ((o, n) -> (o == null ? n : o))}
    2029      *      {@code     : ((o, n) -> n)}</pre>
    2030      * </li>
    2031      * </ul>
    2032      * <p>
    2033      * To completely reinitialize a configuration, an application can first call
    2034      * {@link #reset() reset} to fully remove the old configuration, followed by
    2035      * {@code updateConfiguration} to initialize the new configuration.
    2036      *
    2037      * @param ins    a stream to read properties from
    2038      * @param mapper a functional interface that takes a configuration
    2039      *   key <i>k</i> and returns a function <i>f(o,n)</i> whose returned
    2040      *   value will be applied to the resulting configuration. The
    2041      *   function <i>f</i> may return {@code null} to indicate that the property
    2042      *   <i>k</i> will not be added to the resulting configuration.
    2043      *   <br>
    2044      *   If {@code mapper} is {@code null} then {@code (k) -> ((o, n) -> n)} is
    2045      *   assumed.
    2046      *   <br>
    2047      *   For each <i>k</i>, the mapped function <i>f</i> will
    2048      *   be invoked with the value associated with <i>k</i> in the old
    2049      *   configuration (i.e <i>o</i>) and the value associated with
    2050      *   <i>k</i> in the new configuration (i.e. <i>n</i>).
    2051      *   <br>A {@code null} value for <i>o</i> or <i>n</i> indicates that no
    2052      *   value was present for <i>k</i> in the corresponding configuration.
    2053      *
    2054      * @throws  SecurityException if a security manager exists and if
    2055      *          the caller does not have LoggingPermission("control"), or
    2056      *          does not have the permissions required to set up the
    2057      *          configuration (e.g. open files specified for FileHandlers)
    2058      *
    2059      * @throws  NullPointerException if {@code ins} is null or if
    2060      *          {@code mapper} returns a null function when invoked.
    2061      *
    2062      * @throws  IOException if there are problems reading from the stream,
    2063      *          or the given stream is not in the
    2064      *          {@linkplain java.util.Properties properties file} format.
    2065      * @since 9
    2066      */
    2067     public void updateConfiguration(InputStream ins,
    2068             Function<String, BiFunction<String,String,String>> mapper)
    2069             throws IOException {
    2070         checkPermission();
    2071         ensureLogManagerInitialized();
    2072         drainLoggerRefQueueBounded();
    2073
    2074         final Properties previous;
    2075         final Set<String> updatePropertyNames;
    2076         List<LoggerContext> cxs = Collections.emptyList();
    2077         final VisitedLoggers visited = new VisitedLoggers();
    2078         final Properties next = new Properties();
    2079
    2080         try {
    2081             // Load the properties
    2082             next.load(ins);
    2083         } catch (IllegalArgumentException x) {
    2084             // props.load may throw an IllegalArgumentException if the stream
    2085             // contains malformed Unicode escape sequences.
    2086             // We wrap that in an IOException as updateConfiguration is
    2087             // specified to throw IOException if there are problems reading
    2088             // from the stream.
    2089             // Note: new IOException(x.getMessage(), x) allow us to get a more
    2090             // concise error message than new IOException(x);
    2091             throw new IOException(x.getMessage(), x);
    2092         }
    2093
    2094         if (globalHandlersState == STATE_SHUTDOWN) return;
    2095
    2096         // exclusive lock: readConfiguration/reset/updateConfiguration can't
    2097         //           run concurrently.
    2098         // configurationLock.writeLock().lock();
    2099         configurationLock.lock();
    2100         try {
    2101             if (globalHandlersState == STATE_SHUTDOWN) return;
    2102             previous = props;
    2103
    2104             // Builds a TreeSet of all (old and new) property names.
    2105             updatePropertyNames =
    2106                     Stream.concat(previous.stringPropertyNames().stream(),
    2107                                   next.stringPropertyNames().stream())
    2108                         .collect(Collectors.toCollection(TreeSet::new));
    2109
    2110             if (mapper != null) {
    2111                 // mapper will potentially modify the content of
    2112                 // 'next', so we need to call it before affecting props=next.
    2113                 // give a chance to the mapper to control all
    2114                 // properties - not just those we will reset.
    2115                 updatePropertyNames.stream()
    2116                         .forEachOrdered(k -> ConfigProperty
    2117                                 .merge(k, previous, next,
    2118                                        Objects.requireNonNull(mapper.apply(k))));
    2119             }
    2120
    2121             props = next;
    2122
    2123             // allKeys will contain all keys:
    2124             //    - which correspond to a configuration property we are interested in
    2125             //      (first filter)
    2126             //    - whose value needs to be updated (because it's new, removed, or
    2127             //      different) in the resulting configuration (second filter)
    2128             final Stream<String> allKeys = updatePropertyNames.stream()
    2129                     .filter(ConfigProperty::matches)
    2130                     .filter(k -> ConfigProperty.needsUpdating(k, previous, next));
    2131
    2132             // Group configuration properties by logger name
    2133             // We use a TreeMap so that parent loggers will be visited before
    2134             // child loggers.
    2135             final Map<String, TreeSet<String>> loggerConfigs =
    2136                     allKeys.collect(Collectors.groupingBy(ConfigProperty::getLoggerName,
    2137                                     TreeMap::new,
    2138                                     Collectors.toCollection(TreeSet::new)));
    2139
    2140             if (!loggerConfigs.isEmpty()) {
    2141                 cxs = contexts();
    2142             }
    2143             final List<Logger> loggers = cxs.isEmpty()
    2144                     ? Collections.emptyList() : new ArrayList<>(cxs.size());
    2145             for (Map.Entry<String, TreeSet<String>> e : loggerConfigs.entrySet()) {
    2146                 // This can be a logger name, or something else...
    2147                 // The only thing we know is that we found a property
    2148                 //    we are interested in.
    2149                 // For instance, if we found x.y.z.level, then x.y.z could be
    2150                 // a logger, but it could also be a handler class...
    2151                 // Anyway...
    2152                 final String name = e.getKey();
    2153                 final Set<String> properties = e.getValue();
    2154                 loggers.clear();
    2155                 for (LoggerContext cx : cxs) {
    2156                     Logger l = cx.findLogger(name);
    2157                     if (l != null && !visited.test(l)) {
    2158                         loggers.add(l);
    2159                     }
    2160                 }
    2161                 if (loggers.isEmpty()) continue;
    2162                 for (String pk : properties) {
    2163                     ConfigProperty cp = ConfigProperty.find(pk).get();
    2164                     String p = previous.getProperty(pk, null);
    2165                     String n = next.getProperty(pk, null);
    2166
    2167                     // Determines the type of modification.
    2168                     ModType mod = ModType.of(p, n);
    2169
    2170                     // mod == SAME means that the two values are equals, there
    2171                     // is nothing to do. Usually, this should not happen as such
    2172                     // properties should have been filtered above.
    2173                     // It could happen however if the properties had
    2174                     // trailing/leading whitespaces.
    2175                     if (mod == ModType.SAME) continue;
    2176
    2177                     switch (cp) {
    2178                         case LEVEL:
    2179                             if (mod == ModType.REMOVED) continue;
    2180                             Level level = Level.findLevel(trim(n));
    2181                             if (level != null) {
    2182                                 if (name.isEmpty()) {
    2183                                     rootLogger.setLevel(level);
    2184                                 }
    2185                                 for (Logger l : loggers) {
    2186                                     if (!name.isEmpty() || l != rootLogger) {
    2187                                         l.setLevel(level);
    2188                                     }
    2189                                 }
    2190                             }
    2191                             break;
    2192                         case USEPARENT:
    2193                             if (!name.isEmpty()) {
    2194                                 boolean useParent = getBooleanProperty(pk, true);
    2195                                 if (n != null || p != null) {
    2196                                     // reset the flag only if the previous value
    2197                                     // or the new value are not null.
    2198                                     for (Logger l : loggers) {
    2199                                         l.setUseParentHandlers(useParent);
    2200                                     }
    2201                                 }
    2202                             }
    2203                             break;
    2204                         case HANDLERS:
    2205                             List<Handler> hdls = null;
    2206                             if (name.isEmpty()) {
    2207                                 // special handling for the root logger.
    2208                                 globalHandlersState = STATE_READING_CONFIG;
    2209                                 try {
    2210                                     closeHandlers(rootLogger);
    2211                                     globalHandlersState = STATE_UNINITIALIZED;
    2212                                 } catch (Throwable t) {
    2213                                     globalHandlersState = STATE_INITIALIZED;
    2214                                     throw t;
    2215                                 }
    2216                             }
    2217                             for (Logger l : loggers) {
    2218                                 if (l == rootLogger) continue;
    2219                                 closeHandlers(l);
    2220                                 if (mod == ModType.REMOVED) {
    2221                                     closeOnResetLoggers.removeIf(c -> c.logger == l);
    2222                                     continue;
    2223                                 }
    2224                                 if (hdls == null) {
    2225                                     hdls = name.isEmpty()
    2226                                             ? Arrays.asList(rootLogger.getHandlers())
    2227                                             : createLoggerHandlers(name, pk);
    2228                                 }
    2229                                 setLoggerHandlers(l, name, pk, hdls);
    2230                             }
    2231                             break;
    2232                         default: break;
    2233                     }
    2234                 }
    2235             }
    2236         } finally {
    2237             configurationLock.unlock();
    2238             visited.clear();
    2239         }
    2240
    2241         // Now ensure that if an existing logger has acquired a new parent
    2242         // in the configuration, this new parent will be created - if needed,
    2243         // and added to the context of the existing child.
    2244         //
    2245         drainLoggerRefQueueBounded();
    2246         for (LoggerContext cx : cxs) {
    2247             for (Enumeration<String> names = cx.getLoggerNames() ; names.hasMoreElements();) {
    2248                 String name = names.nextElement();
    2249                 if (name.isEmpty()) continue;  // don't need to process parents on root.
    2250                 Logger l = cx.findLogger(name);
    2251                 if (l != null && !visited.test(l)) {
    2252                     // should pass visited here to cut the processing when
    2253                     // reaching a logger already visited.
    2254                     cx.processParentHandlers(l, name, visited);
    2255                 }
    2256             }
    2257         }
    2258
    2259         // We changed the configuration: invoke configuration listeners
    2260         invokeConfigurationListeners();
    2261     }
    2262
    2263     /**
    2264      * Get the value of a logging property.
    2265      * The method returns null if the property is not found.
    2266      * @param name      property name
    2267      * @return          property value
    2268      */
    2269     public String getProperty(String name) {
    2270         return props.getProperty(name);
    2271     }
    2272
    2273     // Package private method to get a String property.
    2274     // If the property is not defined we return the given
    2275     // default value.
    2276     String getStringProperty(String name, String defaultValue) {
    2277         String val = getProperty(name);
    2278         if (val == null) {
    2279             return defaultValue;
    2280         }
    2281         return val.trim();
    2282     }
    2283
    2284     // Package private method to get an integer property.
    2285     // If the property is not defined or cannot be parsed
    2286     // we return the given default value.
    2287     int getIntProperty(String name, int defaultValue) {
    2288         String val = getProperty(name);
    2289         if (val == null) {
    2290             return defaultValue;
    2291         }
    2292         try {
    2293             return Integer.parseInt(val.trim());
    2294         } catch (Exception ex) {
    2295             return defaultValue;
    2296         }
    2297     }
    2298
    2299     // Package private method to get a long property.
    2300     // If the property is not defined or cannot be parsed
    2301     // we return the given default value.
    2302     long getLongProperty(String name, long defaultValue) {
    2303         String val = getProperty(name);
    2304         if (val == null) {
    2305             return defaultValue;
    2306         }
    2307         try {
    2308             return Long.parseLong(val.trim());
    2309         } catch (Exception ex) {
    2310             return defaultValue;
    2311         }
    2312     }
    2313
    2314     // Package private method to get a boolean property.
    2315     // If the property is not defined or cannot be parsed
    2316     // we return the given default value.
    2317     boolean getBooleanProperty(String name, boolean defaultValue) {
    2318         String val = getProperty(name);
    2319         if (val == null) {
    2320             return defaultValue;
    2321         }
    2322         val = val.toLowerCase();
    2323         if (val.equals("true") || val.equals("1")) {
    2324             return true;
    2325         } else if (val.equals("false") || val.equals("0")) {
    2326             return false;
    2327         }
    2328         return defaultValue;
    2329     }
    2330
    2331     // Package private method to get a Level property.
    2332     // If the property is not defined or cannot be parsed
    2333     // we return the given default value.
    2334     Level getLevelProperty(String name, Level defaultValue) {
    2335         String val = getProperty(name);
    2336         if (val == null) {
    2337             return defaultValue;
    2338         }
    2339         Level l = Level.findLevel(val.trim());
    2340         return l != null ? l : defaultValue;
    2341     }
    2342
    2343     // Package private method to get a filter property.
    2344     // We return an instance of the class named by the "name"
    2345     // property. If the property is not defined or has problems
    2346     // we return the defaultValue.
    2347     Filter getFilterProperty(String name, Filter defaultValue) {
    2348         String val = getProperty(name);
    2349         try {
    2350             if (val != null) {
    2351                 @SuppressWarnings("deprecation")
    2352                 Object o = ClassLoader.getSystemClassLoader().loadClass(val).newInstance();
    2353                 return (Filter) o;
    2354             }
    2355         } catch (Exception ex) {
    2356             // We got one of a variety of exceptions in creating the
    2357             // class or creating an instance.
    2358             // Drop through.
    2359         }
    2360         // We got an exception.  Return the defaultValue.
    2361         return defaultValue;
    2362     }
    2363
    2364
    2365     // Package private method to get a formatter property.
    2366     // We return an instance of the class named by the "name"
    2367     // property. If the property is not defined or has problems
    2368     // we return the defaultValue.
    2369     Formatter getFormatterProperty(String name, Formatter defaultValue) {
    2370         String val = getProperty(name);
    2371         try {
    2372             if (val != null) {
    2373                 @SuppressWarnings("deprecation")
    2374                 Object o = ClassLoader.getSystemClassLoader().loadClass(val).newInstance();
    2375                 return (Formatter) o;
    2376             }
    2377         } catch (Exception ex) {
    2378             // We got one of a variety of exceptions in creating the
    2379             // class or creating an instance.
    2380             // Drop through.
    2381         }
    2382         // We got an exception.  Return the defaultValue.
    2383         return defaultValue;
    2384     }
    2385
    2386     // Private method to load the global handlers.
    2387     // We do the real work lazily, when the global handlers
    2388     // are first used.
    2389     private void initializeGlobalHandlers() {
    2390         int state = globalHandlersState;
    2391         if (state == STATE_INITIALIZED ||
    2392             state == STATE_SHUTDOWN) {
    2393             // Nothing to do: return.
    2394             return;
    2395         }
    2396
    2397         // If we have not initialized global handlers yet (or need to
    2398         // reinitialize them), lets do it now (this case is indicated by
    2399         // globalHandlersState == STATE_UNINITIALIZED).
    2400         // If we are in the process of initializing global handlers we
    2401         // also need to lock & wait (this case is indicated by
    2402         // globalHandlersState == STATE_INITIALIZING).
    2403         // If we are in the process of reading configuration we also need to
    2404         // wait to see what the outcome will be (this case
    2405         // is indicated by globalHandlersState == STATE_READING_CONFIG)
    2406         // So in either case we need to wait for the lock.
    2407         configurationLock.lock();
    2408         try {
    2409             if (globalHandlersState != STATE_UNINITIALIZED) {
    2410                 return; // recursive call or nothing to do
    2411             }
    2412             // set globalHandlersState to STATE_INITIALIZING first to avoid
    2413             // getting an infinite recursion when loadLoggerHandlers(...)
    2414             // is going to call addHandler(...)
    2415             globalHandlersState = STATE_INITIALIZING;
    2416             try {
    2417                 loadLoggerHandlers(rootLogger, null, "handlers");
    2418             } finally {
    2419                 globalHandlersState = STATE_INITIALIZED;
    2420             }
    2421         } finally {
    2422             configurationLock.unlock();
    2423         }
    2424     }
    2425
    2426     static final Permission controlPermission =
    2427             new LoggingPermission("control", null);
    2428
    2429     void checkPermission() {
    2430         SecurityManager sm = System.getSecurityManager();
    2431         if (sm != null)
    2432             sm.checkPermission(controlPermission);
    2433     }
    2434
    2435     /**
    2436      * Check that the current context is trusted to modify the logging
    2437      * configuration.  This requires LoggingPermission("control").
    2438      * <p>
    2439      * If the check fails we throw a SecurityException, otherwise
    2440      * we return normally.
    2441      *
    2442      * @throws  SecurityException  if a security manager exists and if
    2443      *             the caller does not have LoggingPermission("control").
    2444      */
    2445     public void checkAccess() throws SecurityException {
    2446         checkPermission();
    2447     }
    2448
    2449     // Nested class to represent a node in our tree of named loggers.
    2450     private static class LogNode {
    2451         HashMap<String,LogNode> children;
    2452         LoggerWeakRef loggerRef;
    2453         LogNode parent;
    2454         final LoggerContext context;
    2455
    2456         LogNode(LogNode parent, LoggerContext context) {
    2457             this.parent = parent;
    2458             this.context = context;
    2459         }
    2460
    2461         // Recursive method to walk the tree below a node and set
    2462         // a new parent logger.
    2463         void walkAndSetParent(Logger parent) {
    2464             if (children == null) {
    2465                 return;
    2466             }
    2467             for (LogNode node : children.values()) {
    2468                 LoggerWeakRef ref = node.loggerRef;
    2469                 Logger logger = (ref == null) ? null : ref.get();
    2470                 if (logger == null) {
    2471                     node.walkAndSetParent(parent);
    2472                 } else {
    2473                     doSetParent(logger, parent);
    2474                 }
    2475             }
    2476         }
    2477     }
    2478
    2479     // We use a subclass of Logger for the root logger, so
    2480     // that we only instantiate the global handlers when they
    2481     // are first needed.
    2482     private final class RootLogger extends Logger {
    2483         private RootLogger() {
    2484             // We do not call the protected Logger two args constructor here,
    2485             // to avoid calling LogManager.getLogManager() from within the
    2486             // RootLogger constructor.
    2487             super("", null, null, LogManager.this, true);
    2488         }
    2489
    2490         @Override
    2491         public void log(LogRecord record) {
    2492             // Make sure that the global handlers have been instantiated.
    2493             initializeGlobalHandlers();
    2494             super.log(record);
    2495         }
    2496
    2497         @Override
    2498         public void addHandler(Handler h) {
    2499             initializeGlobalHandlers();
    2500             super.addHandler(h);
    2501         }
    2502
    2503         @Override
    2504         public void removeHandler(Handler h) {
    2505             initializeGlobalHandlers();
    2506             super.removeHandler(h);
    2507         }
    2508
    2509         @Override
    2510         Handler[] accessCheckedHandlers() {
    2511             initializeGlobalHandlers();
    2512             return super.accessCheckedHandlers();
    2513         }
    2514     }
    2515
    2516
    2517     // Private method to be called when the configuration has
    2518     // changed to apply any level settings to any pre-existing loggers.
    2519     private void setLevelsOnExistingLoggers() {
    2520         Enumeration<?> enum_ = props.propertyNames();
    2521         while (enum_.hasMoreElements()) {
    2522             String key = (String)enum_.nextElement();
    2523             if (!key.endsWith(".level")) {
    2524                 // Not a level definition.
    2525                 continue;
    2526             }
    2527             int ix = key.length() - 6;
    2528             String name = key.substring(0, ix);
    2529             Level level = getLevelProperty(key, null);
    2530             if (level == null) {
    2531                 System.err.println("Bad level value for property: " + key);
    2532                 continue;
    2533             }
    2534             for (LoggerContext cx : contexts()) {
    2535                 Logger l = cx.findLogger(name);
    2536                 if (l == null) {
    2537                     continue;
    2538                 }
    2539                 l.setLevel(level);
    2540             }
    2541         }
    2542     }
    2543
    2544     /**
    2545      * String representation of the
    2546      * {@link javax.management.ObjectName} for the management interface
    2547      * for the logging facility.
    2548      *
    2549      * @see java.lang.management.PlatformLoggingMXBean
    2550      *
    2551      * @since 1.5
    2552      */
    2553     public static final String LOGGING_MXBEAN_NAME
    2554         = "java.util.logging:type=Logging";
    2555
    2556     /**
    2557      * Returns {@code LoggingMXBean} for managing loggers.
    2558      *
    2559      * @return a {@link LoggingMXBean} object.
    2560      *
    2561      * @deprecated {@code java.util.logging.LoggingMXBean} is deprecated and
    2562      *      replaced with {@code java.lang.management.PlatformLoggingMXBean}. Use
    2563      *      {@link java.lang.management.ManagementFactory#getPlatformMXBean(Class)
    2564      *      ManagementFactory.getPlatformMXBean}(PlatformLoggingMXBean.class)
    2565      *      instead.
    2566      *
    2567      * @see java.lang.management.PlatformLoggingMXBean
    2568      * @since 1.5
    2569      */
    2570     @Deprecated(since="9")
    2571     public static synchronized LoggingMXBean getLoggingMXBean() {
    2572         return Logging.getInstance();
    2573     }
    2574
    2575     /**
    2576      * Adds a configuration listener to be invoked each time the logging
    2577      * configuration is read.
    2578      * If the listener is already registered the method does nothing.
    2579      * <p>
    2580      * The listener is invoked with privileges that are restricted by the
    2581      * calling context of this method.
    2582      * The order in which the listeners are invoked is unspecified.
    2583      * <p>
    2584      * It is recommended that listeners do not throw errors or exceptions.
    2585      *
    2586      * If a listener terminates with an uncaught error or exception then
    2587      * the first exception will be propagated to the caller of
    2588      * {@link #readConfiguration()} (or {@link #readConfiguration(java.io.InputStream)})
    2589      * after all listeners have been invoked.
    2590      *
    2591      * @implNote If more than one listener terminates with an uncaught error or
    2592      * exception, an implementation may record the additional errors or
    2593      * exceptions as {@linkplain Throwable#addSuppressed(java.lang.Throwable)
    2594      * suppressed exceptions}.
    2595      *
    2596      * @param listener A configuration listener that will be invoked after the
    2597      *        configuration changed.
    2598      * @return This LogManager.
    2599      * @throws SecurityException if a security manager exists and if the
    2600      * caller does not have LoggingPermission("control").
    2601      * @throws NullPointerException if the listener is null.
    2602      *
    2603      * @since 9
    2604      */
    2605     public LogManager addConfigurationListener(Runnable listener) {
    2606         final Runnable r = Objects.requireNonNull(listener);
    2607         checkPermission();
    2608         final SecurityManager sm = System.getSecurityManager();
    2609         final AccessControlContext acc =
    2610                 sm == null ? null : AccessController.getContext();
    2611         final PrivilegedAction<Void> pa =
    2612                 acc == null ? null : () -> { r.run() ; return null; };
    2613         final Runnable pr =
    2614                 acc == null ? r : () -> AccessController.doPrivileged(pa, acc);
    2615         // Will do nothing if already registered.
    2616         listeners.putIfAbsent(r, pr);
    2617         return this;
    2618     }
    2619
    2620     /**
    2621      * Removes a previously registered configuration listener.
    2622      *
    2623      * Returns silently if the listener is not found.
    2624      *
    2625      * @param listener the configuration listener to remove.
    2626      * @throws NullPointerException if the listener is null.
    2627      * @throws SecurityException if a security manager exists and if the
    2628      * caller does not have LoggingPermission("control").
    2629      *
    2630      * @since 9
    2631      */
    2632     public void removeConfigurationListener(Runnable listener) {
    2633         final Runnable key = Objects.requireNonNull(listener);
    2634         checkPermission();
    2635         listeners.remove(key);
    2636     }
    2637
    2638     private void invokeConfigurationListeners() {
    2639         Throwable t = null;
    2640
    2641         // We're using an IdentityHashMap because we want to compare
    2642         // keys using identity (==).
    2643         // We don't want to loop within a block synchronized on 'listeners'
    2644         // to avoid invoking listeners from yet another synchronized block.
    2645         // So we're taking a snapshot of the values list to avoid the risk of
    2646         // ConcurrentModificationException while looping.
    2647         //
    2648         for (Runnable c : listeners.values().toArray(new Runnable[0])) {
    2649             try {
    2650                 c.run();
    2651             } catch (ThreadDeath death) {
    2652                 throw death;
    2653             } catch (Error | RuntimeException x) {
    2654                 if (t == null) t = x;
    2655                 else t.addSuppressed(x);
    2656             }
    2657         }
    2658         // Listeners are not supposed to throw exceptions, but if that
    2659         // happens, we will rethrow the first error or exception that is raised
    2660         // after all listeners have been invoked.
    2661         if (t instanceof Error) throw (Error)t;
    2662         if (t instanceof RuntimeException) throw (RuntimeException)t;
    2663     }
    2664
    2665     /**
    2666      * This class allows the {@link LoggingProviderImpl} to demand loggers on
    2667      * behalf of system and application classes.
    2668      */
    2669     private static final class LoggingProviderAccess
    2670         implements LoggingProviderImpl.LogManagerAccess,
    2671                    PrivilegedAction<Void> {
    2672
    2673         private LoggingProviderAccess() {
    2674         }
    2675
    2676         /**
    2677          * Demands a logger on behalf of the given {@code module}.
    2678          * <p>
    2679          * If a named logger suitable for the given module is found
    2680          * returns it.
    2681          * Otherwise, creates a new logger suitable for the given module.
    2682          *
    2683          * @param name   The logger name.
    2684          * @param module The module on which behalf the logger is created/retrieved.
    2685          * @return A logger for the given {@code module}.
    2686          *
    2687          * @throws NullPointerException if {@code name} is {@code null}
    2688          *         or {@code module} is {@code null}.
    2689          * @throws IllegalArgumentException if {@code manager} is not the default
    2690          *         LogManager.
    2691          * @throws SecurityException if a security manager is present and the
    2692          *         calling code doesn't have the
    2693          *        {@link LoggingPermission LoggingPermission("demandLogger", null)}.
    2694          */
    2695         @Override
    2696         public Logger demandLoggerFor(LogManager manager, String name, Module module) {
    2697             if (manager != getLogManager()) {
    2698                 // having LogManager as parameter just ensures that the
    2699                 // caller will have initialized the LogManager before reaching
    2700                 // here.
    2701                 throw new IllegalArgumentException("manager");
    2702             }
    2703             Objects.requireNonNull(name);
    2704             Objects.requireNonNull(module);
    2705             SecurityManager sm = System.getSecurityManager();
    2706             if (sm != null) {
    2707                 sm.checkPermission(controlPermission);
    2708             }
    2709             if (isSystem(module)) {
    2710                 return manager.demandSystemLogger(name,
    2711                     Logger.SYSTEM_LOGGER_RB_NAME, module);
    2712             } else {
    2713                 return manager.demandLogger(name, null, module);
    2714             }
    2715         }
    2716
    2717         @Override
    2718         public Void run() {
    2719             LoggingProviderImpl.setLogManagerAccess(INSTANCE);
    2720             return null;
    2721         }
    2722
    2723         static final LoggingProviderAccess INSTANCE = new LoggingProviderAccess();
    2724     }
    2725
    2726     static {
    2727         AccessController.doPrivileged(LoggingProviderAccess.INSTANCE, null,
    2728                                       controlPermission);
    2729     }
    2730
    2731 }