1 /*
   2  * Copyright (c) 2000, 2016, 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.util.logging.internal.LoggingProviderImpl;
  46 import java.lang.reflect.Module;
  47 import static jdk.internal.logger.DefaultLoggerFinder.isSystem;
  48 
  49 /**
  50  * There is a single global LogManager object that is used to
  51  * maintain a set of shared state about Loggers and log services.
  52  * <p>
  53  * This LogManager object:
  54  * <ul>
  55  * <li> Manages a hierarchical namespace of Logger objects.  All
  56  *      named Loggers are stored in this namespace.
  57  * <li> Manages a set of logging control properties.  These are
  58  *      simple key-value pairs that can be used by Handlers and
  59  *      other logging objects to configure themselves.
  60  * </ul>
  61  * <p>
  62  * The global LogManager object can be retrieved using LogManager.getLogManager().
  63  * The LogManager object is created during class initialization and
  64  * cannot subsequently be changed.
  65  * <p>
  66  * At startup the LogManager class is located using the
  67  * java.util.logging.manager system property.
  68  *
  69  * <h3>LogManager Configuration</h3>
  70  *
  71  * A LogManager initializes the logging configuration via
  72  * the {@link #readConfiguration()} method during LogManager initialization.
  73  * By default, LogManager default configuration is used.
  74  * The logging configuration read by LogManager must be in the
  75  * {@linkplain Properties properties file} format.
  76  * <p>
  77  * The LogManager defines two optional system properties that allow control over
  78  * the initial configuration, as specified in the {@link #readConfiguration()}
  79  * method:
  80  * <ul>
  81  * <li>"java.util.logging.config.class"
  82  * <li>"java.util.logging.config.file"
  83  * </ul>
  84  * <p>
  85  * These two system properties may be specified on the command line to the "java"
  86  * command, or as system property definitions passed to JNI_CreateJavaVM.
  87  * <p>
  88  * The {@linkplain Properties properties} for loggers and Handlers will have
  89  * names starting with the dot-separated name for the handler or logger.<br>
  90  * The global logging properties may include:
  91  * <ul>
  92  * <li>A property "handlers".  This defines a whitespace or comma separated
  93  * list of class names for handler classes to load and register as
  94  * handlers on the root Logger (the Logger named "").  Each class
  95  * name must be for a Handler class which has a default constructor.
  96  * Note that these Handlers may be created lazily, when they are
  97  * first used.
  98  *
  99  * <li>A property "&lt;logger&gt;.handlers". This defines a whitespace or
 100  * comma separated list of class names for handlers classes to
 101  * load and register as handlers to the specified logger. Each class
 102  * name must be for a Handler class which has a default constructor.
 103  * Note that these Handlers may be created lazily, when they are
 104  * first used.
 105  *
 106  * <li>A property "&lt;logger&gt;.handlers.ensureCloseOnReset". This defines a
 107  * a boolean value. If "&lt;logger&gt;.handlers" is not defined or is empty,
 108  * this property is ignored. Otherwise it defaults to {@code true}. When the
 109  * value is {@code true}, the handlers associated with the logger are guaranteed
 110  * to be closed on {@linkplain #reset} and shutdown. This can be turned off
 111  * by explicitly setting "&lt;logger&gt;.handlers.ensureCloseOnReset=false" in
 112  * the configuration. Note that turning this property off causes the risk of
 113  * introducing a resource leak, as the logger may get garbage collected before
 114  * {@code reset()} is called, thus preventing its handlers from being closed
 115  * on {@code reset()}. In that case it is the responsibility of the application
 116  * to ensure that the handlers are closed before the logger is garbage
 117  * collected.
 118  *
 119  * <li>A property "&lt;logger&gt;.useParentHandlers". This defines a boolean
 120  * value. By default every logger calls its parent in addition to
 121  * handling the logging message itself, this often result in messages
 122  * being handled by the root logger as well. When setting this property
 123  * to false a Handler needs to be configured for this logger otherwise
 124  * no logging messages are delivered.
 125  *
 126  * <li>A property "config".  This property is intended to allow
 127  * arbitrary configuration code to be run.  The property defines a
 128  * whitespace or comma separated list of class names.  A new instance will be
 129  * created for each named class.  The default constructor of each class
 130  * may execute arbitrary code to update the logging configuration, such as
 131  * setting logger levels, adding handlers, adding filters, etc.
 132  * </ul>
 133  * <p>
 134  * Note that all classes loaded during LogManager configuration are
 135  * first searched on the system class path before any user class path.
 136  * That includes the LogManager class, any config classes, and any
 137  * handler classes.
 138  * <p>
 139  * Loggers are organized into a naming hierarchy based on their
 140  * dot separated names.  Thus "a.b.c" is a child of "a.b", but
 141  * "a.b1" and a.b2" are peers.
 142  * <p>
 143  * All properties whose names end with ".level" are assumed to define
 144  * log levels for Loggers.  Thus "foo.level" defines a log level for
 145  * the logger called "foo" and (recursively) for any of its children
 146  * in the naming hierarchy.  Log Levels are applied in the order they
 147  * are defined in the properties file.  Thus level settings for child
 148  * nodes in the tree should come after settings for their parents.
 149  * The property name ".level" can be used to set the level for the
 150  * root of the tree.
 151  * <p>
 152  * All methods on the LogManager object are multi-thread safe.
 153  *
 154  * @since 1.4
 155 */
 156 
 157 public class LogManager {
 158     // The global LogManager object
 159     private static final LogManager manager;
 160 
 161     // 'props' is assigned within a lock but accessed without it.
 162     // Declaring it volatile makes sure that another thread will not
 163     // be able to see a partially constructed 'props' object.
 164     // (seeing a partially constructed 'props' object can result in
 165     // NPE being thrown in Hashtable.get(), because it leaves the door
 166     // open for props.getProperties() to be called before the construcor
 167     // of Hashtable is actually completed).
 168     private volatile Properties props = new Properties();
 169     private final static Level defaultLevel = Level.INFO;
 170 
 171     // LoggerContext for system loggers and user loggers
 172     private final LoggerContext systemContext = new SystemLoggerContext();
 173     private final LoggerContext userContext = new LoggerContext();
 174     // non final field - make it volatile to make sure that other threads
 175     // will see the new value once ensureLogManagerInitialized() has finished
 176     // executing.
 177     private volatile Logger rootLogger;
 178     // Have we done the primordial reading of the configuration file?
 179     // (Must be done after a suitable amount of java.lang.System
 180     // initialization has been done)
 181     private volatile boolean readPrimordialConfiguration;
 182     // Have we initialized global (root) handlers yet?
 183     // This gets set to STATE_UNINITIALIZED in readConfiguration
 184     private static final int
 185             STATE_INITIALIZED = 0, // initial state
 186             STATE_INITIALIZING = 1,
 187             STATE_READING_CONFIG = 2,
 188             STATE_UNINITIALIZED = 3,
 189             STATE_SHUTDOWN = 4;    // terminal state
 190     private volatile int globalHandlersState; // = STATE_INITIALIZED;
 191     // A concurrency lock for reset(), readConfiguration() and Cleaner.
 192     private final ReentrantLock configurationLock = new ReentrantLock();
 193 
 194     // This list contains the loggers for which some handlers have been
 195     // explicitly configured in the configuration file.
 196     // It prevents these loggers from being arbitrarily garbage collected.
 197     private static final class CloseOnReset {
 198         private final Logger logger;
 199         private CloseOnReset(Logger ref) {
 200             this.logger = Objects.requireNonNull(ref);
 201         }
 202         @Override
 203         public boolean equals(Object other) {
 204             return (other instanceof CloseOnReset) && ((CloseOnReset)other).logger == logger;
 205         }
 206         @Override
 207         public int hashCode() {
 208             return System.identityHashCode(logger);
 209         }
 210         public Logger get() {
 211             return logger;
 212         }
 213         public static CloseOnReset create(Logger logger) {
 214             return new CloseOnReset(logger);
 215         }
 216     }
 217     private final CopyOnWriteArrayList<CloseOnReset> closeOnResetLoggers =
 218             new CopyOnWriteArrayList<>();
 219 
 220 
 221     private final Map<Object, Runnable> listeners =
 222             Collections.synchronizedMap(new IdentityHashMap<>());
 223 
 224     static {
 225         manager = AccessController.doPrivileged(new PrivilegedAction<LogManager>() {
 226             @Override
 227             public LogManager run() {
 228                 LogManager mgr = null;
 229                 String cname = null;
 230                 try {
 231                     cname = System.getProperty("java.util.logging.manager");
 232                     if (cname != null) {
 233                         try {
 234                             @SuppressWarnings("deprecation")
 235                             Object tmp = ClassLoader.getSystemClassLoader()
 236                                 .loadClass(cname).newInstance();
 237                             mgr = (LogManager) tmp;
 238                         } catch (ClassNotFoundException ex) {
 239                             @SuppressWarnings("deprecation")
 240                             Object tmp = Thread.currentThread()
 241                                 .getContextClassLoader().loadClass(cname).newInstance();
 242                             mgr = (LogManager) tmp;
 243                         }
 244                     }
 245                 } catch (Exception ex) {
 246                     System.err.println("Could not load Logmanager \"" + cname + "\"");
 247                     ex.printStackTrace();
 248                 }
 249                 if (mgr == null) {
 250                     mgr = new LogManager();
 251                 }
 252                 return mgr;
 253 
 254             }
 255         });
 256     }
 257 
 258     // This private class is used as a shutdown hook.
 259     // It does a "reset" to close all open handlers.
 260     private class Cleaner extends Thread {
 261 
 262         private Cleaner() {
 263             super(null, null, "Logging-Cleaner", 0, false);
 264             /* Set context class loader to null in order to avoid
 265              * keeping a strong reference to an application classloader.
 266              */
 267             this.setContextClassLoader(null);
 268         }
 269 
 270         @Override
 271         public void run() {
 272             // This is to ensure the LogManager.<clinit> is completed
 273             // before synchronized block. Otherwise deadlocks are possible.
 274             LogManager mgr = manager;
 275 
 276             // set globalHandlersState to STATE_SHUTDOWN atomically so that
 277             // no attempts are made to (re)initialize the handlers or (re)read
 278             // the configuration again. This is terminal state.
 279             configurationLock.lock();
 280             globalHandlersState = STATE_SHUTDOWN;
 281             configurationLock.unlock();
 282 
 283             // Do a reset to close all active handlers.
 284             reset();
 285         }
 286     }
 287 
 288 
 289     /**
 290      * Protected constructor.  This is protected so that container applications
 291      * (such as J2EE containers) can subclass the object.  It is non-public as
 292      * it is intended that there only be one LogManager object, whose value is
 293      * retrieved by calling LogManager.getLogManager.
 294      */
 295     protected LogManager() {
 296         this(checkSubclassPermissions());
 297     }
 298 
 299     private LogManager(Void checked) {
 300 
 301         // Add a shutdown hook to close the global handlers.
 302         try {
 303             Runtime.getRuntime().addShutdownHook(new Cleaner());
 304         } catch (IllegalStateException e) {
 305             // If the VM is already shutting down,
 306             // We do not need to register shutdownHook.
 307         }
 308     }
 309 
 310     private static Void checkSubclassPermissions() {
 311         final SecurityManager sm = System.getSecurityManager();
 312         if (sm != null) {
 313             // These permission will be checked in the LogManager constructor,
 314             // in order to register the Cleaner() thread as a shutdown hook.
 315             // Check them here to avoid the penalty of constructing the object
 316             // etc...
 317             sm.checkPermission(new RuntimePermission("shutdownHooks"));
 318             sm.checkPermission(new RuntimePermission("setContextClassLoader"));
 319         }
 320         return null;
 321     }
 322 
 323     /**
 324      * Lazy initialization: if this instance of manager is the global
 325      * manager then this method will read the initial configuration and
 326      * add the root logger and global logger by calling addLogger().
 327      *
 328      * Note that it is subtly different from what we do in LoggerContext.
 329      * In LoggerContext we're patching up the logger context tree in order to add
 330      * the root and global logger *to the context tree*.
 331      *
 332      * For this to work, addLogger() must have already have been called
 333      * once on the LogManager instance for the default logger being
 334      * added.
 335      *
 336      * This is why ensureLogManagerInitialized() needs to be called before
 337      * any logger is added to any logger context.
 338      *
 339      */
 340     private boolean initializedCalled = false;
 341     private volatile boolean initializationDone = false;
 342     final void ensureLogManagerInitialized() {
 343         final LogManager owner = this;
 344         if (initializationDone || owner != manager) {
 345             // we don't want to do this twice, and we don't want to do
 346             // this on private manager instances.
 347             return;
 348         }
 349 
 350         // Maybe another thread has called ensureLogManagerInitialized()
 351         // before us and is still executing it. If so we will block until
 352         // the log manager has finished initialized, then acquire the monitor,
 353         // notice that initializationDone is now true and return.
 354         // Otherwise - we have come here first! We will acquire the monitor,
 355         // see that initializationDone is still false, and perform the
 356         // initialization.
 357         //
 358         configurationLock.lock();
 359         try {
 360             // If initializedCalled is true it means that we're already in
 361             // the process of initializing the LogManager in this thread.
 362             // There has been a recursive call to ensureLogManagerInitialized().
 363             final boolean isRecursiveInitialization = (initializedCalled == true);
 364 
 365             assert initializedCalled || !initializationDone
 366                     : "Initialization can't be done if initialized has not been called!";
 367 
 368             if (isRecursiveInitialization || initializationDone) {
 369                 // If isRecursiveInitialization is true it means that we're
 370                 // already in the process of initializing the LogManager in
 371                 // this thread. There has been a recursive call to
 372                 // ensureLogManagerInitialized(). We should not proceed as
 373                 // it would lead to infinite recursion.
 374                 //
 375                 // If initializationDone is true then it means the manager
 376                 // has finished initializing; just return: we're done.
 377                 return;
 378             }
 379             // Calling addLogger below will in turn call requiresDefaultLogger()
 380             // which will call ensureLogManagerInitialized().
 381             // We use initializedCalled to break the recursion.
 382             initializedCalled = true;
 383             try {
 384                 AccessController.doPrivileged(new PrivilegedAction<Object>() {
 385                     @Override
 386                     public Object run() {
 387                         assert rootLogger == null;
 388                         assert initializedCalled && !initializationDone;
 389 
 390                         // create root logger before reading primordial
 391                         // configuration - to ensure that it will be added
 392                         // before the global logger, and not after.
 393                         owner.rootLogger = owner.new RootLogger();
 394 
 395                         // Read configuration.
 396                         owner.readPrimordialConfiguration();
 397 
 398                         // Create and retain Logger for the root of the namespace.
 399                         owner.addLogger(owner.rootLogger);
 400                         if (!owner.rootLogger.isLevelInitialized()) {
 401                             owner.rootLogger.setLevel(defaultLevel);
 402                         }
 403 
 404                         // Adding the global Logger.
 405                         // Do not call Logger.getGlobal() here as this might trigger
 406                         // subtle inter-dependency issues.
 407                         @SuppressWarnings("deprecation")
 408                         final Logger global = Logger.global;
 409 
 410                         // Make sure the global logger will be registered in the
 411                         // global manager
 412                         owner.addLogger(global);
 413                         return null;
 414                     }
 415                 });
 416             } finally {
 417                 initializationDone = true;
 418             }
 419         } finally {
 420             configurationLock.unlock();
 421         }
 422     }
 423 
 424     /**
 425      * Returns the global LogManager object.
 426      * @return the global LogManager object
 427      */
 428     public static LogManager getLogManager() {
 429         if (manager != null) {
 430             manager.ensureLogManagerInitialized();
 431         }
 432         return manager;
 433     }
 434 
 435     private void readPrimordialConfiguration() { // must be called while holding configurationLock
 436         if (!readPrimordialConfiguration) {
 437             // If System.in/out/err are null, it's a good
 438             // indication that we're still in the
 439             // bootstrapping phase
 440             if (System.out == null) {
 441                 return;
 442             }
 443             readPrimordialConfiguration = true;
 444             try {
 445                 readConfiguration();
 446 
 447                 // Platform loggers begin to delegate to java.util.logging.Logger
 448                 jdk.internal.logger.BootstrapLogger.redirectTemporaryLoggers();
 449 
 450             } catch (Exception ex) {
 451                 assert false : "Exception raised while reading logging configuration: " + ex;
 452             }
 453         }
 454     }
 455 
 456     // LoggerContext maps from AppContext
 457     private WeakHashMap<Object, LoggerContext> contextsMap = null;
 458 
 459     // Returns the LoggerContext for the user code (i.e. application or AppContext).
 460     // Loggers are isolated from each AppContext.
 461     private LoggerContext getUserContext() {
 462         LoggerContext context = null;
 463 
 464         SecurityManager sm = System.getSecurityManager();
 465         JavaAWTAccess javaAwtAccess = SharedSecrets.getJavaAWTAccess();
 466         if (sm != null && javaAwtAccess != null) {
 467             // for each applet, it has its own LoggerContext isolated from others
 468             final Object ecx = javaAwtAccess.getAppletContext();
 469             if (ecx != null) {
 470                 synchronized (javaAwtAccess) {
 471                     // find the AppContext of the applet code
 472                     // will be null if we are in the main app context.
 473                     if (contextsMap == null) {
 474                         contextsMap = new WeakHashMap<>();
 475                     }
 476                     context = contextsMap.get(ecx);
 477                     if (context == null) {
 478                         // Create a new LoggerContext for the applet.
 479                         context = new LoggerContext();
 480                         contextsMap.put(ecx, context);
 481                     }
 482                 }
 483             }
 484         }
 485         // for standalone app, return userContext
 486         return context != null ? context : userContext;
 487     }
 488 
 489     // The system context.
 490     final LoggerContext getSystemContext() {
 491         return systemContext;
 492     }
 493 
 494     private List<LoggerContext> contexts() {
 495         List<LoggerContext> cxs = new ArrayList<>();
 496         cxs.add(getSystemContext());
 497         cxs.add(getUserContext());
 498         return cxs;
 499     }
 500 
 501     // Find or create a specified logger instance. If a logger has
 502     // already been created with the given name it is returned.
 503     // Otherwise a new logger instance is created and registered
 504     // in the LogManager global namespace.
 505     // This method will always return a non-null Logger object.
 506     // Synchronization is not required here. All synchronization for
 507     // adding a new Logger object is handled by addLogger().
 508     //
 509     // This method must delegate to the LogManager implementation to
 510     // add a new Logger or return the one that has been added previously
 511     // as a LogManager subclass may override the addLogger, getLogger,
 512     // readConfiguration, and other methods.
 513     Logger demandLogger(String name, String resourceBundleName, Class<?> caller) {
 514         final Module module = caller == null ? null : caller.getModule();
 515         return demandLogger(name, resourceBundleName, module);
 516     }
 517 
 518     Logger demandLogger(String name, String resourceBundleName, Module module) {
 519         Logger result = getLogger(name);
 520         if (result == null) {
 521             // only allocate the new logger once
 522             Logger newLogger = new Logger(name, resourceBundleName,
 523                                           module, this, false);
 524             do {
 525                 if (addLogger(newLogger)) {
 526                     // We successfully added the new Logger that we
 527                     // created above so return it without refetching.
 528                     return newLogger;
 529                 }
 530 
 531                 // We didn't add the new Logger that we created above
 532                 // because another thread added a Logger with the same
 533                 // name after our null check above and before our call
 534                 // to addLogger(). We have to refetch the Logger because
 535                 // addLogger() returns a boolean instead of the Logger
 536                 // reference itself. However, if the thread that created
 537                 // the other Logger is not holding a strong reference to
 538                 // the other Logger, then it is possible for the other
 539                 // Logger to be GC'ed after we saw it in addLogger() and
 540                 // before we can refetch it. If it has been GC'ed then
 541                 // we'll just loop around and try again.
 542                 result = getLogger(name);
 543             } while (result == null);
 544         }
 545         return result;
 546     }
 547 
 548     Logger demandSystemLogger(String name, String resourceBundleName, Class<?> caller) {
 549         final Module module = caller == null ? null : caller.getModule();
 550         return demandSystemLogger(name, resourceBundleName, module);
 551     }
 552 
 553     Logger demandSystemLogger(String name, String resourceBundleName, Module module) {
 554         // Add a system logger in the system context's namespace
 555         final Logger sysLogger = getSystemContext()
 556                 .demandLogger(name, resourceBundleName, module);
 557 
 558         // Add the system logger to the LogManager's namespace if not exist
 559         // so that there is only one single logger of the given name.
 560         // System loggers are visible to applications unless a logger of
 561         // the same name has been added.
 562         Logger logger;
 563         do {
 564             // First attempt to call addLogger instead of getLogger
 565             // This would avoid potential bug in custom LogManager.getLogger
 566             // implementation that adds a logger if does not exist
 567             if (addLogger(sysLogger)) {
 568                 // successfully added the new system logger
 569                 logger = sysLogger;
 570             } else {
 571                 logger = getLogger(name);
 572             }
 573         } while (logger == null);
 574 
 575         // LogManager will set the sysLogger's handlers via LogManager.addLogger method.
 576         if (logger != sysLogger) {
 577             // if logger already exists we merge the two logger configurations.
 578             final Logger l = logger;
 579             AccessController.doPrivileged(new PrivilegedAction<Void>() {
 580                 @Override
 581                 public Void run() {
 582                     l.mergeWithSystemLogger(sysLogger);
 583                     return null;
 584                 }
 585             });
 586         }
 587         return sysLogger;
 588     }
 589 
 590     // LoggerContext maintains the logger namespace per context.
 591     // The default LogManager implementation has one system context and user
 592     // context.  The system context is used to maintain the namespace for
 593     // all system loggers and is queried by the system code.  If a system logger
 594     // doesn't exist in the user context, it'll also be added to the user context.
 595     // The user context is queried by the user code and all other loggers are
 596     // added in the user context.
 597     class LoggerContext {
 598         // Table of named Loggers that maps names to Loggers.
 599         private final ConcurrentHashMap<String,LoggerWeakRef> namedLoggers =
 600                 new ConcurrentHashMap<>();
 601         // Tree of named Loggers
 602         private final LogNode root;
 603         private LoggerContext() {
 604             this.root = new LogNode(null, this);
 605         }
 606 
 607 
 608         // Tells whether default loggers are required in this context.
 609         // If true, the default loggers will be lazily added.
 610         final boolean requiresDefaultLoggers() {
 611             final boolean requiresDefaultLoggers = (getOwner() == manager);
 612             if (requiresDefaultLoggers) {
 613                 getOwner().ensureLogManagerInitialized();
 614             }
 615             return requiresDefaultLoggers;
 616         }
 617 
 618         // This context's LogManager.
 619         final LogManager getOwner() {
 620             return LogManager.this;
 621         }
 622 
 623         // This context owner's root logger, which if not null, and if
 624         // the context requires default loggers, will be added to the context
 625         // logger's tree.
 626         final Logger getRootLogger() {
 627             return getOwner().rootLogger;
 628         }
 629 
 630         // The global logger, which if not null, and if
 631         // the context requires default loggers, will be added to the context
 632         // logger's tree.
 633         final Logger getGlobalLogger() {
 634             @SuppressWarnings("deprecation") // avoids initialization cycles.
 635             final Logger global = Logger.global;
 636             return global;
 637         }
 638 
 639         Logger demandLogger(String name, String resourceBundleName, Module module) {
 640             // a LogManager subclass may have its own implementation to add and
 641             // get a Logger.  So delegate to the LogManager to do the work.
 642             final LogManager owner = getOwner();
 643             return owner.demandLogger(name, resourceBundleName, module);
 644         }
 645 
 646 
 647         // Due to subtle deadlock issues getUserContext() no longer
 648         // calls addLocalLogger(rootLogger);
 649         // Therefore - we need to add the default loggers later on.
 650         // Checks that the context is properly initialized
 651         // This is necessary before calling e.g. find(name)
 652         // or getLoggerNames()
 653         //
 654         private void ensureInitialized() {
 655             if (requiresDefaultLoggers()) {
 656                 // Ensure that the root and global loggers are set.
 657                 ensureDefaultLogger(getRootLogger());
 658                 ensureDefaultLogger(getGlobalLogger());
 659             }
 660         }
 661 
 662 
 663         Logger findLogger(String name) {
 664             // Attempt to find logger without locking.
 665             LoggerWeakRef ref = namedLoggers.get(name);
 666             Logger logger = ref == null ? null : ref.get();
 667 
 668             // if logger is not null, then we can return it right away.
 669             // if name is "" or "global" and logger is null
 670             // we need to fall through and check that this context is
 671             // initialized.
 672             // if ref is not null and logger is null we also need to
 673             // fall through.
 674             if (logger != null || (ref == null && !name.isEmpty()
 675                     && !name.equals(Logger.GLOBAL_LOGGER_NAME))) {
 676                 return logger;
 677             }
 678 
 679             // We either found a stale reference, or we were looking for
 680             // "" or "global" and didn't find them.
 681             // Make sure context is initialized (has the default loggers),
 682             // and look up again, cleaning the stale reference if it hasn't
 683             // been cleaned up in between. All this needs to be done inside
 684             // a synchronized block.
 685             synchronized(this) {
 686                 // ensure that this context is properly initialized before
 687                 // looking for loggers.
 688                 ensureInitialized();
 689                 ref = namedLoggers.get(name);
 690                 if (ref == null) {
 691                     return null;
 692                 }
 693                 logger = ref.get();
 694                 if (logger == null) {
 695                     // The namedLoggers map holds stale weak reference
 696                     // to a logger which has been GC-ed.
 697                     ref.dispose();
 698                 }
 699                 return logger;
 700             }
 701         }
 702 
 703         // This method is called before adding a logger to the
 704         // context.
 705         // 'logger' is the context that will be added.
 706         // This method will ensure that the defaults loggers are added
 707         // before adding 'logger'.
 708         //
 709         private void ensureAllDefaultLoggers(Logger logger) {
 710             if (requiresDefaultLoggers()) {
 711                 final String name = logger.getName();
 712                 if (!name.isEmpty()) {
 713                     ensureDefaultLogger(getRootLogger());
 714                     if (!Logger.GLOBAL_LOGGER_NAME.equals(name)) {
 715                         ensureDefaultLogger(getGlobalLogger());
 716                     }
 717                 }
 718             }
 719         }
 720 
 721         private void ensureDefaultLogger(Logger logger) {
 722             // Used for lazy addition of root logger and global logger
 723             // to a LoggerContext.
 724 
 725             // This check is simple sanity: we do not want that this
 726             // method be called for anything else than Logger.global
 727             // or owner.rootLogger.
 728             if (!requiresDefaultLoggers() || logger == null
 729                     || logger != getGlobalLogger() && logger != LogManager.this.rootLogger ) {
 730 
 731                 // the case where we have a non null logger which is neither
 732                 // Logger.global nor manager.rootLogger indicates a serious
 733                 // issue - as ensureDefaultLogger should never be called
 734                 // with any other loggers than one of these two (or null - if
 735                 // e.g manager.rootLogger is not yet initialized)...
 736                 assert logger == null;
 737 
 738                 return;
 739             }
 740 
 741             // Adds the logger if it's not already there.
 742             if (!namedLoggers.containsKey(logger.getName())) {
 743                 // It is important to prevent addLocalLogger to
 744                 // call ensureAllDefaultLoggers when we're in the process
 745                 // off adding one of those default loggers - as this would
 746                 // immediately cause a stack overflow.
 747                 // Therefore we must pass addDefaultLoggersIfNeeded=false,
 748                 // even if requiresDefaultLoggers is true.
 749                 addLocalLogger(logger, false);
 750             }
 751         }
 752 
 753         boolean addLocalLogger(Logger logger) {
 754             // no need to add default loggers if it's not required
 755             return addLocalLogger(logger, requiresDefaultLoggers());
 756         }
 757 
 758         // Add a logger to this context.  This method will only set its level
 759         // and process parent loggers.  It doesn't set its handlers.
 760         synchronized boolean addLocalLogger(Logger logger, boolean addDefaultLoggersIfNeeded) {
 761             // addDefaultLoggersIfNeeded serves to break recursion when adding
 762             // default loggers. If we're adding one of the default loggers
 763             // (we're being called from ensureDefaultLogger()) then
 764             // addDefaultLoggersIfNeeded will be false: we don't want to
 765             // call ensureAllDefaultLoggers again.
 766             //
 767             // Note: addDefaultLoggersIfNeeded can also be false when
 768             //       requiresDefaultLoggers is false - since calling
 769             //       ensureAllDefaultLoggers would have no effect in this case.
 770             if (addDefaultLoggersIfNeeded) {
 771                 ensureAllDefaultLoggers(logger);
 772             }
 773 
 774             final String name = logger.getName();
 775             if (name == null) {
 776                 throw new NullPointerException();
 777             }
 778             LoggerWeakRef ref = namedLoggers.get(name);
 779             if (ref != null) {
 780                 if (ref.get() == null) {
 781                     // It's possible that the Logger was GC'ed after a
 782                     // drainLoggerRefQueueBounded() call above so allow
 783                     // a new one to be registered.
 784                     ref.dispose();
 785                 } else {
 786                     // We already have a registered logger with the given name.
 787                     return false;
 788                 }
 789             }
 790 
 791             // We're adding a new logger.
 792             // Note that we are creating a weak reference here.
 793             final LogManager owner = getOwner();
 794             logger.setLogManager(owner);
 795             ref = owner.new LoggerWeakRef(logger);
 796 
 797             // Apply any initial level defined for the new logger, unless
 798             // the logger's level is already initialized
 799             Level level = owner.getLevelProperty(name + ".level", null);
 800             if (level != null && !logger.isLevelInitialized()) {
 801                 doSetLevel(logger, level);
 802             }
 803 
 804             // instantiation of the handler is done in the LogManager.addLogger
 805             // implementation as a handler class may be only visible to LogManager
 806             // subclass for the custom log manager case
 807             processParentHandlers(logger, name, VisitedLoggers.NEVER);
 808 
 809             // Find the new node and its parent.
 810             LogNode node = getNode(name);
 811             node.loggerRef = ref;
 812             Logger parent = null;
 813             LogNode nodep = node.parent;
 814             while (nodep != null) {
 815                 LoggerWeakRef nodeRef = nodep.loggerRef;
 816                 if (nodeRef != null) {
 817                     parent = nodeRef.get();
 818                     if (parent != null) {
 819                         break;
 820                     }
 821                 }
 822                 nodep = nodep.parent;
 823             }
 824 
 825             if (parent != null) {
 826                 doSetParent(logger, parent);
 827             }
 828             // Walk over the children and tell them we are their new parent.
 829             node.walkAndSetParent(logger);
 830             // new LogNode is ready so tell the LoggerWeakRef about it
 831             ref.setNode(node);
 832 
 833             // Do not publish 'ref' in namedLoggers before the logger tree
 834             // is fully updated - because the named logger will be visible as
 835             // soon as it is published in namedLoggers (findLogger takes
 836             // benefit of the ConcurrentHashMap implementation of namedLoggers
 837             // to avoid synchronizing on retrieval when that is possible).
 838             namedLoggers.put(name, ref);
 839             return true;
 840         }
 841 
 842         void removeLoggerRef(String name, LoggerWeakRef ref) {
 843             namedLoggers.remove(name, ref);
 844         }
 845 
 846         synchronized Enumeration<String> getLoggerNames() {
 847             // ensure that this context is properly initialized before
 848             // returning logger names.
 849             ensureInitialized();
 850             return Collections.enumeration(namedLoggers.keySet());
 851         }
 852 
 853         // If logger.getUseParentHandlers() returns 'true' and any of the logger's
 854         // parents have levels or handlers defined, make sure they are instantiated.
 855         private void processParentHandlers(final Logger logger, final String name,
 856                Predicate<Logger> visited) {
 857             final LogManager owner = getOwner();
 858             AccessController.doPrivileged(new PrivilegedAction<Void>() {
 859                 @Override
 860                 public Void run() {
 861                     if (logger != owner.rootLogger) {
 862                         boolean useParent = owner.getBooleanProperty(name + ".useParentHandlers", true);
 863                         if (!useParent) {
 864                             logger.setUseParentHandlers(false);
 865                         }
 866                     }
 867                     return null;
 868                 }
 869             });
 870 
 871             int ix = 1;
 872             for (;;) {
 873                 int ix2 = name.indexOf('.', ix);
 874                 if (ix2 < 0) {
 875                     break;
 876                 }
 877                 String pname = name.substring(0, ix2);
 878                 if (owner.getProperty(pname + ".level") != null ||
 879                     owner.getProperty(pname + ".handlers") != null) {
 880                     // This pname has a level/handlers definition.
 881                     // Make sure it exists.
 882                     if (visited.test(demandLogger(pname, null, null))) {
 883                         break;
 884                     }
 885                 }
 886                 ix = ix2+1;
 887             }
 888         }
 889 
 890         // Gets a node in our tree of logger nodes.
 891         // If necessary, create it.
 892         LogNode getNode(String name) {
 893             if (name == null || name.equals("")) {
 894                 return root;
 895             }
 896             LogNode node = root;
 897             while (name.length() > 0) {
 898                 int ix = name.indexOf('.');
 899                 String head;
 900                 if (ix > 0) {
 901                     head = name.substring(0, ix);
 902                     name = name.substring(ix + 1);
 903                 } else {
 904                     head = name;
 905                     name = "";
 906                 }
 907                 if (node.children == null) {
 908                     node.children = new HashMap<>();
 909                 }
 910                 LogNode child = node.children.get(head);
 911                 if (child == null) {
 912                     child = new LogNode(node, this);
 913                     node.children.put(head, child);
 914                 }
 915                 node = child;
 916             }
 917             return node;
 918         }
 919     }
 920 
 921     final class SystemLoggerContext extends LoggerContext {
 922         // Add a system logger in the system context's namespace as well as
 923         // in the LogManager's namespace if not exist so that there is only
 924         // one single logger of the given name.  System loggers are visible
 925         // to applications unless a logger of the same name has been added.
 926         @Override
 927         Logger demandLogger(String name, String resourceBundleName,
 928                             Module module) {
 929             Logger result = findLogger(name);
 930             if (result == null) {
 931                 // only allocate the new system logger once
 932                 Logger newLogger = new Logger(name, resourceBundleName,
 933                                               module, getOwner(), true);
 934                 do {
 935                     if (addLocalLogger(newLogger)) {
 936                         // We successfully added the new Logger that we
 937                         // created above so return it without refetching.
 938                         result = newLogger;
 939                     } else {
 940                         // We didn't add the new Logger that we created above
 941                         // because another thread added a Logger with the same
 942                         // name after our null check above and before our call
 943                         // to addLogger(). We have to refetch the Logger because
 944                         // addLogger() returns a boolean instead of the Logger
 945                         // reference itself. However, if the thread that created
 946                         // the other Logger is not holding a strong reference to
 947                         // the other Logger, then it is possible for the other
 948                         // Logger to be GC'ed after we saw it in addLogger() and
 949                         // before we can refetch it. If it has been GC'ed then
 950                         // we'll just loop around and try again.
 951                         result = findLogger(name);
 952                     }
 953                 } while (result == null);
 954             }
 955             return result;
 956         }
 957     }
 958 
 959     // Add new per logger handlers.
 960     // We need to raise privilege here. All our decisions will
 961     // be made based on the logging configuration, which can
 962     // only be modified by trusted code.
 963     private void loadLoggerHandlers(final Logger logger, final String name,
 964                                     final String handlersPropertyName)
 965     {
 966         AccessController.doPrivileged(new PrivilegedAction<Void>() {
 967             @Override
 968             public Void run() {
 969                 setLoggerHandlers(logger, name, handlersPropertyName,
 970                     createLoggerHandlers(name, handlersPropertyName));
 971                 return null;
 972             }
 973         });
 974     }
 975 
 976     private void setLoggerHandlers(final Logger logger, final String name,
 977                                    final String handlersPropertyName,
 978                                    List<Handler> handlers)
 979     {
 980         final boolean ensureCloseOnReset = ! handlers.isEmpty()
 981                     && getBooleanProperty(handlersPropertyName + ".ensureCloseOnReset",true);
 982         int count = 0;
 983         for (Handler hdl : handlers) {
 984             logger.addHandler(hdl);
 985             if (++count == 1 && ensureCloseOnReset) {
 986                 // add this logger to the closeOnResetLoggers list.
 987                 closeOnResetLoggers.addIfAbsent(CloseOnReset.create(logger));
 988             }
 989         }
 990     }
 991 
 992     private List<Handler> createLoggerHandlers(final String name, final String handlersPropertyName)
 993     {
 994         String names[] = parseClassNames(handlersPropertyName);
 995         List<Handler> handlers = new ArrayList<>(names.length);
 996         for (String type : names) {
 997             try {
 998                 @SuppressWarnings("deprecation")
 999                 Object o = ClassLoader.getSystemClassLoader().loadClass(type).newInstance();
1000                 Handler hdl = (Handler) o;
1001                 // Check if there is a property defining the
1002                 // this handler's level.
1003                 String levs = getProperty(type + ".level");
1004                 if (levs != null) {
1005                     Level l = Level.findLevel(levs);
1006                     if (l != null) {
1007                         hdl.setLevel(l);
1008                     } else {
1009                         // Probably a bad level. Drop through.
1010                         System.err.println("Can't set level for " + type);
1011                     }
1012                 }
1013                 // Add this Handler to the logger
1014                 handlers.add(hdl);
1015             } catch (Exception ex) {
1016                 System.err.println("Can't load log handler \"" + type + "\"");
1017                 System.err.println("" + ex);
1018                 ex.printStackTrace();
1019             }
1020         }
1021 
1022         return handlers;
1023     }
1024 
1025 
1026     // loggerRefQueue holds LoggerWeakRef objects for Logger objects
1027     // that have been GC'ed.
1028     private final ReferenceQueue<Logger> loggerRefQueue
1029         = new ReferenceQueue<>();
1030 
1031     // Package-level inner class.
1032     // Helper class for managing WeakReferences to Logger objects.
1033     //
1034     // LogManager.namedLoggers
1035     //     - has weak references to all named Loggers
1036     //     - namedLoggers keeps the LoggerWeakRef objects for the named
1037     //       Loggers around until we can deal with the book keeping for
1038     //       the named Logger that is being GC'ed.
1039     // LogManager.LogNode.loggerRef
1040     //     - has a weak reference to a named Logger
1041     //     - the LogNode will also keep the LoggerWeakRef objects for
1042     //       the named Loggers around; currently LogNodes never go away.
1043     // Logger.kids
1044     //     - has a weak reference to each direct child Logger; this
1045     //       includes anonymous and named Loggers
1046     //     - anonymous Loggers are always children of the rootLogger
1047     //       which is a strong reference; rootLogger.kids keeps the
1048     //       LoggerWeakRef objects for the anonymous Loggers around
1049     //       until we can deal with the book keeping.
1050     //
1051     final class LoggerWeakRef extends WeakReference<Logger> {
1052         private String                name;       // for namedLoggers cleanup
1053         private LogNode               node;       // for loggerRef cleanup
1054         private WeakReference<Logger> parentRef;  // for kids cleanup
1055         private boolean disposed = false;         // avoid calling dispose twice
1056 
1057         LoggerWeakRef(Logger logger) {
1058             super(logger, loggerRefQueue);
1059 
1060             name = logger.getName();  // save for namedLoggers cleanup
1061         }
1062 
1063         // dispose of this LoggerWeakRef object
1064         void dispose() {
1065             // Avoid calling dispose twice. When a Logger is gc'ed, its
1066             // LoggerWeakRef will be enqueued.
1067             // However, a new logger of the same name may be added (or looked
1068             // up) before the queue is drained. When that happens, dispose()
1069             // will be called by addLocalLogger() or findLogger().
1070             // Later when the queue is drained, dispose() will be called again
1071             // for the same LoggerWeakRef. Marking LoggerWeakRef as disposed
1072             // avoids processing the data twice (even though the code should
1073             // now be reentrant).
1074             synchronized(this) {
1075                 // Note to maintainers:
1076                 // Be careful not to call any method that tries to acquire
1077                 // another lock from within this block - as this would surely
1078                 // lead to deadlocks, given that dispose() can be called by
1079                 // multiple threads, and from within different synchronized
1080                 // methods/blocks.
1081                 if (disposed) return;
1082                 disposed = true;
1083             }
1084 
1085             final LogNode n = node;
1086             if (n != null) {
1087                 // n.loggerRef can only be safely modified from within
1088                 // a lock on LoggerContext. removeLoggerRef is already
1089                 // synchronized on LoggerContext so calling
1090                 // n.context.removeLoggerRef from within this lock is safe.
1091                 synchronized (n.context) {
1092                     // if we have a LogNode, then we were a named Logger
1093                     // so clear namedLoggers weak ref to us
1094                     n.context.removeLoggerRef(name, this);
1095                     name = null;  // clear our ref to the Logger's name
1096 
1097                     // LogNode may have been reused - so only clear
1098                     // LogNode.loggerRef if LogNode.loggerRef == this
1099                     if (n.loggerRef == this) {
1100                         n.loggerRef = null;  // clear LogNode's weak ref to us
1101                     }
1102                     node = null;            // clear our ref to LogNode
1103                 }
1104             }
1105 
1106             if (parentRef != null) {
1107                 // this LoggerWeakRef has or had a parent Logger
1108                 Logger parent = parentRef.get();
1109                 if (parent != null) {
1110                     // the parent Logger is still there so clear the
1111                     // parent Logger's weak ref to us
1112                     parent.removeChildLogger(this);
1113                 }
1114                 parentRef = null;  // clear our weak ref to the parent Logger
1115             }
1116         }
1117 
1118         // set the node field to the specified value
1119         void setNode(LogNode node) {
1120             this.node = node;
1121         }
1122 
1123         // set the parentRef field to the specified value
1124         void setParentRef(WeakReference<Logger> parentRef) {
1125             this.parentRef = parentRef;
1126         }
1127     }
1128 
1129     // Package-level method.
1130     // Drain some Logger objects that have been GC'ed.
1131     //
1132     // drainLoggerRefQueueBounded() is called by addLogger() below
1133     // and by Logger.getAnonymousLogger(String) so we'll drain up to
1134     // MAX_ITERATIONS GC'ed Loggers for every Logger we add.
1135     //
1136     // On a WinXP VMware client, a MAX_ITERATIONS value of 400 gives
1137     // us about a 50/50 mix in increased weak ref counts versus
1138     // decreased weak ref counts in the AnonLoggerWeakRefLeak test.
1139     // Here are stats for cleaning up sets of 400 anonymous Loggers:
1140     //   - test duration 1 minute
1141     //   - sample size of 125 sets of 400
1142     //   - average: 1.99 ms
1143     //   - minimum: 0.57 ms
1144     //   - maximum: 25.3 ms
1145     //
1146     // The same config gives us a better decreased weak ref count
1147     // than increased weak ref count in the LoggerWeakRefLeak test.
1148     // Here are stats for cleaning up sets of 400 named Loggers:
1149     //   - test duration 2 minutes
1150     //   - sample size of 506 sets of 400
1151     //   - average: 0.57 ms
1152     //   - minimum: 0.02 ms
1153     //   - maximum: 10.9 ms
1154     //
1155     private final static int MAX_ITERATIONS = 400;
1156     final void drainLoggerRefQueueBounded() {
1157         for (int i = 0; i < MAX_ITERATIONS; i++) {
1158             if (loggerRefQueue == null) {
1159                 // haven't finished loading LogManager yet
1160                 break;
1161             }
1162 
1163             LoggerWeakRef ref = (LoggerWeakRef) loggerRefQueue.poll();
1164             if (ref == null) {
1165                 break;
1166             }
1167             // a Logger object has been GC'ed so clean it up
1168             ref.dispose();
1169         }
1170     }
1171 
1172     /**
1173      * Add a named logger.  This does nothing and returns false if a logger
1174      * with the same name is already registered.
1175      * <p>
1176      * The Logger factory methods call this method to register each
1177      * newly created Logger.
1178      * <p>
1179      * The application should retain its own reference to the Logger
1180      * object to avoid it being garbage collected.  The LogManager
1181      * may only retain a weak reference.
1182      *
1183      * @param   logger the new logger.
1184      * @return  true if the argument logger was registered successfully,
1185      *          false if a logger of that name already exists.
1186      * @exception NullPointerException if the logger name is null.
1187      */
1188     public boolean addLogger(Logger logger) {
1189         final String name = logger.getName();
1190         if (name == null) {
1191             throw new NullPointerException();
1192         }
1193         drainLoggerRefQueueBounded();
1194         LoggerContext cx = getUserContext();
1195         if (cx.addLocalLogger(logger)) {
1196             // Do we have a per logger handler too?
1197             // Note: this will add a 200ms penalty
1198             loadLoggerHandlers(logger, name, name + ".handlers");
1199             return true;
1200         } else {
1201             return false;
1202         }
1203     }
1204 
1205     // Private method to set a level on a logger.
1206     // If necessary, we raise privilege before doing the call.
1207     private static void doSetLevel(final Logger logger, final Level level) {
1208         SecurityManager sm = System.getSecurityManager();
1209         if (sm == null) {
1210             // There is no security manager, so things are easy.
1211             logger.setLevel(level);
1212             return;
1213         }
1214         // There is a security manager.  Raise privilege before
1215         // calling setLevel.
1216         AccessController.doPrivileged(new PrivilegedAction<Object>() {
1217             @Override
1218             public Object run() {
1219                 logger.setLevel(level);
1220                 return null;
1221             }});
1222     }
1223 
1224     // Private method to set a parent on a logger.
1225     // If necessary, we raise privilege before doing the setParent call.
1226     private static void doSetParent(final Logger logger, final Logger parent) {
1227         SecurityManager sm = System.getSecurityManager();
1228         if (sm == null) {
1229             // There is no security manager, so things are easy.
1230             logger.setParent(parent);
1231             return;
1232         }
1233         // There is a security manager.  Raise privilege before
1234         // calling setParent.
1235         AccessController.doPrivileged(new PrivilegedAction<Object>() {
1236             @Override
1237             public Object run() {
1238                 logger.setParent(parent);
1239                 return null;
1240             }});
1241     }
1242 
1243     /**
1244      * Method to find a named logger.
1245      * <p>
1246      * Note that since untrusted code may create loggers with
1247      * arbitrary names this method should not be relied on to
1248      * find Loggers for security sensitive logging.
1249      * It is also important to note that the Logger associated with the
1250      * String {@code name} may be garbage collected at any time if there
1251      * is no strong reference to the Logger. The caller of this method
1252      * must check the return value for null in order to properly handle
1253      * the case where the Logger has been garbage collected.
1254      *
1255      * @param name name of the logger
1256      * @return  matching logger or null if none is found
1257      */
1258     public Logger getLogger(String name) {
1259         return getUserContext().findLogger(name);
1260     }
1261 
1262     /**
1263      * Get an enumeration of known logger names.
1264      * <p>
1265      * Note:  Loggers may be added dynamically as new classes are loaded.
1266      * This method only reports on the loggers that are currently registered.
1267      * It is also important to note that this method only returns the name
1268      * of a Logger, not a strong reference to the Logger itself.
1269      * The returned String does nothing to prevent the Logger from being
1270      * garbage collected. In particular, if the returned name is passed
1271      * to {@code LogManager.getLogger()}, then the caller must check the
1272      * return value from {@code LogManager.getLogger()} for null to properly
1273      * handle the case where the Logger has been garbage collected in the
1274      * time since its name was returned by this method.
1275      *
1276      * @return  enumeration of logger name strings
1277      */
1278     public Enumeration<String> getLoggerNames() {
1279         return getUserContext().getLoggerNames();
1280     }
1281 
1282     /**
1283      * Reads and initializes the logging configuration.
1284      * <p>
1285      * If the "java.util.logging.config.class" system property is set, then the
1286      * property value is treated as a class name.  The given class will be
1287      * loaded, an object will be instantiated, and that object's constructor
1288      * is responsible for reading in the initial configuration.  (That object
1289      * may use other system properties to control its configuration.)  The
1290      * alternate configuration class can use {@code readConfiguration(InputStream)}
1291      * to define properties in the LogManager.
1292      * <p>
1293      * If "java.util.logging.config.class" system property is <b>not</b> set,
1294      * then this method will read the initial configuration from a properties
1295      * file and calls the {@link #readConfiguration(InputStream)} method to initialize
1296      * the configuration. The "java.util.logging.config.file" system property can be used
1297      * to specify the properties file that will be read as the initial configuration;
1298      * if not set, then the LogManager default configuration is used.
1299      * The default configuration is typically loaded from the
1300      * properties file "{@code conf/logging.properties}" in the Java installation
1301      * directory.
1302      *
1303      * <p>
1304      * Any {@linkplain #addConfigurationListener registered configuration
1305      * listener} will be invoked after the properties are read.
1306      *
1307      * @apiNote This {@code readConfiguration} method should only be used for
1308      * initializing the configuration during LogManager initialization or
1309      * used with the "java.util.logging.config.class" property.
1310      * When this method is called after loggers have been created, and
1311      * the "java.util.logging.config.class" system property is not set, all
1312      * existing loggers will be {@linkplain #reset() reset}. Then any
1313      * existing loggers that have a level property specified in the new
1314      * configuration stream will be {@linkplain
1315      * Logger#setLevel(java.util.logging.Level) set} to the specified log level.
1316      * <p>
1317      * To properly update the logging configuration, use the
1318      * {@link #updateConfiguration(java.util.function.Function)} or
1319      * {@link #updateConfiguration(java.io.InputStream, java.util.function.Function)}
1320      * methods instead.
1321      *
1322      * @throws   SecurityException  if a security manager exists and if
1323      *              the caller does not have LoggingPermission("control").
1324      * @throws   IOException if there are IO problems reading the configuration.
1325      */
1326     public void readConfiguration() throws IOException, SecurityException {
1327         checkPermission();
1328 
1329         // if a configuration class is specified, load it and use it.
1330         String cname = System.getProperty("java.util.logging.config.class");
1331         if (cname != null) {
1332             try {
1333                 // Instantiate the named class.  It is its constructor's
1334                 // responsibility to initialize the logging configuration, by
1335                 // calling readConfiguration(InputStream) with a suitable stream.
1336                 try {
1337                     Class<?> clz = ClassLoader.getSystemClassLoader().loadClass(cname);
1338                     @SuppressWarnings("deprecation")
1339                     Object witness = clz.newInstance();
1340                     return;
1341                 } catch (ClassNotFoundException ex) {
1342                     Class<?> clz = Thread.currentThread().getContextClassLoader().loadClass(cname);
1343                     @SuppressWarnings("deprecation")
1344                     Object witness = clz.newInstance();
1345                     return;
1346                 }
1347             } catch (Exception ex) {
1348                 System.err.println("Logging configuration class \"" + cname + "\" failed");
1349                 System.err.println("" + ex);
1350                 // keep going and useful config file.
1351             }
1352         }
1353 
1354         String fname = getConfigurationFileName();
1355         try (final InputStream in = new FileInputStream(fname)) {
1356             final BufferedInputStream bin = new BufferedInputStream(in);
1357             readConfiguration(bin);
1358         }
1359     }
1360 
1361     String getConfigurationFileName() throws IOException {
1362         String fname = System.getProperty("java.util.logging.config.file");
1363         if (fname == null) {
1364             fname = System.getProperty("java.home");
1365             if (fname == null) {
1366                 throw new Error("Can't find java.home ??");
1367             }
1368             fname = Paths.get(fname, "conf", "logging.properties")
1369                     .toAbsolutePath().normalize().toString();
1370         }
1371         return fname;
1372     }
1373 
1374     /**
1375      * Reset the logging configuration.
1376      * <p>
1377      * For all named loggers, the reset operation removes and closes
1378      * all Handlers and (except for the root logger) sets the level
1379      * to {@code null}. The root logger's level is set to {@code Level.INFO}.
1380      *
1381      * @apiNote Calling this method also clears the LogManager {@linkplain
1382      * #getProperty(java.lang.String) properties}. The {@link
1383      * #updateConfiguration(java.util.function.Function)
1384      * updateConfiguration(Function)} or
1385      * {@link #updateConfiguration(java.io.InputStream, java.util.function.Function)
1386      * updateConfiguration(InputStream, Function)} method can be used to
1387      * properly update to a new configuration.
1388      *
1389      * @throws  SecurityException  if a security manager exists and if
1390      *             the caller does not have LoggingPermission("control").
1391      */
1392 
1393     public void reset() throws SecurityException {
1394         checkPermission();
1395 
1396         List<CloseOnReset> persistent;
1397 
1398         // We don't want reset() and readConfiguration()
1399         // to run in parallel
1400         configurationLock.lock();
1401         try {
1402             // install new empty properties
1403             props = new Properties();
1404             // make sure we keep the loggers persistent until reset is done.
1405             // Those are the loggers for which we previously created a
1406             // handler from the configuration, and we need to prevent them
1407             // from being gc'ed until those handlers are closed.
1408             persistent = new ArrayList<>(closeOnResetLoggers);
1409             closeOnResetLoggers.clear();
1410 
1411             // if reset has been called from shutdown-hook (Cleaner),
1412             // or if reset has been called from readConfiguration() which
1413             // already holds the lock and will change the state itself,
1414             // then do not change state here...
1415             if (globalHandlersState != STATE_SHUTDOWN &&
1416                 globalHandlersState != STATE_READING_CONFIG) {
1417                 // ...else user called reset()...
1418                 // Since we are doing a reset we no longer want to initialize
1419                 // the global handlers, if they haven't been initialized yet.
1420                 globalHandlersState = STATE_INITIALIZED;
1421             }
1422 
1423             for (LoggerContext cx : contexts()) {
1424                 resetLoggerContext(cx);
1425             }
1426 
1427             persistent.clear();
1428         } finally {
1429             configurationLock.unlock();
1430         }
1431     }
1432 
1433     private void resetLoggerContext(LoggerContext cx) {
1434         Enumeration<String> enum_ = cx.getLoggerNames();
1435         while (enum_.hasMoreElements()) {
1436             String name = enum_.nextElement();
1437             Logger logger = cx.findLogger(name);
1438             if (logger != null) {
1439                 resetLogger(logger);
1440             }
1441         }
1442     }
1443 
1444     private void closeHandlers(Logger logger) {
1445         Handler[] targets = logger.getHandlers();
1446         for (Handler h : targets) {
1447             logger.removeHandler(h);
1448             try {
1449                 h.close();
1450             } catch (Exception ex) {
1451                 // Problems closing a handler?  Keep going...
1452             } catch (Error e) {
1453                 // ignore Errors while shutting down
1454                 if (globalHandlersState != STATE_SHUTDOWN) {
1455                     throw e;
1456                 }
1457             }
1458         }
1459     }
1460 
1461     // Private method to reset an individual target logger.
1462     private void resetLogger(Logger logger) {
1463         // Close all the Logger handlers.
1464         closeHandlers(logger);
1465 
1466         // Reset Logger level
1467         String name = logger.getName();
1468         if (name != null && name.equals("")) {
1469             // This is the root logger.
1470             logger.setLevel(defaultLevel);
1471         } else {
1472             logger.setLevel(null);
1473         }
1474     }
1475 
1476     // get a list of whitespace separated classnames from a property.
1477     private String[] parseClassNames(String propertyName) {
1478         String hands = getProperty(propertyName);
1479         if (hands == null) {
1480             return new String[0];
1481         }
1482         hands = hands.trim();
1483         int ix = 0;
1484         final List<String> result = new ArrayList<>();
1485         while (ix < hands.length()) {
1486             int end = ix;
1487             while (end < hands.length()) {
1488                 if (Character.isWhitespace(hands.charAt(end))) {
1489                     break;
1490                 }
1491                 if (hands.charAt(end) == ',') {
1492                     break;
1493                 }
1494                 end++;
1495             }
1496             String word = hands.substring(ix, end);
1497             ix = end+1;
1498             word = word.trim();
1499             if (word.length() == 0) {
1500                 continue;
1501             }
1502             result.add(word);
1503         }
1504         return result.toArray(new String[result.size()]);
1505     }
1506 
1507     /**
1508      * Reads and initializes the logging configuration from the given input stream.
1509      *
1510      * <p>
1511      * Any {@linkplain #addConfigurationListener registered configuration
1512      * listener} will be invoked after the properties are read.
1513      *
1514      * @apiNote This {@code readConfiguration} method should only be used for
1515      * initializing the configuration during LogManager initialization or
1516      * used with the "java.util.logging.config.class" property.
1517      * When this method is called after loggers have been created, all
1518      * existing loggers will be {@linkplain #reset() reset}. Then any
1519      * existing loggers that have a level property specified in the
1520      * given input stream will be {@linkplain
1521      * Logger#setLevel(java.util.logging.Level) set} to the specified log level.
1522      * <p>
1523      * To properly update the logging configuration, use the
1524      * {@link #updateConfiguration(java.util.function.Function)} or
1525      * {@link #updateConfiguration(java.io.InputStream, java.util.function.Function)}
1526      * method instead.
1527      *
1528      * @param ins  stream to read properties from
1529      * @throws  SecurityException  if a security manager exists and if
1530      *             the caller does not have LoggingPermission("control").
1531      * @throws  IOException if there are problems reading from the stream,
1532      *             or the given stream is not in the
1533      *             {@linkplain java.util.Properties properties file} format.
1534      */
1535     public void readConfiguration(InputStream ins) throws IOException, SecurityException {
1536         checkPermission();
1537 
1538         // We don't want reset() and readConfiguration() to run
1539         // in parallel.
1540         configurationLock.lock();
1541         try {
1542             if (globalHandlersState == STATE_SHUTDOWN) {
1543                 // already in terminal state: don't even bother
1544                 // to read the configuration
1545                 return;
1546             }
1547 
1548             // change state to STATE_READING_CONFIG to signal reset() to not change it
1549             globalHandlersState = STATE_READING_CONFIG;
1550             try {
1551                 // reset configuration which leaves globalHandlersState at STATE_READING_CONFIG
1552                 // so that while reading configuration, any ongoing logging requests block and
1553                 // wait for the outcome (see the end of this try statement)
1554                 reset();
1555 
1556                 try {
1557                     // Load the properties
1558                     props.load(ins);
1559                 } catch (IllegalArgumentException x) {
1560                     // props.load may throw an IllegalArgumentException if the stream
1561                     // contains malformed Unicode escape sequences.
1562                     // We wrap that in an IOException as readConfiguration is
1563                     // specified to throw IOException if there are problems reading
1564                     // from the stream.
1565                     // Note: new IOException(x.getMessage(), x) allow us to get a more
1566                     // concise error message than new IOException(x);
1567                     throw new IOException(x.getMessage(), x);
1568                 }
1569 
1570                 // Instantiate new configuration objects.
1571                 String names[] = parseClassNames("config");
1572 
1573                 for (String word : names) {
1574                     try {
1575                         Class<?> clz = ClassLoader.getSystemClassLoader().loadClass(word);
1576                         @SuppressWarnings("deprecation")
1577                         Object witness = clz.newInstance();
1578                     } catch (Exception ex) {
1579                         System.err.println("Can't load config class \"" + word + "\"");
1580                         System.err.println("" + ex);
1581                         // ex.printStackTrace();
1582                     }
1583                 }
1584 
1585                 // Set levels on any pre-existing loggers, based on the new properties.
1586                 setLevelsOnExistingLoggers();
1587 
1588                 // Note that we need to reinitialize global handles when
1589                 // they are first referenced.
1590                 globalHandlersState = STATE_UNINITIALIZED;
1591             } catch (Throwable t) {
1592                 // If there were any trouble, then set state to STATE_INITIALIZED
1593                 // so that no global handlers reinitialization is performed on not fully
1594                 // initialized configuration.
1595                 globalHandlersState = STATE_INITIALIZED;
1596                 // re-throw
1597                 throw t;
1598             }
1599         } finally {
1600             configurationLock.unlock();
1601         }
1602 
1603         // should be called out of lock to avoid dead-lock situations
1604         // when user code is involved
1605         invokeConfigurationListeners();
1606     }
1607 
1608     // This enum enumerate the configuration properties that will be
1609     // updated on existing loggers when the configuration is updated
1610     // with LogManager.updateConfiguration().
1611     //
1612     // Note that this works properly only for the global LogManager - as
1613     // Handler and its subclasses get their configuration from
1614     // LogManager.getLogManager().
1615     //
1616     static enum ConfigProperty {
1617         LEVEL(".level"), HANDLERS(".handlers"), USEPARENT(".useParentHandlers");
1618         final String suffix;
1619         final int length;
1620         private ConfigProperty(String suffix) {
1621             this.suffix = Objects.requireNonNull(suffix);
1622             length = suffix.length();
1623         }
1624 
1625         public boolean handleKey(String key) {
1626             if (this == HANDLERS && suffix.substring(1).equals(key)) return true;
1627             if (this == HANDLERS && suffix.equals(key)) return false;
1628             return key.endsWith(suffix);
1629         }
1630         String key(String loggerName) {
1631             if (this == HANDLERS && (loggerName == null || loggerName.isEmpty())) {
1632                 return suffix.substring(1);
1633             }
1634             return loggerName + suffix;
1635         }
1636         String loggerName(String key) {
1637             assert key.equals(suffix.substring(1)) && this == HANDLERS || key.endsWith(suffix);
1638             if (this == HANDLERS && suffix.substring(1).equals(key)) return "";
1639             return key.substring(0, key.length() - length);
1640         }
1641 
1642         /**
1643          * If the property is one that should be updated on existing loggers by
1644          * updateConfiguration, returns the name of the logger for which the
1645          * property is configured. Otherwise, returns null.
1646          * @param property a property key in 'props'
1647          * @return the name of the logger on which the property is to be set,
1648          *         if the property is one that should be updated on existing
1649          *         loggers, {@code null} otherwise.
1650          */
1651         static String getLoggerName(String property) {
1652             for (ConfigProperty p : ConfigProperty.ALL) {
1653                 if (p.handleKey(property)) {
1654                     return p.loggerName(property);
1655                 }
1656             }
1657             return null; // Not a property that should be updated.
1658         }
1659 
1660         /**
1661          * Find the ConfigProperty corresponding to the given
1662          * property key (may find none).
1663          * @param property a property key in 'props'
1664          * @return An optional containing a ConfigProperty object,
1665          *         if the property is one that should be updated on existing
1666          *         loggers, empty otherwise.
1667          */
1668         static Optional<ConfigProperty> find(String property) {
1669             return ConfigProperty.ALL.stream()
1670                     .filter(p -> p.handleKey(property))
1671                     .findFirst();
1672          }
1673 
1674         /**
1675          * Returns true if the given property is one that should be updated
1676          * on existing loggers.
1677          * Used to filter property name streams.
1678          * @param property a property key from the configuration.
1679          * @return true if this property is of interest for updateConfiguration.
1680          */
1681         static boolean matches(String property) {
1682             return find(property).isPresent();
1683         }
1684 
1685         /**
1686          * Returns true if the new property value is different from the old,
1687          * and therefore needs to be updated on existing loggers.
1688          * @param k a property key in the configuration
1689          * @param previous the old configuration
1690          * @param next the new configuration
1691          * @return true if the property is changing value between the two
1692          *         configurations.
1693          */
1694         static boolean needsUpdating(String k, Properties previous, Properties next) {
1695             final String p = trim(previous.getProperty(k, null));
1696             final String n = trim(next.getProperty(k, null));
1697             return ! Objects.equals(p,n);
1698         }
1699 
1700         /**
1701          * Applies the mapping function for the given key to the next
1702          * configuration.
1703          * If the mapping function is null then this method does nothing.
1704          * Otherwise, it calls the mapping function to compute the value
1705          * that should be associated with {@code key} in the resulting
1706          * configuration, and applies it to {@code next}.
1707          * If the mapping function returns {@code null} the key is removed
1708          * from {@code next}.
1709          *
1710          * @param k a property key in the configuration
1711          * @param previous the old configuration
1712          * @param next the new configuration (modified by this function)
1713          * @param remappingFunction the mapping function.
1714          */
1715         static void merge(String k, Properties previous, Properties next,
1716                           BiFunction<String, String, String> mappingFunction) {
1717             String p = trim(previous.getProperty(k, null));
1718             String n = trim(next.getProperty(k, null));
1719             String mapped = trim(mappingFunction.apply(p,n));
1720             if (!Objects.equals(n, mapped)) {
1721                 if (mapped == null) {
1722                     next.remove(k);
1723                 } else {
1724                     next.setProperty(k, mapped);
1725                 }
1726             }
1727         }
1728 
1729         private static final EnumSet<ConfigProperty> ALL =
1730                 EnumSet.allOf(ConfigProperty.class);
1731     }
1732 
1733     // trim the value if not null.
1734     private static String trim(String value) {
1735         return value == null ? null : value.trim();
1736     }
1737 
1738     /**
1739      * An object that keep track of loggers we have already visited.
1740      * Used when updating configuration, to avoid processing the same logger
1741      * twice.
1742      */
1743     static final class VisitedLoggers implements Predicate<Logger> {
1744         final IdentityHashMap<Logger,Boolean> visited;
1745         private VisitedLoggers(IdentityHashMap<Logger,Boolean> visited) {
1746             this.visited = visited;
1747         }
1748         VisitedLoggers() {
1749             this(new IdentityHashMap<>());
1750         }
1751         @Override
1752         public boolean test(Logger logger) {
1753             return visited != null && visited.put(logger, Boolean.TRUE) != null;
1754         }
1755         public void clear() {
1756             if (visited != null) visited.clear();
1757         }
1758 
1759         // An object that considers that no logger has ever been visited.
1760         // This is used when processParentHandlers is called from
1761         // LoggerContext.addLocalLogger
1762         static final VisitedLoggers NEVER = new VisitedLoggers(null);
1763     }
1764 
1765 
1766     /**
1767      * Type of the modification for a given property. One of SAME, ADDED, CHANGED,
1768      * or REMOVED.
1769      */
1770     static enum ModType {
1771         SAME,    // property had no value in the old and new conf, or had the
1772                  // same value in both.
1773         ADDED,   // property had no value in the old conf, but has one in the new.
1774         CHANGED, // property has a different value in the old conf and the new conf.
1775         REMOVED; // property has no value in the new conf, but had one in the old.
1776         static ModType of(String previous, String next) {
1777             if (previous == null && next != null) {
1778                 return ADDED;
1779             }
1780             if (next == null && previous != null) {
1781                 return REMOVED;
1782             }
1783             if (!Objects.equals(trim(previous), trim(next))) {
1784                 return CHANGED;
1785             }
1786             return SAME;
1787         }
1788     }
1789 
1790     /**
1791      * Updates the logging configuration.
1792      * <p>
1793      * If the "java.util.logging.config.file" system property is set,
1794      * then the property value specifies the properties file to be read
1795      * as the new configuration. Otherwise, the LogManager default
1796      * configuration is used.
1797      * <br>The default configuration is typically loaded from the
1798      * properties file "{@code conf/logging.properties}" in the
1799      * Java installation directory.
1800      * <p>
1801      * This method reads the new configuration and calls the {@link
1802      * #updateConfiguration(java.io.InputStream, java.util.function.Function)
1803      * updateConfiguration(ins, mapper)} method to
1804      * update the configuration.
1805      *
1806      * @apiNote
1807      * This method updates the logging configuration from reading
1808      * a properties file and ignores the "java.util.logging.config.class"
1809      * system property.  The "java.util.logging.config.class" property is
1810      * only used by the {@link #readConfiguration()}  method to load a custom
1811      * configuration class as an initial configuration.
1812      *
1813      * @param mapper a functional interface that takes a configuration
1814      *   key <i>k</i> and returns a function <i>f(o,n)</i> whose returned
1815      *   value will be applied to the resulting configuration. The
1816      *   function <i>f</i> may return {@code null} to indicate that the property
1817      *   <i>k</i> will not be added to the resulting configuration.
1818      *   <br>
1819      *   If {@code mapper} is {@code null} then {@code (k) -> ((o, n) -> n)} is
1820      *   assumed.
1821      *   <br>
1822      *   For each <i>k</i>, the mapped function <i>f</i> will
1823      *   be invoked with the value associated with <i>k</i> in the old
1824      *   configuration (i.e <i>o</i>) and the value associated with
1825      *   <i>k</i> in the new configuration (i.e. <i>n</i>).
1826      *   <br>A {@code null} value for <i>o</i> or <i>n</i> indicates that no
1827      *   value was present for <i>k</i> in the corresponding configuration.
1828      *
1829      * @throws  SecurityException  if a security manager exists and if
1830      *          the caller does not have LoggingPermission("control"), or
1831      *          does not have the permissions required to set up the
1832      *          configuration (e.g. open file specified for FileHandlers
1833      *          etc...)
1834      *
1835      * @throws  NullPointerException  if {@code mapper} returns a {@code null}
1836      *         function when invoked.
1837      *
1838      * @throws  IOException if there are problems reading from the
1839      *          logging configuration file.
1840      *
1841      * @see #updateConfiguration(java.io.InputStream, java.util.function.Function)

1842      */
1843     public void updateConfiguration(Function<String, BiFunction<String,String,String>> mapper)
1844             throws IOException {
1845         checkPermission();
1846         ensureLogManagerInitialized();
1847         drainLoggerRefQueueBounded();
1848 
1849         String fname = getConfigurationFileName();
1850         try (final InputStream in = new FileInputStream(fname)) {
1851             final BufferedInputStream bin = new BufferedInputStream(in);
1852             updateConfiguration(bin, mapper);
1853         }
1854     }
1855 
1856     /**
1857      * Updates the logging configuration.
1858      * <p>
1859      * For each configuration key in the {@linkplain
1860      * #getProperty(java.lang.String) existing configuration} and
1861      * the given input stream configuration, the given {@code mapper} function
1862      * is invoked to map from the configuration key to a function,
1863      * <i>f(o,n)</i>, that takes the old value and new value and returns
1864      * the resulting value to be applied in the resulting configuration,
1865      * as specified in the table below.
1866      * <p>Let <i>k</i> be a configuration key in the old or new configuration,
1867      * <i>o</i> be the old value (i.e. the value associated
1868      * with <i>k</i> in the old configuration), <i>n</i> be the
1869      * new value (i.e. the value associated with <i>k</i> in the new
1870      * configuration), and <i>f</i> be the function returned
1871      * by {@code mapper.apply(}<i>k</i>{@code )}: then <i>v = f(o,n)</i> is the
1872      * resulting value. If <i>v</i> is not {@code null}, then a property
1873      * <i>k</i> with value <i>v</i> will be added to the resulting configuration.
1874      * Otherwise, it will be omitted.
1875      * <br>A {@code null} value may be passed to function
1876      * <i>f</i> to indicate that the corresponding configuration has no
1877      * configuration key <i>k</i>.
1878      * The function <i>f</i> may return {@code null} to indicate that
1879      * there will be no value associated with <i>k</i> in the resulting
1880      * configuration.
1881      * <p>
1882      * If {@code mapper} is {@code null}, then <i>v</i> will be set to
1883      * <i>n</i>.
1884      * <p>
1885      * LogManager {@linkplain #getProperty(java.lang.String) properties} are
1886      * updated with the resulting value in the resulting configuration.
1887      * <p>
1888      * The registered {@linkplain #addConfigurationListener configuration
1889      * listeners} will be invoked after the configuration is successfully updated.
1890      * <br><br>
1891      * <table summary="Updating configuration properties">
1892      * <tr>
1893      * <th>Property</th>
1894      * <th>Resulting Behavior</th>
1895      * </tr>
1896      * <tr>
1897      * <td valign="top">{@code <logger>.level}</td>
1898      * <td>
1899      * <ul>
1900      *   <li>If the resulting configuration defines a level for a logger and
1901      *       if the resulting level is different than the level specified in the
1902      *       the old configuration, or not specified in
1903      *       the old configuration, then if the logger exists or if children for
1904      *       that logger exist, the level for that logger will be updated,
1905      *       and the change propagated to any existing logger children.
1906      *       This may cause the logger to be created, if necessary.
1907      *   </li>
1908      *   <li>If the old configuration defined a level for a logger, and the
1909      *       resulting configuration doesn't, then this change will not be
1910      *       propagated to existing loggers, if any.
1911      *       To completely replace a configuration - the caller should therefore
1912      *       call {@link #reset() reset} to empty the current configuration,
1913      *       before calling {@code updateConfiguration}.
1914      *   </li>
1915      * </ul>
1916      * </td>
1917      * <tr>
1918      * <td valign="top">{@code <logger>.useParentHandlers}</td>
1919      * <td>
1920      * <ul>
1921      *   <li>If either the resulting or the old value for the useParentHandlers
1922      *       property is not null, then if the logger exists or if children for
1923      *       that logger exist, that logger will be updated to the resulting
1924      *       value.
1925      *       The value of the useParentHandlers property is the value specified
1926      *       in the configuration; if not specified, the default is true.
1927      *   </li>
1928      * </ul>
1929      * </td>
1930      * </tr>
1931      * <tr>
1932      * <td valign="top">{@code <logger>.handlers}</td>
1933      * <td>
1934      * <ul>
1935      *   <li>If the resulting configuration defines a list of handlers for a
1936      *       logger, and if the resulting list is different than the list
1937      *       specified in the old configuration for that logger (that could be
1938      *       empty), then if the logger exists or its children exist, the
1939      *       handlers associated with that logger are closed and removed and
1940      *       the new handlers will be created per the resulting configuration
1941      *       and added to that logger, creating that logger if necessary.
1942      *   </li>
1943      *   <li>If the old configuration defined some handlers for a logger, and
1944      *       the resulting configuration doesn't, if that logger exists,
1945      *       its handlers will be removed and closed.
1946      *   </li>
1947      *   <li>Changing the list of handlers on an existing logger will cause all
1948      *       its previous handlers to be removed and closed, regardless of whether
1949      *       they had been created from the configuration or programmatically.
1950      *       The old handlers will be replaced by new handlers, if any.
1951      *   </li>
1952      * </ul>
1953      * </td>
1954      * </tr>
1955      * <tr>
1956      * <td valign="top">{@code <handler-name>.*}</td>
1957      * <td>
1958      * <ul>
1959      *   <li>Properties configured/changed on handler classes will only affect
1960      *       newly created handlers. If a node is configured with the same list
1961      *       of handlers in the old and the resulting configuration, then these
1962      *       handlers will remain unchanged.
1963      *   </li>
1964      * </ul>
1965      * </td>
1966      * </tr>
1967      * <tr>
1968      * <td valign="top">{@code config} and any other property</td>
1969      * <td>
1970      * <ul>
1971      *   <li>The resulting value for these property will be stored in the
1972      *   LogManager properties, but {@code updateConfiguration} will not parse
1973      *   or process their values.
1974      *   </li>
1975      * </ul>
1976      * </td>
1977      * </tr>
1978      * </table>
1979      * <p>
1980      * <em>Example mapper functions:</em>
1981      * <br><br>
1982      * <ul>
1983      * <li>Replace all logging properties with the new configuration:
1984      * <br><br>{@code     (k) -> ((o, n) -> n)}:
1985      * <br><br>this is equivalent to passing a null {@code mapper} parameter.
1986      * </li>
1987      * <li>Merge the new configuration and old configuration and use the
1988      * new value if <i>k</i> exists in the new configuration:
1989      * <br><br>{@code     (k) -> ((o, n) -> n == null ? o : n)}:
1990      * <br><br>as if merging two collections as follows:
1991      * {@code result.putAll(oldc); result.putAll(newc)}.<br></li>
1992      * <li>Merge the new configuration and old configuration and use the old
1993      * value if <i>k</i> exists in the old configuration:
1994      * <br><br>{@code     (k) -> ((o, n) -> o == null ? n : o)}:
1995      * <br><br>as if merging two collections as follows:
1996      * {@code result.putAll(newc); result.putAll(oldc)}.<br></li>
1997      * <li>Replace all properties with the new configuration except the handler
1998      * property to configure Logger's handler that is not root logger:
1999      * <br>
2000      * <pre>{@code (k) -> k.endsWith(".handlers")}
2001      *      {@code     ? ((o, n) -> (o == null ? n : o))}
2002      *      {@code     : ((o, n) -> n)}</pre>
2003      * </li>
2004      * </ul>
2005      * <p>
2006      * To completely reinitialize a configuration, an application can first call
2007      * {@link #reset() reset} to fully remove the old configuration, followed by
2008      * {@code updateConfiguration} to initialize the new configuration.
2009      *
2010      * @param ins    a stream to read properties from
2011      * @param mapper a functional interface that takes a configuration
2012      *   key <i>k</i> and returns a function <i>f(o,n)</i> whose returned
2013      *   value will be applied to the resulting configuration. The
2014      *   function <i>f</i> may return {@code null} to indicate that the property
2015      *   <i>k</i> will not be added to the resulting configuration.
2016      *   <br>
2017      *   If {@code mapper} is {@code null} then {@code (k) -> ((o, n) -> n)} is
2018      *   assumed.
2019      *   <br>
2020      *   For each <i>k</i>, the mapped function <i>f</i> will
2021      *   be invoked with the value associated with <i>k</i> in the old
2022      *   configuration (i.e <i>o</i>) and the value associated with
2023      *   <i>k</i> in the new configuration (i.e. <i>n</i>).
2024      *   <br>A {@code null} value for <i>o</i> or <i>n</i> indicates that no
2025      *   value was present for <i>k</i> in the corresponding configuration.
2026      *
2027      * @throws  SecurityException if a security manager exists and if
2028      *          the caller does not have LoggingPermission("control"), or
2029      *          does not have the permissions required to set up the
2030      *          configuration (e.g. open files specified for FileHandlers)
2031      *
2032      * @throws  NullPointerException if {@code ins} is null or if
2033      *          {@code mapper} returns a null function when invoked.
2034      *
2035      * @throws  IOException if there are problems reading from the stream,
2036      *          or the given stream is not in the
2037      *          {@linkplain java.util.Properties properties file} format.

2038      */
2039     public void updateConfiguration(InputStream ins,
2040             Function<String, BiFunction<String,String,String>> mapper)
2041             throws IOException {
2042         checkPermission();
2043         ensureLogManagerInitialized();
2044         drainLoggerRefQueueBounded();
2045 
2046         final Properties previous;
2047         final Set<String> updatePropertyNames;
2048         List<LoggerContext> cxs = Collections.emptyList();
2049         final VisitedLoggers visited = new VisitedLoggers();
2050         final Properties next = new Properties();
2051 
2052         try {
2053             // Load the properties
2054             next.load(ins);
2055         } catch (IllegalArgumentException x) {
2056             // props.load may throw an IllegalArgumentException if the stream
2057             // contains malformed Unicode escape sequences.
2058             // We wrap that in an IOException as updateConfiguration is
2059             // specified to throw IOException if there are problems reading
2060             // from the stream.
2061             // Note: new IOException(x.getMessage(), x) allow us to get a more
2062             // concise error message than new IOException(x);
2063             throw new IOException(x.getMessage(), x);
2064         }
2065 
2066         if (globalHandlersState == STATE_SHUTDOWN) return;
2067 
2068         // exclusive lock: readConfiguration/reset/updateConfiguration can't
2069         //           run concurrently.
2070         // configurationLock.writeLock().lock();
2071         configurationLock.lock();
2072         try {
2073             if (globalHandlersState == STATE_SHUTDOWN) return;
2074             previous = props;
2075 
2076             // Builds a TreeSet of all (old and new) property names.
2077             updatePropertyNames =
2078                     Stream.concat(previous.stringPropertyNames().stream(),
2079                                   next.stringPropertyNames().stream())
2080                         .collect(Collectors.toCollection(TreeSet::new));
2081 
2082             if (mapper != null) {
2083                 // mapper will potentially modify the content of
2084                 // 'next', so we need to call it before affecting props=next.
2085                 // give a chance to the mapper to control all
2086                 // properties - not just those we will reset.
2087                 updatePropertyNames.stream()
2088                         .forEachOrdered(k -> ConfigProperty
2089                                 .merge(k, previous, next,
2090                                        Objects.requireNonNull(mapper.apply(k))));
2091             }
2092 
2093             props = next;
2094 
2095             // allKeys will contain all keys:
2096             //    - which correspond to a configuration property we are interested in
2097             //      (first filter)
2098             //    - whose value needs to be updated (because it's new, removed, or
2099             //      different) in the resulting configuration (second filter)
2100             final Stream<String> allKeys = updatePropertyNames.stream()
2101                     .filter(ConfigProperty::matches)
2102                     .filter(k -> ConfigProperty.needsUpdating(k, previous, next));
2103 
2104             // Group configuration properties by logger name
2105             // We use a TreeMap so that parent loggers will be visited before
2106             // child loggers.
2107             final Map<String, TreeSet<String>> loggerConfigs =
2108                     allKeys.collect(Collectors.groupingBy(ConfigProperty::getLoggerName,
2109                                     TreeMap::new,
2110                                     Collectors.toCollection(TreeSet::new)));
2111 
2112             if (!loggerConfigs.isEmpty()) {
2113                 cxs = contexts();
2114             }
2115             final List<Logger> loggers = cxs.isEmpty()
2116                     ? Collections.emptyList() : new ArrayList<>(cxs.size());
2117             for (Map.Entry<String, TreeSet<String>> e : loggerConfigs.entrySet()) {
2118                 // This can be a logger name, or something else...
2119                 // The only thing we know is that we found a property
2120                 //    we are interested in.
2121                 // For instance, if we found x.y.z.level, then x.y.z could be
2122                 // a logger, but it could also be a handler class...
2123                 // Anyway...
2124                 final String name = e.getKey();
2125                 final Set<String> properties = e.getValue();
2126                 loggers.clear();
2127                 for (LoggerContext cx : cxs) {
2128                     Logger l = cx.findLogger(name);
2129                     if (l != null && !visited.test(l)) {
2130                         loggers.add(l);
2131                     }
2132                 }
2133                 if (loggers.isEmpty()) continue;
2134                 for (String pk : properties) {
2135                     ConfigProperty cp = ConfigProperty.find(pk).get();
2136                     String p = previous.getProperty(pk, null);
2137                     String n = next.getProperty(pk, null);
2138 
2139                     // Determines the type of modification.
2140                     ModType mod = ModType.of(p, n);
2141 
2142                     // mod == SAME means that the two values are equals, there
2143                     // is nothing to do. Usually, this should not happen as such
2144                     // properties should have been filtered above.
2145                     // It could happen however if the properties had
2146                     // trailing/leading whitespaces.
2147                     if (mod == ModType.SAME) continue;
2148 
2149                     switch (cp) {
2150                         case LEVEL:
2151                             if (mod == ModType.REMOVED) continue;
2152                             Level level = Level.findLevel(trim(n));
2153                             if (level != null) {
2154                                 if (name.isEmpty()) {
2155                                     rootLogger.setLevel(level);
2156                                 }
2157                                 for (Logger l : loggers) {
2158                                     if (!name.isEmpty() || l != rootLogger) {
2159                                         l.setLevel(level);
2160                                     }
2161                                 }
2162                             }
2163                             break;
2164                         case USEPARENT:
2165                             if (!name.isEmpty()) {
2166                                 boolean useParent = getBooleanProperty(pk, true);
2167                                 if (n != null || p != null) {
2168                                     // reset the flag only if the previous value
2169                                     // or the new value are not null.
2170                                     for (Logger l : loggers) {
2171                                         l.setUseParentHandlers(useParent);
2172                                     }
2173                                 }
2174                             }
2175                             break;
2176                         case HANDLERS:
2177                             List<Handler> hdls = null;
2178                             if (name.isEmpty()) {
2179                                 // special handling for the root logger.
2180                                 globalHandlersState = STATE_READING_CONFIG;
2181                                 try {
2182                                     closeHandlers(rootLogger);
2183                                     globalHandlersState = STATE_UNINITIALIZED;
2184                                 } catch (Throwable t) {
2185                                     globalHandlersState = STATE_INITIALIZED;
2186                                     throw t;
2187                                 }
2188                             }
2189                             for (Logger l : loggers) {
2190                                 if (l == rootLogger) continue;
2191                                 closeHandlers(l);
2192                                 if (mod == ModType.REMOVED) {
2193                                     closeOnResetLoggers.removeIf(c -> c.logger == l);
2194                                     continue;
2195                                 }
2196                                 if (hdls == null) {
2197                                     hdls = name.isEmpty()
2198                                             ? Arrays.asList(rootLogger.getHandlers())
2199                                             : createLoggerHandlers(name, pk);
2200                                 }
2201                                 setLoggerHandlers(l, name, pk, hdls);
2202                             }
2203                             break;
2204                         default: break;
2205                     }
2206                 }
2207             }
2208         } finally {
2209             configurationLock.unlock();
2210             visited.clear();
2211         }
2212 
2213         // Now ensure that if an existing logger has acquired a new parent
2214         // in the configuration, this new parent will be created - if needed,
2215         // and added to the context of the existing child.
2216         //
2217         drainLoggerRefQueueBounded();
2218         for (LoggerContext cx : cxs) {
2219             for (Enumeration<String> names = cx.getLoggerNames() ; names.hasMoreElements();) {
2220                 String name = names.nextElement();
2221                 if (name.isEmpty()) continue;  // don't need to process parents on root.
2222                 Logger l = cx.findLogger(name);
2223                 if (l != null && !visited.test(l)) {
2224                     // should pass visited here to cut the processing when
2225                     // reaching a logger already visited.
2226                     cx.processParentHandlers(l, name, visited);
2227                 }
2228             }
2229         }
2230 
2231         // We changed the configuration: invoke configuration listeners
2232         invokeConfigurationListeners();
2233     }
2234 
2235     /**
2236      * Get the value of a logging property.
2237      * The method returns null if the property is not found.
2238      * @param name      property name
2239      * @return          property value
2240      */
2241     public String getProperty(String name) {
2242         return props.getProperty(name);
2243     }
2244 
2245     // Package private method to get a String property.
2246     // If the property is not defined we return the given
2247     // default value.
2248     String getStringProperty(String name, String defaultValue) {
2249         String val = getProperty(name);
2250         if (val == null) {
2251             return defaultValue;
2252         }
2253         return val.trim();
2254     }
2255 
2256     // Package private method to get an integer property.
2257     // If the property is not defined or cannot be parsed
2258     // we return the given default value.
2259     int getIntProperty(String name, int defaultValue) {
2260         String val = getProperty(name);
2261         if (val == null) {
2262             return defaultValue;
2263         }
2264         try {
2265             return Integer.parseInt(val.trim());
2266         } catch (Exception ex) {
2267             return defaultValue;
2268         }
2269     }
2270 
2271     // Package private method to get a long property.
2272     // If the property is not defined or cannot be parsed
2273     // we return the given default value.
2274     long getLongProperty(String name, long defaultValue) {
2275         String val = getProperty(name);
2276         if (val == null) {
2277             return defaultValue;
2278         }
2279         try {
2280             return Long.parseLong(val.trim());
2281         } catch (Exception ex) {
2282             return defaultValue;
2283         }
2284     }
2285 
2286     // Package private method to get a boolean property.
2287     // If the property is not defined or cannot be parsed
2288     // we return the given default value.
2289     boolean getBooleanProperty(String name, boolean defaultValue) {
2290         String val = getProperty(name);
2291         if (val == null) {
2292             return defaultValue;
2293         }
2294         val = val.toLowerCase();
2295         if (val.equals("true") || val.equals("1")) {
2296             return true;
2297         } else if (val.equals("false") || val.equals("0")) {
2298             return false;
2299         }
2300         return defaultValue;
2301     }
2302 
2303     // Package private method to get a Level property.
2304     // If the property is not defined or cannot be parsed
2305     // we return the given default value.
2306     Level getLevelProperty(String name, Level defaultValue) {
2307         String val = getProperty(name);
2308         if (val == null) {
2309             return defaultValue;
2310         }
2311         Level l = Level.findLevel(val.trim());
2312         return l != null ? l : defaultValue;
2313     }
2314 
2315     // Package private method to get a filter property.
2316     // We return an instance of the class named by the "name"
2317     // property. If the property is not defined or has problems
2318     // we return the defaultValue.
2319     Filter getFilterProperty(String name, Filter defaultValue) {
2320         String val = getProperty(name);
2321         try {
2322             if (val != null) {
2323                 @SuppressWarnings("deprecation")
2324                 Object o = ClassLoader.getSystemClassLoader().loadClass(val).newInstance();
2325                 return (Filter) o;
2326             }
2327         } catch (Exception ex) {
2328             // We got one of a variety of exceptions in creating the
2329             // class or creating an instance.
2330             // Drop through.
2331         }
2332         // We got an exception.  Return the defaultValue.
2333         return defaultValue;
2334     }
2335 
2336 
2337     // Package private method to get a formatter property.
2338     // We return an instance of the class named by the "name"
2339     // property. If the property is not defined or has problems
2340     // we return the defaultValue.
2341     Formatter getFormatterProperty(String name, Formatter defaultValue) {
2342         String val = getProperty(name);
2343         try {
2344             if (val != null) {
2345                 @SuppressWarnings("deprecation")
2346                 Object o = ClassLoader.getSystemClassLoader().loadClass(val).newInstance();
2347                 return (Formatter) o;
2348             }
2349         } catch (Exception ex) {
2350             // We got one of a variety of exceptions in creating the
2351             // class or creating an instance.
2352             // Drop through.
2353         }
2354         // We got an exception.  Return the defaultValue.
2355         return defaultValue;
2356     }
2357 
2358     // Private method to load the global handlers.
2359     // We do the real work lazily, when the global handlers
2360     // are first used.
2361     private void initializeGlobalHandlers() {
2362         int state = globalHandlersState;
2363         if (state == STATE_INITIALIZED ||
2364             state == STATE_SHUTDOWN) {
2365             // Nothing to do: return.
2366             return;
2367         }
2368 
2369         // If we have not initialized global handlers yet (or need to
2370         // reinitialize them), lets do it now (this case is indicated by
2371         // globalHandlersState == STATE_UNINITIALIZED).
2372         // If we are in the process of initializing global handlers we
2373         // also need to lock & wait (this case is indicated by
2374         // globalHandlersState == STATE_INITIALIZING).
2375         // If we are in the process of reading configuration we also need to
2376         // wait to see what the outcome will be (this case
2377         // is indicated by globalHandlersState == STATE_READING_CONFIG)
2378         // So in either case we need to wait for the lock.
2379         configurationLock.lock();
2380         try {
2381             if (globalHandlersState != STATE_UNINITIALIZED) {
2382                 return; // recursive call or nothing to do
2383             }
2384             // set globalHandlersState to STATE_INITIALIZING first to avoid
2385             // getting an infinite recursion when loadLoggerHandlers(...)
2386             // is going to call addHandler(...)
2387             globalHandlersState = STATE_INITIALIZING;
2388             try {
2389                 loadLoggerHandlers(rootLogger, null, "handlers");
2390             } finally {
2391                 globalHandlersState = STATE_INITIALIZED;
2392             }
2393         } finally {
2394             configurationLock.unlock();
2395         }
2396     }
2397 
2398     static final Permission controlPermission =
2399             new LoggingPermission("control", null);
2400 
2401     void checkPermission() {
2402         SecurityManager sm = System.getSecurityManager();
2403         if (sm != null)
2404             sm.checkPermission(controlPermission);
2405     }
2406 
2407     /**
2408      * Check that the current context is trusted to modify the logging
2409      * configuration.  This requires LoggingPermission("control").
2410      * <p>
2411      * If the check fails we throw a SecurityException, otherwise
2412      * we return normally.
2413      *
2414      * @exception  SecurityException  if a security manager exists and if
2415      *             the caller does not have LoggingPermission("control").
2416      */
2417     public void checkAccess() throws SecurityException {
2418         checkPermission();
2419     }
2420 
2421     // Nested class to represent a node in our tree of named loggers.
2422     private static class LogNode {
2423         HashMap<String,LogNode> children;
2424         LoggerWeakRef loggerRef;
2425         LogNode parent;
2426         final LoggerContext context;
2427 
2428         LogNode(LogNode parent, LoggerContext context) {
2429             this.parent = parent;
2430             this.context = context;
2431         }
2432 
2433         // Recursive method to walk the tree below a node and set
2434         // a new parent logger.
2435         void walkAndSetParent(Logger parent) {
2436             if (children == null) {
2437                 return;
2438             }
2439             for (LogNode node : children.values()) {
2440                 LoggerWeakRef ref = node.loggerRef;
2441                 Logger logger = (ref == null) ? null : ref.get();
2442                 if (logger == null) {
2443                     node.walkAndSetParent(parent);
2444                 } else {
2445                     doSetParent(logger, parent);
2446                 }
2447             }
2448         }
2449     }
2450 
2451     // We use a subclass of Logger for the root logger, so
2452     // that we only instantiate the global handlers when they
2453     // are first needed.
2454     private final class RootLogger extends Logger {
2455         private RootLogger() {
2456             // We do not call the protected Logger two args constructor here,
2457             // to avoid calling LogManager.getLogManager() from within the
2458             // RootLogger constructor.
2459             super("", null, null, LogManager.this, true);
2460         }
2461 
2462         @Override
2463         public void log(LogRecord record) {
2464             // Make sure that the global handlers have been instantiated.
2465             initializeGlobalHandlers();
2466             super.log(record);
2467         }
2468 
2469         @Override
2470         public void addHandler(Handler h) {
2471             initializeGlobalHandlers();
2472             super.addHandler(h);
2473         }
2474 
2475         @Override
2476         public void removeHandler(Handler h) {
2477             initializeGlobalHandlers();
2478             super.removeHandler(h);
2479         }
2480 
2481         @Override
2482         Handler[] accessCheckedHandlers() {
2483             initializeGlobalHandlers();
2484             return super.accessCheckedHandlers();
2485         }
2486     }
2487 
2488 
2489     // Private method to be called when the configuration has
2490     // changed to apply any level settings to any pre-existing loggers.
2491     private void setLevelsOnExistingLoggers() {
2492         Enumeration<?> enum_ = props.propertyNames();
2493         while (enum_.hasMoreElements()) {
2494             String key = (String)enum_.nextElement();
2495             if (!key.endsWith(".level")) {
2496                 // Not a level definition.
2497                 continue;
2498             }
2499             int ix = key.length() - 6;
2500             String name = key.substring(0, ix);
2501             Level level = getLevelProperty(key, null);
2502             if (level == null) {
2503                 System.err.println("Bad level value for property: " + key);
2504                 continue;
2505             }
2506             for (LoggerContext cx : contexts()) {
2507                 Logger l = cx.findLogger(name);
2508                 if (l == null) {
2509                     continue;
2510                 }
2511                 l.setLevel(level);
2512             }
2513         }
2514     }
2515 
2516     /**
2517      * String representation of the
2518      * {@link javax.management.ObjectName} for the management interface
2519      * for the logging facility.
2520      *
2521      * @see java.lang.management.PlatformLoggingMXBean
2522      *
2523      * @since 1.5
2524      */
2525     public final static String LOGGING_MXBEAN_NAME
2526         = "java.util.logging:type=Logging";
2527 
2528     /**
2529      * Returns {@code LoggingMXBean} for managing loggers.
2530      *
2531      * @return a {@link LoggingMXBean} object.
2532      *
2533      * @deprecated {@code java.util.logging.LoggingMXBean} is deprecated and
2534      *      replaced with {@code java.lang.management.PlatformLoggingMXBean}. Use
2535      *      {@link java.lang.management.ManagementFactory#getPlatformMXBean(Class)
2536      *      ManagementFactory.getPlatformMXBean}(PlatformLoggingMXBean.class)
2537      *      instead.
2538      *
2539      * @see java.lang.management.PlatformLoggingMXBean
2540      * @since 1.5
2541      */
2542     @Deprecated(since="9")
2543     public static synchronized LoggingMXBean getLoggingMXBean() {
2544         return Logging.getInstance();
2545     }
2546 
2547     /**
2548      * Adds a configuration listener to be invoked each time the logging
2549      * configuration is read.
2550      * If the listener is already registered the method does nothing.
2551      * <p>
2552      * The listener is invoked with privileges that are restricted by the
2553      * calling context of this method.
2554      * The order in which the listeners are invoked is unspecified.
2555      * <p>
2556      * It is recommended that listeners do not throw errors or exceptions.
2557      *
2558      * If a listener terminates with an uncaught error or exception then
2559      * the first exception will be propagated to the caller of
2560      * {@link #readConfiguration()} (or {@link #readConfiguration(java.io.InputStream)})
2561      * after all listeners have been invoked.
2562      *
2563      * @implNote If more than one listener terminates with an uncaught error or
2564      * exception, an implementation may record the additional errors or
2565      * exceptions as {@linkplain Throwable#addSuppressed(java.lang.Throwable)
2566      * suppressed exceptions}.
2567      *
2568      * @param listener A configuration listener that will be invoked after the
2569      *        configuration changed.
2570      * @return This LogManager.
2571      * @throws SecurityException if a security manager exists and if the
2572      * caller does not have LoggingPermission("control").
2573      * @throws NullPointerException if the listener is null.
2574      *
2575      * @since 9
2576      */
2577     public LogManager addConfigurationListener(Runnable listener) {
2578         final Runnable r = Objects.requireNonNull(listener);
2579         checkPermission();
2580         final SecurityManager sm = System.getSecurityManager();
2581         final AccessControlContext acc =
2582                 sm == null ? null : AccessController.getContext();
2583         final PrivilegedAction<Void> pa =
2584                 acc == null ? null : () -> { r.run() ; return null; };
2585         final Runnable pr =
2586                 acc == null ? r : () -> AccessController.doPrivileged(pa, acc);
2587         // Will do nothing if already registered.
2588         listeners.putIfAbsent(r, pr);
2589         return this;
2590     }
2591 
2592     /**
2593      * Removes a previously registered configuration listener.
2594      *
2595      * Returns silently if the listener is not found.
2596      *
2597      * @param listener the configuration listener to remove.
2598      * @throws NullPointerException if the listener is null.
2599      * @throws SecurityException if a security manager exists and if the
2600      * caller does not have LoggingPermission("control").
2601      *
2602      * @since 9
2603      */
2604     public void removeConfigurationListener(Runnable listener) {
2605         final Runnable key = Objects.requireNonNull(listener);
2606         checkPermission();
2607         listeners.remove(key);
2608     }
2609 
2610     private void invokeConfigurationListeners() {
2611         Throwable t = null;
2612 
2613         // We're using an IdentityHashMap because we want to compare
2614         // keys using identity (==).
2615         // We don't want to loop within a block synchronized on 'listeners'
2616         // to avoid invoking listeners from yet another synchronized block.
2617         // So we're taking a snapshot of the values list to avoid the risk of
2618         // ConcurrentModificationException while looping.
2619         //
2620         for (Runnable c : listeners.values().toArray(new Runnable[0])) {
2621             try {
2622                 c.run();
2623             } catch (ThreadDeath death) {
2624                 throw death;
2625             } catch (Error | RuntimeException x) {
2626                 if (t == null) t = x;
2627                 else t.addSuppressed(x);
2628             }
2629         }
2630         // Listeners are not supposed to throw exceptions, but if that
2631         // happens, we will rethrow the first error or exception that is raised
2632         // after all listeners have been invoked.
2633         if (t instanceof Error) throw (Error)t;
2634         if (t instanceof RuntimeException) throw (RuntimeException)t;
2635     }
2636 
2637     /**
2638      * This class allows the {@link LoggingProviderImpl} to demand loggers on
2639      * behalf of system and application classes.
2640      */
2641     private static final class LoggingProviderAccess
2642         implements LoggingProviderImpl.LogManagerAccess,
2643                    PrivilegedAction<Void> {
2644 
2645         private LoggingProviderAccess() {
2646         }
2647 
2648         /**
2649          * Demands a logger on behalf of the given {@code module}.
2650          * <p>
2651          * If a named logger suitable for the given module is found
2652          * returns it.
2653          * Otherwise, creates a new logger suitable for the given module.
2654          *
2655          * @param name   The logger name.
2656          * @param module The module on which behalf the logger is created/retrieved.
2657          * @return A logger for the given {@code module}.
2658          *
2659          * @throws NullPointerException if {@code name} is {@code null}
2660          *         or {@code module} is {@code null}.
2661          * @throws IllegalArgumentException if {@code manager} is not the default
2662          *         LogManager.
2663          * @throws SecurityException if a security manager is present and the
2664          *         calling code doesn't have the
2665          *        {@link LoggingPermission LoggingPermission("demandLogger", null)}.
2666          */
2667         @Override
2668         public Logger demandLoggerFor(LogManager manager, String name, Module module) {
2669             if (manager != getLogManager()) {
2670                 // having LogManager as parameter just ensures that the
2671                 // caller will have initialized the LogManager before reaching
2672                 // here.
2673                 throw new IllegalArgumentException("manager");
2674             }
2675             Objects.requireNonNull(name);
2676             Objects.requireNonNull(module);
2677             SecurityManager sm = System.getSecurityManager();
2678             if (sm != null) {
2679                 sm.checkPermission(controlPermission);
2680             }
2681             if (isSystem(module)) {
2682                 return manager.demandSystemLogger(name,
2683                     Logger.SYSTEM_LOGGER_RB_NAME, module);
2684             } else {
2685                 return manager.demandLogger(name, null, module);
2686             }
2687         }
2688 
2689         @Override
2690         public Void run() {
2691             LoggingProviderImpl.setLogManagerAccess(INSTANCE);
2692             return null;
2693         }
2694 
2695         static final LoggingProviderAccess INSTANCE = new LoggingProviderAccess();
2696     }
2697 
2698     static {
2699         AccessController.doPrivileged(LoggingProviderAccess.INSTANCE, null,
2700                                       controlPermission);
2701     }
2702 
2703 }
--- EOF ---