1 /*
   2  * Copyright (c) 2000, 2011, 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.getGlobal().setLogManager(manager);
 204                     manager.addLogger(Logger.getGlobal());
 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 
 346     // This method will always return a non-null Logger object.
 347     // Synchronization is not required here. All synchronization for
 348     // adding a new Logger object is handled by addLogger().
 349     Logger demandLogger(String name) {
 350         Logger result = getLogger(name);
 351         if (result == null) {
 352             // only allocate the new logger once
 353             Logger newLogger = new Logger(name, null);
 354             do {
 355                 if (addLogger(newLogger)) {
 356                     // We successfully added the new Logger that we
 357                     // created above so return it without refetching.
 358                     return newLogger;
 359                 }
 360 
 361                 // We didn't add the new Logger that we created above
 362                 // because another thread added a Logger with the same
 363                 // name after our null check above and before our call
 364                 // to addLogger(). We have to refetch the Logger because
 365                 // addLogger() returns a boolean instead of the Logger
 366                 // reference itself. However, if the thread that created
 367                 // the other Logger is not holding a strong reference to
 368                 // the other Logger, then it is possible for the other
 369                 // Logger to be GC'ed after we saw it in addLogger() and
 370                 // before we can refetch it. If it has been GC'ed then
 371                 // we'll just loop around and try again.
 372                 result = getLogger(name);
 373             } while (result == null);
 374         }
 375         return result;
 376     }
 377 
 378     // If logger.getUseParentHandlers() returns 'true' and any of the logger's
 379     // parents have levels or handlers defined, make sure they are instantiated.
 380     private void processParentHandlers(Logger logger, String name) {
 381         int ix = 1;
 382         for (;;) {
 383             int ix2 = name.indexOf(".", ix);
 384             if (ix2 < 0) {
 385                 break;
 386             }
 387             String pname = name.substring(0,ix2);
 388 
 389             if (getProperty(pname+".level")    != null ||
 390                 getProperty(pname+".handlers") != null) {
 391                 // This pname has a level/handlers definition.
 392                 // Make sure it exists.
 393                 demandLogger(pname);
 394             }
 395             ix = ix2+1;
 396         }
 397     }
 398 
 399     // Add new per logger handlers.
 400     // We need to raise privilege here. All our decisions will
 401     // be made based on the logging configuration, which can
 402     // only be modified by trusted code.
 403     private void loadLoggerHandlers(final Logger logger, final String name,
 404                                     final String handlersPropertyName) {
 405         AccessController.doPrivileged(new PrivilegedAction<Object>() {
 406             public Object run() {
 407                 if (logger != rootLogger) {
 408                     boolean useParent = getBooleanProperty(name + ".useParentHandlers", true);
 409                     if (!useParent) {
 410                         logger.setUseParentHandlers(false);
 411                     }
 412                 }
 413 
 414                 String names[] = parseClassNames(handlersPropertyName);
 415                 for (int i = 0; i < names.length; i++) {
 416                     String word = names[i];
 417                     try {
 418                         Class<?>   clz = ClassLoader.getSystemClassLoader().loadClass(word);
 419                         Handler hdl = (Handler) clz.newInstance();
 420                         try {
 421                             // Check if there is a property defining the
 422                             // this handler's level.
 423                             String levs = getProperty(word + ".level");
 424                             if (levs != null) {
 425                                 hdl.setLevel(Level.parse(levs));
 426                             }
 427                         } catch (Exception ex) {
 428                             System.err.println("Can't set level for " + word);
 429                             // Probably a bad level. Drop through.
 430                         }
 431                         // Add this Handler to the logger
 432                         logger.addHandler(hdl);
 433                     } catch (Exception ex) {
 434                         System.err.println("Can't load log handler \"" + word + "\"");
 435                         System.err.println("" + ex);
 436                         ex.printStackTrace();
 437                     }
 438                 }
 439                 return null;
 440             }});
 441     }
 442 
 443 
 444     // loggerRefQueue holds LoggerWeakRef objects for Logger objects
 445     // that have been GC'ed.
 446     private final ReferenceQueue<Logger> loggerRefQueue
 447         = new ReferenceQueue<>();
 448 
 449     // Package-level inner class.
 450     // Helper class for managing WeakReferences to Logger objects.
 451     //
 452     // LogManager.namedLoggers
 453     //     - has weak references to all named Loggers
 454     //     - namedLoggers keeps the LoggerWeakRef objects for the named
 455     //       Loggers around until we can deal with the book keeping for
 456     //       the named Logger that is being GC'ed.
 457     // LogManager.LogNode.loggerRef
 458     //     - has a weak reference to a named Logger
 459     //     - the LogNode will also keep the LoggerWeakRef objects for
 460     //       the named Loggers around; currently LogNodes never go away.
 461     // Logger.kids
 462     //     - has a weak reference to each direct child Logger; this
 463     //       includes anonymous and named Loggers
 464     //     - anonymous Loggers are always children of the rootLogger
 465     //       which is a strong reference; rootLogger.kids keeps the
 466     //       LoggerWeakRef objects for the anonymous Loggers around
 467     //       until we can deal with the book keeping.
 468     //
 469     final class LoggerWeakRef extends WeakReference<Logger> {
 470         private String                name;       // for namedLoggers cleanup
 471         private LogNode               node;       // for loggerRef cleanup
 472         private WeakReference<Logger> parentRef;  // for kids cleanup
 473 
 474         LoggerWeakRef(Logger logger) {
 475             super(logger, loggerRefQueue);
 476 
 477             name = logger.getName();  // save for namedLoggers cleanup
 478         }
 479 
 480         // dispose of this LoggerWeakRef object
 481         void dispose() {
 482             if (node != null) {
 483                 // if we have a LogNode, then we were a named Logger
 484                 // so clear namedLoggers weak ref to us
 485                 manager.namedLoggers.remove(name);
 486                 name = null;  // clear our ref to the Logger's name
 487 
 488                 node.loggerRef = null;  // clear LogNode's weak ref to us
 489                 node = null;            // clear our ref to LogNode
 490             }
 491 
 492             if (parentRef != null) {
 493                 // this LoggerWeakRef has or had a parent Logger
 494                 Logger parent = parentRef.get();
 495                 if (parent != null) {
 496                     // the parent Logger is still there so clear the
 497                     // parent Logger's weak ref to us
 498                     parent.removeChildLogger(this);
 499                 }
 500                 parentRef = null;  // clear our weak ref to the parent Logger
 501             }
 502         }
 503 
 504         // set the node field to the specified value
 505         void setNode(LogNode node) {
 506             this.node = node;
 507         }
 508 
 509         // set the parentRef field to the specified value
 510         void setParentRef(WeakReference<Logger> parentRef) {
 511             this.parentRef = parentRef;
 512         }
 513     }
 514 
 515     // Package-level method.
 516     // Drain some Logger objects that have been GC'ed.
 517     //
 518     // drainLoggerRefQueueBounded() is called by addLogger() below
 519     // and by Logger.getAnonymousLogger(String) so we'll drain up to
 520     // MAX_ITERATIONS GC'ed Loggers for every Logger we add.
 521     //
 522     // On a WinXP VMware client, a MAX_ITERATIONS value of 400 gives
 523     // us about a 50/50 mix in increased weak ref counts versus
 524     // decreased weak ref counts in the AnonLoggerWeakRefLeak test.
 525     // Here are stats for cleaning up sets of 400 anonymous Loggers:
 526     //   - test duration 1 minute
 527     //   - sample size of 125 sets of 400
 528     //   - average: 1.99 ms
 529     //   - minimum: 0.57 ms
 530     //   - maximum: 25.3 ms
 531     //
 532     // The same config gives us a better decreased weak ref count
 533     // than increased weak ref count in the LoggerWeakRefLeak test.
 534     // Here are stats for cleaning up sets of 400 named Loggers:
 535     //   - test duration 2 minutes
 536     //   - sample size of 506 sets of 400
 537     //   - average: 0.57 ms
 538     //   - minimum: 0.02 ms
 539     //   - maximum: 10.9 ms
 540     //
 541     private final static int MAX_ITERATIONS = 400;
 542     final synchronized void drainLoggerRefQueueBounded() {
 543         for (int i = 0; i < MAX_ITERATIONS; i++) {
 544             if (loggerRefQueue == null) {
 545                 // haven't finished loading LogManager yet
 546                 break;
 547             }
 548 
 549             LoggerWeakRef ref = (LoggerWeakRef) loggerRefQueue.poll();
 550             if (ref == null) {
 551                 break;
 552             }
 553             // a Logger object has been GC'ed so clean it up
 554             ref.dispose();
 555         }
 556     }
 557 
 558     /**
 559      * Add a named logger.  This does nothing and returns false if a logger
 560      * with the same name is already registered.
 561      * <p>
 562      * The Logger factory methods call this method to register each
 563      * newly created Logger.
 564      * <p>
 565      * The application should retain its own reference to the Logger
 566      * object to avoid it being garbage collected.  The LogManager
 567      * may only retain a weak reference.
 568      *
 569      * @param   logger the new logger.
 570      * @return  true if the argument logger was registered successfully,
 571      *          false if a logger of that name already exists.
 572      * @exception NullPointerException if the logger name is null.
 573      */
 574     public synchronized boolean addLogger(Logger logger) {
 575         final String name = logger.getName();
 576         if (name == null) {
 577             throw new NullPointerException();
 578         }
 579 
 580         // cleanup some Loggers that have been GC'ed
 581         drainLoggerRefQueueBounded();
 582 
 583         LoggerWeakRef ref = namedLoggers.get(name);
 584         if (ref != null) {
 585             if (ref.get() == null) {
 586                 // It's possible that the Logger was GC'ed after the
 587                 // drainLoggerRefQueueBounded() call above so allow
 588                 // a new one to be registered.
 589                 namedLoggers.remove(name);
 590             } else {
 591                 // We already have a registered logger with the given name.
 592                 return false;
 593             }
 594         }
 595 
 596         // We're adding a new logger.
 597         // Note that we are creating a weak reference here.
 598         ref = new LoggerWeakRef(logger);
 599         namedLoggers.put(name, ref);
 600 
 601         // Apply any initial level defined for the new logger.
 602         Level level = getLevelProperty(name+".level", null);
 603         if (level != null) {
 604             doSetLevel(logger, level);
 605         }
 606 
 607         // Do we have a per logger handler too?
 608         // Note: this will add a 200ms penalty
 609         loadLoggerHandlers(logger, name, name+".handlers");
 610         processParentHandlers(logger, name);
 611 
 612         // Find the new node and its parent.
 613         LogNode node = findNode(name);
 614         node.loggerRef = ref;
 615         Logger parent = null;
 616         LogNode nodep = node.parent;
 617         while (nodep != null) {
 618             LoggerWeakRef nodeRef = nodep.loggerRef;
 619             if (nodeRef != null) {
 620                 parent = nodeRef.get();
 621                 if (parent != null) {
 622                     break;
 623                 }
 624             }
 625             nodep = nodep.parent;
 626         }
 627 
 628         if (parent != null) {
 629             doSetParent(logger, parent);
 630         }
 631         // Walk over the children and tell them we are their new parent.
 632         node.walkAndSetParent(logger);
 633 
 634         // new LogNode is ready so tell the LoggerWeakRef about it
 635         ref.setNode(node);
 636 
 637         return true;
 638     }
 639 
 640 
 641     // Private method to set a level on a logger.
 642     // If necessary, we raise privilege before doing the call.
 643     private static void doSetLevel(final Logger logger, final Level level) {
 644         SecurityManager sm = System.getSecurityManager();
 645         if (sm == null) {
 646             // There is no security manager, so things are easy.
 647             logger.setLevel(level);
 648             return;
 649         }
 650         // There is a security manager.  Raise privilege before
 651         // calling setLevel.
 652         AccessController.doPrivileged(new PrivilegedAction<Object>() {
 653             public Object run() {
 654                 logger.setLevel(level);
 655                 return null;
 656             }});
 657     }
 658 
 659 
 660 
 661     // Private method to set a parent on a logger.
 662     // If necessary, we raise privilege before doing the setParent call.
 663     private static void doSetParent(final Logger logger, final Logger parent) {
 664         SecurityManager sm = System.getSecurityManager();
 665         if (sm == null) {
 666             // There is no security manager, so things are easy.
 667             logger.setParent(parent);
 668             return;
 669         }
 670         // There is a security manager.  Raise privilege before
 671         // calling setParent.
 672         AccessController.doPrivileged(new PrivilegedAction<Object>() {
 673             public Object run() {
 674                 logger.setParent(parent);
 675                 return null;
 676             }});
 677     }
 678 
 679     // Find a node in our tree of logger nodes.
 680     // If necessary, create it.
 681     private LogNode findNode(String name) {
 682         if (name == null || name.equals("")) {
 683             return root;
 684         }
 685         LogNode node = root;
 686         while (name.length() > 0) {
 687             int ix = name.indexOf(".");
 688             String head;
 689             if (ix > 0) {
 690                 head = name.substring(0,ix);
 691                 name = name.substring(ix+1);
 692             } else {
 693                 head = name;
 694                 name = "";
 695             }
 696             if (node.children == null) {
 697                 node.children = new HashMap<>();
 698             }
 699             LogNode child = node.children.get(head);
 700             if (child == null) {
 701                 child = new LogNode(node);
 702                 node.children.put(head, child);
 703             }
 704             node = child;
 705         }
 706         return node;
 707     }
 708 
 709     /**
 710      * Method to find a named logger.
 711      * <p>
 712      * Note that since untrusted code may create loggers with
 713      * arbitrary names this method should not be relied on to
 714      * find Loggers for security sensitive logging.
 715      * It is also important to note that the Logger associated with the
 716      * String {@code name} may be garbage collected at any time if there
 717      * is no strong reference to the Logger. The caller of this method
 718      * must check the return value for null in order to properly handle
 719      * the case where the Logger has been garbage collected.
 720      * <p>
 721      * @param name name of the logger
 722      * @return  matching logger or null if none is found
 723      */
 724     public synchronized Logger getLogger(String name) {
 725         LoggerWeakRef ref = namedLoggers.get(name);
 726         if (ref == null) {
 727             return null;
 728         }
 729         Logger logger = ref.get();
 730         if (logger == null) {
 731             // Hashtable holds stale weak reference
 732             // to a logger which has been GC-ed.
 733             namedLoggers.remove(name);
 734         }
 735         return logger;
 736     }
 737 
 738     /**
 739      * Get an enumeration of known logger names.
 740      * <p>
 741      * Note:  Loggers may be added dynamically as new classes are loaded.
 742      * This method only reports on the loggers that are currently registered.
 743      * It is also important to note that this method only returns the name
 744      * of a Logger, not a strong reference to the Logger itself.
 745      * The returned String does nothing to prevent the Logger from being
 746      * garbage collected. In particular, if the returned name is passed
 747      * to {@code LogManager.getLogger()}, then the caller must check the
 748      * return value from {@code LogManager.getLogger()} for null to properly
 749      * handle the case where the Logger has been garbage collected in the
 750      * time since its name was returned by this method.
 751      * <p>
 752      * @return  enumeration of logger name strings
 753      */
 754     public synchronized Enumeration<String> getLoggerNames() {
 755         return namedLoggers.keys();
 756     }
 757 
 758     /**
 759      * Reinitialize the logging properties and reread the logging configuration.
 760      * <p>
 761      * The same rules are used for locating the configuration properties
 762      * as are used at startup.  So normally the logging properties will
 763      * be re-read from the same file that was used at startup.
 764      * <P>
 765      * Any log level definitions in the new configuration file will be
 766      * applied using Logger.setLevel(), if the target Logger exists.
 767      * <p>
 768      * A PropertyChangeEvent will be fired after the properties are read.
 769      *
 770      * @exception  SecurityException  if a security manager exists and if
 771      *             the caller does not have LoggingPermission("control").
 772      * @exception  IOException if there are IO problems reading the configuration.
 773      */
 774     public void readConfiguration() throws IOException, SecurityException {
 775         checkAccess();
 776 
 777         // if a configuration class is specified, load it and use it.
 778         String cname = System.getProperty("java.util.logging.config.class");
 779         if (cname != null) {
 780             try {
 781                 // Instantiate the named class.  It is its constructor's
 782                 // responsibility to initialize the logging configuration, by
 783                 // calling readConfiguration(InputStream) with a suitable stream.
 784                 try {
 785                     Class<?> clz = ClassLoader.getSystemClassLoader().loadClass(cname);
 786                     clz.newInstance();
 787                     return;
 788                 } catch (ClassNotFoundException ex) {
 789                     Class<?> clz = Thread.currentThread().getContextClassLoader().loadClass(cname);
 790                     clz.newInstance();
 791                     return;
 792                 }
 793             } catch (Exception ex) {
 794                 System.err.println("Logging configuration class \"" + cname + "\" failed");
 795                 System.err.println("" + ex);
 796                 // keep going and useful config file.
 797             }
 798         }
 799 
 800         String fname = System.getProperty("java.util.logging.config.file");
 801         if (fname == null) {
 802             fname = System.getProperty("java.home");
 803             if (fname == null) {
 804                 throw new Error("Can't find java.home ??");
 805             }
 806             File f = new File(fname, "lib");
 807             f = new File(f, "logging.properties");
 808             fname = f.getCanonicalPath();
 809         }
 810         InputStream in = new FileInputStream(fname);
 811         BufferedInputStream bin = new BufferedInputStream(in);
 812         try {
 813             readConfiguration(bin);
 814         } finally {
 815             if (in != null) {
 816                 in.close();
 817             }
 818         }
 819     }
 820 
 821     /**
 822      * Reset the logging configuration.
 823      * <p>
 824      * For all named loggers, the reset operation removes and closes
 825      * all Handlers and (except for the root logger) sets the level
 826      * to null.  The root logger's level is set to Level.INFO.
 827      *
 828      * @exception  SecurityException  if a security manager exists and if
 829      *             the caller does not have LoggingPermission("control").
 830      */
 831 
 832     public void reset() throws SecurityException {
 833         checkAccess();
 834         synchronized (this) {
 835             props = new Properties();
 836             // Since we are doing a reset we no longer want to initialize
 837             // the global handlers, if they haven't been initialized yet.
 838             initializedGlobalHandlers = true;
 839         }
 840         Enumeration<String> enum_ = getLoggerNames();
 841         while (enum_.hasMoreElements()) {
 842             String name = enum_.nextElement();
 843             resetLogger(name);
 844         }
 845     }
 846 
 847 
 848     // Private method to reset an individual target logger.
 849     private void resetLogger(String name) {
 850         Logger logger = getLogger(name);
 851         if (logger == null) {
 852             return;
 853         }
 854         // Close all the Logger's handlers.
 855         Handler[] targets = logger.getHandlers();
 856         for (int i = 0; i < targets.length; i++) {
 857             Handler h = targets[i];
 858             logger.removeHandler(h);
 859             try {
 860                 h.close();
 861             } catch (Exception ex) {
 862                 // Problems closing a handler?  Keep going...
 863             }
 864         }
 865         if (name != null && name.equals("")) {
 866             // This is the root logger.
 867             logger.setLevel(defaultLevel);
 868         } else {
 869             logger.setLevel(null);
 870         }
 871     }
 872 
 873     // get a list of whitespace separated classnames from a property.
 874     private String[] parseClassNames(String propertyName) {
 875         String hands = getProperty(propertyName);
 876         if (hands == null) {
 877             return new String[0];
 878         }
 879         hands = hands.trim();
 880         int ix = 0;
 881         Vector<String> result = new Vector<>();
 882         while (ix < hands.length()) {
 883             int end = ix;
 884             while (end < hands.length()) {
 885                 if (Character.isWhitespace(hands.charAt(end))) {
 886                     break;
 887                 }
 888                 if (hands.charAt(end) == ',') {
 889                     break;
 890                 }
 891                 end++;
 892             }
 893             String word = hands.substring(ix, end);
 894             ix = end+1;
 895             word = word.trim();
 896             if (word.length() == 0) {
 897                 continue;
 898             }
 899             result.add(word);
 900         }
 901         return result.toArray(new String[result.size()]);
 902     }
 903 
 904     /**
 905      * Reinitialize the logging properties and reread the logging configuration
 906      * from the given stream, which should be in java.util.Properties format.
 907      * A PropertyChangeEvent will be fired after the properties are read.
 908      * <p>
 909      * Any log level definitions in the new configuration file will be
 910      * applied using Logger.setLevel(), if the target Logger exists.
 911      *
 912      * @param ins       stream to read properties from
 913      * @exception  SecurityException  if a security manager exists and if
 914      *             the caller does not have LoggingPermission("control").
 915      * @exception  IOException if there are problems reading from the stream.
 916      */
 917     public void readConfiguration(InputStream ins) throws IOException, SecurityException {
 918         checkAccess();
 919         reset();
 920 
 921         // Load the properties
 922         props.load(ins);
 923         // Instantiate new configuration objects.
 924         String names[] = parseClassNames("config");
 925 
 926         for (int i = 0; i < names.length; i++) {
 927             String word = names[i];
 928             try {
 929                 Class<?> clz = ClassLoader.getSystemClassLoader().loadClass(word);
 930                 clz.newInstance();
 931             } catch (Exception ex) {
 932                 System.err.println("Can't load config class \"" + word + "\"");
 933                 System.err.println("" + ex);
 934                 // ex.printStackTrace();
 935             }
 936         }
 937 
 938         // Set levels on any pre-existing loggers, based on the new properties.
 939         setLevelsOnExistingLoggers();
 940 
 941         // Notify any interested parties that our properties have changed.
 942         changes.firePropertyChange(null, null, null);
 943 
 944         // Note that we need to reinitialize global handles when
 945         // they are first referenced.
 946         synchronized (this) {
 947             initializedGlobalHandlers = false;
 948         }
 949     }
 950 
 951     /**
 952      * Get the value of a logging property.
 953      * The method returns null if the property is not found.
 954      * @param name      property name
 955      * @return          property value
 956      */
 957     public String getProperty(String name) {
 958         return props.getProperty(name);
 959     }
 960 
 961     // Package private method to get a String property.
 962     // If the property is not defined we return the given
 963     // default value.
 964     String getStringProperty(String name, String defaultValue) {
 965         String val = getProperty(name);
 966         if (val == null) {
 967             return defaultValue;
 968         }
 969         return val.trim();
 970     }
 971 
 972     // Package private method to get an integer property.
 973     // If the property is not defined or cannot be parsed
 974     // we return the given default value.
 975     int getIntProperty(String name, int defaultValue) {
 976         String val = getProperty(name);
 977         if (val == null) {
 978             return defaultValue;
 979         }
 980         try {
 981             return Integer.parseInt(val.trim());
 982         } catch (Exception ex) {
 983             return defaultValue;
 984         }
 985     }
 986 
 987     // Package private method to get a boolean property.
 988     // If the property is not defined or cannot be parsed
 989     // we return the given default value.
 990     boolean getBooleanProperty(String name, boolean defaultValue) {
 991         String val = getProperty(name);
 992         if (val == null) {
 993             return defaultValue;
 994         }
 995         val = val.toLowerCase();
 996         if (val.equals("true") || val.equals("1")) {
 997             return true;
 998         } else if (val.equals("false") || val.equals("0")) {
 999             return false;
1000         }
1001         return defaultValue;
1002     }
1003 
1004     // Package private method to get a Level property.
1005     // If the property is not defined or cannot be parsed
1006     // we return the given default value.
1007     Level getLevelProperty(String name, Level defaultValue) {
1008         String val = getProperty(name);
1009         if (val == null) {
1010             return defaultValue;
1011         }
1012         try {
1013             return Level.parse(val.trim());
1014         } catch (Exception ex) {
1015             return defaultValue;
1016         }
1017     }
1018 
1019     // Package private method to get a filter property.
1020     // We return an instance of the class named by the "name"
1021     // property. If the property is not defined or has problems
1022     // we return the defaultValue.
1023     Filter getFilterProperty(String name, Filter defaultValue) {
1024         String val = getProperty(name);
1025         try {
1026             if (val != null) {
1027                 Class<?> clz = ClassLoader.getSystemClassLoader().loadClass(val);
1028                 return (Filter) clz.newInstance();
1029             }
1030         } catch (Exception ex) {
1031             // We got one of a variety of exceptions in creating the
1032             // class or creating an instance.
1033             // Drop through.
1034         }
1035         // We got an exception.  Return the defaultValue.
1036         return defaultValue;
1037     }
1038 
1039 
1040     // Package private method to get a formatter property.
1041     // We return an instance of the class named by the "name"
1042     // property. If the property is not defined or has problems
1043     // we return the defaultValue.
1044     Formatter getFormatterProperty(String name, Formatter defaultValue) {
1045         String val = getProperty(name);
1046         try {
1047             if (val != null) {
1048                 Class<?> clz = ClassLoader.getSystemClassLoader().loadClass(val);
1049                 return (Formatter) clz.newInstance();
1050             }
1051         } catch (Exception ex) {
1052             // We got one of a variety of exceptions in creating the
1053             // class or creating an instance.
1054             // Drop through.
1055         }
1056         // We got an exception.  Return the defaultValue.
1057         return defaultValue;
1058     }
1059 
1060     // Private method to load the global handlers.
1061     // We do the real work lazily, when the global handlers
1062     // are first used.
1063     private synchronized void initializeGlobalHandlers() {
1064         if (initializedGlobalHandlers) {
1065             return;
1066         }
1067 
1068         initializedGlobalHandlers = true;
1069 
1070         if (deathImminent) {
1071             // Aaargh...
1072             // The VM is shutting down and our exit hook has been called.
1073             // Avoid allocating global handlers.
1074             return;
1075         }
1076         loadLoggerHandlers(rootLogger, null, "handlers");
1077     }
1078 
1079 
1080     private Permission ourPermission = new LoggingPermission("control", null);
1081 
1082     /**
1083      * Check that the current context is trusted to modify the logging
1084      * configuration.  This requires LoggingPermission("control").
1085      * <p>
1086      * If the check fails we throw a SecurityException, otherwise
1087      * we return normally.
1088      *
1089      * @exception  SecurityException  if a security manager exists and if
1090      *             the caller does not have LoggingPermission("control").
1091      */
1092     public void checkAccess() throws SecurityException {
1093         SecurityManager sm = System.getSecurityManager();
1094         if (sm == null) {
1095             return;
1096         }
1097         sm.checkPermission(ourPermission);
1098     }
1099 
1100     // Nested class to represent a node in our tree of named loggers.
1101     private static class LogNode {
1102         HashMap<String,LogNode> children;
1103         LoggerWeakRef loggerRef;
1104         LogNode parent;
1105 
1106         LogNode(LogNode parent) {
1107             this.parent = parent;
1108         }
1109 
1110         // Recursive method to walk the tree below a node and set
1111         // a new parent logger.
1112         void walkAndSetParent(Logger parent) {
1113             if (children == null) {
1114                 return;
1115             }
1116             Iterator<LogNode> values = children.values().iterator();
1117             while (values.hasNext()) {
1118                 LogNode node = values.next();
1119                 LoggerWeakRef ref = node.loggerRef;
1120                 Logger logger = (ref == null) ? null : ref.get();
1121                 if (logger == null) {
1122                     node.walkAndSetParent(parent);
1123                 } else {
1124                     doSetParent(logger, parent);
1125                 }
1126             }
1127         }
1128     }
1129 
1130     // We use a subclass of Logger for the root logger, so
1131     // that we only instantiate the global handlers when they
1132     // are first needed.
1133     private class RootLogger extends Logger {
1134 
1135         private RootLogger() {
1136             super("", null);
1137             setLevel(defaultLevel);
1138         }
1139 
1140         public void log(LogRecord record) {
1141             // Make sure that the global handlers have been instantiated.
1142             initializeGlobalHandlers();
1143             super.log(record);
1144         }
1145 
1146         public void addHandler(Handler h) {
1147             initializeGlobalHandlers();
1148             super.addHandler(h);
1149         }
1150 
1151         public void removeHandler(Handler h) {
1152             initializeGlobalHandlers();
1153             super.removeHandler(h);
1154         }
1155 
1156         public Handler[] getHandlers() {
1157             initializeGlobalHandlers();
1158             return super.getHandlers();
1159         }
1160     }
1161 
1162 
1163     // Private method to be called when the configuration has
1164     // changed to apply any level settings to any pre-existing loggers.
1165     synchronized private void setLevelsOnExistingLoggers() {
1166         Enumeration<?> enum_ = props.propertyNames();
1167         while (enum_.hasMoreElements()) {
1168             String key = (String)enum_.nextElement();
1169             if (!key.endsWith(".level")) {
1170                 // Not a level definition.
1171                 continue;
1172             }
1173             int ix = key.length() - 6;
1174             String name = key.substring(0, ix);
1175             Level level = getLevelProperty(key, null);
1176             if (level == null) {
1177                 System.err.println("Bad level value for property: " + key);
1178                 continue;
1179             }
1180             Logger l = getLogger(name);
1181             if (l == null) {
1182                 continue;
1183             }
1184             l.setLevel(level);
1185         }
1186     }
1187 
1188     // Management Support
1189     private static LoggingMXBean loggingMXBean = null;
1190     /**
1191      * String representation of the
1192      * {@link javax.management.ObjectName} for the management interface
1193      * for the logging facility.
1194      *
1195      * @see java.lang.management.PlatformLoggingMXBean
1196      * @see java.util.logging.LoggingMXBean
1197      *
1198      * @since 1.5
1199      */
1200     public final static String LOGGING_MXBEAN_NAME
1201         = "java.util.logging:type=Logging";
1202 
1203     /**
1204      * Returns <tt>LoggingMXBean</tt> for managing loggers.
1205      * An alternative way to manage loggers is through the
1206      * {@link java.lang.management.PlatformLoggingMXBean} interface
1207      * that can be obtained by calling:
1208      * <pre>
1209      *     PlatformLoggingMXBean logging = {@link java.lang.management.ManagementFactory#getPlatformMXBean(Class)
1210      *         ManagementFactory.getPlatformMXBean}(PlatformLoggingMXBean.class);
1211      * </pre>
1212      *
1213      * @return a {@link LoggingMXBean} object.
1214      *
1215      * @see java.lang.management.PlatformLoggingMXBean
1216      * @since 1.5
1217      */
1218     public static synchronized LoggingMXBean getLoggingMXBean() {
1219         if (loggingMXBean == null) {
1220             loggingMXBean =  new Logging();
1221         }
1222         return loggingMXBean;
1223     }
1224 
1225 }