1 /* 2 * Copyright (c) 2000, 2013, 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.ReferenceQueue; 33 import java.lang.ref.WeakReference; 34 import java.lang.reflect.Constructor; 35 import java.lang.reflect.InvocationTargetException; 36 import java.lang.reflect.Method; 37 import java.beans.PropertyChangeListener; 38 import sun.misc.JavaAWTAccess; 39 import sun.misc.SharedSecrets; 40 41 /** 42 * There is a single global LogManager object that is used to 43 * maintain a set of shared state about Loggers and log services. 44 * <p> 45 * This LogManager object: 46 * <ul> 47 * <li> Manages a hierarchical namespace of Logger objects. All 48 * named Loggers are stored in this namespace. 49 * <li> Manages a set of logging control properties. These are 50 * simple key-value pairs that can be used by Handlers and 51 * other logging objects to configure themselves. 52 * </ul> 53 * <p> 54 * The global LogManager object can be retrieved using LogManager.getLogManager(). 55 * The LogManager object is created during class initialization and 56 * cannot subsequently be changed. 57 * <p> 58 * At startup the LogManager class is located using the 59 * java.util.logging.manager system property. 60 * <p> 61 * The LogManager defines two optional system properties that allow control over 62 * the initial configuration: 63 * <ul> 64 * <li>"java.util.logging.config.class" 65 * <li>"java.util.logging.config.file" 66 * </ul> 67 * These two properties may be specified on the command line to the "java" 68 * command, or as system property definitions passed to JNI_CreateJavaVM. 69 * <p> 70 * If the "java.util.logging.config.class" property is set, then the 71 * property value is treated as a class name. The given class will be 72 * loaded, an object will be instantiated, and that object's constructor 73 * is responsible for reading in the initial configuration. (That object 74 * may use other system properties to control its configuration.) The 75 * alternate configuration class can use <tt>readConfiguration(InputStream)</tt> 76 * to define properties in the LogManager. 77 * <p> 78 * If "java.util.logging.config.class" property is <b>not</b> set, 79 * then the "java.util.logging.config.file" system property can be used 80 * to specify a properties file (in java.util.Properties format). The 81 * initial logging configuration will be read from this file. 82 * <p> 83 * If neither of these properties is defined then the LogManager uses its 84 * default configuration. The default configuration is typically loaded from the 85 * properties file "{@code lib/logging.properties}" in the Java installation 86 * directory. 87 * <p> 88 * The properties for loggers and Handlers will have names starting 89 * with the dot-separated name for the handler or logger. 90 * <p> 91 * The global logging properties may include: 92 * <ul> 93 * <li>A property "handlers". This defines a whitespace or comma separated 94 * list of class names for handler classes to load and register as 95 * handlers on the root Logger (the Logger named ""). Each class 96 * name must be for a Handler class which has a default constructor. 97 * Note that these Handlers may be created lazily, when they are 98 * first used. 99 * 100 * <li>A property "<logger>.handlers". This defines a whitespace or 101 * comma separated list of class names for handlers classes to 102 * load and register as handlers to the specified logger. Each class 103 * name must be for a Handler class which has a default constructor. 104 * Note that these Handlers may be created lazily, when they are 105 * first used. 106 * 107 * <li>A property "<logger>.useParentHandlers". This defines a boolean 108 * value. By default every logger calls its parent in addition to 109 * handling the logging message itself, this often result in messages 110 * being handled by the root logger as well. When setting this property 111 * to false a Handler needs to be configured for this logger otherwise 112 * no logging messages are delivered. 113 * 114 * <li>A property "config". This property is intended to allow 115 * arbitrary configuration code to be run. The property defines a 116 * whitespace or comma separated list of class names. A new instance will be 117 * created for each named class. The default constructor of each class 118 * may execute arbitrary code to update the logging configuration, such as 119 * setting logger levels, adding handlers, adding filters, etc. 120 * </ul> 121 * <p> 122 * Note that all classes loaded during LogManager configuration are 123 * first searched on the system class path before any user class path. 124 * That includes the LogManager class, any config classes, and any 125 * handler classes. 126 * <p> 127 * Loggers are organized into a naming hierarchy based on their 128 * dot separated names. Thus "a.b.c" is a child of "a.b", but 129 * "a.b1" and a.b2" are peers. 130 * <p> 131 * All properties whose names end with ".level" are assumed to define 132 * log levels for Loggers. Thus "foo.level" defines a log level for 133 * the logger called "foo" and (recursively) for any of its children 134 * in the naming hierarchy. Log Levels are applied in the order they 135 * are defined in the properties file. Thus level settings for child 136 * nodes in the tree should come after settings for their parents. 137 * The property name ".level" can be used to set the level for the 138 * root of the tree. 139 * <p> 140 * All methods on the LogManager object are multi-thread safe. 141 * 142 * @since 1.4 143 */ 144 145 public class LogManager { 146 // The global LogManager object 147 private static LogManager manager; 148 149 private Properties props = new Properties(); 150 private final static Level defaultLevel = Level.INFO; 151 152 // The map of the registered listeners. The map value is the registration 153 // count to allow for cases where the same listener is registered many times. 154 private final Map<Object,Integer> listenerMap = new HashMap<>(); 155 156 // LoggerContext for system loggers and user loggers 157 private final LoggerContext systemContext = new SystemLoggerContext(); 158 private final LoggerContext userContext = new LoggerContext(); 159 private Logger rootLogger; 160 161 // Have we done the primordial reading of the configuration file? 162 // (Must be done after a suitable amount of java.lang.System 163 // initialization has been done) 164 private volatile boolean readPrimordialConfiguration; 165 // Have we initialized global (root) handlers yet? 166 // This gets set to false in readConfiguration 167 private boolean initializedGlobalHandlers = true; 168 // True if JVM death is imminent and the exit hook has been called. 169 private boolean deathImminent; 170 171 static { 172 AccessController.doPrivileged(new PrivilegedAction<Object>() { 173 public Object run() { 174 String cname = null; 175 try { 176 cname = System.getProperty("java.util.logging.manager"); 177 if (cname != null) { 178 try { 179 Class<?> clz = ClassLoader.getSystemClassLoader().loadClass(cname); 180 manager = (LogManager) clz.newInstance(); 181 } catch (ClassNotFoundException ex) { 182 Class<?> clz = Thread.currentThread().getContextClassLoader().loadClass(cname); 183 manager = (LogManager) clz.newInstance(); 184 } 185 } 186 } catch (Exception ex) { 187 System.err.println("Could not load Logmanager \"" + cname + "\""); 188 ex.printStackTrace(); 189 } 190 if (manager == null) { 191 manager = new LogManager(); 192 } 193 194 // Create and retain Logger for the root of the namespace. 195 manager.rootLogger = manager.new RootLogger(); 196 manager.addLogger(manager.rootLogger); 197 manager.systemContext.addLocalLogger(manager.rootLogger); 198 199 // Adding the global Logger. Doing so in the Logger.<clinit> 200 // would deadlock with the LogManager.<clinit>. 201 // Do not call Logger.getGlobal() here as this might trigger 202 // the deadlock too. 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 283 try { 284 AccessController.doPrivileged(new PrivilegedExceptionAction<Void>() { 285 public Void run() throws Exception { 286 readConfiguration(); 287 288 // Platform loggers begin to delegate to java.util.logging.Logger 289 sun.util.logging.PlatformLogger.redirectPlatformLoggers(); 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 * <p><b>WARNING:</b> This method is omitted from this class in all subset 309 * Profiles of Java SE that do not include the {@code java.beans} package. 310 * </p> 311 * 312 * @param l event listener 313 * @exception SecurityException if a security manager exists and if 314 * the caller does not have LoggingPermission("control"). 315 * @exception NullPointerException if the PropertyChangeListener is null. 316 * @deprecated The dependency on {@code PropertyChangeListener} creates a 317 * significant impediment to future modularization of the Java 318 * platform. This method will be removed in a future release. 319 * The global {@code LogManager} can detect changes to the 320 * logging configuration by overridding the {@link 321 * #readConfiguration readConfiguration} method. 322 */ 323 @Deprecated 324 public void addPropertyChangeListener(PropertyChangeListener l) throws SecurityException { 325 PropertyChangeListener listener = Objects.requireNonNull(l); 326 checkPermission(); 327 synchronized (listenerMap) { 328 // increment the registration count if already registered 329 Integer value = listenerMap.get(listener); 330 value = (value == null) ? 1 : (value + 1); 331 listenerMap.put(listener, value); 332 } 333 } 334 335 /** 336 * Removes an event listener for property change events. 337 * If the same listener instance has been added to the listener table 338 * through multiple invocations of <CODE>addPropertyChangeListener</CODE>, 339 * then an equivalent number of 340 * <CODE>removePropertyChangeListener</CODE> invocations are required to remove 341 * all instances of that listener from the listener table. 342 * <P> 343 * Returns silently if the given listener is not found. 344 * 345 * <p><b>WARNING:</b> This method is omitted from this class in all subset 346 * Profiles of Java SE that do not include the {@code java.beans} package. 347 * </p> 348 * 349 * @param l event listener (can be null) 350 * @exception SecurityException if a security manager exists and if 351 * the caller does not have LoggingPermission("control"). 352 * @deprecated The dependency on {@code PropertyChangeListener} creates a 353 * significant impediment to future modularization of the Java 354 * platform. This method will be removed in a future release. 355 * The global {@code LogManager} can detect changes to the 356 * logging configuration by overridding the {@link 357 * #readConfiguration readConfiguration} method. 358 */ 359 @Deprecated 360 public void removePropertyChangeListener(PropertyChangeListener l) throws SecurityException { 361 checkPermission(); 362 if (l != null) { 363 PropertyChangeListener listener = l; 364 synchronized (listenerMap) { 365 Integer value = listenerMap.get(listener); 366 if (value != null) { 367 // remove from map if registration count is 1, otherwise 368 // just decrement its count 369 int i = value.intValue(); 370 if (i == 1) { 371 listenerMap.remove(listener); 372 } else { 373 assert i > 1; 374 listenerMap.put(listener, i - 1); 375 } 376 } 377 } 378 } 379 } 380 381 // Returns the LoggerContext for the user code (i.e. application or AppContext). 382 // Loggers are isolated from each AppContext. 383 private LoggerContext getUserContext() { 384 LoggerContext context = null; 385 386 SecurityManager sm = System.getSecurityManager(); 387 JavaAWTAccess javaAwtAccess = SharedSecrets.getJavaAWTAccess(); 388 if (sm != null && javaAwtAccess != null) { 389 synchronized (javaAwtAccess) { 390 // AppContext.getAppContext() returns the system AppContext if called 391 // from a system thread but Logger.getLogger might be called from 392 // an applet code. Instead, find the AppContext of the applet code 393 // from the execution stack. 394 Object ecx = javaAwtAccess.getExecutionContext(); 395 if (ecx == null) { 396 // fall back to thread group seach of AppContext 397 ecx = javaAwtAccess.getContext(); 398 } 399 if (ecx != null) { 400 context = (LoggerContext)javaAwtAccess.get(ecx, LoggerContext.class); 401 if (context == null) { 402 if (javaAwtAccess.isMainAppContext()) { 403 context = userContext; 404 } else { 405 context = new LoggerContext(); 406 } 407 javaAwtAccess.put(ecx, LoggerContext.class, context); 408 } 409 } 410 } 411 } 412 return context != null ? context : userContext; 413 } 414 415 private List<LoggerContext> contexts() { 416 List<LoggerContext> cxs = new ArrayList<>(); 417 cxs.add(systemContext); 418 cxs.add(getUserContext()); 419 return cxs; 420 } 421 422 // Find or create a specified logger instance. If a logger has 423 // already been created with the given name it is returned. 424 // Otherwise a new logger instance is created and registered 425 // in the LogManager global namespace. 426 // This method will always return a non-null Logger object. 427 // Synchronization is not required here. All synchronization for 428 // adding a new Logger object is handled by addLogger(). 429 // 430 // This method must delegate to the LogManager implementation to 431 // add a new Logger or return the one that has been added previously 432 // as a LogManager subclass may override the addLogger, getLogger, 433 // readConfiguration, and other methods. 434 Logger demandLogger(String name, String resourceBundleName, Class<?> caller) { 435 Logger result = getLogger(name); 436 if (result == null) { 437 // only allocate the new logger once 438 Logger newLogger = new Logger(name, resourceBundleName, caller); 439 do { 440 if (addLogger(newLogger)) { 441 // We successfully added the new Logger that we 442 // created above so return it without refetching. 443 return newLogger; 444 } 445 446 // We didn't add the new Logger that we created above 447 // because another thread added a Logger with the same 448 // name after our null check above and before our call 449 // to addLogger(). We have to refetch the Logger because 450 // addLogger() returns a boolean instead of the Logger 451 // reference itself. However, if the thread that created 452 // the other Logger is not holding a strong reference to 453 // the other Logger, then it is possible for the other 454 // Logger to be GC'ed after we saw it in addLogger() and 455 // before we can refetch it. If it has been GC'ed then 456 // we'll just loop around and try again. 457 result = getLogger(name); 458 } while (result == null); 459 } 460 return result; 461 } 462 463 Logger demandSystemLogger(String name, String resourceBundleName) { 464 // Add a system logger in the system context's namespace 465 final Logger sysLogger = systemContext.demandLogger(name, resourceBundleName); 466 467 // Add the system logger to the LogManager's namespace if not exist 468 // so that there is only one single logger of the given name. 469 // System loggers are visible to applications unless a logger of 470 // the same name has been added. 471 Logger logger; 472 do { 473 // First attempt to call addLogger instead of getLogger 474 // This would avoid potential bug in custom LogManager.getLogger 475 // implementation that adds a logger if does not exist 476 if (addLogger(sysLogger)) { 477 // successfully added the new system logger 478 logger = sysLogger; 479 } else { 480 logger = getLogger(name); 481 } 482 } while (logger == null); 483 484 // LogManager will set the sysLogger's handlers via LogManager.addLogger method. 485 if (logger != sysLogger && sysLogger.getHandlers().length == 0) { 486 // if logger already exists but handlers not set 487 final Logger l = logger; 488 AccessController.doPrivileged(new PrivilegedAction<Void>() { 489 public Void run() { 490 for (Handler hdl : l.getHandlers()) { 491 sysLogger.addHandler(hdl); 492 } 493 return null; 494 } 495 }); 496 } 497 return sysLogger; 498 } 499 500 // LoggerContext maintains the logger namespace per context. 501 // The default LogManager implementation has one system context and user 502 // context. The system context is used to maintain the namespace for 503 // all system loggers and is queried by the system code. If a system logger 504 // doesn't exist in the user context, it'll also be added to the user context. 505 // The user context is queried by the user code and all other loggers are 506 // added in the user context. 507 static class LoggerContext { 508 // Table of named Loggers that maps names to Loggers. 509 private final Hashtable<String,LoggerWeakRef> namedLoggers = new Hashtable<>(); 510 // Tree of named Loggers 511 private final LogNode root; 512 513 private LoggerContext() { 514 this.root = new LogNode(null, this); 515 } 516 517 Logger demandLogger(String name, String resourceBundleName) { 518 // a LogManager subclass may have its own implementation to add and 519 // get a Logger. So delegate to the LogManager to do the work. 520 return manager.demandLogger(name, resourceBundleName, null); 521 } 522 523 synchronized Logger findLogger(String name) { 524 LoggerWeakRef ref = namedLoggers.get(name); 525 if (ref == null) { 526 return null; 527 } 528 Logger logger = ref.get(); 529 if (logger == null) { 530 // Hashtable holds stale weak reference 531 // to a logger which has been GC-ed. 532 removeLogger(name); 533 } 534 return logger; 535 } 536 537 synchronized void ensureRootLogger(Logger logger) { 538 if (logger.getName().isEmpty()) 539 return; 540 541 // during initialization, rootLogger is null when 542 // instantiating itself RootLogger 543 if (findLogger("") == null && manager.rootLogger != null) { 544 addLocalLogger(manager.rootLogger); 545 } 546 } 547 548 // Add a logger to this context. This method will only set its level 549 // and process parent loggers. It doesn't set its handlers. 550 synchronized boolean addLocalLogger(Logger logger) { 551 ensureRootLogger(logger); 552 553 final String name = logger.getName(); 554 if (name == null) { 555 throw new NullPointerException(); 556 } 557 LoggerWeakRef ref = namedLoggers.get(name); 558 if (ref != null) { 559 if (ref.get() == null) { 560 // It's possible that the Logger was GC'ed after a 561 // drainLoggerRefQueueBounded() call above so allow 562 // a new one to be registered. 563 removeLogger(name); 564 } else { 565 // We already have a registered logger with the given name. 566 return false; 567 } 568 } 569 570 // We're adding a new logger. 571 // Note that we are creating a weak reference here. 572 ref = manager.new LoggerWeakRef(logger); 573 namedLoggers.put(name, ref); 574 575 // Apply any initial level defined for the new logger. 576 Level level = manager.getLevelProperty(name + ".level", null); 577 if (level != null) { 578 doSetLevel(logger, level); 579 } 580 581 // instantiation of the handler is done in the LogManager.addLogger 582 // implementation as a handler class may be only visible to LogManager 583 // subclass for the custom log manager case 584 processParentHandlers(logger, name); 585 586 // Find the new node and its parent. 587 LogNode node = getNode(name); 588 node.loggerRef = ref; 589 Logger parent = null; 590 LogNode nodep = node.parent; 591 while (nodep != null) { 592 LoggerWeakRef nodeRef = nodep.loggerRef; 593 if (nodeRef != null) { 594 parent = nodeRef.get(); 595 if (parent != null) { 596 break; 597 } 598 } 599 nodep = nodep.parent; 600 } 601 602 if (parent != null) { 603 doSetParent(logger, parent); 604 } 605 // Walk over the children and tell them we are their new parent. 606 node.walkAndSetParent(logger); 607 // new LogNode is ready so tell the LoggerWeakRef about it 608 ref.setNode(node); 609 return true; 610 } 611 612 // note: all calls to removeLogger are synchronized on LogManager's 613 // intrinsic lock 614 void removeLogger(String name) { 615 namedLoggers.remove(name); 616 } 617 618 synchronized Enumeration<String> getLoggerNames() { 619 return namedLoggers.keys(); 620 } 621 622 // If logger.getUseParentHandlers() returns 'true' and any of the logger's 623 // parents have levels or handlers defined, make sure they are instantiated. 624 private void processParentHandlers(final Logger logger, final String name) { 625 AccessController.doPrivileged(new PrivilegedAction<Void>() { 626 public Void run() { 627 if (logger != manager.rootLogger) { 628 boolean useParent = manager.getBooleanProperty(name + ".useParentHandlers", true); 629 if (!useParent) { 630 logger.setUseParentHandlers(false); 631 } 632 } 633 return null; 634 } 635 }); 636 637 int ix = 1; 638 for (;;) { 639 int ix2 = name.indexOf(".", ix); 640 if (ix2 < 0) { 641 break; 642 } 643 String pname = name.substring(0, ix2); 644 if (manager.getProperty(pname + ".level") != null || 645 manager.getProperty(pname + ".handlers") != null) { 646 // This pname has a level/handlers definition. 647 // Make sure it exists. 648 demandLogger(pname, null); 649 } 650 ix = ix2+1; 651 } 652 } 653 654 // Gets a node in our tree of logger nodes. 655 // If necessary, create it. 656 LogNode getNode(String name) { 657 if (name == null || name.equals("")) { 658 return root; 659 } 660 LogNode node = root; 661 while (name.length() > 0) { 662 int ix = name.indexOf("."); 663 String head; 664 if (ix > 0) { 665 head = name.substring(0, ix); 666 name = name.substring(ix + 1); 667 } else { 668 head = name; 669 name = ""; 670 } 671 if (node.children == null) { 672 node.children = new HashMap<>(); 673 } 674 LogNode child = node.children.get(head); 675 if (child == null) { 676 child = new LogNode(node, this); 677 node.children.put(head, child); 678 } 679 node = child; 680 } 681 return node; 682 } 683 } 684 685 static class SystemLoggerContext extends LoggerContext { 686 // Add a system logger in the system context's namespace as well as 687 // in the LogManager's namespace if not exist so that there is only 688 // one single logger of the given name. System loggers are visible 689 // to applications unless a logger of the same name has been added. 690 Logger demandLogger(String name, String resourceBundleName) { 691 Logger result = findLogger(name); 692 if (result == null) { 693 // only allocate the new system logger once 694 Logger newLogger = new Logger(name, resourceBundleName); 695 do { 696 if (addLocalLogger(newLogger)) { 697 // We successfully added the new Logger that we 698 // created above so return it without refetching. 699 result = newLogger; 700 } else { 701 // We didn't add the new Logger that we created above 702 // because another thread added a Logger with the same 703 // name after our null check above and before our call 704 // to addLogger(). We have to refetch the Logger because 705 // addLogger() returns a boolean instead of the Logger 706 // reference itself. However, if the thread that created 707 // the other Logger is not holding a strong reference to 708 // the other Logger, then it is possible for the other 709 // Logger to be GC'ed after we saw it in addLogger() and 710 // before we can refetch it. If it has been GC'ed then 711 // we'll just loop around and try again. 712 result = findLogger(name); 713 } 714 } while (result == null); 715 } 716 return result; 717 } 718 } 719 720 // Add new per logger handlers. 721 // We need to raise privilege here. All our decisions will 722 // be made based on the logging configuration, which can 723 // only be modified by trusted code. 724 private void loadLoggerHandlers(final Logger logger, final String name, 725 final String handlersPropertyName) 726 { 727 AccessController.doPrivileged(new PrivilegedAction<Object>() { 728 public Object run() { 729 String names[] = parseClassNames(handlersPropertyName); 730 for (int i = 0; i < names.length; i++) { 731 String word = names[i]; 732 try { 733 Class<?> clz = ClassLoader.getSystemClassLoader().loadClass(word); 734 Handler hdl = (Handler) clz.newInstance(); 735 // Check if there is a property defining the 736 // this handler's level. 737 String levs = getProperty(word + ".level"); 738 if (levs != null) { 739 Level l = Level.findLevel(levs); 740 if (l != null) { 741 hdl.setLevel(l); 742 } else { 743 // Probably a bad level. Drop through. 744 System.err.println("Can't set level for " + word); 745 } 746 } 747 // Add this Handler to the logger 748 logger.addHandler(hdl); 749 } catch (Exception ex) { 750 System.err.println("Can't load log handler \"" + word + "\""); 751 System.err.println("" + ex); 752 ex.printStackTrace(); 753 } 754 } 755 return null; 756 } 757 }); 758 } 759 760 761 // loggerRefQueue holds LoggerWeakRef objects for Logger objects 762 // that have been GC'ed. 763 private final ReferenceQueue<Logger> loggerRefQueue 764 = new ReferenceQueue<>(); 765 766 // Package-level inner class. 767 // Helper class for managing WeakReferences to Logger objects. 768 // 769 // LogManager.namedLoggers 770 // - has weak references to all named Loggers 771 // - namedLoggers keeps the LoggerWeakRef objects for the named 772 // Loggers around until we can deal with the book keeping for 773 // the named Logger that is being GC'ed. 774 // LogManager.LogNode.loggerRef 775 // - has a weak reference to a named Logger 776 // - the LogNode will also keep the LoggerWeakRef objects for 777 // the named Loggers around; currently LogNodes never go away. 778 // Logger.kids 779 // - has a weak reference to each direct child Logger; this 780 // includes anonymous and named Loggers 781 // - anonymous Loggers are always children of the rootLogger 782 // which is a strong reference; rootLogger.kids keeps the 783 // LoggerWeakRef objects for the anonymous Loggers around 784 // until we can deal with the book keeping. 785 // 786 final class LoggerWeakRef extends WeakReference<Logger> { 787 private String name; // for namedLoggers cleanup 788 private LogNode node; // for loggerRef cleanup 789 private WeakReference<Logger> parentRef; // for kids cleanup 790 791 LoggerWeakRef(Logger logger) { 792 super(logger, loggerRefQueue); 793 794 name = logger.getName(); // save for namedLoggers cleanup 795 } 796 797 // dispose of this LoggerWeakRef object 798 void dispose() { 799 if (node != null) { 800 // if we have a LogNode, then we were a named Logger 801 // so clear namedLoggers weak ref to us 802 node.context.removeLogger(name); 803 name = null; // clear our ref to the Logger's name 804 805 node.loggerRef = null; // clear LogNode's weak ref to us 806 node = null; // clear our ref to LogNode 807 } 808 809 if (parentRef != null) { 810 // this LoggerWeakRef has or had a parent Logger 811 Logger parent = parentRef.get(); 812 if (parent != null) { 813 // the parent Logger is still there so clear the 814 // parent Logger's weak ref to us 815 parent.removeChildLogger(this); 816 } 817 parentRef = null; // clear our weak ref to the parent Logger 818 } 819 } 820 821 // set the node field to the specified value 822 void setNode(LogNode node) { 823 this.node = node; 824 } 825 826 // set the parentRef field to the specified value 827 void setParentRef(WeakReference<Logger> parentRef) { 828 this.parentRef = parentRef; 829 } 830 } 831 832 // Package-level method. 833 // Drain some Logger objects that have been GC'ed. 834 // 835 // drainLoggerRefQueueBounded() is called by addLogger() below 836 // and by Logger.getAnonymousLogger(String) so we'll drain up to 837 // MAX_ITERATIONS GC'ed Loggers for every Logger we add. 838 // 839 // On a WinXP VMware client, a MAX_ITERATIONS value of 400 gives 840 // us about a 50/50 mix in increased weak ref counts versus 841 // decreased weak ref counts in the AnonLoggerWeakRefLeak test. 842 // Here are stats for cleaning up sets of 400 anonymous Loggers: 843 // - test duration 1 minute 844 // - sample size of 125 sets of 400 845 // - average: 1.99 ms 846 // - minimum: 0.57 ms 847 // - maximum: 25.3 ms 848 // 849 // The same config gives us a better decreased weak ref count 850 // than increased weak ref count in the LoggerWeakRefLeak test. 851 // Here are stats for cleaning up sets of 400 named Loggers: 852 // - test duration 2 minutes 853 // - sample size of 506 sets of 400 854 // - average: 0.57 ms 855 // - minimum: 0.02 ms 856 // - maximum: 10.9 ms 857 // 858 private final static int MAX_ITERATIONS = 400; 859 final synchronized void drainLoggerRefQueueBounded() { 860 for (int i = 0; i < MAX_ITERATIONS; i++) { 861 if (loggerRefQueue == null) { 862 // haven't finished loading LogManager yet 863 break; 864 } 865 866 LoggerWeakRef ref = (LoggerWeakRef) loggerRefQueue.poll(); 867 if (ref == null) { 868 break; 869 } 870 // a Logger object has been GC'ed so clean it up 871 ref.dispose(); 872 } 873 } 874 875 /** 876 * Add a named logger. This does nothing and returns false if a logger 877 * with the same name is already registered. 878 * <p> 879 * The Logger factory methods call this method to register each 880 * newly created Logger. 881 * <p> 882 * The application should retain its own reference to the Logger 883 * object to avoid it being garbage collected. The LogManager 884 * may only retain a weak reference. 885 * 886 * @param logger the new logger. 887 * @return true if the argument logger was registered successfully, 888 * false if a logger of that name already exists. 889 * @exception NullPointerException if the logger name is null. 890 */ 891 public boolean addLogger(Logger logger) { 892 final String name = logger.getName(); 893 if (name == null) { 894 throw new NullPointerException(); 895 } 896 drainLoggerRefQueueBounded(); 897 LoggerContext cx = getUserContext(); 898 if (cx.addLocalLogger(logger)) { 899 // Do we have a per logger handler too? 900 // Note: this will add a 200ms penalty 901 loadLoggerHandlers(logger, name, name + ".handlers"); 902 return true; 903 } else { 904 return false; 905 } 906 } 907 908 // Private method to set a level on a logger. 909 // If necessary, we raise privilege before doing the call. 910 private static void doSetLevel(final Logger logger, final Level level) { 911 SecurityManager sm = System.getSecurityManager(); 912 if (sm == null) { 913 // There is no security manager, so things are easy. 914 logger.setLevel(level); 915 return; 916 } 917 // There is a security manager. Raise privilege before 918 // calling setLevel. 919 AccessController.doPrivileged(new PrivilegedAction<Object>() { 920 public Object run() { 921 logger.setLevel(level); 922 return null; 923 }}); 924 } 925 926 // Private method to set a parent on a logger. 927 // If necessary, we raise privilege before doing the setParent call. 928 private static void doSetParent(final Logger logger, final Logger parent) { 929 SecurityManager sm = System.getSecurityManager(); 930 if (sm == null) { 931 // There is no security manager, so things are easy. 932 logger.setParent(parent); 933 return; 934 } 935 // There is a security manager. Raise privilege before 936 // calling setParent. 937 AccessController.doPrivileged(new PrivilegedAction<Object>() { 938 public Object run() { 939 logger.setParent(parent); 940 return null; 941 }}); 942 } 943 944 /** 945 * Method to find a named logger. 946 * <p> 947 * Note that since untrusted code may create loggers with 948 * arbitrary names this method should not be relied on to 949 * find Loggers for security sensitive logging. 950 * It is also important to note that the Logger associated with the 951 * String {@code name} may be garbage collected at any time if there 952 * is no strong reference to the Logger. The caller of this method 953 * must check the return value for null in order to properly handle 954 * the case where the Logger has been garbage collected. 955 * <p> 956 * @param name name of the logger 957 * @return matching logger or null if none is found 958 */ 959 public Logger getLogger(String name) { 960 return getUserContext().findLogger(name); 961 } 962 963 /** 964 * Get an enumeration of known logger names. 965 * <p> 966 * Note: Loggers may be added dynamically as new classes are loaded. 967 * This method only reports on the loggers that are currently registered. 968 * It is also important to note that this method only returns the name 969 * of a Logger, not a strong reference to the Logger itself. 970 * The returned String does nothing to prevent the Logger from being 971 * garbage collected. In particular, if the returned name is passed 972 * to {@code LogManager.getLogger()}, then the caller must check the 973 * return value from {@code LogManager.getLogger()} for null to properly 974 * handle the case where the Logger has been garbage collected in the 975 * time since its name was returned by this method. 976 * <p> 977 * @return enumeration of logger name strings 978 */ 979 public Enumeration<String> getLoggerNames() { 980 return getUserContext().getLoggerNames(); 981 } 982 983 /** 984 * Reinitialize the logging properties and reread the logging configuration. 985 * <p> 986 * The same rules are used for locating the configuration properties 987 * as are used at startup. So normally the logging properties will 988 * be re-read from the same file that was used at startup. 989 * <P> 990 * Any log level definitions in the new configuration file will be 991 * applied using Logger.setLevel(), if the target Logger exists. 992 * <p> 993 * A PropertyChangeEvent will be fired after the properties are read. 994 * 995 * @exception SecurityException if a security manager exists and if 996 * the caller does not have LoggingPermission("control"). 997 * @exception IOException if there are IO problems reading the configuration. 998 */ 999 public void readConfiguration() throws IOException, SecurityException { 1000 checkPermission(); 1001 1002 // if a configuration class is specified, load it and use it. 1003 String cname = System.getProperty("java.util.logging.config.class"); 1004 if (cname != null) { 1005 try { 1006 // Instantiate the named class. It is its constructor's 1007 // responsibility to initialize the logging configuration, by 1008 // calling readConfiguration(InputStream) with a suitable stream. 1009 try { 1010 Class<?> clz = ClassLoader.getSystemClassLoader().loadClass(cname); 1011 clz.newInstance(); 1012 return; 1013 } catch (ClassNotFoundException ex) { 1014 Class<?> clz = Thread.currentThread().getContextClassLoader().loadClass(cname); 1015 clz.newInstance(); 1016 return; 1017 } 1018 } catch (Exception ex) { 1019 System.err.println("Logging configuration class \"" + cname + "\" failed"); 1020 System.err.println("" + ex); 1021 // keep going and useful config file. 1022 } 1023 } 1024 1025 String fname = System.getProperty("java.util.logging.config.file"); 1026 if (fname == null) { 1027 fname = System.getProperty("java.home"); 1028 if (fname == null) { 1029 throw new Error("Can't find java.home ??"); 1030 } 1031 File f = new File(fname, "lib"); 1032 f = new File(f, "logging.properties"); 1033 fname = f.getCanonicalPath(); 1034 } 1035 InputStream in = new FileInputStream(fname); 1036 BufferedInputStream bin = new BufferedInputStream(in); 1037 try { 1038 readConfiguration(bin); 1039 } finally { 1040 if (in != null) { 1041 in.close(); 1042 } 1043 } 1044 } 1045 1046 /** 1047 * Reset the logging configuration. 1048 * <p> 1049 * For all named loggers, the reset operation removes and closes 1050 * all Handlers and (except for the root logger) sets the level 1051 * to null. The root logger's level is set to Level.INFO. 1052 * 1053 * @exception SecurityException if a security manager exists and if 1054 * the caller does not have LoggingPermission("control"). 1055 */ 1056 1057 public void reset() throws SecurityException { 1058 checkPermission(); 1059 synchronized (this) { 1060 props = new Properties(); 1061 // Since we are doing a reset we no longer want to initialize 1062 // the global handlers, if they haven't been initialized yet. 1063 initializedGlobalHandlers = true; 1064 } 1065 for (LoggerContext cx : contexts()) { 1066 Enumeration<String> enum_ = cx.getLoggerNames(); 1067 while (enum_.hasMoreElements()) { 1068 String name = enum_.nextElement(); 1069 Logger logger = cx.findLogger(name); 1070 if (logger != null) { 1071 resetLogger(logger); 1072 } 1073 } 1074 } 1075 } 1076 1077 // Private method to reset an individual target logger. 1078 private void resetLogger(Logger logger) { 1079 // Close all the Logger's handlers. 1080 Handler[] targets = logger.getHandlers(); 1081 for (int i = 0; i < targets.length; i++) { 1082 Handler h = targets[i]; 1083 logger.removeHandler(h); 1084 try { 1085 h.close(); 1086 } catch (Exception ex) { 1087 // Problems closing a handler? Keep going... 1088 } 1089 } 1090 String name = logger.getName(); 1091 if (name != null && name.equals("")) { 1092 // This is the root logger. 1093 logger.setLevel(defaultLevel); 1094 } else { 1095 logger.setLevel(null); 1096 } 1097 } 1098 1099 // get a list of whitespace separated classnames from a property. 1100 private String[] parseClassNames(String propertyName) { 1101 String hands = getProperty(propertyName); 1102 if (hands == null) { 1103 return new String[0]; 1104 } 1105 hands = hands.trim(); 1106 int ix = 0; 1107 Vector<String> result = new Vector<>(); 1108 while (ix < hands.length()) { 1109 int end = ix; 1110 while (end < hands.length()) { 1111 if (Character.isWhitespace(hands.charAt(end))) { 1112 break; 1113 } 1114 if (hands.charAt(end) == ',') { 1115 break; 1116 } 1117 end++; 1118 } 1119 String word = hands.substring(ix, end); 1120 ix = end+1; 1121 word = word.trim(); 1122 if (word.length() == 0) { 1123 continue; 1124 } 1125 result.add(word); 1126 } 1127 return result.toArray(new String[result.size()]); 1128 } 1129 1130 /** 1131 * Reinitialize the logging properties and reread the logging configuration 1132 * from the given stream, which should be in java.util.Properties format. 1133 * A PropertyChangeEvent will be fired after the properties are read. 1134 * <p> 1135 * Any log level definitions in the new configuration file will be 1136 * applied using Logger.setLevel(), if the target Logger exists. 1137 * 1138 * @param ins stream to read properties from 1139 * @exception SecurityException if a security manager exists and if 1140 * the caller does not have LoggingPermission("control"). 1141 * @exception IOException if there are problems reading from the stream. 1142 */ 1143 public void readConfiguration(InputStream ins) throws IOException, SecurityException { 1144 checkPermission(); 1145 reset(); 1146 1147 // Load the properties 1148 props.load(ins); 1149 // Instantiate new configuration objects. 1150 String names[] = parseClassNames("config"); 1151 1152 for (int i = 0; i < names.length; i++) { 1153 String word = names[i]; 1154 try { 1155 Class<?> clz = ClassLoader.getSystemClassLoader().loadClass(word); 1156 clz.newInstance(); 1157 } catch (Exception ex) { 1158 System.err.println("Can't load config class \"" + word + "\""); 1159 System.err.println("" + ex); 1160 // ex.printStackTrace(); 1161 } 1162 } 1163 1164 // Set levels on any pre-existing loggers, based on the new properties. 1165 setLevelsOnExistingLoggers(); 1166 1167 // Notify any interested parties that our properties have changed. 1168 // We first take a copy of the listener map so that we aren't holding any 1169 // locks when calling the listeners. 1170 Map<Object,Integer> listeners = null; 1171 synchronized (listenerMap) { 1172 if (!listenerMap.isEmpty()) 1173 listeners = new HashMap<>(listenerMap); 1174 } 1175 if (listeners != null) { 1176 assert Beans.isBeansPresent(); 1177 Object ev = Beans.newPropertyChangeEvent(LogManager.class, null, null, null); 1178 for (Map.Entry<Object,Integer> entry : listeners.entrySet()) { 1179 Object listener = entry.getKey(); 1180 int count = entry.getValue().intValue(); 1181 for (int i = 0; i < count; i++) { 1182 Beans.invokePropertyChange(listener, ev); 1183 } 1184 } 1185 } 1186 1187 1188 // Note that we need to reinitialize global handles when 1189 // they are first referenced. 1190 synchronized (this) { 1191 initializedGlobalHandlers = false; 1192 } 1193 } 1194 1195 /** 1196 * Get the value of a logging property. 1197 * The method returns null if the property is not found. 1198 * @param name property name 1199 * @return property value 1200 */ 1201 public String getProperty(String name) { 1202 return props.getProperty(name); 1203 } 1204 1205 // Package private method to get a String property. 1206 // If the property is not defined we return the given 1207 // default value. 1208 String getStringProperty(String name, String defaultValue) { 1209 String val = getProperty(name); 1210 if (val == null) { 1211 return defaultValue; 1212 } 1213 return val.trim(); 1214 } 1215 1216 // Package private method to get an integer property. 1217 // If the property is not defined or cannot be parsed 1218 // we return the given default value. 1219 int getIntProperty(String name, int defaultValue) { 1220 String val = getProperty(name); 1221 if (val == null) { 1222 return defaultValue; 1223 } 1224 try { 1225 return Integer.parseInt(val.trim()); 1226 } catch (Exception ex) { 1227 return defaultValue; 1228 } 1229 } 1230 1231 // Package private method to get a boolean property. 1232 // If the property is not defined or cannot be parsed 1233 // we return the given default value. 1234 boolean getBooleanProperty(String name, boolean defaultValue) { 1235 String val = getProperty(name); 1236 if (val == null) { 1237 return defaultValue; 1238 } 1239 val = val.toLowerCase(); 1240 if (val.equals("true") || val.equals("1")) { 1241 return true; 1242 } else if (val.equals("false") || val.equals("0")) { 1243 return false; 1244 } 1245 return defaultValue; 1246 } 1247 1248 // Package private method to get a Level property. 1249 // If the property is not defined or cannot be parsed 1250 // we return the given default value. 1251 Level getLevelProperty(String name, Level defaultValue) { 1252 String val = getProperty(name); 1253 if (val == null) { 1254 return defaultValue; 1255 } 1256 Level l = Level.findLevel(val.trim()); 1257 return l != null ? l : defaultValue; 1258 } 1259 1260 // Package private method to get a filter property. 1261 // We return an instance of the class named by the "name" 1262 // property. If the property is not defined or has problems 1263 // we return the defaultValue. 1264 Filter getFilterProperty(String name, Filter defaultValue) { 1265 String val = getProperty(name); 1266 try { 1267 if (val != null) { 1268 Class<?> clz = ClassLoader.getSystemClassLoader().loadClass(val); 1269 return (Filter) clz.newInstance(); 1270 } 1271 } catch (Exception ex) { 1272 // We got one of a variety of exceptions in creating the 1273 // class or creating an instance. 1274 // Drop through. 1275 } 1276 // We got an exception. Return the defaultValue. 1277 return defaultValue; 1278 } 1279 1280 1281 // Package private method to get a formatter property. 1282 // We return an instance of the class named by the "name" 1283 // property. If the property is not defined or has problems 1284 // we return the defaultValue. 1285 Formatter getFormatterProperty(String name, Formatter defaultValue) { 1286 String val = getProperty(name); 1287 try { 1288 if (val != null) { 1289 Class<?> clz = ClassLoader.getSystemClassLoader().loadClass(val); 1290 return (Formatter) clz.newInstance(); 1291 } 1292 } catch (Exception ex) { 1293 // We got one of a variety of exceptions in creating the 1294 // class or creating an instance. 1295 // Drop through. 1296 } 1297 // We got an exception. Return the defaultValue. 1298 return defaultValue; 1299 } 1300 1301 // Private method to load the global handlers. 1302 // We do the real work lazily, when the global handlers 1303 // are first used. 1304 private synchronized void initializeGlobalHandlers() { 1305 if (initializedGlobalHandlers) { 1306 return; 1307 } 1308 1309 initializedGlobalHandlers = true; 1310 1311 if (deathImminent) { 1312 // Aaargh... 1313 // The VM is shutting down and our exit hook has been called. 1314 // Avoid allocating global handlers. 1315 return; 1316 } 1317 loadLoggerHandlers(rootLogger, null, "handlers"); 1318 } 1319 1320 private final Permission controlPermission = new LoggingPermission("control", null); 1321 1322 void checkPermission() { 1323 SecurityManager sm = System.getSecurityManager(); 1324 if (sm != null) 1325 sm.checkPermission(controlPermission); 1326 } 1327 1328 /** 1329 * Check that the current context is trusted to modify the logging 1330 * configuration. This requires LoggingPermission("control"). 1331 * <p> 1332 * If the check fails we throw a SecurityException, otherwise 1333 * we return normally. 1334 * 1335 * @exception SecurityException if a security manager exists and if 1336 * the caller does not have LoggingPermission("control"). 1337 */ 1338 public void checkAccess() throws SecurityException { 1339 checkPermission(); 1340 } 1341 1342 // Nested class to represent a node in our tree of named loggers. 1343 private static class LogNode { 1344 HashMap<String,LogNode> children; 1345 LoggerWeakRef loggerRef; 1346 LogNode parent; 1347 final LoggerContext context; 1348 1349 LogNode(LogNode parent, LoggerContext context) { 1350 this.parent = parent; 1351 this.context = context; 1352 } 1353 1354 // Recursive method to walk the tree below a node and set 1355 // a new parent logger. 1356 void walkAndSetParent(Logger parent) { 1357 if (children == null) { 1358 return; 1359 } 1360 Iterator<LogNode> values = children.values().iterator(); 1361 while (values.hasNext()) { 1362 LogNode node = values.next(); 1363 LoggerWeakRef ref = node.loggerRef; 1364 Logger logger = (ref == null) ? null : ref.get(); 1365 if (logger == null) { 1366 node.walkAndSetParent(parent); 1367 } else { 1368 doSetParent(logger, parent); 1369 } 1370 } 1371 } 1372 } 1373 1374 // We use a subclass of Logger for the root logger, so 1375 // that we only instantiate the global handlers when they 1376 // are first needed. 1377 private class RootLogger extends Logger { 1378 private RootLogger() { 1379 super("", null); 1380 setLevel(defaultLevel); 1381 } 1382 1383 public void log(LogRecord record) { 1384 // Make sure that the global handlers have been instantiated. 1385 initializeGlobalHandlers(); 1386 super.log(record); 1387 } 1388 1389 public void addHandler(Handler h) { 1390 initializeGlobalHandlers(); 1391 super.addHandler(h); 1392 } 1393 1394 public void removeHandler(Handler h) { 1395 initializeGlobalHandlers(); 1396 super.removeHandler(h); 1397 } 1398 1399 public Handler[] getHandlers() { 1400 initializeGlobalHandlers(); 1401 return super.getHandlers(); 1402 } 1403 } 1404 1405 1406 // Private method to be called when the configuration has 1407 // changed to apply any level settings to any pre-existing loggers. 1408 synchronized private void setLevelsOnExistingLoggers() { 1409 Enumeration<?> enum_ = props.propertyNames(); 1410 while (enum_.hasMoreElements()) { 1411 String key = (String)enum_.nextElement(); 1412 if (!key.endsWith(".level")) { 1413 // Not a level definition. 1414 continue; 1415 } 1416 int ix = key.length() - 6; 1417 String name = key.substring(0, ix); 1418 Level level = getLevelProperty(key, null); 1419 if (level == null) { 1420 System.err.println("Bad level value for property: " + key); 1421 continue; 1422 } 1423 for (LoggerContext cx : contexts()) { 1424 Logger l = cx.findLogger(name); 1425 if (l == null) { 1426 continue; 1427 } 1428 l.setLevel(level); 1429 } 1430 } 1431 } 1432 1433 // Management Support 1434 private static LoggingMXBean loggingMXBean = null; 1435 /** 1436 * String representation of the 1437 * {@link javax.management.ObjectName} for the management interface 1438 * for the logging facility. 1439 * 1440 * @see java.lang.management.PlatformLoggingMXBean 1441 * @see java.util.logging.LoggingMXBean 1442 * 1443 * @since 1.5 1444 */ 1445 public final static String LOGGING_MXBEAN_NAME 1446 = "java.util.logging:type=Logging"; 1447 1448 /** 1449 * Returns <tt>LoggingMXBean</tt> for managing loggers. 1450 * An alternative way to manage loggers is through the 1451 * {@link java.lang.management.PlatformLoggingMXBean} interface 1452 * that can be obtained by calling: 1453 * <pre> 1454 * PlatformLoggingMXBean logging = {@link java.lang.management.ManagementFactory#getPlatformMXBean(Class) 1455 * ManagementFactory.getPlatformMXBean}(PlatformLoggingMXBean.class); 1456 * </pre> 1457 * 1458 * @return a {@link LoggingMXBean} object. 1459 * 1460 * @see java.lang.management.PlatformLoggingMXBean 1461 * @since 1.5 1462 */ 1463 public static synchronized LoggingMXBean getLoggingMXBean() { 1464 if (loggingMXBean == null) { 1465 loggingMXBean = new Logging(); 1466 } 1467 return loggingMXBean; 1468 } 1469 1470 /** 1471 * A class that provides access to the java.beans.PropertyChangeListener 1472 * and java.beans.PropertyChangeEvent without creating a static dependency 1473 * on java.beans. This class can be removed once the addPropertyChangeListener 1474 * and removePropertyChangeListener methods are removed. 1475 */ 1476 private static class Beans { 1477 private static final Class<?> propertyChangeListenerClass = 1478 getClass("java.beans.PropertyChangeListener"); 1479 1480 private static final Class<?> propertyChangeEventClass = 1481 getClass("java.beans.PropertyChangeEvent"); 1482 1483 private static final Method propertyChangeMethod = 1484 getMethod(propertyChangeListenerClass, 1485 "propertyChange", 1486 propertyChangeEventClass); 1487 1488 private static final Constructor<?> propertyEventCtor = 1489 getConstructor(propertyChangeEventClass, 1490 Object.class, 1491 String.class, 1492 Object.class, 1493 Object.class); 1494 1495 private static Class<?> getClass(String name) { 1496 try { 1497 return Class.forName(name, true, Beans.class.getClassLoader()); 1498 } catch (ClassNotFoundException e) { 1499 return null; 1500 } 1501 } 1502 private static Constructor<?> getConstructor(Class<?> c, Class<?>... types) { 1503 try { 1504 return (c == null) ? null : c.getDeclaredConstructor(types); 1505 } catch (NoSuchMethodException x) { 1506 throw new AssertionError(x); 1507 } 1508 } 1509 1510 private static Method getMethod(Class<?> c, String name, Class<?>... types) { 1511 try { 1512 return (c == null) ? null : c.getMethod(name, types); 1513 } catch (NoSuchMethodException e) { 1514 throw new AssertionError(e); 1515 } 1516 } 1517 1518 /** 1519 * Returns {@code true} if java.beans is present. 1520 */ 1521 static boolean isBeansPresent() { 1522 return propertyChangeListenerClass != null && 1523 propertyChangeEventClass != null; 1524 } 1525 1526 /** 1527 * Returns a new PropertyChangeEvent with the given source, property 1528 * name, old and new values. 1529 */ 1530 static Object newPropertyChangeEvent(Object source, String prop, 1531 Object oldValue, Object newValue) 1532 { 1533 try { 1534 return propertyEventCtor.newInstance(source, prop, oldValue, newValue); 1535 } catch (InstantiationException | IllegalAccessException x) { 1536 throw new AssertionError(x); 1537 } catch (InvocationTargetException x) { 1538 Throwable cause = x.getCause(); 1539 if (cause instanceof Error) 1540 throw (Error)cause; 1541 if (cause instanceof RuntimeException) 1542 throw (RuntimeException)cause; 1543 throw new AssertionError(x); 1544 } 1545 } 1546 1547 /** 1548 * Invokes the given PropertyChangeListener's propertyChange method 1549 * with the given event. 1550 */ 1551 static void invokePropertyChange(Object listener, Object ev) { 1552 try { 1553 propertyChangeMethod.invoke(listener, ev); 1554 } catch (IllegalAccessException x) { 1555 throw new AssertionError(x); 1556 } catch (InvocationTargetException x) { 1557 Throwable cause = x.getCause(); 1558 if (cause instanceof Error) 1559 throw (Error)cause; 1560 if (cause instanceof RuntimeException) 1561 throw (RuntimeException)cause; 1562 throw new AssertionError(x); 1563 } 1564 } 1565 } 1566 }