--- old/src/java.logging/share/classes/java/util/logging/LogManager.java 2014-09-12 16:57:16.000000000 +0200 +++ new/src/java.logging/share/classes/java/util/logging/LogManager.java 2014-09-12 16:57:16.000000000 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2014, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -169,6 +169,9 @@ // True if JVM death is imminent and the exit hook has been called. private boolean deathImminent; + private final Map listeners = + Collections.synchronizedMap(new IdentityHashMap<>()); + static { manager = AccessController.doPrivileged(new PrivilegedAction() { @Override @@ -1168,7 +1171,8 @@ * Any log level definitions in the new configuration file will be * applied using Logger.setLevel(), if the target Logger exists. *

- * A PropertyChangeEvent will be fired after the properties are read. + * Any {@linkplain #addConfigurationListener registered configuration + * listener} will be invoked after the properties are read. * * @exception SecurityException if a security manager exists and if * the caller does not have LoggingPermission("control"). @@ -1302,7 +1306,8 @@ /** * Reinitialize the logging properties and reread the logging configuration * from the given stream, which should be in java.util.Properties format. - * A PropertyChangeEvent will be fired after the properties are read. + * Any {@linkplain #addConfigurationListener registered configuration + * listener} will be invoked after the properties are read. *

* Any log level definitions in the new configuration file will be * applied using Logger.setLevel(), if the target Logger exists. @@ -1335,10 +1340,14 @@ // Set levels on any pre-existing loggers, based on the new properties. setLevelsOnExistingLoggers(); - // Note that we need to reinitialize global handles when - // they are first referenced. - synchronized (this) { - initializedGlobalHandlers = false; + try { + invokeConfigurationListeners(); + } finally { + // Note that we need to reinitialize global handles when + // they are first referenced. + synchronized (this) { + initializedGlobalHandlers = false; + } } } @@ -1620,4 +1629,68 @@ } return loggingMXBean; } + + /** + * Adds a configuration listener to be invoked each time the logging + * configuration is read. + * If the listener is already registered the method does nothing. + *

+ * The listener is invoked with privileges that are restricted by the + * calling context of this method. + * The order in which the listeners are invoked is unspecified. + *

+ * It is recommended that listeners do not throw errors or exceptions. + * If a listener terminates with an uncaught error or exception then it + * will be propagated to the caller of {@link #readConfiguration()} + * (or {@link #readConfiguration(java.io.InputStream)}) and prevent other + * listeners from being notified. + * + * @param listener A configuration listener that will be invoked after the + * configuration changed. + * @return This LogManager. + * @throws SecurityException if a security manager exists and if the + * caller does not have LoggingPermission("control"). + * @throws NullPointerException if the listener is null. + * + * @since 1.9 + */ + public LogManager addConfigurationListener(Runnable listener) { + final Runnable r = Objects.requireNonNull(listener); + checkPermission(); + final SecurityManager sm = System.getSecurityManager(); + final AccessControlContext acc = + sm == null ? null : AccessController.getContext(); + final PrivilegedAction pa = + acc == null ? null : () -> { r.run() ; return null; }; + final Runnable pr = + acc == null ? r : () -> AccessController.doPrivileged(pa, acc); + // Will do nothing if already registered. + listeners.putIfAbsent(r, pr); + return this; + } + + /** + * Removes a previously registered configuration listener. + * + * Returns silently if the listener is not found. + * + * @param listener the configuration listener to remove. + * @throws NullPointerException if the listener is null. + * @throws SecurityException if a security manager exists and if the + * caller does not have LoggingPermission("control"). + * + * @since 1.9 + */ + public void removeConfigurationListener(Runnable listener) { + final Runnable key = Objects.requireNonNull(listener); + checkPermission(); + listeners.remove(key); + } + + private void invokeConfigurationListeners() { + for (Runnable c : listeners.values().toArray(new Runnable[0])) { + c.run(); + } + } + }