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