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