--- old/src/java.logging/share/classes/java/util/logging/LogManager.java 2014-11-04 17:20:39.000000000 +0100 +++ new/src/java.logging/share/classes/java/util/logging/LogManager.java 2014-11-04 17:20:39.000000000 +0100 @@ -31,6 +31,7 @@ import java.security.*; import java.lang.ref.ReferenceQueue; import java.lang.ref.WeakReference; +import java.util.concurrent.CopyOnWriteArrayList; import sun.misc.JavaAWTAccess; import sun.misc.SharedSecrets; @@ -100,6 +101,18 @@ * Note that these Handlers may be created lazily, when they are * first used. * + *
  • A property "<logger>.handlers.ensureCloseOnReset". This defines a + * a boolean value. If "<logger>.handlers" is not defined or is empty, + * this property is ignored. Otherwise it defaults to "true". When the value + * is "true", the loggers for which at least one handler was configured + * using the previous "<logger>.handlers" property will stay + * strongly referenced until {@code reset()} is called. This can be turned off + * by explicitly setting "<logger>.handlers.ensureCloseOnReset=false" in + * the configuration. Note that turning this property off causes the risk of + * introducing a resource leak, as the logger may get garbage collected before + * {@code reset()} is called, thus preventing its handlers from being closed + * on {@code reset()}. + * *
  • A property "<logger>.useParentHandlers". This defines a boolean * value. By default every logger calls its parent in addition to * handling the logging message itself, this often result in messages @@ -169,6 +182,33 @@ // True if JVM death is imminent and the exit hook has been called. private boolean deathImminent; + // This list contains the loggers for which some handlers have been + // explicitely configured in the configuration file. + // It prevents these loggers from being arbitrarily garbage collected. + private static final class PersistentLogger { + private final Logger logger; + private PersistentLogger(Logger ref) { + this.logger = Objects.requireNonNull(ref); + } + @Override + public boolean equals(Object other) { + return (other instanceof PersistentLogger) && ((PersistentLogger)other).logger == logger; + } + @Override + public int hashCode() { + return System.identityHashCode(logger); + } + public Logger get() { + return logger; + } + public static PersistentLogger create(Logger logger) { + return new PersistentLogger(logger); + } + } + private final CopyOnWriteArrayList persistentLoggers = + new CopyOnWriteArrayList<>(); + + private final Map listeners = Collections.synchronizedMap(new IdentityHashMap<>()); @@ -204,7 +244,6 @@ }); } - // This private class is used as a shutdown hook. // It does a "reset" to close all open handlers. private class Cleaner extends Thread { @@ -875,30 +914,40 @@ @Override public Object run() { String names[] = parseClassNames(handlersPropertyName); - for (String word : names) { + final boolean ensureCloseOnReset = names.length == 0 ? false + : getBooleanProperty(handlersPropertyName + ".ensureCloseOnReset", + names.length > 0); + int count = 0; + for (String type : names) { try { - Class clz = ClassLoader.getSystemClassLoader().loadClass(word); + Class clz = ClassLoader.getSystemClassLoader().loadClass(type); Handler hdl = (Handler) clz.newInstance(); // Check if there is a property defining the // this handler's level. - String levs = getProperty(word + ".level"); + String levs = getProperty(type + ".level"); if (levs != null) { Level l = Level.findLevel(levs); if (l != null) { hdl.setLevel(l); } else { // Probably a bad level. Drop through. - System.err.println("Can't set level for " + word); + System.err.println("Can't set level for " + type); } } // Add this Handler to the logger logger.addHandler(hdl); + if (++count == 1 && ensureCloseOnReset) { + // add this logger to the persitentLoggers list. + persistentLoggers.addIfAbsent( + PersistentLogger.create(logger)); + } } catch (Exception ex) { - System.err.println("Can't load log handler \"" + word + "\""); + System.err.println("Can't load log handler \"" + type + "\""); System.err.println("" + ex); ex.printStackTrace(); } } + return null; } }); @@ -1233,8 +1282,15 @@ public void reset() throws SecurityException { checkPermission(); + List persistent; synchronized (this) { props = new Properties(); + // make sure we keep the loggers persistent until reset is done. + // Those are the loggers for which we previously created a + // handler from the configuration, and we need to prevent them + // from being gc'ed until those handlers are closed. + persistent = new ArrayList<>(persistentLoggers); + persistentLoggers.clear(); // Since we are doing a reset we no longer want to initialize // the global handlers, if they haven't been initialized yet. initializedGlobalHandlers = true; @@ -1249,6 +1305,7 @@ } } } + persistent.clear(); } // Private method to reset an individual target logger.