0001 /*
0002 * Copyright 2000-2007 Sun Microsystems, Inc. All Rights Reserved.
0003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0004 *
0005 * This code is free software; you can redistribute it and/or modify it
0006 * under the terms of the GNU General Public License version 2 only, as
0007 * published by the Free Software Foundation. Sun designates this
0008 * particular file as subject to the "Classpath" exception as provided
0009 * by Sun in the LICENSE file that accompanied this code.
0010 *
0011 * This code is distributed in the hope that it will be useful, but WITHOUT
0012 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0013 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0014 * version 2 for more details (a copy is included in the LICENSE file that
0015 * accompanied this code).
0016 *
0017 * You should have received a copy of the GNU General Public License version
0018 * 2 along with this work; if not, write to the Free Software Foundation,
0019 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0020 *
0021 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
0022 * CA 95054 USA or visit www.sun.com if you need additional information or
0023 * have any questions.
0024 */
0025
0026 package java.util.logging;
0027
0028 import java.io.*;
0029 import java.util.*;
0030 import java.security.*;
0031 import java.lang.ref.WeakReference;
0032 import java.beans.PropertyChangeListener;
0033 import java.beans.PropertyChangeSupport;
0034 import java.net.URL;
0035 import sun.security.action.GetPropertyAction;
0036
0037 /**
0038 * There is a single global LogManager object that is used to
0039 * maintain a set of shared state about Loggers and log services.
0040 * <p>
0041 * This LogManager object:
0042 * <ul>
0043 * <li> Manages a hierarchical namespace of Logger objects. All
0044 * named Loggers are stored in this namespace.
0045 * <li> Manages a set of logging control properties. These are
0046 * simple key-value pairs that can be used by Handlers and
0047 * other logging objects to configure themselves.
0048 * </ul>
0049 * <p>
0050 * The global LogManager object can be retrieved using LogManager.getLogManager().
0051 * The LogManager object is created during class initialization and
0052 * cannot subsequently be changed.
0053 * <p>
0054 * At startup the LogManager class is located using the
0055 * java.util.logging.manager system property.
0056 * <p>
0057 * By default, the LogManager reads its initial configuration from
0058 * a properties file "lib/logging.properties" in the JRE directory.
0059 * If you edit that property file you can change the default logging
0060 * configuration for all uses of that JRE.
0061 * <p>
0062 * In addition, the LogManager uses two optional system properties that
0063 * allow more control over reading the initial configuration:
0064 * <ul>
0065 * <li>"java.util.logging.config.class"
0066 * <li>"java.util.logging.config.file"
0067 * </ul>
0068 * These two properties may be set via the Preferences API, or as
0069 * command line property definitions to the "java" command, or as
0070 * system property definitions passed to JNI_CreateJavaVM.
0071 * <p>
0072 * If the "java.util.logging.config.class" property is set, then the
0073 * property value is treated as a class name. The given class will be
0074 * loaded, an object will be instantiated, and that object's constructor
0075 * is responsible for reading in the initial configuration. (That object
0076 * may use other system properties to control its configuration.) The
0077 * alternate configuration class can use <tt>readConfiguration(InputStream)</tt>
0078 * to define properties in the LogManager.
0079 * <p>
0080 * If "java.util.logging.config.class" property is <b>not</b> set,
0081 * then the "java.util.logging.config.file" system property can be used
0082 * to specify a properties file (in java.util.Properties format). The
0083 * initial logging configuration will be read from this file.
0084 * <p>
0085 * If neither of these properties is defined then, as described
0086 * above, the LogManager will read its initial configuration from
0087 * a properties file "lib/logging.properties" in the JRE directory.
0088 * <p>
0089 * The properties for loggers and Handlers will have names starting
0090 * with the dot-separated name for the handler or logger.
0091 * <p>
0092 * The global logging properties may include:
0093 * <ul>
0094 * <li>A property "handlers". This defines a whitespace or comma separated
0095 * list of class names for handler classes to load and register as
0096 * handlers on the root Logger (the Logger named ""). Each class
0097 * name must be for a Handler class which has a default constructor.
0098 * Note that these Handlers may be created lazily, when they are
0099 * first used.
0100 *
0101 * <li>A property "<logger>.handlers". This defines a whitespace or
0102 * comma separated list of class names for handlers classes to
0103 * load and register as handlers to the specified logger. Each class
0104 * name must be for a Handler class which has a default constructor.
0105 * Note that these Handlers may be created lazily, when they are
0106 * first used.
0107 *
0108 * <li>A property "<logger>.useParentHandlers". This defines a boolean
0109 * value. By default every logger calls its parent in addition to
0110 * handling the logging message itself, this often result in messages
0111 * being handled by the root logger as well. When setting this property
0112 * to false a Handler needs to be configured for this logger otherwise
0113 * no logging messages are delivered.
0114 *
0115 * <li>A property "config". This property is intended to allow
0116 * arbitrary configuration code to be run. The property defines a
0117 * whitespace or comma separated list of class names. A new instance will be
0118 * created for each named class. The default constructor of each class
0119 * may execute arbitrary code to update the logging configuration, such as
0120 * setting logger levels, adding handlers, adding filters, etc.
0121 * </ul>
0122 * <p>
0123 * Note that all classes loaded during LogManager configuration are
0124 * first searched on the system class path before any user class path.
0125 * That includes the LogManager class, any config classes, and any
0126 * handler classes.
0127 * <p>
0128 * Loggers are organized into a naming hierarchy based on their
0129 * dot separated names. Thus "a.b.c" is a child of "a.b", but
0130 * "a.b1" and a.b2" are peers.
0131 * <p>
0132 * All properties whose names end with ".level" are assumed to define
0133 * log levels for Loggers. Thus "foo.level" defines a log level for
0134 * the logger called "foo" and (recursively) for any of its children
0135 * in the naming hierarchy. Log Levels are applied in the order they
0136 * are defined in the properties file. Thus level settings for child
0137 * nodes in the tree should come after settings for their parents.
0138 * The property name ".level" can be used to set the level for the
0139 * root of the tree.
0140 * <p>
0141 * All methods on the LogManager object are multi-thread safe.
0142 *
0143 * @version 1.60, 05/18/07
0144 * @since 1.4
0145 */
0146
0147 public class LogManager {
0148 // The global LogManager object
0149 private static LogManager manager;
0150
0151 private final static Handler[] emptyHandlers = {};
0152 private Properties props = new Properties();
0153 private PropertyChangeSupport changes = new PropertyChangeSupport(
0154 LogManager.class);
0155 private final static Level defaultLevel = Level.INFO;
0156
0157 // Table of known loggers. Maps names to Loggers.
0158 private Hashtable<String, WeakReference<Logger>> loggers = new Hashtable<String, WeakReference<Logger>>();
0159 // Tree of known loggers
0160 private LogNode root = new LogNode(null);
0161 private Logger rootLogger;
0162
0163 // Have we done the primordial reading of the configuration file?
0164 // (Must be done after a suitable amount of java.lang.System
0165 // initialization has been done)
0166 private volatile boolean readPrimordialConfiguration;
0167 // Have we initialized global (root) handlers yet?
0168 // This gets set to false in readConfiguration
0169 private boolean initializedGlobalHandlers = true;
0170 // True if JVM death is imminent and the exit hook has been called.
0171 private boolean deathImminent;
0172
0173 static {
0174 AccessController.doPrivileged(new PrivilegedAction<Object>() {
0175 public Object run() {
0176 String cname = null;
0177 try {
0178 cname = System
0179 .getProperty("java.util.logging.manager");
0180 if (cname != null) {
0181 try {
0182 Class clz = ClassLoader
0183 .getSystemClassLoader().loadClass(
0184 cname);
0185 manager = (LogManager) clz.newInstance();
0186 } catch (ClassNotFoundException ex) {
0187 Class clz = Thread.currentThread()
0188 .getContextClassLoader().loadClass(
0189 cname);
0190 manager = (LogManager) clz.newInstance();
0191 }
0192 }
0193 } catch (Exception ex) {
0194 System.err.println("Could not load Logmanager \""
0195 + cname + "\"");
0196 ex.printStackTrace();
0197 }
0198 if (manager == null) {
0199 manager = new LogManager();
0200 }
0201
0202 // Create and retain Logger for the root of the namespace.
0203 manager.rootLogger = manager.new RootLogger();
0204 manager.addLogger(manager.rootLogger);
0205
0206 // Adding the global Logger. Doing so in the Logger.<clinit>
0207 // would deadlock with the LogManager.<clinit>.
0208 Logger.global.setLogManager(manager);
0209 manager.addLogger(Logger.global);
0210
0211 // We don't call readConfiguration() here, as we may be running
0212 // very early in the JVM startup sequence. Instead readConfiguration
0213 // will be called lazily in getLogManager().
0214 return null;
0215 }
0216 });
0217 }
0218
0219 // This private class is used as a shutdown hook.
0220 // It does a "reset" to close all open handlers.
0221 private class Cleaner extends Thread {
0222 public void run() {
0223 // This is to ensure the LogManager.<clinit> is completed
0224 // before synchronized block. Otherwise deadlocks are possible.
0225 LogManager mgr = manager;
0226
0227 // If the global handlers haven't been initialized yet, we
0228 // don't want to initialize them just so we can close them!
0229 synchronized (LogManager.this ) {
0230 // Note that death is imminent.
0231 deathImminent = true;
0232 initializedGlobalHandlers = true;
0233 }
0234
0235 // Do a reset to close all active handlers.
0236 reset();
0237 }
0238 }
0239
0240 /**
0241 * Protected constructor. This is protected so that container applications
0242 * (such as J2EE containers) can subclass the object. It is non-public as
0243 * it is intended that there only be one LogManager object, whose value is
0244 * retrieved by calling Logmanager.getLogManager.
0245 */
0246 protected LogManager() {
0247 // Add a shutdown hook to close the global handlers.
0248 try {
0249 Runtime.getRuntime().addShutdownHook(new Cleaner());
0250 } catch (IllegalStateException e) {
0251 // If the VM is already shutting down,
0252 // We do not need to register shutdownHook.
0253 }
0254 }
0255
0256 /**
0257 * Return the global LogManager object.
0258 */
0259 public static LogManager getLogManager() {
0260 if (manager != null) {
0261 manager.readPrimordialConfiguration();
0262 }
0263 return manager;
0264 }
0265
0266 private void readPrimordialConfiguration() {
0267 if (!readPrimordialConfiguration) {
0268 synchronized (this ) {
0269 if (!readPrimordialConfiguration) {
0270 // If System.in/out/err are null, it's a good
0271 // indication that we're still in the
0272 // bootstrapping phase
0273 if (System.out == null) {
0274 return;
0275 }
0276 readPrimordialConfiguration = true;
0277 try {
0278 AccessController
0279 .doPrivileged(new PrivilegedExceptionAction<Object>() {
0280 public Object run()
0281 throws Exception {
0282 readConfiguration();
0283 return null;
0284 }
0285 });
0286 } catch (Exception ex) {
0287 // System.err.println("Can't read logging configuration:");
0288 // ex.printStackTrace();
0289 }
0290 }
0291 }
0292 }
0293 }
0294
0295 /**
0296 * Adds an event listener to be invoked when the logging
0297 * properties are re-read. Adding multiple instances of
0298 * the same event Listener results in multiple entries
0299 * in the property event listener table.
0300 *
0301 * @param l event listener
0302 * @exception SecurityException if a security manager exists and if
0303 * the caller does not have LoggingPermission("control").
0304 * @exception NullPointerException if the PropertyChangeListener is null.
0305 */
0306 public void addPropertyChangeListener(PropertyChangeListener l)
0307 throws SecurityException {
0308 if (l == null) {
0309 throw new NullPointerException();
0310 }
0311 checkAccess();
0312 changes.addPropertyChangeListener(l);
0313 }
0314
0315 /**
0316 * Removes an event listener for property change events.
0317 * If the same listener instance has been added to the listener table
0318 * through multiple invocations of <CODE>addPropertyChangeListener</CODE>,
0319 * then an equivalent number of
0320 * <CODE>removePropertyChangeListener</CODE> invocations are required to remove
0321 * all instances of that listener from the listener table.
0322 * <P>
0323 * Returns silently if the given listener is not found.
0324 *
0325 * @param l event listener (can be null)
0326 * @exception SecurityException if a security manager exists and if
0327 * the caller does not have LoggingPermission("control").
0328 */
0329 public void removePropertyChangeListener(PropertyChangeListener l)
0330 throws SecurityException {
0331 checkAccess();
0332 changes.removePropertyChangeListener(l);
0333 }
0334
0335 // Package-level method.
0336 // Find or create a specified logger instance. If a logger has
0337 // already been created with the given name it is returned.
0338 // Otherwise a new logger instance is created and registered
0339 // in the LogManager global namespace.
0340 synchronized Logger demandLogger(String name) {
0341 Logger result = getLogger(name);
0342 if (result == null) {
0343 result = new Logger(name, null);
0344 addLogger(result);
0345 result = getLogger(name);
0346 }
0347 return result;
0348 }
0349
0350 // If logger.getUseParentHandlers() returns 'true' and any of the logger's
0351 // parents have levels or handlers defined, make sure they are instantiated.
0352 private void processParentHandlers(Logger logger, String name) {
0353 int ix = 1;
0354 for (;;) {
0355 int ix2 = name.indexOf(".", ix);
0356 if (ix2 < 0) {
0357 break;
0358 }
0359 String pname = name.substring(0, ix2);
0360
0361 if (getProperty(pname + ".level") != null
0362 || getProperty(pname + ".handlers") != null) {
0363 // This pname has a level/handlers definition.
0364 // Make sure it exists.
0365 demandLogger(pname);
0366 }
0367 ix = ix2 + 1;
0368 }
0369 }
0370
0371 // Add new per logger handlers.
0372 // We need to raise privilege here. All our decisions will
0373 // be made based on the logging configuration, which can
0374 // only be modified by trusted code.
0375 private void loadLoggerHandlers(final Logger logger,
0376 final String name, final String handlersPropertyName) {
0377 AccessController.doPrivileged(new PrivilegedAction<Object>() {
0378 public Object run() {
0379 if (logger != rootLogger) {
0380 boolean useParent = getBooleanProperty(name
0381 + ".useParentHandlers", true);
0382 if (!useParent) {
0383 logger.setUseParentHandlers(false);
0384 }
0385 }
0386
0387 String names[] = parseClassNames(handlersPropertyName);
0388 for (int i = 0; i < names.length; i++) {
0389 String word = names[i];
0390 try {
0391 Class clz = ClassLoader.getSystemClassLoader()
0392 .loadClass(word);
0393 Handler hdl = (Handler) clz.newInstance();
0394 try {
0395 // Check if there is a property defining the
0396 // this handler's level.
0397 String levs = getProperty(word + ".level");
0398 if (levs != null) {
0399 hdl.setLevel(Level.parse(levs));
0400 }
0401 } catch (Exception ex) {
0402 System.err.println("Can't set level for "
0403 + word);
0404 // Probably a bad level. Drop through.
0405 }
0406 // Add this Handler to the logger
0407 logger.addHandler(hdl);
0408 } catch (Exception ex) {
0409 System.err.println("Can't load log handler \""
0410 + word + "\"");
0411 System.err.println("" + ex);
0412 ex.printStackTrace();
0413 }
0414 }
0415 return null;
0416 }
0417 });
0418 }
0419
0420 /**
0421 * Add a named logger. This does nothing and returns false if a logger
0422 * with the same name is already registered.
0423 * <p>
0424 * The Logger factory methods call this method to register each
0425 * newly created Logger.
0426 * <p>
0427 * The application should retain its own reference to the Logger
0428 * object to avoid it being garbage collected. The LogManager
0429 * may only retain a weak reference.
0430 *
0431 * @param logger the new logger.
0432 * @return true if the argument logger was registered successfully,
0433 * false if a logger of that name already exists.
0434 * @exception NullPointerException if the logger name is null.
0435 */
0436 public synchronized boolean addLogger(Logger logger) {
0437 final String name = logger.getName();
0438 if (name == null) {
0439 throw new NullPointerException();
0440 }
0441
0442 WeakReference<Logger> ref = loggers.get(name);
0443 if (ref != null) {
0444 if (ref.get() == null) {
0445 // Hashtable holds stale weak reference
0446 // to a logger which has been GC-ed.
0447 // Allow to register new one.
0448 loggers.remove(name);
0449 } else {
0450 // We already have a registered logger with the given name.
0451 return false;
0452 }
0453 }
0454
0455 // We're adding a new logger.
0456 // Note that we are creating a weak reference here.
0457 loggers.put(name, new WeakReference<Logger>(logger));
0458
0459 // Apply any initial level defined for the new logger.
0460 Level level = getLevelProperty(name + ".level", null);
0461 if (level != null) {
0462 doSetLevel(logger, level);
0463 }
0464
0465 // Do we have a per logger handler too?
0466 // Note: this will add a 200ms penalty
0467 loadLoggerHandlers(logger, name, name + ".handlers");
0468 processParentHandlers(logger, name);
0469
0470 // Find the new node and its parent.
0471 LogNode node = findNode(name);
0472 node.loggerRef = new WeakReference<Logger>(logger);
0473 Logger parent = null;
0474 LogNode nodep = node.parent;
0475 while (nodep != null) {
0476 WeakReference<Logger> nodeRef = nodep.loggerRef;
0477 if (nodeRef != null) {
0478 parent = nodeRef.get();
0479 if (parent != null) {
0480 break;
0481 }
0482 }
0483 nodep = nodep.parent;
0484 }
0485
0486 if (parent != null) {
0487 doSetParent(logger, parent);
0488 }
0489 // Walk over the children and tell them we are their new parent.
0490 node.walkAndSetParent(logger);
0491
0492 return true;
0493 }
0494
0495 // Private method to set a level on a logger.
0496 // If necessary, we raise privilege before doing the call.
0497 private static void doSetLevel(final Logger logger,
0498 final Level level) {
0499 SecurityManager sm = System.getSecurityManager();
0500 if (sm == null) {
0501 // There is no security manager, so things are easy.
0502 logger.setLevel(level);
0503 return;
0504 }
0505 // There is a security manager. Raise privilege before
0506 // calling setLevel.
0507 AccessController.doPrivileged(new PrivilegedAction<Object>() {
0508 public Object run() {
0509 logger.setLevel(level);
0510 return null;
0511 }
0512 });
0513 }
0514
0515 // Private method to set a parent on a logger.
0516 // If necessary, we raise privilege before doing the setParent call.
0517 private static void doSetParent(final Logger logger,
0518 final Logger parent) {
0519 SecurityManager sm = System.getSecurityManager();
0520 if (sm == null) {
0521 // There is no security manager, so things are easy.
0522 logger.setParent(parent);
0523 return;
0524 }
0525 // There is a security manager. Raise privilege before
0526 // calling setParent.
0527 AccessController.doPrivileged(new PrivilegedAction<Object>() {
0528 public Object run() {
0529 logger.setParent(parent);
0530 return null;
0531 }
0532 });
0533 }
0534
0535 // Find a node in our tree of logger nodes.
0536 // If necessary, create it.
0537 private LogNode findNode(String name) {
0538 if (name == null || name.equals("")) {
0539 return root;
0540 }
0541 LogNode node = root;
0542 while (name.length() > 0) {
0543 int ix = name.indexOf(".");
0544 String head;
0545 if (ix > 0) {
0546 head = name.substring(0, ix);
0547 name = name.substring(ix + 1);
0548 } else {
0549 head = name;
0550 name = "";
0551 }
0552 if (node.children == null) {
0553 node.children = new HashMap<String, LogNode>();
0554 }
0555 LogNode child = node.children.get(head);
0556 if (child == null) {
0557 child = new LogNode(node);
0558 node.children.put(head, child);
0559 }
0560 node = child;
0561 }
0562 return node;
0563 }
0564
0565 /**
0566 * Method to find a named logger.
0567 * <p>
0568 * Note that since untrusted code may create loggers with
0569 * arbitrary names this method should not be relied on to
0570 * find Loggers for security sensitive logging.
0571 * <p>
0572 * @param name name of the logger
0573 * @return matching logger or null if none is found
0574 */
0575 public synchronized Logger getLogger(String name) {
0576 WeakReference<Logger> ref = loggers.get(name);
0577 if (ref == null) {
0578 return null;
0579 }
0580 Logger logger = ref.get();
0581 if (logger == null) {
0582 // Hashtable holds stale weak reference
0583 // to a logger which has been GC-ed.
0584 loggers.remove(name);
0585 }
0586 return logger;
0587 }
0588
0589 /**
0590 * Get an enumeration of known logger names.
0591 * <p>
0592 * Note: Loggers may be added dynamically as new classes are loaded.
0593 * This method only reports on the loggers that are currently registered.
0594 * <p>
0595 * @return enumeration of logger name strings
0596 */
0597 public synchronized Enumeration<String> getLoggerNames() {
0598 return loggers.keys();
0599 }
0600
0601 /**
0602 * Reinitialize the logging properties and reread the logging configuration.
0603 * <p>
0604 * The same rules are used for locating the configuration properties
0605 * as are used at startup. So normally the logging properties will
0606 * be re-read from the same file that was used at startup.
0607 * <P>
0608 * Any log level definitions in the new configuration file will be
0609 * applied using Logger.setLevel(), if the target Logger exists.
0610 * <p>
0611 * A PropertyChangeEvent will be fired after the properties are read.
0612 *
0613 * @exception SecurityException if a security manager exists and if
0614 * the caller does not have LoggingPermission("control").
0615 * @exception IOException if there are IO problems reading the configuration.
0616 */
0617 public void readConfiguration() throws IOException,
0618 SecurityException {
0619 checkAccess();
0620
0621 // if a configuration class is specified, load it and use it.
0622 String cname = System
0623 .getProperty("java.util.logging.config.class");
0624 if (cname != null) {
0625 try {
0626 // Instantiate the named class. It is its constructor's
0627 // responsibility to initialize the logging configuration, by
0628 // calling readConfiguration(InputStream) with a suitable stream.
0629 try {
0630 Class clz = ClassLoader.getSystemClassLoader()
0631 .loadClass(cname);
0632 clz.newInstance();
0633 return;
0634 } catch (ClassNotFoundException ex) {
0635 Class clz = Thread.currentThread()
0636 .getContextClassLoader().loadClass(cname);
0637 clz.newInstance();
0638 return;
0639 }
0640 } catch (Exception ex) {
0641 System.err.println("Logging configuration class \""
0642 + cname + "\" failed");
0643 System.err.println("" + ex);
0644 // keep going and useful config file.
0645 }
0646 }
0647
0648 String fname = System
0649 .getProperty("java.util.logging.config.file");
0650 if (fname == null) {
0651 fname = System.getProperty("java.home");
0652 if (fname == null) {
0653 throw new Error("Can't find java.home ??");
0654 }
0655 File f = new File(fname, "lib");
0656 f = new File(f, "logging.properties");
0657 fname = f.getCanonicalPath();
0658 }
0659 InputStream in = new FileInputStream(fname);
0660 BufferedInputStream bin = new BufferedInputStream(in);
0661 try {
0662 readConfiguration(bin);
0663 } finally {
0664 if (in != null) {
0665 in.close();
0666 }
0667 }
0668 }
0669
0670 /**
0671 * Reset the logging configuration.
0672 * <p>
0673 * For all named loggers, the reset operation removes and closes
0674 * all Handlers and (except for the root logger) sets the level
0675 * to null. The root logger's level is set to Level.INFO.
0676 *
0677 * @exception SecurityException if a security manager exists and if
0678 * the caller does not have LoggingPermission("control").
0679 */
0680
0681 public void reset() throws SecurityException {
0682 checkAccess();
0683 synchronized (this ) {
0684 props = new Properties();
0685 // Since we are doing a reset we no longer want to initialize
0686 // the global handlers, if they haven't been initialized yet.
0687 initializedGlobalHandlers = true;
0688 }
0689 Enumeration enum_ = getLoggerNames();
0690 while (enum_.hasMoreElements()) {
0691 String name = (String) enum_.nextElement();
0692 resetLogger(name);
0693 }
0694 }
0695
0696 // Private method to reset an individual target logger.
0697 private void resetLogger(String name) {
0698 Logger logger = getLogger(name);
0699 if (logger == null) {
0700 return;
0701 }
0702 // Close all the Logger's handlers.
0703 Handler[] targets = logger.getHandlers();
0704 for (int i = 0; i < targets.length; i++) {
0705 Handler h = targets[i];
0706 logger.removeHandler(h);
0707 try {
0708 h.close();
0709 } catch (Exception ex) {
0710 // Problems closing a handler? Keep going...
0711 }
0712 }
0713 if (name != null && name.equals("")) {
0714 // This is the root logger.
0715 logger.setLevel(defaultLevel);
0716 } else {
0717 logger.setLevel(null);
0718 }
0719 }
0720
0721 // get a list of whitespace separated classnames from a property.
0722 private String[] parseClassNames(String propertyName) {
0723 String hands = getProperty(propertyName);
0724 if (hands == null) {
0725 return new String[0];
0726 }
0727 hands = hands.trim();
0728 int ix = 0;
0729 Vector<String> result = new Vector<String>();
0730 while (ix < hands.length()) {
0731 int end = ix;
0732 while (end < hands.length()) {
0733 if (Character.isWhitespace(hands.charAt(end))) {
0734 break;
0735 }
0736 if (hands.charAt(end) == ',') {
0737 break;
0738 }
0739 end++;
0740 }
0741 String word = hands.substring(ix, end);
0742 ix = end + 1;
0743 word = word.trim();
0744 if (word.length() == 0) {
0745 continue;
0746 }
0747 result.add(word);
0748 }
0749 return result.toArray(new String[result.size()]);
0750 }
0751
0752 /**
0753 * Reinitialize the logging properties and reread the logging configuration
0754 * from the given stream, which should be in java.util.Properties format.
0755 * A PropertyChangeEvent will be fired after the properties are read.
0756 * <p>
0757 * Any log level definitions in the new configuration file will be
0758 * applied using Logger.setLevel(), if the target Logger exists.
0759 *
0760 * @param ins stream to read properties from
0761 * @exception SecurityException if a security manager exists and if
0762 * the caller does not have LoggingPermission("control").
0763 * @exception IOException if there are problems reading from the stream.
0764 */
0765 public void readConfiguration(InputStream ins) throws IOException,
0766 SecurityException {
0767 checkAccess();
0768 reset();
0769
0770 // Load the properties
0771 props.load(ins);
0772 // Instantiate new configuration objects.
0773 String names[] = parseClassNames("config");
0774
0775 for (int i = 0; i < names.length; i++) {
0776 String word = names[i];
0777 try {
0778 Class clz = ClassLoader.getSystemClassLoader()
0779 .loadClass(word);
0780 clz.newInstance();
0781 } catch (Exception ex) {
0782 System.err.println("Can't load config class \"" + word
0783 + "\"");
0784 System.err.println("" + ex);
0785 // ex.printStackTrace();
0786 }
0787 }
0788
0789 // Set levels on any pre-existing loggers, based on the new properties.
0790 setLevelsOnExistingLoggers();
0791
0792 // Notify any interested parties that our properties have changed.
0793 changes.firePropertyChange(null, null, null);
0794
0795 // Note that we need to reinitialize global handles when
0796 // they are first referenced.
0797 synchronized (this ) {
0798 initializedGlobalHandlers = false;
0799 }
0800 }
0801
0802 /**
0803 * Get the value of a logging property.
0804 * The method returns null if the property is not found.
0805 * @param name property name
0806 * @return property value
0807 */
0808 public String getProperty(String name) {
0809 return props.getProperty(name);
0810 }
0811
0812 // Package private method to get a String property.
0813 // If the property is not defined we return the given
0814 // default value.
0815 String getStringProperty(String name, String defaultValue) {
0816 String val = getProperty(name);
0817 if (val == null) {
0818 return defaultValue;
0819 }
0820 return val.trim();
0821 }
0822
0823 // Package private method to get an integer property.
0824 // If the property is not defined or cannot be parsed
0825 // we return the given default value.
0826 int getIntProperty(String name, int defaultValue) {
0827 String val = getProperty(name);
0828 if (val == null) {
0829 return defaultValue;
0830 }
0831 try {
0832 return Integer.parseInt(val.trim());
0833 } catch (Exception ex) {
0834 return defaultValue;
0835 }
0836 }
0837
0838 // Package private method to get a boolean property.
0839 // If the property is not defined or cannot be parsed
0840 // we return the given default value.
0841 boolean getBooleanProperty(String name, boolean defaultValue) {
0842 String val = getProperty(name);
0843 if (val == null) {
0844 return defaultValue;
0845 }
0846 val = val.toLowerCase();
0847 if (val.equals("true") || val.equals("1")) {
0848 return true;
0849 } else if (val.equals("false") || val.equals("0")) {
0850 return false;
0851 }
0852 return defaultValue;
0853 }
0854
0855 // Package private method to get a Level property.
0856 // If the property is not defined or cannot be parsed
0857 // we return the given default value.
0858 Level getLevelProperty(String name, Level defaultValue) {
0859 String val = getProperty(name);
0860 if (val == null) {
0861 return defaultValue;
0862 }
0863 try {
0864 return Level.parse(val.trim());
0865 } catch (Exception ex) {
0866 return defaultValue;
0867 }
0868 }
0869
0870 // Package private method to get a filter property.
0871 // We return an instance of the class named by the "name"
0872 // property. If the property is not defined or has problems
0873 // we return the defaultValue.
0874 Filter getFilterProperty(String name, Filter defaultValue) {
0875 String val = getProperty(name);
0876 try {
0877 if (val != null) {
0878 Class clz = ClassLoader.getSystemClassLoader()
0879 .loadClass(val);
0880 return (Filter) clz.newInstance();
0881 }
0882 } catch (Exception ex) {
0883 // We got one of a variety of exceptions in creating the
0884 // class or creating an instance.
0885 // Drop through.
0886 }
0887 // We got an exception. Return the defaultValue.
0888 return defaultValue;
0889 }
0890
0891 // Package private method to get a formatter property.
0892 // We return an instance of the class named by the "name"
0893 // property. If the property is not defined or has problems
0894 // we return the defaultValue.
0895 Formatter getFormatterProperty(String name, Formatter defaultValue) {
0896 String val = getProperty(name);
0897 try {
0898 if (val != null) {
0899 Class clz = ClassLoader.getSystemClassLoader()
0900 .loadClass(val);
0901 return (Formatter) clz.newInstance();
0902 }
0903 } catch (Exception ex) {
0904 // We got one of a variety of exceptions in creating the
0905 // class or creating an instance.
0906 // Drop through.
0907 }
0908 // We got an exception. Return the defaultValue.
0909 return defaultValue;
0910 }
0911
0912 // Private method to load the global handlers.
0913 // We do the real work lazily, when the global handlers
0914 // are first used.
0915 private synchronized void initializeGlobalHandlers() {
0916 if (initializedGlobalHandlers) {
0917 return;
0918 }
0919
0920 initializedGlobalHandlers = true;
0921
0922 if (deathImminent) {
0923 // Aaargh...
0924 // The VM is shutting down and our exit hook has been called.
0925 // Avoid allocating global handlers.
0926 return;
0927 }
0928 loadLoggerHandlers(rootLogger, null, "handlers");
0929 }
0930
0931 private Permission ourPermission = new LoggingPermission("control",
0932 null);
0933
0934 /**
0935 * Check that the current context is trusted to modify the logging
0936 * configuration. This requires LoggingPermission("control").
0937 * <p>
0938 * If the check fails we throw a SecurityException, otherwise
0939 * we return normally.
0940 *
0941 * @exception SecurityException if a security manager exists and if
0942 * the caller does not have LoggingPermission("control").
0943 */
0944 public void checkAccess() throws SecurityException {
0945 SecurityManager sm = System.getSecurityManager();
0946 if (sm == null) {
0947 return;
0948 }
0949 sm.checkPermission(ourPermission);
0950 }
0951
0952 // Nested class to represent a node in our tree of named loggers.
0953 private static class LogNode {
0954 HashMap<String, LogNode> children;
0955 WeakReference<Logger> loggerRef;
0956 LogNode parent;
0957
0958 LogNode(LogNode parent) {
0959 this .parent = parent;
0960 }
0961
0962 // Recursive method to walk the tree below a node and set
0963 // a new parent logger.
0964 void walkAndSetParent(Logger parent) {
0965 if (children == null) {
0966 return;
0967 }
0968 Iterator<LogNode> values = children.values().iterator();
0969 while (values.hasNext()) {
0970 LogNode node = values.next();
0971 WeakReference<Logger> ref = node.loggerRef;
0972 Logger logger = (ref == null) ? null : ref.get();
0973 if (logger == null) {
0974 node.walkAndSetParent(parent);
0975 } else {
0976 doSetParent(logger, parent);
0977 }
0978 }
0979 }
0980 }
0981
0982 // We use a subclass of Logger for the root logger, so
0983 // that we only instantiate the global handlers when they
0984 // are first needed.
0985 private class RootLogger extends Logger {
0986
0987 private RootLogger() {
0988 super ("", null);
0989 setLevel(defaultLevel);
0990 }
0991
0992 public void log(LogRecord record) {
0993 // Make sure that the global handlers have been instantiated.
0994 initializeGlobalHandlers();
0995 super .log(record);
0996 }
0997
0998 public void addHandler(Handler h) {
0999 initializeGlobalHandlers();
1000 super .addHandler(h);
1001 }
1002
1003 public void removeHandler(Handler h) {
1004 initializeGlobalHandlers();
1005 super .removeHandler(h);
1006 }
1007
1008 public Handler[] getHandlers() {
1009 initializeGlobalHandlers();
1010 return super .getHandlers();
1011 }
1012 }
1013
1014 // Private method to be called when the configuration has
1015 // changed to apply any level settings to any pre-existing loggers.
1016 synchronized private void setLevelsOnExistingLoggers() {
1017 Enumeration enum_ = props.propertyNames();
1018 while (enum_.hasMoreElements()) {
1019 String key = (String) enum_.nextElement();
1020 if (!key.endsWith(".level")) {
1021 // Not a level definition.
1022 continue;
1023 }
1024 int ix = key.length() - 6;
1025 String name = key.substring(0, ix);
1026 Level level = getLevelProperty(key, null);
1027 if (level == null) {
1028 System.err.println("Bad level value for property: "
1029 + key);
1030 continue;
1031 }
1032 Logger l = getLogger(name);
1033 if (l == null) {
1034 continue;
1035 }
1036 l.setLevel(level);
1037 }
1038 }
1039
1040 // Management Support
1041 private static LoggingMXBean loggingMXBean = null;
1042 /**
1043 * String representation of the
1044 * {@link javax.management.ObjectName} for {@link LoggingMXBean}.
1045 * @since 1.5
1046 */
1047 public final static String LOGGING_MXBEAN_NAME = "java.util.logging:type=Logging";
1048
1049 /**
1050 * Returns <tt>LoggingMXBean</tt> for managing loggers.
1051 * The <tt>LoggingMXBean</tt> can also obtained from the
1052 * {@link java.lang.management.ManagementFactory#getPlatformMBeanServer
1053 * platform <tt>MBeanServer</tt>} method.
1054 *
1055 * @return a {@link LoggingMXBean} object.
1056 *
1057 * @see java.lang.management.ManagementFactory
1058 * @since 1.5
1059 */
1060 public static synchronized LoggingMXBean getLoggingMXBean() {
1061 if (loggingMXBean == null) {
1062 loggingMXBean = new Logging();
1063 }
1064 return loggingMXBean;
1065 }
1066
1067 }
|