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