1 /* LogManager.java -- a class for maintaining Loggers and managing
2 configuration properties
3 Copyright (C) 2002 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)
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
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
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
.IOException
;
45 import java
.io
.InputStream
;
46 import java
.lang
.ref
.WeakReference
;
48 import java
.util
.Collections
;
49 import java
.util
.Enumeration
;
50 import java
.util
.Iterator
;
52 import java
.util
.Properties
;
53 import java
.util
.StringTokenizer
;
56 * The <code>LogManager</code> maintains a hierarchical namespace
57 * of Logger objects and manages properties for configuring the logging
58 * framework. There exists only one single <code>LogManager</code>
59 * per virtual machine. This instance can be retrieved using the
60 * static method {@link #getLogManager()}.
62 * <p><strong>Configuration Process:</strong> The global LogManager
63 * object is created and configured when the class
64 * <code>java.util.logging.LogManager</code> is initialized.
65 * The configuration process includes the subsequent steps:
68 * <li>If the system property <code>java.util.logging.manager</code>
69 * is set to the name of a subclass of
70 * <code>java.util.logging.LogManager</code>, an instance of
71 * that subclass is created and becomes the global LogManager.
72 * Otherwise, a new instance of LogManager is created.</li>
73 * <li>The <code>LogManager</code> constructor tries to create
74 * a new instance of the class specified by the system
75 * property <code>java.util.logging.config.class</code>.
76 * Typically, the constructor of this class will call
77 * <code>LogManager.getLogManager().readConfiguration(java.io.InputStream)</code>
78 * for configuring the logging framework.
79 * The configuration process stops at this point if
80 * the system property <code>java.util.logging.config.class</code>
81 * is set (irrespective of whether the class constructor
82 * could be called or an exception was thrown).</li>
84 * <li>If the system property <code>java.util.logging.config.class</code>
85 * is <em>not</em> set, the configuration parameters are read in from
86 * a file and passed to
87 * {@link #readConfiguration(java.io.InputStream)}.
88 * The name and location of this file are specified by the system
89 * property <code>java.util.logging.config.file</code>.</li>
90 * <li>If the system property <code>java.util.logging.config.file</code>
91 * is not set, however, the contents of the URL
92 * "{gnu.classpath.home.url}/logging.properties" are passed to
93 * {@link #readConfiguration(java.io.InputStream)}.
94 * Here, "{gnu.classpath.home.url}" stands for the value of
95 * the system property <code>gnu.classpath.home.url</code>.</li>
98 * @author Sascha Brawer (brawer@acm.org)
100 public class LogManager
103 * The singleton LogManager instance.
105 private static LogManager logManager
;
108 * The registered named loggers; maps the name of a Logger to
109 * a WeakReference to it.
112 final Logger rootLogger
;
115 * The properties for the logging framework which have been
118 private Properties properties
;
121 * A delegate object that provides support for handling
122 * PropertyChangeEvents. The API specification does not
123 * mention which bean should be the source in the distributed
124 * PropertyChangeEvents, but Mauve test code has determined that
125 * the Sun J2SE 1.4 reference implementation uses the LogManager
126 * class object. This is somewhat strange, as the class object
127 * is not the bean with which listeners have to register, but
128 * there is no reason for the GNU Classpath implementation to
129 * behave differently from the reference implementation in
132 private final PropertyChangeSupport pcs
= new PropertyChangeSupport( /* source bean */
135 protected LogManager()
137 if (logManager
!= null)
138 throw new IllegalStateException("there can be only one LogManager; use LogManager.getLogManager()");
141 loggers
= new java
.util
.HashMap();
142 rootLogger
= new Logger("", null);
143 addLogger(rootLogger
);
145 /* Make sure that Logger.global has the rootLogger as its parent.
147 * Logger.global is set during class initialization of Logger,
148 * which may or may not be before this code is being executed.
149 * For example, on the Sun 1.3.1 and 1.4.0 JVMs, Logger.global
150 * has been set before this code is being executed. In contrast,
151 * Logger.global still is null on GCJ 3.2. Since the LogManager
152 * and Logger classes are mutually dependent, both behaviors are
155 * This means that we cannot depend on Logger.global to have its
156 * value when this code executes, although that variable is final.
157 * Since Logger.getLogger will always return the same logger for
158 * the same name, the subsequent line works fine irrespective of
159 * the order in which classes are initialized.
161 Logger
.getLogger("global").setParent(rootLogger
);
162 Logger
.getLogger("global").setUseParentHandlers(true);
166 * Returns the globally shared LogManager instance.
168 public static LogManager
getLogManager()
177 /* The Javadoc description of the class explains
178 * what is going on here.
180 Object configurator
= createInstance(System
.getProperty("java.util.logging.config.class"),
181 /* must be instance of */ Object
.class);
185 if (configurator
== null)
186 getLogManager().readConfiguration();
188 catch (IOException ex
)
190 /* FIXME: Is it ok to ignore exceptions here? */
194 private static LogManager
makeLogManager()
196 String managerClassName
;
199 managerClassName
= System
.getProperty("java.util.logging.manager");
200 manager
= (LogManager
) createInstance(managerClassName
, LogManager
.class);
204 if (managerClassName
!= null)
205 System
.err
.println("WARNING: System property \"java.util.logging.manager\""
206 + " should be the name of a subclass of java.util.logging.LogManager");
208 return new LogManager();
212 * Registers a listener which will be notified when the
213 * logging properties are re-read.
215 public synchronized void addPropertyChangeListener(PropertyChangeListener listener
)
217 /* do not register null. */
220 pcs
.addPropertyChangeListener(listener
);
224 * Unregisters a listener.
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>.
230 public synchronized void removePropertyChangeListener(PropertyChangeListener listener
)
232 if (listener
!= null)
233 pcs
.removePropertyChangeListener(listener
);
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.
241 * <p>The <code>LogManager</code> only keeps weak references
242 * to registered loggers. Therefore, names can become available
243 * after automatic garbage collection.
245 * @param logger the logger to be added.
247 * @return <code>true</code>if <code>logger</code> was added,
248 * <code>false</code> otherwise.
250 * @throws NullPointerException if <code>name</code> is
253 public synchronized boolean addLogger(Logger logger
)
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.
267 /* This will throw a NullPointerException if logger is null,
268 * as required by the API specification.
270 name
= logger
.getName();
272 ref
= (WeakReference
) loggers
.get(name
);
275 if (ref
.get() != null)
278 /* There has been a logger under this name in the past,
279 * but it has been garbage collected.
284 /* Adding a named logger requires a security permission. */
285 if ((name
!= null) && ! name
.equals(""))
288 Logger parent
= findAncestor(logger
);
289 loggers
.put(name
, new WeakReference(logger
));
290 if (parent
!= logger
.getParent())
291 logger
.setParent(parent
);
293 /* It can happen that existing loggers should be children of
294 * the newly added logger. For example, assume that there
295 * already exist loggers under the names "", "foo", and "foo.bar.baz".
296 * When adding "foo.bar", the logger "foo.bar.baz" should change
297 * its parent to "foo.bar".
299 if (parent
!= rootLogger
)
301 for (Iterator iter
= loggers
.keySet().iterator(); iter
.hasNext();)
303 Logger possChild
= (Logger
) ((WeakReference
) loggers
.get(iter
.next()))
305 if ((possChild
== null) || (possChild
== logger
)
306 || (possChild
.getParent() != parent
))
309 if (! possChild
.getName().startsWith(name
))
312 if (possChild
.getName().charAt(name
.length()) != '.')
315 possChild
.setParent(logger
);
323 * Finds the closest ancestor for a logger among the currently
324 * registered ones. For example, if the currently registered
325 * loggers have the names "", "foo", and "foo.bar", the result for
326 * "foo.bar.baz" will be the logger whose name is "foo.bar".
328 * @param child a logger for whose name no logger has been
331 * @return the closest ancestor for <code>child</code>,
332 * or <code>null</code> if <code>child</code>
333 * is the root logger.
335 * @throws NullPointerException if <code>child</code>
336 * is <code>null</code>.
338 private synchronized Logger
findAncestor(Logger child
)
340 String childName
= child
.getName();
341 int childNameLength
= childName
.length();
342 Logger best
= rootLogger
;
343 int bestNameLength
= 0;
349 if (child
== rootLogger
)
352 for (Iterator iter
= loggers
.keySet().iterator(); iter
.hasNext();)
354 candName
= (String
) iter
.next();
355 candNameLength
= candName
.length();
357 if (candNameLength
> bestNameLength
358 && childNameLength
> candNameLength
359 && childName
.startsWith(candName
)
360 && childName
.charAt(candNameLength
) == '.')
362 cand
= (Logger
) ((WeakReference
) loggers
.get(candName
)).get();
363 if ((cand
== null) || (cand
== child
))
366 bestNameLength
= candName
.length();
375 * Returns a Logger given its name.
377 * @param name the name of the logger.
379 * @return a named Logger, or <code>null</code> if there is no
380 * logger with that name.
382 * @throw java.lang.NullPointerException if <code>name</code>
383 * is <code>null</code>.
385 public synchronized Logger
getLogger(String name
)
389 /* Throw a NullPointerException if name is null. */
392 ref
= (WeakReference
) loggers
.get(name
);
394 return (Logger
) ref
.get();
400 * Returns an Enumeration of currently registered Logger names.
401 * Since other threads can register loggers at any time, the
402 * result could be different any time this method is called.
404 * @return an Enumeration with the names of the currently
405 * registered Loggers.
407 public synchronized Enumeration
getLoggerNames()
409 return Collections
.enumeration(loggers
.keySet());
413 * Resets the logging configuration by removing all handlers for
414 * registered named loggers and setting their level to <code>null</code>.
415 * The level of the root logger will be set to <code>Level.INFO</code>.
417 * @throws SecurityException if a security manager exists and
418 * the caller is not granted the permission to control
419 * the logging infrastructure.
421 public synchronized void reset() throws SecurityException
423 /* Throw a SecurityException if the caller does not have the
424 * permission to control the logging infrastructure.
428 properties
= new Properties();
430 Iterator iter
= loggers
.values().iterator();
431 while (iter
.hasNext())
436 ref
= (WeakReference
) iter
.next();
439 logger
= (Logger
) ref
.get();
443 else if (logger
!= rootLogger
)
444 logger
.setLevel(null);
448 rootLogger
.setLevel(Level
.INFO
);
452 * Configures the logging framework by reading a configuration file.
453 * The name and location of this file are specified by the system
454 * property <code>java.util.logging.config.file</code>. If this
455 * property is not set, the URL
456 * "{gnu.classpath.home.url}/logging.properties" is taken, where
457 * "{gnu.classpath.home.url}" stands for the value of the system
458 * property <code>gnu.classpath.home.url</code>.
460 * <p>The task of configuring the framework is then delegated to
461 * {@link #readConfiguration(java.io.InputStream)}, which will
462 * notify registered listeners after having read the properties.
464 * @throws SecurityException if a security manager exists and
465 * the caller is not granted the permission to control
466 * the logging infrastructure, or if the caller is
467 * not granted the permission to read the configuration
470 * @throws IOException if there is a problem reading in the
471 * configuration file.
473 public synchronized void readConfiguration()
474 throws IOException
, SecurityException
477 InputStream inputStream
;
479 path
= System
.getProperty("java.util.logging.config.file");
480 if ((path
== null) || (path
.length() == 0))
482 String url
= (System
.getProperty("gnu.classpath.home.url")
483 + "/logging.properties");
484 inputStream
= new URL(url
).openStream();
487 inputStream
= new java
.io
.FileInputStream(path
);
491 readConfiguration(inputStream
);
495 /* Close the stream in order to save
496 * resources such as file descriptors.
502 public synchronized void readConfiguration(InputStream inputStream
)
503 throws IOException
, SecurityException
505 Properties newProperties
;
509 newProperties
= new Properties();
510 newProperties
.load(inputStream
);
511 this.properties
= newProperties
;
512 keys
= newProperties
.propertyNames();
514 while (keys
.hasMoreElements())
516 String key
= ((String
) keys
.nextElement()).trim();
517 String value
= newProperties
.getProperty(key
);
522 value
= value
.trim();
524 if ("handlers".equals(key
))
526 StringTokenizer tokenizer
= new StringTokenizer(value
);
527 while (tokenizer
.hasMoreTokens())
529 String handlerName
= tokenizer
.nextToken();
532 Class handlerClass
= Class
.forName(handlerName
);
533 getLogger("").addHandler((Handler
) handlerClass
536 catch (ClassCastException ex
)
538 System
.err
.println("[LogManager] class " + handlerName
539 + " is not subclass of java.util.logging.Handler");
543 //System.out.println("[LogManager.readConfiguration]"+ex);
548 if (key
.endsWith(".level"))
550 String loggerName
= key
.substring(0, key
.length() - 6);
551 Logger logger
= getLogger(loggerName
);
555 logger
= Logger
.getLogger(loggerName
);
560 logger
.setLevel(Level
.parse(value
));
564 //System.out.println("[LogManager.readConfiguration] "+_);
570 /* The API specification does not talk about the
571 * property name that is distributed with the
572 * PropertyChangeEvent. With test code, it could
573 * be determined that the Sun J2SE 1.4 reference
574 * implementation uses null for the property name.
576 pcs
.firePropertyChange(null, null, null);
580 * Returns the value of a configuration property as a String.
582 public synchronized String
getProperty(String name
)
584 if (properties
!= null)
585 return properties
.getProperty(name
);
591 * Returns the value of a configuration property as an integer.
592 * This function is a helper used by the Classpath implementation
593 * of java.util.logging, it is <em>not</em> specified in the
596 * @param name the name of the configuration property.
598 * @param defaultValue the value that will be returned if the
599 * property is not defined, or if its value is not an integer
602 static int getIntProperty(String name
, int defaultValue
)
606 return Integer
.parseInt(getLogManager().getProperty(name
));
615 * Returns the value of a configuration property as an integer,
616 * provided it is inside the acceptable range.
617 * This function is a helper used by the Classpath implementation
618 * of java.util.logging, it is <em>not</em> specified in the
621 * @param name the name of the configuration property.
623 * @param minValue the lowest acceptable value.
625 * @param maxValue the highest acceptable value.
627 * @param defaultValue the value that will be returned if the
628 * property is not defined, or if its value is not an integer
629 * number, or if it is less than the minimum value,
630 * or if it is greater than the maximum value.
632 static int getIntPropertyClamped(String name
, int defaultValue
,
633 int minValue
, int maxValue
)
635 int val
= getIntProperty(name
, defaultValue
);
636 if ((val
< minValue
) || (val
> maxValue
))
642 * Returns the value of a configuration property as a boolean.
643 * This function is a helper used by the Classpath implementation
644 * of java.util.logging, it is <em>not</em> specified in the
647 * @param name the name of the configuration property.
649 * @param defaultValue the value that will be returned if the
650 * property is not defined, or if its value is neither
651 * <code>"true"</code> nor <code>"false"</code>.
653 static boolean getBooleanProperty(String name
, boolean defaultValue
)
657 return (new Boolean(getLogManager().getProperty(name
))).booleanValue();
666 * Returns the value of a configuration property as a Level.
667 * This function is a helper used by the Classpath implementation
668 * of java.util.logging, it is <em>not</em> specified in the
671 * @param propertyName the name of the configuration property.
673 * @param defaultValue the value that will be returned if the
674 * property is not defined, or if
675 * {@link Level.parse(java.lang.String)} does not like
676 * the property value.
678 static Level
getLevelProperty(String propertyName
, Level defaultValue
)
682 return Level
.parse(getLogManager().getProperty(propertyName
));
691 * Returns the value of a configuration property as a Class.
692 * This function is a helper used by the Classpath implementation
693 * of java.util.logging, it is <em>not</em> specified in the
696 * @param propertyName the name of the configuration property.
698 * @param defaultValue the value that will be returned if the
699 * property is not defined, or if it does not specify
700 * the name of a loadable class.
702 static final Class
getClassProperty(String propertyName
, Class defaultValue
)
704 Class usingClass
= null;
708 String propertyValue
= logManager
.getProperty(propertyName
);
709 if (propertyValue
!= null)
710 usingClass
= Class
.forName(propertyValue
);
711 if (usingClass
!= null)
721 static final Object
getInstanceProperty(String propertyName
, Class ofClass
,
724 Class klass
= getClassProperty(propertyName
, defaultClass
);
730 Object obj
= klass
.newInstance();
731 if (ofClass
.isInstance(obj
))
738 if (defaultClass
== null)
743 return defaultClass
.newInstance();
745 catch (java
.lang
.InstantiationException ex
)
747 throw new RuntimeException(ex
.getMessage());
749 catch (java
.lang
.IllegalAccessException ex
)
751 throw new RuntimeException(ex
.getMessage());
756 * An instance of <code>LoggingPermission("control")</code>
757 * that is shared between calls to <code>checkAccess()</code>.
759 private static final LoggingPermission controlPermission
= new LoggingPermission("control",
763 * Checks whether the current security context allows changing
764 * the configuration of the logging framework. For the security
765 * context to be trusted, it has to be granted
766 * a LoggingPermission("control").
768 * @throws SecurityException if a security manager exists and
769 * the caller is not granted the permission to control
770 * the logging infrastructure.
772 public void checkAccess() throws SecurityException
774 SecurityManager sm
= System
.getSecurityManager();
776 sm
.checkPermission(controlPermission
);
780 * Creates a new instance of a class specified by name.
782 * @param className the name of the class of which a new instance
785 * @param ofClass the class to which the new instance should
786 * be either an instance or an instance of a subclass.
787 * FIXME: This description is just terrible.
789 * @return the new instance, or <code>null</code> if
790 * <code>className</code> is <code>null</code>, if no class
791 * with that name could be found, if there was an error
792 * loading that class, or if the constructor of the class
793 * has thrown an exception.
795 static final Object
createInstance(String className
, Class ofClass
)
799 if ((className
== null) || (className
.length() == 0))
804 klass
= Class
.forName(className
);
805 if (! ofClass
.isAssignableFrom(klass
))
808 return klass
.newInstance();
814 catch (java
.lang
.LinkageError _
)