1 /*
2  * Copyright (c) 2000, 2021, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.  Oracle designates this
8  * particular file as subject to the "Classpath" exception as provided
9  * by Oracle in the LICENSE file that accompanied this code.
10  *
11  * This code is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14  * version 2 for more details (a copy is included in the LICENSE file that
15  * accompanied this code).
16  *
17  * You should have received a copy of the GNU General Public License version
18  * 2 along with this work; if not, write to the Free Software Foundation,
19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20  *
21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22  * or visit www.oracle.com if you need additional information or have any
23  * questions.
24  */
25 
26 package java.util.logging;
27 
28 import java.io.*;
29 import java.util.*;
30 import java.security.*;
31 import java.lang.ref.ReferenceQueue;
32 import java.lang.ref.WeakReference;
33 import java.util.concurrent.ConcurrentHashMap;
34 import java.nio.file.Paths;
35 import java.util.concurrent.CopyOnWriteArrayList;
36 import java.util.concurrent.locks.ReentrantLock;
37 import java.util.function.BiFunction;
38 import java.util.function.Function;
39 import java.util.function.Predicate;
40 import java.util.stream.Collectors;
41 import java.util.stream.Stream;
42 import jdk.internal.access.JavaAWTAccess;
43 import jdk.internal.access.SharedSecrets;
44 import sun.util.logging.internal.LoggingProviderImpl;
45 import static jdk.internal.logger.DefaultLoggerFinder.isSystem;
46 
47 /**
48  * There is a single global LogManager object that is used to
49  * maintain a set of shared state about Loggers and log services.
50  * <p>
51  * This LogManager object:
52  * <ul>
53  * <li> Manages a hierarchical namespace of Logger objects.  All
54  *      named Loggers are stored in this namespace.
55  * <li> Manages a set of logging control properties.  These are
56  *      simple key-value pairs that can be used by Handlers and
57  *      other logging objects to configure themselves.
58  * </ul>
59  * <p>
60  * The global LogManager object can be retrieved using LogManager.getLogManager().
61  * The LogManager object is created during class initialization and
62  * cannot subsequently be changed.
63  * <p>
64  * At startup the LogManager class is located using the
65  * java.util.logging.manager system property.
66  *
67  * <h2>LogManager Configuration</h2>
68  *
69  * A LogManager initializes the logging configuration via
70  * the {@link #readConfiguration()} method during LogManager initialization.
71  * By default, LogManager default configuration is used.
72  * The logging configuration read by LogManager must be in the
73  * {@linkplain Properties properties file} format.
74  * <p>
75  * The LogManager defines two optional system properties that allow control over
76  * the initial configuration, as specified in the {@link #readConfiguration()}
77  * method:
78  * <ul>
79  * <li>{@systemProperty java.util.logging.config.class}
80  * <li>{@systemProperty java.util.logging.config.file}
81  * </ul>
82  * <p>
83  * These two system properties may be specified on the command line to the "java"
84  * command, or as system property definitions passed to JNI_CreateJavaVM.
85  * <p>
86  * The {@linkplain Properties properties} for loggers and Handlers will have
87  * names starting with the dot-separated name for the handler or logger.<br>
88  * The global logging properties may include:
89  * <ul>
90  * <li>A property "handlers".  This defines a whitespace or comma separated
91  * list of class names for handler classes to load and register as
92  * handlers on the root Logger (the Logger named "").  Each class
93  * name must be for a Handler class which has a default constructor.
94  * Note that these Handlers may be created lazily, when they are
95  * first used.
96  *
97  * <li>A property "&lt;logger&gt;.handlers". This defines a whitespace or
98  * comma separated list of class names for handlers classes to
99  * load and register as handlers to the specified logger. Each class
100  * name must be for a Handler class which has a default constructor.
101  * Note that these Handlers may be created lazily, when they are
102  * first used.
103  *
104  * <li>A property "&lt;logger&gt;.handlers.ensureCloseOnReset". This defines a
105  * a boolean value. If "&lt;logger&gt;.handlers" is not defined or is empty,
106  * this property is ignored. Otherwise it defaults to {@code true}. When the
107  * value is {@code true}, the handlers associated with the logger are guaranteed
108  * to be closed on {@linkplain #reset} and shutdown. This can be turned off
109  * by explicitly setting "&lt;logger&gt;.handlers.ensureCloseOnReset=false" in
110  * the configuration. Note that turning this property off causes the risk of
111  * introducing a resource leak, as the logger may get garbage collected before
112  * {@code reset()} is called, thus preventing its handlers from being closed
113  * on {@code reset()}. In that case it is the responsibility of the application
114  * to ensure that the handlers are closed before the logger is garbage
115  * collected.
116  *
117  * <li>A property "&lt;logger&gt;.useParentHandlers". This defines a boolean
118  * value. By default every logger calls its parent in addition to
119  * handling the logging message itself, this often result in messages
120  * being handled by the root logger as well. When setting this property
121  * to false a Handler needs to be configured for this logger otherwise
122  * no logging messages are delivered.
123  *
124  * <li>A property "config".  This property is intended to allow
125  * arbitrary configuration code to be run.  The property defines a
126  * whitespace or comma separated list of class names.  A new instance will be
127  * created for each named class.  The default constructor of each class
128  * may execute arbitrary code to update the logging configuration, such as
129  * setting logger levels, adding handlers, adding filters, etc.
130  * </ul>
131  * <p>
132  * Note that all classes loaded during LogManager configuration are
133  * first searched on the system class path before any user class path.
134  * That includes the LogManager class, any config classes, and any
135  * handler classes.
136  * <p>
137  * Loggers are organized into a naming hierarchy based on their
138  * dot separated names.  Thus "a.b.c" is a child of "a.b", but
139  * "a.b1" and a.b2" are peers.
140  * <p>
141  * All properties whose names end with ".level" are assumed to define
142  * log levels for Loggers.  Thus "foo.level" defines a log level for
143  * the logger called "foo" and (recursively) for any of its children
144  * in the naming hierarchy.  Log Levels are applied in the order they
145  * are defined in the properties file.  Thus level settings for child
146  * nodes in the tree should come after settings for their parents.
147  * The property name ".level" can be used to set the level for the
148  * root of the tree.
149  * <p>
150  * All methods on the LogManager object are multi-thread safe.
151  *
152  * @since 1.4
153 */
154 
155 public class LogManager {
156 
157     // 'props' is assigned within a lock but accessed without it.
158     // Declaring it volatile makes sure that another thread will not
159     // be able to see a partially constructed 'props' object.
160     // (seeing a partially constructed 'props' object can result in
161     // NPE being thrown in Hashtable.get(), because it leaves the door
162     // open for props.getProperties() to be called before the construcor
163     // of Hashtable is actually completed).
164     private volatile Properties props = new Properties();
165     private static final Level defaultLevel = Level.INFO;
166 
167     // LoggerContext for system loggers and user loggers
168     private final LoggerContext systemContext = new SystemLoggerContext();
169     private final LoggerContext userContext = new LoggerContext();
170     // non final field - make it volatile to make sure that other threads
171     // will see the new value once ensureLogManagerInitialized() has finished
172     // executing.
173     private volatile Logger rootLogger;
174     // Have we done the primordial reading of the configuration file?
175     // (Must be done after a suitable amount of java.lang.System
176     // initialization has been done)
177     private volatile boolean readPrimordialConfiguration;
178     // Have we initialized global (root) handlers yet?
179     // This gets set to STATE_UNINITIALIZED in readConfiguration
180     private static final int
181             STATE_INITIALIZED = 0, // initial state
182             STATE_INITIALIZING = 1,
183             STATE_READING_CONFIG = 2,
184             STATE_UNINITIALIZED = 3,
185             STATE_SHUTDOWN = 4;    // terminal state
186     private volatile int globalHandlersState; // = STATE_INITIALIZED;
187     // A concurrency lock for reset(), readConfiguration() and Cleaner.
188     private final ReentrantLock configurationLock = new ReentrantLock();
189 
190     // This list contains the loggers for which some handlers have been
191     // explicitly configured in the configuration file.
192     // It prevents these loggers from being arbitrarily garbage collected.
193     private static final class CloseOnReset {
194         private final Logger logger;
CloseOnReset(Logger ref)195         private CloseOnReset(Logger ref) {
196             this.logger = Objects.requireNonNull(ref);
197         }
198         @Override
equals(Object other)199         public boolean equals(Object other) {
200             return (other instanceof CloseOnReset) && ((CloseOnReset)other).logger == logger;
201         }
202         @Override
hashCode()203         public int hashCode() {
204             return System.identityHashCode(logger);
205         }
get()206         public Logger get() {
207             return logger;
208         }
create(Logger logger)209         public static CloseOnReset create(Logger logger) {
210             return new CloseOnReset(logger);
211         }
212     }
213     private final CopyOnWriteArrayList<CloseOnReset> closeOnResetLoggers =
214             new CopyOnWriteArrayList<>();
215 
216 
217     private final Map<Object, Runnable> listeners =
218             Collections.synchronizedMap(new IdentityHashMap<>());
219 
220     // The global LogManager object
221     @SuppressWarnings("removal")
222     private static final LogManager manager = AccessController.doPrivileged(
223             new PrivilegedAction<LogManager>() {
224                 @Override
225                 public LogManager run() {
226                     LogManager mgr = null;
227                     String cname = null;
228                     try {
229                         cname = System.getProperty("java.util.logging.manager");
230                         if (cname != null) {
231                             try {
232                                 @SuppressWarnings("deprecation")
233                                 Object tmp = ClassLoader.getSystemClassLoader()
234                                         .loadClass(cname).newInstance();
235                                 mgr = (LogManager) tmp;
236                             } catch (ClassNotFoundException ex) {
237                                 @SuppressWarnings("deprecation")
238                                 Object tmp = Thread.currentThread()
239                                         .getContextClassLoader().loadClass(cname).newInstance();
240                                 mgr = (LogManager) tmp;
241                             }
242                         }
243                     } catch (Exception ex) {
244                         System.err.println("Could not load Logmanager \"" + cname + "\"");
245                         ex.printStackTrace();
246                     }
247                     if (mgr == null) {
248                         mgr = new LogManager();
249                     }
250                     return mgr;
251 
252                 }
253             });
254 
255     // This private class is used as a shutdown hook.
256     // It does a "reset" to close all open handlers.
257     private class Cleaner extends Thread {
258 
Cleaner()259         private Cleaner() {
260             super(null, null, "Logging-Cleaner", 0, false);
261             /* Set context class loader to null in order to avoid
262              * keeping a strong reference to an application classloader.
263              */
264             this.setContextClassLoader(null);
265         }
266 
267         @Override
run()268         public void run() {
269             // This is to ensure the LogManager.<clinit> is completed
270             // before synchronized block. Otherwise deadlocks are possible.
271             LogManager mgr = manager;
272 
273             // set globalHandlersState to STATE_SHUTDOWN atomically so that
274             // no attempts are made to (re)initialize the handlers or (re)read
275             // the configuration again. This is terminal state.
276             configurationLock.lock();
277             globalHandlersState = STATE_SHUTDOWN;
278             configurationLock.unlock();
279 
280             // Do a reset to close all active handlers.
281             reset();
282         }
283     }
284 
285 
286     /**
287      * Protected constructor.  This is protected so that container applications
288      * (such as J2EE containers) can subclass the object.  It is non-public as
289      * it is intended that there only be one LogManager object, whose value is
290      * retrieved by calling LogManager.getLogManager.
291      */
LogManager()292     protected LogManager() {
293         this(checkSubclassPermissions());
294     }
295 
LogManager(Void checked)296     private LogManager(Void checked) {
297 
298         // Add a shutdown hook to close the global handlers.
299         try {
300             Runtime.getRuntime().addShutdownHook(new Cleaner());
301         } catch (IllegalStateException e) {
302             // If the VM is already shutting down,
303             // We do not need to register shutdownHook.
304         }
305     }
306 
checkSubclassPermissions()307     private static Void checkSubclassPermissions() {
308         @SuppressWarnings("removal")
309         final SecurityManager sm = System.getSecurityManager();
310         if (sm != null) {
311             // These permission will be checked in the LogManager constructor,
312             // in order to register the Cleaner() thread as a shutdown hook.
313             // Check them here to avoid the penalty of constructing the object
314             // etc...
315             sm.checkPermission(new RuntimePermission("shutdownHooks"));
316             sm.checkPermission(new RuntimePermission("setContextClassLoader"));
317         }
318         return null;
319     }
320 
321     /**
322      * Lazy initialization: if this instance of manager is the global
323      * manager then this method will read the initial configuration and
324      * add the root logger and global logger by calling addLogger().
325      *
326      * Note that it is subtly different from what we do in LoggerContext.
327      * In LoggerContext we're patching up the logger context tree in order to add
328      * the root and global logger *to the context tree*.
329      *
330      * For this to work, addLogger() must have already have been called
331      * once on the LogManager instance for the default logger being
332      * added.
333      *
334      * This is why ensureLogManagerInitialized() needs to be called before
335      * any logger is added to any logger context.
336      *
337      */
338     private boolean initializedCalled = false;
339     private volatile boolean initializationDone = false;
340     @SuppressWarnings("removal")
ensureLogManagerInitialized()341     final void ensureLogManagerInitialized() {
342         final LogManager owner = this;
343         if (initializationDone || owner != manager) {
344             // we don't want to do this twice, and we don't want to do
345             // this on private manager instances.
346             return;
347         }
348 
349         // Maybe another thread has called ensureLogManagerInitialized()
350         // before us and is still executing it. If so we will block until
351         // the log manager has finished initialized, then acquire the monitor,
352         // notice that initializationDone is now true and return.
353         // Otherwise - we have come here first! We will acquire the monitor,
354         // see that initializationDone is still false, and perform the
355         // initialization.
356         //
357         configurationLock.lock();
358         try {
359             // If initializedCalled is true it means that we're already in
360             // the process of initializing the LogManager in this thread.
361             // There has been a recursive call to ensureLogManagerInitialized().
362             final boolean isRecursiveInitialization = (initializedCalled == true);
363 
364             assert initializedCalled || !initializationDone
365                     : "Initialization can't be done if initialized has not been called!";
366 
367             if (isRecursiveInitialization || initializationDone) {
368                 // If isRecursiveInitialization is true it means that we're
369                 // already in the process of initializing the LogManager in
370                 // this thread. There has been a recursive call to
371                 // ensureLogManagerInitialized(). We should not proceed as
372                 // it would lead to infinite recursion.
373                 //
374                 // If initializationDone is true then it means the manager
375                 // has finished initializing; just return: we're done.
376                 return;
377             }
378             // Calling addLogger below will in turn call requiresDefaultLogger()
379             // which will call ensureLogManagerInitialized().
380             // We use initializedCalled to break the recursion.
381             initializedCalled = true;
382             try {
383                 AccessController.doPrivileged(new PrivilegedAction<Object>() {
384                     @Override
385                     public Object run() {
386                         assert rootLogger == null;
387                         assert initializedCalled && !initializationDone;
388 
389                         // create root logger before reading primordial
390                         // configuration - to ensure that it will be added
391                         // before the global logger, and not after.
392                         final Logger root = owner.rootLogger = owner.new RootLogger();
393 
394                         // Read configuration.
395                         owner.readPrimordialConfiguration();
396 
397                         // Create and retain Logger for the root of the namespace.
398                         owner.addLogger(root);
399 
400                         // Initialize level if not yet initialized
401                         if (!root.isLevelInitialized()) {
402                             root.setLevel(defaultLevel);
403                         }
404 
405                         // Adding the global Logger.
406                         // Do not call Logger.getGlobal() here as this might trigger
407                         // subtle inter-dependency issues.
408                         @SuppressWarnings("deprecation")
409                         final Logger global = Logger.global;
410 
411                         // Make sure the global logger will be registered in the
412                         // global manager
413                         owner.addLogger(global);
414                         return null;
415                     }
416                 });
417             } finally {
418                 initializationDone = true;
419             }
420         } finally {
421             configurationLock.unlock();
422         }
423     }
424 
425     /**
426      * Returns the global LogManager object.
427      * @return the global LogManager object
428      */
getLogManager()429     public static LogManager getLogManager() {
430         if (manager != null) {
431             manager.ensureLogManagerInitialized();
432         }
433         return manager;
434     }
435 
readPrimordialConfiguration()436     private void readPrimordialConfiguration() { // must be called while holding configurationLock
437         if (!readPrimordialConfiguration) {
438             // If System.in/out/err are null, it's a good
439             // indication that we're still in the
440             // bootstrapping phase
441             if (System.out == null) {
442                 return;
443             }
444             readPrimordialConfiguration = true;
445             try {
446                 readConfiguration();
447 
448                 // Platform loggers begin to delegate to java.util.logging.Logger
449                 jdk.internal.logger.BootstrapLogger.redirectTemporaryLoggers();
450 
451             } catch (Exception ex) {
452                 assert false : "Exception raised while reading logging configuration: " + ex;
453             }
454         }
455     }
456 
457     // LoggerContext maps from AppContext
458     private WeakHashMap<Object, LoggerContext> contextsMap = null;
459 
460     // Returns the LoggerContext for the user code (i.e. application or AppContext).
461     // Loggers are isolated from each AppContext.
getUserContext()462     private LoggerContext getUserContext() {
463         LoggerContext context = null;
464 
465         @SuppressWarnings("removal")
466         SecurityManager sm = System.getSecurityManager();
467         JavaAWTAccess javaAwtAccess = SharedSecrets.getJavaAWTAccess();
468         if (sm != null && javaAwtAccess != null) {
469             // for each applet, it has its own LoggerContext isolated from others
470             final Object ecx = javaAwtAccess.getAppletContext();
471             if (ecx != null) {
472                 synchronized (javaAwtAccess) {
473                     // find the AppContext of the applet code
474                     // will be null if we are in the main app context.
475                     if (contextsMap == null) {
476                         contextsMap = new WeakHashMap<>();
477                     }
478                     context = contextsMap.get(ecx);
479                     if (context == null) {
480                         // Create a new LoggerContext for the applet.
481                         context = new LoggerContext();
482                         contextsMap.put(ecx, context);
483                     }
484                 }
485             }
486         }
487         // for standalone app, return userContext
488         return context != null ? context : userContext;
489     }
490 
491     // The system context.
getSystemContext()492     final LoggerContext getSystemContext() {
493         return systemContext;
494     }
495 
contexts()496     private List<LoggerContext> contexts() {
497         List<LoggerContext> cxs = new ArrayList<>();
498         cxs.add(getSystemContext());
499         cxs.add(getUserContext());
500         return cxs;
501     }
502 
503     // Find or create a specified logger instance. If a logger has
504     // already been created with the given name it is returned.
505     // Otherwise a new logger instance is created and registered
506     // in the LogManager global namespace.
507     // This method will always return a non-null Logger object.
508     // Synchronization is not required here. All synchronization for
509     // adding a new Logger object is handled by addLogger().
510     //
511     // This method must delegate to the LogManager implementation to
512     // add a new Logger or return the one that has been added previously
513     // as a LogManager subclass may override the addLogger, getLogger,
514     // readConfiguration, and other methods.
demandLogger(String name, String resourceBundleName, Class<?> caller)515     Logger demandLogger(String name, String resourceBundleName, Class<?> caller) {
516         final Module module = caller == null ? null : caller.getModule();
517         return demandLogger(name, resourceBundleName, module);
518     }
519 
demandLogger(String name, String resourceBundleName, Module module)520     Logger demandLogger(String name, String resourceBundleName, Module module) {
521         Logger result = getLogger(name);
522         if (result == null) {
523             // only allocate the new logger once
524             Logger newLogger = new Logger(name, resourceBundleName,
525                                           module, this, false);
526             do {
527                 if (addLogger(newLogger)) {
528                     // We successfully added the new Logger that we
529                     // created above so return it without refetching.
530                     return newLogger;
531                 }
532 
533                 // We didn't add the new Logger that we created above
534                 // because another thread added a Logger with the same
535                 // name after our null check above and before our call
536                 // to addLogger(). We have to refetch the Logger because
537                 // addLogger() returns a boolean instead of the Logger
538                 // reference itself. However, if the thread that created
539                 // the other Logger is not holding a strong reference to
540                 // the other Logger, then it is possible for the other
541                 // Logger to be GC'ed after we saw it in addLogger() and
542                 // before we can refetch it. If it has been GC'ed then
543                 // we'll just loop around and try again.
544                 result = getLogger(name);
545             } while (result == null);
546         }
547         return result;
548     }
549 
demandSystemLogger(String name, String resourceBundleName, Class<?> caller)550     Logger demandSystemLogger(String name, String resourceBundleName, Class<?> caller) {
551         final Module module = caller == null ? null : caller.getModule();
552         return demandSystemLogger(name, resourceBundleName, module);
553     }
554 
555     @SuppressWarnings("removal")
demandSystemLogger(String name, String resourceBundleName, Module module)556     Logger demandSystemLogger(String name, String resourceBundleName, Module module) {
557         // Add a system logger in the system context's namespace
558         final Logger sysLogger = getSystemContext()
559                 .demandLogger(name, resourceBundleName, module);
560 
561         // Add the system logger to the LogManager's namespace if not exist
562         // so that there is only one single logger of the given name.
563         // System loggers are visible to applications unless a logger of
564         // the same name has been added.
565         Logger logger;
566         do {
567             // First attempt to call addLogger instead of getLogger
568             // This would avoid potential bug in custom LogManager.getLogger
569             // implementation that adds a logger if does not exist
570             if (addLogger(sysLogger)) {
571                 // successfully added the new system logger
572                 logger = sysLogger;
573             } else {
574                 logger = getLogger(name);
575             }
576         } while (logger == null);
577 
578         // LogManager will set the sysLogger's handlers via LogManager.addLogger method.
579         if (logger != sysLogger) {
580             // if logger already exists we merge the two logger configurations.
581             final Logger l = logger;
582             AccessController.doPrivileged(new PrivilegedAction<Void>() {
583                 @Override
584                 public Void run() {
585                     l.mergeWithSystemLogger(sysLogger);
586                     return null;
587                 }
588             });
589         }
590         return sysLogger;
591     }
592 
593     // LoggerContext maintains the logger namespace per context.
594     // The default LogManager implementation has one system context and user
595     // context.  The system context is used to maintain the namespace for
596     // all system loggers and is queried by the system code.  If a system logger
597     // doesn't exist in the user context, it'll also be added to the user context.
598     // The user context is queried by the user code and all other loggers are
599     // added in the user context.
600     class LoggerContext {
601         // Table of named Loggers that maps names to Loggers.
602         private final ConcurrentHashMap<String,LoggerWeakRef> namedLoggers =
603                 new ConcurrentHashMap<>();
604         // Tree of named Loggers
605         private final LogNode root;
LoggerContext()606         private LoggerContext() {
607             this.root = new LogNode(null, this);
608         }
609 
610 
611         // Tells whether default loggers are required in this context.
612         // If true, the default loggers will be lazily added.
requiresDefaultLoggers()613         final boolean requiresDefaultLoggers() {
614             final boolean requiresDefaultLoggers = (getOwner() == manager);
615             if (requiresDefaultLoggers) {
616                 getOwner().ensureLogManagerInitialized();
617             }
618             return requiresDefaultLoggers;
619         }
620 
621         // This context's LogManager.
getOwner()622         final LogManager getOwner() {
623             return LogManager.this;
624         }
625 
626         // This context owner's root logger, which if not null, and if
627         // the context requires default loggers, will be added to the context
628         // logger's tree.
getRootLogger()629         final Logger getRootLogger() {
630             return getOwner().rootLogger;
631         }
632 
633         // The global logger, which if not null, and if
634         // the context requires default loggers, will be added to the context
635         // logger's tree.
getGlobalLogger()636         final Logger getGlobalLogger() {
637             @SuppressWarnings("deprecation") // avoids initialization cycles.
638             final Logger global = Logger.global;
639             return global;
640         }
641 
demandLogger(String name, String resourceBundleName, Module module)642         Logger demandLogger(String name, String resourceBundleName, Module module) {
643             // a LogManager subclass may have its own implementation to add and
644             // get a Logger.  So delegate to the LogManager to do the work.
645             final LogManager owner = getOwner();
646             return owner.demandLogger(name, resourceBundleName, module);
647         }
648 
649 
650         // Due to subtle deadlock issues getUserContext() no longer
651         // calls addLocalLogger(rootLogger);
652         // Therefore - we need to add the default loggers later on.
653         // Checks that the context is properly initialized
654         // This is necessary before calling e.g. find(name)
655         // or getLoggerNames()
656         //
ensureInitialized()657         private void ensureInitialized() {
658             if (requiresDefaultLoggers()) {
659                 // Ensure that the root and global loggers are set.
660                 ensureDefaultLogger(getRootLogger());
661                 ensureDefaultLogger(getGlobalLogger());
662             }
663         }
664 
665 
findLogger(String name)666         Logger findLogger(String name) {
667             // Attempt to find logger without locking.
668             LoggerWeakRef ref = namedLoggers.get(name);
669             Logger logger = ref == null ? null : ref.get();
670 
671             // if logger is not null, then we can return it right away.
672             // if name is "" or "global" and logger is null
673             // we need to fall through and check that this context is
674             // initialized.
675             // if ref is not null and logger is null we also need to
676             // fall through.
677             if (logger != null || (ref == null && !name.isEmpty()
678                     && !name.equals(Logger.GLOBAL_LOGGER_NAME))) {
679                 return logger;
680             }
681 
682             // We either found a stale reference, or we were looking for
683             // "" or "global" and didn't find them.
684             // Make sure context is initialized (has the default loggers),
685             // and look up again, cleaning the stale reference if it hasn't
686             // been cleaned up in between. All this needs to be done inside
687             // a synchronized block.
688             synchronized(this) {
689                 // ensure that this context is properly initialized before
690                 // looking for loggers.
691                 ensureInitialized();
692                 ref = namedLoggers.get(name);
693                 if (ref == null) {
694                     return null;
695                 }
696                 logger = ref.get();
697                 if (logger == null) {
698                     // The namedLoggers map holds stale weak reference
699                     // to a logger which has been GC-ed.
700                     ref.dispose();
701                 }
702                 return logger;
703             }
704         }
705 
706         // This method is called before adding a logger to the
707         // context.
708         // 'logger' is the context that will be added.
709         // This method will ensure that the defaults loggers are added
710         // before adding 'logger'.
711         //
ensureAllDefaultLoggers(Logger logger)712         private void ensureAllDefaultLoggers(Logger logger) {
713             if (requiresDefaultLoggers()) {
714                 final String name = logger.getName();
715                 if (!name.isEmpty()) {
716                     ensureDefaultLogger(getRootLogger());
717                     if (!Logger.GLOBAL_LOGGER_NAME.equals(name)) {
718                         ensureDefaultLogger(getGlobalLogger());
719                     }
720                 }
721             }
722         }
723 
ensureDefaultLogger(Logger logger)724         private void ensureDefaultLogger(Logger logger) {
725             // Used for lazy addition of root logger and global logger
726             // to a LoggerContext.
727 
728             // This check is simple sanity: we do not want that this
729             // method be called for anything else than Logger.global
730             // or owner.rootLogger.
731             if (!requiresDefaultLoggers() || logger == null
732                     || logger != getGlobalLogger() && logger != LogManager.this.rootLogger ) {
733 
734                 // the case where we have a non null logger which is neither
735                 // Logger.global nor manager.rootLogger indicates a serious
736                 // issue - as ensureDefaultLogger should never be called
737                 // with any other loggers than one of these two (or null - if
738                 // e.g manager.rootLogger is not yet initialized)...
739                 assert logger == null;
740 
741                 return;
742             }
743 
744             // Adds the logger if it's not already there.
745             if (!namedLoggers.containsKey(logger.getName())) {
746                 // It is important to prevent addLocalLogger to
747                 // call ensureAllDefaultLoggers when we're in the process
748                 // off adding one of those default loggers - as this would
749                 // immediately cause a stack overflow.
750                 // Therefore we must pass addDefaultLoggersIfNeeded=false,
751                 // even if requiresDefaultLoggers is true.
752                 addLocalLogger(logger, false);
753             }
754         }
755 
addLocalLogger(Logger logger)756         boolean addLocalLogger(Logger logger) {
757             // no need to add default loggers if it's not required
758             return addLocalLogger(logger, requiresDefaultLoggers());
759         }
760 
761         // Add a logger to this context.  This method will only set its level
762         // and process parent loggers.  It doesn't set its handlers.
addLocalLogger(Logger logger, boolean addDefaultLoggersIfNeeded)763         synchronized boolean addLocalLogger(Logger logger, boolean addDefaultLoggersIfNeeded) {
764             // addDefaultLoggersIfNeeded serves to break recursion when adding
765             // default loggers. If we're adding one of the default loggers
766             // (we're being called from ensureDefaultLogger()) then
767             // addDefaultLoggersIfNeeded will be false: we don't want to
768             // call ensureAllDefaultLoggers again.
769             //
770             // Note: addDefaultLoggersIfNeeded can also be false when
771             //       requiresDefaultLoggers is false - since calling
772             //       ensureAllDefaultLoggers would have no effect in this case.
773             if (addDefaultLoggersIfNeeded) {
774                 ensureAllDefaultLoggers(logger);
775             }
776 
777             final String name = logger.getName();
778             if (name == null) {
779                 throw new NullPointerException();
780             }
781             LoggerWeakRef ref = namedLoggers.get(name);
782             if (ref != null) {
783                 if (ref.refersTo(null)) {
784                     // It's possible that the Logger was GC'ed after a
785                     // drainLoggerRefQueueBounded() call above so allow
786                     // a new one to be registered.
787                     ref.dispose();
788                 } else {
789                     // We already have a registered logger with the given name.
790                     return false;
791                 }
792             }
793 
794             // We're adding a new logger.
795             // Note that we are creating a weak reference here.
796             final LogManager owner = getOwner();
797             logger.setLogManager(owner);
798             ref = owner.new LoggerWeakRef(logger);
799 
800             // Apply any initial level defined for the new logger, unless
801             // the logger's level is already initialized
802             Level level = owner.getLevelProperty(name + ".level", null);
803             if (level != null && !logger.isLevelInitialized()) {
804                 doSetLevel(logger, level);
805             }
806 
807             // instantiation of the handler is done in the LogManager.addLogger
808             // implementation as a handler class may be only visible to LogManager
809             // subclass for the custom log manager case
810             processParentHandlers(logger, name, VisitedLoggers.NEVER);
811 
812             // Find the new node and its parent.
813             LogNode node = getNode(name);
814             node.loggerRef = ref;
815             Logger parent = null;
816             LogNode nodep = node.parent;
817             while (nodep != null) {
818                 LoggerWeakRef nodeRef = nodep.loggerRef;
819                 if (nodeRef != null) {
820                     parent = nodeRef.get();
821                     if (parent != null) {
822                         break;
823                     }
824                 }
825                 nodep = nodep.parent;
826             }
827 
828             if (parent != null) {
829                 doSetParent(logger, parent);
830             }
831             // Walk over the children and tell them we are their new parent.
832             node.walkAndSetParent(logger);
833             // new LogNode is ready so tell the LoggerWeakRef about it
834             ref.setNode(node);
835 
836             // Do not publish 'ref' in namedLoggers before the logger tree
837             // is fully updated - because the named logger will be visible as
838             // soon as it is published in namedLoggers (findLogger takes
839             // benefit of the ConcurrentHashMap implementation of namedLoggers
840             // to avoid synchronizing on retrieval when that is possible).
841             namedLoggers.put(name, ref);
842             return true;
843         }
844 
removeLoggerRef(String name, LoggerWeakRef ref)845         void removeLoggerRef(String name, LoggerWeakRef ref) {
846             namedLoggers.remove(name, ref);
847         }
848 
getLoggerNames()849         synchronized Enumeration<String> getLoggerNames() {
850             // ensure that this context is properly initialized before
851             // returning logger names.
852             ensureInitialized();
853             return Collections.enumeration(namedLoggers.keySet());
854         }
855 
856         // If logger.getUseParentHandlers() returns 'true' and any of the logger's
857         // parents have levels or handlers defined, make sure they are instantiated.
858         @SuppressWarnings("removal")
processParentHandlers(final Logger logger, final String name, Predicate<Logger> visited)859         private void processParentHandlers(final Logger logger, final String name,
860                Predicate<Logger> visited) {
861             final LogManager owner = getOwner();
862             AccessController.doPrivileged(new PrivilegedAction<Void>() {
863                 @Override
864                 public Void run() {
865                     if (logger != owner.rootLogger) {
866                         boolean useParent = owner.getBooleanProperty(name + ".useParentHandlers", true);
867                         if (!useParent) {
868                             logger.setUseParentHandlers(false);
869                         }
870                     }
871                     return null;
872                 }
873             });
874 
875             int ix = 1;
876             for (;;) {
877                 int ix2 = name.indexOf('.', ix);
878                 if (ix2 < 0) {
879                     break;
880                 }
881                 String pname = name.substring(0, ix2);
882                 if (owner.getProperty(pname + ".level") != null ||
883                     owner.getProperty(pname + ".handlers") != null) {
884                     // This pname has a level/handlers definition.
885                     // Make sure it exists.
886                     if (visited.test(demandLogger(pname, null, null))) {
887                         break;
888                     }
889                 }
890                 ix = ix2+1;
891             }
892         }
893 
894         // Gets a node in our tree of logger nodes.
895         // If necessary, create it.
getNode(String name)896         LogNode getNode(String name) {
897             if (name == null || name.isEmpty()) {
898                 return root;
899             }
900             LogNode node = root;
901             while (name.length() > 0) {
902                 int ix = name.indexOf('.');
903                 String head;
904                 if (ix > 0) {
905                     head = name.substring(0, ix);
906                     name = name.substring(ix + 1);
907                 } else {
908                     head = name;
909                     name = "";
910                 }
911                 if (node.children == null) {
912                     node.children = new HashMap<>();
913                 }
914                 LogNode child = node.children.get(head);
915                 if (child == null) {
916                     child = new LogNode(node, this);
917                     node.children.put(head, child);
918                 }
919                 node = child;
920             }
921             return node;
922         }
923     }
924 
925     final class SystemLoggerContext extends LoggerContext {
926         // Add a system logger in the system context's namespace as well as
927         // in the LogManager's namespace if not exist so that there is only
928         // one single logger of the given name.  System loggers are visible
929         // to applications unless a logger of the same name has been added.
930         @Override
demandLogger(String name, String resourceBundleName, Module module)931         Logger demandLogger(String name, String resourceBundleName,
932                             Module module) {
933             Logger result = findLogger(name);
934             if (result == null) {
935                 // only allocate the new system logger once
936                 Logger newLogger = new Logger(name, resourceBundleName,
937                                               module, getOwner(), true);
938                 do {
939                     if (addLocalLogger(newLogger)) {
940                         // We successfully added the new Logger that we
941                         // created above so return it without refetching.
942                         result = newLogger;
943                     } else {
944                         // We didn't add the new Logger that we created above
945                         // because another thread added a Logger with the same
946                         // name after our null check above and before our call
947                         // to addLogger(). We have to refetch the Logger because
948                         // addLogger() returns a boolean instead of the Logger
949                         // reference itself. However, if the thread that created
950                         // the other Logger is not holding a strong reference to
951                         // the other Logger, then it is possible for the other
952                         // Logger to be GC'ed after we saw it in addLogger() and
953                         // before we can refetch it. If it has been GC'ed then
954                         // we'll just loop around and try again.
955                         result = findLogger(name);
956                     }
957                 } while (result == null);
958             }
959             return result;
960         }
961     }
962 
963     // Add new per logger handlers.
964     // We need to raise privilege here. All our decisions will
965     // be made based on the logging configuration, which can
966     // only be modified by trusted code.
967     @SuppressWarnings("removal")
loadLoggerHandlers(final Logger logger, final String name, final String handlersPropertyName)968     private void loadLoggerHandlers(final Logger logger, final String name,
969                                     final String handlersPropertyName)
970     {
971         AccessController.doPrivileged(new PrivilegedAction<Void>() {
972             @Override
973             public Void run() {
974                 setLoggerHandlers(logger, name, handlersPropertyName,
975                     createLoggerHandlers(name, handlersPropertyName));
976                 return null;
977             }
978         });
979     }
980 
setLoggerHandlers(final Logger logger, final String name, final String handlersPropertyName, List<Handler> handlers)981     private void setLoggerHandlers(final Logger logger, final String name,
982                                    final String handlersPropertyName,
983                                    List<Handler> handlers)
984     {
985         final boolean ensureCloseOnReset = ! handlers.isEmpty()
986                     && getBooleanProperty(handlersPropertyName + ".ensureCloseOnReset",true);
987         int count = 0;
988         for (Handler hdl : handlers) {
989             logger.addHandler(hdl);
990             if (++count == 1 && ensureCloseOnReset) {
991                 // add this logger to the closeOnResetLoggers list.
992                 closeOnResetLoggers.addIfAbsent(CloseOnReset.create(logger));
993             }
994         }
995     }
996 
createLoggerHandlers(final String name, final String handlersPropertyName)997     private List<Handler> createLoggerHandlers(final String name,
998                                                final String handlersPropertyName)
999     {
1000         String names[] = parseClassNames(handlersPropertyName);
1001         List<Handler> handlers = new ArrayList<>(names.length);
1002         for (String type : names) {
1003             try {
1004                 @SuppressWarnings("deprecation")
1005                 Object o = ClassLoader.getSystemClassLoader().loadClass(type).newInstance();
1006                 Handler hdl = (Handler) o;
1007                 // Check if there is a property defining the
1008                 // this handler's level.
1009                 String levs = getProperty(type + ".level");
1010                 if (levs != null) {
1011                     Level l = Level.findLevel(levs);
1012                     if (l != null) {
1013                         hdl.setLevel(l);
1014                     } else {
1015                         // Probably a bad level. Drop through.
1016                         System.err.println("Can't set level for " + type);
1017                     }
1018                 }
1019                 // Add this Handler to the logger
1020                 handlers.add(hdl);
1021             } catch (Exception ex) {
1022                 System.err.println("Can't load log handler \"" + type + "\"");
1023                 System.err.println("" + ex);
1024                 ex.printStackTrace();
1025             }
1026         }
1027 
1028         return handlers;
1029     }
1030 
1031 
1032     // loggerRefQueue holds LoggerWeakRef objects for Logger objects
1033     // that have been GC'ed.
1034     private final ReferenceQueue<Logger> loggerRefQueue
1035         = new ReferenceQueue<>();
1036 
1037     // Package-level inner class.
1038     // Helper class for managing WeakReferences to Logger objects.
1039     //
1040     // LogManager.namedLoggers
1041     //     - has weak references to all named Loggers
1042     //     - namedLoggers keeps the LoggerWeakRef objects for the named
1043     //       Loggers around until we can deal with the book keeping for
1044     //       the named Logger that is being GC'ed.
1045     // LogManager.LogNode.loggerRef
1046     //     - has a weak reference to a named Logger
1047     //     - the LogNode will also keep the LoggerWeakRef objects for
1048     //       the named Loggers around; currently LogNodes never go away.
1049     // Logger.kids
1050     //     - has a weak reference to each direct child Logger; this
1051     //       includes anonymous and named Loggers
1052     //     - anonymous Loggers are always children of the rootLogger
1053     //       which is a strong reference; rootLogger.kids keeps the
1054     //       LoggerWeakRef objects for the anonymous Loggers around
1055     //       until we can deal with the book keeping.
1056     //
1057     final class LoggerWeakRef extends WeakReference<Logger> {
1058         private String                name;       // for namedLoggers cleanup
1059         private LogNode               node;       // for loggerRef cleanup
1060         private WeakReference<Logger> parentRef;  // for kids cleanup
1061         private boolean disposed = false;         // avoid calling dispose twice
1062 
LoggerWeakRef(Logger logger)1063         LoggerWeakRef(Logger logger) {
1064             super(logger, loggerRefQueue);
1065 
1066             name = logger.getName();  // save for namedLoggers cleanup
1067         }
1068 
1069         // dispose of this LoggerWeakRef object
dispose()1070         void dispose() {
1071             // Avoid calling dispose twice. When a Logger is gc'ed, its
1072             // LoggerWeakRef will be enqueued.
1073             // However, a new logger of the same name may be added (or looked
1074             // up) before the queue is drained. When that happens, dispose()
1075             // will be called by addLocalLogger() or findLogger().
1076             // Later when the queue is drained, dispose() will be called again
1077             // for the same LoggerWeakRef. Marking LoggerWeakRef as disposed
1078             // avoids processing the data twice (even though the code should
1079             // now be reentrant).
1080             synchronized(this) {
1081                 // Note to maintainers:
1082                 // Be careful not to call any method that tries to acquire
1083                 // another lock from within this block - as this would surely
1084                 // lead to deadlocks, given that dispose() can be called by
1085                 // multiple threads, and from within different synchronized
1086                 // methods/blocks.
1087                 if (disposed) return;
1088                 disposed = true;
1089             }
1090 
1091             final LogNode n = node;
1092             if (n != null) {
1093                 // n.loggerRef can only be safely modified from within
1094                 // a lock on LoggerContext. removeLoggerRef is already
1095                 // synchronized on LoggerContext so calling
1096                 // n.context.removeLoggerRef from within this lock is safe.
1097                 synchronized (n.context) {
1098                     // if we have a LogNode, then we were a named Logger
1099                     // so clear namedLoggers weak ref to us
1100                     n.context.removeLoggerRef(name, this);
1101                     name = null;  // clear our ref to the Logger's name
1102 
1103                     // LogNode may have been reused - so only clear
1104                     // LogNode.loggerRef if LogNode.loggerRef == this
1105                     if (n.loggerRef == this) {
1106                         n.loggerRef = null;  // clear LogNode's weak ref to us
1107                     }
1108                     node = null;            // clear our ref to LogNode
1109                 }
1110             }
1111 
1112             if (parentRef != null) {
1113                 // this LoggerWeakRef has or had a parent Logger
1114                 Logger parent = parentRef.get();
1115                 if (parent != null) {
1116                     // the parent Logger is still there so clear the
1117                     // parent Logger's weak ref to us
1118                     parent.removeChildLogger(this);
1119                 }
1120                 parentRef = null;  // clear our weak ref to the parent Logger
1121             }
1122         }
1123 
1124         // set the node field to the specified value
setNode(LogNode node)1125         void setNode(LogNode node) {
1126             this.node = node;
1127         }
1128 
1129         // set the parentRef field to the specified value
setParentRef(WeakReference<Logger> parentRef)1130         void setParentRef(WeakReference<Logger> parentRef) {
1131             this.parentRef = parentRef;
1132         }
1133     }
1134 
1135     // Package-level method.
1136     // Drain some Logger objects that have been GC'ed.
1137     //
1138     // drainLoggerRefQueueBounded() is called by addLogger() below
1139     // and by Logger.getAnonymousLogger(String) so we'll drain up to
1140     // MAX_ITERATIONS GC'ed Loggers for every Logger we add.
1141     //
1142     // On a WinXP VMware client, a MAX_ITERATIONS value of 400 gives
1143     // us about a 50/50 mix in increased weak ref counts versus
1144     // decreased weak ref counts in the AnonLoggerWeakRefLeak test.
1145     // Here are stats for cleaning up sets of 400 anonymous Loggers:
1146     //   - test duration 1 minute
1147     //   - sample size of 125 sets of 400
1148     //   - average: 1.99 ms
1149     //   - minimum: 0.57 ms
1150     //   - maximum: 25.3 ms
1151     //
1152     // The same config gives us a better decreased weak ref count
1153     // than increased weak ref count in the LoggerWeakRefLeak test.
1154     // Here are stats for cleaning up sets of 400 named Loggers:
1155     //   - test duration 2 minutes
1156     //   - sample size of 506 sets of 400
1157     //   - average: 0.57 ms
1158     //   - minimum: 0.02 ms
1159     //   - maximum: 10.9 ms
1160     //
1161     private static final int MAX_ITERATIONS = 400;
drainLoggerRefQueueBounded()1162     final void drainLoggerRefQueueBounded() {
1163         for (int i = 0; i < MAX_ITERATIONS; i++) {
1164             if (loggerRefQueue == null) {
1165                 // haven't finished loading LogManager yet
1166                 break;
1167             }
1168 
1169             LoggerWeakRef ref = (LoggerWeakRef) loggerRefQueue.poll();
1170             if (ref == null) {
1171                 break;
1172             }
1173             // a Logger object has been GC'ed so clean it up
1174             ref.dispose();
1175         }
1176     }
1177 
1178     /**
1179      * Add a named logger.  This does nothing and returns false if a logger
1180      * with the same name is already registered.
1181      * <p>
1182      * The Logger factory methods call this method to register each
1183      * newly created Logger.
1184      * <p>
1185      * The application should retain its own reference to the Logger
1186      * object to avoid it being garbage collected.  The LogManager
1187      * may only retain a weak reference.
1188      *
1189      * @param   logger the new logger.
1190      * @return  true if the argument logger was registered successfully,
1191      *          false if a logger of that name already exists.
1192      * @throws NullPointerException if the logger name is null.
1193      */
addLogger(Logger logger)1194     public boolean addLogger(Logger logger) {
1195         final String name = logger.getName();
1196         if (name == null) {
1197             throw new NullPointerException();
1198         }
1199         drainLoggerRefQueueBounded();
1200         LoggerContext cx = getUserContext();
1201         if (cx.addLocalLogger(logger) || forceLoadHandlers(logger)) {
1202             // Do we have a per logger handler too?
1203             // Note: this will add a 200ms penalty
1204             loadLoggerHandlers(logger, name, name + ".handlers");
1205             return true;
1206         } else {
1207             return false;
1208         }
1209     }
1210 
1211 
1212     // Checks whether the given logger is a special logger
1213     // that still requires handler initialization.
1214     // This method will only return true for the root and
1215     // global loggers and only if called by the thread that
1216     // performs initialization of the LogManager, during that
1217     // initialization. Must only be called by addLogger.
1218     @SuppressWarnings("deprecation")
forceLoadHandlers(Logger logger)1219     private boolean forceLoadHandlers(Logger logger) {
1220         // Called just after reading the primordial configuration, in
1221         // the same thread that reads it.
1222         // The root and global logger would already be present in the context
1223         // by this point, but we would not have called loadLoggerHandlers
1224         // yet.
1225         return (logger == rootLogger ||  logger == Logger.global)
1226                 && !initializationDone
1227                 && initializedCalled
1228                 && configurationLock.isHeldByCurrentThread();
1229     }
1230 
1231     // Private method to set a level on a logger.
1232     // If necessary, we raise privilege before doing the call.
1233     @SuppressWarnings("removal")
doSetLevel(final Logger logger, final Level level)1234     private static void doSetLevel(final Logger logger, final Level level) {
1235         SecurityManager sm = System.getSecurityManager();
1236         if (sm == null) {
1237             // There is no security manager, so things are easy.
1238             logger.setLevel(level);
1239             return;
1240         }
1241         // There is a security manager.  Raise privilege before
1242         // calling setLevel.
1243         AccessController.doPrivileged(new PrivilegedAction<Object>() {
1244             @Override
1245             public Object run() {
1246                 logger.setLevel(level);
1247                 return null;
1248             }});
1249     }
1250 
1251     // Private method to set a parent on a logger.
1252     // If necessary, we raise privilege before doing the setParent call.
1253     @SuppressWarnings("removal")
doSetParent(final Logger logger, final Logger parent)1254     private static void doSetParent(final Logger logger, final Logger parent) {
1255         SecurityManager sm = System.getSecurityManager();
1256         if (sm == null) {
1257             // There is no security manager, so things are easy.
1258             logger.setParent(parent);
1259             return;
1260         }
1261         // There is a security manager.  Raise privilege before
1262         // calling setParent.
1263         AccessController.doPrivileged(new PrivilegedAction<Object>() {
1264             @Override
1265             public Object run() {
1266                 logger.setParent(parent);
1267                 return null;
1268             }});
1269     }
1270 
1271     /**
1272      * Method to find a named logger.
1273      * <p>
1274      * Note that since untrusted code may create loggers with
1275      * arbitrary names this method should not be relied on to
1276      * find Loggers for security sensitive logging.
1277      * It is also important to note that the Logger associated with the
1278      * String {@code name} may be garbage collected at any time if there
1279      * is no strong reference to the Logger. The caller of this method
1280      * must check the return value for null in order to properly handle
1281      * the case where the Logger has been garbage collected.
1282      *
1283      * @param name name of the logger
1284      * @return  matching logger or null if none is found
1285      */
getLogger(String name)1286     public Logger getLogger(String name) {
1287         return getUserContext().findLogger(name);
1288     }
1289 
1290     /**
1291      * Get an enumeration of known logger names.
1292      * <p>
1293      * Note:  Loggers may be added dynamically as new classes are loaded.
1294      * This method only reports on the loggers that are currently registered.
1295      * It is also important to note that this method only returns the name
1296      * of a Logger, not a strong reference to the Logger itself.
1297      * The returned String does nothing to prevent the Logger from being
1298      * garbage collected. In particular, if the returned name is passed
1299      * to {@code LogManager.getLogger()}, then the caller must check the
1300      * return value from {@code LogManager.getLogger()} for null to properly
1301      * handle the case where the Logger has been garbage collected in the
1302      * time since its name was returned by this method.
1303      *
1304      * @return  enumeration of logger name strings
1305      */
getLoggerNames()1306     public Enumeration<String> getLoggerNames() {
1307         return getUserContext().getLoggerNames();
1308     }
1309 
1310     /**
1311      * Reads and initializes the logging configuration.
1312      * <p>
1313      * If the "java.util.logging.config.class" system property is set, then the
1314      * property value is treated as a class name.  The given class will be
1315      * loaded, an object will be instantiated, and that object's constructor
1316      * is responsible for reading in the initial configuration.  (That object
1317      * may use other system properties to control its configuration.)  The
1318      * alternate configuration class can use {@code readConfiguration(InputStream)}
1319      * to define properties in the LogManager.
1320      * <p>
1321      * If "java.util.logging.config.class" system property is <b>not</b> set,
1322      * then this method will read the initial configuration from a properties
1323      * file and calls the {@link #readConfiguration(InputStream)} method to initialize
1324      * the configuration. The "java.util.logging.config.file" system property can be used
1325      * to specify the properties file that will be read as the initial configuration;
1326      * if not set, then the LogManager default configuration is used.
1327      * The default configuration is typically loaded from the
1328      * properties file "{@code conf/logging.properties}" in the Java installation
1329      * directory.
1330      *
1331      * <p>
1332      * Any {@linkplain #addConfigurationListener registered configuration
1333      * listener} will be invoked after the properties are read.
1334      *
1335      * @apiNote This {@code readConfiguration} method should only be used for
1336      * initializing the configuration during LogManager initialization or
1337      * used with the "java.util.logging.config.class" property.
1338      * When this method is called after loggers have been created, and
1339      * the "java.util.logging.config.class" system property is not set, all
1340      * existing loggers will be {@linkplain #reset() reset}. Then any
1341      * existing loggers that have a level property specified in the new
1342      * configuration stream will be {@linkplain
1343      * Logger#setLevel(java.util.logging.Level) set} to the specified log level.
1344      * <p>
1345      * To properly update the logging configuration, use the
1346      * {@link #updateConfiguration(java.util.function.Function)} or
1347      * {@link #updateConfiguration(java.io.InputStream, java.util.function.Function)}
1348      * methods instead.
1349      *
1350      * @throws   SecurityException  if a security manager exists and if
1351      *              the caller does not have LoggingPermission("control").
1352      * @throws   IOException if there are IO problems reading the configuration.
1353      */
readConfiguration()1354     public void readConfiguration() throws IOException, SecurityException {
1355         checkPermission();
1356 
1357         // if a configuration class is specified, load it and use it.
1358         String cname = System.getProperty("java.util.logging.config.class");
1359         if (cname != null) {
1360             try {
1361                 // Instantiate the named class.  It is its constructor's
1362                 // responsibility to initialize the logging configuration, by
1363                 // calling readConfiguration(InputStream) with a suitable stream.
1364                 try {
1365                     Class<?> clz = ClassLoader.getSystemClassLoader().loadClass(cname);
1366                     @SuppressWarnings("deprecation")
1367                     Object witness = clz.newInstance();
1368                     return;
1369                 } catch (ClassNotFoundException ex) {
1370                     Class<?> clz = Thread.currentThread().getContextClassLoader().loadClass(cname);
1371                     @SuppressWarnings("deprecation")
1372                     Object witness = clz.newInstance();
1373                     return;
1374                 }
1375             } catch (Exception ex) {
1376                 System.err.println("Logging configuration class \"" + cname + "\" failed");
1377                 System.err.println("" + ex);
1378                 // keep going and useful config file.
1379             }
1380         }
1381 
1382         String fname = getConfigurationFileName();
1383         try (final InputStream in = new FileInputStream(fname)) {
1384             final BufferedInputStream bin = new BufferedInputStream(in);
1385             readConfiguration(bin);
1386         }
1387     }
1388 
getConfigurationFileName()1389     String getConfigurationFileName() throws IOException {
1390         String fname = System.getProperty("java.util.logging.config.file");
1391         if (fname == null) {
1392             fname = System.getProperty("java.home");
1393             if (fname == null) {
1394                 throw new Error("Can't find java.home ??");
1395             }
1396             fname = Paths.get(fname, "conf", "logging.properties")
1397                     .toAbsolutePath().normalize().toString();
1398         }
1399         return fname;
1400     }
1401 
1402     /**
1403      * Reset the logging configuration.
1404      * <p>
1405      * For all named loggers, the reset operation removes and closes
1406      * all Handlers and (except for the root logger) sets the level
1407      * to {@code null}. The root logger's level is set to {@code Level.INFO}.
1408      *
1409      * @apiNote Calling this method also clears the LogManager {@linkplain
1410      * #getProperty(java.lang.String) properties}. The {@link
1411      * #updateConfiguration(java.util.function.Function)
1412      * updateConfiguration(Function)} or
1413      * {@link #updateConfiguration(java.io.InputStream, java.util.function.Function)
1414      * updateConfiguration(InputStream, Function)} method can be used to
1415      * properly update to a new configuration.
1416      *
1417      * @throws  SecurityException  if a security manager exists and if
1418      *             the caller does not have LoggingPermission("control").
1419      */
1420 
reset()1421     public void reset() throws SecurityException {
1422         checkPermission();
1423 
1424         List<CloseOnReset> persistent;
1425 
1426         // We don't want reset() and readConfiguration()
1427         // to run in parallel
1428         configurationLock.lock();
1429         try {
1430             // install new empty properties
1431             props = new Properties();
1432             // make sure we keep the loggers persistent until reset is done.
1433             // Those are the loggers for which we previously created a
1434             // handler from the configuration, and we need to prevent them
1435             // from being gc'ed until those handlers are closed.
1436             persistent = new ArrayList<>(closeOnResetLoggers);
1437             closeOnResetLoggers.clear();
1438 
1439             // if reset has been called from shutdown-hook (Cleaner),
1440             // or if reset has been called from readConfiguration() which
1441             // already holds the lock and will change the state itself,
1442             // then do not change state here...
1443             if (globalHandlersState != STATE_SHUTDOWN &&
1444                 globalHandlersState != STATE_READING_CONFIG) {
1445                 // ...else user called reset()...
1446                 // Since we are doing a reset we no longer want to initialize
1447                 // the global handlers, if they haven't been initialized yet.
1448                 globalHandlersState = STATE_INITIALIZED;
1449             }
1450 
1451             for (LoggerContext cx : contexts()) {
1452                 resetLoggerContext(cx);
1453             }
1454 
1455             persistent.clear();
1456         } finally {
1457             configurationLock.unlock();
1458         }
1459     }
1460 
resetLoggerContext(LoggerContext cx)1461     private void resetLoggerContext(LoggerContext cx) {
1462         Enumeration<String> enum_ = cx.getLoggerNames();
1463         while (enum_.hasMoreElements()) {
1464             String name = enum_.nextElement();
1465             Logger logger = cx.findLogger(name);
1466             if (logger != null) {
1467                 resetLogger(logger);
1468             }
1469         }
1470     }
1471 
closeHandlers(Logger logger)1472     private void closeHandlers(Logger logger) {
1473         Handler[] targets = logger.getHandlers();
1474         for (Handler h : targets) {
1475             logger.removeHandler(h);
1476             try {
1477                 h.close();
1478             } catch (Exception ex) {
1479                 // Problems closing a handler?  Keep going...
1480             } catch (Error e) {
1481                 // ignore Errors while shutting down
1482                 if (globalHandlersState != STATE_SHUTDOWN) {
1483                     throw e;
1484                 }
1485             }
1486         }
1487     }
1488 
1489     // Private method to reset an individual target logger.
resetLogger(Logger logger)1490     private void resetLogger(Logger logger) {
1491         // Close all the Logger handlers.
1492         closeHandlers(logger);
1493 
1494         // Reset Logger level
1495         String name = logger.getName();
1496         if (name != null && name.isEmpty()) {
1497             // This is the root logger.
1498             logger.setLevel(defaultLevel);
1499         } else {
1500             logger.setLevel(null);
1501         }
1502     }
1503 
1504     // get a list of whitespace separated classnames from a property.
parseClassNames(String propertyName)1505     private String[] parseClassNames(String propertyName) {
1506         String hands = getProperty(propertyName);
1507         if (hands == null) {
1508             return new String[0];
1509         }
1510         hands = hands.trim();
1511         int ix = 0;
1512         final List<String> result = new ArrayList<>();
1513         while (ix < hands.length()) {
1514             int end = ix;
1515             while (end < hands.length()) {
1516                 if (Character.isWhitespace(hands.charAt(end))) {
1517                     break;
1518                 }
1519                 if (hands.charAt(end) == ',') {
1520                     break;
1521                 }
1522                 end++;
1523             }
1524             String word = hands.substring(ix, end);
1525             ix = end+1;
1526             word = word.trim();
1527             if (word.length() == 0) {
1528                 continue;
1529             }
1530             result.add(word);
1531         }
1532         return result.toArray(new String[result.size()]);
1533     }
1534 
1535     /**
1536      * Reads and initializes the logging configuration from the given input stream.
1537      *
1538      * <p>
1539      * Any {@linkplain #addConfigurationListener registered configuration
1540      * listener} will be invoked after the properties are read.
1541      *
1542      * @apiNote This {@code readConfiguration} method should only be used for
1543      * initializing the configuration during LogManager initialization or
1544      * used with the "java.util.logging.config.class" property.
1545      * When this method is called after loggers have been created, all
1546      * existing loggers will be {@linkplain #reset() reset}. Then any
1547      * existing loggers that have a level property specified in the
1548      * given input stream will be {@linkplain
1549      * Logger#setLevel(java.util.logging.Level) set} to the specified log level.
1550      * <p>
1551      * To properly update the logging configuration, use the
1552      * {@link #updateConfiguration(java.util.function.Function)} or
1553      * {@link #updateConfiguration(java.io.InputStream, java.util.function.Function)}
1554      * method instead.
1555      *
1556      * @param ins  stream to read properties from
1557      * @throws  SecurityException  if a security manager exists and if
1558      *             the caller does not have LoggingPermission("control").
1559      * @throws  IOException if there are problems reading from the stream,
1560      *             or the given stream is not in the
1561      *             {@linkplain java.util.Properties properties file} format.
1562      */
readConfiguration(InputStream ins)1563     public void readConfiguration(InputStream ins) throws IOException, SecurityException {
1564         checkPermission();
1565 
1566         // We don't want reset() and readConfiguration() to run
1567         // in parallel.
1568         configurationLock.lock();
1569         try {
1570             if (globalHandlersState == STATE_SHUTDOWN) {
1571                 // already in terminal state: don't even bother
1572                 // to read the configuration
1573                 return;
1574             }
1575 
1576             // change state to STATE_READING_CONFIG to signal reset() to not change it
1577             globalHandlersState = STATE_READING_CONFIG;
1578             try {
1579                 // reset configuration which leaves globalHandlersState at STATE_READING_CONFIG
1580                 // so that while reading configuration, any ongoing logging requests block and
1581                 // wait for the outcome (see the end of this try statement)
1582                 reset();
1583 
1584                 try {
1585                     // Load the properties
1586                     props.load(ins);
1587                 } catch (IllegalArgumentException x) {
1588                     // props.load may throw an IllegalArgumentException if the stream
1589                     // contains malformed Unicode escape sequences.
1590                     // We wrap that in an IOException as readConfiguration is
1591                     // specified to throw IOException if there are problems reading
1592                     // from the stream.
1593                     // Note: new IOException(x.getMessage(), x) allow us to get a more
1594                     // concise error message than new IOException(x);
1595                     throw new IOException(x.getMessage(), x);
1596                 }
1597 
1598                 // Instantiate new configuration objects.
1599                 String names[] = parseClassNames("config");
1600 
1601                 for (String word : names) {
1602                     try {
1603                         Class<?> clz = ClassLoader.getSystemClassLoader().loadClass(word);
1604                         @SuppressWarnings("deprecation")
1605                         Object witness = clz.newInstance();
1606                     } catch (Exception ex) {
1607                         System.err.println("Can't load config class \"" + word + "\"");
1608                         System.err.println("" + ex);
1609                         // ex.printStackTrace();
1610                     }
1611                 }
1612 
1613                 // Set levels on any pre-existing loggers, based on the new properties.
1614                 setLevelsOnExistingLoggers();
1615 
1616                 // Note that we need to reinitialize global handles when
1617                 // they are first referenced.
1618                 globalHandlersState = STATE_UNINITIALIZED;
1619             } catch (Throwable t) {
1620                 // If there were any trouble, then set state to STATE_INITIALIZED
1621                 // so that no global handlers reinitialization is performed on not fully
1622                 // initialized configuration.
1623                 globalHandlersState = STATE_INITIALIZED;
1624                 // re-throw
1625                 throw t;
1626             }
1627         } finally {
1628             configurationLock.unlock();
1629         }
1630 
1631         // should be called out of lock to avoid dead-lock situations
1632         // when user code is involved
1633         invokeConfigurationListeners();
1634     }
1635 
1636     // This enum enumerate the configuration properties that will be
1637     // updated on existing loggers when the configuration is updated
1638     // with LogManager.updateConfiguration().
1639     //
1640     // Note that this works properly only for the global LogManager - as
1641     // Handler and its subclasses get their configuration from
1642     // LogManager.getLogManager().
1643     //
1644     static enum ConfigProperty {
1645         LEVEL(".level"), HANDLERS(".handlers"), USEPARENT(".useParentHandlers");
1646         final String suffix;
1647         final int length;
ConfigProperty(String suffix)1648         private ConfigProperty(String suffix) {
1649             this.suffix = Objects.requireNonNull(suffix);
1650             length = suffix.length();
1651         }
1652 
handleKey(String key)1653         public boolean handleKey(String key) {
1654             if (this == HANDLERS && suffix.substring(1).equals(key)) return true;
1655             if (this == HANDLERS && suffix.equals(key)) return false;
1656             return key.endsWith(suffix);
1657         }
key(String loggerName)1658         String key(String loggerName) {
1659             if (this == HANDLERS && (loggerName == null || loggerName.isEmpty())) {
1660                 return suffix.substring(1);
1661             }
1662             return loggerName + suffix;
1663         }
loggerName(String key)1664         String loggerName(String key) {
1665             assert key.equals(suffix.substring(1)) && this == HANDLERS || key.endsWith(suffix);
1666             if (this == HANDLERS && suffix.substring(1).equals(key)) return "";
1667             return key.substring(0, key.length() - length);
1668         }
1669 
1670         /**
1671          * If the property is one that should be updated on existing loggers by
1672          * updateConfiguration, returns the name of the logger for which the
1673          * property is configured. Otherwise, returns null.
1674          * @param property a property key in 'props'
1675          * @return the name of the logger on which the property is to be set,
1676          *         if the property is one that should be updated on existing
1677          *         loggers, {@code null} otherwise.
1678          */
getLoggerName(String property)1679         static String getLoggerName(String property) {
1680             for (ConfigProperty p : ConfigProperty.ALL) {
1681                 if (p.handleKey(property)) {
1682                     return p.loggerName(property);
1683                 }
1684             }
1685             return null; // Not a property that should be updated.
1686         }
1687 
1688         /**
1689          * Find the ConfigProperty corresponding to the given
1690          * property key (may find none).
1691          * @param property a property key in 'props'
1692          * @return An optional containing a ConfigProperty object,
1693          *         if the property is one that should be updated on existing
1694          *         loggers, empty otherwise.
1695          */
find(String property)1696         static Optional<ConfigProperty> find(String property) {
1697             return ConfigProperty.ALL.stream()
1698                     .filter(p -> p.handleKey(property))
1699                     .findFirst();
1700          }
1701 
1702         /**
1703          * Returns true if the given property is one that should be updated
1704          * on existing loggers.
1705          * Used to filter property name streams.
1706          * @param property a property key from the configuration.
1707          * @return true if this property is of interest for updateConfiguration.
1708          */
matches(String property)1709         static boolean matches(String property) {
1710             return find(property).isPresent();
1711         }
1712 
1713         /**
1714          * Returns true if the new property value is different from the old,
1715          * and therefore needs to be updated on existing loggers.
1716          * @param k a property key in the configuration
1717          * @param previous the old configuration
1718          * @param next the new configuration
1719          * @return true if the property is changing value between the two
1720          *         configurations.
1721          */
needsUpdating(String k, Properties previous, Properties next)1722         static boolean needsUpdating(String k, Properties previous, Properties next) {
1723             final String p = trim(previous.getProperty(k, null));
1724             final String n = trim(next.getProperty(k, null));
1725             return ! Objects.equals(p,n);
1726         }
1727 
1728         /**
1729          * Applies the mapping function for the given key to the next
1730          * configuration.
1731          * If the mapping function is null then this method does nothing.
1732          * Otherwise, it calls the mapping function to compute the value
1733          * that should be associated with {@code key} in the resulting
1734          * configuration, and applies it to {@code next}.
1735          * If the mapping function returns {@code null} the key is removed
1736          * from {@code next}.
1737          *
1738          * @param k a property key in the configuration
1739          * @param previous the old configuration
1740          * @param next the new configuration (modified by this function)
1741          * @param mappingFunction the mapping function.
1742          */
merge(String k, Properties previous, Properties next, BiFunction<String, String, String> mappingFunction)1743         static void merge(String k, Properties previous, Properties next,
1744                           BiFunction<String, String, String> mappingFunction) {
1745             String p = trim(previous.getProperty(k, null));
1746             String n = trim(next.getProperty(k, null));
1747             String mapped = trim(mappingFunction.apply(p,n));
1748             if (!Objects.equals(n, mapped)) {
1749                 if (mapped == null) {
1750                     next.remove(k);
1751                 } else {
1752                     next.setProperty(k, mapped);
1753                 }
1754             }
1755         }
1756 
1757         private static final EnumSet<ConfigProperty> ALL =
1758                 EnumSet.allOf(ConfigProperty.class);
1759     }
1760 
1761     // trim the value if not null.
trim(String value)1762     private static String trim(String value) {
1763         return value == null ? null : value.trim();
1764     }
1765 
1766     /**
1767      * An object that keep track of loggers we have already visited.
1768      * Used when updating configuration, to avoid processing the same logger
1769      * twice.
1770      */
1771     static final class VisitedLoggers implements Predicate<Logger> {
1772         final IdentityHashMap<Logger,Boolean> visited;
VisitedLoggers(IdentityHashMap<Logger,Boolean> visited)1773         private VisitedLoggers(IdentityHashMap<Logger,Boolean> visited) {
1774             this.visited = visited;
1775         }
VisitedLoggers()1776         VisitedLoggers() {
1777             this(new IdentityHashMap<>());
1778         }
1779         @Override
test(Logger logger)1780         public boolean test(Logger logger) {
1781             return visited != null && visited.put(logger, Boolean.TRUE) != null;
1782         }
clear()1783         public void clear() {
1784             if (visited != null) visited.clear();
1785         }
1786 
1787         // An object that considers that no logger has ever been visited.
1788         // This is used when processParentHandlers is called from
1789         // LoggerContext.addLocalLogger
1790         static final VisitedLoggers NEVER = new VisitedLoggers(null);
1791     }
1792 
1793 
1794     /**
1795      * Type of the modification for a given property. One of SAME, ADDED, CHANGED,
1796      * or REMOVED.
1797      */
1798     static enum ModType {
1799         SAME,    // property had no value in the old and new conf, or had the
1800                  // same value in both.
1801         ADDED,   // property had no value in the old conf, but has one in the new.
1802         CHANGED, // property has a different value in the old conf and the new conf.
1803         REMOVED; // property has no value in the new conf, but had one in the old.
of(String previous, String next)1804         static ModType of(String previous, String next) {
1805             if (previous == null && next != null) {
1806                 return ADDED;
1807             }
1808             if (next == null && previous != null) {
1809                 return REMOVED;
1810             }
1811             if (!Objects.equals(trim(previous), trim(next))) {
1812                 return CHANGED;
1813             }
1814             return SAME;
1815         }
1816     }
1817 
1818     /**
1819      * Updates the logging configuration.
1820      * <p>
1821      * If the "java.util.logging.config.file" system property is set,
1822      * then the property value specifies the properties file to be read
1823      * as the new configuration. Otherwise, the LogManager default
1824      * configuration is used.
1825      * <br>The default configuration is typically loaded from the
1826      * properties file "{@code conf/logging.properties}" in the
1827      * Java installation directory.
1828      * <p>
1829      * This method reads the new configuration and calls the {@link
1830      * #updateConfiguration(java.io.InputStream, java.util.function.Function)
1831      * updateConfiguration(ins, mapper)} method to
1832      * update the configuration.
1833      *
1834      * @apiNote
1835      * This method updates the logging configuration from reading
1836      * a properties file and ignores the "java.util.logging.config.class"
1837      * system property.  The "java.util.logging.config.class" property is
1838      * only used by the {@link #readConfiguration()}  method to load a custom
1839      * configuration class as an initial configuration.
1840      *
1841      * @param mapper a functional interface that takes a configuration
1842      *   key <i>k</i> and returns a function <i>f(o,n)</i> whose returned
1843      *   value will be applied to the resulting configuration. The
1844      *   function <i>f</i> may return {@code null} to indicate that the property
1845      *   <i>k</i> will not be added to the resulting configuration.
1846      *   <br>
1847      *   If {@code mapper} is {@code null} then {@code (k) -> ((o, n) -> n)} is
1848      *   assumed.
1849      *   <br>
1850      *   For each <i>k</i>, the mapped function <i>f</i> will
1851      *   be invoked with the value associated with <i>k</i> in the old
1852      *   configuration (i.e <i>o</i>) and the value associated with
1853      *   <i>k</i> in the new configuration (i.e. <i>n</i>).
1854      *   <br>A {@code null} value for <i>o</i> or <i>n</i> indicates that no
1855      *   value was present for <i>k</i> in the corresponding configuration.
1856      *
1857      * @throws  SecurityException  if a security manager exists and if
1858      *          the caller does not have LoggingPermission("control"), or
1859      *          does not have the permissions required to set up the
1860      *          configuration (e.g. open file specified for FileHandlers
1861      *          etc...)
1862      *
1863      * @throws  NullPointerException  if {@code mapper} returns a {@code null}
1864      *         function when invoked.
1865      *
1866      * @throws  IOException if there are problems reading from the
1867      *          logging configuration file.
1868      *
1869      * @see #updateConfiguration(java.io.InputStream, java.util.function.Function)
1870      * @since 9
1871      */
updateConfiguration(Function<String, BiFunction<String,String,String>> mapper)1872     public void updateConfiguration(Function<String, BiFunction<String,String,String>> mapper)
1873             throws IOException {
1874         checkPermission();
1875         ensureLogManagerInitialized();
1876         drainLoggerRefQueueBounded();
1877 
1878         String fname = getConfigurationFileName();
1879         try (final InputStream in = new FileInputStream(fname)) {
1880             final BufferedInputStream bin = new BufferedInputStream(in);
1881             updateConfiguration(bin, mapper);
1882         }
1883     }
1884 
1885     /**
1886      * Updates the logging configuration.
1887      * <p>
1888      * For each configuration key in the {@linkplain
1889      * #getProperty(java.lang.String) existing configuration} and
1890      * the given input stream configuration, the given {@code mapper} function
1891      * is invoked to map from the configuration key to a function,
1892      * <i>f(o,n)</i>, that takes the old value and new value and returns
1893      * the resulting value to be applied in the resulting configuration,
1894      * as specified in the table below.
1895      * <p>Let <i>k</i> be a configuration key in the old or new configuration,
1896      * <i>o</i> be the old value (i.e. the value associated
1897      * with <i>k</i> in the old configuration), <i>n</i> be the
1898      * new value (i.e. the value associated with <i>k</i> in the new
1899      * configuration), and <i>f</i> be the function returned
1900      * by {@code mapper.apply(}<i>k</i>{@code )}: then <i>v = f(o,n)</i> is the
1901      * resulting value. If <i>v</i> is not {@code null}, then a property
1902      * <i>k</i> with value <i>v</i> will be added to the resulting configuration.
1903      * Otherwise, it will be omitted.
1904      * <br>A {@code null} value may be passed to function
1905      * <i>f</i> to indicate that the corresponding configuration has no
1906      * configuration key <i>k</i>.
1907      * The function <i>f</i> may return {@code null} to indicate that
1908      * there will be no value associated with <i>k</i> in the resulting
1909      * configuration.
1910      * <p>
1911      * If {@code mapper} is {@code null}, then <i>v</i> will be set to
1912      * <i>n</i>.
1913      * <p>
1914      * LogManager {@linkplain #getProperty(java.lang.String) properties} are
1915      * updated with the resulting value in the resulting configuration.
1916      * <p>
1917      * The registered {@linkplain #addConfigurationListener configuration
1918      * listeners} will be invoked after the configuration is successfully updated.
1919      * <br><br>
1920      * <table class="striped">
1921      * <caption style="display:none">Updating configuration properties</caption>
1922      * <thead>
1923      * <tr>
1924      * <th scope="col">Property</th>
1925      * <th scope="col">Resulting Behavior</th>
1926      * </tr>
1927      * </thead>
1928      * <tbody>
1929      * <tr>
1930      * <th scope="row" style="vertical-align:top">{@code <logger>.level}</th>
1931      * <td>
1932      * <ul>
1933      *   <li>If the resulting configuration defines a level for a logger and
1934      *       if the resulting level is different than the level specified in the
1935      *       the old configuration, or not specified in
1936      *       the old configuration, then if the logger exists or if children for
1937      *       that logger exist, the level for that logger will be updated,
1938      *       and the change propagated to any existing logger children.
1939      *       This may cause the logger to be created, if necessary.
1940      *   </li>
1941      *   <li>If the old configuration defined a level for a logger, and the
1942      *       resulting configuration doesn't, then this change will not be
1943      *       propagated to existing loggers, if any.
1944      *       To completely replace a configuration - the caller should therefore
1945      *       call {@link #reset() reset} to empty the current configuration,
1946      *       before calling {@code updateConfiguration}.
1947      *   </li>
1948      * </ul>
1949      * </td>
1950      * <tr>
1951      * <th scope="row" style="vertical-align:top">{@code <logger>.useParentHandlers}</th>
1952      * <td>
1953      * <ul>
1954      *   <li>If either the resulting or the old value for the useParentHandlers
1955      *       property is not null, then if the logger exists or if children for
1956      *       that logger exist, that logger will be updated to the resulting
1957      *       value.
1958      *       The value of the useParentHandlers property is the value specified
1959      *       in the configuration; if not specified, the default is true.
1960      *   </li>
1961      * </ul>
1962      * </td>
1963      * </tr>
1964      * <tr>
1965      * <th scope="row" style="vertical-align:top">{@code <logger>.handlers}</th>
1966      * <td>
1967      * <ul>
1968      *   <li>If the resulting configuration defines a list of handlers for a
1969      *       logger, and if the resulting list is different than the list
1970      *       specified in the old configuration for that logger (that could be
1971      *       empty), then if the logger exists or its children exist, the
1972      *       handlers associated with that logger are closed and removed and
1973      *       the new handlers will be created per the resulting configuration
1974      *       and added to that logger, creating that logger if necessary.
1975      *   </li>
1976      *   <li>If the old configuration defined some handlers for a logger, and
1977      *       the resulting configuration doesn't, if that logger exists,
1978      *       its handlers will be removed and closed.
1979      *   </li>
1980      *   <li>Changing the list of handlers on an existing logger will cause all
1981      *       its previous handlers to be removed and closed, regardless of whether
1982      *       they had been created from the configuration or programmatically.
1983      *       The old handlers will be replaced by new handlers, if any.
1984      *   </li>
1985      * </ul>
1986      * </td>
1987      * </tr>
1988      * <tr>
1989      * <th scope="row" style="vertical-align:top">{@code <handler-name>.*}</th>
1990      * <td>
1991      * <ul>
1992      *   <li>Properties configured/changed on handler classes will only affect
1993      *       newly created handlers. If a node is configured with the same list
1994      *       of handlers in the old and the resulting configuration, then these
1995      *       handlers will remain unchanged.
1996      *   </li>
1997      * </ul>
1998      * </td>
1999      * </tr>
2000      * <tr>
2001      * <th scope="row" style="vertical-align:top">{@code config} and any other property</th>
2002      * <td>
2003      * <ul>
2004      *   <li>The resulting value for these property will be stored in the
2005      *   LogManager properties, but {@code updateConfiguration} will not parse
2006      *   or process their values.
2007      *   </li>
2008      * </ul>
2009      * </td>
2010      * </tr>
2011      * </tbody>
2012      * </table>
2013      * <p>
2014      * <em>Example mapper functions:</em>
2015      * <br><br>
2016      * <ul>
2017      * <li>Replace all logging properties with the new configuration:
2018      * <br><br>{@code     (k) -> ((o, n) -> n)}:
2019      * <br><br>this is equivalent to passing a null {@code mapper} parameter.
2020      * </li>
2021      * <li>Merge the new configuration and old configuration and use the
2022      * new value if <i>k</i> exists in the new configuration:
2023      * <br><br>{@code     (k) -> ((o, n) -> n == null ? o : n)}:
2024      * <br><br>as if merging two collections as follows:
2025      * {@code result.putAll(oldc); result.putAll(newc)}.<br></li>
2026      * <li>Merge the new configuration and old configuration and use the old
2027      * value if <i>k</i> exists in the old configuration:
2028      * <br><br>{@code     (k) -> ((o, n) -> o == null ? n : o)}:
2029      * <br><br>as if merging two collections as follows:
2030      * {@code result.putAll(newc); result.putAll(oldc)}.<br></li>
2031      * <li>Replace all properties with the new configuration except the handler
2032      * property to configure Logger's handler that is not root logger:
2033      * <br>
2034      * <pre>{@code (k) -> k.endsWith(".handlers")}
2035      *      {@code     ? ((o, n) -> (o == null ? n : o))}
2036      *      {@code     : ((o, n) -> n)}</pre>
2037      * </li>
2038      * </ul>
2039      * <p>
2040      * To completely reinitialize a configuration, an application can first call
2041      * {@link #reset() reset} to fully remove the old configuration, followed by
2042      * {@code updateConfiguration} to initialize the new configuration.
2043      *
2044      * @param ins    a stream to read properties from
2045      * @param mapper a functional interface that takes a configuration
2046      *   key <i>k</i> and returns a function <i>f(o,n)</i> whose returned
2047      *   value will be applied to the resulting configuration. The
2048      *   function <i>f</i> may return {@code null} to indicate that the property
2049      *   <i>k</i> will not be added to the resulting configuration.
2050      *   <br>
2051      *   If {@code mapper} is {@code null} then {@code (k) -> ((o, n) -> n)} is
2052      *   assumed.
2053      *   <br>
2054      *   For each <i>k</i>, the mapped function <i>f</i> will
2055      *   be invoked with the value associated with <i>k</i> in the old
2056      *   configuration (i.e <i>o</i>) and the value associated with
2057      *   <i>k</i> in the new configuration (i.e. <i>n</i>).
2058      *   <br>A {@code null} value for <i>o</i> or <i>n</i> indicates that no
2059      *   value was present for <i>k</i> in the corresponding configuration.
2060      *
2061      * @throws  SecurityException if a security manager exists and if
2062      *          the caller does not have LoggingPermission("control"), or
2063      *          does not have the permissions required to set up the
2064      *          configuration (e.g. open files specified for FileHandlers)
2065      *
2066      * @throws  NullPointerException if {@code ins} is null or if
2067      *          {@code mapper} returns a null function when invoked.
2068      *
2069      * @throws  IOException if there are problems reading from the stream,
2070      *          or the given stream is not in the
2071      *          {@linkplain java.util.Properties properties file} format.
2072      * @since 9
2073      */
updateConfiguration(InputStream ins, Function<String, BiFunction<String,String,String>> mapper)2074     public void updateConfiguration(InputStream ins,
2075             Function<String, BiFunction<String,String,String>> mapper)
2076             throws IOException {
2077         checkPermission();
2078         ensureLogManagerInitialized();
2079         drainLoggerRefQueueBounded();
2080 
2081         final Properties previous;
2082         final Set<String> updatePropertyNames;
2083         List<LoggerContext> cxs = Collections.emptyList();
2084         final VisitedLoggers visited = new VisitedLoggers();
2085         final Properties next = new Properties();
2086 
2087         try {
2088             // Load the properties
2089             next.load(ins);
2090         } catch (IllegalArgumentException x) {
2091             // props.load may throw an IllegalArgumentException if the stream
2092             // contains malformed Unicode escape sequences.
2093             // We wrap that in an IOException as updateConfiguration is
2094             // specified to throw IOException if there are problems reading
2095             // from the stream.
2096             // Note: new IOException(x.getMessage(), x) allow us to get a more
2097             // concise error message than new IOException(x);
2098             throw new IOException(x.getMessage(), x);
2099         }
2100 
2101         if (globalHandlersState == STATE_SHUTDOWN) return;
2102 
2103         // exclusive lock: readConfiguration/reset/updateConfiguration can't
2104         //           run concurrently.
2105         // configurationLock.writeLock().lock();
2106         configurationLock.lock();
2107         try {
2108             if (globalHandlersState == STATE_SHUTDOWN) return;
2109             previous = props;
2110 
2111             // Builds a TreeSet of all (old and new) property names.
2112             updatePropertyNames =
2113                     Stream.concat(previous.stringPropertyNames().stream(),
2114                                   next.stringPropertyNames().stream())
2115                         .collect(Collectors.toCollection(TreeSet::new));
2116 
2117             if (mapper != null) {
2118                 // mapper will potentially modify the content of
2119                 // 'next', so we need to call it before affecting props=next.
2120                 // give a chance to the mapper to control all
2121                 // properties - not just those we will reset.
2122                 updatePropertyNames.stream()
2123                         .forEachOrdered(k -> ConfigProperty
2124                                 .merge(k, previous, next,
2125                                        Objects.requireNonNull(mapper.apply(k))));
2126             }
2127 
2128             props = next;
2129 
2130             // allKeys will contain all keys:
2131             //    - which correspond to a configuration property we are interested in
2132             //      (first filter)
2133             //    - whose value needs to be updated (because it's new, removed, or
2134             //      different) in the resulting configuration (second filter)
2135             final Stream<String> allKeys = updatePropertyNames.stream()
2136                     .filter(ConfigProperty::matches)
2137                     .filter(k -> ConfigProperty.needsUpdating(k, previous, next));
2138 
2139             // Group configuration properties by logger name
2140             // We use a TreeMap so that parent loggers will be visited before
2141             // child loggers.
2142             final Map<String, TreeSet<String>> loggerConfigs =
2143                     allKeys.collect(Collectors.groupingBy(ConfigProperty::getLoggerName,
2144                                     TreeMap::new,
2145                                     Collectors.toCollection(TreeSet::new)));
2146 
2147             if (!loggerConfigs.isEmpty()) {
2148                 cxs = contexts();
2149             }
2150             final List<Logger> loggers = cxs.isEmpty()
2151                     ? Collections.emptyList() : new ArrayList<>(cxs.size());
2152             for (Map.Entry<String, TreeSet<String>> e : loggerConfigs.entrySet()) {
2153                 // This can be a logger name, or something else...
2154                 // The only thing we know is that we found a property
2155                 //    we are interested in.
2156                 // For instance, if we found x.y.z.level, then x.y.z could be
2157                 // a logger, but it could also be a handler class...
2158                 // Anyway...
2159                 final String name = e.getKey();
2160                 final Set<String> properties = e.getValue();
2161                 loggers.clear();
2162                 for (LoggerContext cx : cxs) {
2163                     Logger l = cx.findLogger(name);
2164                     if (l != null && !visited.test(l)) {
2165                         loggers.add(l);
2166                     }
2167                 }
2168                 if (loggers.isEmpty()) continue;
2169                 for (String pk : properties) {
2170                     ConfigProperty cp = ConfigProperty.find(pk).get();
2171                     String p = previous.getProperty(pk, null);
2172                     String n = next.getProperty(pk, null);
2173 
2174                     // Determines the type of modification.
2175                     ModType mod = ModType.of(p, n);
2176 
2177                     // mod == SAME means that the two values are equals, there
2178                     // is nothing to do. Usually, this should not happen as such
2179                     // properties should have been filtered above.
2180                     // It could happen however if the properties had
2181                     // trailing/leading whitespaces.
2182                     if (mod == ModType.SAME) continue;
2183 
2184                     switch (cp) {
2185                         case LEVEL:
2186                             if (mod == ModType.REMOVED) continue;
2187                             Level level = Level.findLevel(trim(n));
2188                             if (level != null) {
2189                                 if (name.isEmpty()) {
2190                                     rootLogger.setLevel(level);
2191                                 }
2192                                 for (Logger l : loggers) {
2193                                     if (!name.isEmpty() || l != rootLogger) {
2194                                         l.setLevel(level);
2195                                     }
2196                                 }
2197                             }
2198                             break;
2199                         case USEPARENT:
2200                             if (!name.isEmpty()) {
2201                                 boolean useParent = getBooleanProperty(pk, true);
2202                                 if (n != null || p != null) {
2203                                     // reset the flag only if the previous value
2204                                     // or the new value are not null.
2205                                     for (Logger l : loggers) {
2206                                         l.setUseParentHandlers(useParent);
2207                                     }
2208                                 }
2209                             }
2210                             break;
2211                         case HANDLERS:
2212                             List<Handler> hdls = null;
2213                             if (name.isEmpty()) {
2214                                 // special handling for the root logger.
2215                                 globalHandlersState = STATE_READING_CONFIG;
2216                                 try {
2217                                     closeHandlers(rootLogger);
2218                                     globalHandlersState = STATE_UNINITIALIZED;
2219                                 } catch (Throwable t) {
2220                                     globalHandlersState = STATE_INITIALIZED;
2221                                     throw t;
2222                                 }
2223                             }
2224                             for (Logger l : loggers) {
2225                                 if (l == rootLogger) continue;
2226                                 closeHandlers(l);
2227                                 if (mod == ModType.REMOVED) {
2228                                     closeOnResetLoggers.removeIf(c -> c.logger == l);
2229                                     continue;
2230                                 }
2231                                 if (hdls == null) {
2232                                     hdls = name.isEmpty()
2233                                             ? Arrays.asList(rootLogger.getHandlers())
2234                                             : createLoggerHandlers(name, pk);
2235                                 }
2236                                 setLoggerHandlers(l, name, pk, hdls);
2237                             }
2238                             break;
2239                         default: break;
2240                     }
2241                 }
2242             }
2243         } finally {
2244             configurationLock.unlock();
2245             visited.clear();
2246         }
2247 
2248         // Now ensure that if an existing logger has acquired a new parent
2249         // in the configuration, this new parent will be created - if needed,
2250         // and added to the context of the existing child.
2251         //
2252         drainLoggerRefQueueBounded();
2253         for (LoggerContext cx : cxs) {
2254             for (Enumeration<String> names = cx.getLoggerNames() ; names.hasMoreElements();) {
2255                 String name = names.nextElement();
2256                 if (name.isEmpty()) continue;  // don't need to process parents on root.
2257                 Logger l = cx.findLogger(name);
2258                 if (l != null && !visited.test(l)) {
2259                     // should pass visited here to cut the processing when
2260                     // reaching a logger already visited.
2261                     cx.processParentHandlers(l, name, visited);
2262                 }
2263             }
2264         }
2265 
2266         // We changed the configuration: invoke configuration listeners
2267         invokeConfigurationListeners();
2268     }
2269 
2270     /**
2271      * Get the value of a logging property.
2272      * The method returns null if the property is not found.
2273      * @param name      property name
2274      * @return          property value
2275      */
getProperty(String name)2276     public String getProperty(String name) {
2277         return props.getProperty(name);
2278     }
2279 
2280     // Package private method to get a String property.
2281     // If the property is not defined we return the given
2282     // default value.
getStringProperty(String name, String defaultValue)2283     String getStringProperty(String name, String defaultValue) {
2284         String val = getProperty(name);
2285         if (val == null) {
2286             return defaultValue;
2287         }
2288         return val.trim();
2289     }
2290 
2291     // Package private method to get an integer property.
2292     // If the property is not defined or cannot be parsed
2293     // we return the given default value.
getIntProperty(String name, int defaultValue)2294     int getIntProperty(String name, int defaultValue) {
2295         String val = getProperty(name);
2296         if (val == null) {
2297             return defaultValue;
2298         }
2299         try {
2300             return Integer.parseInt(val.trim());
2301         } catch (Exception ex) {
2302             return defaultValue;
2303         }
2304     }
2305 
2306     // Package private method to get a long property.
2307     // If the property is not defined or cannot be parsed
2308     // we return the given default value.
getLongProperty(String name, long defaultValue)2309     long getLongProperty(String name, long defaultValue) {
2310         String val = getProperty(name);
2311         if (val == null) {
2312             return defaultValue;
2313         }
2314         try {
2315             return Long.parseLong(val.trim());
2316         } catch (Exception ex) {
2317             return defaultValue;
2318         }
2319     }
2320 
2321     // Package private method to get a boolean property.
2322     // If the property is not defined or cannot be parsed
2323     // we return the given default value.
getBooleanProperty(String name, boolean defaultValue)2324     boolean getBooleanProperty(String name, boolean defaultValue) {
2325         String val = getProperty(name);
2326         if (val == null) {
2327             return defaultValue;
2328         }
2329         val = val.toLowerCase();
2330         if (val.equals("true") || val.equals("1")) {
2331             return true;
2332         } else if (val.equals("false") || val.equals("0")) {
2333             return false;
2334         }
2335         return defaultValue;
2336     }
2337 
2338     // Package private method to get a Level property.
2339     // If the property is not defined or cannot be parsed
2340     // we return the given default value.
getLevelProperty(String name, Level defaultValue)2341     Level getLevelProperty(String name, Level defaultValue) {
2342         String val = getProperty(name);
2343         if (val == null) {
2344             return defaultValue;
2345         }
2346         Level l = Level.findLevel(val.trim());
2347         return l != null ? l : defaultValue;
2348     }
2349 
2350     // Package private method to get a filter property.
2351     // We return an instance of the class named by the "name"
2352     // property. If the property is not defined or has problems
2353     // we return the defaultValue.
getFilterProperty(String name, Filter defaultValue)2354     Filter getFilterProperty(String name, Filter defaultValue) {
2355         String val = getProperty(name);
2356         try {
2357             if (val != null) {
2358                 @SuppressWarnings("deprecation")
2359                 Object o = ClassLoader.getSystemClassLoader().loadClass(val).newInstance();
2360                 return (Filter) o;
2361             }
2362         } catch (Exception ex) {
2363             // We got one of a variety of exceptions in creating the
2364             // class or creating an instance.
2365             // Drop through.
2366         }
2367         // We got an exception.  Return the defaultValue.
2368         return defaultValue;
2369     }
2370 
2371 
2372     // Package private method to get a formatter property.
2373     // We return an instance of the class named by the "name"
2374     // property. If the property is not defined or has problems
2375     // we return the defaultValue.
getFormatterProperty(String name, Formatter defaultValue)2376     Formatter getFormatterProperty(String name, Formatter defaultValue) {
2377         String val = getProperty(name);
2378         try {
2379             if (val != null) {
2380                 @SuppressWarnings("deprecation")
2381                 Object o = ClassLoader.getSystemClassLoader().loadClass(val).newInstance();
2382                 return (Formatter) o;
2383             }
2384         } catch (Exception ex) {
2385             // We got one of a variety of exceptions in creating the
2386             // class or creating an instance.
2387             // Drop through.
2388         }
2389         // We got an exception.  Return the defaultValue.
2390         return defaultValue;
2391     }
2392 
2393     // Private method to load the global handlers.
2394     // We do the real work lazily, when the global handlers
2395     // are first used.
initializeGlobalHandlers()2396     private void initializeGlobalHandlers() {
2397         int state = globalHandlersState;
2398         if (state == STATE_INITIALIZED ||
2399             state == STATE_SHUTDOWN) {
2400             // Nothing to do: return.
2401             return;
2402         }
2403 
2404         // If we have not initialized global handlers yet (or need to
2405         // reinitialize them), lets do it now (this case is indicated by
2406         // globalHandlersState == STATE_UNINITIALIZED).
2407         // If we are in the process of initializing global handlers we
2408         // also need to lock & wait (this case is indicated by
2409         // globalHandlersState == STATE_INITIALIZING).
2410         // If we are in the process of reading configuration we also need to
2411         // wait to see what the outcome will be (this case
2412         // is indicated by globalHandlersState == STATE_READING_CONFIG)
2413         // So in either case we need to wait for the lock.
2414         configurationLock.lock();
2415         try {
2416             if (globalHandlersState != STATE_UNINITIALIZED) {
2417                 return; // recursive call or nothing to do
2418             }
2419             // set globalHandlersState to STATE_INITIALIZING first to avoid
2420             // getting an infinite recursion when loadLoggerHandlers(...)
2421             // is going to call addHandler(...)
2422             globalHandlersState = STATE_INITIALIZING;
2423             try {
2424                 loadLoggerHandlers(rootLogger, null, "handlers");
2425             } finally {
2426                 globalHandlersState = STATE_INITIALIZED;
2427             }
2428         } finally {
2429             configurationLock.unlock();
2430         }
2431     }
2432 
2433     static final Permission controlPermission =
2434             new LoggingPermission("control", null);
2435 
checkPermission()2436     void checkPermission() {
2437         @SuppressWarnings("removal")
2438         SecurityManager sm = System.getSecurityManager();
2439         if (sm != null)
2440             sm.checkPermission(controlPermission);
2441     }
2442 
2443     /**
2444      * Check that the current context is trusted to modify the logging
2445      * configuration.  This requires LoggingPermission("control").
2446      * <p>
2447      * If the check fails we throw a SecurityException, otherwise
2448      * we return normally.
2449      *
2450      * @throws  SecurityException  if a security manager exists and if
2451      *             the caller does not have LoggingPermission("control").
2452      * @deprecated This method is only useful in conjunction with
2453      *       {@linkplain SecurityManager the Security Manager}, which is
2454      *       deprecated and subject to removal in a future release.
2455      *       Consequently, this method is also deprecated and subject to
2456      *       removal. There is no replacement for the Security Manager or this
2457      *       method.
2458      */
2459     @Deprecated(since="17", forRemoval=true)
checkAccess()2460     public void checkAccess() throws SecurityException {
2461         checkPermission();
2462     }
2463 
2464     // Nested class to represent a node in our tree of named loggers.
2465     private static class LogNode {
2466         HashMap<String,LogNode> children;
2467         LoggerWeakRef loggerRef;
2468         LogNode parent;
2469         final LoggerContext context;
2470 
LogNode(LogNode parent, LoggerContext context)2471         LogNode(LogNode parent, LoggerContext context) {
2472             this.parent = parent;
2473             this.context = context;
2474         }
2475 
2476         // Recursive method to walk the tree below a node and set
2477         // a new parent logger.
walkAndSetParent(Logger parent)2478         void walkAndSetParent(Logger parent) {
2479             if (children == null) {
2480                 return;
2481             }
2482             for (LogNode node : children.values()) {
2483                 LoggerWeakRef ref = node.loggerRef;
2484                 Logger logger = (ref == null) ? null : ref.get();
2485                 if (logger == null) {
2486                     node.walkAndSetParent(parent);
2487                 } else {
2488                     doSetParent(logger, parent);
2489                 }
2490             }
2491         }
2492     }
2493 
2494     // We use a subclass of Logger for the root logger, so
2495     // that we only instantiate the global handlers when they
2496     // are first needed.
2497     private final class RootLogger extends Logger {
RootLogger()2498         private RootLogger() {
2499             // We do not call the protected Logger two args constructor here,
2500             // to avoid calling LogManager.getLogManager() from within the
2501             // RootLogger constructor.
2502             super("", null, null, LogManager.this, true);
2503         }
2504 
2505         @Override
log(LogRecord record)2506         public void log(LogRecord record) {
2507             // Make sure that the global handlers have been instantiated.
2508             initializeGlobalHandlers();
2509             super.log(record);
2510         }
2511 
2512         @Override
addHandler(Handler h)2513         public void addHandler(Handler h) {
2514             initializeGlobalHandlers();
2515             super.addHandler(h);
2516         }
2517 
2518         @Override
removeHandler(Handler h)2519         public void removeHandler(Handler h) {
2520             initializeGlobalHandlers();
2521             super.removeHandler(h);
2522         }
2523 
2524         @Override
accessCheckedHandlers()2525         Handler[] accessCheckedHandlers() {
2526             initializeGlobalHandlers();
2527             return super.accessCheckedHandlers();
2528         }
2529     }
2530 
2531 
2532     // Private method to be called when the configuration has
2533     // changed to apply any level settings to any pre-existing loggers.
setLevelsOnExistingLoggers()2534     private void setLevelsOnExistingLoggers() {
2535         Enumeration<?> enum_ = props.propertyNames();
2536         while (enum_.hasMoreElements()) {
2537             String key = (String)enum_.nextElement();
2538             if (!key.endsWith(".level")) {
2539                 // Not a level definition.
2540                 continue;
2541             }
2542             int ix = key.length() - 6;
2543             String name = key.substring(0, ix);
2544             Level level = getLevelProperty(key, null);
2545             if (level == null) {
2546                 System.err.println("Bad level value for property: " + key);
2547                 continue;
2548             }
2549             for (LoggerContext cx : contexts()) {
2550                 Logger l = cx.findLogger(name);
2551                 if (l == null) {
2552                     continue;
2553                 }
2554                 l.setLevel(level);
2555             }
2556         }
2557     }
2558 
2559     /**
2560      * String representation of the
2561      * {@link javax.management.ObjectName} for the management interface
2562      * for the logging facility.
2563      *
2564      * @see java.lang.management.PlatformLoggingMXBean
2565      *
2566      * @since 1.5
2567      */
2568     public static final String LOGGING_MXBEAN_NAME
2569         = "java.util.logging:type=Logging";
2570 
2571     /**
2572      * Returns {@code LoggingMXBean} for managing loggers.
2573      *
2574      * @return a {@link LoggingMXBean} object.
2575      *
2576      * @deprecated {@code java.util.logging.LoggingMXBean} is deprecated and
2577      *      replaced with {@code java.lang.management.PlatformLoggingMXBean}. Use
2578      *      {@link java.lang.management.ManagementFactory#getPlatformMXBean(Class)
2579      *      ManagementFactory.getPlatformMXBean}(PlatformLoggingMXBean.class)
2580      *      instead.
2581      *
2582      * @see java.lang.management.PlatformLoggingMXBean
2583      * @since 1.5
2584      */
2585     @Deprecated(since="9")
getLoggingMXBean()2586     public static synchronized LoggingMXBean getLoggingMXBean() {
2587         return Logging.getInstance();
2588     }
2589 
2590     /**
2591      * Adds a configuration listener to be invoked each time the logging
2592      * configuration is read.
2593      * If the listener is already registered the method does nothing.
2594      * <p>
2595      * The listener is invoked with privileges that are restricted by the
2596      * calling context of this method.
2597      * The order in which the listeners are invoked is unspecified.
2598      * <p>
2599      * It is recommended that listeners do not throw errors or exceptions.
2600      *
2601      * If a listener terminates with an uncaught error or exception then
2602      * the first exception will be propagated to the caller of
2603      * {@link #readConfiguration()} (or {@link #readConfiguration(java.io.InputStream)})
2604      * after all listeners have been invoked.
2605      *
2606      * @implNote If more than one listener terminates with an uncaught error or
2607      * exception, an implementation may record the additional errors or
2608      * exceptions as {@linkplain Throwable#addSuppressed(java.lang.Throwable)
2609      * suppressed exceptions}.
2610      *
2611      * @param listener A configuration listener that will be invoked after the
2612      *        configuration changed.
2613      * @return This LogManager.
2614      * @throws SecurityException if a security manager exists and if the
2615      * caller does not have LoggingPermission("control").
2616      * @throws NullPointerException if the listener is null.
2617      *
2618      * @since 9
2619      */
addConfigurationListener(Runnable listener)2620     public LogManager addConfigurationListener(Runnable listener) {
2621         final Runnable r = Objects.requireNonNull(listener);
2622         checkPermission();
2623         @SuppressWarnings("removal")
2624         final SecurityManager sm = System.getSecurityManager();
2625         @SuppressWarnings("removal")
2626         final AccessControlContext acc =
2627                 sm == null ? null : AccessController.getContext();
2628         final PrivilegedAction<Void> pa =
2629                 acc == null ? null : () -> { r.run() ; return null; };
2630         @SuppressWarnings("removal")
2631         final Runnable pr =
2632                 acc == null ? r : () -> AccessController.doPrivileged(pa, acc);
2633         // Will do nothing if already registered.
2634         listeners.putIfAbsent(r, pr);
2635         return this;
2636     }
2637 
2638     /**
2639      * Removes a previously registered configuration listener.
2640      *
2641      * Returns silently if the listener is not found.
2642      *
2643      * @param listener the configuration listener to remove.
2644      * @throws NullPointerException if the listener is null.
2645      * @throws SecurityException if a security manager exists and if the
2646      * caller does not have LoggingPermission("control").
2647      *
2648      * @since 9
2649      */
removeConfigurationListener(Runnable listener)2650     public void removeConfigurationListener(Runnable listener) {
2651         final Runnable key = Objects.requireNonNull(listener);
2652         checkPermission();
2653         listeners.remove(key);
2654     }
2655 
invokeConfigurationListeners()2656     private void invokeConfigurationListeners() {
2657         Throwable t = null;
2658 
2659         // We're using an IdentityHashMap because we want to compare
2660         // keys using identity (==).
2661         // We don't want to loop within a block synchronized on 'listeners'
2662         // to avoid invoking listeners from yet another synchronized block.
2663         // So we're taking a snapshot of the values list to avoid the risk of
2664         // ConcurrentModificationException while looping.
2665         //
2666         for (Runnable c : listeners.values().toArray(new Runnable[0])) {
2667             try {
2668                 c.run();
2669             } catch (ThreadDeath death) {
2670                 throw death;
2671             } catch (Error | RuntimeException x) {
2672                 if (t == null) t = x;
2673                 else t.addSuppressed(x);
2674             }
2675         }
2676         // Listeners are not supposed to throw exceptions, but if that
2677         // happens, we will rethrow the first error or exception that is raised
2678         // after all listeners have been invoked.
2679         if (t instanceof Error) throw (Error)t;
2680         if (t instanceof RuntimeException) throw (RuntimeException)t;
2681     }
2682 
2683     /**
2684      * This class allows the {@link LoggingProviderImpl} to demand loggers on
2685      * behalf of system and application classes.
2686      */
2687     private static final class LoggingProviderAccess
2688         implements LoggingProviderImpl.LogManagerAccess,
2689                    PrivilegedAction<Void> {
2690 
LoggingProviderAccess()2691         private LoggingProviderAccess() {
2692         }
2693 
2694         /**
2695          * Demands a logger on behalf of the given {@code module}.
2696          * <p>
2697          * If a named logger suitable for the given module is found
2698          * returns it.
2699          * Otherwise, creates a new logger suitable for the given module.
2700          *
2701          * @param name   The logger name.
2702          * @param module The module on which behalf the logger is created/retrieved.
2703          * @return A logger for the given {@code module}.
2704          *
2705          * @throws NullPointerException if {@code name} is {@code null}
2706          *         or {@code module} is {@code null}.
2707          * @throws IllegalArgumentException if {@code manager} is not the default
2708          *         LogManager.
2709          * @throws SecurityException if a security manager is present and the
2710          *         calling code doesn't have the
2711          *        {@link LoggingPermission LoggingPermission("demandLogger", null)}.
2712          */
2713         @Override
demandLoggerFor(LogManager manager, String name, Module module)2714         public Logger demandLoggerFor(LogManager manager, String name, Module module) {
2715             if (manager != getLogManager()) {
2716                 // having LogManager as parameter just ensures that the
2717                 // caller will have initialized the LogManager before reaching
2718                 // here.
2719                 throw new IllegalArgumentException("manager");
2720             }
2721             Objects.requireNonNull(name);
2722             Objects.requireNonNull(module);
2723             @SuppressWarnings("removal")
2724             SecurityManager sm = System.getSecurityManager();
2725             if (sm != null) {
2726                 sm.checkPermission(controlPermission);
2727             }
2728             if (isSystem(module)) {
2729                 return manager.demandSystemLogger(name,
2730                     Logger.SYSTEM_LOGGER_RB_NAME, module);
2731             } else {
2732                 return manager.demandLogger(name, null, module);
2733             }
2734         }
2735 
2736         @Override
run()2737         public Void run() {
2738             LoggingProviderImpl.setLogManagerAccess(INSTANCE);
2739             return null;
2740         }
2741 
2742         static final LoggingProviderAccess INSTANCE = new LoggingProviderAccess();
2743     }
2744 
2745     static {
initStatic()2746         initStatic();
2747     }
2748 
2749     @SuppressWarnings("removal")
initStatic()2750     private static void initStatic() {
2751         AccessController.doPrivileged(LoggingProviderAccess.INSTANCE, null,
2752                                       controlPermission);
2753     }
2754 
2755 }
2756