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