001/* LogManager.java -- a class for maintaining Loggers and managing
002   configuration properties
003   Copyright (C) 2002, 2005, 2006, 2007 Free Software Foundation, Inc.
004
005This file is part of GNU Classpath.
006
007GNU Classpath is free software; you can redistribute it and/or modify
008it under the terms of the GNU General Public License as published by
009the Free Software Foundation; either version 2, or (at your option)
010any later version.
011
012GNU Classpath is distributed in the hope that it will be useful, but
013WITHOUT ANY WARRANTY; without even the implied warranty of
014MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
015General Public License for more details.
016
017You should have received a copy of the GNU General Public License
018along with GNU Classpath; see the file COPYING.  If not, write to the
019Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02002110-1301 USA.
021
022Linking this library statically or dynamically with other modules is
023making a combined work based on this library.  Thus, the terms and
024conditions of the GNU General Public License cover the whole
025combination.
026
027As a special exception, the copyright holders of this library give you
028permission to link this library with independent modules to produce an
029executable, regardless of the license terms of these independent
030modules, and to copy and distribute the resulting executable under
031terms of your choice, provided that you also meet, for each linked
032independent module, the terms and conditions of the license of that
033module.  An independent module is a module which is not derived from
034or based on this library.  If you modify this library, you may extend
035this exception to your version of the library, but you are not
036obligated to do so.  If you do not wish to do so, delete this
037exception statement from your version. */
038
039
040package java.util.logging;
041
042import gnu.classpath.SystemProperties;
043
044import java.beans.PropertyChangeListener;
045import java.beans.PropertyChangeSupport;
046import java.io.ByteArrayInputStream;
047import java.io.IOException;
048import java.io.InputStream;
049import java.lang.ref.WeakReference;
050import java.net.URL;
051import java.util.Collections;
052import java.util.Enumeration;
053import java.util.HashMap;
054import java.util.Iterator;
055import java.util.List;
056import java.util.Map;
057import java.util.Properties;
058import java.util.StringTokenizer;
059
060/**
061 * The <code>LogManager</code> maintains a hierarchical namespace
062 * of Logger objects and manages properties for configuring the logging
063 * framework. There exists only one single <code>LogManager</code>
064 * per virtual machine. This instance can be retrieved using the
065 * static method {@link #getLogManager()}.
066 *
067 * <p><strong>Configuration Process:</strong> The global LogManager
068 * object is created and configured when the class
069 * <code>java.util.logging.LogManager</code> is initialized.
070 * The configuration process includes the subsequent steps:
071 *
072 * <ul>
073 * <li>If the system property <code>java.util.logging.manager</code>
074 *     is set to the name of a subclass of
075 *     <code>java.util.logging.LogManager</code>, an instance of
076 *     that subclass is created and becomes the global LogManager.
077 *     Otherwise, a new instance of LogManager is created.</li>
078 * <li>The <code>LogManager</code> constructor tries to create
079 *     a new instance of the class specified by the system
080 *     property <code>java.util.logging.config.class</code>.
081 *     Typically, the constructor of this class will call
082 *     <code>LogManager.getLogManager().readConfiguration(java.io.InputStream)</code>
083 *     for configuring the logging framework.
084 *     The configuration process stops at this point if
085 *     the system property <code>java.util.logging.config.class</code>
086 *     is set (irrespective of whether the class constructor
087 *     could be called or an exception was thrown).</li>
088 *
089 * <li>If the system property <code>java.util.logging.config.class</code>
090 *     is <em>not</em> set, the configuration parameters are read in from
091 *     a file and passed to
092 *     {@link #readConfiguration(java.io.InputStream)}.
093 *     The name and location of this file are specified by the system
094 *     property <code>java.util.logging.config.file</code>.</li>
095 * <li>If the system property <code>java.util.logging.config.file</code>
096 *     is not set, however, the contents of the URL
097 *     "{gnu.classpath.home.url}/logging.properties" are passed to
098 *     {@link #readConfiguration(java.io.InputStream)}.
099 *     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 */
109public 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      {
450        WeakReference<Logger> ref;
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            // In Java 5 and earlier this was specified to be
563            // whitespace-separated, but in reality it also accepted
564            // commas (tomcat relied on this), and in Java 6 the
565            // documentation was updated to fit the implementation.
566            StringTokenizer tokenizer = new StringTokenizer(value,
567                                                            " \t\n\r\f,");
568            while (tokenizer.hasMoreTokens())
569              {
570                String handlerName = tokenizer.nextToken();
571                Handler handler = (Handler)
572                  createInstance(handlerName, Handler.class, key);
573                // Tomcat also relies on the implementation ignoring
574                // items in 'handlers' which are not class names.
575                if (handler != null)
576                  Logger.root.addHandler(handler);
577              }
578          }
579
580        if (key.endsWith(".level"))
581          {
582            String loggerName = key.substring(0, key.length() - 6);
583            Logger logger = getLogger(loggerName);
584
585            if (logger == null)
586              {
587                logger = Logger.getLogger(loggerName);
588                addLogger(logger);
589              }
590            Level level = null;
591            try
592              {
593                level = Level.parse(value);
594              }
595            catch (IllegalArgumentException e)
596              {
597                warn("bad level \'" + value + "\'", e);
598              }
599            if (level != null)
600              {
601                logger.setLevel(level);
602              }
603            continue;
604          }
605      }
606
607    /* The API specification does not talk about the
608     * property name that is distributed with the
609     * PropertyChangeEvent.  With test code, it could
610     * be determined that the Sun J2SE 1.4 reference
611     * implementation uses null for the property name.
612     */
613    pcs.firePropertyChange(null, null, null);
614  }
615
616  /**
617   * Returns the value of a configuration property as a String.
618   */
619  public synchronized String getProperty(String name)
620  {
621    if (properties != null)
622      return properties.getProperty(name);
623    else
624      return null;
625  }
626
627  /**
628   * Returns the value of a configuration property as an integer.
629   * This function is a helper used by the Classpath implementation
630   * of java.util.logging, it is <em>not</em> specified in the
631   * logging API.
632   *
633   * @param name the name of the configuration property.
634   *
635   * @param defaultValue the value that will be returned if the
636   *        property is not defined, or if its value is not an integer
637   *        number.
638   */
639  static int getIntProperty(String name, int defaultValue)
640  {
641    try
642      {
643        return Integer.parseInt(getLogManager().getProperty(name));
644      }
645    catch (Exception ex)
646      {
647        return defaultValue;
648      }
649  }
650
651  /**
652   * Returns the value of a configuration property as an integer,
653   * provided it is inside the acceptable range.
654   * This function is a helper used by the Classpath implementation
655   * of java.util.logging, it is <em>not</em> specified in the
656   * logging API.
657   *
658   * @param name the name of the configuration property.
659   *
660   * @param minValue the lowest acceptable value.
661   *
662   * @param maxValue the highest acceptable value.
663   *
664   * @param defaultValue the value that will be returned if the
665   *        property is not defined, or if its value is not an integer
666   *        number, or if it is less than the minimum value,
667   *        or if it is greater than the maximum value.
668   */
669  static int getIntPropertyClamped(String name, int defaultValue,
670                                   int minValue, int maxValue)
671  {
672    int val = getIntProperty(name, defaultValue);
673    if ((val < minValue) || (val > maxValue))
674      val = defaultValue;
675    return val;
676  }
677
678  /**
679   * Returns the value of a configuration property as a boolean.
680   * This function is a helper used by the Classpath implementation
681   * of java.util.logging, it is <em>not</em> specified in the
682   * logging API.
683   *
684   * @param name the name of the configuration property.
685   *
686   * @param defaultValue the value that will be returned if the
687   *        property is not defined, or if its value is neither
688   *        <code>"true"</code> nor <code>"false"</code>.
689   */
690  static boolean getBooleanProperty(String name, boolean defaultValue)
691  {
692    try
693      {
694        return (Boolean.valueOf(getLogManager().getProperty(name))).booleanValue();
695      }
696    catch (Exception ex)
697      {
698        return defaultValue;
699      }
700  }
701
702  /**
703   * Returns the value of a configuration property as a Level.
704   * This function is a helper used by the Classpath implementation
705   * of java.util.logging, it is <em>not</em> specified in the
706   * logging API.
707   *
708   * @param propertyName the name of the configuration property.
709   *
710   * @param defaultValue the value that will be returned if the
711   *        property is not defined, or if
712   *        {@link Level#parse(java.lang.String)} does not like
713   *        the property value.
714   */
715  static Level getLevelProperty(String propertyName, Level defaultValue)
716  {
717    try
718      {
719        String value = getLogManager().getProperty(propertyName);
720        if (value != null)
721          return Level.parse(getLogManager().getProperty(propertyName));
722        else
723           return defaultValue;
724      }
725    catch (Exception ex)
726      {
727        return defaultValue;
728      }
729  }
730
731  /**
732   * Returns the value of a configuration property as a Class.
733   * This function is a helper used by the Classpath implementation
734   * of java.util.logging, it is <em>not</em> specified in the
735   * logging API.
736   *
737   * @param propertyName the name of the configuration property.
738   *
739   * @param defaultValue the value that will be returned if the
740   *        property is not defined, or if it does not specify
741   *        the name of a loadable class.
742   */
743  static final Class getClassProperty(String propertyName, Class defaultValue)
744  {
745    String propertyValue = logManager.getProperty(propertyName);
746
747    if (propertyValue != null)
748      try
749        {
750          return locateClass(propertyValue);
751        }
752      catch (ClassNotFoundException e)
753        {
754          warn(propertyName + " = " + propertyValue, e);
755        }
756
757    return defaultValue;
758  }
759
760  static final Object getInstanceProperty(String propertyName, Class ofClass,
761                                          Class defaultClass)
762  {
763    Class klass = getClassProperty(propertyName, defaultClass);
764    if (klass == null)
765      return null;
766
767    try
768      {
769        Object obj = klass.newInstance();
770        if (ofClass.isInstance(obj))
771          return obj;
772      }
773    catch (InstantiationException e)
774      {
775        warn(propertyName + " = " + klass.getName(), e);
776      }
777    catch (IllegalAccessException e)
778      {
779        warn(propertyName + " = " + klass.getName(), e);
780      }
781
782    if (defaultClass == null)
783      return null;
784
785    try
786      {
787        return defaultClass.newInstance();
788      }
789    catch (java.lang.InstantiationException ex)
790      {
791        throw new RuntimeException(ex.getMessage());
792      }
793    catch (java.lang.IllegalAccessException ex)
794      {
795        throw new RuntimeException(ex.getMessage());
796      }
797  }
798
799  /**
800   * An instance of <code>LoggingPermission("control")</code>
801   * that is shared between calls to <code>checkAccess()</code>.
802   */
803  private static final LoggingPermission controlPermission = new LoggingPermission("control",
804                                                                                   null);
805
806  /**
807   * Checks whether the current security context allows changing
808   * the configuration of the logging framework.  For the security
809   * context to be trusted, it has to be granted
810   * a LoggingPermission("control").
811   *
812   * @throws SecurityException if a security manager exists and
813   *         the caller is not granted the permission to control
814   *         the logging infrastructure.
815   */
816  public void checkAccess() throws SecurityException
817  {
818    SecurityManager sm = System.getSecurityManager();
819    if (sm != null)
820      sm.checkPermission(controlPermission);
821  }
822
823  /**
824   * Creates a new instance of a class specified by name and verifies
825   * that it is an instance (or subclass of) a given type.
826   *
827   * @param className the name of the class of which a new instance
828   *        should be created.
829   *
830   * @param type the object created must be an instance of
831   * <code>type</code> or any subclass of <code>type</code>
832   *
833   * @param property the system property to reference in error
834   * messages
835   *
836   * @return the new instance, or <code>null</code> if
837   *         <code>className</code> is <code>null</code>, if no class
838   *         with that name could be found, if there was an error
839   *         loading that class, or if the constructor of the class
840   *         has thrown an exception.
841   */
842  private static final Object createInstance(String className, Class type,
843                                             String property)
844  {
845    Class klass = null;
846
847    if ((className == null) || (className.length() == 0))
848      return null;
849
850    try
851      {
852        klass = locateClass(className);
853        if (type.isAssignableFrom(klass))
854          return klass.newInstance();
855        warn(property, className, "not an instance of " + type.getName());
856      }
857    catch (ClassNotFoundException e)
858      {
859        warn(property, className, "class not found", e);
860      }
861    catch (IllegalAccessException e)
862      {
863        warn(property, className, "illegal access", e);
864      }
865    catch (InstantiationException e)
866      {
867        warn(property, className, e);
868      }
869    catch (java.lang.LinkageError e)
870      {
871        warn(property, className, "linkage error", e);
872      }
873
874    return null;
875  }
876
877  private static final void warn(String property, String klass, Throwable t)
878  {
879    warn(property, klass, null, t);
880  }
881
882  private static final void warn(String property, String klass, String msg)
883  {
884    warn(property, klass, msg, null);
885  }
886
887  private static final void warn(String property, String klass, String msg,
888                                 Throwable t)
889  {
890    warn("error instantiating '" + klass + "' referenced by " + property +
891         (msg == null ? "" : ", " + msg), t);
892  }
893
894  /**
895   * All debug warnings go through this method.
896   */
897
898  private static final void warn(String msg, Throwable t)
899  {
900    System.err.println("WARNING: " + msg);
901    if (t != null)
902      t.printStackTrace(System.err);
903  }
904
905  /**
906   * Locates a class by first checking the system class loader and
907   * then checking the context class loader.
908   *
909   * @param name the fully qualified name of the Class to locate
910   * @return Class the located Class
911   */
912
913  private static Class locateClass(String name) throws ClassNotFoundException
914  {
915    // GCJ LOCAL
916    // Unfortunately this can be called during bootstrap when
917    // Thread.currentThread() will return null.
918    // See bug #27658
919    Thread t = Thread.currentThread();
920    ClassLoader loader = (t == null) ? null : t.getContextClassLoader();
921    try
922      {
923        return Class.forName(name, true, loader);
924      }
925    catch (ClassNotFoundException e)
926      {
927        loader = ClassLoader.getSystemClassLoader();
928        return Class.forName(name, true, loader);
929      }
930  }
931
932  /**
933   * Return the logging bean.  There is a single logging bean per
934   * VM instance.
935   * @since 1.5
936   */
937  public static synchronized LoggingMXBean getLoggingMXBean()
938  {
939    if (loggingBean == null)
940      {
941        loggingBean = new LoggingMXBean()
942        {
943          public String getLoggerLevel(String logger)
944          {
945            LogManager mgr = getLogManager();
946            Logger l = mgr.getLogger(logger);
947            if (l == null)
948              return null;
949            Level lev = l.getLevel();
950            if (lev == null)
951              return "";
952            return lev.getName();
953          }
954
955          public List getLoggerNames()
956          {
957            LogManager mgr = getLogManager();
958            // This is inefficient, but perhaps better for maintenance.
959            return Collections.list(mgr.getLoggerNames());
960          }
961
962          public String getParentLoggerName(String logger)
963          {
964            LogManager mgr = getLogManager();
965            Logger l = mgr.getLogger(logger);
966            if (l == null)
967              return null;
968            l = l.getParent();
969            if (l == null)
970              return "";
971            return l.getName();
972          }
973
974          public void setLoggerLevel(String logger, String level)
975          {
976            LogManager mgr = getLogManager();
977            Logger l = mgr.getLogger(logger);
978            if (l == null)
979              throw new IllegalArgumentException("no logger named " + logger);
980            Level newLevel;
981            if (level == null)
982              newLevel = null;
983            else
984              newLevel = Level.parse(level);
985            l.setLevel(newLevel);
986          }
987        };
988      }
989    return loggingBean;
990  }
991}