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