Merge from mainline.
[official-gcc.git] / libjava / classpath / java / util / logging / LogManager.java
blobe2604815b0ca614ee734f0d3078d8fdabfc9a156
1 /* LogManager.java -- a class for maintaining Loggers and managing
2 configuration properties
3 Copyright (C) 2002, 2005, 2006 Free Software Foundation, Inc.
5 This file is part of GNU Classpath.
7 GNU Classpath is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2, or (at your option)
10 any later version.
12 GNU Classpath is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with GNU Classpath; see the file COPYING. If not, write to the
19 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
20 02110-1301 USA.
22 Linking this library statically or dynamically with other modules is
23 making a combined work based on this library. Thus, the terms and
24 conditions of the GNU General Public License cover the whole
25 combination.
27 As a special exception, the copyright holders of this library give you
28 permission to link this library with independent modules to produce an
29 executable, regardless of the license terms of these independent
30 modules, and to copy and distribute the resulting executable under
31 terms of your choice, provided that you also meet, for each linked
32 independent module, the terms and conditions of the license of that
33 module. An independent module is a module which is not derived from
34 or based on this library. If you modify this library, you may extend
35 this exception to your version of the library, but you are not
36 obligated to do so. If you do not wish to do so, delete this
37 exception statement from your version. */
40 package java.util.logging;
42 import java.beans.PropertyChangeListener;
43 import java.beans.PropertyChangeSupport;
44 import java.io.ByteArrayInputStream;
45 import java.io.IOException;
46 import java.io.InputStream;
47 import java.lang.ref.WeakReference;
48 import java.net.URL;
49 import java.util.Collections;
50 import java.util.Enumeration;
51 import java.util.HashMap;
52 import java.util.Iterator;
53 import java.util.Map;
54 import java.util.Properties;
55 import java.util.StringTokenizer;
57 import gnu.classpath.SystemProperties;
59 /**
60 * The <code>LogManager</code> maintains a hierarchical namespace
61 * of Logger objects and manages properties for configuring the logging
62 * framework. There exists only one single <code>LogManager</code>
63 * per virtual machine. This instance can be retrieved using the
64 * static method {@link #getLogManager()}.
66 * <p><strong>Configuration Process:</strong> The global LogManager
67 * object is created and configured when the class
68 * <code>java.util.logging.LogManager</code> is initialized.
69 * The configuration process includes the subsequent steps:
71 * <ul>
72 * <li>If the system property <code>java.util.logging.manager</code>
73 * is set to the name of a subclass of
74 * <code>java.util.logging.LogManager</code>, an instance of
75 * that subclass is created and becomes the global LogManager.
76 * Otherwise, a new instance of LogManager is created.</li>
77 * <li>The <code>LogManager</code> constructor tries to create
78 * a new instance of the class specified by the system
79 * property <code>java.util.logging.config.class</code>.
80 * Typically, the constructor of this class will call
81 * <code>LogManager.getLogManager().readConfiguration(java.io.InputStream)</code>
82 * for configuring the logging framework.
83 * The configuration process stops at this point if
84 * the system property <code>java.util.logging.config.class</code>
85 * is set (irrespective of whether the class constructor
86 * could be called or an exception was thrown).</li>
88 * <li>If the system property <code>java.util.logging.config.class</code>
89 * is <em>not</em> set, the configuration parameters are read in from
90 * a file and passed to
91 * {@link #readConfiguration(java.io.InputStream)}.
92 * The name and location of this file are specified by the system
93 * property <code>java.util.logging.config.file</code>.</li>
94 * <li>If the system property <code>java.util.logging.config.file</code>
95 * is not set, however, the contents of the URL
96 * "{gnu.classpath.home.url}/logging.properties" are passed to
97 * {@link #readConfiguration(java.io.InputStream)}.
98 * Here, "{gnu.classpath.home.url}" stands for the value of
99 * the system property <code>gnu.classpath.home.url</code>.</li>
100 * </ul>
102 * <p>The <code>LogManager</code> has a level of <code>INFO</code> by
103 * default, and this will be inherited by <code>Logger</code>s unless they
104 * override it either by properties or programmatically.
106 * @author Sascha Brawer (brawer@acm.org)
108 public class LogManager
111 * The singleton LogManager instance.
113 private static LogManager logManager;
116 * The registered named loggers; maps the name of a Logger to
117 * a WeakReference to it.
119 private Map loggers;
122 * The properties for the logging framework which have been
123 * read in last.
125 private Properties properties;
128 * A delegate object that provides support for handling
129 * PropertyChangeEvents. The API specification does not
130 * mention which bean should be the source in the distributed
131 * PropertyChangeEvents, but Mauve test code has determined that
132 * the Sun J2SE 1.4 reference implementation uses the LogManager
133 * class object. This is somewhat strange, as the class object
134 * is not the bean with which listeners have to register, but
135 * there is no reason for the GNU Classpath implementation to
136 * behave differently from the reference implementation in
137 * this case.
139 private final PropertyChangeSupport pcs = new PropertyChangeSupport( /* source bean */
140 LogManager.class);
142 protected LogManager()
144 loggers = new HashMap();
148 * Returns the globally shared LogManager instance.
150 public static synchronized LogManager getLogManager()
152 if (logManager == null)
154 logManager = makeLogManager();
155 initLogManager();
157 return logManager;
160 private static final String MANAGER_PROPERTY = "java.util.logging.manager";
162 private static LogManager makeLogManager()
164 String managerClassName = SystemProperties.getProperty(MANAGER_PROPERTY);
165 LogManager manager = (LogManager) createInstance
166 (managerClassName, LogManager.class, MANAGER_PROPERTY);
167 if (manager == null)
168 manager = new LogManager();
169 return manager;
172 private static final String CONFIG_PROPERTY = "java.util.logging.config.class";
174 private static void initLogManager()
176 LogManager manager = getLogManager();
177 Logger.root.setLevel(Level.INFO);
178 manager.addLogger(Logger.root);
180 /* The Javadoc description of the class explains
181 * what is going on here.
183 Object configurator = createInstance(System.getProperty(CONFIG_PROPERTY),
184 /* must be instance of */ Object.class,
185 CONFIG_PROPERTY);
189 if (configurator == null)
190 manager.readConfiguration();
192 catch (IOException ex)
194 /* FIXME: Is it ok to ignore exceptions here? */
199 * Registers a listener which will be notified when the
200 * logging properties are re-read.
202 public synchronized void addPropertyChangeListener(PropertyChangeListener listener)
204 /* do not register null. */
205 listener.getClass();
207 pcs.addPropertyChangeListener(listener);
211 * Unregisters a listener.
213 * If <code>listener</code> has not been registered previously,
214 * nothing happens. Also, no exception is thrown if
215 * <code>listener</code> is <code>null</code>.
217 public synchronized void removePropertyChangeListener(PropertyChangeListener listener)
219 if (listener != null)
220 pcs.removePropertyChangeListener(listener);
224 * Adds a named logger. If a logger with the same name has
225 * already been registered, the method returns <code>false</code>
226 * without adding the logger.
228 * <p>The <code>LogManager</code> only keeps weak references
229 * to registered loggers. Therefore, names can become available
230 * after automatic garbage collection.
232 * @param logger the logger to be added.
234 * @return <code>true</code>if <code>logger</code> was added,
235 * <code>false</code> otherwise.
237 * @throws NullPointerException if <code>name</code> is
238 * <code>null</code>.
240 public synchronized boolean addLogger(Logger logger)
242 /* To developers thinking about to remove the 'synchronized'
243 * declaration from this method: Please read the comment
244 * in java.util.logging.Logger.getLogger(String, String)
245 * and make sure that whatever you change wrt. synchronization
246 * does not endanger thread-safety of Logger.getLogger.
247 * The current implementation of Logger.getLogger assumes
248 * that LogManager does its synchronization on the globally
249 * shared instance of LogManager.
251 String name;
252 WeakReference ref;
254 /* This will throw a NullPointerException if logger is null,
255 * as required by the API specification.
257 name = logger.getName();
259 ref = (WeakReference) loggers.get(name);
260 if (ref != null)
262 if (ref.get() != null)
263 return false;
265 /* There has been a logger under this name in the past,
266 * but it has been garbage collected.
268 loggers.remove(ref);
271 /* Adding a named logger requires a security permission. */
272 if ((name != null) && ! name.equals(""))
273 checkAccess();
275 Logger parent = findAncestor(logger);
276 loggers.put(name, new WeakReference(logger));
277 if (parent != logger.getParent())
278 logger.setParent(parent);
280 // The level of the newly added logger must be specified.
281 // The easiest case is if there is a level for exactly this logger
282 // in the properties. If no such level exists the level needs to be
283 // searched along the hirachy. So if there is a new logger 'foo.blah.blub'
284 // and an existing parent logger 'foo' the properties 'foo.blah.blub.level'
285 // and 'foo.blah.level' need to be checked. If both do not exist in the
286 // properties the level of the new logger is set to 'null' (i.e. it uses the
287 // level of its parent 'foo').
288 Level logLevel = logger.getLevel();
289 String searchName = name;
290 String parentName = parent != null ? parent.getName() : "";
291 while (logLevel == null && ! searchName.equals(parentName))
293 logLevel = getLevelProperty(searchName + ".level", logLevel);
294 int index = searchName.lastIndexOf('.');
295 if(index > -1)
296 searchName = searchName.substring(0,index);
297 else
298 searchName = "";
300 logger.setLevel(logLevel);
302 /* It can happen that existing loggers should be children of
303 * the newly added logger. For example, assume that there
304 * already exist loggers under the names "", "foo", and "foo.bar.baz".
305 * When adding "foo.bar", the logger "foo.bar.baz" should change
306 * its parent to "foo.bar".
308 if (parent != Logger.root)
310 for (Iterator iter = loggers.keySet().iterator(); iter.hasNext();)
312 Logger possChild = (Logger) ((WeakReference) loggers.get(iter.next()))
313 .get();
314 if ((possChild == null) || (possChild == logger)
315 || (possChild.getParent() != parent))
316 continue;
318 if (! possChild.getName().startsWith(name))
319 continue;
321 if (possChild.getName().charAt(name.length()) != '.')
322 continue;
324 possChild.setParent(logger);
328 return true;
332 * Finds the closest ancestor for a logger among the currently
333 * registered ones. For example, if the currently registered
334 * loggers have the names "", "foo", and "foo.bar", the result for
335 * "foo.bar.baz" will be the logger whose name is "foo.bar".
337 * @param child a logger for whose name no logger has been
338 * registered.
340 * @return the closest ancestor for <code>child</code>,
341 * or <code>null</code> if <code>child</code>
342 * is the root logger.
344 * @throws NullPointerException if <code>child</code>
345 * is <code>null</code>.
347 private synchronized Logger findAncestor(Logger child)
349 String childName = child.getName();
350 int childNameLength = childName.length();
351 Logger best = Logger.root;
352 int bestNameLength = 0;
354 Logger cand;
355 String candName;
356 int candNameLength;
358 if (child == Logger.root)
359 return null;
361 for (Iterator iter = loggers.keySet().iterator(); iter.hasNext();)
363 candName = (String) iter.next();
364 candNameLength = candName.length();
366 if (candNameLength > bestNameLength
367 && childNameLength > candNameLength
368 && childName.startsWith(candName)
369 && childName.charAt(candNameLength) == '.')
371 cand = (Logger) ((WeakReference) loggers.get(candName)).get();
372 if ((cand == null) || (cand == child))
373 continue;
375 bestNameLength = candName.length();
376 best = cand;
380 return best;
384 * Returns a Logger given its name.
386 * @param name the name of the logger.
388 * @return a named Logger, or <code>null</code> if there is no
389 * logger with that name.
391 * @throw java.lang.NullPointerException if <code>name</code>
392 * is <code>null</code>.
394 public synchronized Logger getLogger(String name)
396 WeakReference ref;
398 /* Throw a NullPointerException if name is null. */
399 name.getClass();
401 ref = (WeakReference) loggers.get(name);
402 if (ref != null)
403 return (Logger) ref.get();
404 else
405 return null;
409 * Returns an Enumeration of currently registered Logger names.
410 * Since other threads can register loggers at any time, the
411 * result could be different any time this method is called.
413 * @return an Enumeration with the names of the currently
414 * registered Loggers.
416 public synchronized Enumeration getLoggerNames()
418 return Collections.enumeration(loggers.keySet());
422 * Resets the logging configuration by removing all handlers for
423 * registered named loggers and setting their level to <code>null</code>.
424 * The level of the root logger will be set to <code>Level.INFO</code>.
426 * @throws SecurityException if a security manager exists and
427 * the caller is not granted the permission to control
428 * the logging infrastructure.
430 public synchronized void reset() throws SecurityException
432 /* Throw a SecurityException if the caller does not have the
433 * permission to control the logging infrastructure.
435 checkAccess();
437 properties = new Properties();
439 Iterator iter = loggers.values().iterator();
440 while (iter.hasNext())
442 WeakReference ref;
443 Logger logger;
445 ref = (WeakReference) iter.next();
446 if (ref != null)
448 logger = (Logger) ref.get();
450 if (logger == null)
451 iter.remove();
452 else if (logger != Logger.root)
454 logger.resetLogger();
455 logger.setLevel(null);
460 Logger.root.setLevel(Level.INFO);
461 Logger.root.resetLogger();
465 * Configures the logging framework by reading a configuration file.
466 * The name and location of this file are specified by the system
467 * property <code>java.util.logging.config.file</code>. If this
468 * property is not set, the URL
469 * "{gnu.classpath.home.url}/logging.properties" is taken, where
470 * "{gnu.classpath.home.url}" stands for the value of the system
471 * property <code>gnu.classpath.home.url</code>.
473 * <p>The task of configuring the framework is then delegated to
474 * {@link #readConfiguration(java.io.InputStream)}, which will
475 * notify registered listeners after having read the properties.
477 * @throws SecurityException if a security manager exists and
478 * the caller is not granted the permission to control
479 * the logging infrastructure, or if the caller is
480 * not granted the permission to read the configuration
481 * file.
483 * @throws IOException if there is a problem reading in the
484 * configuration file.
486 public synchronized void readConfiguration()
487 throws IOException, SecurityException
489 String path;
490 InputStream inputStream;
492 path = System.getProperty("java.util.logging.config.file");
493 if ((path == null) || (path.length() == 0))
495 String url = (System.getProperty("gnu.classpath.home.url")
496 + "/logging.properties");
499 inputStream = new URL(url).openStream();
501 catch (Exception e)
503 inputStream=null;
506 // If no config file could be found use a default configuration.
507 if(inputStream == null)
509 String defaultConfig = "handlers = java.util.logging.ConsoleHandler \n"
510 + ".level=INFO \n";
511 inputStream = new ByteArrayInputStream(defaultConfig.getBytes());
514 else
515 inputStream = new java.io.FileInputStream(path);
519 readConfiguration(inputStream);
521 finally
523 // Close the stream in order to save
524 // resources such as file descriptors.
525 inputStream.close();
529 public synchronized void readConfiguration(InputStream inputStream)
530 throws IOException, SecurityException
532 Properties newProperties;
533 Enumeration keys;
535 checkAccess();
536 newProperties = new Properties();
537 newProperties.load(inputStream);
538 reset();
539 this.properties = newProperties;
540 keys = newProperties.propertyNames();
542 while (keys.hasMoreElements())
544 String key = ((String) keys.nextElement()).trim();
545 String value = newProperties.getProperty(key);
547 if (value == null)
548 continue;
550 value = value.trim();
552 if ("handlers".equals(key))
554 StringTokenizer tokenizer = new StringTokenizer(value);
555 while (tokenizer.hasMoreTokens())
557 String handlerName = tokenizer.nextToken();
558 Handler handler = (Handler)
559 createInstance(handlerName, Handler.class, key);
560 Logger.root.addHandler(handler);
564 if (key.endsWith(".level"))
566 String loggerName = key.substring(0, key.length() - 6);
567 Logger logger = getLogger(loggerName);
569 if (logger == null)
571 logger = Logger.getLogger(loggerName);
572 addLogger(logger);
574 Level level = null;
577 level = Level.parse(value);
579 catch (IllegalArgumentException e)
581 warn("bad level \'" + value + "\'", e);
583 if (level != null)
585 logger.setLevel(level);
587 continue;
591 /* The API specification does not talk about the
592 * property name that is distributed with the
593 * PropertyChangeEvent. With test code, it could
594 * be determined that the Sun J2SE 1.4 reference
595 * implementation uses null for the property name.
597 pcs.firePropertyChange(null, null, null);
601 * Returns the value of a configuration property as a String.
603 public synchronized String getProperty(String name)
605 if (properties != null)
606 return properties.getProperty(name);
607 else
608 return null;
612 * Returns the value of a configuration property as an integer.
613 * This function is a helper used by the Classpath implementation
614 * of java.util.logging, it is <em>not</em> specified in the
615 * logging API.
617 * @param name the name of the configuration property.
619 * @param defaultValue the value that will be returned if the
620 * property is not defined, or if its value is not an integer
621 * number.
623 static int getIntProperty(String name, int defaultValue)
627 return Integer.parseInt(getLogManager().getProperty(name));
629 catch (Exception ex)
631 return defaultValue;
636 * Returns the value of a configuration property as an integer,
637 * provided it is inside the acceptable range.
638 * This function is a helper used by the Classpath implementation
639 * of java.util.logging, it is <em>not</em> specified in the
640 * logging API.
642 * @param name the name of the configuration property.
644 * @param minValue the lowest acceptable value.
646 * @param maxValue the highest acceptable value.
648 * @param defaultValue the value that will be returned if the
649 * property is not defined, or if its value is not an integer
650 * number, or if it is less than the minimum value,
651 * or if it is greater than the maximum value.
653 static int getIntPropertyClamped(String name, int defaultValue,
654 int minValue, int maxValue)
656 int val = getIntProperty(name, defaultValue);
657 if ((val < minValue) || (val > maxValue))
658 val = defaultValue;
659 return val;
663 * Returns the value of a configuration property as a boolean.
664 * This function is a helper used by the Classpath implementation
665 * of java.util.logging, it is <em>not</em> specified in the
666 * logging API.
668 * @param name the name of the configuration property.
670 * @param defaultValue the value that will be returned if the
671 * property is not defined, or if its value is neither
672 * <code>"true"</code> nor <code>"false"</code>.
674 static boolean getBooleanProperty(String name, boolean defaultValue)
678 return (Boolean.valueOf(getLogManager().getProperty(name))).booleanValue();
680 catch (Exception ex)
682 return defaultValue;
687 * Returns the value of a configuration property as a Level.
688 * This function is a helper used by the Classpath implementation
689 * of java.util.logging, it is <em>not</em> specified in the
690 * logging API.
692 * @param propertyName the name of the configuration property.
694 * @param defaultValue the value that will be returned if the
695 * property is not defined, or if
696 * {@link Level#parse(java.lang.String)} does not like
697 * the property value.
699 static Level getLevelProperty(String propertyName, Level defaultValue)
703 return Level.parse(getLogManager().getProperty(propertyName));
705 catch (Exception ex)
707 return defaultValue;
712 * Returns the value of a configuration property as a Class.
713 * This function is a helper used by the Classpath implementation
714 * of java.util.logging, it is <em>not</em> specified in the
715 * logging API.
717 * @param propertyName the name of the configuration property.
719 * @param defaultValue the value that will be returned if the
720 * property is not defined, or if it does not specify
721 * the name of a loadable class.
723 static final Class getClassProperty(String propertyName, Class defaultValue)
725 String propertyValue = logManager.getProperty(propertyName);
727 if (propertyValue != null)
730 return locateClass(propertyValue);
732 catch (ClassNotFoundException e)
734 warn(propertyName + " = " + propertyValue, e);
737 return defaultValue;
740 static final Object getInstanceProperty(String propertyName, Class ofClass,
741 Class defaultClass)
743 Class klass = getClassProperty(propertyName, defaultClass);
744 if (klass == null)
745 return null;
749 Object obj = klass.newInstance();
750 if (ofClass.isInstance(obj))
751 return obj;
753 catch (InstantiationException e)
755 warn(propertyName + " = " + klass.getName(), e);
757 catch (IllegalAccessException e)
759 warn(propertyName + " = " + klass.getName(), e);
762 if (defaultClass == null)
763 return null;
767 return defaultClass.newInstance();
769 catch (java.lang.InstantiationException ex)
771 throw new RuntimeException(ex.getMessage());
773 catch (java.lang.IllegalAccessException ex)
775 throw new RuntimeException(ex.getMessage());
780 * An instance of <code>LoggingPermission("control")</code>
781 * that is shared between calls to <code>checkAccess()</code>.
783 private static final LoggingPermission controlPermission = new LoggingPermission("control",
784 null);
787 * Checks whether the current security context allows changing
788 * the configuration of the logging framework. For the security
789 * context to be trusted, it has to be granted
790 * a LoggingPermission("control").
792 * @throws SecurityException if a security manager exists and
793 * the caller is not granted the permission to control
794 * the logging infrastructure.
796 public void checkAccess() throws SecurityException
798 SecurityManager sm = System.getSecurityManager();
799 if (sm != null)
800 sm.checkPermission(controlPermission);
804 * Creates a new instance of a class specified by name and verifies
805 * that it is an instance (or subclass of) a given type.
807 * @param className the name of the class of which a new instance
808 * should be created.
810 * @param type the object created must be an instance of
811 * <code>type</code> or any subclass of <code>type</code>
813 * @param property the system property to reference in error
814 * messages
816 * @return the new instance, or <code>null</code> if
817 * <code>className</code> is <code>null</code>, if no class
818 * with that name could be found, if there was an error
819 * loading that class, or if the constructor of the class
820 * has thrown an exception.
822 private static final Object createInstance(String className, Class type,
823 String property)
825 Class klass = null;
827 if ((className == null) || (className.length() == 0))
828 return null;
832 klass = locateClass(className);
833 if (type.isAssignableFrom(klass))
834 return klass.newInstance();
835 warn(property, className, "not an instance of " + type.getName());
837 catch (ClassNotFoundException e)
839 warn(property, className, "class not found");
841 catch (IllegalAccessException e)
843 warn(property, className, "illegal access");
845 catch (InstantiationException e)
847 warn(property, className, e);
849 catch (java.lang.LinkageError e)
851 warn(property, className, "linkage error");
854 return null;
857 private static final void warn(String property, String klass, Throwable t)
859 warn(property, klass, null, t);
862 private static final void warn(String property, String klass, String msg)
864 warn(property, klass, msg, null);
867 private static final void warn(String property, String klass, String msg,
868 Throwable t)
870 warn("error instantiating '" + klass + "' referenced by " + property +
871 (msg == null ? "" : ", " + msg), t);
875 * All debug warnings go through this method.
878 private static final void warn(String msg, Throwable t)
880 System.err.println("WARNING: " + msg);
881 if (t != null)
882 t.printStackTrace(System.err);
886 * Locates a class by first checking the system class loader and
887 * then checking the context class loader.
889 * @param name the fully qualified name of the Class to locate
890 * @return Class the located Class
893 private static Class locateClass(String name) throws ClassNotFoundException
895 ClassLoader loader = Thread.currentThread().getContextClassLoader();
898 return Class.forName(name, true, loader);
900 catch (ClassNotFoundException e)
902 loader = ClassLoader.getSystemClassLoader();
903 return Class.forName(name, true, loader);