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    
005    This file is part of GNU Classpath.
006    
007    GNU Classpath is free software; you can redistribute it and/or modify
008    it under the terms of the GNU General Public License as published by
009    the Free Software Foundation; either version 2, or (at your option)
010    any later version.
011    
012    GNU Classpath is distributed in the hope that it will be useful, but
013    WITHOUT ANY WARRANTY; without even the implied warranty of
014    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
015    General Public License for more details.
016    
017    You should have received a copy of the GNU General Public License
018    along with GNU Classpath; see the file COPYING.  If not, write to the
019    Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
020    02110-1301 USA.
021    
022    Linking this library statically or dynamically with other modules is
023    making a combined work based on this library.  Thus, the terms and
024    conditions of the GNU General Public License cover the whole
025    combination.
026    
027    As a special exception, the copyright holders of this library give you
028    permission to link this library with independent modules to produce an
029    executable, regardless of the license terms of these independent
030    modules, and to copy and distribute the resulting executable under
031    terms of your choice, provided that you also meet, for each linked
032    independent module, the terms and conditions of the license of that
033    module.  An independent module is a module which is not derived from
034    or based on this library.  If you modify this library, you may extend
035    this exception to your version of the library, but you are not
036    obligated to do so.  If you do not wish to do so, delete this
037    exception statement from your version. */
038    
039    
040    package java.util.logging;
041    
042    import gnu.classpath.SystemProperties;
043    
044    import java.beans.PropertyChangeListener;
045    import java.beans.PropertyChangeSupport;
046    import java.io.ByteArrayInputStream;
047    import java.io.IOException;
048    import java.io.InputStream;
049    import java.lang.ref.WeakReference;
050    import java.net.URL;
051    import java.util.Collections;
052    import java.util.Enumeration;
053    import java.util.HashMap;
054    import java.util.Iterator;
055    import java.util.List;
056    import java.util.Map;
057    import java.util.Properties;
058    import 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     */
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          {
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    }