1 /* 2 * Copyright (C) 2014 The Android Open Source Project 3 * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. 4 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 5 * 6 * This code is free software; you can redistribute it and/or modify it 7 * under the terms of the GNU General Public License version 2 only, as 8 * published by the Free Software Foundation. Oracle designates this 9 * particular file as subject to the "Classpath" exception as provided 10 * by Oracle in the LICENSE file that accompanied this code. 11 * 12 * This code is distributed in the hope that it will be useful, but WITHOUT 13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 14 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 15 * version 2 for more details (a copy is included in the LICENSE file that 16 * accompanied this code). 17 * 18 * You should have received a copy of the GNU General Public License version 19 * 2 along with this work; if not, write to the Free Software Foundation, 20 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 21 * 22 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 23 * or visit www.oracle.com if you need additional information or have any 24 * questions. 25 */ 26 27 28 package java.util.logging; 29 30 import java.io.*; 31 import java.util.*; 32 import java.security.*; 33 import java.lang.ref.ReferenceQueue; 34 import java.lang.ref.WeakReference; 35 import java.lang.reflect.Constructor; 36 import java.lang.reflect.InvocationTargetException; 37 import java.lang.reflect.Method; 38 import java.beans.PropertyChangeListener; 39 40 /** 41 * There is a single global LogManager object that is used to 42 * maintain a set of shared state about Loggers and log services. 43 * <p> 44 * This LogManager object: 45 * <ul> 46 * <li> Manages a hierarchical namespace of Logger objects. All 47 * named Loggers are stored in this namespace. 48 * <li> Manages a set of logging control properties. These are 49 * simple key-value pairs that can be used by Handlers and 50 * other logging objects to configure themselves. 51 * </ul> 52 * <p> 53 * The global LogManager object can be retrieved using LogManager.getLogManager(). 54 * The LogManager object is created during class initialization and 55 * cannot subsequently be changed. 56 * <p> 57 * At startup the LogManager class is located using the 58 * java.util.logging.manager system property. 59 * <p> 60 * The LogManager defines two optional system properties that allow control over 61 * the initial configuration: 62 * <ul> 63 * <li>"java.util.logging.config.class" 64 * <li>"java.util.logging.config.file" 65 * </ul> 66 * These two properties may be specified on the command line to the "java" 67 * command, or as system property definitions passed to JNI_CreateJavaVM. 68 * <p> 69 * If the "java.util.logging.config.class" property is set, then the 70 * property value is treated as a class name. The given class will be 71 * loaded, an object will be instantiated, and that object's constructor 72 * is responsible for reading in the initial configuration. (That object 73 * may use other system properties to control its configuration.) The 74 * alternate configuration class can use <tt>readConfiguration(InputStream)</tt> 75 * to define properties in the LogManager. 76 * <p> 77 * If "java.util.logging.config.class" property is <b>not</b> set, 78 * then the "java.util.logging.config.file" system property can be used 79 * to specify a properties file (in java.util.Properties format). The 80 * initial logging configuration will be read from this file. 81 * <p> 82 * If neither of these properties is defined then the LogManager uses its 83 * default configuration. The default configuration is typically loaded from the 84 * properties file "{@code lib/logging.properties}" in the Java installation 85 * directory. 86 * <p> 87 * The properties for loggers and Handlers will have names starting 88 * with the dot-separated name for the handler or logger. 89 * <p> 90 * The global logging properties may include: 91 * <ul> 92 * <li>A property "handlers". This defines a whitespace or comma separated 93 * list of class names for handler classes to load and register as 94 * handlers on the root Logger (the Logger named ""). Each class 95 * name must be for a Handler class which has a default constructor. 96 * Note that these Handlers may be created lazily, when they are 97 * first used. 98 * 99 * <li>A property "<logger>.handlers". This defines a whitespace or 100 * comma separated list of class names for handlers classes to 101 * load and register as handlers to the specified logger. Each class 102 * name must be for a Handler class which has a default constructor. 103 * Note that these Handlers may be created lazily, when they are 104 * first used. 105 * 106 * <li>A property "<logger>.useParentHandlers". This defines a boolean 107 * value. By default every logger calls its parent in addition to 108 * handling the logging message itself, this often result in messages 109 * being handled by the root logger as well. When setting this property 110 * to false a Handler needs to be configured for this logger otherwise 111 * no logging messages are delivered. 112 * 113 * <li>A property "config". This property is intended to allow 114 * arbitrary configuration code to be run. The property defines a 115 * whitespace or comma separated list of class names. A new instance will be 116 * created for each named class. The default constructor of each class 117 * may execute arbitrary code to update the logging configuration, such as 118 * setting logger levels, adding handlers, adding filters, etc. 119 * </ul> 120 * <p> 121 * Note that all classes loaded during LogManager configuration are 122 * first searched on the system class path before any user class path. 123 * That includes the LogManager class, any config classes, and any 124 * handler classes. 125 * <p> 126 * Loggers are organized into a naming hierarchy based on their 127 * dot separated names. Thus "a.b.c" is a child of "a.b", but 128 * "a.b1" and a.b2" are peers. 129 * <p> 130 * All properties whose names end with ".level" are assumed to define 131 * log levels for Loggers. Thus "foo.level" defines a log level for 132 * the logger called "foo" and (recursively) for any of its children 133 * in the naming hierarchy. Log Levels are applied in the order they 134 * are defined in the properties file. Thus level settings for child 135 * nodes in the tree should come after settings for their parents. 136 * The property name ".level" can be used to set the level for the 137 * root of the tree. 138 * <p> 139 * All methods on the LogManager object are multi-thread safe. 140 * 141 * @since 1.4 142 */ 143 144 public class LogManager { 145 // The global LogManager object 146 private static final LogManager manager; 147 148 // 'props' is assigned within a lock but accessed without it. 149 // Declaring it volatile makes sure that another thread will not 150 // be able to see a partially constructed 'props' object. 151 // (seeing a partially constructed 'props' object can result in 152 // NPE being thrown in Hashtable.get(), because it leaves the door 153 // open for props.getProperties() to be called before the construcor 154 // of Hashtable is actually completed). 155 private volatile Properties props = new Properties(); 156 private final static Level defaultLevel = Level.INFO; 157 158 // The map of the registered listeners. The map value is the registration 159 // count to allow for cases where the same listener is registered many times. 160 private final Map<Object,Integer> listenerMap = new HashMap<>(); 161 162 // LoggerContext for system loggers and user loggers 163 private final LoggerContext systemContext = new SystemLoggerContext(); 164 private final LoggerContext userContext = new LoggerContext(); 165 // non final field - make it volatile to make sure that other threads 166 // will see the new value once ensureLogManagerInitialized() has finished 167 // executing. 168 private volatile Logger rootLogger; 169 // Have we done the primordial reading of the configuration file? 170 // (Must be done after a suitable amount of java.lang.System 171 // initialization has been done) 172 private volatile boolean readPrimordialConfiguration; 173 // Have we initialized global (root) handlers yet? 174 // This gets set to false in readConfiguration 175 private boolean initializedGlobalHandlers = true; 176 // True if JVM death is imminent and the exit hook has been called. 177 private boolean deathImminent; 178 179 static { 180 manager = AccessController.doPrivileged(new PrivilegedAction<LogManager>() { 181 @Override 182 public LogManager run() { 183 LogManager mgr = null; 184 String cname = null; 185 try { 186 cname = System.getProperty("java.util.logging.manager"); 187 if (cname != null) { 188 // Android-changed: Extract logic into the getClassInstance() helper. 189 mgr = (LogManager) getClassInstance(cname).newInstance(); 190 } 191 } catch (Exception ex) { 192 System.err.println("Could not load Logmanager \"" + cname + "\""); 193 ex.printStackTrace(); 194 } 195 if (mgr == null) { 196 mgr = new LogManager(); 197 } 198 return mgr; 199 200 } 201 }); 202 } 203 204 205 // This private class is used as a shutdown hook. 206 // It does a "reset" to close all open handlers. 207 private class Cleaner extends Thread { 208 Cleaner()209 private Cleaner() { 210 /* Set context class loader to null in order to avoid 211 * keeping a strong reference to an application classloader. 212 */ 213 this.setContextClassLoader(null); 214 } 215 216 @Override run()217 public void run() { 218 // This is to ensure the LogManager.<clinit> is completed 219 // before synchronized block. Otherwise deadlocks are possible. 220 LogManager mgr = manager; 221 222 // If the global handlers haven't been initialized yet, we 223 // don't want to initialize them just so we can close them! 224 synchronized (LogManager.this) { 225 // Note that death is imminent. 226 deathImminent = true; 227 initializedGlobalHandlers = true; 228 } 229 230 // Do a reset to close all active handlers. 231 reset(); 232 } 233 } 234 235 236 /** 237 * Protected constructor. This is protected so that container applications 238 * (such as J2EE containers) can subclass the object. It is non-public as 239 * it is intended that there only be one LogManager object, whose value is 240 * retrieved by calling LogManager.getLogManager. 241 */ LogManager()242 protected LogManager() { 243 this(checkSubclassPermissions()); 244 } 245 LogManager(Void checked)246 private LogManager(Void checked) { 247 248 // Add a shutdown hook to close the global handlers. 249 try { 250 Runtime.getRuntime().addShutdownHook(new Cleaner()); 251 } catch (IllegalStateException e) { 252 // If the VM is already shutting down, 253 // We do not need to register shutdownHook. 254 } 255 } 256 checkSubclassPermissions()257 private static Void checkSubclassPermissions() { 258 final SecurityManager sm = System.getSecurityManager(); 259 if (sm != null) { 260 // These permission will be checked in the LogManager constructor, 261 // in order to register the Cleaner() thread as a shutdown hook. 262 // Check them here to avoid the penalty of constructing the object 263 // etc... 264 sm.checkPermission(new RuntimePermission("shutdownHooks")); 265 sm.checkPermission(new RuntimePermission("setContextClassLoader")); 266 } 267 return null; 268 } 269 270 /** 271 * Lazy initialization: if this instance of manager is the global 272 * manager then this method will read the initial configuration and 273 * add the root logger and global logger by calling addLogger(). 274 * 275 * Note that it is subtly different from what we do in LoggerContext. 276 * In LoggerContext we're patching up the logger context tree in order to add 277 * the root and global logger *to the context tree*. 278 * 279 * For this to work, addLogger() must have already have been called 280 * once on the LogManager instance for the default logger being 281 * added. 282 * 283 * This is why ensureLogManagerInitialized() needs to be called before 284 * any logger is added to any logger context. 285 * 286 */ 287 private boolean initializedCalled = false; 288 private volatile boolean initializationDone = false; ensureLogManagerInitialized()289 final void ensureLogManagerInitialized() { 290 final LogManager owner = this; 291 if (initializationDone || owner != manager) { 292 // we don't want to do this twice, and we don't want to do 293 // this on private manager instances. 294 return; 295 } 296 297 // Maybe another thread has called ensureLogManagerInitialized() 298 // before us and is still executing it. If so we will block until 299 // the log manager has finished initialized, then acquire the monitor, 300 // notice that initializationDone is now true and return. 301 // Otherwise - we have come here first! We will acquire the monitor, 302 // see that initializationDone is still false, and perform the 303 // initialization. 304 // 305 synchronized(this) { 306 // If initializedCalled is true it means that we're already in 307 // the process of initializing the LogManager in this thread. 308 // There has been a recursive call to ensureLogManagerInitialized(). 309 final boolean isRecursiveInitialization = (initializedCalled == true); 310 311 assert initializedCalled || !initializationDone 312 : "Initialization can't be done if initialized has not been called!"; 313 314 if (isRecursiveInitialization || initializationDone) { 315 // If isRecursiveInitialization is true it means that we're 316 // already in the process of initializing the LogManager in 317 // this thread. There has been a recursive call to 318 // ensureLogManagerInitialized(). We should not proceed as 319 // it would lead to infinite recursion. 320 // 321 // If initializationDone is true then it means the manager 322 // has finished initializing; just return: we're done. 323 return; 324 } 325 // Calling addLogger below will in turn call requiresDefaultLogger() 326 // which will call ensureLogManagerInitialized(). 327 // We use initializedCalled to break the recursion. 328 initializedCalled = true; 329 try { 330 AccessController.doPrivileged(new PrivilegedAction<Object>() { 331 @Override 332 public Object run() { 333 assert rootLogger == null; 334 assert initializedCalled && !initializationDone; 335 336 // Read configuration. 337 owner.readPrimordialConfiguration(); 338 339 // Create and retain Logger for the root of the namespace. 340 owner.rootLogger = owner.new RootLogger(); 341 owner.addLogger(owner.rootLogger); 342 if (!owner.rootLogger.isLevelInitialized()) { 343 owner.rootLogger.setLevel(defaultLevel); 344 } 345 346 // Adding the global Logger. 347 // Do not call Logger.getGlobal() here as this might trigger 348 // subtle inter-dependency issues. 349 @SuppressWarnings("deprecation") 350 final Logger global = Logger.global; 351 352 // Make sure the global logger will be registered in the 353 // global manager 354 owner.addLogger(global); 355 return null; 356 } 357 }); 358 } finally { 359 initializationDone = true; 360 } 361 } 362 } 363 364 /** 365 * Returns the global LogManager object. 366 * @return the global LogManager object 367 */ getLogManager()368 public static LogManager getLogManager() { 369 if (manager != null) { 370 manager.ensureLogManagerInitialized(); 371 } 372 return manager; 373 } 374 readPrimordialConfiguration()375 private void readPrimordialConfiguration() { 376 if (!readPrimordialConfiguration) { 377 synchronized (this) { 378 if (!readPrimordialConfiguration) { 379 // If System.in/out/err are null, it's a good 380 // indication that we're still in the 381 // bootstrapping phase 382 if (System.out == null) { 383 return; 384 } 385 readPrimordialConfiguration = true; 386 387 try { 388 AccessController.doPrivileged(new PrivilegedExceptionAction<Void>() { 389 @Override 390 public Void run() throws Exception { 391 readConfiguration(); 392 393 // Platform loggers begin to delegate to java.util.logging.Logger 394 sun.util.logging.PlatformLogger.redirectPlatformLoggers(); 395 return null; 396 } 397 }); 398 } catch (Exception ex) { 399 assert false : "Exception raised while reading logging configuration: " + ex; 400 } 401 } 402 } 403 } 404 } 405 406 /** 407 * Adds an event listener to be invoked when the logging 408 * properties are re-read. Adding multiple instances of 409 * the same event Listener results in multiple entries 410 * in the property event listener table. 411 * 412 * <p><b>WARNING:</b> This method is omitted from this class in all subset 413 * Profiles of Java SE that do not include the {@code java.beans} package. 414 * </p> 415 * 416 * @param l event listener 417 * @exception SecurityException if a security manager exists and if 418 * the caller does not have LoggingPermission("control"). 419 * @exception NullPointerException if the PropertyChangeListener is null. 420 * @deprecated The dependency on {@code PropertyChangeListener} creates a 421 * significant impediment to future modularization of the Java 422 * platform. This method will be removed in a future release. 423 * The global {@code LogManager} can detect changes to the 424 * logging configuration by overridding the {@link 425 * #readConfiguration readConfiguration} method. 426 */ 427 @Deprecated addPropertyChangeListener(PropertyChangeListener l)428 public void addPropertyChangeListener(PropertyChangeListener l) throws SecurityException { 429 PropertyChangeListener listener = Objects.requireNonNull(l); 430 checkPermission(); 431 synchronized (listenerMap) { 432 // increment the registration count if already registered 433 Integer value = listenerMap.get(listener); 434 value = (value == null) ? 1 : (value + 1); 435 listenerMap.put(listener, value); 436 } 437 } 438 439 /** 440 * Removes an event listener for property change events. 441 * If the same listener instance has been added to the listener table 442 * through multiple invocations of <CODE>addPropertyChangeListener</CODE>, 443 * then an equivalent number of 444 * <CODE>removePropertyChangeListener</CODE> invocations are required to remove 445 * all instances of that listener from the listener table. 446 * <P> 447 * Returns silently if the given listener is not found. 448 * 449 * <p><b>WARNING:</b> This method is omitted from this class in all subset 450 * Profiles of Java SE that do not include the {@code java.beans} package. 451 * </p> 452 * 453 * @param l event listener (can be null) 454 * @exception SecurityException if a security manager exists and if 455 * the caller does not have LoggingPermission("control"). 456 * @deprecated The dependency on {@code PropertyChangeListener} creates a 457 * significant impediment to future modularization of the Java 458 * platform. This method will be removed in a future release. 459 * The global {@code LogManager} can detect changes to the 460 * logging configuration by overridding the {@link 461 * #readConfiguration readConfiguration} method. 462 */ 463 @Deprecated removePropertyChangeListener(PropertyChangeListener l)464 public void removePropertyChangeListener(PropertyChangeListener l) throws SecurityException { 465 checkPermission(); 466 if (l != null) { 467 PropertyChangeListener listener = l; 468 synchronized (listenerMap) { 469 Integer value = listenerMap.get(listener); 470 if (value != null) { 471 // remove from map if registration count is 1, otherwise 472 // just decrement its count 473 int i = value.intValue(); 474 if (i == 1) { 475 listenerMap.remove(listener); 476 } else { 477 assert i > 1; 478 listenerMap.put(listener, i - 1); 479 } 480 } 481 } 482 } 483 } 484 485 // LoggerContext maps from AppContext 486 private WeakHashMap<Object, LoggerContext> contextsMap = null; 487 488 // Returns the LoggerContext for the user code (i.e. application or AppContext). 489 // Loggers are isolated from each AppContext. getUserContext()490 private LoggerContext getUserContext() { 491 // Android-changed: Remove AWT specific hooks. 492 return userContext; 493 } 494 495 // The system context. getSystemContext()496 final LoggerContext getSystemContext() { 497 return systemContext; 498 } 499 contexts()500 private List<LoggerContext> contexts() { 501 List<LoggerContext> cxs = new ArrayList<>(); 502 cxs.add(getSystemContext()); 503 cxs.add(getUserContext()); 504 return cxs; 505 } 506 507 // Find or create a specified logger instance. If a logger has 508 // already been created with the given name it is returned. 509 // Otherwise a new logger instance is created and registered 510 // in the LogManager global namespace. 511 // This method will always return a non-null Logger object. 512 // Synchronization is not required here. All synchronization for 513 // adding a new Logger object is handled by addLogger(). 514 // 515 // This method must delegate to the LogManager implementation to 516 // add a new Logger or return the one that has been added previously 517 // as a LogManager subclass may override the addLogger, getLogger, 518 // readConfiguration, and other methods. demandLogger(String name, String resourceBundleName, Class<?> caller)519 Logger demandLogger(String name, String resourceBundleName, Class<?> caller) { 520 Logger result = getLogger(name); 521 if (result == null) { 522 // only allocate the new logger once 523 Logger newLogger = new Logger(name, resourceBundleName, caller, this, false); 524 do { 525 if (addLogger(newLogger)) { 526 // We successfully added the new Logger that we 527 // created above so return it without refetching. 528 return newLogger; 529 } 530 531 // We didn't add the new Logger that we created above 532 // because another thread added a Logger with the same 533 // name after our null check above and before our call 534 // to addLogger(). We have to refetch the Logger because 535 // addLogger() returns a boolean instead of the Logger 536 // reference itself. However, if the thread that created 537 // the other Logger is not holding a strong reference to 538 // the other Logger, then it is possible for the other 539 // Logger to be GC'ed after we saw it in addLogger() and 540 // before we can refetch it. If it has been GC'ed then 541 // we'll just loop around and try again. 542 result = getLogger(name); 543 } while (result == null); 544 } 545 return result; 546 } 547 demandSystemLogger(String name, String resourceBundleName)548 Logger demandSystemLogger(String name, String resourceBundleName) { 549 // Add a system logger in the system context's namespace 550 final Logger sysLogger = getSystemContext().demandLogger(name, resourceBundleName); 551 552 // Add the system logger to the LogManager's namespace if not exist 553 // so that there is only one single logger of the given name. 554 // System loggers are visible to applications unless a logger of 555 // the same name has been added. 556 Logger logger; 557 do { 558 // First attempt to call addLogger instead of getLogger 559 // This would avoid potential bug in custom LogManager.getLogger 560 // implementation that adds a logger if does not exist 561 if (addLogger(sysLogger)) { 562 // successfully added the new system logger 563 logger = sysLogger; 564 } else { 565 logger = getLogger(name); 566 } 567 } while (logger == null); 568 569 // LogManager will set the sysLogger's handlers via LogManager.addLogger method. 570 if (logger != sysLogger && sysLogger.accessCheckedHandlers().length == 0) { 571 // if logger already exists but handlers not set 572 final Logger l = logger; 573 AccessController.doPrivileged(new PrivilegedAction<Void>() { 574 @Override 575 public Void run() { 576 for (Handler hdl : l.accessCheckedHandlers()) { 577 sysLogger.addHandler(hdl); 578 } 579 return null; 580 } 581 }); 582 } 583 return sysLogger; 584 } 585 586 // Android-added: getClassInstance helper method, used in several places in this class. getClassInstance(String cname)587 private static Class getClassInstance(String cname) throws ClassNotFoundException { 588 try { 589 return ClassLoader.getSystemClassLoader().loadClass(cname); 590 } catch (ClassNotFoundException ex) { 591 return Thread.currentThread().getContextClassLoader().loadClass(cname); 592 } 593 } 594 595 // LoggerContext maintains the logger namespace per context. 596 // The default LogManager implementation has one system context and user 597 // context. The system context is used to maintain the namespace for 598 // all system loggers and is queried by the system code. If a system logger 599 // doesn't exist in the user context, it'll also be added to the user context. 600 // The user context is queried by the user code and all other loggers are 601 // added in the user context. 602 class LoggerContext { 603 // Table of named Loggers that maps names to Loggers. 604 private final Hashtable<String,LoggerWeakRef> namedLoggers = new Hashtable<>(); 605 // Tree of named Loggers 606 private final LogNode root; LoggerContext()607 private LoggerContext() { 608 this.root = new LogNode(null, this); 609 } 610 611 612 // Tells whether default loggers are required in this context. 613 // If true, the default loggers will be lazily added. requiresDefaultLoggers()614 final boolean requiresDefaultLoggers() { 615 final boolean requiresDefaultLoggers = (getOwner() == manager); 616 if (requiresDefaultLoggers) { 617 getOwner().ensureLogManagerInitialized(); 618 } 619 return requiresDefaultLoggers; 620 } 621 622 // This context's LogManager. getOwner()623 final LogManager getOwner() { 624 return LogManager.this; 625 } 626 627 // This context owner's root logger, which if not null, and if 628 // the context requires default loggers, will be added to the context 629 // logger's tree. getRootLogger()630 final Logger getRootLogger() { 631 return getOwner().rootLogger; 632 } 633 634 // The global logger, which if not null, and if 635 // the context requires default loggers, will be added to the context 636 // logger's tree. getGlobalLogger()637 final Logger getGlobalLogger() { 638 // Android-changed: Fix SuppressWarnings from "deprecated" to "deprecation". 639 @SuppressWarnings("deprecation") // avoids initialization cycles. 640 final Logger global = Logger.global; 641 return global; 642 } 643 demandLogger(String name, String resourceBundleName)644 Logger demandLogger(String name, String resourceBundleName) { 645 // a LogManager subclass may have its own implementation to add and 646 // get a Logger. So delegate to the LogManager to do the work. 647 final LogManager owner = getOwner(); 648 return owner.demandLogger(name, resourceBundleName, null); 649 } 650 651 652 // Due to subtle deadlock issues getUserContext() no longer 653 // calls addLocalLogger(rootLogger); 654 // Therefore - we need to add the default loggers later on. 655 // Checks that the context is properly initialized 656 // This is necessary before calling e.g. find(name) 657 // or getLoggerNames() 658 // ensureInitialized()659 private void ensureInitialized() { 660 if (requiresDefaultLoggers()) { 661 // Ensure that the root and global loggers are set. 662 ensureDefaultLogger(getRootLogger()); 663 ensureDefaultLogger(getGlobalLogger()); 664 } 665 } 666 667 findLogger(String name)668 synchronized Logger findLogger(String name) { 669 // ensure that this context is properly initialized before 670 // looking for loggers. 671 ensureInitialized(); 672 LoggerWeakRef ref = namedLoggers.get(name); 673 if (ref == null) { 674 return null; 675 } 676 Logger logger = ref.get(); 677 if (logger == null) { 678 // Hashtable holds stale weak reference 679 // to a logger which has been GC-ed. 680 ref.dispose(); 681 } 682 return logger; 683 } 684 685 // This method is called before adding a logger to the 686 // context. 687 // 'logger' is the context that will be added. 688 // This method will ensure that the defaults loggers are added 689 // before adding 'logger'. 690 // ensureAllDefaultLoggers(Logger logger)691 private void ensureAllDefaultLoggers(Logger logger) { 692 if (requiresDefaultLoggers()) { 693 final String name = logger.getName(); 694 if (!name.isEmpty()) { 695 ensureDefaultLogger(getRootLogger()); 696 if (!Logger.GLOBAL_LOGGER_NAME.equals(name)) { 697 ensureDefaultLogger(getGlobalLogger()); 698 } 699 } 700 } 701 } 702 ensureDefaultLogger(Logger logger)703 private void ensureDefaultLogger(Logger logger) { 704 // Used for lazy addition of root logger and global logger 705 // to a LoggerContext. 706 707 // This check is simple sanity: we do not want that this 708 // method be called for anything else than Logger.global 709 // or owner.rootLogger. 710 if (!requiresDefaultLoggers() || logger == null 711 || logger != Logger.global && logger != LogManager.this.rootLogger) { 712 713 // the case where we have a non null logger which is neither 714 // Logger.global nor manager.rootLogger indicates a serious 715 // issue - as ensureDefaultLogger should never be called 716 // with any other loggers than one of these two (or null - if 717 // e.g manager.rootLogger is not yet initialized)... 718 assert logger == null; 719 720 return; 721 } 722 723 // Adds the logger if it's not already there. 724 if (!namedLoggers.containsKey(logger.getName())) { 725 // It is important to prevent addLocalLogger to 726 // call ensureAllDefaultLoggers when we're in the process 727 // off adding one of those default loggers - as this would 728 // immediately cause a stack overflow. 729 // Therefore we must pass addDefaultLoggersIfNeeded=false, 730 // even if requiresDefaultLoggers is true. 731 addLocalLogger(logger, false); 732 } 733 } 734 addLocalLogger(Logger logger)735 boolean addLocalLogger(Logger logger) { 736 // no need to add default loggers if it's not required 737 return addLocalLogger(logger, requiresDefaultLoggers()); 738 } 739 740 // Add a logger to this context. This method will only set its level 741 // and process parent loggers. It doesn't set its handlers. addLocalLogger(Logger logger, boolean addDefaultLoggersIfNeeded)742 synchronized boolean addLocalLogger(Logger logger, boolean addDefaultLoggersIfNeeded) { 743 // addDefaultLoggersIfNeeded serves to break recursion when adding 744 // default loggers. If we're adding one of the default loggers 745 // (we're being called from ensureDefaultLogger()) then 746 // addDefaultLoggersIfNeeded will be false: we don't want to 747 // call ensureAllDefaultLoggers again. 748 // 749 // Note: addDefaultLoggersIfNeeded can also be false when 750 // requiresDefaultLoggers is false - since calling 751 // ensureAllDefaultLoggers would have no effect in this case. 752 if (addDefaultLoggersIfNeeded) { 753 ensureAllDefaultLoggers(logger); 754 } 755 756 final String name = logger.getName(); 757 if (name == null) { 758 throw new NullPointerException(); 759 } 760 LoggerWeakRef ref = namedLoggers.get(name); 761 if (ref != null) { 762 if (ref.get() == null) { 763 // It's possible that the Logger was GC'ed after a 764 // drainLoggerRefQueueBounded() call above so allow 765 // a new one to be registered. 766 ref.dispose(); 767 } else { 768 // We already have a registered logger with the given name. 769 return false; 770 } 771 } 772 773 // We're adding a new logger. 774 // Note that we are creating a weak reference here. 775 final LogManager owner = getOwner(); 776 logger.setLogManager(owner); 777 ref = owner.new LoggerWeakRef(logger); 778 namedLoggers.put(name, ref); 779 780 // Apply any initial level defined for the new logger, unless 781 // the logger's level is already initialized 782 Level level = owner.getLevelProperty(name + ".level", null); 783 if (level != null && !logger.isLevelInitialized()) { 784 doSetLevel(logger, level); 785 } 786 787 // instantiation of the handler is done in the LogManager.addLogger 788 // implementation as a handler class may be only visible to LogManager 789 // subclass for the custom log manager case 790 processParentHandlers(logger, name); 791 792 // Find the new node and its parent. 793 LogNode node = getNode(name); 794 node.loggerRef = ref; 795 Logger parent = null; 796 LogNode nodep = node.parent; 797 while (nodep != null) { 798 LoggerWeakRef nodeRef = nodep.loggerRef; 799 if (nodeRef != null) { 800 parent = nodeRef.get(); 801 if (parent != null) { 802 break; 803 } 804 } 805 nodep = nodep.parent; 806 } 807 808 if (parent != null) { 809 doSetParent(logger, parent); 810 } 811 // Walk over the children and tell them we are their new parent. 812 node.walkAndSetParent(logger); 813 // new LogNode is ready so tell the LoggerWeakRef about it 814 ref.setNode(node); 815 return true; 816 } 817 removeLoggerRef(String name, LoggerWeakRef ref)818 synchronized void removeLoggerRef(String name, LoggerWeakRef ref) { 819 namedLoggers.remove(name, ref); 820 } 821 getLoggerNames()822 synchronized Enumeration<String> getLoggerNames() { 823 // ensure that this context is properly initialized before 824 // returning logger names. 825 ensureInitialized(); 826 return namedLoggers.keys(); 827 } 828 829 // If logger.getUseParentHandlers() returns 'true' and any of the logger's 830 // parents have levels or handlers defined, make sure they are instantiated. processParentHandlers(final Logger logger, final String name)831 private void processParentHandlers(final Logger logger, final String name) { 832 final LogManager owner = getOwner(); 833 AccessController.doPrivileged(new PrivilegedAction<Void>() { 834 @Override 835 public Void run() { 836 if (logger != owner.rootLogger) { 837 boolean useParent = owner.getBooleanProperty(name + ".useParentHandlers", true); 838 if (!useParent) { 839 logger.setUseParentHandlers(false); 840 } 841 } 842 return null; 843 } 844 }); 845 846 int ix = 1; 847 for (;;) { 848 int ix2 = name.indexOf(".", ix); 849 if (ix2 < 0) { 850 break; 851 } 852 String pname = name.substring(0, ix2); 853 if (owner.getProperty(pname + ".level") != null || 854 owner.getProperty(pname + ".handlers") != null) { 855 // This pname has a level/handlers definition. 856 // Make sure it exists. 857 demandLogger(pname, null); 858 } 859 ix = ix2+1; 860 } 861 } 862 863 // Gets a node in our tree of logger nodes. 864 // If necessary, create it. getNode(String name)865 LogNode getNode(String name) { 866 if (name == null || name.equals("")) { 867 return root; 868 } 869 LogNode node = root; 870 while (name.length() > 0) { 871 int ix = name.indexOf("."); 872 String head; 873 if (ix > 0) { 874 head = name.substring(0, ix); 875 name = name.substring(ix + 1); 876 } else { 877 head = name; 878 name = ""; 879 } 880 if (node.children == null) { 881 node.children = new HashMap<>(); 882 } 883 LogNode child = node.children.get(head); 884 if (child == null) { 885 child = new LogNode(node, this); 886 node.children.put(head, child); 887 } 888 node = child; 889 } 890 return node; 891 } 892 } 893 894 final class SystemLoggerContext extends LoggerContext { 895 // Add a system logger in the system context's namespace as well as 896 // in the LogManager's namespace if not exist so that there is only 897 // one single logger of the given name. System loggers are visible 898 // to applications unless a logger of the same name has been added. 899 @Override demandLogger(String name, String resourceBundleName)900 Logger demandLogger(String name, String resourceBundleName) { 901 Logger result = findLogger(name); 902 if (result == null) { 903 // only allocate the new system logger once 904 Logger newLogger = new Logger(name, resourceBundleName, null, getOwner(), true); 905 do { 906 if (addLocalLogger(newLogger)) { 907 // We successfully added the new Logger that we 908 // created above so return it without refetching. 909 result = newLogger; 910 } else { 911 // We didn't add the new Logger that we created above 912 // because another thread added a Logger with the same 913 // name after our null check above and before our call 914 // to addLogger(). We have to refetch the Logger because 915 // addLogger() returns a boolean instead of the Logger 916 // reference itself. However, if the thread that created 917 // the other Logger is not holding a strong reference to 918 // the other Logger, then it is possible for the other 919 // Logger to be GC'ed after we saw it in addLogger() and 920 // before we can refetch it. If it has been GC'ed then 921 // we'll just loop around and try again. 922 result = findLogger(name); 923 } 924 } while (result == null); 925 } 926 return result; 927 } 928 } 929 930 // Add new per logger handlers. 931 // We need to raise privilege here. All our decisions will 932 // be made based on the logging configuration, which can 933 // only be modified by trusted code. loadLoggerHandlers(final Logger logger, final String name, final String handlersPropertyName)934 private void loadLoggerHandlers(final Logger logger, final String name, 935 final String handlersPropertyName) 936 { 937 AccessController.doPrivileged(new PrivilegedAction<Object>() { 938 @Override 939 public Object run() { 940 String names[] = parseClassNames(handlersPropertyName); 941 for (int i = 0; i < names.length; i++) { 942 String word = names[i]; 943 try { 944 // Android-changed: Fall back from the system to the context classloader. 945 // Class<?> clz = ClassLoader.getSystemClassLoader().loadClass(word); 946 Class<?> clz = getClassInstance(word); 947 Handler hdl = (Handler) clz.newInstance(); 948 // Check if there is a property defining the 949 // this handler's level. 950 String levs = getProperty(word + ".level"); 951 if (levs != null) { 952 Level l = Level.findLevel(levs); 953 if (l != null) { 954 hdl.setLevel(l); 955 } else { 956 // Probably a bad level. Drop through. 957 System.err.println("Can't set level for " + word); 958 } 959 } 960 // Add this Handler to the logger 961 logger.addHandler(hdl); 962 } catch (Exception ex) { 963 System.err.println("Can't load log handler \"" + word + "\""); 964 System.err.println("" + ex); 965 ex.printStackTrace(); 966 } 967 } 968 return null; 969 } 970 }); 971 } 972 973 974 // loggerRefQueue holds LoggerWeakRef objects for Logger objects 975 // that have been GC'ed. 976 private final ReferenceQueue<Logger> loggerRefQueue 977 = new ReferenceQueue<>(); 978 979 // Package-level inner class. 980 // Helper class for managing WeakReferences to Logger objects. 981 // 982 // LogManager.namedLoggers 983 // - has weak references to all named Loggers 984 // - namedLoggers keeps the LoggerWeakRef objects for the named 985 // Loggers around until we can deal with the book keeping for 986 // the named Logger that is being GC'ed. 987 // LogManager.LogNode.loggerRef 988 // - has a weak reference to a named Logger 989 // - the LogNode will also keep the LoggerWeakRef objects for 990 // the named Loggers around; currently LogNodes never go away. 991 // Logger.kids 992 // - has a weak reference to each direct child Logger; this 993 // includes anonymous and named Loggers 994 // - anonymous Loggers are always children of the rootLogger 995 // which is a strong reference; rootLogger.kids keeps the 996 // LoggerWeakRef objects for the anonymous Loggers around 997 // until we can deal with the book keeping. 998 // 999 final class LoggerWeakRef extends WeakReference<Logger> { 1000 private String name; // for namedLoggers cleanup 1001 private LogNode node; // for loggerRef cleanup 1002 private WeakReference<Logger> parentRef; // for kids cleanup 1003 private boolean disposed = false; // avoid calling dispose twice 1004 LoggerWeakRef(Logger logger)1005 LoggerWeakRef(Logger logger) { 1006 super(logger, loggerRefQueue); 1007 1008 name = logger.getName(); // save for namedLoggers cleanup 1009 } 1010 1011 // dispose of this LoggerWeakRef object dispose()1012 void dispose() { 1013 // Avoid calling dispose twice. When a Logger is gc'ed, its 1014 // LoggerWeakRef will be enqueued. 1015 // However, a new logger of the same name may be added (or looked 1016 // up) before the queue is drained. When that happens, dispose() 1017 // will be called by addLocalLogger() or findLogger(). 1018 // Later when the queue is drained, dispose() will be called again 1019 // for the same LoggerWeakRef. Marking LoggerWeakRef as disposed 1020 // avoids processing the data twice (even though the code should 1021 // now be reentrant). 1022 synchronized(this) { 1023 // Note to maintainers: 1024 // Be careful not to call any method that tries to acquire 1025 // another lock from within this block - as this would surely 1026 // lead to deadlocks, given that dispose() can be called by 1027 // multiple threads, and from within different synchronized 1028 // methods/blocks. 1029 if (disposed) return; 1030 disposed = true; 1031 } 1032 1033 final LogNode n = node; 1034 if (n != null) { 1035 // n.loggerRef can only be safely modified from within 1036 // a lock on LoggerContext. removeLoggerRef is already 1037 // synchronized on LoggerContext so calling 1038 // n.context.removeLoggerRef from within this lock is safe. 1039 synchronized (n.context) { 1040 // if we have a LogNode, then we were a named Logger 1041 // so clear namedLoggers weak ref to us 1042 n.context.removeLoggerRef(name, this); 1043 name = null; // clear our ref to the Logger's name 1044 1045 // LogNode may have been reused - so only clear 1046 // LogNode.loggerRef if LogNode.loggerRef == this 1047 if (n.loggerRef == this) { 1048 n.loggerRef = null; // clear LogNode's weak ref to us 1049 } 1050 node = null; // clear our ref to LogNode 1051 } 1052 } 1053 1054 if (parentRef != null) { 1055 // this LoggerWeakRef has or had a parent Logger 1056 Logger parent = parentRef.get(); 1057 if (parent != null) { 1058 // the parent Logger is still there so clear the 1059 // parent Logger's weak ref to us 1060 parent.removeChildLogger(this); 1061 } 1062 parentRef = null; // clear our weak ref to the parent Logger 1063 } 1064 } 1065 1066 // set the node field to the specified value setNode(LogNode node)1067 void setNode(LogNode node) { 1068 this.node = node; 1069 } 1070 1071 // set the parentRef field to the specified value setParentRef(WeakReference<Logger> parentRef)1072 void setParentRef(WeakReference<Logger> parentRef) { 1073 this.parentRef = parentRef; 1074 } 1075 } 1076 1077 // Package-level method. 1078 // Drain some Logger objects that have been GC'ed. 1079 // 1080 // drainLoggerRefQueueBounded() is called by addLogger() below 1081 // and by Logger.getAnonymousLogger(String) so we'll drain up to 1082 // MAX_ITERATIONS GC'ed Loggers for every Logger we add. 1083 // 1084 // On a WinXP VMware client, a MAX_ITERATIONS value of 400 gives 1085 // us about a 50/50 mix in increased weak ref counts versus 1086 // decreased weak ref counts in the AnonLoggerWeakRefLeak test. 1087 // Here are stats for cleaning up sets of 400 anonymous Loggers: 1088 // - test duration 1 minute 1089 // - sample size of 125 sets of 400 1090 // - average: 1.99 ms 1091 // - minimum: 0.57 ms 1092 // - maximum: 25.3 ms 1093 // 1094 // The same config gives us a better decreased weak ref count 1095 // than increased weak ref count in the LoggerWeakRefLeak test. 1096 // Here are stats for cleaning up sets of 400 named Loggers: 1097 // - test duration 2 minutes 1098 // - sample size of 506 sets of 400 1099 // - average: 0.57 ms 1100 // - minimum: 0.02 ms 1101 // - maximum: 10.9 ms 1102 // 1103 private final static int MAX_ITERATIONS = 400; drainLoggerRefQueueBounded()1104 final void drainLoggerRefQueueBounded() { 1105 for (int i = 0; i < MAX_ITERATIONS; i++) { 1106 if (loggerRefQueue == null) { 1107 // haven't finished loading LogManager yet 1108 break; 1109 } 1110 1111 LoggerWeakRef ref = (LoggerWeakRef) loggerRefQueue.poll(); 1112 if (ref == null) { 1113 break; 1114 } 1115 // a Logger object has been GC'ed so clean it up 1116 ref.dispose(); 1117 } 1118 } 1119 1120 /** 1121 * Add a named logger. This does nothing and returns false if a logger 1122 * with the same name is already registered. 1123 * <p> 1124 * The Logger factory methods call this method to register each 1125 * newly created Logger. 1126 * <p> 1127 * The application should retain its own reference to the Logger 1128 * object to avoid it being garbage collected. The LogManager 1129 * may only retain a weak reference. 1130 * 1131 * @param logger the new logger. 1132 * @return true if the argument logger was registered successfully, 1133 * false if a logger of that name already exists. 1134 * @exception NullPointerException if the logger name is null. 1135 */ addLogger(Logger logger)1136 public boolean addLogger(Logger logger) { 1137 final String name = logger.getName(); 1138 if (name == null) { 1139 throw new NullPointerException(); 1140 } 1141 drainLoggerRefQueueBounded(); 1142 LoggerContext cx = getUserContext(); 1143 if (cx.addLocalLogger(logger)) { 1144 // Do we have a per logger handler too? 1145 // Note: this will add a 200ms penalty 1146 loadLoggerHandlers(logger, name, name + ".handlers"); 1147 return true; 1148 } else { 1149 return false; 1150 } 1151 } 1152 1153 // Private method to set a level on a logger. 1154 // If necessary, we raise privilege before doing the call. doSetLevel(final Logger logger, final Level level)1155 private static void doSetLevel(final Logger logger, final Level level) { 1156 SecurityManager sm = System.getSecurityManager(); 1157 if (sm == null) { 1158 // There is no security manager, so things are easy. 1159 logger.setLevel(level); 1160 return; 1161 } 1162 // There is a security manager. Raise privilege before 1163 // calling setLevel. 1164 AccessController.doPrivileged(new PrivilegedAction<Object>() { 1165 @Override 1166 public Object run() { 1167 logger.setLevel(level); 1168 return null; 1169 }}); 1170 } 1171 1172 // Private method to set a parent on a logger. 1173 // If necessary, we raise privilege before doing the setParent call. doSetParent(final Logger logger, final Logger parent)1174 private static void doSetParent(final Logger logger, final Logger parent) { 1175 SecurityManager sm = System.getSecurityManager(); 1176 if (sm == null) { 1177 // There is no security manager, so things are easy. 1178 logger.setParent(parent); 1179 return; 1180 } 1181 // There is a security manager. Raise privilege before 1182 // calling setParent. 1183 AccessController.doPrivileged(new PrivilegedAction<Object>() { 1184 @Override 1185 public Object run() { 1186 logger.setParent(parent); 1187 return null; 1188 }}); 1189 } 1190 1191 /** 1192 * Method to find a named logger. 1193 * <p> 1194 * Note that since untrusted code may create loggers with 1195 * arbitrary names this method should not be relied on to 1196 * find Loggers for security sensitive logging. 1197 * It is also important to note that the Logger associated with the 1198 * String {@code name} may be garbage collected at any time if there 1199 * is no strong reference to the Logger. The caller of this method 1200 * must check the return value for null in order to properly handle 1201 * the case where the Logger has been garbage collected. 1202 * <p> 1203 * @param name name of the logger 1204 * @return matching logger or null if none is found 1205 */ getLogger(String name)1206 public Logger getLogger(String name) { 1207 return getUserContext().findLogger(name); 1208 } 1209 1210 /** 1211 * Get an enumeration of known logger names. 1212 * <p> 1213 * Note: Loggers may be added dynamically as new classes are loaded. 1214 * This method only reports on the loggers that are currently registered. 1215 * It is also important to note that this method only returns the name 1216 * of a Logger, not a strong reference to the Logger itself. 1217 * The returned String does nothing to prevent the Logger from being 1218 * garbage collected. In particular, if the returned name is passed 1219 * to {@code LogManager.getLogger()}, then the caller must check the 1220 * return value from {@code LogManager.getLogger()} for null to properly 1221 * handle the case where the Logger has been garbage collected in the 1222 * time since its name was returned by this method. 1223 * <p> 1224 * @return enumeration of logger name strings 1225 */ getLoggerNames()1226 public Enumeration<String> getLoggerNames() { 1227 return getUserContext().getLoggerNames(); 1228 } 1229 1230 /** 1231 * Reinitialize the logging properties and reread the logging configuration. 1232 * <p> 1233 * The same rules are used for locating the configuration properties 1234 * as are used at startup. So normally the logging properties will 1235 * be re-read from the same file that was used at startup. 1236 * <P> 1237 * Any log level definitions in the new configuration file will be 1238 * applied using Logger.setLevel(), if the target Logger exists. 1239 * <p> 1240 * A PropertyChangeEvent will be fired after the properties are read. 1241 * 1242 * @exception SecurityException if a security manager exists and if 1243 * the caller does not have LoggingPermission("control"). 1244 * @exception IOException if there are IO problems reading the configuration. 1245 */ readConfiguration()1246 public void readConfiguration() throws IOException, SecurityException { 1247 checkPermission(); 1248 1249 // if a configuration class is specified, load it and use it. 1250 String cname = System.getProperty("java.util.logging.config.class"); 1251 if (cname != null) { 1252 try { 1253 // Instantiate the named class. It is its constructor's 1254 // responsibility to initialize the logging configuration, by 1255 // calling readConfiguration(InputStream) with a suitable stream. 1256 // Android-changed: Extract logic into the getClassInstance() helper. 1257 getClassInstance(cname).newInstance(); 1258 return; 1259 } catch (Exception ex) { 1260 System.err.println("Logging configuration class \"" + cname + "\" failed"); 1261 System.err.println("" + ex); 1262 // keep going and useful config file. 1263 } 1264 } 1265 1266 String fname = System.getProperty("java.util.logging.config.file"); 1267 if (fname == null) { 1268 fname = System.getProperty("java.home"); 1269 if (fname == null) { 1270 throw new Error("Can't find java.home ??"); 1271 } 1272 File f = new File(fname, "lib"); 1273 f = new File(f, "logging.properties"); 1274 fname = f.getCanonicalPath(); 1275 } 1276 1277 // BEGIN Android-changed: Look in the boot class-path jar files for the logging.properties. 1278 // It may not be present in the file system. 1279 /* 1280 try (final InputStream in = new FileInputStream(fname)) { 1281 final BufferedInputStream bin = new BufferedInputStream(in); 1282 readConfiguration(bin); 1283 } 1284 */ 1285 InputStream in; 1286 try { 1287 in = new FileInputStream(fname); 1288 } catch (Exception e) { 1289 in = LogManager.class.getResourceAsStream("logging.properties"); 1290 if (in == null) { 1291 throw e; 1292 } 1293 } 1294 1295 BufferedInputStream bin = new BufferedInputStream(in); 1296 try { 1297 readConfiguration(bin); 1298 } finally { 1299 if (in != null) { 1300 in.close(); 1301 } 1302 } 1303 // END Android-changed: Look in the boot class-path jar files for the logging.properties. 1304 } 1305 1306 /** 1307 * Reset the logging configuration. 1308 * <p> 1309 * For all named loggers, the reset operation removes and closes 1310 * all Handlers and (except for the root logger) sets the level 1311 * to null. The root logger's level is set to Level.INFO. 1312 * 1313 * @exception SecurityException if a security manager exists and if 1314 * the caller does not have LoggingPermission("control"). 1315 */ 1316 reset()1317 public void reset() throws SecurityException { 1318 checkPermission(); 1319 synchronized (this) { 1320 props = new Properties(); 1321 // Since we are doing a reset we no longer want to initialize 1322 // the global handlers, if they haven't been initialized yet. 1323 initializedGlobalHandlers = true; 1324 } 1325 for (LoggerContext cx : contexts()) { 1326 Enumeration<String> enum_ = cx.getLoggerNames(); 1327 while (enum_.hasMoreElements()) { 1328 String name = enum_.nextElement(); 1329 Logger logger = cx.findLogger(name); 1330 if (logger != null) { 1331 resetLogger(logger); 1332 } 1333 } 1334 } 1335 } 1336 1337 // Private method to reset an individual target logger. resetLogger(Logger logger)1338 private void resetLogger(Logger logger) { 1339 // Close all the Logger's handlers. 1340 Handler[] targets = logger.getHandlers(); 1341 for (int i = 0; i < targets.length; i++) { 1342 Handler h = targets[i]; 1343 logger.removeHandler(h); 1344 try { 1345 h.close(); 1346 } catch (Exception ex) { 1347 // Problems closing a handler? Keep going... 1348 } 1349 } 1350 String name = logger.getName(); 1351 if (name != null && name.equals("")) { 1352 // This is the root logger. 1353 logger.setLevel(defaultLevel); 1354 } else { 1355 logger.setLevel(null); 1356 } 1357 } 1358 1359 // get a list of whitespace separated classnames from a property. parseClassNames(String propertyName)1360 private String[] parseClassNames(String propertyName) { 1361 String hands = getProperty(propertyName); 1362 if (hands == null) { 1363 return new String[0]; 1364 } 1365 hands = hands.trim(); 1366 int ix = 0; 1367 final List<String> result = new ArrayList<>(); 1368 while (ix < hands.length()) { 1369 int end = ix; 1370 while (end < hands.length()) { 1371 if (Character.isWhitespace(hands.charAt(end))) { 1372 break; 1373 } 1374 if (hands.charAt(end) == ',') { 1375 break; 1376 } 1377 end++; 1378 } 1379 String word = hands.substring(ix, end); 1380 ix = end+1; 1381 word = word.trim(); 1382 if (word.length() == 0) { 1383 continue; 1384 } 1385 result.add(word); 1386 } 1387 return result.toArray(new String[result.size()]); 1388 } 1389 1390 /** 1391 * Reinitialize the logging properties and reread the logging configuration 1392 * from the given stream, which should be in java.util.Properties format. 1393 * A PropertyChangeEvent will be fired after the properties are read. 1394 * <p> 1395 * Any log level definitions in the new configuration file will be 1396 * applied using Logger.setLevel(), if the target Logger exists. 1397 * 1398 * @param ins stream to read properties from 1399 * @exception SecurityException if a security manager exists and if 1400 * the caller does not have LoggingPermission("control"). 1401 * @exception IOException if there are problems reading from the stream. 1402 */ readConfiguration(InputStream ins)1403 public void readConfiguration(InputStream ins) throws IOException, SecurityException { 1404 checkPermission(); 1405 reset(); 1406 1407 // Load the properties 1408 props.load(ins); 1409 // Instantiate new configuration objects. 1410 String names[] = parseClassNames("config"); 1411 1412 for (int i = 0; i < names.length; i++) { 1413 String word = names[i]; 1414 try { 1415 // Android-changed: Fall back from the system to the context classloader. 1416 // Class<?> clz = ClassLoader.getSystemClassLoader().loadClass(word); 1417 // clz.newInstance(); 1418 getClassInstance(word).newInstance(); 1419 } catch (Exception ex) { 1420 System.err.println("Can't load config class \"" + word + "\""); 1421 System.err.println("" + ex); 1422 // ex.printStackTrace(); 1423 } 1424 } 1425 1426 // Set levels on any pre-existing loggers, based on the new properties. 1427 setLevelsOnExistingLoggers(); 1428 1429 // Notify any interested parties that our properties have changed. 1430 // We first take a copy of the listener map so that we aren't holding any 1431 // locks when calling the listeners. 1432 Map<Object,Integer> listeners = null; 1433 synchronized (listenerMap) { 1434 if (!listenerMap.isEmpty()) 1435 listeners = new HashMap<>(listenerMap); 1436 } 1437 if (listeners != null) { 1438 assert Beans.isBeansPresent(); 1439 Object ev = Beans.newPropertyChangeEvent(LogManager.class, null, null, null); 1440 for (Map.Entry<Object,Integer> entry : listeners.entrySet()) { 1441 Object listener = entry.getKey(); 1442 int count = entry.getValue().intValue(); 1443 for (int i = 0; i < count; i++) { 1444 Beans.invokePropertyChange(listener, ev); 1445 } 1446 } 1447 } 1448 1449 1450 // Note that we need to reinitialize global handles when 1451 // they are first referenced. 1452 synchronized (this) { 1453 initializedGlobalHandlers = false; 1454 } 1455 } 1456 1457 /** 1458 * Get the value of a logging property. 1459 * The method returns null if the property is not found. 1460 * @param name property name 1461 * @return property value 1462 */ getProperty(String name)1463 public String getProperty(String name) { 1464 return props.getProperty(name); 1465 } 1466 1467 // Package private method to get a String property. 1468 // If the property is not defined we return the given 1469 // default value. getStringProperty(String name, String defaultValue)1470 String getStringProperty(String name, String defaultValue) { 1471 String val = getProperty(name); 1472 if (val == null) { 1473 return defaultValue; 1474 } 1475 return val.trim(); 1476 } 1477 1478 // Package private method to get an integer property. 1479 // If the property is not defined or cannot be parsed 1480 // we return the given default value. getIntProperty(String name, int defaultValue)1481 int getIntProperty(String name, int defaultValue) { 1482 String val = getProperty(name); 1483 if (val == null) { 1484 return defaultValue; 1485 } 1486 try { 1487 return Integer.parseInt(val.trim()); 1488 } catch (Exception ex) { 1489 return defaultValue; 1490 } 1491 } 1492 1493 // Package private method to get a boolean property. 1494 // If the property is not defined or cannot be parsed 1495 // we return the given default value. getBooleanProperty(String name, boolean defaultValue)1496 boolean getBooleanProperty(String name, boolean defaultValue) { 1497 String val = getProperty(name); 1498 if (val == null) { 1499 return defaultValue; 1500 } 1501 val = val.toLowerCase(); 1502 if (val.equals("true") || val.equals("1")) { 1503 return true; 1504 } else if (val.equals("false") || val.equals("0")) { 1505 return false; 1506 } 1507 return defaultValue; 1508 } 1509 1510 // Package private method to get a Level property. 1511 // If the property is not defined or cannot be parsed 1512 // we return the given default value. getLevelProperty(String name, Level defaultValue)1513 Level getLevelProperty(String name, Level defaultValue) { 1514 String val = getProperty(name); 1515 if (val == null) { 1516 return defaultValue; 1517 } 1518 Level l = Level.findLevel(val.trim()); 1519 return l != null ? l : defaultValue; 1520 } 1521 1522 // Package private method to get a filter property. 1523 // We return an instance of the class named by the "name" 1524 // property. If the property is not defined or has problems 1525 // we return the defaultValue. getFilterProperty(String name, Filter defaultValue)1526 Filter getFilterProperty(String name, Filter defaultValue) { 1527 String val = getProperty(name); 1528 try { 1529 if (val != null) { 1530 // Android-changed: Fall back from the system to the context classloader. 1531 // Class<?> clz = ClassLoader.getSystemClassLoader().loadClass(val); 1532 // return (Filter) clz.newInstance(); 1533 return (Filter) getClassInstance(val).newInstance(); 1534 } 1535 } catch (Exception ex) { 1536 // We got one of a variety of exceptions in creating the 1537 // class or creating an instance. 1538 // Drop through. 1539 } 1540 // We got an exception. Return the defaultValue. 1541 return defaultValue; 1542 } 1543 1544 1545 // Package private method to get a formatter property. 1546 // We return an instance of the class named by the "name" 1547 // property. If the property is not defined or has problems 1548 // we return the defaultValue. getFormatterProperty(String name, Formatter defaultValue)1549 Formatter getFormatterProperty(String name, Formatter defaultValue) { 1550 String val = getProperty(name); 1551 try { 1552 if (val != null) { 1553 // Android-changed: Fall back from the system to the context classloader. 1554 // Class<?> clz = ClassLoader.getSystemClassLoader().loadClass(val); 1555 // return (Formatter) clz.newInstance(); 1556 return (Formatter) getClassInstance(val).newInstance(); 1557 } 1558 } catch (Exception ex) { 1559 // We got one of a variety of exceptions in creating the 1560 // class or creating an instance. 1561 // Drop through. 1562 } 1563 // We got an exception. Return the defaultValue. 1564 return defaultValue; 1565 } 1566 1567 // Private method to load the global handlers. 1568 // We do the real work lazily, when the global handlers 1569 // are first used. initializeGlobalHandlers()1570 private synchronized void initializeGlobalHandlers() { 1571 if (initializedGlobalHandlers) { 1572 return; 1573 } 1574 1575 initializedGlobalHandlers = true; 1576 1577 if (deathImminent) { 1578 // Aaargh... 1579 // The VM is shutting down and our exit hook has been called. 1580 // Avoid allocating global handlers. 1581 return; 1582 } 1583 loadLoggerHandlers(rootLogger, null, "handlers"); 1584 } 1585 1586 private final Permission controlPermission = new LoggingPermission("control", null); 1587 checkPermission()1588 void checkPermission() { 1589 SecurityManager sm = System.getSecurityManager(); 1590 if (sm != null) 1591 sm.checkPermission(controlPermission); 1592 } 1593 1594 /** 1595 * Check that the current context is trusted to modify the logging 1596 * configuration. This requires LoggingPermission("control"). 1597 * <p> 1598 * If the check fails we throw a SecurityException, otherwise 1599 * we return normally. 1600 * 1601 * @exception SecurityException if a security manager exists and if 1602 * the caller does not have LoggingPermission("control"). 1603 */ checkAccess()1604 public void checkAccess() throws SecurityException { 1605 checkPermission(); 1606 } 1607 1608 // Nested class to represent a node in our tree of named loggers. 1609 private static class LogNode { 1610 HashMap<String,LogNode> children; 1611 LoggerWeakRef loggerRef; 1612 LogNode parent; 1613 final LoggerContext context; 1614 LogNode(LogNode parent, LoggerContext context)1615 LogNode(LogNode parent, LoggerContext context) { 1616 this.parent = parent; 1617 this.context = context; 1618 } 1619 1620 // Recursive method to walk the tree below a node and set 1621 // a new parent logger. walkAndSetParent(Logger parent)1622 void walkAndSetParent(Logger parent) { 1623 if (children == null) { 1624 return; 1625 } 1626 Iterator<LogNode> values = children.values().iterator(); 1627 while (values.hasNext()) { 1628 LogNode node = values.next(); 1629 LoggerWeakRef ref = node.loggerRef; 1630 Logger logger = (ref == null) ? null : ref.get(); 1631 if (logger == null) { 1632 node.walkAndSetParent(parent); 1633 } else { 1634 doSetParent(logger, parent); 1635 } 1636 } 1637 } 1638 } 1639 1640 // We use a subclass of Logger for the root logger, so 1641 // that we only instantiate the global handlers when they 1642 // are first needed. 1643 private final class RootLogger extends Logger { RootLogger()1644 private RootLogger() { 1645 // We do not call the protected Logger two args constructor here, 1646 // to avoid calling LogManager.getLogManager() from within the 1647 // RootLogger constructor. 1648 super("", null, null, LogManager.this, true); 1649 } 1650 1651 @Override log(LogRecord record)1652 public void log(LogRecord record) { 1653 // Make sure that the global handlers have been instantiated. 1654 initializeGlobalHandlers(); 1655 super.log(record); 1656 } 1657 1658 @Override addHandler(Handler h)1659 public void addHandler(Handler h) { 1660 initializeGlobalHandlers(); 1661 super.addHandler(h); 1662 } 1663 1664 @Override removeHandler(Handler h)1665 public void removeHandler(Handler h) { 1666 initializeGlobalHandlers(); 1667 super.removeHandler(h); 1668 } 1669 1670 @Override accessCheckedHandlers()1671 Handler[] accessCheckedHandlers() { 1672 initializeGlobalHandlers(); 1673 return super.accessCheckedHandlers(); 1674 } 1675 } 1676 1677 1678 // Private method to be called when the configuration has 1679 // changed to apply any level settings to any pre-existing loggers. setLevelsOnExistingLoggers()1680 synchronized private void setLevelsOnExistingLoggers() { 1681 Enumeration<?> enum_ = props.propertyNames(); 1682 while (enum_.hasMoreElements()) { 1683 String key = (String)enum_.nextElement(); 1684 if (!key.endsWith(".level")) { 1685 // Not a level definition. 1686 continue; 1687 } 1688 int ix = key.length() - 6; 1689 String name = key.substring(0, ix); 1690 Level level = getLevelProperty(key, null); 1691 if (level == null) { 1692 System.err.println("Bad level value for property: " + key); 1693 continue; 1694 } 1695 for (LoggerContext cx : contexts()) { 1696 Logger l = cx.findLogger(name); 1697 if (l == null) { 1698 continue; 1699 } 1700 l.setLevel(level); 1701 } 1702 } 1703 } 1704 1705 // Management Support 1706 private static LoggingMXBean loggingMXBean = null; 1707 // Android-removed: References to java.lang.management in javadoc. 1708 /** 1709 * String representation of the {@code ObjectName} for the management interface 1710 * for the logging facility. 1711 * 1712 * @see java.util.logging.LoggingMXBean 1713 * 1714 * @since 1.5 1715 */ 1716 public final static String LOGGING_MXBEAN_NAME 1717 = "java.util.logging:type=Logging"; 1718 1719 // Android-removed: References to java.lang.management in javadoc. 1720 /** 1721 * Returns <tt>LoggingMXBean</tt> for managing loggers. 1722 * 1723 * @return a {@link LoggingMXBean} object. 1724 * 1725 * @since 1.5 1726 */ getLoggingMXBean()1727 public static synchronized LoggingMXBean getLoggingMXBean() { 1728 if (loggingMXBean == null) { 1729 loggingMXBean = new Logging(); 1730 } 1731 return loggingMXBean; 1732 } 1733 1734 /** 1735 * A class that provides access to the java.beans.PropertyChangeListener 1736 * and java.beans.PropertyChangeEvent without creating a static dependency 1737 * on java.beans. This class can be removed once the addPropertyChangeListener 1738 * and removePropertyChangeListener methods are removed. 1739 */ 1740 private static class Beans { 1741 private static final Class<?> propertyChangeListenerClass = 1742 getClass("java.beans.PropertyChangeListener"); 1743 1744 private static final Class<?> propertyChangeEventClass = 1745 getClass("java.beans.PropertyChangeEvent"); 1746 1747 private static final Method propertyChangeMethod = 1748 getMethod(propertyChangeListenerClass, 1749 "propertyChange", 1750 propertyChangeEventClass); 1751 1752 private static final Constructor<?> propertyEventCtor = 1753 getConstructor(propertyChangeEventClass, 1754 Object.class, 1755 String.class, 1756 Object.class, 1757 Object.class); 1758 getClass(String name)1759 private static Class<?> getClass(String name) { 1760 try { 1761 return Class.forName(name, true, Beans.class.getClassLoader()); 1762 } catch (ClassNotFoundException e) { 1763 return null; 1764 } 1765 } getConstructor(Class<?> c, Class<?>... types)1766 private static Constructor<?> getConstructor(Class<?> c, Class<?>... types) { 1767 try { 1768 return (c == null) ? null : c.getDeclaredConstructor(types); 1769 } catch (NoSuchMethodException x) { 1770 throw new AssertionError(x); 1771 } 1772 } 1773 getMethod(Class<?> c, String name, Class<?>... types)1774 private static Method getMethod(Class<?> c, String name, Class<?>... types) { 1775 try { 1776 return (c == null) ? null : c.getMethod(name, types); 1777 } catch (NoSuchMethodException e) { 1778 throw new AssertionError(e); 1779 } 1780 } 1781 1782 /** 1783 * Returns {@code true} if java.beans is present. 1784 */ isBeansPresent()1785 static boolean isBeansPresent() { 1786 return propertyChangeListenerClass != null && 1787 propertyChangeEventClass != null; 1788 } 1789 1790 /** 1791 * Returns a new PropertyChangeEvent with the given source, property 1792 * name, old and new values. 1793 */ newPropertyChangeEvent(Object source, String prop, Object oldValue, Object newValue)1794 static Object newPropertyChangeEvent(Object source, String prop, 1795 Object oldValue, Object newValue) 1796 { 1797 try { 1798 return propertyEventCtor.newInstance(source, prop, oldValue, newValue); 1799 } catch (InstantiationException | IllegalAccessException x) { 1800 throw new AssertionError(x); 1801 } catch (InvocationTargetException x) { 1802 Throwable cause = x.getCause(); 1803 if (cause instanceof Error) 1804 throw (Error)cause; 1805 if (cause instanceof RuntimeException) 1806 throw (RuntimeException)cause; 1807 throw new AssertionError(x); 1808 } 1809 } 1810 1811 /** 1812 * Invokes the given PropertyChangeListener's propertyChange method 1813 * with the given event. 1814 */ invokePropertyChange(Object listener, Object ev)1815 static void invokePropertyChange(Object listener, Object ev) { 1816 try { 1817 propertyChangeMethod.invoke(listener, ev); 1818 } catch (IllegalAccessException x) { 1819 throw new AssertionError(x); 1820 } catch (InvocationTargetException x) { 1821 Throwable cause = x.getCause(); 1822 if (cause instanceof Error) 1823 throw (Error)cause; 1824 if (cause instanceof RuntimeException) 1825 throw (RuntimeException)cause; 1826 throw new AssertionError(x); 1827 } 1828 } 1829 } 1830 } 1831