You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@logging.apache.org by rp...@apache.org on 2014/02/07 18:49:34 UTC

svn commit: r1565741 - in /logging/log4j/log4j2/trunk: log4j-core/src/main/java/org/apache/logging/log4j/core/jmx/ log4j-jmx-gui/src/main/java/org/apache/logging/log4j/jmx/gui/ src/changes/

Author: rpopma
Date: Fri Feb  7 17:49:34 2014
New Revision: 1565741

URL: http://svn.apache.org/r1565741
Log:
LOG4J2-530 JMX Client GUI should dynamically update when LoggerContext MBeans are registered/unregistered in MBean server

Modified:
    logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/jmx/LoggerContextAdminMBean.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
    logging/log4j/log4j2/trunk/src/changes/changes.xml

Modified: logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/jmx/LoggerContextAdminMBean.java
URL: http://svn.apache.org/viewvc/logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/jmx/LoggerContextAdminMBean.java?rev=1565741&r1=1565740&r2=1565741&view=diff
==============================================================================
--- logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/jmx/LoggerContextAdminMBean.java (original)
+++ logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/jmx/LoggerContextAdminMBean.java Fri Feb  7 17:49:34 2014
@@ -20,16 +20,19 @@ import java.io.IOException;
 import java.net.URISyntaxException;
 import java.util.Map;
 
