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