You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@logging.apache.org by ma...@apache.org on 2014/10/06 00:56:51 UTC

[1/6] git commit: Add Cancellable interface.

Repository: logging-log4j2
Updated Branches:
  refs/heads/master 4bfb0952f -> faa143979


Add Cancellable interface.

  - Provides a tiny subset of RunnableFuture.


Project: http://git-wip-us.apache.org/repos/asf/logging-log4j2/repo
Commit: http://git-wip-us.apache.org/repos/asf/logging-log4j2/commit/69c96c9b
Tree: http://git-wip-us.apache.org/repos/asf/logging-log4j2/tree/69c96c9b
Diff: http://git-wip-us.apache.org/repos/asf/logging-log4j2/diff/69c96c9b

Branch: refs/heads/master
Commit: 69c96c9b05f183c76ff09799e0130c9f6dcf4daf
Parents: 4bfb095
Author: Matt Sicker <ma...@apache.org>
Authored: Sun Oct 5 15:49:12 2014 -0500
Committer: Matt Sicker <ma...@apache.org>
Committed: Sun Oct 5 15:49:12 2014 -0500

----------------------------------------------------------------------
 .../logging/log4j/core/util/Cancellable.java    | 30 ++++++++++++++++++++
 1 file changed, 30 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/69c96c9b/log4j-core/src/main/java/org/apache/logging/log4j/core/util/Cancellable.java
----------------------------------------------------------------------
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/util/Cancellable.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/util/Cancellable.java
new file mode 100644
index 0000000..bc58dd9
--- /dev/null
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/util/Cancellable.java
@@ -0,0 +1,30 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.core.util;
+
+/**
+ * Interface to cancel a Runnable callback.
+ *
+ * @since 2.1
+ */
+public interface Cancellable extends Runnable {
+
+    /**
+     * Cancels the execution of this Runnable callback. This method has no effect if this has already executed.
+     */
+    void cancel();
+}


[4/6] git commit: Create customizable ShutdownCallbackRegistry.

Posted by ma...@apache.org.
Create customizable ShutdownCallbackRegistry.

  - Implements LOG4J2-868.
  - Renamed ShutdownRegistrationStrategy to ShutdownCallbackRegistry.
  - Shutdown callback registry has been scoped to Log4jContextFactory
  for use in application containers.
  - ShutdownCallbackRegistry can optionally implement LifeCycle.
  - DefaultShutdownCallbackRegistry can also be extended.
  - Added constructors to Log4jContextFactory (mainly seems useful
  for subclasses).


Project: http://git-wip-us.apache.org/repos/asf/logging-log4j2/repo
Commit: http://git-wip-us.apache.org/repos/asf/logging-log4j2/commit/cd5edee0
Tree: http://git-wip-us.apache.org/repos/asf/logging-log4j2/tree/cd5edee0
Diff: http://git-wip-us.apache.org/repos/asf/logging-log4j2/diff/cd5edee0

Branch: refs/heads/master
Commit: cd5edee067cb577d9d2f7607af41bf1f99da0a60
Parents: d9e52a3
Author: Matt Sicker <ma...@apache.org>
Authored: Sun Oct 5 17:52:40 2014 -0500
Committer: Matt Sicker <ma...@apache.org>
Committed: Sun Oct 5 17:52:40 2014 -0500

----------------------------------------------------------------------
 .../logging/log4j/core/LoggerContext.java       | 109 ++++--------
 .../log4j/core/impl/Log4jContextFactory.java    |  71 +++++++-
 .../util/DefaultShutdownCallbackRegistry.java   | 175 +++++++++++++++++++
 .../DefaultShutdownRegistrationStrategy.java    |  41 -----
 .../core/util/ShutdownCallbackRegistry.java     |  56 ++++++
 .../core/util/ShutdownRegistrationStrategy.java |  61 -------
 6 files changed, 332 insertions(+), 181 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/cd5edee0/log4j-core/src/main/java/org/apache/logging/log4j/core/LoggerContext.java
----------------------------------------------------------------------
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/LoggerContext.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/LoggerContext.java
index fb3b57c..25c6bc1 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/LoggerContext.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/LoggerContext.java
@@ -19,8 +19,6 @@ package org.apache.logging.log4j.core;
 import java.beans.PropertyChangeEvent;
 import java.beans.PropertyChangeListener;
 import java.io.File;
-import java.lang.ref.Reference;
-import java.lang.ref.SoftReference;
 import java.net.URI;
 import java.util.Collection;
 import java.util.concurrent.ConcurrentHashMap;
@@ -39,15 +37,14 @@ import org.apache.logging.log4j.core.config.NullConfiguration;
 import org.apache.logging.log4j.core.config.Reconfigurable;
 import org.apache.logging.log4j.core.jmx.Server;
 import org.apache.logging.log4j.core.util.Assert;
-import org.apache.logging.log4j.core.util.DefaultShutdownRegistrationStrategy;
-import org.apache.logging.log4j.core.util.Loader;
+import org.apache.logging.log4j.core.util.Cancellable;
 import org.apache.logging.log4j.core.util.NetUtils;
-import org.apache.logging.log4j.core.util.ShutdownRegistrationStrategy;
+import org.apache.logging.log4j.core.util.ShutdownCallbackRegistry;
 import org.apache.logging.log4j.message.MessageFactory;
 import org.apache.logging.log4j.spi.AbstractLogger;
