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