OSDN Git Service

Merged gcj-eclipse branch to trunk.
[pf3gnuchains/gcc-fork.git] / libjava / classpath / java / util / logging / LogManager.java
1 /* LogManager.java -- a class for maintaining Loggers and managing
2    configuration properties
3    Copyright (C) 2002, 2005, 2006 Free Software Foundation, Inc.
4
5 This file is part of GNU Classpath.
6
7 GNU Classpath is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2, or (at your option)
10 any later version.
11
12 GNU Classpath is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15 General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with GNU Classpath; see the file COPYING.  If not, write to the
19 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
20 02110-1301 USA.
21
22 Linking this library statically or dynamically with other modules is
23 making a combined work based on this library.  Thus, the terms and
24 conditions of the GNU General Public License cover the whole
25 combination.
26
27 As a special exception, the copyright holders of this library give you
28 permission to link this library with independent modules to produce an
29 executable, regardless of the license terms of these independent
30 modules, and to copy and distribute the resulting executable under
31 terms of your choice, provided that you also meet, for each linked
32 independent module, the terms and conditions of the license of that
33 module.  An independent module is a module which is not derived from
34 or based on this library.  If you modify this library, you may extend
35 this exception to your version of the library, but you are not
36 obligated to do so.  If you do not wish to do so, delete this
37 exception statement from your version. */
38
39
40 package java.util.logging;
41
42 import gnu.classpath.SystemProperties;
43
44 import java.beans.PropertyChangeListener;
45 import java.beans.PropertyChangeSupport;
46 import java.io.ByteArrayInputStream;
47 import java.io.IOException;
48 import java.io.InputStream;
49 import java.lang.ref.WeakReference;
50 import java.net.URL;
51 import java.util.Collections;
52 import java.util.Enumeration;
53 import java.util.HashMap;
54 import java.util.Iterator;
55 import java.util.List;
56 import java.util.Map;
57 import java.util.Properties;
58 import java.util.StringTokenizer;
59
60 /**
61  * The <code>LogManager</code> maintains a hierarchical namespace
62  * of Logger objects and manages properties for configuring the logging
63  * framework. There exists only one single <code>LogManager</code>
64  * per virtual machine. This instance can be retrieved using the
65  * static method {@link #getLogManager()}.
66  *
67  * <p><strong>Configuration Process:</strong> The global LogManager
68  * object is created and configured when the class
69  * <code>java.util.logging.LogManager</code> is initialized.
70  * The configuration process includes the subsequent steps:
71  *
72  * <ul>
73  * <li>If the system property <code>java.util.logging.manager</code>
74  *     is set to the name of a subclass of
75  *     <code>java.util.logging.LogManager</code>, an instance of
76  *     that subclass is created and becomes the global LogManager.
77  *     Otherwise, a new instance of LogManager is created.</li>
78  * <li>The <code>LogManager</code> constructor tries to create
79  *     a new instance of the class specified by the system
80  *     property <code>java.util.logging.config.class</code>.
81  *     Typically, the constructor of this class will call
82  *     <code>LogManager.getLogManager().readConfiguration(java.io.InputStream)</code>
83  *     for configuring the logging framework.
84  *     The configuration process stops at this point if
85  *     the system property <code>java.util.logging.config.class</code>
86  *     is set (irrespective of whether the class constructor
87  *     could be called or an exception was thrown).</li>
88  *
89  * <li>If the system property <code>java.util.logging.config.class</code>
90  *     is <em>not</em> set, the configuration parameters are read in from
91  *     a file and passed to
92  *     {@link #readConfiguration(java.io.InputStream)}.
93  *     The name and location of this file are specified by the system
94  *     property <code>java.util.logging.config.file</code>.</li>
95  * <li>If the system property <code>java.util.logging.config.file</code>
96  *     is not set, however, the contents of the URL
97  *     "{gnu.classpath.home.url}/logging.properties" are passed to
98  *     {@link #readConfiguration(java.io.InputStream)}.
99  *     Here, "{gnu.classpath.home.url}" stands for the value of
100  *     the system property <code>gnu.classpath.home.url</code>.</li>
101  * </ul>
102  *
103  * <p>The <code>LogManager</code> has a level of <code>INFO</code> by
104  * default, and this will be inherited by <code>Logger</code>s unless they
105  * override it either by properties or programmatically.
106  *
107  * @author Sascha Brawer (brawer@acm.org)
108  */
109 public class LogManager
110 {
111   /**
112    * The object name for the logging management bean.
113    * @since 1.5
114    */
115   public static final String LOGGING_MXBEAN_NAME
116     = "java.util.logging:type=Logging";
117
118   /**
119    * The singleton LogManager instance.
120    */
121   private static LogManager logManager;
122
123   /**
124    * The singleton logging bean.
125    */
126   private static LoggingMXBean loggingBean;
127
128   /**
129    * The registered named loggers; maps the name of a Logger to
130    * a WeakReference to it.
131    */
132   private Map<String, WeakReference<Logger>> loggers;
133
134   /**
135    * The properties for the logging framework which have been
136    * read in last.
137    */
138   private Properties properties;
139
140   /**
141    * A delegate object that provides support for handling
142    * PropertyChangeEvents.  The API specification does not
143    * mention which bean should be the source in the distributed
144    * PropertyChangeEvents, but Mauve test code has determined that
145    * the Sun J2SE 1.4 reference implementation uses the LogManager
146    * class object. This is somewhat strange, as the class object
147    * is not the bean with which listeners have to register, but
148    * there is no reason for the GNU Classpath implementation to
149    * behave differently from the reference implementation in
150    * this case.
151    */
152   private final PropertyChangeSupport pcs = new PropertyChangeSupport( /* source bean */
153                                                                       LogManager.class);
154
155   protected LogManager()
156   {
157     loggers = new HashMap();
158   }
159
160   /**
161    * Returns the globally shared LogManager instance.
162    */
163   public static synchronized LogManager getLogManager()
164   {
165     if (logManager == null)
166       {
167         logManager = makeLogManager();
168         initLogManager();
169       }
170     return logManager;
171   }
172
173   private static final String MANAGER_PROPERTY = "java.util.logging.manager";
174
175   private static LogManager makeLogManager()
176   {
177     String managerClassName = SystemProperties.getProperty(MANAGER_PROPERTY);
178     LogManager manager = (LogManager) createInstance
179       (managerClassName, LogManager.class, MANAGER_PROPERTY);
180     if (manager == null)
181       manager = new LogManager();
182     return manager;
183   }
184
185   private static final String CONFIG_PROPERTY = "java.util.logging.config.class";
186
187   private static void initLogManager()
188   {
189     LogManager manager = getLogManager();
190     Logger.root.setLevel(Level.INFO);
191     manager.addLogger(Logger.root);
192
193     /* The Javadoc description of the class explains
194      * what is going on here.
195      */
196     Object configurator = createInstance(System.getProperty(CONFIG_PROPERTY),
197                                          /* must be instance of */ Object.class,
198                                          CONFIG_PROPERTY);
199
200     try
201       {
202         if (configurator == null)
203           manager.readConfiguration();
204       }
205     catch (IOException ex)
206       {
207         /* FIXME: Is it ok to ignore exceptions here? */
208       }
209   }
210
211   /**
212    * Registers a listener which will be notified when the
213    * logging properties are re-read.
214    */
215   public synchronized void addPropertyChangeListener(PropertyChangeListener listener)
216   {
217     /* do not register null. */
218     listener.getClass();
219
220     pcs.addPropertyChangeListener(listener);
221   }
222
223   /**
224    * Unregisters a listener.
225    *
226    * If <code>listener</code> has not been registered previously,
227    * nothing happens.  Also, no exception is thrown if
228    * <code>listener</code> is <code>null</code>.
229    */
230   public synchronized void removePropertyChangeListener(PropertyChangeListener listener)
231   {
232     if (listener != null)
233       pcs.removePropertyChangeListener(listener);
234   }
235
236   /**
237    * Adds a named logger.  If a logger with the same name has
238    * already been registered, the method returns <code>false</code>
239    * without adding the logger.
240    *
241    * <p>The <code>LogManager</code> only keeps weak references
242    * to registered loggers.  Therefore, names can become available
243    * after automatic garbage collection.
244    *
245    * @param logger the logger to be added.
246    *
247    * @return <code>true</code>if <code>logger</code> was added,
248    *         <code>false</code> otherwise.
249    *
250    * @throws NullPointerException if <code>name</code> is
251    *         <code>null</code>.
252    */
253   public synchronized boolean addLogger(Logger logger)
254   {
255     /* To developers thinking about to remove the 'synchronized'
256      * declaration from this method: Please read the comment
257      * in java.util.logging.Logger.getLogger(String, String)
258      * and make sure that whatever you change wrt. synchronization
259      * does not endanger thread-safety of Logger.getLogger.
260      * The current implementation of Logger.getLogger assumes
261      * that LogManager does its synchronization on the globally
262      * shared instance of LogManager.
263      */
264     String name;
265     WeakReference ref;
266
267     /* This will throw a NullPointerException if logger is null,
268      * as required by the API specification.
269      */
270     name = logger.getName();
271
272     ref = loggers.get(name);
273     if (ref != null)
274       {
275         if (ref.get() != null)
276           return false;
277
278         /* There has been a logger under this name in the past,
279          * but it has been garbage collected.
280          */
281         loggers.remove(ref);
282       }
283
284     /* Adding a named logger requires a security permission. */
285     if ((name != null) && ! name.equals(""))
286       checkAccess();
287
288     Logger parent = findAncestor(logger);
289     loggers.put(name, new WeakReference<Logger>(logger));
290     if (parent != logger.getParent())
291       logger.setParent(parent);
292
293     // The level of the newly added logger must be specified.
294     // The easiest case is if there is a level for exactly this logger
295     // in the properties. If no such level exists the level needs to be 
296     // searched along the hirachy. So if there is a new logger 'foo.blah.blub'
297     // and an existing parent logger 'foo' the properties 'foo.blah.blub.level'
298     // and 'foo.blah.level' need to be checked. If both do not exist in the 
299     // properties the level of the new logger is set to 'null' (i.e. it uses the
300     // level of its parent 'foo').
301     Level logLevel = logger.getLevel();
302     String searchName = name;
303     String parentName = parent != null ? parent.getName() : "";
304     while (logLevel == null && ! searchName.equals(parentName))
305       {
306         logLevel = getLevelProperty(searchName + ".level", logLevel);
307         int index = searchName.lastIndexOf('.');
308         if(index > -1)
309           searchName = searchName.substring(0,index);
310         else
311           searchName = "";
312       }
313     logger.setLevel(logLevel);
314
315     /* It can happen that existing loggers should be children of
316      * the newly added logger. For example, assume that there
317      * already exist loggers under the names "", "foo", and "foo.bar.baz".
318      * When adding "foo.bar", the logger "foo.bar.baz" should change
319      * its parent to "foo.bar".
320      */
321     for (Iterator iter = loggers.keySet().iterator(); iter.hasNext();)
322       {
323         Logger possChild = (Logger) ((WeakReference) loggers.get(iter.next()))
324           .get();
325         if ((possChild == null) || (possChild == logger)
326             || (possChild.getParent() != parent))
327           continue;
328         
329         if (! possChild.getName().startsWith(name))
330           continue;
331         
332         if (possChild.getName().charAt(name.length()) != '.')
333           continue;
334         
335         possChild.setParent(logger);
336       }
337     
338     return true;
339   }
340
341   /**
342    * Finds the closest ancestor for a logger among the currently
343    * registered ones.  For example, if the currently registered
344    * loggers have the names "", "foo", and "foo.bar", the result for
345    * "foo.bar.baz" will be the logger whose name is "foo.bar".
346    *
347    * @param child a logger for whose name no logger has been
348    *        registered.
349    *
350    * @return the closest ancestor for <code>child</code>,
351    *         or <code>null</code> if <code>child</code>
352    *         is the root logger.
353    *
354    * @throws NullPointerException if <code>child</code>
355    *         is <code>null</code>.
356    */
357   private synchronized Logger findAncestor(Logger child)
358   {
359     String childName = child.getName();
360     int childNameLength = childName.length();
361     Logger best = Logger.root;
362     int bestNameLength = 0;
363
364     Logger cand;
365     int candNameLength;
366
367     if (child == Logger.root)
368       return null;
369
370     for (String candName : loggers.keySet())
371       {
372         candNameLength = candName.length();
373
374         if (candNameLength > bestNameLength
375             && childNameLength > candNameLength
376             && childName.startsWith(candName)
377             && childName.charAt(candNameLength) == '.')
378           {
379             cand = loggers.get(candName).get();
380             if ((cand == null) || (cand == child))
381               continue;
382
383             bestNameLength = candName.length();
384             best = cand;
385           }
386       }
387
388     return best;
389   }
390
391   /**
392    * Returns a Logger given its name.
393    *
394    * @param name the name of the logger.
395    *
396    * @return a named Logger, or <code>null</code> if there is no
397    *     logger with that name.
398    *
399    * @throw java.lang.NullPointerException if <code>name</code>
400    *     is <code>null</code>.
401    */
402   public synchronized Logger getLogger(String name)
403   {
404     WeakReference<Logger> ref;
405
406     /* Throw a NullPointerException if name is null. */
407     name.getClass();
408
409     ref = loggers.get(name);
410     if (ref != null)
411       return ref.get();
412     else
413       return null;
414   }
415
416   /**
417    * Returns an Enumeration of currently registered Logger names.
418    * Since other threads can register loggers at any time, the
419    * result could be different any time this method is called.
420    *
421    * @return an Enumeration with the names of the currently
422    *    registered Loggers.
423    */
424   public synchronized Enumeration<String> getLoggerNames()
425   {
426     return Collections.enumeration(loggers.keySet());
427   }
428
429   /**
430    * Resets the logging configuration by removing all handlers for
431    * registered named loggers and setting their level to <code>null</code>.
432    * The level of the root logger will be set to <code>Level.INFO</code>.
433    *
434    * @throws SecurityException if a security manager exists and
435    *         the caller is not granted the permission to control
436    *         the logging infrastructure.
437    */
438   public synchronized void reset() throws SecurityException
439   {
440     /* Throw a SecurityException if the caller does not have the
441      * permission to control the logging infrastructure.
442      */
443     checkAccess();
444
445     properties = new Properties();
446
447     Iterator<WeakReference<Logger>> iter = loggers.values().iterator();
448     while (iter.hasNext())
449       for (WeakReference<Logger> ref : loggers.values())
450       {
451         Logger logger;
452
453         ref = iter.next();
454         if (ref != null)
455           {
456             logger = ref.get();
457
458             if (logger == null)
459               iter.remove();
460             else if (logger != Logger.root)
461               {
462                 logger.resetLogger();
463                 logger.setLevel(null);
464               }
465           }
466       }
467
468     Logger.root.setLevel(Level.INFO);
469     Logger.root.resetLogger();
470   }
471
472   /**
473    * Configures the logging framework by reading a configuration file.
474    * The name and location of this file are specified by the system
475    * property <code>java.util.logging.config.file</code>.  If this
476    * property is not set, the URL
477    * "{gnu.classpath.home.url}/logging.properties" is taken, where
478    * "{gnu.classpath.home.url}" stands for the value of the system
479    * property <code>gnu.classpath.home.url</code>.
480    *
481    * <p>The task of configuring the framework is then delegated to
482    * {@link #readConfiguration(java.io.InputStream)}, which will
483    * notify registered listeners after having read the properties.
484    *
485    * @throws SecurityException if a security manager exists and
486    *         the caller is not granted the permission to control
487    *         the logging infrastructure, or if the caller is
488    *         not granted the permission to read the configuration
489    *         file.
490    *
491    * @throws IOException if there is a problem reading in the
492    *         configuration file.
493    */
494   public synchronized void readConfiguration()
495     throws IOException, SecurityException
496   {
497     String path;
498     InputStream inputStream;
499
500     path = System.getProperty("java.util.logging.config.file");
501     if ((path == null) || (path.length() == 0))
502       {
503         String url = (System.getProperty("gnu.classpath.home.url")
504                       + "/logging.properties");
505         try
506           {
507             inputStream = new URL(url).openStream();
508           } 
509         catch (Exception e)
510           {
511             inputStream=null;
512           }
513
514         // If no config file could be found use a default configuration.
515         if(inputStream == null)
516           {
517             String defaultConfig = "handlers = java.util.logging.ConsoleHandler   \n"
518               + ".level=INFO \n";
519             inputStream = new ByteArrayInputStream(defaultConfig.getBytes());
520           }
521       }
522     else
523       inputStream = new java.io.FileInputStream(path);
524
525     try
526       {
527         readConfiguration(inputStream);
528       }
529     finally
530       {
531         // Close the stream in order to save
532         // resources such as file descriptors.
533         inputStream.close();
534       }
535   }
536
537   public synchronized void readConfiguration(InputStream inputStream)
538     throws IOException, SecurityException
539   {
540     Properties newProperties;
541     Enumeration keys;
542
543     checkAccess();
544     newProperties = new Properties();
545     newProperties.load(inputStream);
546     reset();
547     this.properties = newProperties;
548     keys = newProperties.propertyNames();
549
550     while (keys.hasMoreElements())
551       {
552         String key = ((String) keys.nextElement()).trim();
553         String value = newProperties.getProperty(key);
554
555         if (value == null)
556           continue;
557
558         value = value.trim();
559
560         if ("handlers".equals(key))
561           {
562             StringTokenizer tokenizer = new StringTokenizer(value);
563             while (tokenizer.hasMoreTokens())
564               {
565                 String handlerName = tokenizer.nextToken();
566                 Handler handler = (Handler)
567                   createInstance(handlerName, Handler.class, key);
568                 Logger.root.addHandler(handler);
569               }
570           }
571
572         if (key.endsWith(".level"))
573           {
574             String loggerName = key.substring(0, key.length() - 6);
575             Logger logger = getLogger(loggerName);
576
577             if (logger == null)
578               {
579                 logger = Logger.getLogger(loggerName);
580                 addLogger(logger);
581               }
582             Level level = null;
583             try
584               {
585                 level = Level.parse(value);
586               }
587             catch (IllegalArgumentException e)
588               {
589                 warn("bad level \'" + value + "\'", e);
590               }
591             if (level != null)
592               {
593                 logger.setLevel(level);
594               }
595             continue;
596           }
597       }
598
599     /* The API specification does not talk about the
600      * property name that is distributed with the
601      * PropertyChangeEvent.  With test code, it could
602      * be determined that the Sun J2SE 1.4 reference
603      * implementation uses null for the property name.
604      */
605     pcs.firePropertyChange(null, null, null);
606   }
607
608   /**
609    * Returns the value of a configuration property as a String.
610    */
611   public synchronized String getProperty(String name)
612   {
613     if (properties != null)
614       return properties.getProperty(name);
615     else
616       return null;
617   }
618
619   /**
620    * Returns the value of a configuration property as an integer.
621    * This function is a helper used by the Classpath implementation
622    * of java.util.logging, it is <em>not</em> specified in the
623    * logging API.
624    *
625    * @param name the name of the configuration property.
626    *
627    * @param defaultValue the value that will be returned if the
628    *        property is not defined, or if its value is not an integer
629    *        number.
630    */
631   static int getIntProperty(String name, int defaultValue)
632   {
633     try
634       {
635         return Integer.parseInt(getLogManager().getProperty(name));
636       }
637     catch (Exception ex)
638       {
639         return defaultValue;
640       }
641   }
642
643   /**
644    * Returns the value of a configuration property as an integer,
645    * provided it is inside the acceptable range.
646    * This function is a helper used by the Classpath implementation
647    * of java.util.logging, it is <em>not</em> specified in the
648    * logging API.
649    *
650    * @param name the name of the configuration property.
651    *
652    * @param minValue the lowest acceptable value.
653    *
654    * @param maxValue the highest acceptable value.
655    *
656    * @param defaultValue the value that will be returned if the
657    *        property is not defined, or if its value is not an integer
658    *        number, or if it is less than the minimum value,
659    *        or if it is greater than the maximum value.
660    */
661   static int getIntPropertyClamped(String name, int defaultValue,
662                                    int minValue, int maxValue)
663   {
664     int val = getIntProperty(name, defaultValue);
665     if ((val < minValue) || (val > maxValue))
666       val = defaultValue;
667     return val;
668   }
669
670   /**
671    * Returns the value of a configuration property as a boolean.
672    * This function is a helper used by the Classpath implementation
673    * of java.util.logging, it is <em>not</em> specified in the
674    * logging API.
675    *
676    * @param name the name of the configuration property.
677    *
678    * @param defaultValue the value that will be returned if the
679    *        property is not defined, or if its value is neither
680    *        <code>"true"</code> nor <code>"false"</code>.
681    */
682   static boolean getBooleanProperty(String name, boolean defaultValue)
683   {
684     try
685       {
686         return (Boolean.valueOf(getLogManager().getProperty(name))).booleanValue();
687       }
688     catch (Exception ex)
689       {
690         return defaultValue;
691       }
692   }
693
694   /**
695    * Returns the value of a configuration property as a Level.
696    * This function is a helper used by the Classpath implementation
697    * of java.util.logging, it is <em>not</em> specified in the
698    * logging API.
699    *
700    * @param propertyName the name of the configuration property.
701    *
702    * @param defaultValue the value that will be returned if the
703    *        property is not defined, or if
704    *        {@link Level#parse(java.lang.String)} does not like
705    *        the property value.
706    */
707   static Level getLevelProperty(String propertyName, Level defaultValue)
708   {
709     try
710       {
711         String value = getLogManager().getProperty(propertyName);
712         if (value != null)
713           return Level.parse(getLogManager().getProperty(propertyName));
714         else
715            return defaultValue;
716       }
717     catch (Exception ex)
718       {
719         return defaultValue;
720       }
721   }
722
723   /**
724    * Returns the value of a configuration property as a Class.
725    * This function is a helper used by the Classpath implementation
726    * of java.util.logging, it is <em>not</em> specified in the
727    * logging API.
728    *
729    * @param propertyName the name of the configuration property.
730    *
731    * @param defaultValue the value that will be returned if the
732    *        property is not defined, or if it does not specify
733    *        the name of a loadable class.
734    */
735   static final Class getClassProperty(String propertyName, Class defaultValue)
736   {
737     String propertyValue = logManager.getProperty(propertyName);
738
739     if (propertyValue != null)
740       try
741         {
742           return locateClass(propertyValue);
743         }
744       catch (ClassNotFoundException e)
745         {
746           warn(propertyName + " = " + propertyValue, e);
747         }
748
749     return defaultValue;
750   }
751
752   static final Object getInstanceProperty(String propertyName, Class ofClass,
753                                           Class defaultClass)
754   {
755     Class klass = getClassProperty(propertyName, defaultClass);
756     if (klass == null)
757       return null;
758
759     try
760       {
761         Object obj = klass.newInstance();
762         if (ofClass.isInstance(obj))
763           return obj;
764       }
765     catch (InstantiationException e)
766       {
767         warn(propertyName + " = " + klass.getName(), e);
768       }
769     catch (IllegalAccessException e)
770       {
771         warn(propertyName + " = " + klass.getName(), e);
772       }
773
774     if (defaultClass == null)
775       return null;
776
777     try
778       {
779         return defaultClass.newInstance();
780       }
781     catch (java.lang.InstantiationException ex)
782       {
783         throw new RuntimeException(ex.getMessage());
784       }
785     catch (java.lang.IllegalAccessException ex)
786       {
787         throw new RuntimeException(ex.getMessage());
788       }
789   }
790
791   /**
792    * An instance of <code>LoggingPermission("control")</code>
793    * that is shared between calls to <code>checkAccess()</code>.
794    */
795   private static final LoggingPermission controlPermission = new LoggingPermission("control",
796                                                                                    null);
797
798   /**
799    * Checks whether the current security context allows changing
800    * the configuration of the logging framework.  For the security
801    * context to be trusted, it has to be granted
802    * a LoggingPermission("control").
803    *
804    * @throws SecurityException if a security manager exists and
805    *         the caller is not granted the permission to control
806    *         the logging infrastructure.
807    */
808   public void checkAccess() throws SecurityException
809   {
810     SecurityManager sm = System.getSecurityManager();
811     if (sm != null)
812       sm.checkPermission(controlPermission);
813   }
814
815   /**
816    * Creates a new instance of a class specified by name and verifies
817    * that it is an instance (or subclass of) a given type.
818    *
819    * @param className the name of the class of which a new instance
820    *        should be created.
821    *
822    * @param type the object created must be an instance of
823    * <code>type</code> or any subclass of <code>type</code>
824    *
825    * @param property the system property to reference in error
826    * messages
827    *
828    * @return the new instance, or <code>null</code> if
829    *         <code>className</code> is <code>null</code>, if no class
830    *         with that name could be found, if there was an error
831    *         loading that class, or if the constructor of the class
832    *         has thrown an exception.
833    */
834   private static final Object createInstance(String className, Class type,
835                                              String property)
836   {
837     Class klass = null;
838
839     if ((className == null) || (className.length() == 0))
840       return null;
841
842     try
843       {
844         klass = locateClass(className);
845         if (type.isAssignableFrom(klass))
846           return klass.newInstance();
847         warn(property, className, "not an instance of " + type.getName());
848       }
849     catch (ClassNotFoundException e)
850       {
851         warn(property, className, "class not found", e);
852       }
853     catch (IllegalAccessException e)
854       {
855         warn(property, className, "illegal access", e);
856       }
857     catch (InstantiationException e)
858       {
859         warn(property, className, e);
860       }
861     catch (java.lang.LinkageError e)
862       {
863         warn(property, className, "linkage error", e);
864       }
865
866     return null;
867   }
868
869   private static final void warn(String property, String klass, Throwable t)
870   {
871     warn(property, klass, null, t);
872   }
873
874   private static final void warn(String property, String klass, String msg)
875   {
876     warn(property, klass, msg, null);
877   }
878
879   private static final void warn(String property, String klass, String msg,
880                                  Throwable t)
881   {
882     warn("error instantiating '" + klass + "' referenced by " + property +
883          (msg == null ? "" : ", " + msg), t);
884   }
885
886   /**
887    * All debug warnings go through this method.
888    */
889
890   private static final void warn(String msg, Throwable t)
891   {
892     System.err.println("WARNING: " + msg);
893     if (t != null)
894       t.printStackTrace(System.err);
895   }
896
897   /**
898    * Locates a class by first checking the system class loader and
899    * then checking the context class loader.
900    *
901    * @param name the fully qualified name of the Class to locate
902    * @return Class the located Class
903    */
904
905   private static Class locateClass(String name) throws ClassNotFoundException
906   {
907     ClassLoader loader = Thread.currentThread().getContextClassLoader();
908     try
909       {
910         return Class.forName(name, true, loader);
911       }
912     catch (ClassNotFoundException e)
913       {
914         loader = ClassLoader.getSystemClassLoader();
915         return Class.forName(name, true, loader);
916       }
917   }
918
919   /**
920    * Return the logging bean.  There is a single logging bean per
921    * VM instance.
922    * @since 1.5
923    */
924   public static synchronized LoggingMXBean getLoggingMXBean()
925   {
926     if (loggingBean == null)
927       {
928         loggingBean = new LoggingMXBean()
929         {
930           public String getLoggerLevel(String logger)
931           {
932             LogManager mgr = getLogManager();
933             Logger l = mgr.getLogger(logger);
934             if (l == null)
935               return null;
936             Level lev = l.getLevel();
937             if (lev == null)
938               return "";
939             return lev.getName();
940           }
941
942           public List getLoggerNames()
943           {
944             LogManager mgr = getLogManager();
945             // This is inefficient, but perhaps better for maintenance.
946             return Collections.list(mgr.getLoggerNames());
947           }
948
949           public String getParentLoggerName(String logger)
950           {
951             LogManager mgr = getLogManager();
952             Logger l = mgr.getLogger(logger);
953             if (l == null)
954               return null;
955             l = l.getParent();
956             if (l == null)
957               return "";
958             return l.getName();
959           }
960
961           public void setLoggerLevel(String logger, String level)
962           {
963             LogManager mgr = getLogManager();
964             Logger l = mgr.getLogger(logger);
965             if (l == null)
966               throw new IllegalArgumentException("no logger named " + logger);
967             Level newLevel;
968             if (level == null)
969               newLevel = null;
970             else
971               newLevel = Level.parse(level);
972             l.setLevel(newLevel);
973           }
974         };
975       }
976     return loggingBean;
977   }
978 }