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 }