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