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