1 /*
2 * Copyright (c) 2000, 2007, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26
27 package java.util.logging;
28
29 import java.io.*;
30 import java.util.*;
31 import java.security.*;
32 import java.lang.ref.WeakReference;
33 import java.beans.PropertyChangeListener;
34 import java.beans.PropertyChangeSupport;
35 import java.net.URL;
36 import sun.security.action.GetPropertyAction;
37
38 /**
39 * There is a single global LogManager object that is used to
40 * maintain a set of shared state about Loggers and log services.
41 * <p>
42 * This LogManager object:
43 * <ul>
44 * <li> Manages a hierarchical namespace of Logger objects. All
45 * named Loggers are stored in this namespace.
46 * <li> Manages a set of logging control properties. These are
47 * simple key-value pairs that can be used by Handlers and
48 * other logging objects to configure themselves.
49 * </ul>
50 * <p>
51 * The global LogManager object can be retrieved using LogManager.getLogManager().
52 * The LogManager object is created during class initialization and
53 * cannot subsequently be changed.
54 * <p>
55 * At startup the LogManager class is located using the
56 * java.util.logging.manager system property.
57 * <p>
58 * By default, the LogManager reads its initial configuration from
59 * a properties file "lib/logging.properties" in the JRE directory.
60 * If you edit that property file you can change the default logging
61 * configuration for all uses of that JRE.
62 * <p>
63 * In addition, the LogManager uses two optional system properties that
64 * allow more control over reading the initial configuration:
65 * <ul>
66 * <li>"java.util.logging.config.class"
67 * <li>"java.util.logging.config.file"
68 * </ul>
69 * These two properties may be set via the Preferences API, or as
70 * command line property definitions to the "java" command, or as
71 * system property definitions passed to JNI_CreateJavaVM.
72 * <p>
73 * If the "java.util.logging.config.class" property is set, then the
74 * property value is treated as a class name. The given class will be
75 * loaded, an object will be instantiated, and that object's constructor
76 * is responsible for reading in the initial configuration. (That object
77 * may use other system properties to control its configuration.) The
78 * alternate configuration class can use <tt>readConfiguration(InputStream)</tt>
79 * to define properties in the LogManager.
80 * <p>
81 * If "java.util.logging.config.class" property is <b>not</b> set,
82 * then the "java.util.logging.config.file" system property can be used
83 * to specify a properties file (in java.util.Properties format). The
84 * initial logging configuration will be read from this file.
85 * <p>
86 * If neither of these properties is defined then, as described
87 * above, the LogManager will read its initial configuration from
88 * a properties file "lib/logging.properties" in the JRE directory.
89 * <p>
90 * The properties for loggers and Handlers will have names starting
91 * with the dot-separated name for the handler or logger.
92 * <p>
93 * The global logging properties may include:
94 * <ul>
95 * <li>A property "handlers". This defines a whitespace or comma separated
96 * list of class names for handler classes to load and register as
97 * handlers on the root Logger (the Logger named ""). Each class
98 * name must be for a Handler class which has a default constructor.
99 * Note that these Handlers may be created lazily, when they are
100 * first used.
101 *
102 * <li>A property "<logger>.handlers". This defines a whitespace or
103 * comma separated list of class names for handlers classes to
104 * load and register as handlers to the specified logger. Each class
105 * name must be for a Handler class which has a default constructor.
106 * Note that these Handlers may be created lazily, when they are
107 * first used.
108 *
109 * <li>A property "<logger>.useParentHandlers". This defines a boolean
110 * value. By default every logger calls its parent in addition to
111 * handling the logging message itself, this often result in messages
112 * being handled by the root logger as well. When setting this property
113 * to false a Handler needs to be configured for this logger otherwise
114 * no logging messages are delivered.
115 *
116 * <li>A property "config". This property is intended to allow
117 * arbitrary configuration code to be run. The property defines a
118 * whitespace or comma separated list of class names. A new instance will be
119 * created for each named class. The default constructor of each class
120 * may execute arbitrary code to update the logging configuration, such as
121 * setting logger levels, adding handlers, adding filters, etc.
122 * </ul>
123 * <p>
124 * Note that all classes loaded during LogManager configuration are
125 * first searched on the system class path before any user class path.
126 * That includes the LogManager class, any config classes, and any
127 * handler classes.
128 * <p>
129 * Loggers are organized into a naming hierarchy based on their
130 * dot separated names. Thus "a.b.c" is a child of "a.b", but
131 * "a.b1" and a.b2" are peers.
132 * <p>
133 * All properties whose names end with ".level" are assumed to define
134 * log levels for Loggers. Thus "foo.level" defines a log level for
135 * the logger called "foo" and (recursively) for any of its children
136 * in the naming hierarchy. Log Levels are applied in the order they
137 * are defined in the properties file. Thus level settings for child
138 * nodes in the tree should come after settings for their parents.
139 * The property name ".level" can be used to set the level for the
140 * root of the tree.
141 * <p>
142 * All methods on the LogManager object are multi-thread safe.
143 *
144 * @since 1.4
145 */
146
147 public class LogManager {
148 // The global LogManager object
149 private static LogManager manager;
150
151 private final static Handler[] emptyHandlers = { };
152 private Properties props = new Properties();
153 private PropertyChangeSupport changes
154 = new PropertyChangeSupport(LogManager.class);
155 private final static Level defaultLevel = Level.INFO;
156
157 // Table of known loggers. Maps names to Loggers.
158 private Hashtable<String,WeakReference<Logger>> loggers =
159 new Hashtable<String,WeakReference<Logger>>();
160 // Tree of known loggers
161 private LogNode root = new LogNode(null);
162 private Logger rootLogger;
163
164 // Have we done the primordial reading of the configuration file?
165 // (Must be done after a suitable amount of java.lang.System
166 // initialization has been done)
167 private volatile boolean readPrimordialConfiguration;
168 // Have we initialized global (root) handlers yet?
169 // This gets set to false in readConfiguration
170 private boolean initializedGlobalHandlers = true;
171 // True if JVM death is imminent and the exit hook has been called.
172 private boolean deathImminent;
173
174 static {
175 AccessController.doPrivileged(new PrivilegedAction<Object>() {
176 public Object run() {
177 String cname = null;
178 try {
179 cname = System.getProperty("java.util.logging.manager");
180 if (cname != null) {
181 try {
182 Class clz = ClassLoader.getSystemClassLoader().loadClass(cname);
183 manager = (LogManager) clz.newInstance();
184 } catch (ClassNotFoundException ex) {
185 Class clz = Thread.currentThread().getContextClassLoader().loadClass(cname);
186 manager = (LogManager) clz.newInstance();
187 }
188 }
189 } catch (Exception ex) {
190 System.err.println("Could not load Logmanager \"" + cname + "\"");
191 ex.printStackTrace();
192 }
193 if (manager == null) {
194 manager = new LogManager();
195 }
196
197 // Create and retain Logger for the root of the namespace.
198 manager.rootLogger = manager.new RootLogger();
199 manager.addLogger(manager.rootLogger);
200
201 // Adding the global Logger. Doing so in the Logger.<clinit>
202 // would deadlock with the LogManager.<clinit>.
203 Logger.global.setLogManager(manager);
204 manager.addLogger(Logger.global);
205
206 // We don't call readConfiguration() here, as we may be running
207 // very early in the JVM startup sequence. Instead readConfiguration
208 // will be called lazily in getLogManager().
209 return null;
210 }
211 });
212 }
213
214
215 // This private class is used as a shutdown hook.
216 // It does a "reset" to close all open handlers.
217 private class Cleaner extends Thread {
218
219 private Cleaner() {
220 /* Set context class loader to null in order to avoid
221 * keeping a strong reference to an application classloader.
222 */
223 this.setContextClassLoader(null);
224 }
225
226 public void run() {
227 // This is to ensure the LogManager.<clinit> is completed
228 // before synchronized block. Otherwise deadlocks are possible.
229 LogManager mgr = manager;
230
231 // If the global handlers haven't been initialized yet, we
232 // don't want to initialize them just so we can close them!
233 synchronized (LogManager.this) {
234 // Note that death is imminent.
235 deathImminent = true;
236 initializedGlobalHandlers = true;
237 }
238
239 // Do a reset to close all active handlers.
240 reset();
241 }
242 }
243
244
245 /**
246 * Protected constructor. This is protected so that container applications
247 * (such as J2EE containers) can subclass the object. It is non-public as
248 * it is intended that there only be one LogManager object, whose value is
249 * retrieved by calling Logmanager.getLogManager.
250 */
251 protected LogManager() {
252 // Add a shutdown hook to close the global handlers.
253 try {
254 Runtime.getRuntime().addShutdownHook(new Cleaner());
255 } catch (IllegalStateException e) {
256 // If the VM is already shutting down,
257 // We do not need to register shutdownHook.
258 }
259 }
260
261 /**
262 * Return the global LogManager object.
263 */
264 public static LogManager getLogManager() {
265 if (manager != null) {
266 manager.readPrimordialConfiguration();
267 }
268 return manager;
269 }
270
271 private void readPrimordialConfiguration() {
272 if (!readPrimordialConfiguration) {
273 synchronized (this) {
274 if (!readPrimordialConfiguration) {
275 // If System.in/out/err are null, it's a good
276 // indication that we're still in the
277 // bootstrapping phase
278 if (System.out == null) {
279 return;
280 }
281 readPrimordialConfiguration = true;
282 try {
283 AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() {
284 public Object run() throws Exception {
285 readConfiguration();
286
287 // Platform loggers begin to delegate to java.util.logging.Logger
288 sun.util.logging.PlatformLogger.redirectPlatformLoggers();
289
290 return null;
291 }
292 });
293 } catch (Exception ex) {
294 // System.err.println("Can't read logging configuration:");
295 // ex.printStackTrace();
296 }
297 }
298 }
299 }
300 }
301
302 /**
303 * Adds an event listener to be invoked when the logging
304 * properties are re-read. Adding multiple instances of
305 * the same event Listener results in multiple entries
306 * in the property event listener table.
307 *
308 * @param l event listener
309 * @exception SecurityException if a security manager exists and if
310 * the caller does not have LoggingPermission("control").
311 * @exception NullPointerException if the PropertyChangeListener is null.
312 */
313 public void addPropertyChangeListener(PropertyChangeListener l) throws SecurityException {
314 if (l == null) {
315 throw new NullPointerException();
316 }
317 checkAccess();
318 changes.addPropertyChangeListener(l);
319 }
320
321 /**
322 * Removes an event listener for property change events.
323 * If the same listener instance has been added to the listener table
324 * through multiple invocations of <CODE>addPropertyChangeListener</CODE>,
325 * then an equivalent number of
326 * <CODE>removePropertyChangeListener</CODE> invocations are required to remove
327 * all instances of that listener from the listener table.
328 * <P>
329 * Returns silently if the given listener is not found.
330 *
331 * @param l event listener (can be null)
332 * @exception SecurityException if a security manager exists and if
333 * the caller does not have LoggingPermission("control").
334 */
335 public void removePropertyChangeListener(PropertyChangeListener l) throws SecurityException {
336 checkAccess();
337 changes.removePropertyChangeListener(l);
338 }
339
340 // Package-level method.
341 // Find or create a specified logger instance. If a logger has
342 // already been created with the given name it is returned.
343 // Otherwise a new logger instance is created and registered
344 // in the LogManager global namespace.
345 Logger demandLogger(String name) {
346 Logger result = getLogger(name);
347 if (result == null) {
348 result = new Logger(name, null);
349 addLogger(result);
350 result = getLogger(name);
351 }
352 return result;
353 }
354
355 // If logger.getUseParentHandlers() returns 'true' and any of the logger's
356 // parents have levels or handlers defined, make sure they are instantiated.
357 private void processParentHandlers(Logger logger, String name) {
358 int ix = 1;
359 for (;;) {
360 int ix2 = name.indexOf(".", ix);
361 if (ix2 < 0) {
362 break;
363 }
364 String pname = name.substring(0,ix2);
365
366 if (getProperty(pname+".level") != null ||
367 getProperty(pname+".handlers") != null) {
368 // This pname has a level/handlers definition.
369 // Make sure it exists.
370 demandLogger(pname);
371 }
372 ix = ix2+1;
373 }
374 }
375
376 // Add new per logger handlers.
377 // We need to raise privilege here. All our decisions will
378 // be made based on the logging configuration, which can
379 // only be modified by trusted code.
380 private void loadLoggerHandlers(final Logger logger, final String name,
381 final String handlersPropertyName) {
382 AccessController.doPrivileged(new PrivilegedAction<Object>() {
383 public Object run() {
384 if (logger != rootLogger) {
385 boolean useParent = getBooleanProperty(name + ".useParentHandlers", true);
386 if (!useParent) {
387 logger.setUseParentHandlers(false);
388 }
389 }
390
391 String names[] = parseClassNames(handlersPropertyName);
392 for (int i = 0; i < names.length; i++) {
393 String word = names[i];
394 try {
395 Class clz = ClassLoader.getSystemClassLoader().loadClass(word);
396 Handler hdl = (Handler) clz.newInstance();
397 try {
398 // Check if there is a property defining the
399 // this handler's level.
400 String levs = getProperty(word + ".level");
401 if (levs != null) {
402 hdl.setLevel(Level.parse(levs));
403 }
404 } catch (Exception ex) {
405 System.err.println("Can't set level for " + word);
406 // Probably a bad level. Drop through.
407 }
408 // Add this Handler to the logger
409 logger.addHandler(hdl);
410 } catch (Exception ex) {
411 System.err.println("Can't load log handler \"" + word + "\"");
412 System.err.println("" + ex);
413 ex.printStackTrace();
414 }
415 }
416 return null;
417 }});
418 }
419
420 /**
421 * Add a named logger. This does nothing and returns false if a logger
422 * with the same name is already registered.
423 * <p>
424 * The Logger factory methods call this method to register each
425 * newly created Logger.
426 * <p>
427 * The application should retain its own reference to the Logger
428 * object to avoid it being garbage collected. The LogManager
429 * may only retain a weak reference.
430 *
431 * @param logger the new logger.
432 * @return true if the argument logger was registered successfully,
433 * false if a logger of that name already exists.
434 * @exception NullPointerException if the logger name is null.
435 */
436 public synchronized boolean addLogger(Logger logger) {
437 final String name = logger.getName();
438 if (name == null) {
439 throw new NullPointerException();
440 }
441
442 WeakReference<Logger> ref = loggers.get(name);
443 if (ref != null) {
444 if (ref.get() == null) {
445 // Hashtable holds stale weak reference
446 // to a logger which has been GC-ed.
447 // Allow to register new one.
448 loggers.remove(name);
449 } else {
450 // We already have a registered logger with the given name.
451 return false;
452 }
453 }
454
455 // We're adding a new logger.
456 // Note that we are creating a weak reference here.
457 loggers.put(name, new WeakReference<Logger>(logger));
458
459 // Apply any initial level defined for the new logger.
460 Level level = getLevelProperty(name+".level", null);
461 if (level != null) {
462 doSetLevel(logger, level);
463 }
464
465 // Do we have a per logger handler too?
466 // Note: this will add a 200ms penalty
467 loadLoggerHandlers(logger, name, name+".handlers");
468 processParentHandlers(logger, name);
469
470 // Find the new node and its parent.
471 LogNode node = findNode(name);
472 node.loggerRef = new WeakReference<Logger>(logger);
473 Logger parent = null;
474 LogNode nodep = node.parent;
475 while (nodep != null) {
476 WeakReference<Logger> nodeRef = nodep.loggerRef;
477 if (nodeRef != null) {
478 parent = nodeRef.get();
479 if (parent != null) {
480 break;
481 }
482 }
483 nodep = nodep.parent;
484 }
485
486 if (parent != null) {
487 doSetParent(logger, parent);
488 }
489 // Walk over the children and tell them we are their new parent.
490 node.walkAndSetParent(logger);
491
492 return true;
493 }
494
495
496 // Private method to set a level on a logger.
497 // If necessary, we raise privilege before doing the call.
498 private static void doSetLevel(final Logger logger, final Level level) {
499 SecurityManager sm = System.getSecurityManager();
500 if (sm == null) {
501 // There is no security manager, so things are easy.
502 logger.setLevel(level);
503 return;
504 }
505 // There is a security manager. Raise privilege before
506 // calling setLevel.
507 AccessController.doPrivileged(new PrivilegedAction<Object>() {
508 public Object run() {
509 logger.setLevel(level);
510 return null;
511 }});
512 }
513
514
515
516 // Private method to set a parent on a logger.
517 // If necessary, we raise privilege before doing the setParent call.
518 private static void doSetParent(final Logger logger, final Logger parent) {
519 SecurityManager sm = System.getSecurityManager();
520 if (sm == null) {
521 // There is no security manager, so things are easy.
522 logger.setParent(parent);
523 return;
524 }
525 // There is a security manager. Raise privilege before
526 // calling setParent.
527 AccessController.doPrivileged(new PrivilegedAction<Object>() {
528 public Object run() {
529 logger.setParent(parent);
530 return null;
531 }});
532 }
533
534 // Find a node in our tree of logger nodes.
535 // If necessary, create it.
536 private LogNode findNode(String name) {
537 if (name == null || name.equals("")) {
538 return root;
539 }
540 LogNode node = root;
541 while (name.length() > 0) {
542 int ix = name.indexOf(".");
543 String head;
544 if (ix > 0) {
545 head = name.substring(0,ix);
546 name = name.substring(ix+1);
547 } else {
548 head = name;
549 name = "";
550 }
551 if (node.children == null) {
552 node.children = new HashMap<String,LogNode>();
553 }
554 LogNode child = node.children.get(head);
555 if (child == null) {
556 child = new LogNode(node);
557 node.children.put(head, child);
558 }
559 node = child;
560 }
561 return node;
562 }
563
564 /**
565 * Method to find a named logger.
566 * <p>
567 * Note that since untrusted code may create loggers with
568 * arbitrary names this method should not be relied on to
569 * find Loggers for security sensitive logging.
570 * <p>
571 * @param name name of the logger
572 * @return matching logger or null if none is found
573 */
574 public synchronized Logger getLogger(String name) {
575 WeakReference<Logger> ref = loggers.get(name);
576 if (ref == null) {
577 return null;
578 }
579 Logger logger = ref.get();
580 if (logger == null) {
581 // Hashtable holds stale weak reference
582 // to a logger which has been GC-ed.
583 loggers.remove(name);
584 }
585 return logger;
586 }
587
588 /**
589 * Get an enumeration of known logger names.
590 * <p>
591 * Note: Loggers may be added dynamically as new classes are loaded.
592 * This method only reports on the loggers that are currently registered.
593 * <p>
594 * @return enumeration of logger name strings
595 */
596 public synchronized Enumeration<String> getLoggerNames() {
597 return loggers.keys();
598 }
599
600 /**
601 * Reinitialize the logging properties and reread the logging configuration.
602 * <p>
603 * The same rules are used for locating the configuration properties
604 * as are used at startup. So normally the logging properties will
605 * be re-read from the same file that was used at startup.
606 * <P>
607 * Any log level definitions in the new configuration file will be
608 * applied using Logger.setLevel(), if the target Logger exists.
609 * <p>
610 * A PropertyChangeEvent will be fired after the properties are read.
611 *
612 * @exception SecurityException if a security manager exists and if
613 * the caller does not have LoggingPermission("control").
614 * @exception IOException if there are IO problems reading the configuration.
615 */
616 public void readConfiguration() throws IOException, SecurityException {
617 checkAccess();
618
619 // if a configuration class is specified, load it and use it.
620 String cname = System.getProperty("java.util.logging.config.class");
621 if (cname != null) {
622 try {
623 // Instantiate the named class. It is its constructor's
624 // responsibility to initialize the logging configuration, by
625 // calling readConfiguration(InputStream) with a suitable stream.
626 try {
627 Class clz = ClassLoader.getSystemClassLoader().loadClass(cname);
628 clz.newInstance();
629 return;
630 } catch (ClassNotFoundException ex) {
631 Class clz = Thread.currentThread().getContextClassLoader().loadClass(cname);
632 clz.newInstance();
633 return;
634 }
635 } catch (Exception ex) {
636 System.err.println("Logging configuration class \"" + cname + "\" failed");
637 System.err.println("" + ex);
638 // keep going and useful config file.
639 }
640 }
641
642 String fname = System.getProperty("java.util.logging.config.file");
643 if (fname == null) {
644 fname = System.getProperty("java.home");
645 if (fname == null) {
646 throw new Error("Can't find java.home ??");
647 }
648 File f = new File(fname, "lib");
649 f = new File(f, "logging.properties");
650 fname = f.getCanonicalPath();
651 }
652 InputStream in = new FileInputStream(fname);
653 BufferedInputStream bin = new BufferedInputStream(in);
654 try {
655 readConfiguration(bin);
656 } finally {
657 if (in != null) {
658 in.close();
659 }
660 }
661 }
662
663 /**
664 * Reset the logging configuration.
665 * <p>
666 * For all named loggers, the reset operation removes and closes
667 * all Handlers and (except for the root logger) sets the level
668 * to null. The root logger's level is set to Level.INFO.
669 *
670 * @exception SecurityException if a security manager exists and if
671 * the caller does not have LoggingPermission("control").
672 */
673
674 public void reset() throws SecurityException {
675 checkAccess();
676 synchronized (this) {
677 props = new Properties();
678 // Since we are doing a reset we no longer want to initialize
679 // the global handlers, if they haven't been initialized yet.
680 initializedGlobalHandlers = true;
681 }
682 Enumeration enum_ = getLoggerNames();
683 while (enum_.hasMoreElements()) {
684 String name = (String)enum_.nextElement();
685 resetLogger(name);
686 }
687 }
688
689
690 // Private method to reset an individual target logger.
691 private void resetLogger(String name) {
692 Logger logger = getLogger(name);
693 if (logger == null) {
694 return;
695 }
696 // Close all the Logger's handlers.
697 Handler[] targets = logger.getHandlers();
698 for (int i = 0; i < targets.length; i++) {
699 Handler h = targets[i];
700 logger.removeHandler(h);
701 try {
702 h.close();
703 } catch (Exception ex) {
704 // Problems closing a handler? Keep going...
705 }
706 }
707 if (name != null && name.equals("")) {
708 // This is the root logger.
709 logger.setLevel(defaultLevel);
710 } else {
711 logger.setLevel(null);
712 }
713 }
714
715 // get a list of whitespace separated classnames from a property.
716 private String[] parseClassNames(String propertyName) {
717 String hands = getProperty(propertyName);
718 if (hands == null) {
719 return new String[0];
720 }
721 hands = hands.trim();
722 int ix = 0;
723 Vector<String> result = new Vector<String>();
724 while (ix < hands.length()) {
725 int end = ix;
726 while (end < hands.length()) {
727 if (Character.isWhitespace(hands.charAt(end))) {
728 break;
729 }
730 if (hands.charAt(end) == ',') {
731 break;
732 }
733 end++;
734 }
735 String word = hands.substring(ix, end);
736 ix = end+1;
737 word = word.trim();
738 if (word.length() == 0) {
739 continue;
740 }
741 result.add(word);
742 }
743 return result.toArray(new String[result.size()]);
744 }
745
746 /**
747 * Reinitialize the logging properties and reread the logging configuration
748 * from the given stream, which should be in java.util.Properties format.
749 * A PropertyChangeEvent will be fired after the properties are read.
750 * <p>
751 * Any log level definitions in the new configuration file will be
752 * applied using Logger.setLevel(), if the target Logger exists.
753 *
754 * @param ins stream to read properties from
755 * @exception SecurityException if a security manager exists and if
756 * the caller does not have LoggingPermission("control").
757 * @exception IOException if there are problems reading from the stream.
758 */
759 public void readConfiguration(InputStream ins) throws IOException, SecurityException {
760 checkAccess();
761 reset();
762
763 // Load the properties
764 props.load(ins);
765 // Instantiate new configuration objects.
766 String names[] = parseClassNames("config");
767
768 for (int i = 0; i < names.length; i++) {
769 String word = names[i];
770 try {
771 Class clz = ClassLoader.getSystemClassLoader().loadClass(word);
772 clz.newInstance();
773 } catch (Exception ex) {
774 System.err.println("Can't load config class \"" + word + "\"");
775 System.err.println("" + ex);
776 // ex.printStackTrace();
777 }
778 }
779
780 // Set levels on any pre-existing loggers, based on the new properties.
781 setLevelsOnExistingLoggers();
782
783 // Notify any interested parties that our properties have changed.
784 changes.firePropertyChange(null, null, null);
785
786 // Note that we need to reinitialize global handles when
787 // they are first referenced.
788 synchronized (this) {
789 initializedGlobalHandlers = false;
790 }
791 }
792
793 /**
794 * Get the value of a logging property.
795 * The method returns null if the property is not found.
796 * @param name property name
797 * @return property value
798 */
799 public String getProperty(String name) {
800 return props.getProperty(name);
801 }
802
803 // Package private method to get a String property.
804 // If the property is not defined we return the given
805 // default value.
806 String getStringProperty(String name, String defaultValue) {
807 String val = getProperty(name);
808 if (val == null) {
809 return defaultValue;
810 }
811 return val.trim();
812 }
813
814 // Package private method to get an integer property.
815 // If the property is not defined or cannot be parsed
816 // we return the given default value.
817 int getIntProperty(String name, int defaultValue) {
818 String val = getProperty(name);
819 if (val == null) {
820 return defaultValue;
821 }
822 try {
823 return Integer.parseInt(val.trim());
824 } catch (Exception ex) {
825 return defaultValue;
826 }
827 }
828
829 // Package private method to get a boolean property.
830 // If the property is not defined or cannot be parsed
831 // we return the given default value.
832 boolean getBooleanProperty(String name, boolean defaultValue) {
833 String val = getProperty(name);
834 if (val == null) {
835 return defaultValue;
836 }
837 val = val.toLowerCase();
838 if (val.equals("true") || val.equals("1")) {
839 return true;
840 } else if (val.equals("false") || val.equals("0")) {
841 return false;
842 }
843 return defaultValue;
844 }
845
846 // Package private method to get a Level property.
847 // If the property is not defined or cannot be parsed
848 // we return the given default value.
849 Level getLevelProperty(String name, Level defaultValue) {
850 String val = getProperty(name);
851 if (val == null) {
852 return defaultValue;
853 }
854 try {
855 return Level.parse(val.trim());
856 } catch (Exception ex) {
857 return defaultValue;
858 }
859 }
860
861 // Package private method to get a filter property.
862 // We return an instance of the class named by the "name"
863 // property. If the property is not defined or has problems
864 // we return the defaultValue.
865 Filter getFilterProperty(String name, Filter defaultValue) {
866 String val = getProperty(name);
867 try {
868 if (val != null) {
869 Class clz = ClassLoader.getSystemClassLoader().loadClass(val);
870 return (Filter) clz.newInstance();
871 }
872 } catch (Exception ex) {
873 // We got one of a variety of exceptions in creating the
874 // class or creating an instance.
875 // Drop through.
876 }
877 // We got an exception. Return the defaultValue.
878 return defaultValue;
879 }
880
881
882 // Package private method to get a formatter property.
883 // We return an instance of the class named by the "name"
884 // property. If the property is not defined or has problems
885 // we return the defaultValue.
886 Formatter getFormatterProperty(String name, Formatter defaultValue) {
887 String val = getProperty(name);
888 try {
889 if (val != null) {
890 Class clz = ClassLoader.getSystemClassLoader().loadClass(val);
891 return (Formatter) clz.newInstance();
892 }
893 } catch (Exception ex) {
894 // We got one of a variety of exceptions in creating the
895 // class or creating an instance.
896 // Drop through.
897 }
898 // We got an exception. Return the defaultValue.
899 return defaultValue;
900 }
901
902 // Private method to load the global handlers.
903 // We do the real work lazily, when the global handlers
904 // are first used.
905 private synchronized void initializeGlobalHandlers() {
906 if (initializedGlobalHandlers) {
907 return;
908 }
909
910 initializedGlobalHandlers = true;
911
912 if (deathImminent) {
913 // Aaargh...
914 // The VM is shutting down and our exit hook has been called.
915 // Avoid allocating global handlers.
916 return;
917 }
918 loadLoggerHandlers(rootLogger, null, "handlers");
919 }
920
921
922 private Permission ourPermission = new LoggingPermission("control", null);
923
924 /**
925 * Check that the current context is trusted to modify the logging
926 * configuration. This requires LoggingPermission("control").
927 * <p>
928 * If the check fails we throw a SecurityException, otherwise
929 * we return normally.
930 *
931 * @exception SecurityException if a security manager exists and if
932 * the caller does not have LoggingPermission("control").
933 */
934 public void checkAccess() throws SecurityException {
935 SecurityManager sm = System.getSecurityManager();
936 if (sm == null) {
937 return;
938 }
939 sm.checkPermission(ourPermission);
940 }
941
942 // Nested class to represent a node in our tree of named loggers.
943 private static class LogNode {
944 HashMap<String,LogNode> children;
945 WeakReference<Logger> loggerRef;
946 LogNode parent;
947
948 LogNode(LogNode parent) {
949 this.parent = parent;
950 }
951
952 // Recursive method to walk the tree below a node and set
953 // a new parent logger.
954 void walkAndSetParent(Logger parent) {
955 if (children == null) {
956 return;
957 }
958 Iterator<LogNode> values = children.values().iterator();
959 while (values.hasNext()) {
960 LogNode node = values.next();
961 WeakReference<Logger> ref = node.loggerRef;
962 Logger logger = (ref == null) ? null : ref.get();
963 if (logger == null) {
964 node.walkAndSetParent(parent);
965 } else {
966 doSetParent(logger, parent);
967 }
968 }
969 }
970 }
971
972 // We use a subclass of Logger for the root logger, so
973 // that we only instantiate the global handlers when they
974 // are first needed.
975 private class RootLogger extends Logger {
976
977 private RootLogger() {
978 super("", null);
979 setLevel(defaultLevel);
980 }
981
982 public void log(LogRecord record) {
983 // Make sure that the global handlers have been instantiated.
984 initializeGlobalHandlers();
985 super.log(record);
986 }
987
988 public void addHandler(Handler h) {
989 initializeGlobalHandlers();
990 super.addHandler(h);
991 }
992
993 public void removeHandler(Handler h) {
994 initializeGlobalHandlers();
995 super.removeHandler(h);
996 }
997
998 public Handler[] getHandlers() {
999 initializeGlobalHandlers();
1000 return super.getHandlers();
1001 }
1002 }
1003
1004
1005 // Private method to be called when the configuration has
1006 // changed to apply any level settings to any pre-existing loggers.
1007 synchronized private void setLevelsOnExistingLoggers() {
1008 Enumeration enum_ = props.propertyNames();
1009 while (enum_.hasMoreElements()) {
1010 String key = (String)enum_.nextElement();
1011 if (!key.endsWith(".level")) {
1012 // Not a level definition.
1013 continue;
1014 }
1015 int ix = key.length() - 6;
1016 String name = key.substring(0, ix);
1017 Level level = getLevelProperty(key, null);
1018 if (level == null) {
1019 System.err.println("Bad level value for property: " + key);
1020 continue;
1021 }
1022 Logger l = getLogger(name);
1023 if (l == null) {
1024 continue;
1025 }
1026 l.setLevel(level);
1027 }
1028 }
1029
1030 // Management Support
1031 private static LoggingMXBean loggingMXBean = null;
1032 /**
1033 * String representation of the
1034 * {@link javax.management.ObjectName} for {@link LoggingMXBean}.
1035 * @since 1.5
1036 */
1037 public final static String LOGGING_MXBEAN_NAME
1038 = "java.util.logging:type=Logging";
1039
1040 /**
1041 * Returns <tt>LoggingMXBean</tt> for managing loggers.
1042 * An alternative way to manage loggers is using
1043 * the {@link java.lang.management.ManagementFactory#getPlatformMXBeans(Class)
1044 * ManagementFactory.getPlatformMXBeans} method as follows:
1045 * <pre>
1046 * List<{@link PlatformLoggingMXBean}> result = ManagementFactory.getPlatformMXBeans(PlatformLoggingMXBean.class);
1047 * </pre>
1048 *
1049 * @return a {@link LoggingMXBean} object.
1050 *
1051 * @see PlatformLoggingMXBean
1052 * @see java.lang.management.ManagementFactory
1053 * @since 1.5
1054 */
1055 public static synchronized LoggingMXBean getLoggingMXBean() {
1056 if (loggingMXBean == null) {
1057 loggingMXBean = new Logging();
1058 }
1059 return loggingMXBean;
1060 }
1061
1062 }
--- EOF ---