-import org.apache.logging.log4j.util.PropertiesUtil;
+import org.apache.logging.log4j.spi.LoggerContextFactory;
 
-import static org.apache.logging.log4j.core.util.ShutdownRegistrationStrategy.SHUTDOWN_HOOK_MARKER;
+import static org.apache.logging.log4j.core.util.ShutdownCallbackRegistry.SHUTDOWN_HOOK_MARKER;
 
 /**
  * The LoggerContext is the anchor for the logging system. It maintains a list
@@ -60,9 +57,6 @@ public class LoggerContext extends AbstractLifeCycle implements org.apache.loggi
 
     private static final long serialVersionUID = 1L;
 
-    private static final boolean SHUTDOWN_HOOK_ENABLED =
-        PropertiesUtil.getProperties().getBooleanProperty(ShutdownRegistrationStrategy.SHUTDOWN_HOOK_ENABLED, true);
-
     public static final String PROPERTY_CONFIG = "config";
     private static final Configuration NULL_CONFIGURATION = new NullConfiguration();
 
@@ -77,14 +71,7 @@ public class LoggerContext extends AbstractLifeCycle implements org.apache.loggi
     private Object externalContext;
     private final String name;
     private URI configLocation;
-
-    /**
-     * Once a shutdown hook thread executes, we can't remove it from the runtime (throws an exception). Therefore,
-     * it's really pointless to keep a strongly accessible reference to said thread. Thus, please use a
-     * SoftReference here to prevent possible cyclic memory references.
-     */
-    private Reference<Thread> shutdownThread;
-    private ShutdownRegistrationStrategy shutdownRegistrationStrategy;
+    private Cancellable shutdownCallback;
 
     private final Lock configLock = new ReentrantLock();
 