+import javax.management.ObjectName;
+
 /**
  * The MBean interface for monitoring and managing a {@code LoggerContext}.
  */
 public interface LoggerContextAdminMBean {
     /**
-     * ObjectName pattern ({@value}) for LoggerContextAdmin MBeans.
-     * This pattern contains a variable, which is the name of the logger context.
+     * ObjectName pattern ({@value} ) for LoggerContextAdmin MBeans. This
+     * pattern contains a variable, which is the name of the logger context.
      * <p>
      * You can find all registered LoggerContextAdmin MBeans like this:
      * </p>
+     * 
      * <pre>
      * MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
      * String pattern = String.format(LoggerContextAdminMBean.PATTERN, &quot;*&quot;);
@@ -37,14 +40,16 @@ public interface LoggerContextAdminMBean
      * </pre>
      * <p>
      * Some characters are not allowed in ObjectNames. The logger context name
-     * may be quoted. When LoggerContextAdmin MBeans are
-     * registered, their ObjectNames are created using this pattern as follows:
+     * may be quoted. When LoggerContextAdmin 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 = Server.DOMAIN + ":type=%s";
@@ -52,27 +57,33 @@ public interface LoggerContextAdminMBean
     /**
      * Notification that the {@code Configuration} of the instrumented
      * {@code LoggerContext} has been reconfigured. Notifications of this type
-     * ({@value}) do not carry a message or user data.
+     * ({@value} ) do not carry a message or user data.
      */
     String NOTIF_TYPE_RECONFIGURED = "com.apache.logging.log4j.core.jmx.config.reconfigured";
 
     /**
+     * Returns the {@code ObjectName} that this MBean is registered with in the
+     * MBean server.
+     */
+    ObjectName getObjectName();
+
+    /**
      * Returns the status of the instrumented {@code LoggerContext}.
-     *
+     * 
      * @return the LoggerContext status.
      */
     String getStatus();
 
     /**
      * Returns the name of the instrumented {@code LoggerContext}.
-     *
+     * 
      * @return the name of the instrumented {@code LoggerContext}.
      */
     String getName();
 
     /**
      * Returns the configuration location URI as a String.
-     *
+     * 
      * @return the configuration location
      */
     String getConfigLocationURI();
@@ -80,22 +91,21 @@ public interface LoggerContextAdminMBean
     /**
      * Sets the configuration location to the specified URI. This will cause the
      * instrumented {@code LoggerContext} to reconfigure.
-     *
+     * 
      * @param configLocation location of the configuration file in
      *            {@link java.net.URI} format.
      * @throws URISyntaxException if the format of the specified
      *             configLocationURI is incorrect
      * @throws IOException if an error occurred reading the specified location
      */
-    void setConfigLocationURI(String configLocation) throws URISyntaxException,
-            IOException;
+    void setConfigLocationURI(String configLocation) throws URISyntaxException, IOException;
 
     /**
      * Returns the configuration text, which may be the contents of the
      * configuration file or the text that was last set with a call to
      * {@code setConfigText}. If reading a file, this method assumes the file's
      * character encoding is UTF-8.
-     *
+     * 
      * @return the configuration text
      * @throws IOException if a problem occurred reading the contents of the
      *             config file.
@@ -106,7 +116,7 @@ public interface LoggerContextAdminMBean
      * Returns the configuration text, which may be the contents of the
      * configuration file or the text that was last set with a call to
      * {@code setConfigText}.
-     *
+     * 
      * @param charsetName the encoding to use to convert the file's bytes into
      *            the resulting string.
      * @return the configuration text
@@ -119,7 +129,7 @@ public interface LoggerContextAdminMBean
      * Sets the configuration text. This does not replace the contents of the
      * configuration file, but <em>does</em> cause the instrumented
      * {@code LoggerContext} to be reconfigured with the specified text.
-     *
+     * 
      * @param configText the configuration text in XML or JSON format
      * @param charsetName name of the {@code Charset} used to convert the
      *            specified configText to bytes
@@ -130,7 +140,7 @@ public interface LoggerContextAdminMBean
 
     /**
      * Returns the name of the Configuration of the instrumented LoggerContext.
-     *
+     * 
      * @return the Configuration name
      */
     String getConfigName();
@@ -138,7 +148,7 @@ public interface LoggerContextAdminMBean
     /**
      * Returns the class name of the {@code Configuration} of the instrumented
      * LoggerContext.
-     *
+     * 
      * @return the class name of the {@code Configuration}.
      */
     String getConfigClassName();
@@ -146,7 +156,7 @@ public interface LoggerContextAdminMBean
     /**
      * Returns a string description of all Filters configured in the
      * {@code Configuration} of the instrumented LoggerContext.
-     *
+     * 
      * @return a string description of all Filters configured
      */
     String getConfigFilter();
@@ -154,7 +164,7 @@ public interface LoggerContextAdminMBean
     /**
      * Returns the class name of the object that is monitoring the configuration
      * file for modifications.
-     *
+     * 
      * @return the class name of the object that is monitoring the configuration
      *         file for modifications
      */
@@ -162,7 +172,7 @@ public interface LoggerContextAdminMBean
 
     /**
      * Returns a map with configured properties.
-     *
+     * 
      * @return a map with configured properties.
      */
     Map<String, String> getConfigProperties();

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=1565741&r1=1565740&r2=1565741&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 Fri Feb  7 17:49:34 2014
@@ -40,6 +40,7 @@ import org.apache.logging.log4j.core.jmx
 public class Client {
     private JMXConnector connector;
     private final MBeanServerConnection connection;
+
     /**
      * Constructs a new {@code Client} object and creates proxies for all known
      * remote MBeans.
@@ -93,14 +94,18 @@ public class Client {
         List<LoggerContextAdminMBean> result = new ArrayList<LoggerContextAdminMBean>();
         final Set<ObjectName> contextNames = find(LoggerContextAdminMBean.PATTERN);
         for (final ObjectName contextName : contextNames) {
-            final LoggerContextAdminMBean ctx = JMX.newMBeanProxy(connection, //
-                    contextName, //
-                    LoggerContextAdminMBean.class, false);
-            result.add(ctx);
+            result.add(getLoggerContextAdmin(contextName));
         }
         return result;
     }
 
+    public LoggerContextAdminMBean getLoggerContextAdmin(ObjectName name) {
+        final LoggerContextAdminMBean ctx = JMX.newMBeanProxy(connection, //
+                name, //
+                LoggerContextAdminMBean.class, false);
+        return ctx;
+    }
+
     /**
      * Closes the client connection to its server. Any ongoing or new requests
      * to the MBeanServerConnection will fail.
@@ -149,4 +154,39 @@ public class Client {
                 StatusLoggerAdminMBean.class, true); // notificationBroadcaster
         return proxy;
     }
+
+    /**
+     * Returns {@code true} if the specified {@code ObjectName} is for a
+     * {@code LoggerContextAdminMBean}, {@code false} otherwise.
+     * 
+     * @param mbeanName the {@code ObjectName} to check.
+     * @return {@code true} if the specified {@code ObjectName} is for a
+     *         {@code LoggerContextAdminMBean}, {@code false} otherwise
+     */
+    public boolean isLoggerContext(ObjectName mbeanName) {
+        return Server.DOMAIN.equals(mbeanName.getDomain()) //
+                && mbeanName.getKeyPropertyList().containsKey("type") //
+                && mbeanName.getKeyPropertyList().size() == 1;
+    }
+
+    /**
+     * Returns the {@code ObjectName} of the {@code StatusLoggerAdminMBean}
+     * associated with the specified {@code LoggerContextAdminMBean}.
+     * 
+     * @param loggerContextObjName the {@code ObjectName} of a
+     *            {@code LoggerContextAdminMBean}
+     * @return {@code ObjectName} of the {@code StatusLoggerAdminMBean}
+     */
+    public ObjectName getStatusLoggerObjectName(ObjectName loggerContextObjName) {
+        if (!isLoggerContext(loggerContextObjName)) {
+            throw new IllegalArgumentException("Not a LoggerContext: " + loggerContextObjName);
+        }
+        final String cxtName = loggerContextObjName.getKeyProperty("type");
+        final String name = String.format(StatusLoggerAdminMBean.PATTERN, cxtName);
+        try {
+            return new ObjectName(name);
+        } catch (MalformedObjectNameException ex) {
+            throw new IllegalStateException(name, ex);
+        }
+    }
 }

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=1565741&r1=1565740&r2=1565741&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 Fri Feb  7 17:49:34 2014
@@ -18,6 +18,7 @@ package org.apache.logging.log4j.jmx.gui
 
 import java.awt.BorderLayout;
 import java.awt.Color;
+import java.awt.Component;
 import java.awt.Font;
 import java.awt.event.ActionEvent;
 import java.io.IOException;
@@ -28,6 +29,7 @@ import java.util.Map;
 
 import javax.management.InstanceNotFoundException;
 import javax.management.JMException;
+import javax.management.ListenerNotFoundException;
 import javax.management.MBeanServerDelegate;
 import javax.management.MBeanServerNotification;
 import javax.management.MalformedObjectNameException;
@@ -68,7 +70,8 @@ import org.apache.logging.log4j.core.jmx
 public class ClientGUI extends JPanel implements NotificationListener {
     private static final long serialVersionUID = -253621277232291174L;
     private final Client client;
-    private Map<String, JTextArea> statusLogTextAreaMap = new HashMap<String, JTextArea>();
+    private Map<ObjectName, Component> contextObjNameToTabbedPaneMap = new HashMap<ObjectName, Component>();
+    private Map<ObjectName, JTextArea> statusLogTextAreaMap = new HashMap<ObjectName, JTextArea>();
     private JTabbedPane tabbedPaneContexts;
 
     public ClientGUI(final Client client) throws IOException, JMException {
@@ -80,7 +83,7 @@ public class ClientGUI extends JPanel im
         ObjectName addRemoveNotifs = MBeanServerDelegate.DELEGATE_NAME;
         NotificationFilterSupport filter = new NotificationFilterSupport();
         filter.enableType(Server.DOMAIN); // only interested in Log4J2 MBeans
-        client.getConnection().addNotificationListener(addRemoveNotifs, this, filter, null);
+        client.getConnection().addNotificationListener(addRemoveNotifs, this, null, null);
     }
 
     private void createWidgets() {
@@ -90,27 +93,46 @@ public class ClientGUI extends JPanel im
     }
 
     private void populateWidgets() throws IOException, JMException {
-
         for (final LoggerContextAdminMBean ctx : client.getLoggerContextAdmins()) {
-            JTabbedPane contextTabs = new JTabbedPane();
-            tabbedPaneContexts.addTab("LoggerContext: " + ctx.getName(), contextTabs);
+            addWidgetForLoggerContext(ctx);
+        }
+    }
 
-            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);
+    private void addWidgetForLoggerContext(final LoggerContextAdminMBean ctx) throws MalformedObjectNameException,
+            IOException, InstanceNotFoundException {
+        JTabbedPane contextTabs = new JTabbedPane();
+        contextObjNameToTabbedPaneMap.put(ctx.getObjectName(), contextTabs);
+        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(ctx.getObjectName(), text);
+            registerListeners(status);
+            JScrollPane scroll = scroll(text);
+            contextTabs.addTab("StatusLogger", scroll);
+        }
 
-            final ClientEditConfigPanel editor = new ClientEditConfigPanel(ctx);
-            contextTabs.addTab("Configuration", editor);
+        final ClientEditConfigPanel editor = new ClientEditConfigPanel(ctx);
+        contextTabs.addTab("Configuration", editor);
+    }
+
+    private void removeWidgetForLoggerContext(ObjectName loggerContextObjName) throws JMException, IOException {
+        Component tab = contextObjNameToTabbedPaneMap.get(loggerContextObjName);
+        if (tab != null) {
+            tabbedPaneContexts.remove(tab);
+        }
+        statusLogTextAreaMap.remove(loggerContextObjName);
+        final ObjectName objName = client.getStatusLoggerObjectName(loggerContextObjName);
+        try {
+            // System.out.println("Remove listener for " + objName);
+            client.getConnection().removeNotificationListener(objName, this);
+        } catch (ListenerNotFoundException ignored) {
         }
     }
 
