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