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