@@ -148,6 +170,7 @@ public class ClientGUI extends JPanel im
         final NotificationFilterSupport filter = new NotificationFilterSupport();
         filter.enableType(StatusLoggerAdminMBean.NOTIF_TYPE_MESSAGE);
         final ObjectName objName = status.getObjectName();
+        // System.out.println("Add listener for " + objName);
         client.getConnection().addNotificationListener(objName, this, filter, status.getContextName());
     }
 
@@ -172,17 +195,43 @@ public class ClientGUI extends JPanel im
     }
 
     /**
-     * @param mbeanName
+     * Called every time a Log4J2 MBean was registered in the MBean server.
+     * 
+     * @param mbeanName ObjectName of the registered Log4J2 MBean
      */
     private void onMBeanRegistered(ObjectName mbeanName) {
-        // TODO update widgets if logger context was added
+        if (client.isLoggerContext(mbeanName)) {
+            try {
+                LoggerContextAdminMBean ctx = client.getLoggerContextAdmin(mbeanName);
+                addWidgetForLoggerContext(ctx);
+            } catch (Exception ex) {
+                handle("Could not add tab for new MBean " + mbeanName, ex);
+            }
+        }
     }
 
     /**
-     * @param mbeanName
+     * Called every time a Log4J2 MBean was unregistered from the MBean server.
+     * 
+     * @param mbeanName ObjectName of the unregistered Log4J2 MBean
      */
     private void onMBeanUnregistered(ObjectName mbeanName) {
-        // TODO update widgets if logger context was removed
+        if (client.isLoggerContext(mbeanName)) {
+            try {
+                removeWidgetForLoggerContext(mbeanName);
+            } catch (Exception ex) {
+                handle("Could not remove tab for " + mbeanName, ex);
+            }
+        }
+    }
+
+    private void handle(String msg, Exception ex) {
+        System.err.println(msg);
+        ex.printStackTrace();
+
+        StringWriter sw = new StringWriter(1024);
+        ex.printStackTrace(new PrintWriter(sw));
+        JOptionPane.showMessageDialog(this, sw.toString(), msg, JOptionPane.ERROR_MESSAGE);
     }
 
     /**

Modified: logging/log4j/log4j2/trunk/src/changes/changes.xml
URL: http://svn.apache.org/viewvc/logging/log4j/log4j2/trunk/src/changes/changes.xml?rev=1565741&r1=1565740&r2=1565741&view=diff
==============================================================================
--- logging/log4j/log4j2/trunk/src/changes/changes.xml (original)
+++ logging/log4j/log4j2/trunk/src/changes/changes.xml Fri Feb  7 17:49:34 2014
@@ -21,6 +21,12 @@
   </properties>
   <body>
     <release version="2.0-RC1" date="2014-MM-DD" description="Bug fixes and enhancements">
+      <action issue="LOG4J2-530" dev="rpopma" type="add">
+        JMX Client GUI should dynamically update when LoggerContext MBeans are registered/unregistered in MBean server.
+      </action>
+      <action issue="LOG4J2-500" dev="rpopma" type="fix">
+        Unloading one webapp unloads JMX MBeans for all webapps.
+      </action>
       <action issue="LOG4J2-511" dev="rpopma" type="fix" due-to="James Pretorius">
         Stop AsyncLoggerConfig Disruptor thread(s), then AsyncAppender thread(s) first
         before stopping other appenders.