2003-12-26 Guilhem Lavaux <guilhem@kaffe.org>
[official-gcc.git] / libjava / java / util / logging / LogManager.java
blob83379bb4942786f6727b8a6c319d5e7bcd73d88f
1 /* LogManager.java
2 -- a class for maintaining Loggers and managing configuration
3 properties
5 Copyright (C) 2002 Free Software Foundation, Inc.
7 This file is part of GNU Classpath.
9 GNU Classpath is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 2, or (at your option)
12 any later version.
14 GNU Classpath is distributed in the hope that it will be useful, but
15 WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with GNU Classpath; see the file COPYING. If not, write to the
21 Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
22 02111-1307 USA.
24 Linking this library statically or dynamically with other modules is
25 making a combined work based on this library. Thus, the terms and
26 conditions of the GNU General Public License cover the whole
27 combination.
29 As a special exception, the copyright holders of this library give you
30 permission to link this library with independent modules to produce an
31 executable, regardless of the license terms of these independent
32 modules, and to copy and distribute the resulting executable under
33 terms of your choice, provided that you also meet, for each linked
34 independent module, the terms and conditions of the license of that
35 module. An independent module is a module which is not derived from
36 or based on this library. If you modify this library, you may extend
37 this exception to your version of the library, but you are not
38 obligated to do so. If you do not wish to do so, delete this
39 exception statement from your version.
43 package java.util.logging;
45 import java.beans.PropertyChangeListener;
46 import java.beans.PropertyChangeSupport;
47 import java.io.IOException;
48 import java.io.InputStream;
49 import java.net.URL;
50 import java.util.Collections;
51 import java.util.Properties;
52 import java.util.Enumeration;
53 import java.util.Iterator;
54 import java.util.Map;
55 import java.util.StringTokenizer;
56 import java.lang.ref.WeakReference;
58 /**
59 * The <code>LogManager</code> maintains a hierarchical namespace
60 * of Logger objects and manages properties for configuring the logging
61 * framework. There exists only one single <code>LogManager</code>
62 * per virtual machine. This instance can be retrieved using the
63 * static method {@link #getLogManager()}.
65 * <p><strong>Configuration Process:</strong> The global LogManager
66 * object is created and configured when the class
67 * <code>java.util.logging.LogManager</code> is initialized.
68 * The configuration process includes the subsequent steps:
70 * <ol>
71 * <li>If the system property <code>java.util.logging.manager</code>
72 * is set to the name of a subclass of
73 * <code>java.util.logging.LogManager</code>, an instance of
74 * that subclass is created and becomes the global LogManager.
75 * 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>
95 * <li>If the system property <code>java.util.logging.config.file</code>
96 * is not set, however, the contents of the URL
97 * "{gnu.classpath.home.url}/logging.properties" are passed to
98 * {@link #readConfiguration(java.io.InputStream)}.
99 * Here, "{gnu.classpath.home.url}" stands for the value of
100 * the system property <code>gnu.classpath.home.url</code>.</li>
101 * </ol>
103 * @author Sascha Brawer (brawer@acm.org)
105 public class LogManager
108 * The singleton LogManager instance.
110 private static LogManager logManager;
114 * The registered named loggers; maps the name of a Logger to
115 * a WeakReference to it.
117 private Map loggers;
119 final Logger rootLogger;
123 * The properties for the logging framework which have been
124 * read in last.
126 private Properties properties;
129 * A delegate object that provides support for handling
130 * PropertyChangeEvents. The API specification does not
131 * mention which bean should be the source in the distributed
132 * PropertyChangeEvents, but Mauve test code has determined that
133 * the Sun J2SE 1.4 reference implementation uses the LogManager
134 * class object. This is somewhat strange, as the class object
135 * is not the bean with which listeners have to register, but
136 * there is no reason for the GNU Classpath implementation to
137 * behave differently from the reference implementation in
138 * this case.
140 private final PropertyChangeSupport pcs
141 = new PropertyChangeSupport(/* source bean */ LogManager.class);
143 protected LogManager()
145 if (logManager != null)
146 throw new IllegalStateException(
147 "there can be only one LogManager; use LogManager.getLogManager()");
149 logManager = this;
150 loggers = new java.util.HashMap();
151 rootLogger = new Logger("", null);
152 addLogger(rootLogger);
154 /* Make sure that Logger.global has the rootLogger as its parent.
156 * Logger.global is set during class initialization of Logger,
157 * which may or may not be before this code is being executed.
158 * For example, on the Sun 1.3.1 and 1.4.0 JVMs, Logger.global
159 * has been set before this code is being executed. In contrast,
160 * Logger.global still is null on GCJ 3.2. Since the LogManager
161 * and Logger classes are mutually dependent, both behaviors are
162 * correct.
164 * This means that we cannot depend on Logger.global to have its
165 * value when this code executes, although that variable is final.
166 * Since Logger.getLogger will always return the same logger for
167 * the same name, the subsequent line works fine irrespective of
168 * the order in which classes are initialized.
170 Logger.getLogger("global").setParent(rootLogger);
171 Logger.getLogger("global").setUseParentHandlers(true);
176 * Returns the globally shared LogManager instance.
178 public static LogManager getLogManager()
180 return logManager;
183 static
185 makeLogManager();
187 /* The Javadoc description of the class explains
188 * what is going on here.
190 Object configurator = createInstance(
191 System.getProperty("java.util.logging.config.class"),
192 /* must be instance of */ Object.class);
196 if (configurator == null)
197 getLogManager().readConfiguration();
199 catch (IOException ex)
201 /* FIXME: Is it ok to ignore exceptions here? */
206 private static LogManager makeLogManager()
208 String managerClassName;
209 LogManager manager;
211 managerClassName = System.getProperty("java.util.logging.manager");
212 manager = (LogManager) createInstance(managerClassName, LogManager.class);
213 if (manager != null)
214 return manager;
216 if (managerClassName != null)
217 System.err.println("WARNING: System property \"java.util.logging.manager\""
218 + " should be the name of a subclass of java.util.logging.LogManager");
220 return new LogManager();
225 * Registers a listener which will be notified when the
226 * logging properties are re-read.
228 public synchronized void addPropertyChangeListener(PropertyChangeListener listener)
230 /* do not register null. */
231 listener.getClass();
233 pcs.addPropertyChangeListener(listener);
238 * Unregisters a listener.
240 * If <code>listener</code> has not been registered previously,
241 * nothing happens. Also, no exception is thrown if
242 * <code>listener</code> is <code>null</code>.
244 public synchronized void removePropertyChangeListener(PropertyChangeListener listener)
246 if (listener != null)
247 pcs.removePropertyChangeListener(listener);
252 * Adds a named logger. If a logger with the same name has
253 * already been registered, the method returns <code>false</code>
254 * without adding the logger.
256 * <p>The <code>LogManager</code> only keeps weak references
257 * to registered loggers. Therefore, names can become available
258 * after automatic garbage collection.
260 * @param logger the logger to be added.
262 * @return <code>true<code>if <code>logger</code> was added,
263 * <code>false</code> otherwise.
265 * @throws NullPointerException if <code>name<code> is
266 * <code>null</code>.
268 public synchronized boolean addLogger(Logger logger)
270 /* To developers thinking about to remove the 'synchronized'
271 * declaration from this method: Please read the comment
272 * in java.util.logging.Logger.getLogger(String, String)
273 * and make sure that whatever you change wrt. synchronization
274 * does not endanger thread-safety of Logger.getLogger.
275 * The current implementation of Logger.getLogger assumes
276 * that LogManager does its synchronization on the globally
277 * shared instance of LogManager.
280 String name;
281 WeakReference ref;
283 /* This will throw a NullPointerException if logger is null,
284 * as required by the API specification.
286 name = logger.getName();
288 ref = (WeakReference) loggers.get(name);
289 if (ref != null)
291 if (ref.get() != null)
292 return false;
294 /* There has been a logger under this name in the past,
295 * but it has been garbage collected.
297 loggers.remove(ref);
300 /* Adding a named logger requires a security permission. */
301 if ((name != null) && !name.equals(""))
302 checkAccess();
304 Logger parent = findAncestor(logger);
305 loggers.put(name, new WeakReference(logger));
306 if (parent != logger.getParent())
307 logger.setParent(parent);
309 /* It can happen that existing loggers should be children of
310 * the newly added logger. For example, assume that there
311 * already exist loggers under the names "", "foo", and "foo.bar.baz".
312 * When adding "foo.bar", the logger "foo.bar.baz" should change
313 * its parent to "foo.bar".
315 if (parent != rootLogger)
317 for (Iterator iter = loggers.keySet().iterator(); iter.hasNext();)
319 Logger possChild = (Logger) ((WeakReference) loggers.get(iter.next())).get();
320 if ((possChild == null) || (possChild == logger) || (possChild.getParent() != parent))
321 continue;
323 if (!possChild.getName().startsWith(name))
324 continue;
326 if (possChild.getName().charAt(name.length()) != '.')
327 continue;
329 possChild.setParent(logger);
333 return true;
338 * Finds the closest ancestor for a logger among the currently
339 * registered ones. For example, if the currently registered
340 * loggers have the names "", "foo", and "foo.bar", the result for
341 * "foo.bar.baz" will be the logger whose name is "foo.bar".
343 * @param child a logger for whose name no logger has been
344 * registered.
346 * @return the closest ancestor for <code>child</code>,
347 * or <code>null</code> if <code>child</code>
348 * is the root logger.
350 * @throws NullPointerException if <code>child</code>
351 * is <code>null</code>.
353 private synchronized Logger findAncestor(Logger child)
355 String childName = child.getName();
356 Logger best = rootLogger;
357 int bestNameLength = 0;
359 Logger cand;
360 String candName;
361 int candNameLength;
363 if (child == rootLogger)
364 return null;
366 for (Iterator iter = loggers.keySet().iterator(); iter.hasNext();)
368 candName = (String) iter.next();
369 candNameLength = candName.length();
371 if ((candNameLength > bestNameLength)
372 && childName.startsWith(candName)
373 && (childName.charAt(candNameLength) == '.'))
375 cand = (Logger) ((WeakReference) loggers.get(candName)).get();
376 if ((cand == null) || (cand == child))
377 continue;
379 bestNameLength = candName.length();
380 best = cand;
384 return best;
389 * Returns a Logger given its name.
391 * @param name the name of the logger.
393 * @return a named Logger, or <code>null</code> if there is no
394 * logger with that name.
396 * @throw java.lang.NullPointerException if <code>name</code>
397 * is <code>null</code>.
399 public synchronized Logger getLogger(String name)
401 WeakReference ref;
403 /* Throw a NullPointerException if name is null. */
404 name.getClass();
406 ref = (WeakReference) loggers.get(name);
407 if (ref != null)
408 return (Logger) ref.get();
409 else
410 return null;
415 * Returns an Enumeration of currently registered Logger names.
416 * Since other threads can register loggers at any time, the
417 * result could be different any time this method is called.
419 * @return an Enumeration with the names of the currently
420 * registered Loggers.
422 public synchronized Enumeration getLoggerNames()
424 return Collections.enumeration(loggers.keySet());
429 * Resets the logging configuration by removing all handlers for
430 * registered named loggers and setting their level to <code>null</code>.
431 * The level of the root logger will be set to <code>Level.INFO</code>.
433 * @throws SecurityException if a security manager exists and
434 * the caller is not granted the permission to control
435 * the logging infrastructure.
437 public synchronized void reset()
438 throws SecurityException
440 /* Throw a SecurityException if the caller does not have the
441 * permission to control the logging infrastructure.
443 checkAccess();
445 properties = new Properties();
447 Iterator iter = loggers.values().iterator();
448 while (iter.hasNext())
450 WeakReference ref;
451 Logger logger;
453 ref = (WeakReference) iter.next();
454 if (ref != null)
456 logger = (Logger) ref.get();
458 if (logger == null)
459 iter.remove();
460 else if (logger != rootLogger)
461 logger.setLevel(null);
465 rootLogger.setLevel(Level.INFO);
470 * Configures the logging framework by reading a configuration file.
471 * The name and location of this file are specified by the system
472 * property <code>java.util.logging.config.file</code>. If this
473 * property is not set, the URL
474 * "{gnu.classpath.home.url}/logging.properties" is taken, where
475 * "{gnu.classpath.home.url}" stands for the value of the system
476 * property <code>gnu.classpath.home.url</code>.
478 * <p>The task of configuring the framework is then delegated to
479 * {@link #readConfiguration(java.io.InputStream)}, which will
480 * notify registered listeners after having read the properties.
482 * @throws SecurityException if a security manager exists and
483 * the caller is not granted the permission to control
484 * the logging infrastructure, or if the caller is
485 * not granted the permission to read the configuration
486 * file.
488 * @throws IOException if there is a problem reading in the
489 * configuration file.
491 public synchronized void readConfiguration()
492 throws IOException, SecurityException
494 String path;
495 InputStream inputStream;
497 path = System.getProperty("java.util.logging.config.file");
498 if ((path == null) || (path.length() == 0))
500 String url = (System.getProperty("gnu.classpath.home.url")
501 + "/logging.properties");
502 inputStream = new URL(url).openStream();
504 else
506 inputStream = new java.io.FileInputStream(path);
511 readConfiguration(inputStream);
513 finally
515 /* Close the stream in order to save
516 * resources such as file descriptors.
518 inputStream.close();
523 public synchronized void readConfiguration(InputStream inputStream)
524 throws IOException, SecurityException
526 Properties newProperties;
527 Enumeration keys;
529 checkAccess();
530 newProperties = new Properties();
531 newProperties.load(inputStream);
532 this.properties = newProperties;
533 keys = newProperties.propertyNames();
535 while (keys.hasMoreElements())
537 String key = ((String) keys.nextElement()).trim();
538 String value = newProperties.getProperty(key);
540 if (value == null)
541 continue;
543 value = value.trim();
545 if("handlers".equals(key))
547 StringTokenizer tokenizer = new StringTokenizer(value);
548 while(tokenizer.hasMoreTokens())
550 String handlerName = tokenizer.nextToken();
553 Class handlerClass = Class.forName(handlerName);
554 getLogger("").addHandler((Handler)handlerClass.newInstance());
556 catch (ClassCastException ex)
558 System.err.println("[LogManager] class " + handlerName + " is not subclass of java.util.logging.Handler");
560 catch (Exception ex)
562 //System.out.println("[LogManager.readConfiguration]"+ex);
567 if (key.endsWith(".level"))
569 String loggerName = key.substring(0, key.length() - 6);
570 Logger logger = getLogger(loggerName);
571 if (logger != null)
575 logger.setLevel(Level.parse(value));
577 catch (Exception _)
579 //System.out.println("[LogManager.readConfiguration] "+_);
581 continue;
586 /* The API specification does not talk about the
587 * property name that is distributed with the
588 * PropertyChangeEvent. With test code, it could
589 * be determined that the Sun J2SE 1.4 reference
590 * implementation uses null for the property name.
592 pcs.firePropertyChange(null, null, null);
597 * Returns the value of a configuration property as a String.
599 public synchronized String getProperty(String name)
601 if (properties != null)
602 return properties.getProperty(name);
603 else
604 return null;
609 * Returns the value of a configuration property as an integer.
610 * This function is a helper used by the Classpath implementation
611 * of java.util.logging, it is <em>not</em> specified in the
612 * logging API.
614 * @param name the name of the configuration property.
616 * @param defaultValue the value that will be returned if the
617 * property is not defined, or if its value is not an integer
618 * number.
620 static int getIntProperty(String name, int defaultValue)
624 return Integer.parseInt(getLogManager().getProperty(name));
626 catch (Exception ex)
628 return defaultValue;
634 * Returns the value of a configuration property as an integer,
635 * provided it is inside the acceptable range.
636 * This function is a helper used by the Classpath implementation
637 * of java.util.logging, it is <em>not</em> specified in the
638 * logging API.
640 * @param name the name of the configuration property.
642 * @param minValue the lowest acceptable value.
644 * @param maxValue the highest acceptable value.
646 * @param defaultValue the value that will be returned if the
647 * property is not defined, or if its value is not an integer
648 * number, or if it is less than the minimum value,
649 * or if it is greater than the maximum value.
651 static int getIntPropertyClamped(String name, int defaultValue,
652 int minValue, int maxValue)
654 int val = getIntProperty(name, defaultValue);
655 if ((val < minValue) || (val > maxValue))
656 val = defaultValue;
657 return val;
662 * Returns the value of a configuration property as a boolean.
663 * This function is a helper used by the Classpath implementation
664 * of java.util.logging, it is <em>not</em> specified in the
665 * logging API.
667 * @param name the name of the configuration property.
669 * @param defaultValue the value that will be returned if the
670 * property is not defined, or if its value is neither
671 * <code>"true"</code> nor <code>"false"</code>.
673 static boolean getBooleanProperty(String name, boolean defaultValue)
677 return (new Boolean(getLogManager().getProperty(name)))
678 .booleanValue();
680 catch (Exception ex)
682 return defaultValue;
688 * Returns the value of a configuration property as a Level.
689 * This function is a helper used by the Classpath implementation
690 * of java.util.logging, it is <em>not</em> specified in the
691 * logging API.
693 * @param propertyName the name of the configuration property.
695 * @param defaultValue the value that will be returned if the
696 * property is not defined, or if
697 * {@link Level.parse(java.lang.String)} does not like
698 * the property value.
700 static Level getLevelProperty(String propertyName, Level defaultValue)
704 return Level.parse(getLogManager().getProperty(propertyName));
706 catch (Exception ex)
708 return defaultValue;
714 * Returns the value of a configuration property as a Class.
715 * This function is a helper used by the Classpath implementation
716 * of java.util.logging, it is <em>not</em> specified in the
717 * logging API.
719 * @param propertyName the name of the configuration property.
721 * @param defaultValue the value that will be returned if the
722 * property is not defined, or if it does not specify
723 * the name of a loadable class.
725 static final Class getClassProperty(String propertyName, Class defaultValue)
727 Class usingClass = null;
731 String propertyValue = logManager.getProperty(propertyName);
732 if (propertyValue != null)
733 usingClass = Class.forName(propertyValue);
734 if (usingClass != null)
735 return usingClass;
737 catch (Exception _)
741 return defaultValue;
745 static final Object getInstanceProperty(String propertyName,
746 Class ofClass,
747 Class defaultClass)
749 Class klass = getClassProperty(propertyName, defaultClass);
750 if (klass == null)
751 return null;
755 Object obj = klass.newInstance();
756 if (ofClass.isInstance(obj))
757 return obj;
759 catch (Exception _)
763 if (defaultClass == null)
764 return null;
768 return defaultClass.newInstance();
770 catch (java.lang.InstantiationException ex)
772 throw new RuntimeException(ex.getMessage());
774 catch (java.lang.IllegalAccessException ex)
776 throw new RuntimeException(ex.getMessage());
782 * An instance of <code>LoggingPermission("control")</code>
783 * that is shared between calls to <code>checkAccess()</code>.
785 private static final LoggingPermission controlPermission
786 = new LoggingPermission("control", null);
790 * Checks whether the current security context allows changing
791 * the configuration of the logging framework. For the security
792 * context to be trusted, it has to be granted
793 * a LoggingPermission("control").
795 * @throws SecurityException if a security manager exists and
796 * the caller is not granted the permission to control
797 * the logging infrastructure.
799 public void checkAccess()
800 throws SecurityException
802 SecurityManager sm = System.getSecurityManager();
803 if (sm != null)
804 sm.checkPermission(controlPermission);
808 /**
809 * Creates a new instance of a class specified by name.
811 * @param className the name of the class of which a new instance
812 * should be created.
814 * @param ofClass the class to which the new instance should
815 * be either an instance or an instance of a subclass.
816 * FIXME: This description is just terrible.
818 * @return the new instance, or <code>null</code> if
819 * <code>className</code> is <code>null</code>, if no class
820 * with that name could be found, if there was an error
821 * loading that class, or if the constructor of the class
822 * has thrown an exception.
824 static final Object createInstance(String className, Class ofClass)
826 Class klass;
828 if ((className == null) || (className.length() == 0))
829 return null;
833 klass = Class.forName(className);
834 if (!ofClass.isAssignableFrom(klass))
835 return null;
837 return klass.newInstance();
839 catch (Exception _)
841 return null;
843 catch (java.lang.LinkageError _)
845 return null;