Author: rpopma
Date: Mon Jan 13 07:36:48 2014
New Revision: 1557654
URL: http://svn.apache.org/r1557654
Log:
LOG4J2-406: Unregister all log4j JMX MBeans when the LoggerContext is stopped
to allow web application classes to be GC-ed on undeploy.
Modified:
logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/LoggerContext.java
logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/jmx/Server.java
logging/log4j/log4j2/trunk/src/changes/changes.xml
Modified:
logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/LoggerContext.java
URL:
http://svn.apache.org/viewvc/logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/LoggerContext.java?rev=1557654&r1=1557653&r2=1557654&view=diff
==============================================================================
---
logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/LoggerContext.java
(original)
+++
logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/LoggerContext.java
Mon Jan 13 07:36:48 2014
@@ -20,8 +20,6 @@ import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.File;
import java.net.URI;
-import java.util.HashMap;
-import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.CopyOnWriteArrayList;
@@ -217,6 +215,9 @@ public class LoggerContext implements or
status = Status.STOPPED;
} finally {
configLock.unlock();
+
+ // in finally: unregister MBeans even if an exception occurred
while stopping
+ Server.unregisterMBeans(); // LOG4J2-406
}
}
@@ -350,6 +351,12 @@ public class LoggerContext implements or
for (final PropertyChangeListener listener : propertyChangeListeners) {
listener.propertyChange(evt);
}
+
+ try {
+ Server.reregisterMBeansAfterReconfigure();
+ } catch (final Exception ex) {
+ LOGGER.error("Could not reconfigure JMX", ex);
+ }
return prev;
}
@@ -382,12 +389,6 @@ public class LoggerContext implements or
* updateLoggers(); if (old != null) { old.stop(); }
*/
- try {
- Server.reregisterMBeansAfterReconfigure();
- } catch (final Exception ex) {
- LOGGER.error("Could not reconfigure JMX", ex);
- }
-
LOGGER.debug("Reconfiguration completed");
}
Modified:
logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/jmx/Server.java
URL:
http://svn.apache.org/viewvc/logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/jmx/Server.java?rev=1557654&r1=1557653&r2=1557654&view=diff
==============================================================================
---
logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/jmx/Server.java
(original)
+++
logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/jmx/Server.java
Mon Jan 13 07:36:48 2014
@@ -62,9 +62,8 @@ public final class Server {
* Either returns the specified name as is, or returns a quoted value
* containing the specified name with the special characters (comma,
equals,
* colon, quote, asterisk, or question mark) preceded with a backslash.
- *
- * @param name
- * the name to escape so it can be used as a value in an
+ *
+ * @param name the name to escape so it can be used as a value in an
* {@link ObjectName}.
* @return the escaped name
*/
@@ -78,13 +77,15 @@ public final class Server {
case '*':
case '?':
case '\"':
- sb.append('\\'); // quote, star, question & backslash must be
escaped
+ // quote, star, question & backslash must be escaped
+ sb.append('\\');
needsQuotes = true; // ... and can only appear in quoted value
break;
case ',':
case '=':
case ':':
- needsQuotes = true; // no need to escape these, but value must
be quoted
+ // no need to escape these, but value must be quoted
+ needsQuotes = true;
break;
}
sb.append(c);
@@ -97,17 +98,26 @@ public final class Server {
}
/**
+ * Creates MBeans to instrument classes in the log4j class hierarchy and
+ * registers the MBeans in the platform MBean server so they can be
accessed
+ * by remote clients.
+ *
+ * @throws JMException if a problem occurs during registration
+ */
+ public static void registerMBeans() throws JMException {
+ final ContextSelector selector = getContextSelector();
+ registerMBeans(selector);
+ }
+
+ /**
* Creates MBeans to instrument the specified selector and other classes in
* the log4j class hierarchy and registers the MBeans in the platform MBean
* server so they can be accessed by remote clients.
- *
- * @param selector
- * starting point in the log4j class hierarchy
- * @throws JMException
- * if a problem occurs during registration
+ *
+ * @param selector starting point in the log4j class hierarchy
+ * @throws JMException if a problem occurs during registration
*/
- public static void registerMBeans(final ContextSelector selector)
- throws JMException {
+ public static void registerMBeans(final ContextSelector selector) throws
JMException {
// avoid creating Platform MBean Server if JMX disabled
if (Boolean.getBoolean(PROPERTY_DISABLE_JMX)) {
@@ -122,16 +132,12 @@ public final class Server {
* Creates MBeans to instrument the specified selector and other classes in
* the log4j class hierarchy and registers the MBeans in the specified
MBean
* server so they can be accessed by remote clients.
- *
- * @param selector
- * starting point in the log4j class hierarchy
- * @param mbs
- * the MBean Server to register the instrumented objects in
- * @throws JMException
- * if a problem occurs during registration
+ *
+ * @param selector starting point in the log4j class hierarchy
+ * @param mbs the MBean Server to register the instrumented objects in
+ * @throws JMException if a problem occurs during registration
*/
- public static void registerMBeans(final ContextSelector selector,
- final MBeanServer mbs) throws JMException {
+ public static void registerMBeans(final ContextSelector selector, final
MBeanServer mbs) throws JMException {
if (Boolean.getBoolean(PROPERTY_DISABLE_JMX)) {
LOGGER.debug("JMX disabled for log4j2. Not registering MBeans.");
@@ -143,7 +149,7 @@ public final class Server {
final List<LoggerContext> contexts = selector.getLoggerContexts();
registerContexts(contexts, mbs, executor);
}
-
+
public static void reregisterMBeansAfterReconfigure() {
// avoid creating Platform MBean Server if JMX disabled
if (Boolean.getBoolean(PROPERTY_DISABLE_JMX)) {
@@ -159,31 +165,19 @@ public final class Server {
LOGGER.debug("JMX disabled for log4j2. Not registering MBeans.");
return;
}
-
- try {
- unregisterStatusLogger(mbs);
- registerStatusLogger(mbs, executor);
- } catch (Exception ex) {
- LOGGER.error("Could not register MBeans", ex);
- }
-
- final ContextSelector selector = getContextSelector();
- if (selector == null) {
- LOGGER.debug("Could not register MBeans: no ContextSelector
found.");
- return;
- }
// first unregister the old MBeans
- unregisterContextSelector(mbs);
- unregisterContexts(mbs);
- unregisterLoggerConfigs("*", mbs);
- unregisterAsyncLoggerConfigRingBufferAdmins("*", mbs);
- unregisterAppenders("*", mbs);
- unregisterAsyncAppenders("*", mbs);
+ unregisterMBeans(mbs);
// now provide instrumentation for the newly configured
// LoggerConfigs and Appenders
try {
+ registerStatusLogger(mbs, executor);
+ final ContextSelector selector = getContextSelector();
+ if (selector == null) {
+ LOGGER.debug("Could not register MBeans: no ContextSelector
found.");
+ return;
+ }
registerContextSelector(selector, mbs, executor);
final List<LoggerContext> contexts = selector.getLoggerContexts();
registerContexts(contexts, mbs, executor);
@@ -196,22 +190,52 @@ public final class Server {
}
}
+ /**
+ * Unregister all log4j MBeans from the platform MBean server.
+ */
+ public static void unregisterMBeans() {
+ final MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
+ unregisterMBeans(mbs);
+ }
+
+ /**
+ * Unregister all log4j MBeans from the specified MBean server.
+ *
+ * @param mbs the MBean server to unregister from.
+ */
+ public static void unregisterMBeans(MBeanServer mbs) {
+ unregisterStatusLogger(mbs);
+ unregisterContextSelector(mbs);
+ unregisterContexts(mbs);
+ unregisterLoggerConfigs("*", mbs);
+ unregisterAsyncLoggerRingBufferAdmins("*", mbs);
+ unregisterAsyncLoggerConfigRingBufferAdmins("*", mbs);
+ unregisterAppenders("*", mbs);
+ unregisterAsyncAppenders("*", mbs);
+ }
+
+ /**
+ * Returns the {@code ContextSelector} of the current
+ * {@code Log4jContextFactory}.
+ *
+ * @return the {@code ContextSelector} of the current
+ * {@code Log4jContextFactory}
+ */
private static ContextSelector getContextSelector() {
- ContextSelector selector = null;
final LoggerContextFactory factory = LogManager.getFactory();
if (factory instanceof Log4jContextFactory) {
- selector = ((Log4jContextFactory) factory).getSelector();
+ ContextSelector selector = ((Log4jContextFactory)
factory).getSelector();
+ return selector;
}
- return selector;
+ return null;
}
/**
* Unregisters all MBeans associated with the specified logger context
* (including MBeans for {@code LoggerConfig}s and {@code Appender}s from
* the platform MBean server.
- *
- * @param loggerContextName
- * name of the logger context to unregister
+ *
+ * @param loggerContextName name of the logger context to unregister
*/
public static void unregisterContext(String loggerContextName) {
final MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
@@ -222,11 +246,9 @@ public final class Server {
* Unregisters all MBeans associated with the specified logger context
* (including MBeans for {@code LoggerConfig}s and {@code Appender}s from
* the platform MBean server.
- *
- * @param loggerContextName
- * name of the logger context to unregister
- * @param mbs
- * the MBean Server to unregister the instrumented objects from
+ *
+ * @param loggerContextName name of the logger context to unregister
+ * @param mbs the MBean Server to unregister the instrumented objects from
*/
public static void unregisterContext(String contextName, MBeanServer mbs) {
final String pattern = LoggerContextAdminMBean.PATTERN;
@@ -255,9 +277,9 @@ public final class Server {
}
/**
- * Registers MBeans for all contexts in the list.
- * First unregisters each context (and nested loggers, appender etc)
- * to prevent InstanceAlreadyExistsExceptions.
+ * Registers MBeans for all contexts in the list. First unregisters each
+ * context (and nested loggers, appender etc) to prevent
+ * InstanceAlreadyExistsExceptions.
*/
private static void registerContexts(final List<LoggerContext> contexts,
final MBeanServer mbs,
final Executor executor) throws InstanceAlreadyExistsException,
MBeanRegistrationException,
@@ -266,10 +288,10 @@ public final class Server {
for (final LoggerContext ctx : contexts) {
// first unregister the context and all nested loggers & appenders
unregisterContext(ctx.getName());
-
+
final LoggerContextAdmin mbean = new LoggerContextAdmin(ctx,
executor);
register(mbs, mbean, mbean.getObjectName());
-
+
if (ctx instanceof AsyncLoggerContext) {
RingBufferAdmin rbmbean =
AsyncLogger.createRingBufferAdmin(ctx.getName());
register(mbs, rbmbean, rbmbean.getObjectName());
@@ -285,8 +307,7 @@ public final class Server {
unregisterAllMatching(ContextSelectorAdminMBean.NAME, mbs);
}
- private static void unregisterLoggerConfigs(final String contextName,
- final MBeanServer mbs) {
+ private static void unregisterLoggerConfigs(final String contextName,
final MBeanServer mbs) {
final String pattern = LoggerConfigAdminMBean.PATTERN;
final String search = String.format(pattern, contextName, "*");
unregisterAllMatching(search, mbs);
@@ -298,29 +319,25 @@ public final class Server {
unregisterAllMatching(search, mbs);
}
- private static void unregisterAppenders(final String contextName,
- final MBeanServer mbs) {
+ private static void unregisterAppenders(final String contextName, final
MBeanServer mbs) {
final String pattern = AppenderAdminMBean.PATTERN;
final String search = String.format(pattern, contextName, "*");
unregisterAllMatching(search, mbs);
}
- private static void unregisterAsyncAppenders(final String contextName,
- final MBeanServer mbs) {
+ private static void unregisterAsyncAppenders(final String contextName,
final MBeanServer mbs) {
final String pattern = AsyncAppenderAdminMBean.PATTERN;
final String search = String.format(pattern, contextName, "*");
unregisterAllMatching(search, mbs);
}
- private static void unregisterAsyncLoggerRingBufferAdmins(final String
contextName,
- final MBeanServer mbs) {
+ private static void unregisterAsyncLoggerRingBufferAdmins(final String
contextName, final MBeanServer mbs) {
final String pattern1 = RingBufferAdminMBean.PATTERN_ASYNC_LOGGER;
final String search1 = String.format(pattern1, contextName);
unregisterAllMatching(search1, mbs);
}
- private static void unregisterAsyncLoggerConfigRingBufferAdmins(final
String contextName,
- final MBeanServer mbs) {
+ private static void unregisterAsyncLoggerConfigRingBufferAdmins(final
String contextName, final MBeanServer mbs) {
final String pattern2 =
RingBufferAdminMBean.PATTERN_ASYNC_LOGGER_CONFIG;
final String search2 = String.format(pattern2, contextName, "*");
unregisterAllMatching(search2, mbs);
@@ -347,7 +364,7 @@ public final class Server {
final LoggerConfig cfg = map.get(name);
final LoggerConfigAdmin mbean = new
LoggerConfigAdmin(ctx.getName(), cfg);
register(mbs, mbean, mbean.getObjectName());
-
+
if (cfg instanceof AsyncLoggerConfig) {
AsyncLoggerConfig async = (AsyncLoggerConfig) cfg;
RingBufferAdmin rbmbean =
async.createRingBufferAdmin(ctx.getName());
@@ -362,7 +379,7 @@ public final class Server {
final Map<String, Appender> map =
ctx.getConfiguration().getAppenders();
for (final String name : map.keySet()) {
final Appender appender = map.get(name);
-
+
if (appender instanceof AsyncAppender) {
AsyncAppender async = ((AsyncAppender) appender);
final AsyncAppenderAdmin mbean = new
AsyncAppenderAdmin(ctx.getName(), async);
@@ -373,8 +390,8 @@ public final class Server {
}
}
}
-
- private static void register(MBeanServer mbs, Object mbean, ObjectName
objectName)
+
+ private static void register(MBeanServer mbs, Object mbean, ObjectName
objectName)
throws InstanceAlreadyExistsException, MBeanRegistrationException,
NotCompliantMBeanException {
LOGGER.debug("Registering MBean {}", objectName);
mbs.registerMBean(mbean, objectName);
Modified: logging/log4j/log4j2/trunk/src/changes/changes.xml
URL:
http://svn.apache.org/viewvc/logging/log4j/log4j2/trunk/src/changes/changes.xml?rev=1557654&r1=1557653&r2=1557654&view=diff
==============================================================================
--- logging/log4j/log4j2/trunk/src/changes/changes.xml (original)
+++ logging/log4j/log4j2/trunk/src/changes/changes.xml Mon Jan 13 07:36:48 2014
@@ -21,6 +21,10 @@
</properties>
<body>
<release version="2.0-RC1" date="2013-MM-DD" description="Bug fixes and
enhancements">
+ <action issue="LOG4J2-406" dev="rpopma" type="fix" due-to="Kerrigan
Joseph">
+ Unregister all log4j JMX MBeans when the LoggerContext is stopped
+ to allow web application classes to be GC-ed on undeploy.
+ </action>
<action issue="LOG4J2-405" dev="rgoers" type="fix">
Configuration was being processed twice at startup.
</action>