@@ -149,7 +136,9 @@ public class LoggerContext extends AbstractLifeCycle implements org.apache.loggi
                 if (this.isInitialized() || this.isStopped()) {
                     this.setStarting();
                     reconfigure();
-                    setUpShutdownHook();
+                    if (this.config.isShutdownHookEnabled()) {
+                        setUpShutdownHook();
+                    }
                     this.setStarted();
                 }
             } finally {
@@ -168,7 +157,9 @@ public class LoggerContext extends AbstractLifeCycle implements org.apache.loggi
         if (configLock.tryLock()) {
             try {
                 if (this.isInitialized() || this.isStopped()) {
-                    setUpShutdownHook();
+                    if (this.config.isShutdownHookEnabled()) {
+                        setUpShutdownHook();
+                    }
                     this.setStarted();
                 }
             } finally {
@@ -180,33 +171,25 @@ public class LoggerContext extends AbstractLifeCycle implements org.apache.loggi
     }
 
     private void setUpShutdownHook() {
-        if (config.isShutdownHookEnabled() && SHUTDOWN_HOOK_ENABLED) {
-            LOGGER.debug(SHUTDOWN_HOOK_MARKER, "Shutdown hook enabled. Registering a new one.");
-            shutdownThread = new SoftReference<Thread>(
-                    new Thread(new ShutdownThread(this), "log4j-shutdown")
-            );
-            addShutdownHook();
-        }
-    }
-
-    private void addShutdownHook() {
-        final Thread hook = getShutdownThread();
-        if (hook != null) {
-            final String shutdownStrategyClassName = PropertiesUtil.getProperties().getStringProperty(
-                ShutdownRegistrationStrategy.SHUTDOWN_REGISTRATION_STRATEGY);
-            if (shutdownStrategyClassName != null) {
-                try {
-                    shutdownRegistrationStrategy = Loader.newCheckedInstanceOf(shutdownStrategyClassName,
-                            ShutdownRegistrationStrategy.class);
-                } catch (final Exception e) {
-                    LOGGER.error(SHUTDOWN_HOOK_MARKER,
-                            "There was an error loading the ShutdownRegistrationStrategy [{}]. "
-                                    + "Falling back to DefaultShutdownRegistrationStrategy.",
-                            shutdownStrategyClassName, e);
-                    shutdownRegistrationStrategy = new DefaultShutdownRegistrationStrategy();
-                }
+        if (shutdownCallback == null) {
+            final LoggerContextFactory factory = LogManager.getFactory();
+            if (factory instanceof ShutdownCallbackRegistry) {
+                LOGGER.debug(SHUTDOWN_HOOK_MARKER, "Shutdown hook enabled. Registering a new one.");
                 try {
-                    shutdownRegistrationStrategy.registerShutdownHook(hook);
+                    this.shutdownCallback = ((ShutdownCallbackRegistry) factory).addShutdownCallback(new Runnable() {
+                        @Override
+                        public void run() {
+                            final LoggerContext context = LoggerContext.this;
+                            LOGGER.debug(SHUTDOWN_HOOK_MARKER, "Stopping LoggerContext[name={}, {}]", context.getName(),
+                                context);
+                            context.stop();
+                        }
+
+                        @Override
+                        public String toString() {
+                            return "Shutdown callback for LoggerContext[name=" + LoggerContext.this.getName() + ']';
+                        }
+                    });
                 } catch (final IllegalStateException ise) {
                     LOGGER.fatal(SHUTDOWN_HOOK_MARKER, "Unable to register shutdown hook because JVM is shutting down.");
                 } catch (final SecurityException se) {
@@ -216,10 +199,6 @@ public class LoggerContext extends AbstractLifeCycle implements org.apache.loggi
         }
     }
 
-    private Thread getShutdownThread() {
-        return shutdownThread == null ? null : shutdownThread.get();
-    }
-
     @Override
     public void stop() {
         LOGGER.debug("Stopping LoggerContext[name={}, {}]...", getName(), this);
@@ -229,7 +208,10 @@ public class LoggerContext extends AbstractLifeCycle implements org.apache.loggi
                 return;
             }
             this.setStopping();
-            tearDownShutdownHook();
+            if (shutdownCallback != null) {
+                shutdownCallback.cancel();
+                shutdownCallback = null;
+            }
             final Configuration prev = config;
             config = NULL_CONFIGURATION;
             updateLoggers();
@@ -246,15 +228,6 @@ public class LoggerContext extends AbstractLifeCycle implements org.apache.loggi
         LOGGER.debug("Stopped LoggerContext[name={}, {}]...", getName(), this);
     }
 
-    private void tearDownShutdownHook() {
-        final Thread thread = this.getShutdownThread();
-        if (shutdownRegistrationStrategy != null && thread != null) {
-            shutdownRegistrationStrategy.unregisterShutdownHook(thread);
-            LOGGER.debug(SHUTDOWN_HOOK_MARKER, "Enqueue shutdown hook for garbage collection.");
-            shutdownThread.enqueue();
-        }
-    }
-
     /**
      * Gets the name.
      *
@@ -491,20 +464,4 @@ public class LoggerContext extends AbstractLifeCycle implements org.apache.loggi
         return new Logger(ctx, name, messageFactory);
     }
 
-    private static class ShutdownThread implements Runnable {
-
-        private final LoggerContext context;
-
-        public ShutdownThread(final LoggerContext context) {
-            this.context = context;
-        }
-
-        @Override
-        public void run() {
-            LOGGER.debug("ShutdownThread stopping LoggerContext[name={}, {}]...", context.getName(), context);
-            context.stop();
-            LOGGER.debug("ShutdownThread stopped LoggerContext[name={}, {}].", context.getName(), context);
-        }
-    }
-
 }

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/cd5edee0/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/Log4jContextFactory.java
----------------------------------------------------------------------
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/Log4jContextFactory.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/Log4jContextFactory.java
index ee45061..67139c7 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/Log4jContextFactory.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/Log4jContextFactory.java
@@ -25,8 +25,12 @@ import org.apache.logging.log4j.core.config.ConfigurationFactory;
 import org.apache.logging.log4j.core.config.ConfigurationSource;
 import org.apache.logging.log4j.core.selector.ClassLoaderContextSelector;
 import org.apache.logging.log4j.core.selector.ContextSelector;
+import org.apache.logging.log4j.core.util.Assert;
+import org.apache.logging.log4j.core.util.Cancellable;
 import org.apache.logging.log4j.core.util.Constants;
+import org.apache.logging.log4j.core.util.DefaultShutdownCallbackRegistry;
 import org.apache.logging.log4j.core.util.Loader;
+import org.apache.logging.log4j.core.util.ShutdownCallbackRegistry;
 import org.apache.logging.log4j.spi.LoggerContextFactory;
 import org.apache.logging.log4j.status.StatusLogger;
 import org.apache.logging.log4j.util.PropertiesUtil;
@@ -34,17 +38,20 @@ import org.apache.logging.log4j.util.PropertiesUtil;
 /**
  * Factory to locate a ContextSelector and then load a LoggerContext.
  */
-public class Log4jContextFactory implements LoggerContextFactory {
+public class Log4jContextFactory implements LoggerContextFactory, ShutdownCallbackRegistry {
 
     private static final StatusLogger LOGGER = StatusLogger.getLogger();
+    private static final boolean SHUTDOWN_HOOK_ENABLED =
+        PropertiesUtil.getProperties().getBooleanProperty(ShutdownCallbackRegistry.SHUTDOWN_HOOK_ENABLED, true);
 
     private final ContextSelector selector;
+    private final ShutdownCallbackRegistry shutdownCallbackRegistry;
 
     /**
      * Initializes the ContextSelector from system property {@link Constants#LOG4J_CONTEXT_SELECTOR}.
      */
     public Log4jContextFactory() {
-        this(createContextSelector());
+        this(createContextSelector(), createShutdownCallbackRegistry());
     }
 
     /**
@@ -52,7 +59,34 @@ public class Log4jContextFactory implements LoggerContextFactory {
      * @param selector the selector to use
      */
     public Log4jContextFactory(final ContextSelector selector) {
-        this.selector = selector;
+        this(selector, createShutdownCallbackRegistry());
+    }
+
+    /**
+     * Constructs a Log4jContextFactory using the ContextSelector from {@link Constants#LOG4J_CONTEXT_SELECTOR}
+     * and the provided ShutdownRegistrationStrategy.
+     *
+     * @param shutdownCallbackRegistry the ShutdownRegistrationStrategy to use
+     * @since 2.1
+     */
+    public Log4jContextFactory(final ShutdownCallbackRegistry shutdownCallbackRegistry) {
+        this(createContextSelector(), shutdownCallbackRegistry);
+    }
+
+    /**
+     * Constructs a Log4jContextFactory using the provided ContextSelector and ShutdownRegistrationStrategy.
+     *
+     * @param selector                     the selector to use
+     * @param shutdownCallbackRegistry the ShutdownRegistrationStrategy to use
+     * @since 2.1
+     */
+    public Log4jContextFactory(final ContextSelector selector,
+                               final ShutdownCallbackRegistry shutdownCallbackRegistry) {
+        this.selector = Assert.requireNonNull(selector, "No ContextSelector provided");
+        this.shutdownCallbackRegistry = Assert.requireNonNull(shutdownCallbackRegistry,
+            "No ShutdownCallbackRegistry provided");
+        LOGGER.debug("Using ShutdownCallbackRegistry {}", shutdownCallbackRegistry.getClass());
+        initializeShutdownCallbackRegistry();
     }
 
     private static ContextSelector createContextSelector() {
@@ -67,6 +101,32 @@ public class Log4jContextFactory implements LoggerContextFactory {
         return new ClassLoaderContextSelector();
     }
 
+    private static ShutdownCallbackRegistry createShutdownCallbackRegistry() {
+        // TODO: this is such a common idiom it really deserves a utility method somewhere
+        final String registry = PropertiesUtil.getProperties().getStringProperty(
+            ShutdownCallbackRegistry.SHUTDOWN_CALLBACK_REGISTRY);
+        if (registry != null) {
+            try {
+                return Loader.newCheckedInstanceOf(registry, ShutdownCallbackRegistry.class);
+            } catch (final Exception e) {
+                LOGGER.error(SHUTDOWN_HOOK_MARKER,
+                    "There was an error loading the ShutdownCallbackRegistry [{}]. "
+                        + "Falling back to DefaultShutdownCallbackRegistry.", registry, e);
+            }
+        }
+        return new DefaultShutdownCallbackRegistry();
+    }
+
+    private void initializeShutdownCallbackRegistry() {
+        if (SHUTDOWN_HOOK_ENABLED && this.shutdownCallbackRegistry instanceof LifeCycle) {
+            try {
+                ((LifeCycle) this.shutdownCallbackRegistry).start();
+            } catch (final Exception e) {
+                LOGGER.error("There was an error starting the ShutdownCallbackRegistry.", e);
+            }
+        }
+    }
+
     /**
      * Loads the LoggerContext using the ContextSelector.
      * @param fqcn The fully qualified class name of the caller.
@@ -167,4 +227,9 @@ public class Log4jContextFactory implements LoggerContextFactory {
             selector.removeContext((LoggerContext) context);
         }
     }
+
+    @Override
+    public Cancellable addShutdownCallback(final Runnable callback) {
+        return SHUTDOWN_HOOK_ENABLED ? shutdownCallbackRegistry.addShutdownCallback(callback) : null;
+    }
 }

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/cd5edee0/log4j-core/src/main/java/org/apache/logging/log4j/core/util/DefaultShutdownCallbackRegistry.java
----------------------------------------------------------------------
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/util/DefaultShutdownCallbackRegistry.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/util/DefaultShutdownCallbackRegistry.java
new file mode 100644
index 0000000..21cf8d2
--- /dev/null
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/util/DefaultShutdownCallbackRegistry.java
@@ -0,0 +1,175 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+
+package org.apache.logging.log4j.core.util;
+
+import java.io.Serializable;
+import java.lang.ref.Reference;
+import java.lang.ref.SoftReference;
+import java.lang.ref.WeakReference;
+import java.util.Collection;
+import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.core.LifeCycle;
+import org.apache.logging.log4j.status.StatusLogger;
+
+/**
+ * ShutdownRegistrationStrategy that simply uses {@link Runtime#addShutdownHook(Thread)}. If no strategy is specified,
+ * this one is used for shutdown hook registration.
+ *
+ * @since 2.1
+ */
+public class DefaultShutdownCallbackRegistry implements ShutdownCallbackRegistry, LifeCycle, Runnable, Serializable {
+
+    private static final long serialVersionUID = 1L;
+    protected static final Logger LOGGER = StatusLogger.getLogger();
+
+    private final AtomicReference<State> state = new AtomicReference<State>(State.INITIALIZED);
+    private final ThreadFactory threadFactory;
+    private final Collection<Cancellable> hooks = new CopyOnWriteArrayList<Cancellable>();
+    private Reference<Thread> shutdownHookRef;
+
+    /**
+     * Constructs a DefaultShutdownRegistrationStrategy.
+     */
+    public DefaultShutdownCallbackRegistry() {
+        this(Executors.privilegedThreadFactory());
+    }
+
+    /**
+     * Constructs a DefaultShutdownRegistrationStrategy using the given {@link ThreadFactory}.
+     *
+     * @param threadFactory the ThreadFactory to use to create a {@link Runtime} shutdown hook thread
+     */
+    protected DefaultShutdownCallbackRegistry(final ThreadFactory threadFactory) {
+        this.threadFactory = threadFactory;
+    }
+
+    /**
+     * Executes the registered shutdown callbacks.
+     */
+    @Override
+    public void run() {
+        if (state.compareAndSet(State.STARTED, State.STOPPING)) {
+            for (final Runnable hook : hooks) {
+                try {
+                    hook.run();
+                } catch (final Throwable t) {
+                    LOGGER.error(SHUTDOWN_HOOK_MARKER, "Caught exception executing shutdown hook {}", hook, t);
+                }
+            }
+            state.set(State.STOPPED);
+        }
+    }
+
+    @Override
+    public Cancellable addShutdownCallback(final Runnable callback) {
+        if (isStarted()) {
+            final Cancellable receipt = new Cancellable() {
+                // use a reference to prevent memory leaks
+                private final Reference<Runnable> hook = new SoftReference<Runnable>(callback);
+
+                @Override
+                public void cancel() {
+                    hook.clear();
+                    hooks.remove(this);
+                }
+
+                @Override
+                public void run() {
+                    final Runnable hook = this.hook.get();
+                    if (hook != null) {
+                        hook.run();
+                        this.hook.clear();
+                    }
+                }
+
+                @Override
+                public String toString() {
+                    return String.valueOf(hook.get());
+                }
+            };
+            hooks.add(receipt);
+            return receipt;
+        }
+        throw new IllegalStateException("Cannot add new shutdown hook as this is not started. Current state: " +
+            state.get().name());
+    }
+
+    /**
+     * Registers the shutdown thread only if this is initialized.
+     */
+    @Override
+    public void start() {
+        if (state.compareAndSet(State.INITIALIZED, State.STARTING)) {
+            try {
+                addShutdownHook(threadFactory.newThread(this));
+                state.set(State.STARTED);
+            } catch (final Exception e) {
+                LOGGER.catching(e);
+                state.set(State.STOPPED);
+            }
+        }
+    }
+
+    private void addShutdownHook(final Thread thread) {
+        shutdownHookRef = new WeakReference<Thread>(thread);
+        Runtime.getRuntime().addShutdownHook(thread);
+    }
+
+    /**
+     * Cancels the shutdown thread only if this is started.
+     */
+    @Override
+    public void stop() {
+        if (state.compareAndSet(State.STARTED, State.STOPPING)) {
+            try {
+                removeShutdownHook();
+            } finally {
+                state.set(State.STOPPED);
+            }
+        }
+    }
+
+    private void removeShutdownHook() {
+        final Thread shutdownThread = shutdownHookRef.get();
+        if (shutdownThread != null) {
+            Runtime.getRuntime().removeShutdownHook(shutdownThread);
+            shutdownHookRef.enqueue();
+        }
+    }
+
+    /**
+     * Indicates if this can accept shutdown hooks.
+     *
+     * @return true if this can accept shutdown hooks
+     */
+    @Override
+    public boolean isStarted() {
+        return state.get() == State.STARTED;
+    }
+
+    @Override
+    public boolean isStopped() {
+        return state.get() == State.STOPPED;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/cd5edee0/log4j-core/src/main/java/org/apache/logging/log4j/core/util/DefaultShutdownRegistrationStrategy.java
----------------------------------------------------------------------
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/util/DefaultShutdownRegistrationStrategy.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/util/DefaultShutdownRegistrationStrategy.java
deleted file mode 100644
index 723ce59..0000000
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/util/DefaultShutdownRegistrationStrategy.java
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache license, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the license for the specific language governing permissions and
- * limitations under the license.
- */
-
-package org.apache.logging.log4j.core.util;
-
-import java.io.Serializable;
-
-/**
- * ShutdownRegistrationStrategy that simply uses {@link Runtime#addShutdownHook(Thread)}. If no strategy is specified,
- * this one is used for shutdown hook registration.
- *
- * @since 2.1
- */
-public class DefaultShutdownRegistrationStrategy implements ShutdownRegistrationStrategy, Serializable {
-
-    private static final long serialVersionUID = 1L;
-
-    @Override
-    public void registerShutdownHook(final Thread hook) {
-        Runtime.getRuntime().addShutdownHook(hook);
-    }
-
-    @Override
-    public void unregisterShutdownHook(final Thread hook) {
-        Runtime.getRuntime().removeShutdownHook(hook);
-    }
-}

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/cd5edee0/log4j-core/src/main/java/org/apache/logging/log4j/core/util/ShutdownCallbackRegistry.java
----------------------------------------------------------------------
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/util/ShutdownCallbackRegistry.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/util/ShutdownCallbackRegistry.java
new file mode 100644
index 0000000..68478dd
--- /dev/null
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/util/ShutdownCallbackRegistry.java
@@ -0,0 +1,56 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+
+package org.apache.logging.log4j.core.util;
+
+import org.apache.logging.log4j.Marker;
+import org.apache.logging.log4j.MarkerManager;
+
+/**
+ * Registry used for Runnable shutdown callback instances. Due to differing requirements of how late in the JVM
+ * lifecycle Log4j should be shut down, this interface is provided for customizing how to register shutdown hook
+ * callbacks. Implementations may optionally implement {@link org.apache.logging.log4j.core.LifeCycle}.
+ *
+ * @since 2.1
+ */
+public interface ShutdownCallbackRegistry {
+
+    /**
+     * System property to set to choose the ShutdownCallbackRegistry.
+     */
+    String SHUTDOWN_CALLBACK_REGISTRY = "log4j.shutdownCallbackRegistry";
+
+    /**
+     * System property to set to override the global ability to register shutdown hooks.
+     */
+    String SHUTDOWN_HOOK_ENABLED = "log4j.shutdownHookEnabled";
+
+    /**
+     * Shared Marker to indicate log messages corresponding to shutdown hooks.
+     */
+    Marker SHUTDOWN_HOOK_MARKER = MarkerManager.getMarker("SHUTDOWN HOOK");
+
+    /**
+     * Adds a Runnable shutdown callback to this class.
+     *
+     * @param callback the shutdown callback to be executed upon shutdown.
+     * @return a Cancellable wrapper of the provided callback or {@code null} if the shutdown hook is disabled and
+     * cannot be added.
+     * @since 2.1
+     */
+    Cancellable addShutdownCallback(Runnable callback);
+}

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/cd5edee0/log4j-core/src/main/java/org/apache/logging/log4j/core/util/ShutdownRegistrationStrategy.java
----------------------------------------------------------------------
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/util/ShutdownRegistrationStrategy.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/util/ShutdownRegistrationStrategy.java
deleted file mode 100644
index adeeda2..0000000
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/util/ShutdownRegistrationStrategy.java
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache license, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the license for the specific language governing permissions and
- * limitations under the license.
- */
-
-package org.apache.logging.log4j.core.util;
-
-import org.apache.logging.log4j.Marker;
-import org.apache.logging.log4j.MarkerManager;
-
-/**
- * Strategy used for registering shutdown hook threads. Due to differing requirements of how late in the JVM lifecycle
- * Log4j should be shut down, this interface is provided for customizing how to register shutdown hook threads.
- *
- * @since 2.1
- */
-public interface ShutdownRegistrationStrategy {
-
-    /**
-     * System property to set to choose the ShutdownRegistryStrategy.
-     */
-    String SHUTDOWN_REGISTRATION_STRATEGY = "log4j.shutdownRegistrationStrategy";
-
-    /**
-     * System property to set to override the global ability to register shutdown hooks.
-     */
-    String SHUTDOWN_HOOK_ENABLED = "log4j.shutdownHookEnabled";
-
-    /**
-     * Shared Marker to indicate log messages corresponding to shutdown hooks.
-     */
-    Marker SHUTDOWN_HOOK_MARKER = MarkerManager.getMarker("SHUTDOWN HOOK");
-
-    /**
-     * Adds a shutdown hook to be executed upon JVM exit.
-     *
-     * @param hook a Thread in the {@code State.NEW} state
-     * @throws IllegalStateException If the virtual machine is already in the process of shutting down
-     */
-    void registerShutdownHook(Thread hook);
-
-    /**
-     * Removes a shutdown hook.
-     *
-     * @param hook a previously registered shutdown hook Thread.
-     * @throws IllegalStateException If the virtual machine is already in the process of shutting down
-     */
-    void unregisterShutdownHook(Thread hook);
-}


[2/6] git commit: Add mvn -DEBUG system property support to ILC.

Posted by ma...@apache.org.
Add mvn -DEBUG system property support to ILC.

  - Just run mvn with the "-DEBUG" option to automatically enable
  debug logging for the StatusLogger when using ILC.


Project: http://git-wip-us.apache.org/repos/asf/logging-log4j2/repo
Commit: http://git-wip-us.apache.org/repos/asf/logging-log4j2/commit/29304061
Tree: http://git-wip-us.apache.org/repos/asf/logging-log4j2/tree/29304061
Diff: http://git-wip-us.apache.org/repos/asf/logging-log4j2/diff/29304061

Branch: refs/heads/master
Commit: 29304061e98e074c709eadcce5d2fa47c8072a42
Parents: 69c96c9
Author: Matt Sicker <ma...@apache.org>
Authored: Sun Oct 5 17:18:23 2014 -0500
Committer: Matt Sicker <ma...@apache.org>
Committed: Sun Oct 5 17:18:23 2014 -0500

----------------------------------------------------------------------
 .../org/apache/logging/log4j/junit/InitialLoggerContext.java  | 7 +++++++
 1 file changed, 7 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/29304061/log4j-core/src/test/java/org/apache/logging/log4j/junit/InitialLoggerContext.java
----------------------------------------------------------------------
diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/junit/InitialLoggerContext.java b/log4j-core/src/test/java/org/apache/logging/log4j/junit/InitialLoggerContext.java
index 2867b81..138edf4 100644
--- a/log4j-core/src/test/java/org/apache/logging/log4j/junit/InitialLoggerContext.java
+++ b/log4j-core/src/test/java/org/apache/logging/log4j/junit/InitialLoggerContext.java
@@ -16,6 +16,7 @@
  */
 package org.apache.logging.log4j.junit;
 
+import org.apache.logging.log4j.Level;
 import org.apache.logging.log4j.core.Appender;
 import org.apache.logging.log4j.core.Logger;
 import org.apache.logging.log4j.core.LoggerContext;
@@ -31,6 +32,9 @@ import static org.junit.Assert.*;
 
 /**
  * JUnit {@link TestRule} for constructing a new LoggerContext using a specified configuration file.
+ * If the system property {@code EBUG} is set (e.g., through the command line option {@code -DEBUG}), then the
+ * StatusLogger will be set to the debug level. This allows for more debug messages as the StatusLogger will be in the
+ * error level until a configuration file has been read and parsed into a tree of Nodes.
  */
 public class InitialLoggerContext implements TestRule {
 
@@ -46,6 +50,9 @@ public class InitialLoggerContext implements TestRule {
 
     @Override
     public Statement apply(final Statement base, final Description description) {
+        if (System.getProperties().containsKey("EBUG")) {
+            StatusLogger.getLogger().setLevel(Level.DEBUG);
+        }
         testClassName = description.getClassName();
         return new Statement() {
             @Override


[6/6] git commit: Update changelog for LOG4J2-868.

Posted by ma...@apache.org.
Update changelog for LOG4J2-868.


Project: http://git-wip-us.apache.org/repos/asf/logging-log4j2/repo
Commit: http://git-wip-us.apache.org/repos/asf/logging-log4j2/commit/faa14397
Tree: http://git-wip-us.apache.org/repos/asf/logging-log4j2/tree/faa14397
Diff: http://git-wip-us.apache.org/repos/asf/logging-log4j2/diff/faa14397

Branch: refs/heads/master
Commit: faa14397987f199f30e77e938bf79d72a225e12d
Parents: 0efdad9
Author: Matt Sicker <ma...@apache.org>
Authored: Sun Oct 5 17:56:49 2014 -0500
Committer: Matt Sicker <ma...@apache.org>
Committed: Sun Oct 5 17:56:49 2014 -0500

----------------------------------------------------------------------
 src/changes/changes.xml | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/faa14397/src/changes/changes.xml
----------------------------------------------------------------------
diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index b4944e6..3a402f7 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -23,7 +23,11 @@
     <title>Changes</title>
   </properties>
   <body>
-    <release version="2.1" date="2014-10-04" description="GA Release 2.1">
+    <release version="2.1" date="2014-10-05" description="GA Release 2.1">
+      <action issue="LOG4J2-868" dev="mattsicker" type="add">
+        Add ShutdownCallbackRegistry interface for customizable shutdown callback handling. This is particularly
+        useful for application servers that wish to integrate with Log4j 2.
+      </action>
       <action issue="LOG4J2-866" dev="rpopma" type="fix" due-to="Gerard Weatherby">
         Documentation: fixed missing closing parenthesis in code example.
       </action>


[5/6] git commit: Add test for ShutdownCallbackRegistry.

Posted by ma...@apache.org.
Add test for ShutdownCallbackRegistry.


Project: http://git-wip-us.apache.org/repos/asf/logging-log4j2/repo
Commit: http://git-wip-us.apache.org/repos/asf/logging-log4j2/commit/0efdad9e
Tree: http://git-wip-us.apache.org/repos/asf/logging-log4j2/tree/0efdad9e
Diff: http://git-wip-us.apache.org/repos/asf/logging-log4j2/diff/0efdad9e

Branch: refs/heads/master
Commit: 0efdad9ecbdc579503545c9e4ea3acfe6981d272
Parents: cd5edee
Author: Matt Sicker <ma...@apache.org>
Authored: Sun Oct 5 17:55:06 2014 -0500
Committer: Matt Sicker <ma...@apache.org>
Committed: Sun Oct 5 17:55:06 2014 -0500

----------------------------------------------------------------------
 .../core/util/ShutdownCallbackRegistryTest.java | 96 ++++++++++++++++++++
 .../resources/ShutdownCallbackRegistryTest.xml  | 32 +++++++
 2 files changed, 128 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/0efdad9e/log4j-core/src/test/java/org/apache/logging/log4j/core/util/ShutdownCallbackRegistryTest.java
----------------------------------------------------------------------
diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/core/util/ShutdownCallbackRegistryTest.java b/log4j-core/src/test/java/org/apache/logging/log4j/core/util/ShutdownCallbackRegistryTest.java
new file mode 100644
index 0000000..56d9a58
--- /dev/null
+++ b/log4j-core/src/test/java/org/apache/logging/log4j/core/util/ShutdownCallbackRegistryTest.java
@@ -0,0 +1,96 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.core.util;
+
+import java.util.Collection;
+import java.util.LinkedList;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.core.LoggerContext;
+import org.apache.logging.log4j.core.impl.Log4jContextFactory;
+import org.apache.logging.log4j.core.selector.ContextSelector;
+import org.apache.logging.log4j.junit.InitialLoggerContext;
+import org.apache.logging.log4j.status.StatusLogger;
+import org.junit.BeforeClass;
+import org.junit.Rule;
+import org.junit.Test;
+
+import static org.hamcrest.Matchers.hasItem;
+import static org.hamcrest.Matchers.hasSize;
+import static org.hamcrest.Matchers.not;
+import static org.junit.Assert.*;
+
+public class ShutdownCallbackRegistryTest {
+
+    @Rule
+    public final InitialLoggerContext ctx = new InitialLoggerContext("ShutdownCallbackRegistryTest.xml");
+
+    @BeforeClass
+    public static void setUpClass() throws Exception {
+        System.setProperty(ShutdownCallbackRegistry.SHUTDOWN_CALLBACK_REGISTRY, Registry.class.getName());
+    }
+
+    @Test
+    public void testShutdownCallbackRegistry() throws Exception {
+        final LoggerContext context = ctx.getContext();
+        assertTrue("LoggerContext should be started", context.isStarted());
+        assertThat(Registry.CALLBACKS, hasSize(1));
+        Registry.shutdown();
+        assertTrue("LoggerContext should be stopped", context.isStopped());
+        assertThat(Registry.CALLBACKS, hasSize(0));
+        final ContextSelector selector = ((Log4jContextFactory) LogManager.getFactory()).getSelector();
+        assertThat(selector.getLoggerContexts(), not(hasItem(context)));
+    }
+
+    public static class Registry implements ShutdownCallbackRegistry {
+        private static final Logger LOGGER = StatusLogger.getLogger();
+        private static final Collection<Cancellable> CALLBACKS = new LinkedList<Cancellable>();
+
+        @Override
+        public Cancellable addShutdownCallback(final Runnable callback) {
+            final Cancellable cancellable = new Cancellable() {
+                @Override
+                public void cancel() {
+                    LOGGER.debug("Cancelled shutdown callback: {}", callback);
+                    CALLBACKS.remove(this);
+                }
+
+                @Override
+                public void run() {
+                    LOGGER.debug("Called shutdown callback: {}", callback);
+                    callback.run();
+                }
+            };
+            synchronized (CALLBACKS) {
+                CALLBACKS.add(cancellable);
+            }
+            return cancellable;
+        }
+
+        private static void shutdown() {
+            synchronized (CALLBACKS) {
+                for (final Runnable callback : CALLBACKS) {
+                    LOGGER.debug("Calling shutdown callback: {}", callback);
+                    callback.run();
+                }
+                CALLBACKS.clear();
+            }
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/0efdad9e/log4j-core/src/test/resources/ShutdownCallbackRegistryTest.xml
----------------------------------------------------------------------
diff --git a/log4j-core/src/test/resources/ShutdownCallbackRegistryTest.xml b/log4j-core/src/test/resources/ShutdownCallbackRegistryTest.xml
new file mode 100644
index 0000000..eb8ec4c
--- /dev/null
+++ b/log4j-core/src/test/resources/ShutdownCallbackRegistryTest.xml
@@ -0,0 +1,32 @@
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements. See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You under the Apache license, Version 2.0
+  (the "License"); you may not use this file except in compliance with
+  the License. You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the license for the specific language governing permissions and
+  limitations under the license.
+  -->
+<Configuration name="ShutdownCallbackRegistryTest" status="off">
+  <Appenders>
+    <Console name="Console">
+      <PatternLayout pattern="%m%n"/>
+    </Console>
+    <List name="List"/>
+  </Appenders>
+  <Loggers>
+    <Logger name="org.apache.logging.log4j.core.util.ShutdownCallbackRegistryTest" level="trace" additivity="false">
+      <AppenderRef ref="List"/>
+    </Logger>
+    <Root level="error">
+      <AppenderRef ref="Console"/>
+    </Root>
+  </Loggers>
+</Configuration>


[3/6] git commit: Update configuration manual for ShutdownCallbackRegistry rename.

Posted by ma...@apache.org.
Update configuration manual for ShutdownCallbackRegistry rename.


Project: http://git-wip-us.apache.org/repos/asf/logging-log4j2/repo
Commit: http://git-wip-us.apache.org/repos/asf/logging-log4j2/commit/d9e52a36
Tree: http://git-wip-us.apache.org/repos/asf/logging-log4j2/tree/d9e52a36
Diff: http://git-wip-us.apache.org/repos/asf/logging-log4j2/diff/d9e52a36

Branch: refs/heads/master
Commit: d9e52a369d36fb360bc38fd7f885caad3477ffdc
Parents: 2930406
Author: Matt Sicker <ma...@apache.org>
Authored: Sun Oct 5 17:40:05 2014 -0500
Committer: Matt Sicker <ma...@apache.org>
Committed: Sun Oct 5 17:40:05 2014 -0500

----------------------------------------------------------------------
 src/site/xdoc/manual/configuration.xml.vm | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/d9e52a36/src/site/xdoc/manual/configuration.xml.vm
----------------------------------------------------------------------
diff --git a/src/site/xdoc/manual/configuration.xml.vm b/src/site/xdoc/manual/configuration.xml.vm
index be1e529..766f43b 100644
--- a/src/site/xdoc/manual/configuration.xml.vm
+++ b/src/site/xdoc/manual/configuration.xml.vm
@@ -1330,13 +1330,13 @@ public class AwesomeTest {
     </td>
   </tr>
   <tr>
-    <td>log4j.shutdownRegistrationStrategy</td>
+    <td>log4j.shutdownCallbackRegistry</td>
     <!-- deliberately inserted spaces to allow line break -->
-    <td>org.apache.logging .log4j.core.util .DefaultShutdownRegistrationStrategy</td>
+    <td>org.apache.logging .log4j.core.util .DefaultShutdownCallbackRegistry</td>
     <td>
       Fully specified class name of a class implementing
-      <a class="javadoc" href="../log4j-core/apidocs/org/apache/logging/log4j/core/util/ShutdownRegistrationStrategy.html">ShutdownRegistrationStrategy</a>.
-      If specified, an instance of this class is used instead of <tt>DefaultShutdownRegistrationStrategy</tt>.
+      <a class="javadoc" href="../log4j-core/apidocs/org/apache/logging/log4j/core/util/ShutdownCallbackRegistry.html">ShutdownCallbackRegistry</a>.
+      If specified, an instance of this class is used instead of <tt>DefaultShutdownCallbackRegistry</tt>.
       The specified class must have a default constructor.
     </td>
   </tr>