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