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