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