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