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