Path: blob/master/src/java.logging/share/classes/java/util/logging/LogManager.java
41159 views
/*1* Copyright (c) 2000, 2021, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package java.util.logging;2627import java.io.*;28import java.util.*;29import java.security.*;30import java.lang.ref.ReferenceQueue;31import java.lang.ref.WeakReference;32import java.util.concurrent.ConcurrentHashMap;33import java.nio.file.Paths;34import java.util.concurrent.CopyOnWriteArrayList;35import java.util.concurrent.locks.ReentrantLock;36import java.util.function.BiFunction;37import java.util.function.Function;38import java.util.function.Predicate;39import java.util.stream.Collectors;40import java.util.stream.Stream;41import jdk.internal.access.JavaAWTAccess;42import jdk.internal.access.SharedSecrets;43import sun.util.logging.internal.LoggingProviderImpl;44import static jdk.internal.logger.DefaultLoggerFinder.isSystem;4546/**47* There is a single global LogManager object that is used to48* maintain a set of shared state about Loggers and log services.49* <p>50* This LogManager object:51* <ul>52* <li> Manages a hierarchical namespace of Logger objects. All53* named Loggers are stored in this namespace.54* <li> Manages a set of logging control properties. These are55* simple key-value pairs that can be used by Handlers and56* other logging objects to configure themselves.57* </ul>58* <p>59* The global LogManager object can be retrieved using LogManager.getLogManager().60* The LogManager object is created during class initialization and61* cannot subsequently be changed.62* <p>63* At startup the LogManager class is located using the64* java.util.logging.manager system property.65*66* <h2>LogManager Configuration</h2>67*68* A LogManager initializes the logging configuration via69* the {@link #readConfiguration()} method during LogManager initialization.70* By default, LogManager default configuration is used.71* The logging configuration read by LogManager must be in the72* {@linkplain Properties properties file} format.73* <p>74* The LogManager defines two optional system properties that allow control over75* the initial configuration, as specified in the {@link #readConfiguration()}76* method:77* <ul>78* <li>{@systemProperty java.util.logging.config.class}79* <li>{@systemProperty java.util.logging.config.file}80* </ul>81* <p>82* These two system properties may be specified on the command line to the "java"83* command, or as system property definitions passed to JNI_CreateJavaVM.84* <p>85* The {@linkplain Properties properties} for loggers and Handlers will have86* names starting with the dot-separated name for the handler or logger.<br>87* The global logging properties may include:88* <ul>89* <li>A property "handlers". This defines a whitespace or comma separated90* list of class names for handler classes to load and register as91* handlers on the root Logger (the Logger named ""). Each class92* name must be for a Handler class which has a default constructor.93* Note that these Handlers may be created lazily, when they are94* first used.95*96* <li>A property "<logger>.handlers". This defines a whitespace or97* comma separated list of class names for handlers classes to98* load and register as handlers to the specified logger. Each class99* name must be for a Handler class which has a default constructor.100* Note that these Handlers may be created lazily, when they are101* first used.102*103* <li>A property "<logger>.handlers.ensureCloseOnReset". This defines a104* a boolean value. If "<logger>.handlers" is not defined or is empty,105* this property is ignored. Otherwise it defaults to {@code true}. When the106* value is {@code true}, the handlers associated with the logger are guaranteed107* to be closed on {@linkplain #reset} and shutdown. This can be turned off108* by explicitly setting "<logger>.handlers.ensureCloseOnReset=false" in109* the configuration. Note that turning this property off causes the risk of110* introducing a resource leak, as the logger may get garbage collected before111* {@code reset()} is called, thus preventing its handlers from being closed112* on {@code reset()}. In that case it is the responsibility of the application113* to ensure that the handlers are closed before the logger is garbage114* collected.115*116* <li>A property "<logger>.useParentHandlers". This defines a boolean117* value. By default every logger calls its parent in addition to118* handling the logging message itself, this often result in messages119* being handled by the root logger as well. When setting this property120* to false a Handler needs to be configured for this logger otherwise121* no logging messages are delivered.122*123* <li>A property "config". This property is intended to allow124* arbitrary configuration code to be run. The property defines a125* whitespace or comma separated list of class names. A new instance will be126* created for each named class. The default constructor of each class127* may execute arbitrary code to update the logging configuration, such as128* setting logger levels, adding handlers, adding filters, etc.129* </ul>130* <p>131* Note that all classes loaded during LogManager configuration are132* first searched on the system class path before any user class path.133* That includes the LogManager class, any config classes, and any134* handler classes.135* <p>136* Loggers are organized into a naming hierarchy based on their137* dot separated names. Thus "a.b.c" is a child of "a.b", but138* "a.b1" and a.b2" are peers.139* <p>140* All properties whose names end with ".level" are assumed to define141* log levels for Loggers. Thus "foo.level" defines a log level for142* the logger called "foo" and (recursively) for any of its children143* in the naming hierarchy. Log Levels are applied in the order they144* are defined in the properties file. Thus level settings for child145* nodes in the tree should come after settings for their parents.146* The property name ".level" can be used to set the level for the147* root of the tree.148* <p>149* All methods on the LogManager object are multi-thread safe.150*151* @since 1.4152*/153154public class LogManager {155156// 'props' is assigned within a lock but accessed without it.157// Declaring it volatile makes sure that another thread will not158// be able to see a partially constructed 'props' object.159// (seeing a partially constructed 'props' object can result in160// NPE being thrown in Hashtable.get(), because it leaves the door161// open for props.getProperties() to be called before the construcor162// of Hashtable is actually completed).163private volatile Properties props = new Properties();164private static final Level defaultLevel = Level.INFO;165166// LoggerContext for system loggers and user loggers167private final LoggerContext systemContext = new SystemLoggerContext();168private final LoggerContext userContext = new LoggerContext();169// non final field - make it volatile to make sure that other threads170// will see the new value once ensureLogManagerInitialized() has finished171// executing.172private volatile Logger rootLogger;173// Have we done the primordial reading of the configuration file?174// (Must be done after a suitable amount of java.lang.System175// initialization has been done)176private volatile boolean readPrimordialConfiguration;177// Have we initialized global (root) handlers yet?178// This gets set to STATE_UNINITIALIZED in readConfiguration179private static final int180STATE_INITIALIZED = 0, // initial state181STATE_INITIALIZING = 1,182STATE_READING_CONFIG = 2,183STATE_UNINITIALIZED = 3,184STATE_SHUTDOWN = 4; // terminal state185private volatile int globalHandlersState; // = STATE_INITIALIZED;186// A concurrency lock for reset(), readConfiguration() and Cleaner.187private final ReentrantLock configurationLock = new ReentrantLock();188189// This list contains the loggers for which some handlers have been190// explicitly configured in the configuration file.191// It prevents these loggers from being arbitrarily garbage collected.192private static final class CloseOnReset {193private final Logger logger;194private CloseOnReset(Logger ref) {195this.logger = Objects.requireNonNull(ref);196}197@Override198public boolean equals(Object other) {199return (other instanceof CloseOnReset) && ((CloseOnReset)other).logger == logger;200}201@Override202public int hashCode() {203return System.identityHashCode(logger);204}205public Logger get() {206return logger;207}208public static CloseOnReset create(Logger logger) {209return new CloseOnReset(logger);210}211}212private final CopyOnWriteArrayList<CloseOnReset> closeOnResetLoggers =213new CopyOnWriteArrayList<>();214215216private final Map<Object, Runnable> listeners =217Collections.synchronizedMap(new IdentityHashMap<>());218219// The global LogManager object220@SuppressWarnings("removal")221private static final LogManager manager = AccessController.doPrivileged(222new PrivilegedAction<LogManager>() {223@Override224public LogManager run() {225LogManager mgr = null;226String cname = null;227try {228cname = System.getProperty("java.util.logging.manager");229if (cname != null) {230try {231@SuppressWarnings("deprecation")232Object tmp = ClassLoader.getSystemClassLoader()233.loadClass(cname).newInstance();234mgr = (LogManager) tmp;235} catch (ClassNotFoundException ex) {236@SuppressWarnings("deprecation")237Object tmp = Thread.currentThread()238.getContextClassLoader().loadClass(cname).newInstance();239mgr = (LogManager) tmp;240}241}242} catch (Exception ex) {243System.err.println("Could not load Logmanager \"" + cname + "\"");244ex.printStackTrace();245}246if (mgr == null) {247mgr = new LogManager();248}249return mgr;250251}252});253254// This private class is used as a shutdown hook.255// It does a "reset" to close all open handlers.256private class Cleaner extends Thread {257258private Cleaner() {259super(null, null, "Logging-Cleaner", 0, false);260/* Set context class loader to null in order to avoid261* keeping a strong reference to an application classloader.262*/263this.setContextClassLoader(null);264}265266@Override267public void run() {268// This is to ensure the LogManager.<clinit> is completed269// before synchronized block. Otherwise deadlocks are possible.270LogManager mgr = manager;271272// set globalHandlersState to STATE_SHUTDOWN atomically so that273// no attempts are made to (re)initialize the handlers or (re)read274// the configuration again. This is terminal state.275configurationLock.lock();276globalHandlersState = STATE_SHUTDOWN;277configurationLock.unlock();278279// Do a reset to close all active handlers.280reset();281}282}283284285/**286* Protected constructor. This is protected so that container applications287* (such as J2EE containers) can subclass the object. It is non-public as288* it is intended that there only be one LogManager object, whose value is289* retrieved by calling LogManager.getLogManager.290*/291protected LogManager() {292this(checkSubclassPermissions());293}294295private LogManager(Void checked) {296297// Add a shutdown hook to close the global handlers.298try {299Runtime.getRuntime().addShutdownHook(new Cleaner());300} catch (IllegalStateException e) {301// If the VM is already shutting down,302// We do not need to register shutdownHook.303}304}305306private static Void checkSubclassPermissions() {307@SuppressWarnings("removal")308final SecurityManager sm = System.getSecurityManager();309if (sm != null) {310// These permission will be checked in the LogManager constructor,311// in order to register the Cleaner() thread as a shutdown hook.312// Check them here to avoid the penalty of constructing the object313// etc...314sm.checkPermission(new RuntimePermission("shutdownHooks"));315sm.checkPermission(new RuntimePermission("setContextClassLoader"));316}317return null;318}319320/**321* Lazy initialization: if this instance of manager is the global322* manager then this method will read the initial configuration and323* add the root logger and global logger by calling addLogger().324*325* Note that it is subtly different from what we do in LoggerContext.326* In LoggerContext we're patching up the logger context tree in order to add327* the root and global logger *to the context tree*.328*329* For this to work, addLogger() must have already have been called330* once on the LogManager instance for the default logger being331* added.332*333* This is why ensureLogManagerInitialized() needs to be called before334* any logger is added to any logger context.335*336*/337private boolean initializedCalled = false;338private volatile boolean initializationDone = false;339@SuppressWarnings("removal")340final void ensureLogManagerInitialized() {341final LogManager owner = this;342if (initializationDone || owner != manager) {343// we don't want to do this twice, and we don't want to do344// this on private manager instances.345return;346}347348// Maybe another thread has called ensureLogManagerInitialized()349// before us and is still executing it. If so we will block until350// the log manager has finished initialized, then acquire the monitor,351// notice that initializationDone is now true and return.352// Otherwise - we have come here first! We will acquire the monitor,353// see that initializationDone is still false, and perform the354// initialization.355//356configurationLock.lock();357try {358// If initializedCalled is true it means that we're already in359// the process of initializing the LogManager in this thread.360// There has been a recursive call to ensureLogManagerInitialized().361final boolean isRecursiveInitialization = (initializedCalled == true);362363assert initializedCalled || !initializationDone364: "Initialization can't be done if initialized has not been called!";365366if (isRecursiveInitialization || initializationDone) {367// If isRecursiveInitialization is true it means that we're368// already in the process of initializing the LogManager in369// this thread. There has been a recursive call to370// ensureLogManagerInitialized(). We should not proceed as371// it would lead to infinite recursion.372//373// If initializationDone is true then it means the manager374// has finished initializing; just return: we're done.375return;376}377// Calling addLogger below will in turn call requiresDefaultLogger()378// which will call ensureLogManagerInitialized().379// We use initializedCalled to break the recursion.380initializedCalled = true;381try {382AccessController.doPrivileged(new PrivilegedAction<Object>() {383@Override384public Object run() {385assert rootLogger == null;386assert initializedCalled && !initializationDone;387388// create root logger before reading primordial389// configuration - to ensure that it will be added390// before the global logger, and not after.391final Logger root = owner.rootLogger = owner.new RootLogger();392393// Read configuration.394owner.readPrimordialConfiguration();395396// Create and retain Logger for the root of the namespace.397owner.addLogger(root);398399// Initialize level if not yet initialized400if (!root.isLevelInitialized()) {401root.setLevel(defaultLevel);402}403404// Adding the global Logger.405// Do not call Logger.getGlobal() here as this might trigger406// subtle inter-dependency issues.407@SuppressWarnings("deprecation")408final Logger global = Logger.global;409410// Make sure the global logger will be registered in the411// global manager412owner.addLogger(global);413return null;414}415});416} finally {417initializationDone = true;418}419} finally {420configurationLock.unlock();421}422}423424/**425* Returns the global LogManager object.426* @return the global LogManager object427*/428public static LogManager getLogManager() {429if (manager != null) {430manager.ensureLogManagerInitialized();431}432return manager;433}434435private void readPrimordialConfiguration() { // must be called while holding configurationLock436if (!readPrimordialConfiguration) {437// If System.in/out/err are null, it's a good438// indication that we're still in the439// bootstrapping phase440if (System.out == null) {441return;442}443readPrimordialConfiguration = true;444try {445readConfiguration();446447// Platform loggers begin to delegate to java.util.logging.Logger448jdk.internal.logger.BootstrapLogger.redirectTemporaryLoggers();449450} catch (Exception ex) {451assert false : "Exception raised while reading logging configuration: " + ex;452}453}454}455456// LoggerContext maps from AppContext457private WeakHashMap<Object, LoggerContext> contextsMap = null;458459// Returns the LoggerContext for the user code (i.e. application or AppContext).460// Loggers are isolated from each AppContext.461private LoggerContext getUserContext() {462LoggerContext context = null;463464@SuppressWarnings("removal")465SecurityManager sm = System.getSecurityManager();466JavaAWTAccess javaAwtAccess = SharedSecrets.getJavaAWTAccess();467if (sm != null && javaAwtAccess != null) {468// for each applet, it has its own LoggerContext isolated from others469final Object ecx = javaAwtAccess.getAppletContext();470if (ecx != null) {471synchronized (javaAwtAccess) {472// find the AppContext of the applet code473// will be null if we are in the main app context.474if (contextsMap == null) {475contextsMap = new WeakHashMap<>();476}477context = contextsMap.get(ecx);478if (context == null) {479// Create a new LoggerContext for the applet.480context = new LoggerContext();481contextsMap.put(ecx, context);482}483}484}485}486// for standalone app, return userContext487return context != null ? context : userContext;488}489490// The system context.491final LoggerContext getSystemContext() {492return systemContext;493}494495private List<LoggerContext> contexts() {496List<LoggerContext> cxs = new ArrayList<>();497cxs.add(getSystemContext());498cxs.add(getUserContext());499return cxs;500}501502// Find or create a specified logger instance. If a logger has503// already been created with the given name it is returned.504// Otherwise a new logger instance is created and registered505// in the LogManager global namespace.506// This method will always return a non-null Logger object.507// Synchronization is not required here. All synchronization for508// adding a new Logger object is handled by addLogger().509//510// This method must delegate to the LogManager implementation to511// add a new Logger or return the one that has been added previously512// as a LogManager subclass may override the addLogger, getLogger,513// readConfiguration, and other methods.514Logger demandLogger(String name, String resourceBundleName, Class<?> caller) {515final Module module = caller == null ? null : caller.getModule();516return demandLogger(name, resourceBundleName, module);517}518519Logger demandLogger(String name, String resourceBundleName, Module module) {520Logger result = getLogger(name);521if (result == null) {522// only allocate the new logger once523Logger newLogger = new Logger(name, resourceBundleName,524module, this, false);525do {526if (addLogger(newLogger)) {527// We successfully added the new Logger that we528// created above so return it without refetching.529return newLogger;530}531532// We didn't add the new Logger that we created above533// because another thread added a Logger with the same534// name after our null check above and before our call535// to addLogger(). We have to refetch the Logger because536// addLogger() returns a boolean instead of the Logger537// reference itself. However, if the thread that created538// the other Logger is not holding a strong reference to539// the other Logger, then it is possible for the other540// Logger to be GC'ed after we saw it in addLogger() and541// before we can refetch it. If it has been GC'ed then542// we'll just loop around and try again.543result = getLogger(name);544} while (result == null);545}546return result;547}548549Logger demandSystemLogger(String name, String resourceBundleName, Class<?> caller) {550final Module module = caller == null ? null : caller.getModule();551return demandSystemLogger(name, resourceBundleName, module);552}553554@SuppressWarnings("removal")555Logger demandSystemLogger(String name, String resourceBundleName, Module module) {556// Add a system logger in the system context's namespace557final Logger sysLogger = getSystemContext()558.demandLogger(name, resourceBundleName, module);559560// Add the system logger to the LogManager's namespace if not exist561// so that there is only one single logger of the given name.562// System loggers are visible to applications unless a logger of563// the same name has been added.564Logger logger;565do {566// First attempt to call addLogger instead of getLogger567// This would avoid potential bug in custom LogManager.getLogger568// implementation that adds a logger if does not exist569if (addLogger(sysLogger)) {570// successfully added the new system logger571logger = sysLogger;572} else {573logger = getLogger(name);574}575} while (logger == null);576577// LogManager will set the sysLogger's handlers via LogManager.addLogger method.578if (logger != sysLogger) {579// if logger already exists we merge the two logger configurations.580final Logger l = logger;581AccessController.doPrivileged(new PrivilegedAction<Void>() {582@Override583public Void run() {584l.mergeWithSystemLogger(sysLogger);585return null;586}587});588}589return sysLogger;590}591592// LoggerContext maintains the logger namespace per context.593// The default LogManager implementation has one system context and user594// context. The system context is used to maintain the namespace for595// all system loggers and is queried by the system code. If a system logger596// doesn't exist in the user context, it'll also be added to the user context.597// The user context is queried by the user code and all other loggers are598// added in the user context.599class LoggerContext {600// Table of named Loggers that maps names to Loggers.601private final ConcurrentHashMap<String,LoggerWeakRef> namedLoggers =602new ConcurrentHashMap<>();603// Tree of named Loggers604private final LogNode root;605private LoggerContext() {606this.root = new LogNode(null, this);607}608609610// Tells whether default loggers are required in this context.611// If true, the default loggers will be lazily added.612final boolean requiresDefaultLoggers() {613final boolean requiresDefaultLoggers = (getOwner() == manager);614if (requiresDefaultLoggers) {615getOwner().ensureLogManagerInitialized();616}617return requiresDefaultLoggers;618}619620// This context's LogManager.621final LogManager getOwner() {622return LogManager.this;623}624625// This context owner's root logger, which if not null, and if626// the context requires default loggers, will be added to the context627// logger's tree.628final Logger getRootLogger() {629return getOwner().rootLogger;630}631632// The global logger, which if not null, and if633// the context requires default loggers, will be added to the context634// logger's tree.635final Logger getGlobalLogger() {636@SuppressWarnings("deprecation") // avoids initialization cycles.637final Logger global = Logger.global;638return global;639}640641Logger demandLogger(String name, String resourceBundleName, Module module) {642// a LogManager subclass may have its own implementation to add and643// get a Logger. So delegate to the LogManager to do the work.644final LogManager owner = getOwner();645return owner.demandLogger(name, resourceBundleName, module);646}647648649// Due to subtle deadlock issues getUserContext() no longer650// calls addLocalLogger(rootLogger);651// Therefore - we need to add the default loggers later on.652// Checks that the context is properly initialized653// This is necessary before calling e.g. find(name)654// or getLoggerNames()655//656private void ensureInitialized() {657if (requiresDefaultLoggers()) {658// Ensure that the root and global loggers are set.659ensureDefaultLogger(getRootLogger());660ensureDefaultLogger(getGlobalLogger());661}662}663664665Logger findLogger(String name) {666// Attempt to find logger without locking.667LoggerWeakRef ref = namedLoggers.get(name);668Logger logger = ref == null ? null : ref.get();669670// if logger is not null, then we can return it right away.671// if name is "" or "global" and logger is null672// we need to fall through and check that this context is673// initialized.674// if ref is not null and logger is null we also need to675// fall through.676if (logger != null || (ref == null && !name.isEmpty()677&& !name.equals(Logger.GLOBAL_LOGGER_NAME))) {678return logger;679}680681// We either found a stale reference, or we were looking for682// "" or "global" and didn't find them.683// Make sure context is initialized (has the default loggers),684// and look up again, cleaning the stale reference if it hasn't685// been cleaned up in between. All this needs to be done inside686// a synchronized block.687synchronized(this) {688// ensure that this context is properly initialized before689// looking for loggers.690ensureInitialized();691ref = namedLoggers.get(name);692if (ref == null) {693return null;694}695logger = ref.get();696if (logger == null) {697// The namedLoggers map holds stale weak reference698// to a logger which has been GC-ed.699ref.dispose();700}701return logger;702}703}704705// This method is called before adding a logger to the706// context.707// 'logger' is the context that will be added.708// This method will ensure that the defaults loggers are added709// before adding 'logger'.710//711private void ensureAllDefaultLoggers(Logger logger) {712if (requiresDefaultLoggers()) {713final String name = logger.getName();714if (!name.isEmpty()) {715ensureDefaultLogger(getRootLogger());716if (!Logger.GLOBAL_LOGGER_NAME.equals(name)) {717ensureDefaultLogger(getGlobalLogger());718}719}720}721}722723private void ensureDefaultLogger(Logger logger) {724// Used for lazy addition of root logger and global logger725// to a LoggerContext.726727// This check is simple sanity: we do not want that this728// method be called for anything else than Logger.global729// or owner.rootLogger.730if (!requiresDefaultLoggers() || logger == null731|| logger != getGlobalLogger() && logger != LogManager.this.rootLogger ) {732733// the case where we have a non null logger which is neither734// Logger.global nor manager.rootLogger indicates a serious735// issue - as ensureDefaultLogger should never be called736// with any other loggers than one of these two (or null - if737// e.g manager.rootLogger is not yet initialized)...738assert logger == null;739740return;741}742743// Adds the logger if it's not already there.744if (!namedLoggers.containsKey(logger.getName())) {745// It is important to prevent addLocalLogger to746// call ensureAllDefaultLoggers when we're in the process747// off adding one of those default loggers - as this would748// immediately cause a stack overflow.749// Therefore we must pass addDefaultLoggersIfNeeded=false,750// even if requiresDefaultLoggers is true.751addLocalLogger(logger, false);752}753}754755boolean addLocalLogger(Logger logger) {756// no need to add default loggers if it's not required757return addLocalLogger(logger, requiresDefaultLoggers());758}759760// Add a logger to this context. This method will only set its level761// and process parent loggers. It doesn't set its handlers.762synchronized boolean addLocalLogger(Logger logger, boolean addDefaultLoggersIfNeeded) {763// addDefaultLoggersIfNeeded serves to break recursion when adding764// default loggers. If we're adding one of the default loggers765// (we're being called from ensureDefaultLogger()) then766// addDefaultLoggersIfNeeded will be false: we don't want to767// call ensureAllDefaultLoggers again.768//769// Note: addDefaultLoggersIfNeeded can also be false when770// requiresDefaultLoggers is false - since calling771// ensureAllDefaultLoggers would have no effect in this case.772if (addDefaultLoggersIfNeeded) {773ensureAllDefaultLoggers(logger);774}775776final String name = logger.getName();777if (name == null) {778throw new NullPointerException();779}780LoggerWeakRef ref = namedLoggers.get(name);781if (ref != null) {782if (ref.refersTo(null)) {783// It's possible that the Logger was GC'ed after a784// drainLoggerRefQueueBounded() call above so allow785// a new one to be registered.786ref.dispose();787} else {788// We already have a registered logger with the given name.789return false;790}791}792793// We're adding a new logger.794// Note that we are creating a weak reference here.795final LogManager owner = getOwner();796logger.setLogManager(owner);797ref = owner.new LoggerWeakRef(logger);798799// Apply any initial level defined for the new logger, unless800// the logger's level is already initialized801Level level = owner.getLevelProperty(name + ".level", null);802if (level != null && !logger.isLevelInitialized()) {803doSetLevel(logger, level);804}805806// instantiation of the handler is done in the LogManager.addLogger807// implementation as a handler class may be only visible to LogManager808// subclass for the custom log manager case809processParentHandlers(logger, name, VisitedLoggers.NEVER);810811// Find the new node and its parent.812LogNode node = getNode(name);813node.loggerRef = ref;814Logger parent = null;815LogNode nodep = node.parent;816while (nodep != null) {817LoggerWeakRef nodeRef = nodep.loggerRef;818if (nodeRef != null) {819parent = nodeRef.get();820if (parent != null) {821break;822}823}824nodep = nodep.parent;825}826827if (parent != null) {828doSetParent(logger, parent);829}830// Walk over the children and tell them we are their new parent.831node.walkAndSetParent(logger);832// new LogNode is ready so tell the LoggerWeakRef about it833ref.setNode(node);834835// Do not publish 'ref' in namedLoggers before the logger tree836// is fully updated - because the named logger will be visible as837// soon as it is published in namedLoggers (findLogger takes838// benefit of the ConcurrentHashMap implementation of namedLoggers839// to avoid synchronizing on retrieval when that is possible).840namedLoggers.put(name, ref);841return true;842}843844void removeLoggerRef(String name, LoggerWeakRef ref) {845namedLoggers.remove(name, ref);846}847848synchronized Enumeration<String> getLoggerNames() {849// ensure that this context is properly initialized before850// returning logger names.851ensureInitialized();852return Collections.enumeration(namedLoggers.keySet());853}854855// If logger.getUseParentHandlers() returns 'true' and any of the logger's856// parents have levels or handlers defined, make sure they are instantiated.857@SuppressWarnings("removal")858private void processParentHandlers(final Logger logger, final String name,859Predicate<Logger> visited) {860final LogManager owner = getOwner();861AccessController.doPrivileged(new PrivilegedAction<Void>() {862@Override863public Void run() {864if (logger != owner.rootLogger) {865boolean useParent = owner.getBooleanProperty(name + ".useParentHandlers", true);866if (!useParent) {867logger.setUseParentHandlers(false);868}869}870return null;871}872});873874int ix = 1;875for (;;) {876int ix2 = name.indexOf('.', ix);877if (ix2 < 0) {878break;879}880String pname = name.substring(0, ix2);881if (owner.getProperty(pname + ".level") != null ||882owner.getProperty(pname + ".handlers") != null) {883// This pname has a level/handlers definition.884// Make sure it exists.885if (visited.test(demandLogger(pname, null, null))) {886break;887}888}889ix = ix2+1;890}891}892893// Gets a node in our tree of logger nodes.894// If necessary, create it.895LogNode getNode(String name) {896if (name == null || name.isEmpty()) {897return root;898}899LogNode node = root;900while (name.length() > 0) {901int ix = name.indexOf('.');902String head;903if (ix > 0) {904head = name.substring(0, ix);905name = name.substring(ix + 1);906} else {907head = name;908name = "";909}910if (node.children == null) {911node.children = new HashMap<>();912}913LogNode child = node.children.get(head);914if (child == null) {915child = new LogNode(node, this);916node.children.put(head, child);917}918node = child;919}920return node;921}922}923924final class SystemLoggerContext extends LoggerContext {925// Add a system logger in the system context's namespace as well as926// in the LogManager's namespace if not exist so that there is only927// one single logger of the given name. System loggers are visible928// to applications unless a logger of the same name has been added.929@Override930Logger demandLogger(String name, String resourceBundleName,931Module module) {932Logger result = findLogger(name);933if (result == null) {934// only allocate the new system logger once935Logger newLogger = new Logger(name, resourceBundleName,936module, getOwner(), true);937do {938if (addLocalLogger(newLogger)) {939// We successfully added the new Logger that we940// created above so return it without refetching.941result = newLogger;942} else {943// We didn't add the new Logger that we created above944// because another thread added a Logger with the same945// name after our null check above and before our call946// to addLogger(). We have to refetch the Logger because947// addLogger() returns a boolean instead of the Logger948// reference itself. However, if the thread that created949// the other Logger is not holding a strong reference to950// the other Logger, then it is possible for the other951// Logger to be GC'ed after we saw it in addLogger() and952// before we can refetch it. If it has been GC'ed then953// we'll just loop around and try again.954result = findLogger(name);955}956} while (result == null);957}958return result;959}960}961962// Add new per logger handlers.963// We need to raise privilege here. All our decisions will964// be made based on the logging configuration, which can965// only be modified by trusted code.966@SuppressWarnings("removal")967private void loadLoggerHandlers(final Logger logger, final String name,968final String handlersPropertyName)969{970AccessController.doPrivileged(new PrivilegedAction<Void>() {971@Override972public Void run() {973setLoggerHandlers(logger, name, handlersPropertyName,974createLoggerHandlers(name, handlersPropertyName));975return null;976}977});978}979980private void setLoggerHandlers(final Logger logger, final String name,981final String handlersPropertyName,982List<Handler> handlers)983{984final boolean ensureCloseOnReset = ! handlers.isEmpty()985&& getBooleanProperty(handlersPropertyName + ".ensureCloseOnReset",true);986int count = 0;987for (Handler hdl : handlers) {988logger.addHandler(hdl);989if (++count == 1 && ensureCloseOnReset) {990// add this logger to the closeOnResetLoggers list.991closeOnResetLoggers.addIfAbsent(CloseOnReset.create(logger));992}993}994}995996private List<Handler> createLoggerHandlers(final String name,997final String handlersPropertyName)998{999String names[] = parseClassNames(handlersPropertyName);1000List<Handler> handlers = new ArrayList<>(names.length);1001for (String type : names) {1002try {1003@SuppressWarnings("deprecation")1004Object o = ClassLoader.getSystemClassLoader().loadClass(type).newInstance();1005Handler hdl = (Handler) o;1006// Check if there is a property defining the1007// this handler's level.1008String levs = getProperty(type + ".level");1009if (levs != null) {1010Level l = Level.findLevel(levs);1011if (l != null) {1012hdl.setLevel(l);1013} else {1014// Probably a bad level. Drop through.1015System.err.println("Can't set level for " + type);1016}1017}1018// Add this Handler to the logger1019handlers.add(hdl);1020} catch (Exception ex) {1021System.err.println("Can't load log handler \"" + type + "\"");1022System.err.println("" + ex);1023ex.printStackTrace();1024}1025}10261027return handlers;1028}102910301031// loggerRefQueue holds LoggerWeakRef objects for Logger objects1032// that have been GC'ed.1033private final ReferenceQueue<Logger> loggerRefQueue1034= new ReferenceQueue<>();10351036// Package-level inner class.1037// Helper class for managing WeakReferences to Logger objects.1038//1039// LogManager.namedLoggers1040// - has weak references to all named Loggers1041// - namedLoggers keeps the LoggerWeakRef objects for the named1042// Loggers around until we can deal with the book keeping for1043// the named Logger that is being GC'ed.1044// LogManager.LogNode.loggerRef1045// - has a weak reference to a named Logger1046// - the LogNode will also keep the LoggerWeakRef objects for1047// the named Loggers around; currently LogNodes never go away.1048// Logger.kids1049// - has a weak reference to each direct child Logger; this1050// includes anonymous and named Loggers1051// - anonymous Loggers are always children of the rootLogger1052// which is a strong reference; rootLogger.kids keeps the1053// LoggerWeakRef objects for the anonymous Loggers around1054// until we can deal with the book keeping.1055//1056final class LoggerWeakRef extends WeakReference<Logger> {1057private String name; // for namedLoggers cleanup1058private LogNode node; // for loggerRef cleanup1059private WeakReference<Logger> parentRef; // for kids cleanup1060private boolean disposed = false; // avoid calling dispose twice10611062LoggerWeakRef(Logger logger) {1063super(logger, loggerRefQueue);10641065name = logger.getName(); // save for namedLoggers cleanup1066}10671068// dispose of this LoggerWeakRef object1069void dispose() {1070// Avoid calling dispose twice. When a Logger is gc'ed, its1071// LoggerWeakRef will be enqueued.1072// However, a new logger of the same name may be added (or looked1073// up) before the queue is drained. When that happens, dispose()1074// will be called by addLocalLogger() or findLogger().1075// Later when the queue is drained, dispose() will be called again1076// for the same LoggerWeakRef. Marking LoggerWeakRef as disposed1077// avoids processing the data twice (even though the code should1078// now be reentrant).1079synchronized(this) {1080// Note to maintainers:1081// Be careful not to call any method that tries to acquire1082// another lock from within this block - as this would surely1083// lead to deadlocks, given that dispose() can be called by1084// multiple threads, and from within different synchronized1085// methods/blocks.1086if (disposed) return;1087disposed = true;1088}10891090final LogNode n = node;1091if (n != null) {1092// n.loggerRef can only be safely modified from within1093// a lock on LoggerContext. removeLoggerRef is already1094// synchronized on LoggerContext so calling1095// n.context.removeLoggerRef from within this lock is safe.1096synchronized (n.context) {1097// if we have a LogNode, then we were a named Logger1098// so clear namedLoggers weak ref to us1099n.context.removeLoggerRef(name, this);1100name = null; // clear our ref to the Logger's name11011102// LogNode may have been reused - so only clear1103// LogNode.loggerRef if LogNode.loggerRef == this1104if (n.loggerRef == this) {1105n.loggerRef = null; // clear LogNode's weak ref to us1106}1107node = null; // clear our ref to LogNode1108}1109}11101111if (parentRef != null) {1112// this LoggerWeakRef has or had a parent Logger1113Logger parent = parentRef.get();1114if (parent != null) {1115// the parent Logger is still there so clear the1116// parent Logger's weak ref to us1117parent.removeChildLogger(this);1118}1119parentRef = null; // clear our weak ref to the parent Logger1120}1121}11221123// set the node field to the specified value1124void setNode(LogNode node) {1125this.node = node;1126}11271128// set the parentRef field to the specified value1129void setParentRef(WeakReference<Logger> parentRef) {1130this.parentRef = parentRef;1131}1132}11331134// Package-level method.1135// Drain some Logger objects that have been GC'ed.1136//1137// drainLoggerRefQueueBounded() is called by addLogger() below1138// and by Logger.getAnonymousLogger(String) so we'll drain up to1139// MAX_ITERATIONS GC'ed Loggers for every Logger we add.1140//1141// On a WinXP VMware client, a MAX_ITERATIONS value of 400 gives1142// us about a 50/50 mix in increased weak ref counts versus1143// decreased weak ref counts in the AnonLoggerWeakRefLeak test.1144// Here are stats for cleaning up sets of 400 anonymous Loggers:1145// - test duration 1 minute1146// - sample size of 125 sets of 4001147// - average: 1.99 ms1148// - minimum: 0.57 ms1149// - maximum: 25.3 ms1150//1151// The same config gives us a better decreased weak ref count1152// than increased weak ref count in the LoggerWeakRefLeak test.1153// Here are stats for cleaning up sets of 400 named Loggers:1154// - test duration 2 minutes1155// - sample size of 506 sets of 4001156// - average: 0.57 ms1157// - minimum: 0.02 ms1158// - maximum: 10.9 ms1159//1160private static final int MAX_ITERATIONS = 400;1161final void drainLoggerRefQueueBounded() {1162for (int i = 0; i < MAX_ITERATIONS; i++) {1163if (loggerRefQueue == null) {1164// haven't finished loading LogManager yet1165break;1166}11671168LoggerWeakRef ref = (LoggerWeakRef) loggerRefQueue.poll();1169if (ref == null) {1170break;1171}1172// a Logger object has been GC'ed so clean it up1173ref.dispose();1174}1175}11761177/**1178* Add a named logger. This does nothing and returns false if a logger1179* with the same name is already registered.1180* <p>1181* The Logger factory methods call this method to register each1182* newly created Logger.1183* <p>1184* The application should retain its own reference to the Logger1185* object to avoid it being garbage collected. The LogManager1186* may only retain a weak reference.1187*1188* @param logger the new logger.1189* @return true if the argument logger was registered successfully,1190* false if a logger of that name already exists.1191* @throws NullPointerException if the logger name is null.1192*/1193public boolean addLogger(Logger logger) {1194final String name = logger.getName();1195if (name == null) {1196throw new NullPointerException();1197}1198drainLoggerRefQueueBounded();1199LoggerContext cx = getUserContext();1200if (cx.addLocalLogger(logger) || forceLoadHandlers(logger)) {1201// Do we have a per logger handler too?1202// Note: this will add a 200ms penalty1203loadLoggerHandlers(logger, name, name + ".handlers");1204return true;1205} else {1206return false;1207}1208}120912101211// Checks whether the given logger is a special logger1212// that still requires handler initialization.1213// This method will only return true for the root and1214// global loggers and only if called by the thread that1215// performs initialization of the LogManager, during that1216// initialization. Must only be called by addLogger.1217@SuppressWarnings("deprecation")1218private boolean forceLoadHandlers(Logger logger) {1219// Called just after reading the primordial configuration, in1220// the same thread that reads it.1221// The root and global logger would already be present in the context1222// by this point, but we would not have called loadLoggerHandlers1223// yet.1224return (logger == rootLogger || logger == Logger.global)1225&& !initializationDone1226&& initializedCalled1227&& configurationLock.isHeldByCurrentThread();1228}12291230// Private method to set a level on a logger.1231// If necessary, we raise privilege before doing the call.1232@SuppressWarnings("removal")1233private static void doSetLevel(final Logger logger, final Level level) {1234SecurityManager sm = System.getSecurityManager();1235if (sm == null) {1236// There is no security manager, so things are easy.1237logger.setLevel(level);1238return;1239}1240// There is a security manager. Raise privilege before1241// calling setLevel.1242AccessController.doPrivileged(new PrivilegedAction<Object>() {1243@Override1244public Object run() {1245logger.setLevel(level);1246return null;1247}});1248}12491250// Private method to set a parent on a logger.1251// If necessary, we raise privilege before doing the setParent call.1252@SuppressWarnings("removal")1253private static void doSetParent(final Logger logger, final Logger parent) {1254SecurityManager sm = System.getSecurityManager();1255if (sm == null) {1256// There is no security manager, so things are easy.1257logger.setParent(parent);1258return;1259}1260// There is a security manager. Raise privilege before1261// calling setParent.1262AccessController.doPrivileged(new PrivilegedAction<Object>() {1263@Override1264public Object run() {1265logger.setParent(parent);1266return null;1267}});1268}12691270/**1271* Method to find a named logger.1272* <p>1273* Note that since untrusted code may create loggers with1274* arbitrary names this method should not be relied on to1275* find Loggers for security sensitive logging.1276* It is also important to note that the Logger associated with the1277* String {@code name} may be garbage collected at any time if there1278* is no strong reference to the Logger. The caller of this method1279* must check the return value for null in order to properly handle1280* the case where the Logger has been garbage collected.1281*1282* @param name name of the logger1283* @return matching logger or null if none is found1284*/1285public Logger getLogger(String name) {1286return getUserContext().findLogger(name);1287}12881289/**1290* Get an enumeration of known logger names.1291* <p>1292* Note: Loggers may be added dynamically as new classes are loaded.1293* This method only reports on the loggers that are currently registered.1294* It is also important to note that this method only returns the name1295* of a Logger, not a strong reference to the Logger itself.1296* The returned String does nothing to prevent the Logger from being1297* garbage collected. In particular, if the returned name is passed1298* to {@code LogManager.getLogger()}, then the caller must check the1299* return value from {@code LogManager.getLogger()} for null to properly1300* handle the case where the Logger has been garbage collected in the1301* time since its name was returned by this method.1302*1303* @return enumeration of logger name strings1304*/1305public Enumeration<String> getLoggerNames() {1306return getUserContext().getLoggerNames();1307}13081309/**1310* Reads and initializes the logging configuration.1311* <p>1312* If the "java.util.logging.config.class" system property is set, then the1313* property value is treated as a class name. The given class will be1314* loaded, an object will be instantiated, and that object's constructor1315* is responsible for reading in the initial configuration. (That object1316* may use other system properties to control its configuration.) The1317* alternate configuration class can use {@code readConfiguration(InputStream)}1318* to define properties in the LogManager.1319* <p>1320* If "java.util.logging.config.class" system property is <b>not</b> set,1321* then this method will read the initial configuration from a properties1322* file and calls the {@link #readConfiguration(InputStream)} method to initialize1323* the configuration. The "java.util.logging.config.file" system property can be used1324* to specify the properties file that will be read as the initial configuration;1325* if not set, then the LogManager default configuration is used.1326* The default configuration is typically loaded from the1327* properties file "{@code conf/logging.properties}" in the Java installation1328* directory.1329*1330* <p>1331* Any {@linkplain #addConfigurationListener registered configuration1332* listener} will be invoked after the properties are read.1333*1334* @apiNote This {@code readConfiguration} method should only be used for1335* initializing the configuration during LogManager initialization or1336* used with the "java.util.logging.config.class" property.1337* When this method is called after loggers have been created, and1338* the "java.util.logging.config.class" system property is not set, all1339* existing loggers will be {@linkplain #reset() reset}. Then any1340* existing loggers that have a level property specified in the new1341* configuration stream will be {@linkplain1342* Logger#setLevel(java.util.logging.Level) set} to the specified log level.1343* <p>1344* To properly update the logging configuration, use the1345* {@link #updateConfiguration(java.util.function.Function)} or1346* {@link #updateConfiguration(java.io.InputStream, java.util.function.Function)}1347* methods instead.1348*1349* @throws SecurityException if a security manager exists and if1350* the caller does not have LoggingPermission("control").1351* @throws IOException if there are IO problems reading the configuration.1352*/1353public void readConfiguration() throws IOException, SecurityException {1354checkPermission();13551356// if a configuration class is specified, load it and use it.1357String cname = System.getProperty("java.util.logging.config.class");1358if (cname != null) {1359try {1360// Instantiate the named class. It is its constructor's1361// responsibility to initialize the logging configuration, by1362// calling readConfiguration(InputStream) with a suitable stream.1363try {1364Class<?> clz = ClassLoader.getSystemClassLoader().loadClass(cname);1365@SuppressWarnings("deprecation")1366Object witness = clz.newInstance();1367return;1368} catch (ClassNotFoundException ex) {1369Class<?> clz = Thread.currentThread().getContextClassLoader().loadClass(cname);1370@SuppressWarnings("deprecation")1371Object witness = clz.newInstance();1372return;1373}1374} catch (Exception ex) {1375System.err.println("Logging configuration class \"" + cname + "\" failed");1376System.err.println("" + ex);1377// keep going and useful config file.1378}1379}13801381String fname = getConfigurationFileName();1382try (final InputStream in = new FileInputStream(fname)) {1383final BufferedInputStream bin = new BufferedInputStream(in);1384readConfiguration(bin);1385}1386}13871388String getConfigurationFileName() throws IOException {1389String fname = System.getProperty("java.util.logging.config.file");1390if (fname == null) {1391fname = System.getProperty("java.home");1392if (fname == null) {1393throw new Error("Can't find java.home ??");1394}1395fname = Paths.get(fname, "conf", "logging.properties")1396.toAbsolutePath().normalize().toString();1397}1398return fname;1399}14001401/**1402* Reset the logging configuration.1403* <p>1404* For all named loggers, the reset operation removes and closes1405* all Handlers and (except for the root logger) sets the level1406* to {@code null}. The root logger's level is set to {@code Level.INFO}.1407*1408* @apiNote Calling this method also clears the LogManager {@linkplain1409* #getProperty(java.lang.String) properties}. The {@link1410* #updateConfiguration(java.util.function.Function)1411* updateConfiguration(Function)} or1412* {@link #updateConfiguration(java.io.InputStream, java.util.function.Function)1413* updateConfiguration(InputStream, Function)} method can be used to1414* properly update to a new configuration.1415*1416* @throws SecurityException if a security manager exists and if1417* the caller does not have LoggingPermission("control").1418*/14191420public void reset() throws SecurityException {1421checkPermission();14221423List<CloseOnReset> persistent;14241425// We don't want reset() and readConfiguration()1426// to run in parallel1427configurationLock.lock();1428try {1429// install new empty properties1430props = new Properties();1431// make sure we keep the loggers persistent until reset is done.1432// Those are the loggers for which we previously created a1433// handler from the configuration, and we need to prevent them1434// from being gc'ed until those handlers are closed.1435persistent = new ArrayList<>(closeOnResetLoggers);1436closeOnResetLoggers.clear();14371438// if reset has been called from shutdown-hook (Cleaner),1439// or if reset has been called from readConfiguration() which1440// already holds the lock and will change the state itself,1441// then do not change state here...1442if (globalHandlersState != STATE_SHUTDOWN &&1443globalHandlersState != STATE_READING_CONFIG) {1444// ...else user called reset()...1445// Since we are doing a reset we no longer want to initialize1446// the global handlers, if they haven't been initialized yet.1447globalHandlersState = STATE_INITIALIZED;1448}14491450for (LoggerContext cx : contexts()) {1451resetLoggerContext(cx);1452}14531454persistent.clear();1455} finally {1456configurationLock.unlock();1457}1458}14591460private void resetLoggerContext(LoggerContext cx) {1461Enumeration<String> enum_ = cx.getLoggerNames();1462while (enum_.hasMoreElements()) {1463String name = enum_.nextElement();1464Logger logger = cx.findLogger(name);1465if (logger != null) {1466resetLogger(logger);1467}1468}1469}14701471private void closeHandlers(Logger logger) {1472Handler[] targets = logger.getHandlers();1473for (Handler h : targets) {1474logger.removeHandler(h);1475try {1476h.close();1477} catch (Exception ex) {1478// Problems closing a handler? Keep going...1479} catch (Error e) {1480// ignore Errors while shutting down1481if (globalHandlersState != STATE_SHUTDOWN) {1482throw e;1483}1484}1485}1486}14871488// Private method to reset an individual target logger.1489private void resetLogger(Logger logger) {1490// Close all the Logger handlers.1491closeHandlers(logger);14921493// Reset Logger level1494String name = logger.getName();1495if (name != null && name.isEmpty()) {1496// This is the root logger.1497logger.setLevel(defaultLevel);1498} else {1499logger.setLevel(null);1500}1501}15021503// get a list of whitespace separated classnames from a property.1504private String[] parseClassNames(String propertyName) {1505String hands = getProperty(propertyName);1506if (hands == null) {1507return new String[0];1508}1509hands = hands.trim();1510int ix = 0;1511final List<String> result = new ArrayList<>();1512while (ix < hands.length()) {1513int end = ix;1514while (end < hands.length()) {1515if (Character.isWhitespace(hands.charAt(end))) {1516break;1517}1518if (hands.charAt(end) == ',') {1519break;1520}1521end++;1522}1523String word = hands.substring(ix, end);1524ix = end+1;1525word = word.trim();1526if (word.length() == 0) {1527continue;1528}1529result.add(word);1530}1531return result.toArray(new String[result.size()]);1532}15331534/**1535* Reads and initializes the logging configuration from the given input stream.1536*1537* <p>1538* Any {@linkplain #addConfigurationListener registered configuration1539* listener} will be invoked after the properties are read.1540*1541* @apiNote This {@code readConfiguration} method should only be used for1542* initializing the configuration during LogManager initialization or1543* used with the "java.util.logging.config.class" property.1544* When this method is called after loggers have been created, all1545* existing loggers will be {@linkplain #reset() reset}. Then any1546* existing loggers that have a level property specified in the1547* given input stream will be {@linkplain1548* Logger#setLevel(java.util.logging.Level) set} to the specified log level.1549* <p>1550* To properly update the logging configuration, use the1551* {@link #updateConfiguration(java.util.function.Function)} or1552* {@link #updateConfiguration(java.io.InputStream, java.util.function.Function)}1553* method instead.1554*1555* @param ins stream to read properties from1556* @throws SecurityException if a security manager exists and if1557* the caller does not have LoggingPermission("control").1558* @throws IOException if there are problems reading from the stream,1559* or the given stream is not in the1560* {@linkplain java.util.Properties properties file} format.1561*/1562public void readConfiguration(InputStream ins) throws IOException, SecurityException {1563checkPermission();15641565// We don't want reset() and readConfiguration() to run1566// in parallel.1567configurationLock.lock();1568try {1569if (globalHandlersState == STATE_SHUTDOWN) {1570// already in terminal state: don't even bother1571// to read the configuration1572return;1573}15741575// change state to STATE_READING_CONFIG to signal reset() to not change it1576globalHandlersState = STATE_READING_CONFIG;1577try {1578// reset configuration which leaves globalHandlersState at STATE_READING_CONFIG1579// so that while reading configuration, any ongoing logging requests block and1580// wait for the outcome (see the end of this try statement)1581reset();15821583try {1584// Load the properties1585props.load(ins);1586} catch (IllegalArgumentException x) {1587// props.load may throw an IllegalArgumentException if the stream1588// contains malformed Unicode escape sequences.1589// We wrap that in an IOException as readConfiguration is1590// specified to throw IOException if there are problems reading1591// from the stream.1592// Note: new IOException(x.getMessage(), x) allow us to get a more1593// concise error message than new IOException(x);1594throw new IOException(x.getMessage(), x);1595}15961597// Instantiate new configuration objects.1598String names[] = parseClassNames("config");15991600for (String word : names) {1601try {1602Class<?> clz = ClassLoader.getSystemClassLoader().loadClass(word);1603@SuppressWarnings("deprecation")1604Object witness = clz.newInstance();1605} catch (Exception ex) {1606System.err.println("Can't load config class \"" + word + "\"");1607System.err.println("" + ex);1608// ex.printStackTrace();1609}1610}16111612// Set levels on any pre-existing loggers, based on the new properties.1613setLevelsOnExistingLoggers();16141615// Note that we need to reinitialize global handles when1616// they are first referenced.1617globalHandlersState = STATE_UNINITIALIZED;1618} catch (Throwable t) {1619// If there were any trouble, then set state to STATE_INITIALIZED1620// so that no global handlers reinitialization is performed on not fully1621// initialized configuration.1622globalHandlersState = STATE_INITIALIZED;1623// re-throw1624throw t;1625}1626} finally {1627configurationLock.unlock();1628}16291630// should be called out of lock to avoid dead-lock situations1631// when user code is involved1632invokeConfigurationListeners();1633}16341635// This enum enumerate the configuration properties that will be1636// updated on existing loggers when the configuration is updated1637// with LogManager.updateConfiguration().1638//1639// Note that this works properly only for the global LogManager - as1640// Handler and its subclasses get their configuration from1641// LogManager.getLogManager().1642//1643static enum ConfigProperty {1644LEVEL(".level"), HANDLERS(".handlers"), USEPARENT(".useParentHandlers");1645final String suffix;1646final int length;1647private ConfigProperty(String suffix) {1648this.suffix = Objects.requireNonNull(suffix);1649length = suffix.length();1650}16511652public boolean handleKey(String key) {1653if (this == HANDLERS && suffix.substring(1).equals(key)) return true;1654if (this == HANDLERS && suffix.equals(key)) return false;1655return key.endsWith(suffix);1656}1657String key(String loggerName) {1658if (this == HANDLERS && (loggerName == null || loggerName.isEmpty())) {1659return suffix.substring(1);1660}1661return loggerName + suffix;1662}1663String loggerName(String key) {1664assert key.equals(suffix.substring(1)) && this == HANDLERS || key.endsWith(suffix);1665if (this == HANDLERS && suffix.substring(1).equals(key)) return "";1666return key.substring(0, key.length() - length);1667}16681669/**1670* If the property is one that should be updated on existing loggers by1671* updateConfiguration, returns the name of the logger for which the1672* property is configured. Otherwise, returns null.1673* @param property a property key in 'props'1674* @return the name of the logger on which the property is to be set,1675* if the property is one that should be updated on existing1676* loggers, {@code null} otherwise.1677*/1678static String getLoggerName(String property) {1679for (ConfigProperty p : ConfigProperty.ALL) {1680if (p.handleKey(property)) {1681return p.loggerName(property);1682}1683}1684return null; // Not a property that should be updated.1685}16861687/**1688* Find the ConfigProperty corresponding to the given1689* property key (may find none).1690* @param property a property key in 'props'1691* @return An optional containing a ConfigProperty object,1692* if the property is one that should be updated on existing1693* loggers, empty otherwise.1694*/1695static Optional<ConfigProperty> find(String property) {1696return ConfigProperty.ALL.stream()1697.filter(p -> p.handleKey(property))1698.findFirst();1699}17001701/**1702* Returns true if the given property is one that should be updated1703* on existing loggers.1704* Used to filter property name streams.1705* @param property a property key from the configuration.1706* @return true if this property is of interest for updateConfiguration.1707*/1708static boolean matches(String property) {1709return find(property).isPresent();1710}17111712/**1713* Returns true if the new property value is different from the old,1714* and therefore needs to be updated on existing loggers.1715* @param k a property key in the configuration1716* @param previous the old configuration1717* @param next the new configuration1718* @return true if the property is changing value between the two1719* configurations.1720*/1721static boolean needsUpdating(String k, Properties previous, Properties next) {1722final String p = trim(previous.getProperty(k, null));1723final String n = trim(next.getProperty(k, null));1724return ! Objects.equals(p,n);1725}17261727/**1728* Applies the mapping function for the given key to the next1729* configuration.1730* If the mapping function is null then this method does nothing.1731* Otherwise, it calls the mapping function to compute the value1732* that should be associated with {@code key} in the resulting1733* configuration, and applies it to {@code next}.1734* If the mapping function returns {@code null} the key is removed1735* from {@code next}.1736*1737* @param k a property key in the configuration1738* @param previous the old configuration1739* @param next the new configuration (modified by this function)1740* @param mappingFunction the mapping function.1741*/1742static void merge(String k, Properties previous, Properties next,1743BiFunction<String, String, String> mappingFunction) {1744String p = trim(previous.getProperty(k, null));1745String n = trim(next.getProperty(k, null));1746String mapped = trim(mappingFunction.apply(p,n));1747if (!Objects.equals(n, mapped)) {1748if (mapped == null) {1749next.remove(k);1750} else {1751next.setProperty(k, mapped);1752}1753}1754}17551756private static final EnumSet<ConfigProperty> ALL =1757EnumSet.allOf(ConfigProperty.class);1758}17591760// trim the value if not null.1761private static String trim(String value) {1762return value == null ? null : value.trim();1763}17641765/**1766* An object that keep track of loggers we have already visited.1767* Used when updating configuration, to avoid processing the same logger1768* twice.1769*/1770static final class VisitedLoggers implements Predicate<Logger> {1771final IdentityHashMap<Logger,Boolean> visited;1772private VisitedLoggers(IdentityHashMap<Logger,Boolean> visited) {1773this.visited = visited;1774}1775VisitedLoggers() {1776this(new IdentityHashMap<>());1777}1778@Override1779public boolean test(Logger logger) {1780return visited != null && visited.put(logger, Boolean.TRUE) != null;1781}1782public void clear() {1783if (visited != null) visited.clear();1784}17851786// An object that considers that no logger has ever been visited.1787// This is used when processParentHandlers is called from1788// LoggerContext.addLocalLogger1789static final VisitedLoggers NEVER = new VisitedLoggers(null);1790}179117921793/**1794* Type of the modification for a given property. One of SAME, ADDED, CHANGED,1795* or REMOVED.1796*/1797static enum ModType {1798SAME, // property had no value in the old and new conf, or had the1799// same value in both.1800ADDED, // property had no value in the old conf, but has one in the new.1801CHANGED, // property has a different value in the old conf and the new conf.1802REMOVED; // property has no value in the new conf, but had one in the old.1803static ModType of(String previous, String next) {1804if (previous == null && next != null) {1805return ADDED;1806}1807if (next == null && previous != null) {1808return REMOVED;1809}1810if (!Objects.equals(trim(previous), trim(next))) {1811return CHANGED;1812}1813return SAME;1814}1815}18161817/**1818* Updates the logging configuration.1819* <p>1820* If the "java.util.logging.config.file" system property is set,1821* then the property value specifies the properties file to be read1822* as the new configuration. Otherwise, the LogManager default1823* configuration is used.1824* <br>The default configuration is typically loaded from the1825* properties file "{@code conf/logging.properties}" in the1826* Java installation directory.1827* <p>1828* This method reads the new configuration and calls the {@link1829* #updateConfiguration(java.io.InputStream, java.util.function.Function)1830* updateConfiguration(ins, mapper)} method to1831* update the configuration.1832*1833* @apiNote1834* This method updates the logging configuration from reading1835* a properties file and ignores the "java.util.logging.config.class"1836* system property. The "java.util.logging.config.class" property is1837* only used by the {@link #readConfiguration()} method to load a custom1838* configuration class as an initial configuration.1839*1840* @param mapper a functional interface that takes a configuration1841* key <i>k</i> and returns a function <i>f(o,n)</i> whose returned1842* value will be applied to the resulting configuration. The1843* function <i>f</i> may return {@code null} to indicate that the property1844* <i>k</i> will not be added to the resulting configuration.1845* <br>1846* If {@code mapper} is {@code null} then {@code (k) -> ((o, n) -> n)} is1847* assumed.1848* <br>1849* For each <i>k</i>, the mapped function <i>f</i> will1850* be invoked with the value associated with <i>k</i> in the old1851* configuration (i.e <i>o</i>) and the value associated with1852* <i>k</i> in the new configuration (i.e. <i>n</i>).1853* <br>A {@code null} value for <i>o</i> or <i>n</i> indicates that no1854* value was present for <i>k</i> in the corresponding configuration.1855*1856* @throws SecurityException if a security manager exists and if1857* the caller does not have LoggingPermission("control"), or1858* does not have the permissions required to set up the1859* configuration (e.g. open file specified for FileHandlers1860* etc...)1861*1862* @throws NullPointerException if {@code mapper} returns a {@code null}1863* function when invoked.1864*1865* @throws IOException if there are problems reading from the1866* logging configuration file.1867*1868* @see #updateConfiguration(java.io.InputStream, java.util.function.Function)1869* @since 91870*/1871public void updateConfiguration(Function<String, BiFunction<String,String,String>> mapper)1872throws IOException {1873checkPermission();1874ensureLogManagerInitialized();1875drainLoggerRefQueueBounded();18761877String fname = getConfigurationFileName();1878try (final InputStream in = new FileInputStream(fname)) {1879final BufferedInputStream bin = new BufferedInputStream(in);1880updateConfiguration(bin, mapper);1881}1882}18831884/**1885* Updates the logging configuration.1886* <p>1887* For each configuration key in the {@linkplain1888* #getProperty(java.lang.String) existing configuration} and1889* the given input stream configuration, the given {@code mapper} function1890* is invoked to map from the configuration key to a function,1891* <i>f(o,n)</i>, that takes the old value and new value and returns1892* the resulting value to be applied in the resulting configuration,1893* as specified in the table below.1894* <p>Let <i>k</i> be a configuration key in the old or new configuration,1895* <i>o</i> be the old value (i.e. the value associated1896* with <i>k</i> in the old configuration), <i>n</i> be the1897* new value (i.e. the value associated with <i>k</i> in the new1898* configuration), and <i>f</i> be the function returned1899* by {@code mapper.apply(}<i>k</i>{@code )}: then <i>v = f(o,n)</i> is the1900* resulting value. If <i>v</i> is not {@code null}, then a property1901* <i>k</i> with value <i>v</i> will be added to the resulting configuration.1902* Otherwise, it will be omitted.1903* <br>A {@code null} value may be passed to function1904* <i>f</i> to indicate that the corresponding configuration has no1905* configuration key <i>k</i>.1906* The function <i>f</i> may return {@code null} to indicate that1907* there will be no value associated with <i>k</i> in the resulting1908* configuration.1909* <p>1910* If {@code mapper} is {@code null}, then <i>v</i> will be set to1911* <i>n</i>.1912* <p>1913* LogManager {@linkplain #getProperty(java.lang.String) properties} are1914* updated with the resulting value in the resulting configuration.1915* <p>1916* The registered {@linkplain #addConfigurationListener configuration1917* listeners} will be invoked after the configuration is successfully updated.1918* <br><br>1919* <table class="striped">1920* <caption style="display:none">Updating configuration properties</caption>1921* <thead>1922* <tr>1923* <th scope="col">Property</th>1924* <th scope="col">Resulting Behavior</th>1925* </tr>1926* </thead>1927* <tbody>1928* <tr>1929* <th scope="row" style="vertical-align:top">{@code <logger>.level}</th>1930* <td>1931* <ul>1932* <li>If the resulting configuration defines a level for a logger and1933* if the resulting level is different than the level specified in the1934* the old configuration, or not specified in1935* the old configuration, then if the logger exists or if children for1936* that logger exist, the level for that logger will be updated,1937* and the change propagated to any existing logger children.1938* This may cause the logger to be created, if necessary.1939* </li>1940* <li>If the old configuration defined a level for a logger, and the1941* resulting configuration doesn't, then this change will not be1942* propagated to existing loggers, if any.1943* To completely replace a configuration - the caller should therefore1944* call {@link #reset() reset} to empty the current configuration,1945* before calling {@code updateConfiguration}.1946* </li>1947* </ul>1948* </td>1949* <tr>1950* <th scope="row" style="vertical-align:top">{@code <logger>.useParentHandlers}</th>1951* <td>1952* <ul>1953* <li>If either the resulting or the old value for the useParentHandlers1954* property is not null, then if the logger exists or if children for1955* that logger exist, that logger will be updated to the resulting1956* value.1957* The value of the useParentHandlers property is the value specified1958* in the configuration; if not specified, the default is true.1959* </li>1960* </ul>1961* </td>1962* </tr>1963* <tr>1964* <th scope="row" style="vertical-align:top">{@code <logger>.handlers}</th>1965* <td>1966* <ul>1967* <li>If the resulting configuration defines a list of handlers for a1968* logger, and if the resulting list is different than the list1969* specified in the old configuration for that logger (that could be1970* empty), then if the logger exists or its children exist, the1971* handlers associated with that logger are closed and removed and1972* the new handlers will be created per the resulting configuration1973* and added to that logger, creating that logger if necessary.1974* </li>1975* <li>If the old configuration defined some handlers for a logger, and1976* the resulting configuration doesn't, if that logger exists,1977* its handlers will be removed and closed.1978* </li>1979* <li>Changing the list of handlers on an existing logger will cause all1980* its previous handlers to be removed and closed, regardless of whether1981* they had been created from the configuration or programmatically.1982* The old handlers will be replaced by new handlers, if any.1983* </li>1984* </ul>1985* </td>1986* </tr>1987* <tr>1988* <th scope="row" style="vertical-align:top">{@code <handler-name>.*}</th>1989* <td>1990* <ul>1991* <li>Properties configured/changed on handler classes will only affect1992* newly created handlers. If a node is configured with the same list1993* of handlers in the old and the resulting configuration, then these1994* handlers will remain unchanged.1995* </li>1996* </ul>1997* </td>1998* </tr>1999* <tr>2000* <th scope="row" style="vertical-align:top">{@code config} and any other property</th>2001* <td>2002* <ul>2003* <li>The resulting value for these property will be stored in the2004* LogManager properties, but {@code updateConfiguration} will not parse2005* or process their values.2006* </li>2007* </ul>2008* </td>2009* </tr>2010* </tbody>2011* </table>2012* <p>2013* <em>Example mapper functions:</em>2014* <br><br>2015* <ul>2016* <li>Replace all logging properties with the new configuration:2017* <br><br>{@code (k) -> ((o, n) -> n)}:2018* <br><br>this is equivalent to passing a null {@code mapper} parameter.2019* </li>2020* <li>Merge the new configuration and old configuration and use the2021* new value if <i>k</i> exists in the new configuration:2022* <br><br>{@code (k) -> ((o, n) -> n == null ? o : n)}:2023* <br><br>as if merging two collections as follows:2024* {@code result.putAll(oldc); result.putAll(newc)}.<br></li>2025* <li>Merge the new configuration and old configuration and use the old2026* value if <i>k</i> exists in the old configuration:2027* <br><br>{@code (k) -> ((o, n) -> o == null ? n : o)}:2028* <br><br>as if merging two collections as follows:2029* {@code result.putAll(newc); result.putAll(oldc)}.<br></li>2030* <li>Replace all properties with the new configuration except the handler2031* property to configure Logger's handler that is not root logger:2032* <br>2033* <pre>{@code (k) -> k.endsWith(".handlers")}2034* {@code ? ((o, n) -> (o == null ? n : o))}2035* {@code : ((o, n) -> n)}</pre>2036* </li>2037* </ul>2038* <p>2039* To completely reinitialize a configuration, an application can first call2040* {@link #reset() reset} to fully remove the old configuration, followed by2041* {@code updateConfiguration} to initialize the new configuration.2042*2043* @param ins a stream to read properties from2044* @param mapper a functional interface that takes a configuration2045* key <i>k</i> and returns a function <i>f(o,n)</i> whose returned2046* value will be applied to the resulting configuration. The2047* function <i>f</i> may return {@code null} to indicate that the property2048* <i>k</i> will not be added to the resulting configuration.2049* <br>2050* If {@code mapper} is {@code null} then {@code (k) -> ((o, n) -> n)} is2051* assumed.2052* <br>2053* For each <i>k</i>, the mapped function <i>f</i> will2054* be invoked with the value associated with <i>k</i> in the old2055* configuration (i.e <i>o</i>) and the value associated with2056* <i>k</i> in the new configuration (i.e. <i>n</i>).2057* <br>A {@code null} value for <i>o</i> or <i>n</i> indicates that no2058* value was present for <i>k</i> in the corresponding configuration.2059*2060* @throws SecurityException if a security manager exists and if2061* the caller does not have LoggingPermission("control"), or2062* does not have the permissions required to set up the2063* configuration (e.g. open files specified for FileHandlers)2064*2065* @throws NullPointerException if {@code ins} is null or if2066* {@code mapper} returns a null function when invoked.2067*2068* @throws IOException if there are problems reading from the stream,2069* or the given stream is not in the2070* {@linkplain java.util.Properties properties file} format.2071* @since 92072*/2073public void updateConfiguration(InputStream ins,2074Function<String, BiFunction<String,String,String>> mapper)2075throws IOException {2076checkPermission();2077ensureLogManagerInitialized();2078drainLoggerRefQueueBounded();20792080final Properties previous;2081final Set<String> updatePropertyNames;2082List<LoggerContext> cxs = Collections.emptyList();2083final VisitedLoggers visited = new VisitedLoggers();2084final Properties next = new Properties();20852086try {2087// Load the properties2088next.load(ins);2089} catch (IllegalArgumentException x) {2090// props.load may throw an IllegalArgumentException if the stream2091// contains malformed Unicode escape sequences.2092// We wrap that in an IOException as updateConfiguration is2093// specified to throw IOException if there are problems reading2094// from the stream.2095// Note: new IOException(x.getMessage(), x) allow us to get a more2096// concise error message than new IOException(x);2097throw new IOException(x.getMessage(), x);2098}20992100if (globalHandlersState == STATE_SHUTDOWN) return;21012102// exclusive lock: readConfiguration/reset/updateConfiguration can't2103// run concurrently.2104// configurationLock.writeLock().lock();2105configurationLock.lock();2106try {2107if (globalHandlersState == STATE_SHUTDOWN) return;2108previous = props;21092110// Builds a TreeSet of all (old and new) property names.2111updatePropertyNames =2112Stream.concat(previous.stringPropertyNames().stream(),2113next.stringPropertyNames().stream())2114.collect(Collectors.toCollection(TreeSet::new));21152116if (mapper != null) {2117// mapper will potentially modify the content of2118// 'next', so we need to call it before affecting props=next.2119// give a chance to the mapper to control all2120// properties - not just those we will reset.2121updatePropertyNames.stream()2122.forEachOrdered(k -> ConfigProperty2123.merge(k, previous, next,2124Objects.requireNonNull(mapper.apply(k))));2125}21262127props = next;21282129// allKeys will contain all keys:2130// - which correspond to a configuration property we are interested in2131// (first filter)2132// - whose value needs to be updated (because it's new, removed, or2133// different) in the resulting configuration (second filter)2134final Stream<String> allKeys = updatePropertyNames.stream()2135.filter(ConfigProperty::matches)2136.filter(k -> ConfigProperty.needsUpdating(k, previous, next));21372138// Group configuration properties by logger name2139// We use a TreeMap so that parent loggers will be visited before2140// child loggers.2141final Map<String, TreeSet<String>> loggerConfigs =2142allKeys.collect(Collectors.groupingBy(ConfigProperty::getLoggerName,2143TreeMap::new,2144Collectors.toCollection(TreeSet::new)));21452146if (!loggerConfigs.isEmpty()) {2147cxs = contexts();2148}2149final List<Logger> loggers = cxs.isEmpty()2150? Collections.emptyList() : new ArrayList<>(cxs.size());2151for (Map.Entry<String, TreeSet<String>> e : loggerConfigs.entrySet()) {2152// This can be a logger name, or something else...2153// The only thing we know is that we found a property2154// we are interested in.2155// For instance, if we found x.y.z.level, then x.y.z could be2156// a logger, but it could also be a handler class...2157// Anyway...2158final String name = e.getKey();2159final Set<String> properties = e.getValue();2160loggers.clear();2161for (LoggerContext cx : cxs) {2162Logger l = cx.findLogger(name);2163if (l != null && !visited.test(l)) {2164loggers.add(l);2165}2166}2167if (loggers.isEmpty()) continue;2168for (String pk : properties) {2169ConfigProperty cp = ConfigProperty.find(pk).get();2170String p = previous.getProperty(pk, null);2171String n = next.getProperty(pk, null);21722173// Determines the type of modification.2174ModType mod = ModType.of(p, n);21752176// mod == SAME means that the two values are equals, there2177// is nothing to do. Usually, this should not happen as such2178// properties should have been filtered above.2179// It could happen however if the properties had2180// trailing/leading whitespaces.2181if (mod == ModType.SAME) continue;21822183switch (cp) {2184case LEVEL:2185if (mod == ModType.REMOVED) continue;2186Level level = Level.findLevel(trim(n));2187if (level != null) {2188if (name.isEmpty()) {2189rootLogger.setLevel(level);2190}2191for (Logger l : loggers) {2192if (!name.isEmpty() || l != rootLogger) {2193l.setLevel(level);2194}2195}2196}2197break;2198case USEPARENT:2199if (!name.isEmpty()) {2200boolean useParent = getBooleanProperty(pk, true);2201if (n != null || p != null) {2202// reset the flag only if the previous value2203// or the new value are not null.2204for (Logger l : loggers) {2205l.setUseParentHandlers(useParent);2206}2207}2208}2209break;2210case HANDLERS:2211List<Handler> hdls = null;2212if (name.isEmpty()) {2213// special handling for the root logger.2214globalHandlersState = STATE_READING_CONFIG;2215try {2216closeHandlers(rootLogger);2217globalHandlersState = STATE_UNINITIALIZED;2218} catch (Throwable t) {2219globalHandlersState = STATE_INITIALIZED;2220throw t;2221}2222}2223for (Logger l : loggers) {2224if (l == rootLogger) continue;2225closeHandlers(l);2226if (mod == ModType.REMOVED) {2227closeOnResetLoggers.removeIf(c -> c.logger == l);2228continue;2229}2230if (hdls == null) {2231hdls = name.isEmpty()2232? Arrays.asList(rootLogger.getHandlers())2233: createLoggerHandlers(name, pk);2234}2235setLoggerHandlers(l, name, pk, hdls);2236}2237break;2238default: break;2239}2240}2241}2242} finally {2243configurationLock.unlock();2244visited.clear();2245}22462247// Now ensure that if an existing logger has acquired a new parent2248// in the configuration, this new parent will be created - if needed,2249// and added to the context of the existing child.2250//2251drainLoggerRefQueueBounded();2252for (LoggerContext cx : cxs) {2253for (Enumeration<String> names = cx.getLoggerNames() ; names.hasMoreElements();) {2254String name = names.nextElement();2255if (name.isEmpty()) continue; // don't need to process parents on root.2256Logger l = cx.findLogger(name);2257if (l != null && !visited.test(l)) {2258// should pass visited here to cut the processing when2259// reaching a logger already visited.2260cx.processParentHandlers(l, name, visited);2261}2262}2263}22642265// We changed the configuration: invoke configuration listeners2266invokeConfigurationListeners();2267}22682269/**2270* Get the value of a logging property.2271* The method returns null if the property is not found.2272* @param name property name2273* @return property value2274*/2275public String getProperty(String name) {2276return props.getProperty(name);2277}22782279// Package private method to get a String property.2280// If the property is not defined we return the given2281// default value.2282String getStringProperty(String name, String defaultValue) {2283String val = getProperty(name);2284if (val == null) {2285return defaultValue;2286}2287return val.trim();2288}22892290// Package private method to get an integer property.2291// If the property is not defined or cannot be parsed2292// we return the given default value.2293int getIntProperty(String name, int defaultValue) {2294String val = getProperty(name);2295if (val == null) {2296return defaultValue;2297}2298try {2299return Integer.parseInt(val.trim());2300} catch (Exception ex) {2301return defaultValue;2302}2303}23042305// Package private method to get a long property.2306// If the property is not defined or cannot be parsed2307// we return the given default value.2308long getLongProperty(String name, long defaultValue) {2309String val = getProperty(name);2310if (val == null) {2311return defaultValue;2312}2313try {2314return Long.parseLong(val.trim());2315} catch (Exception ex) {2316return defaultValue;2317}2318}23192320// Package private method to get a boolean property.2321// If the property is not defined or cannot be parsed2322// we return the given default value.2323boolean getBooleanProperty(String name, boolean defaultValue) {2324String val = getProperty(name);2325if (val == null) {2326return defaultValue;2327}2328val = val.toLowerCase();2329if (val.equals("true") || val.equals("1")) {2330return true;2331} else if (val.equals("false") || val.equals("0")) {2332return false;2333}2334return defaultValue;2335}23362337// Package private method to get a Level property.2338// If the property is not defined or cannot be parsed2339// we return the given default value.2340Level getLevelProperty(String name, Level defaultValue) {2341String val = getProperty(name);2342if (val == null) {2343return defaultValue;2344}2345Level l = Level.findLevel(val.trim());2346return l != null ? l : defaultValue;2347}23482349// Package private method to get a filter property.2350// We return an instance of the class named by the "name"2351// property. If the property is not defined or has problems2352// we return the defaultValue.2353Filter getFilterProperty(String name, Filter defaultValue) {2354String val = getProperty(name);2355try {2356if (val != null) {2357@SuppressWarnings("deprecation")2358Object o = ClassLoader.getSystemClassLoader().loadClass(val).newInstance();2359return (Filter) o;2360}2361} catch (Exception ex) {2362// We got one of a variety of exceptions in creating the2363// class or creating an instance.2364// Drop through.2365}2366// We got an exception. Return the defaultValue.2367return defaultValue;2368}236923702371// Package private method to get a formatter property.2372// We return an instance of the class named by the "name"2373// property. If the property is not defined or has problems2374// we return the defaultValue.2375Formatter getFormatterProperty(String name, Formatter defaultValue) {2376String val = getProperty(name);2377try {2378if (val != null) {2379@SuppressWarnings("deprecation")2380Object o = ClassLoader.getSystemClassLoader().loadClass(val).newInstance();2381return (Formatter) o;2382}2383} catch (Exception ex) {2384// We got one of a variety of exceptions in creating the2385// class or creating an instance.2386// Drop through.2387}2388// We got an exception. Return the defaultValue.2389return defaultValue;2390}23912392// Private method to load the global handlers.2393// We do the real work lazily, when the global handlers2394// are first used.2395private void initializeGlobalHandlers() {2396int state = globalHandlersState;2397if (state == STATE_INITIALIZED ||2398state == STATE_SHUTDOWN) {2399// Nothing to do: return.2400return;2401}24022403// If we have not initialized global handlers yet (or need to2404// reinitialize them), lets do it now (this case is indicated by2405// globalHandlersState == STATE_UNINITIALIZED).2406// If we are in the process of initializing global handlers we2407// also need to lock & wait (this case is indicated by2408// globalHandlersState == STATE_INITIALIZING).2409// If we are in the process of reading configuration we also need to2410// wait to see what the outcome will be (this case2411// is indicated by globalHandlersState == STATE_READING_CONFIG)2412// So in either case we need to wait for the lock.2413configurationLock.lock();2414try {2415if (globalHandlersState != STATE_UNINITIALIZED) {2416return; // recursive call or nothing to do2417}2418// set globalHandlersState to STATE_INITIALIZING first to avoid2419// getting an infinite recursion when loadLoggerHandlers(...)2420// is going to call addHandler(...)2421globalHandlersState = STATE_INITIALIZING;2422try {2423loadLoggerHandlers(rootLogger, null, "handlers");2424} finally {2425globalHandlersState = STATE_INITIALIZED;2426}2427} finally {2428configurationLock.unlock();2429}2430}24312432static final Permission controlPermission =2433new LoggingPermission("control", null);24342435void checkPermission() {2436@SuppressWarnings("removal")2437SecurityManager sm = System.getSecurityManager();2438if (sm != null)2439sm.checkPermission(controlPermission);2440}24412442/**2443* Check that the current context is trusted to modify the logging2444* configuration. This requires LoggingPermission("control").2445* <p>2446* If the check fails we throw a SecurityException, otherwise2447* we return normally.2448*2449* @throws SecurityException if a security manager exists and if2450* the caller does not have LoggingPermission("control").2451* @deprecated This method is only useful in conjunction with2452* {@linkplain SecurityManager the Security Manager}, which is2453* deprecated and subject to removal in a future release.2454* Consequently, this method is also deprecated and subject to2455* removal. There is no replacement for the Security Manager or this2456* method.2457*/2458@Deprecated(since="17", forRemoval=true)2459public void checkAccess() throws SecurityException {2460checkPermission();2461}24622463// Nested class to represent a node in our tree of named loggers.2464private static class LogNode {2465HashMap<String,LogNode> children;2466LoggerWeakRef loggerRef;2467LogNode parent;2468final LoggerContext context;24692470LogNode(LogNode parent, LoggerContext context) {2471this.parent = parent;2472this.context = context;2473}24742475// Recursive method to walk the tree below a node and set2476// a new parent logger.2477void walkAndSetParent(Logger parent) {2478if (children == null) {2479return;2480}2481for (LogNode node : children.values()) {2482LoggerWeakRef ref = node.loggerRef;2483Logger logger = (ref == null) ? null : ref.get();2484if (logger == null) {2485node.walkAndSetParent(parent);2486} else {2487doSetParent(logger, parent);2488}2489}2490}2491}24922493// We use a subclass of Logger for the root logger, so2494// that we only instantiate the global handlers when they2495// are first needed.2496private final class RootLogger extends Logger {2497private RootLogger() {2498// We do not call the protected Logger two args constructor here,2499// to avoid calling LogManager.getLogManager() from within the2500// RootLogger constructor.2501super("", null, null, LogManager.this, true);2502}25032504@Override2505public void log(LogRecord record) {2506// Make sure that the global handlers have been instantiated.2507initializeGlobalHandlers();2508super.log(record);2509}25102511@Override2512public void addHandler(Handler h) {2513initializeGlobalHandlers();2514super.addHandler(h);2515}25162517@Override2518public void removeHandler(Handler h) {2519initializeGlobalHandlers();2520super.removeHandler(h);2521}25222523@Override2524Handler[] accessCheckedHandlers() {2525initializeGlobalHandlers();2526return super.accessCheckedHandlers();2527}2528}252925302531// Private method to be called when the configuration has2532// changed to apply any level settings to any pre-existing loggers.2533private void setLevelsOnExistingLoggers() {2534Enumeration<?> enum_ = props.propertyNames();2535while (enum_.hasMoreElements()) {2536String key = (String)enum_.nextElement();2537if (!key.endsWith(".level")) {2538// Not a level definition.2539continue;2540}2541int ix = key.length() - 6;2542String name = key.substring(0, ix);2543Level level = getLevelProperty(key, null);2544if (level == null) {2545System.err.println("Bad level value for property: " + key);2546continue;2547}2548for (LoggerContext cx : contexts()) {2549Logger l = cx.findLogger(name);2550if (l == null) {2551continue;2552}2553l.setLevel(level);2554}2555}2556}25572558/**2559* String representation of the2560* {@link javax.management.ObjectName} for the management interface2561* for the logging facility.2562*2563* @see java.lang.management.PlatformLoggingMXBean2564*2565* @since 1.52566*/2567public static final String LOGGING_MXBEAN_NAME2568= "java.util.logging:type=Logging";25692570/**2571* Returns {@code LoggingMXBean} for managing loggers.2572*2573* @return a {@link LoggingMXBean} object.2574*2575* @deprecated {@code java.util.logging.LoggingMXBean} is deprecated and2576* replaced with {@code java.lang.management.PlatformLoggingMXBean}. Use2577* {@link java.lang.management.ManagementFactory#getPlatformMXBean(Class)2578* ManagementFactory.getPlatformMXBean}(PlatformLoggingMXBean.class)2579* instead.2580*2581* @see java.lang.management.PlatformLoggingMXBean2582* @since 1.52583*/2584@Deprecated(since="9")2585public static synchronized LoggingMXBean getLoggingMXBean() {2586return Logging.getInstance();2587}25882589/**2590* Adds a configuration listener to be invoked each time the logging2591* configuration is read.2592* If the listener is already registered the method does nothing.2593* <p>2594* The listener is invoked with privileges that are restricted by the2595* calling context of this method.2596* The order in which the listeners are invoked is unspecified.2597* <p>2598* It is recommended that listeners do not throw errors or exceptions.2599*2600* If a listener terminates with an uncaught error or exception then2601* the first exception will be propagated to the caller of2602* {@link #readConfiguration()} (or {@link #readConfiguration(java.io.InputStream)})2603* after all listeners have been invoked.2604*2605* @implNote If more than one listener terminates with an uncaught error or2606* exception, an implementation may record the additional errors or2607* exceptions as {@linkplain Throwable#addSuppressed(java.lang.Throwable)2608* suppressed exceptions}.2609*2610* @param listener A configuration listener that will be invoked after the2611* configuration changed.2612* @return This LogManager.2613* @throws SecurityException if a security manager exists and if the2614* caller does not have LoggingPermission("control").2615* @throws NullPointerException if the listener is null.2616*2617* @since 92618*/2619public LogManager addConfigurationListener(Runnable listener) {2620final Runnable r = Objects.requireNonNull(listener);2621checkPermission();2622@SuppressWarnings("removal")2623final SecurityManager sm = System.getSecurityManager();2624@SuppressWarnings("removal")2625final AccessControlContext acc =2626sm == null ? null : AccessController.getContext();2627final PrivilegedAction<Void> pa =2628acc == null ? null : () -> { r.run() ; return null; };2629@SuppressWarnings("removal")2630final Runnable pr =2631acc == null ? r : () -> AccessController.doPrivileged(pa, acc);2632// Will do nothing if already registered.2633listeners.putIfAbsent(r, pr);2634return this;2635}26362637/**2638* Removes a previously registered configuration listener.2639*2640* Returns silently if the listener is not found.2641*2642* @param listener the configuration listener to remove.2643* @throws NullPointerException if the listener is null.2644* @throws SecurityException if a security manager exists and if the2645* caller does not have LoggingPermission("control").2646*2647* @since 92648*/2649public void removeConfigurationListener(Runnable listener) {2650final Runnable key = Objects.requireNonNull(listener);2651checkPermission();2652listeners.remove(key);2653}26542655private void invokeConfigurationListeners() {2656Throwable t = null;26572658// We're using an IdentityHashMap because we want to compare2659// keys using identity (==).2660// We don't want to loop within a block synchronized on 'listeners'2661// to avoid invoking listeners from yet another synchronized block.2662// So we're taking a snapshot of the values list to avoid the risk of2663// ConcurrentModificationException while looping.2664//2665for (Runnable c : listeners.values().toArray(new Runnable[0])) {2666try {2667c.run();2668} catch (ThreadDeath death) {2669throw death;2670} catch (Error | RuntimeException x) {2671if (t == null) t = x;2672else t.addSuppressed(x);2673}2674}2675// Listeners are not supposed to throw exceptions, but if that2676// happens, we will rethrow the first error or exception that is raised2677// after all listeners have been invoked.2678if (t instanceof Error) throw (Error)t;2679if (t instanceof RuntimeException) throw (RuntimeException)t;2680}26812682/**2683* This class allows the {@link LoggingProviderImpl} to demand loggers on2684* behalf of system and application classes.2685*/2686private static final class LoggingProviderAccess2687implements LoggingProviderImpl.LogManagerAccess,2688PrivilegedAction<Void> {26892690private LoggingProviderAccess() {2691}26922693/**2694* Demands a logger on behalf of the given {@code module}.2695* <p>2696* If a named logger suitable for the given module is found2697* returns it.2698* Otherwise, creates a new logger suitable for the given module.2699*2700* @param name The logger name.2701* @param module The module on which behalf the logger is created/retrieved.2702* @return A logger for the given {@code module}.2703*2704* @throws NullPointerException if {@code name} is {@code null}2705* or {@code module} is {@code null}.2706* @throws IllegalArgumentException if {@code manager} is not the default2707* LogManager.2708* @throws SecurityException if a security manager is present and the2709* calling code doesn't have the2710* {@link LoggingPermission LoggingPermission("demandLogger", null)}.2711*/2712@Override2713public Logger demandLoggerFor(LogManager manager, String name, Module module) {2714if (manager != getLogManager()) {2715// having LogManager as parameter just ensures that the2716// caller will have initialized the LogManager before reaching2717// here.2718throw new IllegalArgumentException("manager");2719}2720Objects.requireNonNull(name);2721Objects.requireNonNull(module);2722@SuppressWarnings("removal")2723SecurityManager sm = System.getSecurityManager();2724if (sm != null) {2725sm.checkPermission(controlPermission);2726}2727if (isSystem(module)) {2728return manager.demandSystemLogger(name,2729Logger.SYSTEM_LOGGER_RB_NAME, module);2730} else {2731return manager.demandLogger(name, null, module);2732}2733}27342735@Override2736public Void run() {2737LoggingProviderImpl.setLogManagerAccess(INSTANCE);2738return null;2739}27402741static final LoggingProviderAccess INSTANCE = new LoggingProviderAccess();2742}27432744static {2745initStatic();2746}27472748@SuppressWarnings("removal")2749private static void initStatic() {2750AccessController.doPrivileged(LoggingProviderAccess.INSTANCE, null,2751controlPermission);2752}27532754}275527562757