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