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