Author: rpopma
Date: Sun Feb 2 23:24:40 2014
New Revision: 1563745
URL: http://svn.apache.org/r1563745
Log:
LOG4J2-500 Better JMX support for multiple contexts (todo: tests & doc updates)
Modified:
logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/jmx/ContextSelectorAdmin.java
logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/jmx/ContextSelectorAdminMBean.java
logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/jmx/Server.java
logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/jmx/StatusLoggerAdmin.java
logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/jmx/StatusLoggerAdminMBean.java
logging/log4j/log4j2/trunk/log4j-jmx-gui/src/main/java/org/apache/logging/log4j/jmx/gui/Client.java
logging/log4j/log4j2/trunk/log4j-jmx-gui/src/main/java/org/apache/logging/log4j/jmx/gui/ClientGUI.java
Modified:
logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/jmx/ContextSelectorAdmin.java
URL:
http://svn.apache.org/viewvc/logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/jmx/ContextSelectorAdmin.java?rev=1563745&r1=1563744&r2=1563745&view=diff
==============================================================================
---
logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/jmx/ContextSelectorAdmin.java
(original)
+++
logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/jmx/ContextSelectorAdmin.java
Sun Feb 2 23:24:40 2014
@@ -31,14 +31,22 @@ public class ContextSelectorAdmin implem
/**
* Constructs a new {@code ContextSelectorAdmin}.
- *
+ *
+ * @param contextName name of the LoggerContext under which to register
this
+ * ContextSelectorAdmin. Note that the ContextSelector may be
+ * registered multiple times, once for each LoggerContext. In
web
+ * containers, each web application has its own LoggerContext
and
+ * by associating the ContextSelector with the LoggerContext,
all
+ * associated MBeans can be unloaded when the web application is
+ * undeployed.
* @param selector the instrumented object
*/
- public ContextSelectorAdmin(final ContextSelector selector) {
+ public ContextSelectorAdmin(final String contextName, final
ContextSelector selector) {
super();
this.selector = Assert.isNotNull(selector, "ContextSelector");
try {
- objectName = new ObjectName(NAME);
+ final String mbeanName = String.format(PATTERN,
Server.escape(contextName));
+ objectName = new ObjectName(mbeanName);
} catch (final Exception e) {
throw new IllegalStateException(e);
}
@@ -46,7 +54,7 @@ public class ContextSelectorAdmin implem
/**
* Returns the {@code ObjectName} of this mbean.
- *
+ *
* @return the {@code ObjectName}
* @see ContextSelectorAdminMBean#NAME
*/
Modified:
logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/jmx/ContextSelectorAdminMBean.java
URL:
http://svn.apache.org/viewvc/logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/jmx/ContextSelectorAdminMBean.java?rev=1563745&r1=1563744&r2=1563745&view=diff
==============================================================================
---
logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/jmx/ContextSelectorAdminMBean.java
(original)
+++
logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/jmx/ContextSelectorAdminMBean.java
Sun Feb 2 23:24:40 2014
@@ -21,9 +21,29 @@ package org.apache.logging.log4j.core.jm
*/
public interface ContextSelectorAdminMBean {
/**
- * ObjectName pattern ({@value}) for the context selector MBean.
+ * ObjectName pattern ({@value}) for ContextSelectorAdmin MBeans.
+ * This pattern contains a variable, which is the name of the logger
context.
+ * <p>
+ * You can find all registered ContextSelectorAdmin MBeans like this:
+ * </p>
+ * <pre>
+ * MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
+ * String pattern = String.format(ContextSelectorAdminMBean.PATTERN,
"*");
+ * Set<ObjectName> contextSelectorNames = mbs.queryNames(new
ObjectName(pattern), null);
+ * </pre>
+ * <p>
+ * Some characters are not allowed in ObjectNames. The logger context name
+ * may be quoted. When ContextSelectorAdmin MBeans are
+ * registered, their ObjectNames are created using this pattern as follows:
+ * </p>
+ * <pre>
+ * String ctxName = Server.escape(loggerContext.getName());
+ * String name = String.format(PATTERN, ctxName);
+ * ObjectName objectName = new ObjectName(name);
+ * </pre>
+ * @see Server#escape(String)
*/
- String NAME = "org.apache.logging.log4j2:type=ContextSelector";
+ String PATTERN =
"org.apache.logging.log4j2:type=LoggerContext,ctx=%s,sub=ContextSelector";
/**
* Returns the name of the class implementing the {@code ContextSelector}
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=1563745&r1=1563744&r2=1563745&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
Sun Feb 2 23:24:40 2014
@@ -24,7 +24,6 @@ import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import javax.management.InstanceAlreadyExistsException;
-import javax.management.JMException;
import javax.management.MBeanRegistrationException;
import javax.management.MBeanServer;
import javax.management.NotCompliantMBeanException;
@@ -105,59 +104,6 @@ public final class Server {
return sb.toString();
}
- /**
- * 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
- */
- public static void registerMBeans(final ContextSelector selector) throws
JMException {
-
- // avoid creating Platform MBean Server if JMX disabled
- if (Boolean.getBoolean(PROPERTY_DISABLE_JMX)) {
- LOGGER.debug("JMX disabled for log4j2. Not registering MBeans.");
- return;
- }
- final MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
- registerMBeans(selector, mbs);
- }
-
- /**
- * 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
- */
- 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.");
- return;
- }
- registerStatusLogger(mbs, executor);
- registerContextSelector(selector, mbs, executor);
-
- 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)) {
@@ -175,23 +121,43 @@ public final class Server {
}
// first unregister the old MBeans
+ // TODO is this too drastic? This may impact the MBean of other
+ // webapps...
+ // but below we will only re-register the MBeans of OUR context
selector
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);
- for (LoggerContext context : contexts) {
- registerLoggerConfigs(context, mbs, executor);
- registerAppenders(context, mbs, executor);
+ for (LoggerContext ctx : contexts) {
+ // first unregister the context and all nested loggers,
+ // appenders, statusLogger, contextSelector, ringbuffers...
+ unregisterAll(ctx.getName(), mbs);
+
+ 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());
+ }
+
+ // register the status logger and the context selector
+ // repeatedly
+ // for each known context: if one context is unregistered,
+ // these MBeans should still be available for the other
+ // contexts.
+ registerStatusLogger(ctx.getName(), mbs, executor);
+ registerContextSelector(ctx.getName(), selector, mbs,
executor);
+
+ registerLoggerConfigs(ctx, mbs, executor);
+ registerAppenders(ctx, mbs, executor);
}
} catch (final Exception ex) {
LOGGER.error("Could not register mbeans", ex);
@@ -212,8 +178,8 @@ public final class Server {
* @param mbs the MBean server to unregister from.
*/
public static void unregisterMBeans(MBeanServer mbs) {
- unregisterStatusLogger(mbs);
- unregisterContextSelector(mbs);
+ unregisterStatusLogger("*", mbs);
+ unregisterContextSelector("*", mbs);
unregisterContexts(mbs);
unregisterLoggerConfigs("*", mbs);
unregisterAsyncLoggerRingBufferAdmins("*", mbs);
@@ -245,9 +211,9 @@ public final class Server {
*
* @param loggerContextName name of the logger context to unregister
*/
- public static void unregisterContext(String loggerContextName) {
+ public static void unregisterAll(String loggerContextName) {
final MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
- unregisterContext(loggerContextName, mbs);
+ unregisterAll(loggerContextName, mbs);
}
/**
@@ -258,67 +224,51 @@ public final class Server {
* @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) {
+ public static void unregisterAll(String contextName, MBeanServer mbs) {
final String pattern = LoggerContextAdminMBean.PATTERN;
- final String safeContextName = escape(contextName);
- final String search = String.format(pattern, safeContextName, "*");
+ final String search = String.format(pattern, escape(contextName), "*");
unregisterAllMatching(search, mbs); // unregister context mbean
- unregisterLoggerConfigs(safeContextName, mbs);
- unregisterAppenders(safeContextName, mbs);
- unregisterAsyncAppenders(safeContextName, mbs);
- unregisterAsyncLoggerRingBufferAdmins(safeContextName, mbs);
- unregisterAsyncLoggerConfigRingBufferAdmins(safeContextName, mbs);
+
+ // now unregister all MBeans associated with this logger context
+ unregisterStatusLogger(contextName, mbs);
+ unregisterContextSelector(contextName, mbs);
+ unregisterLoggerConfigs(contextName, mbs);
+ unregisterAppenders(contextName, mbs);
+ unregisterAsyncAppenders(contextName, mbs);
+ unregisterAsyncLoggerRingBufferAdmins(contextName, mbs);
+ unregisterAsyncLoggerConfigRingBufferAdmins(contextName, mbs);
}
- private static void registerStatusLogger(final MBeanServer mbs, final
Executor executor)
+ private static void registerStatusLogger(final String contextName, final
MBeanServer mbs, final Executor executor)
throws InstanceAlreadyExistsException, MBeanRegistrationException,
NotCompliantMBeanException {
- final StatusLoggerAdmin mbean = new StatusLoggerAdmin(executor);
+ final StatusLoggerAdmin mbean = new StatusLoggerAdmin(contextName,
executor);
register(mbs, mbean, mbean.getObjectName());
}
- private static void registerContextSelector(final ContextSelector
selector, final MBeanServer mbs,
- final Executor executor) throws InstanceAlreadyExistsException,
MBeanRegistrationException,
- NotCompliantMBeanException {
+ private static void registerContextSelector(final String contextName,
final ContextSelector selector,
+ final MBeanServer mbs, final Executor executor) throws
InstanceAlreadyExistsException,
+ MBeanRegistrationException, NotCompliantMBeanException {
- final ContextSelectorAdmin mbean = new ContextSelectorAdmin(selector);
+ final ContextSelectorAdmin mbean = new
ContextSelectorAdmin(contextName, selector);
register(mbs, mbean, mbean.getObjectName());
}
- /**
- * 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,
- NotCompliantMBeanException {
-
- 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());
- }
- }
- }
-
- private static void unregisterStatusLogger(final MBeanServer mbs) {
- unregisterAllMatching(StatusLoggerAdminMBean.NAME, mbs);
+ private static void unregisterStatusLogger(final String contextName, final
MBeanServer mbs) {
+ final String pattern = StatusLoggerAdminMBean.PATTERN;
+ final String search = String.format(pattern, escape(contextName), "*");
+ unregisterAllMatching(search, mbs);
}
- private static void unregisterContextSelector(final MBeanServer mbs) {
- unregisterAllMatching(ContextSelectorAdminMBean.NAME, mbs);
+ private static void unregisterContextSelector(final String contextName,
final MBeanServer mbs) {
+ final String pattern = ContextSelectorAdminMBean.PATTERN;
+ final String search = String.format(pattern, escape(contextName), "*");
+ unregisterAllMatching(search, mbs);
}
private static void unregisterLoggerConfigs(final String contextName,
final MBeanServer mbs) {
final String pattern = LoggerConfigAdminMBean.PATTERN;
- final String search = String.format(pattern, contextName, "*");
+ final String search = String.format(pattern, escape(contextName), "*");
unregisterAllMatching(search, mbs);
}
@@ -330,25 +280,25 @@ public final class Server {
private static void unregisterAppenders(final String contextName, final
MBeanServer mbs) {
final String pattern = AppenderAdminMBean.PATTERN;
- final String search = String.format(pattern, contextName, "*");
+ final String search = String.format(pattern, escape(contextName), "*");
unregisterAllMatching(search, mbs);
}
private static void unregisterAsyncAppenders(final String contextName,
final MBeanServer mbs) {
final String pattern = AsyncAppenderAdminMBean.PATTERN;
- final String search = String.format(pattern, contextName, "*");
+ final String search = String.format(pattern, escape(contextName), "*");
unregisterAllMatching(search, 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);
+ final String search1 = String.format(pattern1, escape(contextName));
unregisterAllMatching(search1, 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, "*");
+ final String search2 = String.format(pattern2, escape(contextName),
"*");
unregisterAllMatching(search2, mbs);
}
Modified:
logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/jmx/StatusLoggerAdmin.java
URL:
http://svn.apache.org/viewvc/logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/jmx/StatusLoggerAdmin.java?rev=1563745&r1=1563744&r2=1563745&view=diff
==============================================================================
---
logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/jmx/StatusLoggerAdmin.java
(original)
+++
logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/jmx/StatusLoggerAdmin.java
Sun Feb 2 23:24:40 2014
@@ -32,23 +32,32 @@ import org.apache.logging.log4j.status.S
/**
* Implementation of the {@code StatusLoggerAdminMBean} interface.
*/
-public class StatusLoggerAdmin extends NotificationBroadcasterSupport
implements
- StatusListener, StatusLoggerAdminMBean {
+public class StatusLoggerAdmin extends NotificationBroadcasterSupport
implements StatusListener, StatusLoggerAdminMBean {
private final AtomicLong sequenceNo = new AtomicLong();
private final ObjectName objectName;
+ private final String contextName;
private Level level = Level.WARN;
/**
* Constructs a new {@code StatusLoggerAdmin} with the {@code Executor} to
* be used for sending {@code Notification}s asynchronously to listeners.
- *
+ *
+ * @param contextName name of the LoggerContext under which to register
this
+ * StatusLoggerAdmin. Note that the StatusLogger may be
+ * registered multiple times, once for each LoggerContext. In
web
+ * containers, each web application has its own LoggerContext
and
+ * by associating the StatusLogger with the LoggerContext, all
+ * associated MBeans can be unloaded when the web application is
+ * undeployed.
* @param executor used to send notifications asynchronously
*/
- public StatusLoggerAdmin(final Executor executor) {
+ public StatusLoggerAdmin(final String contextName, final Executor
executor) {
super(executor, createNotificationInfo());
+ this.contextName = contextName;
try {
- objectName = new ObjectName(NAME);
+ final String mbeanName = String.format(PATTERN,
Server.escape(contextName));
+ objectName = new ObjectName(mbeanName);
} catch (final Exception e) {
throw new IllegalStateException(e);
}
@@ -93,28 +102,31 @@ public class StatusLoggerAdmin extends N
this.level = Level.toLevel(level, Level.ERROR);
}
+ public String getContextName() {
+ return contextName;
+ }
+
/*
* (non-Javadoc)
- *
+ *
* @see
* org.apache.logging.log4j.status.StatusListener#log(org.apache.logging
* .log4j.status.StatusData)
*/
@Override
public void log(final StatusData data) {
- final Notification notifMsg = new Notification(NOTIF_TYPE_MESSAGE,
- getObjectName(), nextSeqNo(), now(),
data.getFormattedStatus());
+ final Notification notifMsg = new Notification(NOTIF_TYPE_MESSAGE,
getObjectName(), nextSeqNo(), now(),
+ data.getFormattedStatus());
sendNotification(notifMsg);
- final Notification notifData = new Notification(NOTIF_TYPE_DATA,
- getObjectName(), nextSeqNo(), now());
+ final Notification notifData = new Notification(NOTIF_TYPE_DATA,
getObjectName(), nextSeqNo(), now());
notifData.setUserData(data);
sendNotification(notifData);
}
/**
* Returns the {@code ObjectName} of this mbean.
- *
+ *
* @return the {@code ObjectName}
* @see StatusLoggerAdminMBean#NAME
*/
Modified:
logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/jmx/StatusLoggerAdminMBean.java
URL:
http://svn.apache.org/viewvc/logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/jmx/StatusLoggerAdminMBean.java?rev=1563745&r1=1563744&r2=1563745&view=diff
==============================================================================
---
logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/jmx/StatusLoggerAdminMBean.java
(original)
+++
logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/jmx/StatusLoggerAdminMBean.java
Sun Feb 2 23:24:40 2014
@@ -18,14 +18,38 @@ package org.apache.logging.log4j.core.jm
import java.util.List;
+import javax.management.ObjectName;
+
import org.apache.logging.log4j.status.StatusData;
/**
* The MBean interface for monitoring and managing the {@code StatusLogger}.
*/
public interface StatusLoggerAdminMBean {
- /** Object name ({@value}) of this MBean. */
- String NAME = "org.apache.logging.log4j2:type=StatusLogger";
+ /**
+ * ObjectName pattern ({@value}) for StatusLoggerAdmin MBeans.
+ * This pattern contains a variable, which is the name of the logger
context.
+ * <p>
+ * You can find all registered StatusLoggerAdmin MBeans like this:
+ * </p>
+ * <pre>
+ * MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
+ * String pattern = String.format(StatusLoggerAdminMBean.PATTERN,
"*");
+ * Set<ObjectName> statusLoggerNames = mbs.queryNames(new
ObjectName(pattern), null);
+ * </pre>
+ * <p>
+ * Some characters are not allowed in ObjectNames. The logger context name
+ * may be quoted. When StatusLoggerAdmin MBeans are
+ * registered, their ObjectNames are created using this pattern as follows:
+ * </p>
+ * <pre>
+ * String ctxName = Server.escape(loggerContext.getName());
+ * String name = String.format(PATTERN, ctxName);
+ * ObjectName objectName = new ObjectName(name);
+ * </pre>
+ * @see Server#escape(String)
+ */
+ String PATTERN =
"org.apache.logging.log4j2:type=LoggerContext,ctx=%s,sub=StatusLogger";
/**
* Notifications with this type have a {@code StatusData} userData object
@@ -40,6 +64,12 @@ public interface StatusLoggerAdminMBean
String NOTIF_TYPE_MESSAGE =
"com.apache.logging.log4j.core.jmx.statuslogger.message";
/**
+ * Returns the {@code ObjectName} that this status logger mbean is
registered with.
+ * @return
+ */
+ public ObjectName getObjectName();
+
+ /**
* Returns a list with the most recent {@code StatusData} objects in the
* status history. The list has up to 200 entries by default but the length
* can be configured with system property {@code "log4j2.status.entries"}.
@@ -81,4 +111,9 @@ public interface StatusLoggerAdminMBean
*/
void setLevel(String level);
+ /**
+ * Returns the name of the LoggerContext that the {@code StatusLogger} is
associated with.
+ * @return logger context name
+ */
+ String getContextName();
}
Modified:
logging/log4j/log4j2/trunk/log4j-jmx-gui/src/main/java/org/apache/logging/log4j/jmx/gui/Client.java
URL:
http://svn.apache.org/viewvc/logging/log4j/log4j2/trunk/log4j-jmx-gui/src/main/java/org/apache/logging/log4j/jmx/gui/Client.java?rev=1563745&r1=1563744&r2=1563745&view=diff
==============================================================================
---
logging/log4j/log4j2/trunk/log4j-jmx-gui/src/main/java/org/apache/logging/log4j/jmx/gui/Client.java
(original)
+++
logging/log4j/log4j2/trunk/log4j-jmx-gui/src/main/java/org/apache/logging/log4j/jmx/gui/Client.java
Sun Feb 2 23:24:40 2014
@@ -21,6 +21,7 @@ import java.util.ArrayList;
import java.util.List;
import java.util.Set;
+import javax.management.JMException;
import javax.management.JMX;
import javax.management.MBeanServerConnection;
import javax.management.MalformedObjectNameException;
@@ -30,6 +31,7 @@ import javax.management.remote.JMXConnec
import org.apache.logging.log4j.core.helpers.Assert;
import org.apache.logging.log4j.core.jmx.ContextSelectorAdminMBean;
import org.apache.logging.log4j.core.jmx.LoggerContextAdminMBean;
+import org.apache.logging.log4j.core.jmx.Server;
import org.apache.logging.log4j.core.jmx.StatusLoggerAdminMBean;
/**
@@ -39,22 +41,21 @@ import org.apache.logging.log4j.core.jmx
public class Client {
private JMXConnector connector;
private final MBeanServerConnection connection;
- private StatusLoggerAdminMBean statusLoggerAdmin;
- private ContextSelectorAdminMBean contextSelectorAdmin;
+ private List<StatusLoggerAdminMBean> statusLoggerAdminList;
+ private List<ContextSelectorAdminMBean> contextSelectorAdminList;
private List<LoggerContextAdminMBean> contextAdminList;
/**
* Constructs a new {@code Client} object and creates proxies for all known
* remote MBeans.
- *
+ *
* @param connector used to create the MBean server connection through
which
* to communicate with the remote mbeans
* @throws MalformedObjectNameException if a problem occurred identifying
* one of the remote mbeans
* @throws IOException if the connection failed
*/
- public Client(final JMXConnector connector) throws
MalformedObjectNameException,
- IOException {
+ public Client(final JMXConnector connector) throws JMException,
IOException {
this.connector = Assert.isNotNull(connector, "JMXConnector");
this.connector.connect();
this.connection = connector.getMBeanServerConnection();
@@ -64,33 +65,40 @@ public class Client {
/**
* Constructs a new {@code Client} object and creates proxies for all known
* remote MBeans.
- *
+ *
* @param mBeanServerConnection the MBean server connection through which
to
* communicate with the remote mbeans
* @throws MalformedObjectNameException if a problem occurred identifying
* one of the remote mbeans
* @throws IOException if the connection failed
*/
- public Client(final MBeanServerConnection mBeanServerConnection)
- throws MalformedObjectNameException, IOException {
+ public Client(final MBeanServerConnection mBeanServerConnection) throws
JMException, IOException {
this.connection = mBeanServerConnection;
init();
}
- private void init() throws MalformedObjectNameException, IOException {
- statusLoggerAdmin = JMX.newMBeanProxy(connection, //
- new ObjectName(StatusLoggerAdminMBean.NAME), //
- StatusLoggerAdminMBean.class, true);
-
- contextSelectorAdmin = JMX.newMBeanProxy(connection, //
- new ObjectName(ContextSelectorAdminMBean.NAME), //
- ContextSelectorAdminMBean.class, false);
+ private void init() throws JMException, IOException {
+ statusLoggerAdminList = new ArrayList<StatusLoggerAdminMBean>();
+ final Set<ObjectName> statusLogNames =
find(StatusLoggerAdminMBean.PATTERN);
+ for (final ObjectName statusLogName : statusLogNames) {
+ final StatusLoggerAdminMBean ctx = JMX.newMBeanProxy(connection, //
+ statusLogName, //
+ StatusLoggerAdminMBean.class, true); //
notificationBroadcaster
+ statusLoggerAdminList.add(ctx);
+ }
+
+ contextSelectorAdminList = new ArrayList<ContextSelectorAdminMBean>();
+ final Set<ObjectName> selectorNames =
find(ContextSelectorAdminMBean.PATTERN);
+ for (final ObjectName selectorName : selectorNames) {
+ final ContextSelectorAdminMBean ctx =
JMX.newMBeanProxy(connection, //
+ selectorName, //
+ ContextSelectorAdminMBean.class, false);
+ contextSelectorAdminList.add(ctx);
+ }
contextAdminList = new ArrayList<LoggerContextAdminMBean>();
- final String pattern = String.format(LoggerContextAdminMBean.PATTERN,
"*");
- final ObjectName search = new ObjectName(pattern);
- final Set<ObjectName> found = connection.queryNames(search, null);
- for (final ObjectName contextName : found) {
+ final Set<ObjectName> contextNames =
find(LoggerContextAdminMBean.PATTERN);
+ for (final ObjectName contextName : contextNames) {
final LoggerContextAdminMBean ctx = JMX.newMBeanProxy(connection,
//
contextName, //
LoggerContextAdminMBean.class, false);
@@ -100,20 +108,27 @@ public class Client {
}
}
+ private Set<ObjectName> find(String pattern) throws JMException,
IOException {
+ final ObjectName search = new ObjectName(String.format(pattern, "*"));
+ final Set<ObjectName> result = connection.queryNames(search, null);
+ return result;
+ }
+
/**
- * Returns a proxy that allows operations to be performed on the remote
- * {@code ContextSelectorAdminMBean}.
- *
- * @return a proxy to the remote {@code ContextSelectorAdminMBean}
+ * Returns a list of proxies that allows operations to be performed on the
+ * remote {@code ContextSelectorAdminMBean}s.
+ *
+ * @return a list of proxies to the remote {@code
ContextSelectorAdminMBean}
+ * s
*/
- public ContextSelectorAdminMBean getContextSelectorAdmin() {
- return contextSelectorAdmin;
+ public List<ContextSelectorAdminMBean> getContextSelectorAdminList() {
+ return contextSelectorAdminList;
}
/**
* Returns a list of proxies that allow operations to be performed on the
* remote {@code LoggerContextAdminMBean}s.
- *
+ *
* @return a list of proxies to the remote {@code LoggerContextAdminMBean}s
*/
public List<LoggerContextAdminMBean> getLoggerContextAdmins() {
@@ -135,7 +150,7 @@ public class Client {
/**
* Returns the MBean server connection through which to communicate with
the
* remote mbeans.
- *
+ *
* @return the MBean server connection
*/
public MBeanServerConnection getConnection() {
@@ -143,12 +158,39 @@ public class Client {
}
/**
- * Returns a proxy that allows operations to be performed on the remote
- * {@code StatusLoggerAdminMBean}.
- *
- * @return a proxy to the remote {@code StatusLoggerAdminMBean}
+ * Returns a list of proxies that allows operations to be performed on the
+ * remote {@code StatusLoggerAdminMBean}s.
+ *
+ * @return a list of proxies to the remote {@code StatusLoggerAdminMBean}s
*/
- public StatusLoggerAdminMBean getStatusLoggerAdmin() {
- return statusLoggerAdmin;
+ public List<StatusLoggerAdminMBean> getStatusLoggerAdminList() {
+ return statusLoggerAdminList;
+ }
+
+ /**
+ * Returns the {@code StatusLoggerAdminMBean} associated with the specified
+ * context name, or {@code null}.
+ *
+ * @param contextName search key
+ * @return StatusLoggerAdminMBean or null
+ * @throws MalformedObjectNameException
+ * @throws IOException
+ */
+ public StatusLoggerAdminMBean getStatusLoggerAdmin(String contextName)
throws MalformedObjectNameException,
+ IOException {
+ final String pattern = StatusLoggerAdminMBean.PATTERN;
+ final String mbean = String.format(pattern,
Server.escape(contextName));
+ final ObjectName search = new ObjectName(mbean);
+ final Set<ObjectName> result = connection.queryNames(search, null);
+ if (result.size() == 0) {
+ return null;
+ }
+ if (result.size() > 1) {
+ System.err.println("WARN: multiple status loggers found for " +
contextName + ": " + result);
+ }
+ final StatusLoggerAdminMBean proxy = JMX.newMBeanProxy(connection, //
+ result.iterator().next(), //
+ StatusLoggerAdminMBean.class, true); // notificationBroadcaster
+ return proxy;
}
}
Modified:
logging/log4j/log4j2/trunk/log4j-jmx-gui/src/main/java/org/apache/logging/log4j/jmx/gui/ClientGUI.java
URL:
http://svn.apache.org/viewvc/logging/log4j/log4j2/trunk/log4j-jmx-gui/src/main/java/org/apache/logging/log4j/jmx/gui/ClientGUI.java?rev=1563745&r1=1563744&r2=1563745&view=diff
==============================================================================
---
logging/log4j/log4j2/trunk/log4j-jmx-gui/src/main/java/org/apache/logging/log4j/jmx/gui/ClientGUI.java
(original)
+++
logging/log4j/log4j2/trunk/log4j-jmx-gui/src/main/java/org/apache/logging/log4j/jmx/gui/ClientGUI.java
Sun Feb 2 23:24:40 2014
@@ -53,88 +53,102 @@ import org.apache.logging.log4j.core.jmx
import org.apache.logging.log4j.core.jmx.StatusLoggerAdminMBean;
/**
- * Swing GUI that connects to a Java process via JMX and allows the user to
view and
- * modify the Log4j 2 configuration, as well as monitor status logs.
- *
+ * Swing GUI that connects to a Java process via JMX and allows the user to
view
+ * and modify the Log4j 2 configuration, as well as monitor status logs.
+ *
* @see <a href=
*
"http://docs.oracle.com/javase/6/docs/technotes/guides/management/jconsole.html"
- *
>http://docs.oracle.com/javase/6/docs/technotes/guides/management/jconsole.html</a
>
+ * >http://docs.oracle.com/javase/6/docs/technotes/guides/management/
+ * jconsole.html</a >
*/
public class ClientGUI extends JPanel implements NotificationListener {
private static final long serialVersionUID = -253621277232291174L;
private final Client client;
- private JTextArea statusLogTextArea;
- private JTabbedPane tabbedPane;
- private JToggleButton wrapLinesToggleButton;
+ private Map<String, JTextArea> statusLogTextAreaMap = new HashMap<String,
JTextArea>();
+ private JTabbedPane tabbedPaneContexts;
- private final AbstractAction toggleWrapAction = new AbstractAction() {
- private static final long serialVersionUID = -4214143754637722322L;
-
- @Override
- public void actionPerformed(final ActionEvent e) {
- final boolean wrap = wrapLinesToggleButton.isSelected();
- statusLogTextArea.setLineWrap(wrap);
- }
- };
-
- public ClientGUI(final Client client) throws InstanceNotFoundException,
- MalformedObjectNameException, IOException {
+ public ClientGUI(final Client client) throws InstanceNotFoundException,
MalformedObjectNameException, IOException {
this.client = Assert.isNotNull(client, "client");
createWidgets();
populateWidgets();
- registerListeners();
}
private void createWidgets() {
- statusLogTextArea = new JTextArea();
- statusLogTextArea.setEditable(false);
- statusLogTextArea.setBackground(this.getBackground());
- statusLogTextArea.setForeground(Color.black);
- statusLogTextArea.setFont(new Font("Monospaced", Font.PLAIN,
- statusLogTextArea.getFont().getSize()));
- statusLogTextArea.setWrapStyleWord(true);
-
- wrapLinesToggleButton = new JToggleButton(toggleWrapAction);
- wrapLinesToggleButton.setToolTipText("Toggle line wrapping");
- final JScrollPane scrollStatusLog = new JScrollPane(statusLogTextArea,
//
- ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS, //
- ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS);
- scrollStatusLog.setCorner(ScrollPaneConstants.LOWER_RIGHT_CORNER,
wrapLinesToggleButton);
-
- tabbedPane = new JTabbedPane();
- tabbedPane.addTab("StatusLogger", scrollStatusLog);
-
+ tabbedPaneContexts = new JTabbedPane();
this.setLayout(new BorderLayout());
- this.add(tabbedPane, BorderLayout.CENTER);
+ this.add(tabbedPaneContexts, BorderLayout.CENTER);
}
- private void populateWidgets() {
-
- final StatusLoggerAdminMBean statusAdmin =
client.getStatusLoggerAdmin();
- final String[] messages = statusAdmin.getStatusDataHistory();
- for (final String message : messages) {
- statusLogTextArea.append(message + "\n");
- }
+ private void populateWidgets() throws MalformedObjectNameException,
IOException, InstanceNotFoundException {
for (final LoggerContextAdminMBean ctx :
client.getLoggerContextAdmins()) {
+ JTabbedPane contextTabs = new JTabbedPane();
+ tabbedPaneContexts.addTab("LoggerContext: " + ctx.getName(),
contextTabs);
+
+ String contextName = ctx.getName();
+ StatusLoggerAdminMBean status =
client.getStatusLoggerAdmin(contextName);
+ if (status != null) {
+ JTextArea text = createTextArea();
+ final String[] messages = status.getStatusDataHistory();
+ for (final String message : messages) {
+ text.append(message + "\n");
+ }
+ statusLogTextAreaMap.put(status.getContextName(), text);
+ registerListeners(status);
+ JScrollPane scroll = scroll(text);
+ contextTabs.addTab("StatusLogger", scroll);
+ }
+
final ClientEditConfigPanel editor = new
ClientEditConfigPanel(ctx);
- tabbedPane.addTab("LoggerContext: " + ctx.getName(), editor);
+ contextTabs.addTab("Configuration", editor);
}
}
- private void registerListeners() throws InstanceNotFoundException,
+ private JTextArea createTextArea() {
+ JTextArea result = new JTextArea();
+ result.setEditable(false);
+ result.setBackground(this.getBackground());
+ result.setForeground(Color.black);
+ result.setFont(new Font("Monospaced", Font.PLAIN,
result.getFont().getSize()));
+ result.setWrapStyleWord(true);
+ return result;
+ }
+
+ private JScrollPane scroll(final JTextArea text) {
+ final JToggleButton toggleButton = new JToggleButton();
+ toggleButton.setAction(new AbstractAction() {
+ private static final long serialVersionUID = -4214143754637722322L;
+
+ @Override
+ public void actionPerformed(final ActionEvent e) {
+ final boolean wrap = toggleButton.isSelected();
+ text.setLineWrap(wrap);
+ }
+ });
+ toggleButton.setToolTipText("Toggle line wrapping");
+ final JScrollPane scrollStatusLog = new JScrollPane(text, //
+ ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS, //
+ ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS);
+ scrollStatusLog.setCorner(ScrollPaneConstants.LOWER_RIGHT_CORNER,
toggleButton);
+ return scrollStatusLog;
+ }
+
+ private void registerListeners(StatusLoggerAdminMBean status) throws
InstanceNotFoundException,
MalformedObjectNameException, IOException {
final NotificationFilterSupport filter = new
NotificationFilterSupport();
filter.enableType(StatusLoggerAdminMBean.NOTIF_TYPE_MESSAGE);
- final ObjectName objName = new ObjectName(StatusLoggerAdminMBean.NAME);
+ final ObjectName objName = status.getObjectName();
client.getConnection().addNotificationListener(objName, this, filter,
- null);
+ status.getContextName());
}
@Override
public void handleNotification(final Notification notif, final Object
paramObject) {
if (StatusLoggerAdminMBean.NOTIF_TYPE_MESSAGE.equals(notif.getType()))
{
- statusLogTextArea.append(notif.getMessage() + "\n");
+ JTextArea text = statusLogTextAreaMap.get(paramObject);
+ if (text != null) {
+ text.append(notif.getMessage() + "\n");
+ }
}
}
@@ -144,7 +158,7 @@ public class ClientGUI extends JPanel im
* Useful links:
* http://www.componative.com/content/controller/developer/insights
* /jconsole3/
- *
+ *
* @param args must have at least one parameter, which specifies the
* location to connect to. Must be of the form {@code host:port}
* or {@code service:jmx:rmi:///jndi/rmi://<host>:<port>/jmxrmi}
@@ -192,8 +206,7 @@ public class ClientGUI extends JPanel im
// visible
final StringWriter sr = new StringWriter();
ex.printStackTrace(new PrintWriter(sr));
- JOptionPane.showMessageDialog(null, sr.toString(), "Error",
- JOptionPane.ERROR_MESSAGE);
+ JOptionPane.showMessageDialog(null, sr.toString(),
"Error", JOptionPane.ERROR_MESSAGE);
}
}
});
@@ -202,8 +215,7 @@ public class ClientGUI extends JPanel im
private static void usage() {
final String me = ClientGUI.class.getName();
System.err.println("Usage: java " + me + " <host>:<port>");
- System.err.println(" or: java " + me
- + " service:jmx:rmi:///jndi/rmi://<host>:<port>/jmxrmi");
+ System.err.println(" or: java " + me + "
service:jmx:rmi:///jndi/rmi://<host>:<port>/jmxrmi");
final String longAdr = "
service:jmx:rmi://<host>:<port>/jndi/rmi://<host>:<port>/jmxrmi";
System.err.println(" or: java " + me + longAdr);
}