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