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   * @param listener the event listener to register.
216   * @throws NullPointerException if the listener is {@code null}.
217   * @throws SecurityException if a security manager exists and the
218   *                           calling code does not have the permission
219   *                           {@code LoggingPermission("control")}.
220   */
221  public synchronized void addPropertyChangeListener(PropertyChangeListener listener)
222  {
223    if (listener == null)
224      throw new NullPointerException("Attempt to add null property change listener");
225
226    SecurityManager sm = System.getSecurityManager();
227    if (sm != null)
228      sm.checkPermission(new LoggingPermission("control", null));
229
230    pcs.addPropertyChangeListener(listener);
231  }
232
233  /**
234   * Unregisters a listener.
235   *
236   * If <code>listener</code> has not been registered previously,
237   * nothing happens.  Also, no exception is thrown if
238   * <code>listener</code> is <code>null</code>.
239   *
240   * @param listener the listener to remove.
241   * @throws SecurityException if a security manager exists and the
242   *                           calling code does not have the permission
243   *                           {@code LoggingPermission("control")}.
244   */
245  public synchronized void removePropertyChangeListener(PropertyChangeListener listener)
246  {
247    if (listener != null)
248      {
249        SecurityManager sm = System.getSecurityManager();
250        if (sm != null)
251          sm.checkPermission(new LoggingPermission("control", null));
252
253        pcs.removePropertyChangeListener(listener);
254      }
255  }
256
257  /**
258   * Adds a named logger.  If a logger with the same name has
259   * already been registered, the method returns <code>false</code>
260   * without adding the logger.
261   *
262   * <p>The <code>LogManager</code> only keeps weak references
263   * to registered loggers.  Therefore, names can become available
264   * after automatic garbage collection.
265   *
266   * @param logger the logger to be added.
267   *
268   * @return <code>true</code>if <code>logger</code> was added,
269   *         <code>false</code> otherwise.
270   *
271   * @throws NullPointerException if <code>name</code> is
272   *         <code>null</code>.
273   */
274  public synchronized boolean addLogger(Logger logger)
275  {
276    /* To developers thinking about to remove the 'synchronized'
277     * declaration from this method: Please read the comment
278     * in java.util.logging.Logger.getLogger(String, String)
279     * and make sure that whatever you change wrt. synchronization
280     * does not endanger thread-safety of Logger.getLogger.
281     * The current implementation of Logger.getLogger assumes
282     * that LogManager does its synchronization on the globally
283     * shared instance of LogManager.
284     */
285    String name;
286    WeakReference ref;
287
288    /* This will throw a NullPointerException if logger is null,
289     * as required by the API specification.
290     */
291    name = logger.getName();
292
293    ref = loggers.get(name);
294    if (ref != null)
295      {
296        if (ref.get() != null)
297          return false;
298
299        /* There has been a logger under this name in the past,
300         * but it has been garbage collected.
301         */
302        loggers.remove(ref);
303      }
304
305    /* Adding a named logger requires a security permission. */
306    if ((name != null) && ! name.equals(""))
307      checkAccess();
308
309    Logger parent = findAncestor(logger);
310    loggers.put(name, new WeakReference<Logger>(logger));
311    if (parent != logger.getParent())
312      logger.setParent(parent);
313
314    // The level of the newly added logger must be specified.
315    // The easiest case is if there is a level for exactly this logger
316    // in the properties. If no such level exists the level needs to be
317    // searched along the hirachy. So if there is a new logger 'foo.blah.blub'
318    // and an existing parent logger 'foo' the properties 'foo.blah.blub.level'
319    // and 'foo.blah.level' need to be checked. If both do not exist in the
320    // properties the level of the new logger is set to 'null' (i.e. it uses the
321    // level of its parent 'foo').
322    Level logLevel = logger.getLevel();
323    String searchName = name;
324    String parentName = parent != null ? parent.getName() : "";
325    while (logLevel == null && ! searchName.equals(parentName))
326      {
327        logLevel = getLevelProperty(searchName + ".level", logLevel);
328        int index = searchName.lastIndexOf('.');
329        if(index > -1)
330          searchName = searchName.substring(0,index);
331        else
332          searchName = "";
333      }
334    logger.setLevel(logLevel);
335
336    /* It can happen that existing loggers should be children of
337     * the newly added logger. For example, assume that there
338     * already exist loggers under the names "", "foo", and "foo.bar.baz".
339     * When adding "foo.bar", the logger "foo.bar.baz" should change
340     * its parent to "foo.bar".
341     */
342    for (Iterator iter = loggers.keySet().iterator(); iter.hasNext();)
343      {
344        Logger possChild = (Logger) ((WeakReference) loggers.get(iter.next()))
345          .get();
346        if ((possChild == null) || (possChild == logger)
347            || (possChild.getParent() != parent))
348          continue;
349
350        if (! possChild.getName().startsWith(name))
351          continue;
352
353        if (possChild.getName().charAt(name.length()) != '.')
354          continue;
355
356        possChild.setParent(logger);
357      }
358
359    return true;
360  }
361
362  /**
363   * Finds the closest ancestor for a logger among the currently
364   * registered ones.  For example, if the currently registered
365   * loggers have the names "", "foo", and "foo.bar", the result for
366   * "foo.bar.baz" will be the logger whose name is "foo.bar".
367   *
368   * @param child a logger for whose name no logger has been
369   *        registered.
370   *
371   * @return the closest ancestor for <code>child</code>,
372   *         or <code>null</code> if <code>child</code>
373   *         is the root logger.
374   *
375   * @throws NullPointerException if <code>child</code>
376   *         is <code>null</code>.
377   */
378  private synchronized Logger findAncestor(Logger child)
379  {
380    String childName = child.getName();
381    int childNameLength = childName.length();
382    Logger best = Logger.root;
383    int bestNameLength = 0;
384
385    Logger cand;
386    int candNameLength;
387
388    if (child == Logger.root)
389      return null;
390
391    for (String candName : loggers.keySet())
392      {
393        candNameLength = candName.length();
394
395        if (candNameLength > bestNameLength
396            && childNameLength > candNameLength
397            && childName.startsWith(candName)
398            && childName.charAt(candNameLength) == '.')
399          {
400            cand = loggers.get(candName).get();
401            if ((cand == null) || (cand == child))
402              continue;
403
404            bestNameLength = candName.length();
405            best = cand;
406          }
407      }
408
409    return best;
410  }
411
412  /**
413   * Returns a Logger given its name.
414   *
415   * @param name the name of the logger.
416   *
417   * @return a named Logger, or <code>null</code> if there is no
418   *     logger with that name.
419   *
420   * @throw java.lang.NullPointerException if <code>name</code>
421   *     is <code>null</code>.
422   */
423  public synchronized Logger getLogger(String name)
424  {
425    WeakReference<Logger> ref;
426
427    /* Throw a NullPointerException if name is null. */
428    name.getClass();
429
430    ref = loggers.get(name);
431    if (ref != null)
432      return ref.get();
433    else
434      return null;
435  }
436
437  /**
438   * Returns an Enumeration of currently registered Logger names.
439   * Since other threads can register loggers at any time, the
440   * result could be different any time this method is called.
441   *
442   * @return an Enumeration with the names of the currently
443   *    registered Loggers.
444   */
445  public synchronized Enumeration<String> getLoggerNames()
446  {
447    return Collections.enumeration(loggers.keySet());
448  }
449
450  /**
451   * Resets the logging configuration by removing all handlers for
452   * registered named loggers and setting their level to <code>null</code>.
453   * The level of the root logger will be set to <code>Level.INFO</code>.
454   *
455   * @throws SecurityException if a security manager exists and
456   *         the caller is not granted the permission to control
457   *         the logging infrastructure.
458   */
459  public synchronized void reset() throws SecurityException
460  {
461    /* Throw a SecurityException if the caller does not have the
462     * permission to control the logging infrastructure.
463     */
464    checkAccess();
465
466    properties = new Properties();
467
468    Iterator<WeakReference<Logger>> iter = loggers.values().iterator();
469    while (iter.hasNext())
470      {
471        WeakReference<Logger> ref;
472        Logger logger;
473
474        ref = iter.next();
475        if (ref != null)
476          {
477            logger = ref.get();
478
479            if (logger == null)
480              iter.remove();
481            else if (logger != Logger.root)
482              {
483                logger.resetLogger();
484                logger.setLevel(null);
485              }
486          }
487      }
488
489    Logger.root.setLevel(Level.INFO);
490    Logger.root.resetLogger();
491  }
492
493  /**
494   * Configures the logging framework by reading a configuration file.
495   * The name and location of this file are specified by the system
496   * property <code>java.util.logging.config.file</code>.  If this
497   * property is not set, the URL
498   * "{gnu.classpath.home.url}/logging.properties" is taken, where
499   * "{gnu.classpath.home.url}" stands for the value of the system
500   * property <code>gnu.classpath.home.url</code>.
501   *
502   * <p>The task of configuring the framework is then delegated to
503   * {@link #readConfiguration(java.io.InputStream)}, which will
504   * notify registered listeners after having read the properties.
505   *
506   * @throws SecurityException if a security manager exists and
507   *         the caller is not granted the permission to control
508   *         the logging infrastructure, or if the caller is
509   *         not granted the permission to read the configuration
510   *         file.
511   *
512   * @throws IOException if there is a problem reading in the
513   *         configuration file.
514   */
515  public synchronized void readConfiguration()
516    throws IOException, SecurityException
517  {
518    String path;
519    InputStream inputStream;
520
521    path = System.getProperty("java.util.logging.config.file");
522    if ((path == null) || (path.length() == 0))
523      {
524        String url = (System.getProperty("gnu.classpath.home.url")
525                      + "/logging.properties");
526        try
527          {
528            inputStream = new URL(url).openStream();
529          }
530        catch (Exception e)
531          {
532            inputStream=null;
533          }
534
535        // If no config file could be found use a default configuration.
536        if(inputStream == null)
537          {
538            String defaultConfig = "handlers = java.util.logging.ConsoleHandler   \n"
539              + ".level=INFO \n";
540            inputStream = new ByteArrayInputStream(defaultConfig.getBytes());
541          }
542      }
543    else
544      inputStream = new java.io.FileInputStream(path);
545
546    try
547      {
548        readConfiguration(inputStream);
549      }
550    finally
551      {
552        // Close the stream in order to save
553        // resources such as file descriptors.
554        inputStream.close();
555      }
556  }
557
558  public synchronized void readConfiguration(InputStream inputStream)
559    throws IOException, SecurityException
560  {
561    Properties newProperties;
562    Enumeration keys;
563
564    checkAccess();
565    newProperties = new Properties();
566    newProperties.load(inputStream);
567    reset();
568    this.properties = newProperties;
569    keys = newProperties.propertyNames();
570
571    while (keys.hasMoreElements())
572      {
573        String key = ((String) keys.nextElement()).trim();
574        String value = newProperties.getProperty(key);
575
576        if (value == null)
577          continue;
578
579        value = value.trim();
580
581        if ("handlers".equals(key))
582          {
583            // In Java 5 and earlier this was specified to be
584            // whitespace-separated, but in reality it also accepted
585            // commas (tomcat relied on this), and in Java 6 the
586            // documentation was updated to fit the implementation.
587            StringTokenizer tokenizer = new StringTokenizer(value,
588                                                            " \t\n\r\f,");
589            while (tokenizer.hasMoreTokens())
590              {
591                String handlerName = tokenizer.nextToken();
592                Handler handler = (Handler)
593                  createInstance(handlerName, Handler.class, key);
594                // Tomcat also relies on the implementation ignoring
595                // items in 'handlers' which are not class names.
596                if (handler != null)
597                  Logger.root.addHandler(handler);
598              }
599          }
600
601        if (key.endsWith(".level"))
602          {
603            String loggerName = key.substring(0, key.length() - 6);
604            Logger logger = getLogger(loggerName);
605
606            if (logger == null)
607              {
608                logger = Logger.getLogger(loggerName);
609                addLogger(logger);
610              }
611            Level level = null;
612            try
613              {
614                level = Level.parse(value);
615              }
616            catch (IllegalArgumentException e)
617              {
618                warn("bad level \'" + value + "\'", e);
619              }
620            if (level != null)
621              {
622                logger.setLevel(level);
623              }
624            continue;
625          }
626      }
627
628    /* The API specification does not talk about the
629     * property name that is distributed with the
630     * PropertyChangeEvent.  With test code, it could
631     * be determined that the Sun J2SE 1.4 reference
632     * implementation uses null for the property name.
633     */
634    pcs.firePropertyChange(null, null, null);
635  }
636
637  /**
638   * Returns the value of a configuration property as a String.
639   */
640  public synchronized String getProperty(String name)
641  {
642    if (properties != null)
643      return properties.getProperty(name);
644    else
645      return null;
646  }
647
648  /**
649   * Returns the value of a configuration property as an integer.
650   * This function is a helper used by the Classpath implementation
651   * of java.util.logging, it is <em>not</em> specified in the
652   * logging API.
653   *
654   * @param name the name of the configuration property.
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.
659   */
660  static int getIntProperty(String name, int defaultValue)
661  {
662    try
663      {
664        return Integer.parseInt(getLogManager().getProperty(name));
665      }
666    catch (Exception ex)
667      {
668        return defaultValue;
669      }
670  }
671
672  /**
673   * Returns the value of a configuration property as an integer,
674   * provided it is inside the acceptable range.
675   * This function is a helper used by the Classpath implementation
676   * of java.util.logging, it is <em>not</em> specified in the
677   * logging API.
678   *
679   * @param name the name of the configuration property.
680   *
681   * @param minValue the lowest acceptable value.
682   *
683   * @param maxValue the highest acceptable value.
684   *
685   * @param defaultValue the value that will be returned if the
686   *        property is not defined, or if its value is not an integer
687   *        number, or if it is less than the minimum value,
688   *        or if it is greater than the maximum value.
689   */
690  static int getIntPropertyClamped(String name, int defaultValue,
691                                   int minValue, int maxValue)
692  {
693    int val = getIntProperty(name, defaultValue);
694    if ((val < minValue) || (val > maxValue))
695      val = defaultValue;
696    return val;
697  }
698
699  /**
700   * Returns the value of a configuration property as a boolean.
701   * This function is a helper used by the Classpath implementation
702   * of java.util.logging, it is <em>not</em> specified in the
703   * logging API.
704   *
705   * @param name the name of the configuration property.
706   *
707   * @param defaultValue the value that will be returned if the
708   *        property is not defined, or if its value is neither
709   *        <code>"true"</code> nor <code>"false"</code>.
710   */
711  static boolean getBooleanProperty(String name, boolean defaultValue)
712  {
713    try
714      {
715        return (Boolean.valueOf(getLogManager().getProperty(name))).booleanValue();
716      }
717    catch (Exception ex)
718      {
719        return defaultValue;
720      }
721  }
722
723  /**
724   * Returns the value of a configuration property as a Level.
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
733   *        {@link Level#parse(java.lang.String)} does not like
734   *        the property value.
735   */
736  static Level getLevelProperty(String propertyName, Level defaultValue)
737  {
738    try
739      {
740        String value = getLogManager().getProperty(propertyName);
741        if (value != null)
742          return Level.parse(getLogManager().getProperty(propertyName));
743        else
744           return defaultValue;
745      }
746    catch (Exception ex)
747      {
748        return defaultValue;
749      }
750  }
751
752  /**
753   * Returns the value of a configuration property as a Class.
754   * This function is a helper used by the Classpath implementation
755   * of java.util.logging, it is <em>not</em> specified in the
756   * logging API.
757   *
758   * @param propertyName the name of the configuration property.
759   *
760   * @param defaultValue the value that will be returned if the
761   *        property is not defined, or if it does not specify
762   *        the name of a loadable class.
763   */
764  static final Class getClassProperty(String propertyName, Class defaultValue)
765  {
766    String propertyValue = logManager.getProperty(propertyName);
767
768    if (propertyValue != null)
769      try
770        {
771          return locateClass(propertyValue);
772        }
773      catch (ClassNotFoundException e)
774        {
775          warn(propertyName + " = " + propertyValue, e);
776        }
777
778    return defaultValue;
779  }
780
781  static final Object getInstanceProperty(String propertyName, Class ofClass,
782                                          Class defaultClass)
783  {
784    Class klass = getClassProperty(propertyName, defaultClass);
785    if (klass == null)
786      return null;
787
788    try
789      {
790        Object obj = klass.newInstance();
791        if (ofClass.isInstance(obj))
792          return obj;
793      }
794    catch (InstantiationException e)
795      {
796        warn(propertyName + " = " + klass.getName(), e);
797      }
798    catch (IllegalAccessException e)
799      {
800        warn(propertyName + " = " + klass.getName(), e);
801      }
802
803    if (defaultClass == null)
804      return null;
805
806    try
807      {
808        return defaultClass.newInstance();
809      }
810    catch (java.lang.InstantiationException ex)
811      {
812        throw new RuntimeException(ex.getMessage());
813      }
814    catch (java.lang.IllegalAccessException ex)
815      {
816        throw new RuntimeException(ex.getMessage());
817      }
818  }
819
820  /**
821   * An instance of <code>LoggingPermission("control")</code>
822   * that is shared between calls to <code>checkAccess()</code>.
823   */
824  private static final LoggingPermission controlPermission = new LoggingPermission("control",
825                                                                                   null);
826
827  /**
828   * Checks whether the current security context allows changing
829   * the configuration of the logging framework.  For the security
830   * context to be trusted, it has to be granted
831   * a LoggingPermission("control").
832   *
833   * @throws SecurityException if a security manager exists and
834   *         the caller is not granted the permission to control
835   *         the logging infrastructure.
836   */
837  public void checkAccess() throws SecurityException
838  {
839    SecurityManager sm = System.getSecurityManager();
840    if (sm != null)
841      sm.checkPermission(controlPermission);
842  }
843
844  /**
845   * Creates a new instance of a class specified by name and verifies
846   * that it is an instance (or subclass of) a given type.
847   *
848   * @param className the name of the class of which a new instance
849   *        should be created.
850   *
851   * @param type the object created must be an instance of
852   * <code>type</code> or any subclass of <code>type</code>
853   *
854   * @param property the system property to reference in error
855   * messages
856   *
857   * @return the new instance, or <code>null</code> if
858   *         <code>className</code> is <code>null</code>, if no class
859   *         with that name could be found, if there was an error
860   *         loading that class, or if the constructor of the class
861   *         has thrown an exception.
862   */
863  private static final Object createInstance(String className, Class type,
864                                             String property)
865  {
866    Class klass = null;
867
868    if ((className == null) || (className.length() == 0))
869      return null;
870
871    try
872      {
873        klass = locateClass(className);
874        if (type.isAssignableFrom(klass))
875          return klass.newInstance();
876        warn(property, className, "not an instance of " + type.getName());
877      }
878    catch (ClassNotFoundException e)
879      {
880        warn(property, className, "class not found", e);
881      }
882    catch (IllegalAccessException e)
883      {
884        warn(property, className, "illegal access", e);
885      }
886    catch (InstantiationException e)
887      {
888        warn(property, className, e);
889      }
890    catch (java.lang.LinkageError e)
891      {
892        warn(property, className, "linkage error", e);
893      }
894
895    return null;
896  }
897
898  private static final void warn(String property, String klass, Throwable t)
899  {
900    warn(property, klass, null, t);
901  }
902
903  private static final void warn(String property, String klass, String msg)
904  {
905    warn(property, klass, msg, null);
906  }
907
908  private static final void warn(String property, String klass, String msg,
909                                 Throwable t)
910  {
911    warn("error instantiating '" + klass + "' referenced by " + property +
912         (msg == null ? "" : ", " + msg), t);
913  }
914
915  /**
916   * All debug warnings go through this method.
917   */
918
919  private static final void warn(String msg, Throwable t)
920  {
921    System.err.println("WARNING: " + msg);
922    if (t != null)
923      t.printStackTrace(System.err);
924  }
925
926  /**
927   * Locates a class by first checking the system class loader and
928   * then checking the context class loader.
929   *
930   * @param name the fully qualified name of the Class to locate
931   * @return Class the located Class
932   */
933
934  private static Class locateClass(String name) throws ClassNotFoundException
935  {
936    ClassLoader loader = Thread.currentThread().getContextClassLoader();
937    try
938      {
939        return Class.forName(name, true, loader);
940      }
941    catch (ClassNotFoundException e)
942      {
943        loader = ClassLoader.getSystemClassLoader();
944        return Class.forName(name, true, loader);
945      }
946  }
947
948  /**
949   * Return the logging bean.  There is a single logging bean per
950   * VM instance.
951   * @since 1.5
952   */
953  public static synchronized LoggingMXBean getLoggingMXBean()
954  {
955    if (loggingBean == null)
956      {
957        loggingBean = new LoggingMXBean()
958        {
959          public String getLoggerLevel(String logger)
960          {
961            LogManager mgr = getLogManager();
962            Logger l = mgr.getLogger(logger);
963            if (l == null)
964              return null;
965            Level lev = l.getLevel();
966            if (lev == null)
967              return "";
968            return lev.getName();
969          }
970
971          public List getLoggerNames()
972          {
973            LogManager mgr = getLogManager();
974            // This is inefficient, but perhaps better for maintenance.
975            return Collections.list(mgr.getLoggerNames());
976          }
977
978          public String getParentLoggerName(String logger)
979          {
980            LogManager mgr = getLogManager();
981            Logger l = mgr.getLogger(logger);
982            if (l == null)
983              return null;
984            l = l.getParent();
985            if (l == null)
986              return "";
987            return l.getName();
988          }
989
990          public void setLoggerLevel(String logger, String level)
991          {
992            LogManager mgr = getLogManager();
993            Logger l = mgr.getLogger(logger);
994            if (l == null)
995              throw new IllegalArgumentException("no logger named " + logger);
996            Level newLevel;
997            if (level == null)
998              newLevel = null;
999            else
1000              newLevel = Level.parse(level);
1001            l.setLevel(newLevel);
1002          }
1003        };
1004      }
1005    return loggingBean;
1006  }
1007}