1 /*
   2  * Copyright (c) 2000, 2010, 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.beans.PropertyChangeListener;
  35 import java.beans.PropertyChangeSupport;
  36 import java.net.URL;
  37 import sun.security.action.GetPropertyAction;
  38 
  39 /**
  40  * There is a single global LogManager object that is used to
  41  * maintain a set of shared state about Loggers and log services.
  42  * <p>
  43  * This LogManager object:
  44  * <ul>
  45  * <li> Manages a hierarchical namespace of Logger objects.  All
  46  *      named Loggers are stored in this namespace.
  47  * <li> Manages a set of logging control properties.  These are
  48  *      simple key-value pairs that can be used by Handlers and
  49  *      other logging objects to configure themselves.
  50  * </ul>
  51  * <p>
  52  * The global LogManager object can be retrieved using LogManager.getLogManager().
  53  * The LogManager object is created during class initialization and
  54  * cannot subsequently be changed.
  55  * <p>
  56  * At startup the LogManager class is located using the
  57  * java.util.logging.manager system property.
  58  * <p>
  59  * By default, the LogManager reads its initial configuration from
  60  * a properties file "lib/logging.properties" in the JRE directory.
  61  * If you edit that property file you can change the default logging
  62  * configuration for all uses of that JRE.
  63  * <p>
  64  * In addition, the LogManager uses two optional system properties that
  65  * allow more control over reading the initial configuration:
  66  * <ul>
  67  * <li>"java.util.logging.config.class"
  68  * <li>"java.util.logging.config.file"
  69  * </ul>
  70  * These two properties may be set via the Preferences API, or as
  71  * command line property definitions to the "java" command, or as
  72  * system property definitions passed to JNI_CreateJavaVM.
  73  * <p>
  74  * If the "java.util.logging.config.class" property is set, then the
  75  * property value is treated as a class name.  The given class will be
  76  * loaded, an object will be instantiated, and that object's constructor
  77  * is responsible for reading in the initial configuration.  (That object
  78  * may use other system properties to control its configuration.)  The
  79  * alternate configuration class can use <tt>readConfiguration(InputStream)</tt>
  80  * to define properties in the LogManager.
  81  * <p>
  82  * If "java.util.logging.config.class" property is <b>not</b> set,
  83  * then the "java.util.logging.config.file" system property can be used
  84  * to specify a properties file (in java.util.Properties format). The
  85  * initial logging configuration will be read from this file.
  86  * <p>
  87  * If neither of these properties is defined then, as described
  88  * above, the LogManager will read its initial configuration from
  89  * a properties file "lib/logging.properties" in the JRE directory.
  90  * <p>
  91  * The properties for loggers and Handlers will have names starting
  92  * with the dot-separated name for the handler or logger.
  93  * <p>
  94  * The global logging properties may include:
  95  * <ul>
  96  * <li>A property "handlers".  This defines a whitespace or comma separated
  97  * list of class names for handler classes to load and register as
  98  * handlers on the root Logger (the Logger named "").  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;.handlers". This defines a whitespace or
 104  * comma separated list of class names for handlers classes to
 105  * load and register as handlers to the specified logger. Each class
 106  * name must be for a Handler class which has a default constructor.
 107  * Note that these Handlers may be created lazily, when they are
 108  * first used.
 109  *
 110  * <li>A property "&lt;logger&gt;.useParentHandlers". This defines a boolean
 111  * value. By default every logger calls its parent in addition to
 112  * handling the logging message itself, this often result in messages
 113  * being handled by the root logger as well. When setting this property
 114  * to false a Handler needs to be configured for this logger otherwise
 115  * no logging messages are delivered.
 116  *
 117  * <li>A property "config".  This property is intended to allow
 118  * arbitrary configuration code to be run.  The property defines a
 119  * whitespace or comma separated list of class names.  A new instance will be
 120  * created for each named class.  The default constructor of each class
 121  * may execute arbitrary code to update the logging configuration, such as
 122  * setting logger levels, adding handlers, adding filters, etc.
 123  * </ul>
 124  * <p>
 125  * Note that all classes loaded during LogManager configuration are
 126  * first searched on the system class path before any user class path.
 127  * That includes the LogManager class, any config classes, and any
 128  * handler classes.
 129  * <p>
 130  * Loggers are organized into a naming hierarchy based on their
 131  * dot separated names.  Thus "a.b.c" is a child of "a.b", but
 132  * "a.b1" and a.b2" are peers.
 133  * <p>
 134  * All properties whose names end with ".level" are assumed to define
 135  * log levels for Loggers.  Thus "foo.level" defines a log level for
 136  * the logger called "foo" and (recursively) for any of its children
 137  * in the naming hierarchy.  Log Levels are applied in the order they
 138  * are defined in the properties file.  Thus level settings for child
 139  * nodes in the tree should come after settings for their parents.
 140  * The property name ".level" can be used to set the level for the
 141  * root of the tree.
 142  * <p>
 143  * All methods on the LogManager object are multi-thread safe.
 144  *
 145  * @since 1.4
 146 */
 147 
 148 public class LogManager {
 149     // The global LogManager object
 150     private static LogManager manager;
 151 
 152     private final static Handler[] emptyHandlers = { };
 153     private Properties props = new Properties();
 154     private PropertyChangeSupport changes
 155                          = new PropertyChangeSupport(LogManager.class);
 156     private final static Level defaultLevel = Level.INFO;
 157 
 158     // Table of named Loggers that maps names to Loggers.
 159     private Hashtable<String,LoggerWeakRef> namedLoggers = new Hashtable<>();
 160     // Tree of named Loggers
 161     private LogNode root = new LogNode(null);
 162     private Logger rootLogger;
 163 
 164     // Have we done the primordial reading of the configuration file?
 165     // (Must be done after a suitable amount of java.lang.System
 166     // initialization has been done)
 167     private volatile boolean readPrimordialConfiguration;
 168     // Have we initialized global (root) handlers yet?
 169     // This gets set to false in readConfiguration
 170     private boolean initializedGlobalHandlers = true;
 171     // True if JVM death is imminent and the exit hook has been called.
 172     private boolean deathImminent;
 173 
 174     static {
 175         AccessController.doPrivileged(new PrivilegedAction<Object>() {
 176                 public Object run() {
 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().loadClass(cname);
 183                                 manager = (LogManager) clz.newInstance();
 184                             } catch (ClassNotFoundException ex) {
 185                                 Class clz = Thread.currentThread().getContextClassLoader().loadClass(cname);
 186                                 manager = (LogManager) clz.newInstance();
 187                             }
 188                         }
 189                     } catch (Exception ex) {
 190                         System.err.println("Could not load Logmanager \"" + cname + "\"");
 191                         ex.printStackTrace();
 192                     }
 193                     if (manager == null) {
 194                         manager = new LogManager();
 195                     }
 196 
 197                     // Create and retain Logger for the root of the namespace.
 198                     manager.rootLogger = manager.new RootLogger();
 199                     manager.addLogger(manager.rootLogger);
 200 
 201                     // Adding the global Logger. Doing so in the Logger.<clinit>
 202                     // would deadlock with the LogManager.<clinit>.
 203                     Logger.global.setLogManager(manager);
 204                     manager.addLogger(Logger.global);
 205 
 206                     // We don't call readConfiguration() here, as we may be running
 207                     // very early in the JVM startup sequence.  Instead readConfiguration
 208                     // will be called lazily in getLogManager().
 209                     return null;
 210                 }
 211             });
 212     }
 213 
 214 
 215     // This private class is used as a shutdown hook.
 216     // It does a "reset" to close all open handlers.
 217     private class Cleaner extends Thread {
 218 
 219         private Cleaner() {
 220             /* Set context class loader to null in order to avoid
 221              * keeping a strong reference to an application classloader.
 222              */
 223             this.setContextClassLoader(null);
 224         }
 225 
 226         public void run() {
 227             // This is to ensure the LogManager.<clinit> is completed
 228             // before synchronized block. Otherwise deadlocks are possible.
 229             LogManager mgr = manager;
 230 
 231             // If the global handlers haven't been initialized yet, we
 232             // don't want to initialize them just so we can close them!
 233             synchronized (LogManager.this) {
 234                 // Note that death is imminent.
 235                 deathImminent = true;
 236                 initializedGlobalHandlers = true;
 237             }
 238 
 239             // Do a reset to close all active handlers.
 240             reset();
 241         }
 242     }
 243 
 244 
 245     /**
 246      * Protected constructor.  This is protected so that container applications
 247      * (such as J2EE containers) can subclass the object.  It is non-public as
 248      * it is intended that there only be one LogManager object, whose value is
 249      * retrieved by calling Logmanager.getLogManager.
 250      */
 251     protected LogManager() {
 252         // Add a shutdown hook to close the global handlers.
 253         try {
 254             Runtime.getRuntime().addShutdownHook(new Cleaner());
 255         } catch (IllegalStateException e) {
 256             // If the VM is already shutting down,
 257             // We do not need to register shutdownHook.
 258         }
 259     }
 260 
 261     /**
 262      * Return the global LogManager object.
 263      */
 264     public static LogManager getLogManager() {
 265         if (manager != null) {
 266             manager.readPrimordialConfiguration();
 267         }
 268         return manager;
 269     }
 270 
 271     private void readPrimordialConfiguration() {
 272         if (!readPrimordialConfiguration) {
 273             synchronized (this) {
 274                 if (!readPrimordialConfiguration) {
 275                     // If System.in/out/err are null, it's a good
 276                     // indication that we're still in the
 277                     // bootstrapping phase
 278                     if (System.out == null) {
 279                         return;
 280                     }
 281                     readPrimordialConfiguration = true;
 282                     try {
 283                         AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() {
 284                                 public Object run() throws Exception {
 285                                     readConfiguration();
 286 
 287                                     // Platform loggers begin to delegate to java.util.logging.Logger
 288                                     sun.util.logging.PlatformLogger.redirectPlatformLoggers();
 289 
 290                                     return null;
 291                                 }
 292                             });
 293                     } catch (Exception ex) {
 294                         // System.err.println("Can't read logging configuration:");
 295                         // ex.printStackTrace();
 296                     }
 297                 }
 298             }
 299         }
 300     }
 301 
 302     /**
 303      * Adds an event listener to be invoked when the logging
 304      * properties are re-read. Adding multiple instances of
 305      * the same event Listener results in multiple entries
 306      * in the property event listener table.
 307      *
 308      * @param l  event listener
 309      * @exception  SecurityException  if a security manager exists and if
 310      *             the caller does not have LoggingPermission("control").
 311      * @exception NullPointerException if the PropertyChangeListener is null.
 312      */
 313     public void addPropertyChangeListener(PropertyChangeListener l) throws SecurityException {
 314         if (l == null) {
 315             throw new NullPointerException();
 316         }
 317         checkAccess();
 318         changes.addPropertyChangeListener(l);
 319     }
 320 
 321     /**
 322      * Removes an event listener for property change events.
 323      * If the same listener instance has been added to the listener table
 324      * through multiple invocations of <CODE>addPropertyChangeListener</CODE>,
 325      * then an equivalent number of
 326      * <CODE>removePropertyChangeListener</CODE> invocations are required to remove
 327      * all instances of that listener from the listener table.
 328      * <P>
 329      * Returns silently if the given listener is not found.
 330      *
 331      * @param l  event listener (can be null)
 332      * @exception  SecurityException  if a security manager exists and if
 333      *             the caller does not have LoggingPermission("control").
 334      */
 335     public void removePropertyChangeListener(PropertyChangeListener l) throws SecurityException {
 336         checkAccess();
 337         changes.removePropertyChangeListener(l);
 338     }
 339 
 340     // Package-level method.
 341     // Find or create a specified logger instance. If a logger has
 342     // already been created with the given name it is returned.
 343     // Otherwise a new logger instance is created and registered
 344     // in the LogManager global namespace.
 345     Logger demandLogger(String name) {
 346         Logger result = getLogger(name);
 347         if (result == null) {
 348             result = new Logger(name, null);
 349             addLogger(result);
 350             result = getLogger(name);
 351         }
 352         return result;
 353     }
 354 
 355     // If logger.getUseParentHandlers() returns 'true' and any of the logger's
 356     // parents have levels or handlers defined, make sure they are instantiated.
 357     private void processParentHandlers(Logger logger, String name) {
 358         int ix = 1;
 359         for (;;) {
 360             int ix2 = name.indexOf(".", ix);
 361             if (ix2 < 0) {
 362                 break;
 363             }
 364             String pname = name.substring(0,ix2);
 365 
 366             if (getProperty(pname+".level")    != null ||
 367                 getProperty(pname+".handlers") != null) {
 368                 // This pname has a level/handlers definition.
 369                 // Make sure it exists.
 370                 demandLogger(pname);
 371             }
 372             ix = ix2+1;
 373         }
 374     }
 375 
 376     // Add new per logger handlers.
 377     // We need to raise privilege here. All our decisions will
 378     // be made based on the logging configuration, which can
 379     // only be modified by trusted code.
 380     private void loadLoggerHandlers(final Logger logger, final String name,
 381                                     final String handlersPropertyName) {
 382         AccessController.doPrivileged(new PrivilegedAction<Object>() {
 383             public Object run() {
 384                 if (logger != rootLogger) {
 385                     boolean useParent = getBooleanProperty(name + ".useParentHandlers", true);
 386                     if (!useParent) {
 387                         logger.setUseParentHandlers(false);
 388                     }
 389                 }
 390 
 391                 String names[] = parseClassNames(handlersPropertyName);
 392                 for (int i = 0; i < names.length; i++) {
 393                     String word = names[i];
 394                     try {
 395                         Class   clz = ClassLoader.getSystemClassLoader().loadClass(word);
 396                         Handler hdl = (Handler) clz.newInstance();
 397                         try {
 398                             // Check if there is a property defining the
 399                             // this handler's level.
 400                             String levs = getProperty(word + ".level");
 401                             if (levs != null) {
 402                                 hdl.setLevel(Level.parse(levs));
 403                             }
 404                         } catch (Exception ex) {
 405                             System.err.println("Can't set level for " + word);
 406                             // Probably a bad level. Drop through.
 407                         }
 408                         // Add this Handler to the logger
 409                         logger.addHandler(hdl);
 410                     } catch (Exception ex) {
 411                         System.err.println("Can't load log handler \"" + word + "\"");
 412                         System.err.println("" + ex);
 413                         ex.printStackTrace();
 414                     }
 415                 }
 416                 return null;
 417             }});
 418     }
 419 
 420 
 421     // loggerRefQueue holds LoggerWeakRef objects for Logger objects
 422     // that have been GC'ed.
 423     private final ReferenceQueue<Logger> loggerRefQueue
 424         = new ReferenceQueue<>();
 425 
 426     // Package-level inner class.
 427     // Helper class for managing WeakReferences to Logger objects.
 428     //
 429     // LogManager.namedLoggers
 430     //     - has weak references to all named Loggers
 431     //     - namedLoggers keeps the LoggerWeakRef objects for the named
 432     //       Loggers around until we can deal with the book keeping for
 433     //       the named Logger that is being GC'ed.
 434     // LogManager.LogNode.loggerRef
 435     //     - has a weak reference to a named Logger
 436     //     - the LogNode will also keep the LoggerWeakRef objects for
 437     //       the named Loggers around; currently LogNodes never go away.
 438     // Logger.kids
 439     //     - has a weak reference to each direct child Logger; this
 440     //       includes anonymous and named Loggers
 441     //     - anonymous Loggers are always children of the rootLogger
 442     //       which is a strong reference; rootLogger.kids keeps the
 443     //       LoggerWeakRef objects for the anonymous Loggers around
 444     //       until we can deal with the book keeping.
 445     //
 446     final class LoggerWeakRef extends WeakReference<Logger> {
 447         private String                name;       // for namedLoggers cleanup
 448         private LogNode               node;       // for loggerRef cleanup
 449         private WeakReference<Logger> parentRef;  // for kids cleanup
 450 
 451         LoggerWeakRef(Logger logger) {
 452             super(logger, loggerRefQueue);
 453 
 454             name = logger.getName();  // save for namedLoggers cleanup
 455         }
 456 
 457         // dispose of this LoggerWeakRef object
 458         void dispose() {
 459             if (node != null) {
 460                 // if we have a LogNode, then we were a named Logger
 461                 // so clear namedLoggers weak ref to us
 462                 manager.namedLoggers.remove(name);
 463                 name = null;  // clear our ref to the Logger's name
 464 
 465                 node.loggerRef = null;  // clear LogNode's weak ref to us
 466                 node = null;            // clear our ref to LogNode
 467             }
 468 
 469             if (parentRef != null) {
 470                 // this LoggerWeakRef has or had a parent Logger
 471                 Logger parent = parentRef.get();
 472                 if (parent != null) {
 473                     // the parent Logger is still there so clear the
 474                     // parent Logger's weak ref to us
 475                     parent.removeChildLogger(this);
 476                 }
 477                 parentRef = null;  // clear our weak ref to the parent Logger
 478             }
 479         }
 480 
 481         // set the node field to the specified value
 482         void setNode(LogNode node) {
 483             this.node = node;
 484         }
 485 
 486         // set the parentRef field to the specified value
 487         void setParentRef(WeakReference<Logger> parentRef) {
 488             this.parentRef = parentRef;
 489         }
 490     }
 491 
 492     // Package-level method.
 493     // Drain some Logger objects that have been GC'ed.
 494     //
 495     // drainLoggerRefQueueBounded() is called by addLogger() below
 496     // and by Logger.getAnonymousLogger(String) so we'll drain up to
 497     // MAX_ITERATIONS GC'ed Loggers for every Logger we add.
 498     //
 499     // On a WinXP VMware client, a MAX_ITERATIONS value of 400 gives
 500     // us about a 50/50 mix in increased weak ref counts versus
 501     // decreased weak ref counts in the AnonLoggerWeakRefLeak test.
 502     // Here are stats for cleaning up sets of 400 anonymous Loggers:
 503     //   - test duration 1 minute
 504     //   - sample size of 125 sets of 400
 505     //   - average: 1.99 ms
 506     //   - minimum: 0.57 ms
 507     //   - maximum: 25.3 ms
 508     //
 509     // The same config gives us a better decreased weak ref count
 510     // than increased weak ref count in the LoggerWeakRefLeak test.
 511     // Here are stats for cleaning up sets of 400 named Loggers:
 512     //   - test duration 2 minutes
 513     //   - sample size of 506 sets of 400
 514     //   - average: 0.57 ms
 515     //   - minimum: 0.02 ms
 516     //   - maximum: 10.9 ms
 517     //
 518     private final static int MAX_ITERATIONS = 400;
 519     final synchronized void drainLoggerRefQueueBounded() {
 520         for (int i = 0; i < MAX_ITERATIONS; i++) {
 521             if (loggerRefQueue == null) {
 522                 // haven't finished loading LogManager yet
 523                 break;
 524             }
 525 
 526             LoggerWeakRef ref = (LoggerWeakRef) loggerRefQueue.poll();
 527             if (ref == null) {
 528                 break;
 529             }
 530             // a Logger object has been GC'ed so clean it up
 531             ref.dispose();
 532         }
 533     }
 534 
 535     /**
 536      * Add a named logger.  This does nothing and returns false if a logger
 537      * with the same name is already registered.
 538      * <p>
 539      * The Logger factory methods call this method to register each
 540      * newly created Logger.
 541      * <p>
 542      * The application should retain its own reference to the Logger
 543      * object to avoid it being garbage collected.  The LogManager
 544      * may only retain a weak reference.
 545      *
 546      * @param   logger the new logger.
 547      * @return  true if the argument logger was registered successfully,
 548      *          false if a logger of that name already exists.
 549      * @exception NullPointerException if the logger name is null.
 550      */
 551     public synchronized boolean addLogger(Logger logger) {
 552         final String name = logger.getName();
 553         if (name == null) {
 554             throw new NullPointerException();
 555         }
 556 
 557         // cleanup some Loggers that have been GC'ed
 558         drainLoggerRefQueueBounded();
 559 
 560         LoggerWeakRef ref = namedLoggers.get(name);
 561         if (ref != null) {
 562             if (ref.get() == null) {
 563                 // It's possible that the Logger was GC'ed after the
 564                 // drainLoggerRefQueueBounded() call above so allow
 565                 // a new one to be registered.
 566                 namedLoggers.remove(name);
 567             } else {
 568                 // We already have a registered logger with the given name.
 569                 return false;
 570             }
 571         }
 572 
 573         // We're adding a new logger.
 574         // Note that we are creating a weak reference here.
 575         ref = new LoggerWeakRef(logger);
 576         namedLoggers.put(name, ref);
 577 
 578         // Apply any initial level defined for the new logger.
 579         Level level = getLevelProperty(name+".level", null);
 580         if (level != null) {
 581             doSetLevel(logger, level);
 582         }
 583 
 584         // Do we have a per logger handler too?
 585         // Note: this will add a 200ms penalty
 586         loadLoggerHandlers(logger, name, name+".handlers");
 587         processParentHandlers(logger, name);
 588 
 589         // Find the new node and its parent.
 590         LogNode node = findNode(name);
 591         node.loggerRef = ref;
 592         Logger parent = null;
 593         LogNode nodep = node.parent;
 594         while (nodep != null) {
 595             LoggerWeakRef nodeRef = nodep.loggerRef;
 596             if (nodeRef != null) {
 597                 parent = nodeRef.get();
 598                 if (parent != null) {
 599                     break;
 600                 }
 601             }
 602             nodep = nodep.parent;
 603         }
 604 
 605         if (parent != null) {
 606             doSetParent(logger, parent);
 607         }
 608         // Walk over the children and tell them we are their new parent.
 609         node.walkAndSetParent(logger);
 610 
 611         // new LogNode is ready so tell the LoggerWeakRef about it
 612         ref.setNode(node);
 613 
 614         return true;
 615     }
 616 
 617 
 618     // Private method to set a level on a logger.
 619     // If necessary, we raise privilege before doing the call.
 620     private static void doSetLevel(final Logger logger, final Level level) {
 621         SecurityManager sm = System.getSecurityManager();
 622         if (sm == null) {
 623             // There is no security manager, so things are easy.
 624             logger.setLevel(level);
 625             return;
 626         }
 627         // There is a security manager.  Raise privilege before
 628         // calling setLevel.
 629         AccessController.doPrivileged(new PrivilegedAction<Object>() {
 630             public Object run() {
 631                 logger.setLevel(level);
 632                 return null;
 633             }});
 634     }
 635 
 636 
 637 
 638     // Private method to set a parent on a logger.
 639     // If necessary, we raise privilege before doing the setParent call.
 640     private static void doSetParent(final Logger logger, final Logger parent) {
 641         SecurityManager sm = System.getSecurityManager();
 642         if (sm == null) {
 643             // There is no security manager, so things are easy.
 644             logger.setParent(parent);
 645             return;
 646         }
 647         // There is a security manager.  Raise privilege before
 648         // calling setParent.
 649         AccessController.doPrivileged(new PrivilegedAction<Object>() {
 650             public Object run() {
 651                 logger.setParent(parent);
 652                 return null;
 653             }});
 654     }
 655 
 656     // Find a node in our tree of logger nodes.
 657     // If necessary, create it.
 658     private LogNode findNode(String name) {
 659         if (name == null || name.equals("")) {
 660             return root;
 661         }
 662         LogNode node = root;
 663         while (name.length() > 0) {
 664             int ix = name.indexOf(".");
 665             String head;
 666             if (ix > 0) {
 667                 head = name.substring(0,ix);
 668                 name = name.substring(ix+1);
 669             } else {
 670                 head = name;
 671                 name = "";
 672             }
 673             if (node.children == null) {
 674                 node.children = new HashMap<>();
 675             }
 676             LogNode child = node.children.get(head);
 677             if (child == null) {
 678                 child = new LogNode(node);
 679                 node.children.put(head, child);
 680             }
 681             node = child;
 682         }
 683         return node;
 684     }
 685 
 686     /**
 687      * Method to find a named logger.
 688      * <p>
 689      * Note that since untrusted code may create loggers with
 690      * arbitrary names this method should not be relied on to
 691      * find Loggers for security sensitive logging.
 692      * It is also important to note that the Logger associated with the
 693      * String {@code name} may be garbage collected at any time if there
 694      * is no strong reference to the Logger. The caller of this method
 695      * must check the return value for null in order to properly handle
 696      * the case where the Logger has been garbage collected.
 697      * <p>
 698      * @param name name of the logger
 699      * @return  matching logger or null if none is found
 700      */
 701     public synchronized Logger getLogger(String name) {
 702         LoggerWeakRef ref = namedLoggers.get(name);
 703         if (ref == null) {
 704             return null;
 705         }
 706         Logger logger = ref.get();
 707         if (logger == null) {
 708             // Hashtable holds stale weak reference
 709             // to a logger which has been GC-ed.
 710             namedLoggers.remove(name);
 711         }
 712         return logger;
 713     }
 714 
 715     /**
 716      * Get an enumeration of known logger names.
 717      * <p>
 718      * Note:  Loggers may be added dynamically as new classes are loaded.
 719      * This method only reports on the loggers that are currently registered.
 720      * It is also important to note that this method only returns the name
 721      * of a Logger, not a strong reference to the Logger itself.
 722      * The returned String does nothing to prevent the Logger from being
 723      * garbage collected. In particular, if the returned name is passed
 724      * to {@code LogManager.getLogger()}, then the caller must check the
 725      * return value from {@code LogManager.getLogger()} for null to properly
 726      * handle the case where the Logger has been garbage collected in the
 727      * time since its name was returned by this method.
 728      * <p>
 729      * @return  enumeration of logger name strings
 730      */
 731     public synchronized Enumeration<String> getLoggerNames() {
 732         return namedLoggers.keys();
 733     }
 734 
 735     /**
 736      * Reinitialize the logging properties and reread the logging configuration.
 737      * <p>
 738      * The same rules are used for locating the configuration properties
 739      * as are used at startup.  So normally the logging properties will
 740      * be re-read from the same file that was used at startup.
 741      * <P>
 742      * Any log level definitions in the new configuration file will be
 743      * applied using Logger.setLevel(), if the target Logger exists.
 744      * <p>
 745      * A PropertyChangeEvent will be fired after the properties are read.
 746      *
 747      * @exception  SecurityException  if a security manager exists and if
 748      *             the caller does not have LoggingPermission("control").
 749      * @exception  IOException if there are IO problems reading the configuration.
 750      */
 751     public void readConfiguration() throws IOException, SecurityException {
 752         checkAccess();
 753 
 754         // if a configuration class is specified, load it and use it.
 755         String cname = System.getProperty("java.util.logging.config.class");
 756         if (cname != null) {
 757             try {
 758                 // Instantiate the named class.  It is its constructor's
 759                 // responsibility to initialize the logging configuration, by
 760                 // calling readConfiguration(InputStream) with a suitable stream.
 761                 try {
 762                     Class clz = ClassLoader.getSystemClassLoader().loadClass(cname);
 763                     clz.newInstance();
 764                     return;
 765                 } catch (ClassNotFoundException ex) {
 766                     Class clz = Thread.currentThread().getContextClassLoader().loadClass(cname);
 767                     clz.newInstance();
 768                     return;
 769                 }
 770             } catch (Exception ex) {
 771                 System.err.println("Logging configuration class \"" + cname + "\" failed");
 772                 System.err.println("" + ex);
 773                 // keep going and useful config file.
 774             }
 775         }
 776 
 777         String fname = System.getProperty("java.util.logging.config.file");
 778         if (fname == null) {
 779             fname = System.getProperty("java.home");
 780             if (fname == null) {
 781                 throw new Error("Can't find java.home ??");
 782             }
 783             File f = new File(fname, "lib");
 784             f = new File(f, "logging.properties");
 785             fname = f.getCanonicalPath();
 786         }
 787         InputStream in = new FileInputStream(fname);
 788         BufferedInputStream bin = new BufferedInputStream(in);
 789         try {
 790             readConfiguration(bin);
 791         } finally {
 792             if (in != null) {
 793                 in.close();
 794             }
 795         }
 796     }
 797 
 798     /**
 799      * Reset the logging configuration.
 800      * <p>
 801      * For all named loggers, the reset operation removes and closes
 802      * all Handlers and (except for the root logger) sets the level
 803      * to null.  The root logger's level is set to Level.INFO.
 804      *
 805      * @exception  SecurityException  if a security manager exists and if
 806      *             the caller does not have LoggingPermission("control").
 807      */
 808 
 809     public void reset() throws SecurityException {
 810         checkAccess();
 811         synchronized (this) {
 812             props = new Properties();
 813             // Since we are doing a reset we no longer want to initialize
 814             // the global handlers, if they haven't been initialized yet.
 815             initializedGlobalHandlers = true;
 816         }
 817         Enumeration enum_ = getLoggerNames();
 818         while (enum_.hasMoreElements()) {
 819             String name = (String)enum_.nextElement();
 820             resetLogger(name);
 821         }
 822     }
 823 
 824 
 825     // Private method to reset an individual target logger.
 826     private void resetLogger(String name) {
 827         Logger logger = getLogger(name);
 828         if (logger == null) {
 829             return;
 830         }
 831         // Close all the Logger's handlers.
 832         Handler[] targets = logger.getHandlers();
 833         for (int i = 0; i < targets.length; i++) {
 834             Handler h = targets[i];
 835             logger.removeHandler(h);
 836             try {
 837                 h.close();
 838             } catch (Exception ex) {
 839                 // Problems closing a handler?  Keep going...
 840             }
 841         }
 842         if (name != null && name.equals("")) {
 843             // This is the root logger.
 844             logger.setLevel(defaultLevel);
 845         } else {
 846             logger.setLevel(null);
 847         }
 848     }
 849 
 850     // get a list of whitespace separated classnames from a property.
 851     private String[] parseClassNames(String propertyName) {
 852         String hands = getProperty(propertyName);
 853         if (hands == null) {
 854             return new String[0];
 855         }
 856         hands = hands.trim();
 857         int ix = 0;
 858         Vector<String> result = new Vector<>();
 859         while (ix < hands.length()) {
 860             int end = ix;
 861             while (end < hands.length()) {
 862                 if (Character.isWhitespace(hands.charAt(end))) {
 863                     break;
 864                 }
 865                 if (hands.charAt(end) == ',') {
 866                     break;
 867                 }
 868                 end++;
 869             }
 870             String word = hands.substring(ix, end);
 871             ix = end+1;
 872             word = word.trim();
 873             if (word.length() == 0) {
 874                 continue;
 875             }
 876             result.add(word);
 877         }
 878         return result.toArray(new String[result.size()]);
 879     }
 880 
 881     /**
 882      * Reinitialize the logging properties and reread the logging configuration
 883      * from the given stream, which should be in java.util.Properties format.
 884      * A PropertyChangeEvent will be fired after the properties are read.
 885      * <p>
 886      * Any log level definitions in the new configuration file will be
 887      * applied using Logger.setLevel(), if the target Logger exists.
 888      *
 889      * @param ins       stream to read properties from
 890      * @exception  SecurityException  if a security manager exists and if
 891      *             the caller does not have LoggingPermission("control").
 892      * @exception  IOException if there are problems reading from the stream.
 893      */
 894     public void readConfiguration(InputStream ins) throws IOException, SecurityException {
 895         checkAccess();
 896         reset();
 897 
 898         // Load the properties
 899         props.load(ins);
 900         // Instantiate new configuration objects.
 901         String names[] = parseClassNames("config");
 902 
 903         for (int i = 0; i < names.length; i++) {
 904             String word = names[i];
 905             try {
 906                 Class clz = ClassLoader.getSystemClassLoader().loadClass(word);
 907                 clz.newInstance();
 908             } catch (Exception ex) {
 909                 System.err.println("Can't load config class \"" + word + "\"");
 910                 System.err.println("" + ex);
 911                 // ex.printStackTrace();
 912             }
 913         }
 914 
 915         // Set levels on any pre-existing loggers, based on the new properties.
 916         setLevelsOnExistingLoggers();
 917 
 918         // Notify any interested parties that our properties have changed.
 919         changes.firePropertyChange(null, null, null);
 920 
 921         // Note that we need to reinitialize global handles when
 922         // they are first referenced.
 923         synchronized (this) {
 924             initializedGlobalHandlers = false;
 925         }
 926     }
 927 
 928     /**
 929      * Get the value of a logging property.
 930      * The method returns null if the property is not found.
 931      * @param name      property name
 932      * @return          property value
 933      */
 934     public String getProperty(String name) {
 935         return props.getProperty(name);
 936     }
 937 
 938     // Package private method to get a String property.
 939     // If the property is not defined we return the given
 940     // default value.
 941     String getStringProperty(String name, String defaultValue) {
 942         String val = getProperty(name);
 943         if (val == null) {
 944             return defaultValue;
 945         }
 946         return val.trim();
 947     }
 948 
 949     // Package private method to get an integer property.
 950     // If the property is not defined or cannot be parsed
 951     // we return the given default value.
 952     int getIntProperty(String name, int defaultValue) {
 953         String val = getProperty(name);
 954         if (val == null) {
 955             return defaultValue;
 956         }
 957         try {
 958             return Integer.parseInt(val.trim());
 959         } catch (Exception ex) {
 960             return defaultValue;
 961         }
 962     }
 963 
 964     // Package private method to get a boolean property.
 965     // If the property is not defined or cannot be parsed
 966     // we return the given default value.
 967     boolean getBooleanProperty(String name, boolean defaultValue) {
 968         String val = getProperty(name);
 969         if (val == null) {
 970             return defaultValue;
 971         }
 972         val = val.toLowerCase();
 973         if (val.equals("true") || val.equals("1")) {
 974             return true;
 975         } else if (val.equals("false") || val.equals("0")) {
 976             return false;
 977         }
 978         return defaultValue;
 979     }
 980 
 981     // Package private method to get a Level property.
 982     // If the property is not defined or cannot be parsed
 983     // we return the given default value.
 984     Level getLevelProperty(String name, Level defaultValue) {
 985         String val = getProperty(name);
 986         if (val == null) {
 987             return defaultValue;
 988         }
 989         try {
 990             return Level.parse(val.trim());
 991         } catch (Exception ex) {
 992             return defaultValue;
 993         }
 994     }
 995 
 996     // Package private method to get a filter property.
 997     // We return an instance of the class named by the "name"
 998     // property. If the property is not defined or has problems
 999     // we return the defaultValue.
