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