1000     Filter getFilterProperty(String name, Filter defaultValue) {
1001         String val = getProperty(name);
1002         try {
1003             if (val != null) {
1004                 Class clz = ClassLoader.getSystemClassLoader().loadClass(val);
1005                 return (Filter) clz.newInstance();
1006             }
1007         } catch (Exception ex) {
1008             // We got one of a variety of exceptions in creating the
1009             // class or creating an instance.
1010             // Drop through.
1011         }
1012         // We got an exception.  Return the defaultValue.
1013         return defaultValue;
1014     }
1015 
1016 
1017     // Package private method to get a formatter property.
1018     // We return an instance of the class named by the "name"
1019     // property. If the property is not defined or has problems
1020     // we return the defaultValue.
1021     Formatter getFormatterProperty(String name, Formatter defaultValue) {
1022         String val = getProperty(name);
1023         try {
1024             if (val != null) {
1025                 Class clz = ClassLoader.getSystemClassLoader().loadClass(val);
1026                 return (Formatter) clz.newInstance();
1027             }
1028         } catch (Exception ex) {
1029             // We got one of a variety of exceptions in creating the
1030             // class or creating an instance.
1031             // Drop through.
1032         }
1033         // We got an exception.  Return the defaultValue.
1034         return defaultValue;
1035     }
1036 
1037     // Private method to load the global handlers.
1038     // We do the real work lazily, when the global handlers
1039     // are first used.
1040     private synchronized void initializeGlobalHandlers() {
1041         if (initializedGlobalHandlers) {
1042             return;
1043         }
1044 
1045         initializedGlobalHandlers = true;
1046 
1047         if (deathImminent) {
1048             // Aaargh...
1049             // The VM is shutting down and our exit hook has been called.
1050             // Avoid allocating global handlers.
1051             return;
1052         }
1053         loadLoggerHandlers(rootLogger, null, "handlers");
1054     }
1055 
1056 
1057     private Permission ourPermission = new LoggingPermission("control", null);
1058 
1059     /**
1060      * Check that the current context is trusted to modify the logging
1061      * configuration.  This requires LoggingPermission("control").
1062      * <p>
1063      * If the check fails we throw a SecurityException, otherwise
1064      * we return normally.
1065      *
1066      * @exception  SecurityException  if a security manager exists and if
1067      *             the caller does not have LoggingPermission("control").
1068      */
1069     public void checkAccess() throws SecurityException {
1070         SecurityManager sm = System.getSecurityManager();
1071         if (sm == null) {
1072             return;
1073         }
1074         sm.checkPermission(ourPermission);
1075     }
1076 
1077     // Nested class to represent a node in our tree of named loggers.
1078     private static class LogNode {
1079         HashMap<String,LogNode> children;
1080         LoggerWeakRef loggerRef;
1081         LogNode parent;
1082 
1083         LogNode(LogNode parent) {
1084             this.parent = parent;
1085         }
1086 
1087         // Recursive method to walk the tree below a node and set
1088         // a new parent logger.
1089         void walkAndSetParent(Logger parent) {
1090             if (children == null) {
1091                 return;
1092             }
1093             Iterator<LogNode> values = children.values().iterator();
1094             while (values.hasNext()) {
1095                 LogNode node = values.next();
1096                 LoggerWeakRef ref = node.loggerRef;
1097                 Logger logger = (ref == null) ? null : ref.get();
1098                 if (logger == null) {
1099                     node.walkAndSetParent(parent);
1100                 } else {
1101                     doSetParent(logger, parent);
1102                 }
1103             }
1104         }
1105     }
1106 
1107     // We use a subclass of Logger for the root logger, so
1108     // that we only instantiate the global handlers when they
1109     // are first needed.
1110     private class RootLogger extends Logger {
1111 
1112         private RootLogger() {
1113             super("", null);
1114             setLevel(defaultLevel);
1115         }
1116 
1117         public void log(LogRecord record) {
1118             // Make sure that the global handlers have been instantiated.
1119             initializeGlobalHandlers();
1120             super.log(record);
1121         }
1122 
1123         public void addHandler(Handler h) {
1124             initializeGlobalHandlers();
1125             super.addHandler(h);
1126         }
1127 
1128         public void removeHandler(Handler h) {
1129             initializeGlobalHandlers();
1130             super.removeHandler(h);
1131         }
1132 
1133         public Handler[] getHandlers() {
1134             initializeGlobalHandlers();
1135             return super.getHandlers();
1136         }
1137     }
1138 
1139 
1140     // Private method to be called when the configuration has
1141     // changed to apply any level settings to any pre-existing loggers.
1142     synchronized private void setLevelsOnExistingLoggers() {
1143         Enumeration enum_ = props.propertyNames();
1144         while (enum_.hasMoreElements()) {
1145             String key = (String)enum_.nextElement();
1146             if (!key.endsWith(".level")) {
1147                 // Not a level definition.
1148                 continue;
1149             }
1150             int ix = key.length() - 6;
1151             String name = key.substring(0, ix);
1152             Level level = getLevelProperty(key, null);
1153             if (level == null) {
1154                 System.err.println("Bad level value for property: " + key);
1155                 continue;
1156             }
1157             Logger l = getLogger(name);
1158             if (l == null) {
1159                 continue;
1160             }
1161             l.setLevel(level);
1162         }
1163     }
1164 
1165     // Management Support
1166     private static LoggingMXBean loggingMXBean = null;
1167     /**
1168      * String representation of the
1169      * {@link javax.management.ObjectName} for the management interface
1170      * for the logging facility.
1171      *
1172      * @see java.lang.management.PlatformLoggingMXBean
1173      * @see java.util.logging.LoggingMXBean
1174      *
1175      * @since 1.5
1176      */
1177     public final static String LOGGING_MXBEAN_NAME
1178         = "java.util.logging:type=Logging";
1179 
1180     /**
1181      * Returns <tt>LoggingMXBean</tt> for managing loggers.
1182      * An alternative way to manage loggers is through the
1183      * {@link java.lang.management.PlatformLoggingMXBean} interface
1184      * that can be obtained by calling:
1185      * <pre>
1186      *     PlatformLoggingMXBean logging = {@link java.lang.management.ManagementFactory#getPlatformMXBean(Class)
1187      *         ManagementFactory.getPlatformMXBean}(PlatformLoggingMXBean.class);
1188      * </pre>
1189      *
1190      * @return a {@link LoggingMXBean} object.
1191      *
1192      * @see java.lang.management.PlatformLoggingMXBean
1193      * @since 1.5
1194      */
1195     public static synchronized LoggingMXBean getLoggingMXBean() {
1196         if (loggingMXBean == null) {
1197             loggingMXBean =  new Logging();
1198         }
1199         return loggingMXBean;
1200     }
1201 
1202 }