changeset 32:32334945b32e

6655515: MBeans tab: operation return values of type Component displayed as String 6439590: MBeans tab: jconsole mbean tree not correctly refreshed 6446434: MBeans tab: Not possible to view MBean content before all MBeans have been initially loaded 6520144: Hard to find MBean Attributes, Operations, and Notifications in Java 6 jconsole 6522091: VMPanel.java contains non-ASCII character 6608334: JConsole fails to display MBean operation with <null> return type 6611445: MBeans tab: MBean tree algorithm wrongly removes intermediate nodes. Reviewed-by: dfuchs, jfdenise
author lmalvent
date Tue, 11 Mar 2008 01:20:55 +0100
parents 7618b0596aab
children 7ddbf4c837b9
files src/share/classes/sun/tools/jconsole/MBeansTab.java src/share/classes/sun/tools/jconsole/MemoryPoolStat.java src/share/classes/sun/tools/jconsole/VMPanel.java src/share/classes/sun/tools/jconsole/inspector/OperationEntry.java src/share/classes/sun/tools/jconsole/inspector/TableSorter.java src/share/classes/sun/tools/jconsole/inspector/ThreadDialog.java src/share/classes/sun/tools/jconsole/inspector/Utils.java src/share/classes/sun/tools/jconsole/inspector/XDataViewer.java src/share/classes/sun/tools/jconsole/inspector/XMBean.java src/share/classes/sun/tools/jconsole/inspector/XMBeanInfo.java src/share/classes/sun/tools/jconsole/inspector/XMBeanNotifications.java src/share/classes/sun/tools/jconsole/inspector/XMBeanOperations.java src/share/classes/sun/tools/jconsole/inspector/XObject.java src/share/classes/sun/tools/jconsole/inspector/XOperations.java src/share/classes/sun/tools/jconsole/inspector/XPlotter.java src/share/classes/sun/tools/jconsole/inspector/XPlottingViewer.java src/share/classes/sun/tools/jconsole/inspector/XSheet.java src/share/classes/sun/tools/jconsole/inspector/XTable.java src/share/classes/sun/tools/jconsole/inspector/XTextField.java src/share/classes/sun/tools/jconsole/inspector/XTextFieldEditor.java src/share/classes/sun/tools/jconsole/inspector/XTree.java
diffstat 21 files changed, 1494 insertions(+), 1321 deletions(-) [+]
line wrap: on
line diff
--- a/src/share/classes/sun/tools/jconsole/MBeansTab.java	Mon Mar 10 23:51:13 2008 +0100
+++ b/src/share/classes/sun/tools/jconsole/MBeansTab.java	Tue Mar 11 01:20:55 2008 +0100
@@ -1,5 +1,5 @@
 /*
- * Copyright 2004-2006 Sun Microsystems, Inc.  All Rights Reserved.
+ * Copyright 2004-2007 Sun Microsystems, Inc.  All Rights Reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -26,6 +26,7 @@
 package sun.tools.jconsole;
 
 import java.awt.BorderLayout;
+import java.awt.EventQueue;
 import java.awt.event.MouseAdapter;
 import java.awt.event.MouseEvent;
 import java.awt.event.MouseListener;
@@ -42,7 +43,8 @@
 
 @SuppressWarnings("serial")
 public class MBeansTab extends Tab implements
-        NotificationListener, PropertyChangeListener, TreeSelectionListener {
+        NotificationListener, PropertyChangeListener,
+        TreeSelectionListener, TreeWillExpandListener {
 
     private XTree tree;
     private XSheet sheet;
@@ -70,6 +72,7 @@
         return sheet;
     }
 
+    @Override
     public void dispose() {
         super.dispose();
         sheet.dispose();
@@ -79,61 +82,79 @@
         return vmPanel.getUpdateInterval();
     }
 
-    void synchroniseMBeanServerView() {
-        // Register listener for MBean registration/unregistration
-        //
-        try {
-            getMBeanServerConnection().addNotificationListener(
-                    MBeanServerDelegate.DELEGATE_NAME,
-                    this,
-                    null,
-                    null);
-        } catch (InstanceNotFoundException e) {
-            // Should never happen because the MBeanServerDelegate
-            // is always present in any standard MBeanServer
-            //
-            if (JConsole.isDebug()) {
-                e.printStackTrace();
+    private void buildMBeanServerView() {
+        new SwingWorker<Set<ObjectName>, Void>() {
+            @Override
+            public Set<ObjectName> doInBackground() {
+                // Register listener for MBean registration/unregistration
+                //
+                try {
+                    getMBeanServerConnection().addNotificationListener(
+                            MBeanServerDelegate.DELEGATE_NAME,
+                            MBeansTab.this,
+                            null,
+                            null);
+                } catch (InstanceNotFoundException e) {
+                    // Should never happen because the MBeanServerDelegate
+                    // is always present in any standard MBeanServer
+                    //
+                    if (JConsole.isDebug()) {
+                        e.printStackTrace();
+                    }
+                } catch (IOException e) {
+                    if (JConsole.isDebug()) {
+                        e.printStackTrace();
+                    }
+                    vmPanel.getProxyClient().markAsDead();
+                    return null;
+                }
+                // Retrieve MBeans from MBeanServer
+                //
+                Set<ObjectName> mbeans = null;
+                try {
+                    mbeans = getMBeanServerConnection().queryNames(null, null);
+                } catch (IOException e) {
+                    if (JConsole.isDebug()) {
+                        e.printStackTrace();
+                    }
+                    vmPanel.getProxyClient().markAsDead();
+                    return null;
+                }
+                return mbeans;
             }
-        } catch (IOException e) {
-            if (JConsole.isDebug()) {
-                e.printStackTrace();
+            @Override
+            protected void done() {
+                try {
+                    // Wait for mbsc.queryNames() result
+                    Set<ObjectName> mbeans = get();
+                    // Do not display anything until the new tree has been built
+                    //
+                    tree.setVisible(false);
+                    // Cleanup current tree
+                    //
+                    tree.removeAll();
+                    // Add MBeans to tree
+                    //
+                    tree.addMBeansToView(mbeans);
+                    // Display the new tree
+                    //
+                    tree.setVisible(true);
+                } catch (Exception e) {
+                    Throwable t = Utils.getActualException(e);
+                    if (JConsole.isDebug()) {
+                        System.err.println("Problem at MBean tree construction");
+                        t.printStackTrace();
+                    }
+                }
             }
-            vmPanel.getProxyClient().markAsDead();
-            return;
-        }
-        // Retrieve MBeans from MBeanServer
-        //
-        Set<ObjectName> newSet = null;
-        try {
-            newSet = getMBeanServerConnection().queryNames(null,null);
-        } catch (IOException e) {
-            if (JConsole.isDebug()) {
-                e.printStackTrace();
-            }
-            vmPanel.getProxyClient().markAsDead();
-            return;
-        }
-        // Cleanup current tree
-        //
-        tree.removeAll();
-        // Do not display anything until the new tree has been built
-        //
-        tree.setVisible(false);
-        // Add MBeans to tree
-        //
-        for (ObjectName mbean : newSet) {
-            tree.addMBeanToView(mbean);
-        }
-        // Display the new tree
-        //
-        tree.setVisible(true);
+        }.execute();
     }
 
     public MBeanServerConnection getMBeanServerConnection() {
         return vmPanel.getProxyClient().getMBeanServerConnection();
     }
 
+    @Override
     public void update() {
         // Ping the connection to see if it is still alive. At
         // some point the ProxyClient class should centralize
@@ -160,6 +181,7 @@
         tree.getSelectionModel().setSelectionMode(
                 TreeSelectionModel.SINGLE_TREE_SELECTION);
         tree.addTreeSelectionListener(this);
+        tree.addTreeWillExpandListener(this);
         tree.addMouseListener(ml);
         JScrollPane theScrollPane = new JScrollPane(
                 tree,
@@ -177,55 +199,55 @@
         add(mainSplit);
     }
 
-    /* notification listener */
-    public void handleNotification(Notification notification, Object handback) {
-        if (notification instanceof MBeanServerNotification) {
-            ObjectName mbean =
-                    ((MBeanServerNotification) notification).getMBeanName();
-            if (notification.getType().equals(
-                    MBeanServerNotification.REGISTRATION_NOTIFICATION)) {
-                tree.addMBeanToView(mbean);
-            } else if (notification.getType().equals(
-                    MBeanServerNotification.UNREGISTRATION_NOTIFICATION)) {
-                tree.delMBeanFromView(mbean);
+    /* notification listener:  handleNotification */
+    public void handleNotification(
+            final Notification notification, Object handback) {
+        EventQueue.invokeLater(new Runnable() {
+            public void run() {
+                if (notification instanceof MBeanServerNotification) {
+                    ObjectName mbean =
+                            ((MBeanServerNotification) notification).getMBeanName();
+                    if (notification.getType().equals(
+                            MBeanServerNotification.REGISTRATION_NOTIFICATION)) {
+                        tree.addMBeanToView(mbean);
+                    } else if (notification.getType().equals(
+                            MBeanServerNotification.UNREGISTRATION_NOTIFICATION)) {
+                        tree.removeMBeanFromView(mbean);
+                    }
+                }
+            }
+        });
+    }
+
+    /* property change listener:  propertyChange */
+    public void propertyChange(PropertyChangeEvent evt) {
+        if (JConsoleContext.CONNECTION_STATE_PROPERTY.equals(evt.getPropertyName())) {
+            boolean connected = (Boolean) evt.getNewValue();
+            if (connected) {
+                buildMBeanServerView();
+            } else {
+                sheet.dispose();
             }
         }
     }
 
-    /* property change listener */
-    public void propertyChange(PropertyChangeEvent evt) {
-        if (evt.getPropertyName() == JConsoleContext.CONNECTION_STATE_PROPERTY) {
-            boolean connected = (Boolean) evt.getNewValue();
-            if (connected) {
-                workerAdd(new Runnable() {
-                    public void run() {
-                        synchroniseMBeanServerView();
-                    }
-                });
-            } else {
-                sheet.dispose();
-            }
-        }
-
-    }
-
-    /* tree selection listener */
+    /* tree selection listener: valueChanged */
     public void valueChanged(TreeSelectionEvent e) {
         DefaultMutableTreeNode node =
                 (DefaultMutableTreeNode) tree.getLastSelectedPathComponent();
         sheet.displayNode(node);
     }
-
-    /* tree mouse listener */
+    /* tree mouse listener: mousePressed */
     private MouseListener ml = new MouseAdapter() {
+        @Override
         public void mousePressed(MouseEvent e) {
             if (e.getClickCount() == 1) {
                 int selRow = tree.getRowForLocation(e.getX(), e.getY());
                 if (selRow != -1) {
                     TreePath selPath =
                             tree.getPathForLocation(e.getX(), e.getY());
-                    DefaultMutableTreeNode node = (DefaultMutableTreeNode)
-                            selPath.getLastPathComponent();
+                    DefaultMutableTreeNode node =
+                            (DefaultMutableTreeNode) selPath.getLastPathComponent();
                     if (sheet.isMBeanNode(node)) {
                         tree.expandPath(selPath);
                     }
@@ -233,4 +255,22 @@
             }
         }
     };
+
+    /* tree will expand listener: treeWillExpand */
+    public void treeWillExpand(TreeExpansionEvent e)
+            throws ExpandVetoException {
+        TreePath path = e.getPath();
+        if (!tree.hasBeenExpanded(path)) {
+            DefaultMutableTreeNode node =
+                    (DefaultMutableTreeNode) path.getLastPathComponent();
+            if (sheet.isMBeanNode(node) && !tree.hasMetadataNodes(node)) {
+                tree.addMetadataNodes(node);
+            }
+        }
+    }
+
+    /* tree will expand listener: treeWillCollapse */
+    public void treeWillCollapse(TreeExpansionEvent e)
+            throws ExpandVetoException {
+    }
 }
--- a/src/share/classes/sun/tools/jconsole/MemoryPoolStat.java	Mon Mar 10 23:51:13 2008 +0100
+++ b/src/share/classes/sun/tools/jconsole/MemoryPoolStat.java	Tue Mar 11 01:20:55 2008 +0100
@@ -22,6 +22,7 @@
  * CA 95054 USA or visit www.sun.com if you need additional information or
  * have any questions.
  */
+
 package sun.tools.jconsole;
 
 import java.lang.management.MemoryUsage;
--- a/src/share/classes/sun/tools/jconsole/VMPanel.java	Mon Mar 10 23:51:13 2008 +0100
+++ b/src/share/classes/sun/tools/jconsole/VMPanel.java	Tue Mar 11 01:20:55 2008 +0100
@@ -45,6 +45,7 @@
 
 @SuppressWarnings("serial")
 public class VMPanel extends JTabbedPane implements PropertyChangeListener {
+
     private ProxyClient proxyClient;
     private Timer timer;
     private int updateInterval;
@@ -55,12 +56,9 @@
     private String password;
     private String url;
     private VMInternalFrame vmIF = null;
-
     private static final String windowsLaF =
-        "com.sun.java.swing.plaf.windows.WindowsLookAndFeel";
-
+            "com.sun.java.swing.plaf.windows.WindowsLookAndFeel";
     private static ArrayList<TabInfo> tabInfos = new ArrayList<TabInfo>();
-
     private boolean wasConnected = false;
 
     // The everConnected flag keeps track of whether the window can be
@@ -76,7 +74,7 @@
 
     // Each VMPanel has its own instance of the JConsolePlugin
     // A map of JConsolePlugin to the previous SwingWorker
-    private Map<JConsolePlugin, SwingWorker<?,?>> plugins = null;
+    private Map<JConsolePlugin, SwingWorker<?, ?>> plugins = null;
     private boolean pluginTabsAdded = false;
 
     // Update these only on the EDT
@@ -86,11 +84,11 @@
 
     static {
         tabInfos.add(new TabInfo(OverviewTab.class, OverviewTab.getTabName(), true));
-        tabInfos.add(new TabInfo(MemoryTab.class,  MemoryTab.getTabName(),  true));
-        tabInfos.add(new TabInfo(ThreadTab.class,  ThreadTab.getTabName(),  true));
-        tabInfos.add(new TabInfo(ClassTab.class,   ClassTab.getTabName(),   true));
+        tabInfos.add(new TabInfo(MemoryTab.class, MemoryTab.getTabName(), true));
+        tabInfos.add(new TabInfo(ThreadTab.class, ThreadTab.getTabName(), true));
+        tabInfos.add(new TabInfo(ClassTab.class, ClassTab.getTabName(), true));
         tabInfos.add(new TabInfo(SummaryTab.class, SummaryTab.getTabName(), true));
-        tabInfos.add(new TabInfo(MBeansTab.class,  MBeansTab.getTabName(),  true));
+        tabInfos.add(new TabInfo(MBeansTab.class, MBeansTab.getTabName(), true));
     }
 
     public static TabInfo[] getTabInfos() {
@@ -101,8 +99,8 @@
         this.proxyClient = proxyClient;
         this.updateInterval = updateInterval;
         this.hostName = proxyClient.getHostName();
-        this.port     = proxyClient.getPort();
-        this.vmid     = proxyClient.getVmid();
+        this.port = proxyClient.getPort();
+        this.vmid = proxyClient.getVmid();
         this.userName = proxyClient.getUserName();
         this.password = proxyClient.getPassword();
         this.url = proxyClient.getUrl();
@@ -113,7 +111,7 @@
             }
         }
 
-        plugins = new LinkedHashMap<JConsolePlugin, SwingWorker<?,?>>();
+        plugins = new LinkedHashMap<JConsolePlugin, SwingWorker<?, ?>>();
         for (JConsolePlugin p : JConsole.getPlugins()) {
             p.setContext(proxyClient);
             plugins.put(p, null);
@@ -128,10 +126,9 @@
         proxyClient.addPropertyChangeListener(this);
 
         addMouseListener(new MouseAdapter() {
+
             public void mouseClicked(MouseEvent e) {
-                if (connectedIconBounds != null
-                    && (e.getModifiers() & MouseEvent.BUTTON1_MASK) != 0
-                    && connectedIconBounds.contains(e.getPoint())) {
+                if (connectedIconBounds != null && (e.getModifiers() & MouseEvent.BUTTON1_MASK) != 0 && connectedIconBounds.contains(e.getPoint())) {
 
                     if (isConnected()) {
                         disconnect();
@@ -145,23 +142,21 @@
         });
 
     }
-
     private static Icon connectedIcon16 =
-        new ImageIcon(VMPanel.class.getResource("resources/connected16.png"));
+            new ImageIcon(VMPanel.class.getResource("resources/connected16.png"));
     private static Icon connectedIcon24 =
-        new ImageIcon(VMPanel.class.getResource("resources/connected24.png"));
+            new ImageIcon(VMPanel.class.getResource("resources/connected24.png"));
     private static Icon disconnectedIcon16 =
-        new ImageIcon(VMPanel.class.getResource("resources/disconnected16.png"));
+            new ImageIcon(VMPanel.class.getResource("resources/disconnected16.png"));
     private static Icon disconnectedIcon24 =
-        new ImageIcon(VMPanel.class.getResource("resources/disconnected24.png"));
-
+            new ImageIcon(VMPanel.class.getResource("resources/disconnected24.png"));
     private Rectangle connectedIconBounds;
 
     // Override to increase right inset for tab area,
     // in order to reserve space for the connect toggle.
     public void setUI(TabbedPaneUI ui) {
-        Insets insets = (Insets)UIManager.getLookAndFeelDefaults().get("TabbedPane.tabAreaInsets");
-        insets = (Insets)insets.clone();
+        Insets insets = (Insets) UIManager.getLookAndFeelDefaults().get("TabbedPane.tabAreaInsets");
+        insets = (Insets) insets.clone();
         insets.right += connectedIcon24.getIconWidth() + 8;
         UIManager.put("TabbedPane.tabAreaInsets", insets);
         super.setUI(ui);
@@ -225,7 +220,7 @@
     private Tab instantiate(TabInfo tabInfo) {
         try {
             Constructor con = tabInfo.tabClass.getConstructor(VMPanel.class);
-            return (Tab)con.newInstance(this);
+            return (Tab) con.newInstance(this);
         } catch (Exception ex) {
             System.err.println(ex);
             return null;
@@ -247,10 +242,11 @@
      * IT IS USED TO MAKE SOME LOCAL MANIPULATIONS.
      */
     ProxyClient getProxyClient(boolean assertThread) {
-        if(assertThread)
+        if (assertThread) {
             return getProxyClient();
-        else
+        } else {
             return proxyClient;
+        }
     }
 
     public ProxyClient getProxyClient() {
@@ -294,6 +290,7 @@
             startUpdateTimer();
         } else {
             new Thread("VMPanel.connect") {
+
                 public void run() {
                     proxyClient.connect();
                 }
@@ -301,68 +298,63 @@
         }
     }
 
-
     // Call on EDT
     public void disconnect() {
         proxyClient.disconnect();
         updateFrameTitle();
     }
 
-
-
     // Called on EDT
     public void propertyChange(PropertyChangeEvent ev) {
         String prop = ev.getPropertyName();
 
         if (prop == CONNECTION_STATE_PROPERTY) {
-            ConnectionState oldState = (ConnectionState)ev.getOldValue();
-            ConnectionState newState = (ConnectionState)ev.getNewValue();
+            ConnectionState oldState = (ConnectionState) ev.getOldValue();
+            ConnectionState newState = (ConnectionState) ev.getNewValue();
             switch (newState) {
-              case CONNECTING:
-                onConnecting();
-                break;
+                case CONNECTING:
+                    onConnecting();
+                    break;
 
-              case CONNECTED:
-                if (progressBar != null) {
-                    progressBar.setIndeterminate(false);
-                    progressBar.setValue(100);
-                }
-                closeOptionPane();
-                updateFrameTitle();
-                // create tabs if not done
-                createPluginTabs();
-                repaint();
-                // Notify tabs
-                fireConnectedChange(true);
-                // Enable/disable tabs on initial update
-                initialUpdate = true;
-                // Start/Restart update timer on connect/reconnect
-                startUpdateTimer();
-                break;
+                case CONNECTED:
+                    if (progressBar != null) {
+                        progressBar.setIndeterminate(false);
+                        progressBar.setValue(100);
+                    }
+                    closeOptionPane();
+                    updateFrameTitle();
+                    // create tabs if not done
+                    createPluginTabs();
+                    repaint();
+                    // Notify tabs
+                    fireConnectedChange(true);
+                    // Enable/disable tabs on initial update
+                    initialUpdate = true;
+                    // Start/Restart update timer on connect/reconnect
+                    startUpdateTimer();
+                    break;
 
-              case DISCONNECTED:
-                if (progressBar != null) {
-                    progressBar.setIndeterminate(false);
-                    progressBar.setValue(0);
-                    closeOptionPane();
-                }
-                vmPanelDied();
-                if (oldState == ConnectionState.CONNECTED) {
-                    // Notify tabs
-                    fireConnectedChange(false);
-                }
-                break;
+                case DISCONNECTED:
+                    if (progressBar != null) {
+                        progressBar.setIndeterminate(false);
+                        progressBar.setValue(0);
+                        closeOptionPane();
+                    }
+                    vmPanelDied();
+                    if (oldState == ConnectionState.CONNECTED) {
+                        // Notify tabs
+                        fireConnectedChange(false);
+                    }
+                    break;
             }
         }
     }
 
-
-
     // Called on EDT
     private void onConnecting() {
         time0 = System.currentTimeMillis();
 
-        final JConsole jc = (JConsole)SwingUtilities.getWindowAncestor(this);
+        final JConsole jc = (JConsole) SwingUtilities.getWindowAncestor(this);
 
         String connectionName = getConnectionName();
         progressBar = new JProgressBar();
@@ -373,17 +365,16 @@
         Object[] message = {
             "<html><h3>" + getText("connectingTo1", connectionName) + "</h3></html>",
             progressPanel,
-            "<html><b>"  + getText("connectingTo2", connectionName) + "</b></html>"
+            "<html><b>" + getText("connectingTo2", connectionName) + "</b></html>"
         };
 
-
         optionPane =
-            SheetDialog.showOptionDialog(this,
-                                         message,
-                                         JOptionPane.DEFAULT_OPTION,
-                                         JOptionPane.INFORMATION_MESSAGE, null,
-                                         new String[] { getText("Cancel") },
-                                         0);
+                SheetDialog.showOptionDialog(this,
+                message,
+                JOptionPane.DEFAULT_OPTION,
+                JOptionPane.INFORMATION_MESSAGE, null,
+                new String[]{getText("Cancel")},
+                0);
 
 
     }
@@ -398,10 +389,11 @@
                         try {
                             sleep(2000 - elapsed);
                         } catch (InterruptedException ex) {
-                            // Ignore
+                        // Ignore
                         }
                     }
                     SwingUtilities.invokeLater(new Runnable() {
+
                         public void run() {
                             optionPane.setVisible(false);
                             progressBar = null;
@@ -425,8 +417,8 @@
 
     private VMInternalFrame getFrame() {
         if (vmIF == null) {
-            vmIF = (VMInternalFrame)SwingUtilities.getAncestorOfClass(VMInternalFrame.class,
-                                                                      this);
+            vmIF = (VMInternalFrame) SwingUtilities.getAncestorOfClass(VMInternalFrame.class,
+                    this);
         }
         return vmIF;
     }
@@ -452,27 +444,27 @@
             timer.cancel();
         }
         TimerTask timerTask = new TimerTask() {
+
             public void run() {
                 update();
             }
         };
-        String timerName = "Timer-"+getConnectionName();
+        String timerName = "Timer-" + getConnectionName();
         timer = new Timer(timerName, true);
         timer.schedule(timerTask, 0, updateInterval);
     }
 
-
     // Call on EDT
     private void vmPanelDied() {
         disconnect();
 
-        final JConsole jc = (JConsole)SwingUtilities.getWindowAncestor(this);
+        final JConsole jc = (JConsole) SwingUtilities.getWindowAncestor(this);
 
         JOptionPane optionPane;
 
-        final String connectStr   = getText("Connect");
+        final String connectStr = getText("Connect");
         final String reconnectStr = getText("Reconnect");
-        final String cancelStr    = getText("Cancel");
+        final String cancelStr = getText("Cancel");
 
         String msgTitle, msgExplanation, buttonStr;
 
@@ -488,15 +480,16 @@
         }
 
         optionPane =
-            SheetDialog.showOptionDialog(this,
-                                         "<html><h3>" + msgTitle + "</h3>" +
-                                         "<b>" + msgExplanation + "</b>",
-                                         JOptionPane.DEFAULT_OPTION,
-                                         JOptionPane.WARNING_MESSAGE, null,
-                                         new String[] { buttonStr, cancelStr },
-                                         0);
+                SheetDialog.showOptionDialog(this,
+                "<html><h3>" + msgTitle + "</h3>" +
+                "<b>" + msgExplanation + "</b>",
+                JOptionPane.DEFAULT_OPTION,
+                JOptionPane.WARNING_MESSAGE, null,
+                new String[]{buttonStr, cancelStr},
+                0);
 
         optionPane.addPropertyChangeListener(new PropertyChangeListener() {
+
             public void propertyChange(PropertyChangeEvent event) {
                 if (event.getPropertyName().equals(JOptionPane.VALUE_PROPERTY)) {
                     Object value = event.getNewValue();
@@ -507,7 +500,7 @@
                         try {
                             getFrame().setClosed(true);
                         } catch (PropertyVetoException ex) {
-                            // Should not happen, but can be ignored.
+                        // Should not happen, but can be ignored.
                         }
                     }
                 }
@@ -518,11 +511,13 @@
     // Note: This method is called on a TimerTask thread. Any GUI manipulation
     // must be performed with invokeLater() or invokeAndWait().
     private Object lockObject = new Object();
+
     private void update() {
-        synchronized(lockObject) {
+        synchronized (lockObject) {
             if (!isConnected()) {
                 if (wasConnected) {
                     EventQueue.invokeLater(new Runnable() {
+
                         public void run() {
                             vmPanelDied();
                         }
@@ -548,6 +543,7 @@
                         //
                         if (initialUpdate) {
                             EventQueue.invokeLater(new Runnable() {
+
                                 public void run() {
                                     setEnabledAt(index, true);
                                 }
@@ -569,8 +565,8 @@
 
             // plugin GUI update
             for (JConsolePlugin p : plugins.keySet()) {
-                SwingWorker<?,?> sw = p.newSwingWorker();
-                SwingWorker<?,?> prevSW = plugins.get(p);
+                SwingWorker<?, ?> sw = p.newSwingWorker();
+                SwingWorker<?, ?> prevSW = plugins.get(p);
                 // schedule SwingWorker to run only if the previous
                 // SwingWorker has finished its task and it hasn't started.
                 if (prevSW == null || prevSW.isDone()) {
@@ -583,7 +579,7 @@
                 }
             }
 
-            // Set the first enabled tab in the tabīs list
+            // Set the first enabled tab in the tab's list
             // as the selected tab on initial update
             //
             if (initialUpdate) {
@@ -622,7 +618,6 @@
         return url;
     }
 
-
     public String getPassword() {
         return password;
     }
@@ -636,6 +631,7 @@
     }
 
     static class TabInfo {
+
         Class<? extends Tab> tabClass;
         String name;
         boolean tabVisible;
--- a/src/share/classes/sun/tools/jconsole/inspector/OperationEntry.java	Mon Mar 10 23:51:13 2008 +0100
+++ b/src/share/classes/sun/tools/jconsole/inspector/OperationEntry.java	Tue Mar 11 01:20:55 2008 +0100
@@ -22,9 +22,9 @@
  * CA 95054 USA or visit www.sun.com if you need additional information or
  * have any questions.
  */
+
 package sun.tools.jconsole.inspector;
 
-
 // java import
 import java.awt.*;
 import java.awt.event.*;
--- a/src/share/classes/sun/tools/jconsole/inspector/TableSorter.java	Mon Mar 10 23:51:13 2008 +0100
+++ b/src/share/classes/sun/tools/jconsole/inspector/TableSorter.java	Tue Mar 11 01:20:55 2008 +0100
@@ -22,6 +22,7 @@
  * CA 95054 USA or visit www.sun.com if you need additional information or
  * have any questions.
  */
+
 package sun.tools.jconsole.inspector;
 
 import java.util.*;
--- a/src/share/classes/sun/tools/jconsole/inspector/ThreadDialog.java	Mon Mar 10 23:51:13 2008 +0100
+++ b/src/share/classes/sun/tools/jconsole/inspector/ThreadDialog.java	Tue Mar 11 01:20:55 2008 +0100
@@ -22,6 +22,7 @@
  * CA 95054 USA or visit www.sun.com if you need additional information or
  * have any questions.
  */
+
 package sun.tools.jconsole.inspector;
 
 // java import
--- a/src/share/classes/sun/tools/jconsole/inspector/Utils.java	Mon Mar 10 23:51:13 2008 +0100
+++ b/src/share/classes/sun/tools/jconsole/inspector/Utils.java	Tue Mar 11 01:20:55 2008 +0100
@@ -1,5 +1,5 @@
 /*
- * Copyright 2004-2006 Sun Microsystems, Inc.  All Rights Reserved.
+ * Copyright 2004-2007 Sun Microsystems, Inc.  All Rights Reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -29,55 +29,51 @@
 import java.lang.reflect.*;
 import java.math.BigDecimal;
 import java.math.BigInteger;
+import java.util.*;
+import java.util.concurrent.ExecutionException;
 import javax.management.*;
 import javax.management.openmbean.*;
 import javax.swing.*;
 import javax.swing.text.*;
-import java.util.*;
 
 public class Utils {
 
     private Utils() {
     }
-
     private static Set<Integer> tableNavigationKeys =
-            new HashSet<Integer>(Arrays.asList(new Integer[] {
+            new HashSet<Integer>(Arrays.asList(new Integer[]{
         KeyEvent.VK_TAB, KeyEvent.VK_ENTER,
         KeyEvent.VK_HOME, KeyEvent.VK_END,
         KeyEvent.VK_LEFT, KeyEvent.VK_RIGHT,
         KeyEvent.VK_UP, KeyEvent.VK_DOWN,
-        KeyEvent.VK_PAGE_UP, KeyEvent.VK_PAGE_DOWN}));
-
+        KeyEvent.VK_PAGE_UP, KeyEvent.VK_PAGE_DOWN
+    }));
     private static final Set<Class<?>> primitiveWrappers =
-            new HashSet<Class<?>>(Arrays.asList(new Class<?>[] {
+            new HashSet<Class<?>>(Arrays.asList(new Class<?>[]{
         Byte.class, Short.class, Integer.class, Long.class,
-        Float.class, Double.class, Character.class, Boolean.class}));
-
+        Float.class, Double.class, Character.class, Boolean.class
+    }));
     private static final Set<Class<?>> primitives = new HashSet<Class<?>>();
-
     private static final Map<String, Class<?>> primitiveMap =
             new HashMap<String, Class<?>>();
-
     private static final Map<String, Class<?>> primitiveToWrapper =
             new HashMap<String, Class<?>>();
-
     private static final Set<String> editableTypes = new HashSet<String>();
-
     private static final Set<Class<?>> extraEditableClasses =
-            new HashSet<Class<?>>(Arrays.asList(new Class<?>[] {
+            new HashSet<Class<?>>(Arrays.asList(new Class<?>[]{
         BigDecimal.class, BigInteger.class, Number.class,
-        String.class, ObjectName.class}));
-
+        String.class, ObjectName.class
+    }));
     private static final Set<String> numericalTypes = new HashSet<String>();
-
     private static final Set<String> extraNumericalTypes =
-            new HashSet<String>(Arrays.asList(new String[] {
+            new HashSet<String>(Arrays.asList(new String[]{
         BigDecimal.class.getName(), BigInteger.class.getName(),
-        Number.class.getName()}));
-
+        Number.class.getName()
+    }));
     private static final Set<String> booleanTypes =
-            new HashSet<String>(Arrays.asList(new String[] {
-        Boolean.TYPE.getName(), Boolean.class.getName()}));
+            new HashSet<String>(Arrays.asList(new String[]{
+        Boolean.TYPE.getName(), Boolean.class.getName()
+    }));
 
     static {
         // compute primitives/primitiveMap/primitiveToWrapper
@@ -122,10 +118,11 @@
      * It's used to cater for the primitive types.
      */
     public static Class<?> getClass(String className)
-    throws ClassNotFoundException {
+            throws ClassNotFoundException {
         Class<?> c;
-        if ((c = primitiveMap.get(className)) != null)
+        if ((c = primitiveMap.get(className)) != null) {
             return c;
+        }
         return Class.forName(className);
     }
 
@@ -155,7 +152,9 @@
      * structure, i.e. a data structure jconsole can render as an array.
      */
     public static boolean canBeRenderedAsArray(Object elem) {
-        if (isSupportedArray(elem)) return true;
+        if (isSupportedArray(elem)) {
+            return true;
+        }
         if (elem instanceof Collection) {
             Collection<?> c = (Collection<?>) elem;
             if (c.isEmpty()) {
@@ -168,7 +167,7 @@
                 // - Collections of other Java types are handled as arrays
                 //
                 return !isUniformCollection(c, CompositeData.class) &&
-                       !isUniformCollection(c, TabularData.class);
+                        !isUniformCollection(c, TabularData.class);
             }
         }
         if (elem instanceof Map) {
@@ -239,7 +238,9 @@
      */
     public static String getReadableClassName(String name) {
         String className = getArrayClassName(name);
-        if (className == null) return name;
+        if (className == null) {
+            return name;
+        }
         int index = name.lastIndexOf("[");
         StringBuilder brackets = new StringBuilder(className);
         for (int i = 0; i <= index; i++) {
@@ -282,7 +283,7 @@
      * Try to create a Java object using a one-string-param constructor.
      */
     public static Object newStringConstructor(String type, String param)
-    throws Exception {
+            throws Exception {
         Constructor c = Utils.getClass(type).getConstructor(String.class);
         try {
             return c.newInstance(param);
@@ -300,7 +301,7 @@
      * Try to convert a string value into a numerical value.
      */
     private static Number createNumberFromStringValue(String value)
-    throws NumberFormatException {
+            throws NumberFormatException {
         final String suffix = value.substring(value.length() - 1);
         if ("L".equalsIgnoreCase(suffix)) {
             return Long.valueOf(value.substring(0, value.length() - 1));
@@ -314,17 +315,17 @@
         try {
             return Integer.valueOf(value);
         } catch (NumberFormatException e) {
-            // OK: Ignore exception...
+        // OK: Ignore exception...
         }
         try {
             return Long.valueOf(value);
         } catch (NumberFormatException e1) {
-            // OK: Ignore exception...
+        // OK: Ignore exception...
         }
         try {
             return Double.valueOf(value);
         } catch (NumberFormatException e2) {
-            // OK: Ignore exception...
+        // OK: Ignore exception...
         }
         throw new NumberFormatException("Cannot convert string value '" +
                 value + "' into a numerical value");
@@ -337,7 +338,7 @@
      * will return an Integer object initialized to 10.
      */
     public static Object createObjectFromString(String type, String value)
-    throws Exception {
+            throws Exception {
         Object result;
         if (primitiveToWrapper.containsKey(type)) {
             if (type.equals(Character.TYPE.getName())) {
@@ -367,7 +368,7 @@
      * into a useful object array for passing into a parameter array.
      */
     public static Object[] getParameters(XTextField[] inputs, String[] params)
-    throws Exception {
+            throws Exception {
         Object result[] = new Object[inputs.length];
         Object userInput;
         for (int i = 0; i < inputs.length; i++) {
@@ -388,12 +389,17 @@
      * If the exception is wrapped, unwrap it.
      */
     public static Throwable getActualException(Throwable e) {
+        if (e instanceof ExecutionException) {
+            e = e.getCause();
+        }
         if (e instanceof MBeanException ||
                 e instanceof RuntimeMBeanException ||
                 e instanceof RuntimeOperationsException ||
                 e instanceof ReflectionException) {
             Throwable t = e.getCause();
-            if (t != null) return t;
+            if (t != null) {
+                return t;
+            }
         }
         return e;
     }
@@ -401,6 +407,7 @@
     @SuppressWarnings("serial")
     public static class ReadOnlyTableCellEditor
             extends DefaultCellEditor {
+
         public ReadOnlyTableCellEditor(JTextField tf) {
             super(tf);
             tf.addFocusListener(new Utils.EditFocusAdapter(this));
@@ -409,20 +416,25 @@
     }
 
     public static class EditFocusAdapter extends FocusAdapter {
+
         private CellEditor editor;
+
         public EditFocusAdapter(CellEditor editor) {
             this.editor = editor;
         }
+
+        @Override
         public void focusLost(FocusEvent e) {
             editor.stopCellEditing();
         }
-    };
+    }
 
     public static class CopyKeyAdapter extends KeyAdapter {
         private static final String defaultEditorKitCopyActionName =
                 DefaultEditorKit.copyAction;
         private static final String transferHandlerCopyActionName =
                 (String) TransferHandler.getCopyAction().getValue(Action.NAME);
+        @Override
         public void keyPressed(KeyEvent e) {
             // Accept "copy" key strokes
             KeyStroke ks = KeyStroke.getKeyStroke(
@@ -441,6 +453,8 @@
                 e.consume();
             }
         }
+
+        @Override
         public void keyTyped(KeyEvent e) {
             e.consume();
         }
--- a/src/share/classes/sun/tools/jconsole/inspector/XDataViewer.java	Mon Mar 10 23:51:13 2008 +0100
+++ b/src/share/classes/sun/tools/jconsole/inspector/XDataViewer.java	Tue Mar 11 01:20:55 2008 +0100
@@ -22,6 +22,7 @@
  * CA 95054 USA or visit www.sun.com if you need additional information or
  * have any questions.
  */
+
 package sun.tools.jconsole.inspector;
 
 import javax.swing.JTable;
@@ -108,6 +109,7 @@
     public Component createOperationViewer(Object value,
                                            XMBean mbean) {
         if(value instanceof Number) return null;
+        if(value instanceof Component) return (Component) value;
         return createAttributeViewer(value, mbean, null, null);
     }
 
--- a/src/share/classes/sun/tools/jconsole/inspector/XMBean.java	Mon Mar 10 23:51:13 2008 +0100
+++ b/src/share/classes/sun/tools/jconsole/inspector/XMBean.java	Tue Mar 11 01:20:55 2008 +0100
@@ -1,5 +1,5 @@
 /*
- * Copyright 2004-2006 Sun Microsystems, Inc.  All Rights Reserved.
+ * Copyright 2004-2007 Sun Microsystems, Inc.  All Rights Reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -28,47 +28,56 @@
 import java.io.IOException;
 import javax.management.*;
 import javax.swing.Icon;
+import sun.tools.jconsole.JConsole;
 import sun.tools.jconsole.MBeansTab;
 
-public class XMBean extends Object {
-    private ObjectName objectName;
+public class XMBean {
+
+    private final MBeansTab mbeansTab;
+    private final ObjectName objectName;
     private Icon icon;
     private String text;
-    private boolean broadcaster;
+    private Boolean broadcaster;
+    private final Object broadcasterLock = new Object();
     private MBeanInfo mbeanInfo;
-    private MBeansTab mbeansTab;
+    private final Object mbeanInfoLock = new Object();
 
-    public XMBean(ObjectName objectName, MBeansTab mbeansTab)
-        throws InstanceNotFoundException, IntrospectionException,
-            ReflectionException, IOException {
+    public XMBean(ObjectName objectName, MBeansTab mbeansTab) {
         this.mbeansTab = mbeansTab;
-        setObjectName(objectName);
+        this.objectName = objectName;
+        text = objectName.getKeyProperty("name");
+        if (text == null) {
+            text = objectName.getDomain();
+        }
         if (MBeanServerDelegate.DELEGATE_NAME.equals(objectName)) {
             icon = IconManager.MBEANSERVERDELEGATE;
         } else {
             icon = IconManager.MBEAN;
         }
-        this.broadcaster = isBroadcaster(objectName);
-        this.mbeanInfo = getMBeanInfo(objectName);
     }
 
     MBeanServerConnection getMBeanServerConnection() {
         return mbeansTab.getMBeanServerConnection();
     }
 
-    public boolean isBroadcaster() {
-        return broadcaster;
-    }
-
-    private boolean isBroadcaster(ObjectName name) {
-        try {
-            return getMBeanServerConnection().isInstanceOf(
-                    name, "javax.management.NotificationBroadcaster");
-        } catch (Exception e) {
-            System.out.println("Error calling isBroadcaster: " +
-                    e.getMessage());
+    public Boolean isBroadcaster() {
+        synchronized (broadcasterLock) {
+            if (broadcaster == null) {
+                try {
+                    broadcaster = getMBeanServerConnection().isInstanceOf(
+                            getObjectName(),
+                            "javax.management.NotificationBroadcaster");
+                } catch (Exception e) {
+                    if (JConsole.isDebug()) {
+                        System.err.println("Couldn't check if MBean [" +
+                                objectName + "] is a notification broadcaster");
+                        e.printStackTrace();
+                    }
+                    return false;
+                }
+            }
+            return broadcaster;
         }
-        return false;
     }
 
     public Object invoke(String operationName) throws Exception {
@@ -78,35 +87,35 @@
     }
 
     public Object invoke(String operationName, Object params[], String sig[])
-        throws Exception {
+            throws Exception {
         Object result = getMBeanServerConnection().invoke(
                 getObjectName(), operationName, params, sig);
         return result;
     }
 
     public void setAttribute(Attribute attribute)
-        throws AttributeNotFoundException, InstanceNotFoundException,
+            throws AttributeNotFoundException, InstanceNotFoundException,
             InvalidAttributeValueException, MBeanException,
             ReflectionException, IOException {
         getMBeanServerConnection().setAttribute(getObjectName(), attribute);
     }
 
     public Object getAttribute(String attributeName)
-        throws AttributeNotFoundException, InstanceNotFoundException,
+            throws AttributeNotFoundException, InstanceNotFoundException,
             MBeanException, ReflectionException, IOException {
         return getMBeanServerConnection().getAttribute(
                 getObjectName(), attributeName);
     }
 
     public AttributeList getAttributes(String attributeNames[])
-        throws AttributeNotFoundException, InstanceNotFoundException,
+            throws AttributeNotFoundException, InstanceNotFoundException,
             MBeanException, ReflectionException, IOException {
         return getMBeanServerConnection().getAttributes(
                 getObjectName(), attributeNames);
     }
 
     public AttributeList getAttributes(MBeanAttributeInfo attributeNames[])
-        throws AttributeNotFoundException, InstanceNotFoundException,
+            throws AttributeNotFoundException, InstanceNotFoundException,
             MBeanException, ReflectionException, IOException {
         String attributeString[] = new String[attributeNames.length];
         for (int i = 0; i < attributeNames.length; i++) {
@@ -119,32 +128,34 @@
         return objectName;
     }
 
-    private void setObjectName(ObjectName objectName) {
-        this.objectName = objectName;
-        // generate a readable name now
-        String name = getObjectName().getKeyProperty("name");
-        if (name == null)
-            setText(getObjectName().getDomain());
-        else
-            setText(name);
+    public MBeanInfo getMBeanInfo() throws InstanceNotFoundException,
+            IntrospectionException, ReflectionException, IOException {
+        synchronized (mbeanInfoLock) {
+            if (mbeanInfo == null) {
+                mbeanInfo = getMBeanServerConnection().getMBeanInfo(objectName);
+            }
+            return mbeanInfo;
+        }
     }
 
-    public MBeanInfo getMBeanInfo() {
-        return mbeanInfo;
+    @Override
+    public boolean equals(Object obj) {
+        if (obj == null) {
+            return false;
+        }
+        if (obj == this) {
+            return true;
+        }
+        if (!(obj instanceof XMBean)) {
+            return false;
+        }
+        XMBean that = (XMBean) obj;
+        return getObjectName().equals(that.getObjectName());
     }
 
-    private MBeanInfo getMBeanInfo(ObjectName name)
-        throws InstanceNotFoundException, IntrospectionException,
-            ReflectionException, IOException {
-        return getMBeanServerConnection().getMBeanInfo(name);
-    }
-
-    public boolean equals(Object o) {
-        if (o instanceof XMBean) {
-            XMBean mbean = (XMBean) o;
-            return getObjectName().equals((mbean).getObjectName());
-        }
-        return false;
+    @Override
+    public int hashCode() {
+        return (objectName == null ? 0 : objectName.hashCode());
     }
 
     public String getText() {
@@ -163,6 +174,7 @@
         this.icon = icon;
     }
 
+    @Override
     public String toString() {
         return getText();
     }
--- a/src/share/classes/sun/tools/jconsole/inspector/XMBeanInfo.java	Mon Mar 10 23:51:13 2008 +0100
+++ b/src/share/classes/sun/tools/jconsole/inspector/XMBeanInfo.java	Tue Mar 11 01:20:55 2008 +0100
@@ -1,5 +1,5 @@
 /*
- * Copyright 2004-2006 Sun Microsystems, Inc.  All Rights Reserved.
+ * Copyright 2004-2007 Sun Microsystems, Inc.  All Rights Reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -35,10 +35,7 @@
 import javax.swing.border.TitledBorder;
 import javax.swing.event.*;
 import javax.swing.table.*;
-import javax.swing.tree.*;
-import sun.tools.jconsole.JConsole;
 import sun.tools.jconsole.Resources;
-import sun.tools.jconsole.inspector.XNodeInfo.Type;
 
 import static sun.tools.jconsole.Utilities.*;
 
@@ -46,21 +43,20 @@
 public class XMBeanInfo extends JPanel {
 
     private static final Color lightYellow = new Color(255, 255, 128);
-
     private final int NAME_COLUMN = 0;
     private final int VALUE_COLUMN = 1;
-
     private final String[] columnNames = {
         Resources.getText("Name"),
         Resources.getText("Value")
     };
-
     private JTable infoTable = new JTable();
     private JTable descTable = new JTable();
     private JPanel infoBorderPanel = new JPanel(new BorderLayout());
     private JPanel descBorderPanel = new JPanel(new BorderLayout());
 
     private static class ReadOnlyDefaultTableModel extends DefaultTableModel {
+
+        @Override
         public void setValueAt(Object value, int row, int col) {
         }
     }
@@ -73,17 +69,18 @@
             this.tableRowDividerText = tableRowDividerText;
         }
 
+        @Override
         public String toString() {
             return tableRowDividerText;
         }
     }
-
     private static MBeanInfoTableCellRenderer renderer =
             new MBeanInfoTableCellRenderer();
 
     private static class MBeanInfoTableCellRenderer
             extends DefaultTableCellRenderer {
 
+        @Override
         public Component getTableCellRendererComponent(
                 JTable table, Object value, boolean isSelected,
                 boolean hasFocus, int row, int column) {
@@ -92,22 +89,24 @@
             if (value instanceof TableRowDivider) {
                 JLabel label = new JLabel(value.toString());
                 label.setBackground(ensureContrast(lightYellow,
-                                                   label.getForeground()));
+                        label.getForeground()));
                 label.setOpaque(true);
                 return label;
             }
             return comp;
         }
     }
-
     private static TableCellEditor editor =
             new MBeanInfoTableCellEditor(new JTextField());
 
     private static class MBeanInfoTableCellEditor
             extends Utils.ReadOnlyTableCellEditor {
+
         public MBeanInfoTableCellEditor(JTextField tf) {
             super(tf);
         }
+
+        @Override
         public Component getTableCellEditorComponent(
                 JTable table, Object value, boolean isSelected,
                 int row, int column) {
@@ -116,7 +115,7 @@
             if (value instanceof TableRowDivider) {
                 JLabel label = new JLabel(value.toString());
                 label.setBackground(ensureContrast(lightYellow,
-                                                   label.getForeground()));
+                        label.getForeground()));
                 label.setOpaque(true);
                 return label;
             }
@@ -172,6 +171,7 @@
         add(descBorderPanel);
     }
 
+    // Call on EDT
     public void emptyInfoTable() {
         DefaultTableModel tableModel = (DefaultTableModel) infoTable.getModel();
         while (tableModel.getRowCount() > 0) {
@@ -179,6 +179,7 @@
         }
     }
 
+    // Call on EDT
     public void emptyDescTable() {
         DefaultTableModel tableModel = (DefaultTableModel) descTable.getModel();
         while (tableModel.getRowCount() > 0) {
@@ -186,6 +187,7 @@
         }
     }
 
+    // Call on EDT
     private void addDescriptor(Descriptor desc, String text) {
         if (desc != null && desc.getFieldNames().length > 0) {
             DefaultTableModel tableModel = (DefaultTableModel) descTable.getModel();
@@ -223,6 +225,7 @@
         }
     }
 
+    // Call on EDT
     public void addMBeanInfo(XMBean mbean, MBeanInfo mbeanInfo) {
         emptyInfoTable();
         emptyDescTable();
@@ -263,6 +266,7 @@
         tableModel.newDataAvailable(new TableModelEvent(tableModel));
     }
 
+    // Call on EDT
     public void addMBeanAttributeInfo(MBeanAttributeInfo mbai) {
         emptyInfoTable();
         emptyDescTable();
@@ -296,6 +300,7 @@
         tableModel.newDataAvailable(new TableModelEvent(tableModel));
     }
 
+    // Call on EDT
     public void addMBeanOperationInfo(MBeanOperationInfo mboi) {
         emptyInfoTable();
         emptyDescTable();
@@ -343,6 +348,7 @@
         tableModel.newDataAvailable(new TableModelEvent(tableModel));
     }
 
+    // Call on EDT
     public void addMBeanNotificationInfo(MBeanNotificationInfo mbni) {
         emptyInfoTable();
         emptyDescTable();
@@ -367,6 +373,7 @@
         tableModel.newDataAvailable(new TableModelEvent(tableModel));
     }
 
+    // Call on EDT
     private void addMBeanConstructorInfo(MBeanConstructorInfo mbci, String text) {
         DefaultTableModel tableModel = (DefaultTableModel) infoTable.getModel();
         Object rowData[] = new Object[2];
@@ -383,6 +390,7 @@
         tableModel.newDataAvailable(new TableModelEvent(tableModel));
     }
 
+    // Call on EDT
     private void addMBeanParameterInfo(MBeanParameterInfo mbpi, String text) {
         DefaultTableModel tableModel = (DefaultTableModel) infoTable.getModel();
         Object rowData[] = new Object[2];
@@ -401,91 +409,4 @@
         addDescriptor(mbpi.getDescriptor(), text);
         tableModel.newDataAvailable(new TableModelEvent(tableModel));
     }
-
-    public static void loadInfo(DefaultMutableTreeNode root) {
-        // Retrieve XMBean from XNodeInfo
-        //
-        XMBean mbean = (XMBean) ((XNodeInfo) root.getUserObject()).getData();
-        // Initialize MBean*Info
-        //
-        final MBeanInfo mbeanInfo;
-        try {
-            mbeanInfo = mbean.getMBeanInfo();
-        } catch (Exception e) {
-            if (JConsole.isDebug()) {
-                e.printStackTrace();
-            }
-            return;
-        }
-        MBeanAttributeInfo[] ai = mbeanInfo.getAttributes();
-        MBeanOperationInfo[] oi = mbeanInfo.getOperations();
-        MBeanNotificationInfo[] ni = mbeanInfo.getNotifications();
-        // MBeanAttributeInfo node
-        //
-        if (ai != null && ai.length > 0) {
-            DefaultMutableTreeNode attributes = new DefaultMutableTreeNode();
-            XNodeInfo attributesUO = new XNodeInfo(Type.ATTRIBUTES, mbean,
-                    Resources.getText("Attributes"), null);
-            attributes.setUserObject(attributesUO);
-            root.add(attributes);
-            for (MBeanAttributeInfo mbai : ai) {
-                DefaultMutableTreeNode attribute = new DefaultMutableTreeNode();
-                XNodeInfo attributeUO = new XNodeInfo(Type.ATTRIBUTE,
-                        new Object[] {mbean, mbai}, mbai.getName(), null);
-                attribute.setUserObject(attributeUO);
-                attributes.add(attribute);
-            }
-        }
-        // MBeanOperationInfo node
-        //
-        if (oi != null && oi.length > 0) {
-            DefaultMutableTreeNode operations = new DefaultMutableTreeNode();
-            XNodeInfo operationsUO = new XNodeInfo(Type.OPERATIONS, mbean,
-                    Resources.getText("Operations"), null);
-            operations.setUserObject(operationsUO);
-            root.add(operations);
-            for (MBeanOperationInfo mboi : oi) {
-                // Compute the operation's tool tip text:
-                // "operationname(param1type,param2type,...)"
-                //
-                StringBuilder sb = new StringBuilder();
-                for (MBeanParameterInfo mbpi : mboi.getSignature()) {
-                    sb.append(mbpi.getType() + ",");
-                }
-                String signature = sb.toString();
-                if (signature.length() > 0) {
-                    // Remove the trailing ','
-                    //
-                    signature = signature.substring(0, signature.length() - 1);
-                }
-                String toolTipText = mboi.getName() + "(" + signature + ")";
-                // Create operation node
-                //
-                DefaultMutableTreeNode operation = new DefaultMutableTreeNode();
-                XNodeInfo operationUO = new XNodeInfo(Type.OPERATION,
-                        new Object[] {mbean, mboi}, mboi.getName(), toolTipText);
-                operation.setUserObject(operationUO);
-                operations.add(operation);
-            }
-        }
-        // MBeanNotificationInfo node
-        //
-        if (mbean.isBroadcaster()) {
-            DefaultMutableTreeNode notifications = new DefaultMutableTreeNode();
-            XNodeInfo notificationsUO = new XNodeInfo(Type.NOTIFICATIONS, mbean,
-                    Resources.getText("Notifications"), null);
-            notifications.setUserObject(notificationsUO);
-            root.add(notifications);
-            if (ni != null && ni.length > 0) {
-                for (MBeanNotificationInfo mbni : ni) {
-                    DefaultMutableTreeNode notification =
-                            new DefaultMutableTreeNode();
-                    XNodeInfo notificationUO = new XNodeInfo(Type.NOTIFICATION,
-                            mbni, mbni.getName(), null);
-                    notification.setUserObject(notificationUO);
-                    notifications.add(notification);
-                }
-            }
-        }
-    }
 }
--- a/src/share/classes/sun/tools/jconsole/inspector/XMBeanNotifications.java	Mon Mar 10 23:51:13 2008 +0100
+++ b/src/share/classes/sun/tools/jconsole/inspector/XMBeanNotifications.java	Tue Mar 11 01:20:55 2008 +0100
@@ -29,17 +29,13 @@
 import javax.swing.event.*;
 import javax.swing.table.*;
 import javax.swing.tree.*;
-import java.awt.BorderLayout;
-import java.awt.GridLayout;
 import java.awt.Font;
 
 import java.text.SimpleDateFormat;
 
-import java.awt.FlowLayout;
 import java.awt.Component;
 import java.awt.EventQueue;
 import java.awt.event.*;
-import java.awt.Insets;
 import java.awt.Dimension;
 import java.util.*;
 import java.io.*;
@@ -49,45 +45,44 @@
 import javax.management.openmbean.CompositeData;
 import javax.management.openmbean.TabularData;
 
+import sun.tools.jconsole.JConsole;
 import sun.tools.jconsole.Resources;
 
 @SuppressWarnings("serial")
 public class XMBeanNotifications extends JTable implements NotificationListener {
 
-    private final static String[] columnNames =  {
+    private final static String[] columnNames = {
         Resources.getText("TimeStamp"),
         Resources.getText("Type"),
         Resources.getText("UserData"),
         Resources.getText("SeqNum"),
         Resources.getText("Message"),
         Resources.getText("Event"),
-        Resources.getText("Source")};
-
+        Resources.getText("Source")
+    };
     private HashMap<ObjectName, XMBeanNotificationsListener> listeners =
-        new HashMap<ObjectName, XMBeanNotificationsListener>();
-    private boolean subscribed;
+            new HashMap<ObjectName, XMBeanNotificationsListener>();
+    private volatile boolean subscribed;
     private XMBeanNotificationsListener currentListener;
     public final static String NOTIFICATION_RECEIVED_EVENT =
-        "jconsole.xnotification.received";
-
+            "jconsole.xnotification.received";
     private List<NotificationListener> notificationListenersList;
-    private boolean enabled;
-    private Font normalFont, boldFont;
+    private volatile boolean enabled;
+    private Font normalFont,  boldFont;
     private int rowMinHeight = -1;
     private TableCellEditor userDataEditor = new UserDataCellEditor();
     private NotifMouseListener mouseListener = new NotifMouseListener();
     private SimpleDateFormat timeFormater = new SimpleDateFormat("HH:mm:ss:SSS");
-
     private static TableCellEditor editor =
             new Utils.ReadOnlyTableCellEditor(new JTextField());
 
     public XMBeanNotifications() {
-        super(new TableSorter(columnNames,0));
+        super(new TableSorter(columnNames, 0));
         setColumnSelectionAllowed(false);
         setRowSelectionAllowed(false);
         getTableHeader().setReorderingAllowed(false);
         ArrayList<NotificationListener> l =
-            new ArrayList<NotificationListener>(1);
+                new ArrayList<NotificationListener>(1);
         notificationListenersList = Collections.synchronizedList(l);
 
         addMouseListener(mouseListener);
@@ -103,20 +98,24 @@
         addKeyListener(new Utils.CopyKeyAdapter());
     }
 
+    // Call on EDT
     public void cancelCellEditing() {
-        TableCellEditor editor = getCellEditor();
-        if (editor != null) {
-            editor.cancelCellEditing();
+        TableCellEditor tce = getCellEditor();
+        if (tce != null) {
+            tce.cancelCellEditing();
         }
     }
 
+    // Call on EDT
     public void stopCellEditing() {
-        TableCellEditor editor = getCellEditor();
-        if (editor != null) {
-            editor.stopCellEditing();
+        TableCellEditor tce = getCellEditor();
+        if (tce != null) {
+            tce.stopCellEditing();
         }
     }
 
+    // Call on EDT
+    @Override
     public boolean isCellEditable(int row, int col) {
         UserDataCell cell = getUserDataCell(row, col);
         if (cell != null) {
@@ -125,16 +124,21 @@
         return true;
     }
 
+    // Call on EDT
+    @Override
     public void setValueAt(Object value, int row, int column) {
     }
 
-    public synchronized Component prepareRenderer(TableCellRenderer renderer,
-                                                  int row, int column) {
+    // Call on EDT
+    @Override
+    public synchronized Component prepareRenderer(
+            TableCellRenderer renderer, int row, int column) {
         //In case we have a repaint thread that is in the process of
         //repainting an obsolete table, just ignore the call.
         //It can happen when MBean selection is switched at a very quick rate
-        if(row >= getRowCount())
+        if (row >= getRowCount()) {
             return null;
+        }
 
         Component comp = super.prepareRenderer(renderer, row, column);
 
@@ -146,9 +150,10 @@
         if (column == 2 && cell != null) {
             comp.setFont(boldFont);
             int size = cell.getHeight();
-            if(size > 0) {
-                if(getRowHeight(row) != size)
+            if (size > 0) {
+                if (getRowHeight(row) != size) {
                     setRowHeight(row, size);
+                }
             }
         } else {
             comp.setFont(normalFont);
@@ -157,34 +162,35 @@
         return comp;
     }
 
-    public synchronized TableCellRenderer getCellRenderer(int row,
-                                                          int column) {
+    // Call on EDT
+    @Override
+    public synchronized TableCellRenderer getCellRenderer(int row, int column) {
         //In case we have a repaint thread that is in the process of
         //repainting an obsolete table, just ignore the call.
         //It can happen when MBean selection is switched at a very quick rate
-        if(row >= getRowCount())
+        if (row >= getRowCount()) {
             return null;
+        }
 
         DefaultTableCellRenderer renderer;
         String toolTip = null;
         UserDataCell cell = getUserDataCell(row, column);
-        if(cell != null && cell.isInited()) {
+        if (cell != null && cell.isInited()) {
             renderer = (DefaultTableCellRenderer) cell.getRenderer();
-        }
-        else {
-            renderer = (DefaultTableCellRenderer)
-                super.getCellRenderer(row,
-                                      column);
+        } else {
+            renderer =
+                    (DefaultTableCellRenderer) super.getCellRenderer(row, column);
         }
 
-        if(cell != null)
-            toolTip = Resources.getText("Double click to expand/collapse")+". "
-                + cell.toString();
-        else {
+        if (cell != null) {
+            toolTip = Resources.getText("Double click to expand/collapse") +
+                    ". " + cell.toString();
+        } else {
             Object val =
-                ((DefaultTableModel) getModel()).getValueAt(row,column);
-            if(val != null)
+                    ((DefaultTableModel) getModel()).getValueAt(row, column);
+            if (val != null) {
                 toolTip = val.toString();
+            }
         }
 
         renderer.setToolTipText(toolTip);
@@ -192,9 +198,12 @@
         return renderer;
     }
 
+    // Call on EDT
     private UserDataCell getUserDataCell(int row, int column) {
-        Object obj = ((DefaultTableModel) getModel()).getValueAt(row,column);
-        if(obj instanceof UserDataCell) return (UserDataCell) obj;
+        Object obj = ((DefaultTableModel) getModel()).getValueAt(row, column);
+        if (obj instanceof UserDataCell) {
+            return (UserDataCell) obj;
+        }
         return null;
     }
 
@@ -204,19 +213,22 @@
 
     public long getReceivedNotifications(XMBean mbean) {
         XMBeanNotificationsListener listener =
-            listeners.get(mbean.getObjectName());
-        if(listener == null) return 0;
-        else
+                listeners.get(mbean.getObjectName());
+        if (listener == null) {
+            return 0;
+        } else {
             return listener.getReceivedNotifications();
+        }
     }
 
     public synchronized boolean clearCurrentNotifications() {
         emptyTable();
-        if(currentListener != null) {
+        if (currentListener != null) {
             currentListener.clear();
             return true;
-        } else
+        } else {
             return false;
+        }
     }
 
     public synchronized boolean unregisterListener(DefaultMutableTreeNode node) {
@@ -225,29 +237,25 @@
     }
 
     public synchronized void registerListener(DefaultMutableTreeNode node)
-        throws InstanceNotFoundException, IOException {
+            throws InstanceNotFoundException, IOException {
         XMBean mbean = (XMBean) ((XNodeInfo) node.getUserObject()).getData();
-        if(!subscribed) {
+        if (!subscribed) {
             try {
-                mbean.getMBeanServerConnection().
-                    addNotificationListener(new ObjectName("JMImplementation:type=MBeanServerDelegate"),
-                                            this,
-                                            null,
-                                            null);
+                mbean.getMBeanServerConnection().addNotificationListener(
+                        MBeanServerDelegate.DELEGATE_NAME, this, null, null);
                 subscribed = true;
-            }catch(Exception e) {
-                System.out.println("Error adding listener for delegate :"+
-                                   e.getMessage());
+            } catch (Exception e) {
+                if (JConsole.isDebug()) {
+                    System.err.println("Error adding listener for delegate:");
+                    e.printStackTrace();
+                }
             }
         }
-
         XMBeanNotificationsListener listener =
-            listeners.get(mbean.getObjectName());
+                listeners.get(mbean.getObjectName());
         if (listener == null) {
-            listener = new XMBeanNotificationsListener(this,
-                                                       mbean,
-                                                       node,
-                                                       columnNames);
+            listener = new XMBeanNotificationsListener(
+                    this, mbean, node, columnNames);
             listeners.put(mbean.getObjectName(), listener);
         } else {
             if (!listener.isRegistered()) {
@@ -259,19 +267,21 @@
         currentListener = listener;
     }
 
-    public synchronized void handleNotification(Notification notif,
-                                                Object handback) {
+    public synchronized void handleNotification(
+            Notification notif, Object handback) {
         try {
             if (notif instanceof MBeanServerNotification) {
                 ObjectName mbean =
-                    ((MBeanServerNotification)notif).getMBeanName();
-                if (notif.getType().indexOf("JMX.mbean.unregistered")>=0){
+                        ((MBeanServerNotification) notif).getMBeanName();
+                if (notif.getType().indexOf("JMX.mbean.unregistered") >= 0) {
                     unregister(mbean);
                 }
             }
-        } catch(Exception e) {
-             System.out.println("Error unregistering notification:"+
-                               e.getMessage());
+        } catch (Exception e) {
+            if (JConsole.isDebug()) {
+                System.err.println("Error unregistering notification:");
+                e.printStackTrace();
+            }
         }
     }
 
@@ -283,75 +293,77 @@
 
     private synchronized boolean unregister(ObjectName mbean) {
         XMBeanNotificationsListener listener = listeners.get(mbean);
-        if(listener != null && listener.isRegistered()) {
+        if (listener != null && listener.isRegistered()) {
             listener.unregister();
             return true;
-        } else
+        } else {
             return false;
+        }
     }
 
     public void addNotificationsListener(NotificationListener nl) {
-            notificationListenersList.add(nl);
+        notificationListenersList.add(nl);
     }
 
     public void removeNotificationsListener(NotificationListener nl) {
         notificationListenersList.remove(nl);
     }
 
-    void fireNotificationReceived(XMBeanNotificationsListener listener,
-                                  XMBean mbean,
-                                  DefaultMutableTreeNode node,
-                                  Object[] rowData,
-                                  long received) {
-        if(enabled) {
+    // Call on EDT
+    void fireNotificationReceived(
+            XMBeanNotificationsListener listener, XMBean mbean,
+            DefaultMutableTreeNode node, Object[] rowData, long received) {
+        if (enabled) {
             DefaultTableModel tableModel = (DefaultTableModel) getModel();
-            if(listener == currentListener) {
-
-                //tableModel.addRow(rowData);
+            if (listener == currentListener) {
                 tableModel.insertRow(0, rowData);
-
-                //tableModel.newDataAvailable(new TableModelEvent(tableModel));
                 repaint();
             }
         }
-
-        Notification notif = new Notification(NOTIFICATION_RECEIVED_EVENT,
-                                              this,
-                                              0);
-        notif.setUserData(new Long(received));
-        for(NotificationListener nl : notificationListenersList)
-            nl.handleNotification(notif,node);
+        Notification notif =
+                new Notification(NOTIFICATION_RECEIVED_EVENT, this, 0);
+        notif.setUserData(received);
+        for (NotificationListener nl : notificationListenersList) {
+            nl.handleNotification(notif, node);
+        }
     }
 
+    // Call on EDT
     private void updateModel(List<Object[]> data) {
         emptyTable();
         DefaultTableModel tableModel = (DefaultTableModel) getModel();
-        for(Object[] rowData : data)
+        for (Object[] rowData : data) {
             tableModel.addRow(rowData);
+        }
     }
 
     public synchronized boolean isListenerRegistered(XMBean mbean) {
         XMBeanNotificationsListener listener =
-            listeners.get(mbean.getObjectName());
-        if(listener == null) return false;
+                listeners.get(mbean.getObjectName());
+        if (listener == null) {
+            return false;
+        }
         return listener.isRegistered();
     }
 
+    // Call on EDT
     public synchronized void loadNotifications(XMBean mbean) {
         XMBeanNotificationsListener listener =
-            listeners.get(mbean.getObjectName());
+                listeners.get(mbean.getObjectName());
         emptyTable();
-        if(listener != null ) {
+        if (listener != null) {
             enabled = true;
             List<Object[]> data = listener.getData();
             updateModel(data);
             currentListener = listener;
             validate();
             repaint();
-        } else
+        } else {
             enabled = false;
+        }
     }
 
+    // Call on EDT
     private void setColumnEditors() {
         TableColumnModel tcm = getColumnModel();
         for (int i = 0; i < columnNames.length; i++) {
@@ -364,40 +376,40 @@
         }
     }
 
+    // Call on EDT
     public boolean isTableEditable() {
         return true;
     }
 
+    // Call on EDT
     public synchronized void emptyTable() {
-        DefaultTableModel model = (DefaultTableModel)getModel();
+        DefaultTableModel model = (DefaultTableModel) getModel();
         //invalidate();
-        while (model.getRowCount()>0)
+        while (model.getRowCount() > 0) {
             model.removeRow(0);
+        }
         validate();
     }
 
-    synchronized void updateUserDataCell(int row,
-                                         int col) {
+    // Call on EDT
+    synchronized void updateUserDataCell(int row, int col) {
         Object obj = getModel().getValueAt(row, 2);
-        if(obj instanceof UserDataCell) {
+        if (obj instanceof UserDataCell) {
             UserDataCell cell = (UserDataCell) obj;
-            if(!cell.isInited()) {
-                if(rowMinHeight == -1)
+            if (!cell.isInited()) {
+                if (rowMinHeight == -1) {
                     rowMinHeight = getRowHeight(row);
-
-                cell.init(super.getCellRenderer(row, col),
-                          rowMinHeight);
+                }
+                cell.init(super.getCellRenderer(row, col), rowMinHeight);
             }
 
             cell.switchState();
-            setRowHeight(row,
-                         cell.getHeight());
+            setRowHeight(row, cell.getHeight());
 
-            if(!cell.isMaximized()) {
+            if (!cell.isMaximized()) {
                 cancelCellEditing();
                 //Back to simple editor.
-                editCellAt(row,
-                           2);
+                editCellAt(row, 2);
             }
 
             invalidate();
@@ -406,7 +418,9 @@
     }
 
     class UserDataCellRenderer extends DefaultTableCellRenderer {
+
         Component comp;
+
         UserDataCellRenderer(Component comp) {
             this.comp = comp;
             Dimension d = comp.getPreferredSize();
@@ -415,56 +429,62 @@
             }
         }
 
-        public Component getTableCellRendererComponent(JTable table,
-                                                       Object value,
-                                                       boolean isSelected,
-                                                       boolean hasFocus,
-                                                       int row,
-                                                       int column) {
+        @Override
+        public Component getTableCellRendererComponent(
+                JTable table,
+                Object value,
+                boolean isSelected,
+                boolean hasFocus,
+                int row,
+                int column) {
             return comp;
         }
 
         public Component getComponent() {
             return comp;
         }
-
     }
 
     class UserDataCell {
+
         TableCellRenderer minRenderer;
         UserDataCellRenderer maxRenderer;
         int minHeight;
         boolean minimized = true;
         boolean init = false;
         Object userData;
-       UserDataCell(Object userData, Component max) {
-           this.userData = userData;
-           this.maxRenderer = new UserDataCellRenderer(max);
 
-       }
+        UserDataCell(Object userData, Component max) {
+            this.userData = userData;
+            this.maxRenderer = new UserDataCellRenderer(max);
 
-       public String toString() {
-           if(userData == null) return null;
-           if(userData.getClass().isArray()) {
-               String name =
-                   Utils.getArrayClassName(userData.getClass().getName());
-               int length = Array.getLength(userData);
-               return name + "[" + length +"]";
-           }
+        }
 
-            if(userData instanceof CompositeData ||
-               userData instanceof TabularData)
+        @Override
+        public String toString() {
+            if (userData == null) {
+                return null;
+            }
+            if (userData.getClass().isArray()) {
+                String name =
+                        Utils.getArrayClassName(userData.getClass().getName());
+                int length = Array.getLength(userData);
+                return name + "[" + length + "]";
+            }
+
+            if (userData instanceof CompositeData ||
+                    userData instanceof TabularData) {
                 return userData.getClass().getName();
+            }
 
             return userData.toString();
-       }
+        }
 
         boolean isInited() {
             return init;
         }
 
-        void init(TableCellRenderer minRenderer,
-                  int minHeight) {
+        void init(TableCellRenderer minRenderer, int minHeight) {
             this.minRenderer = minRenderer;
             this.minHeight = minHeight;
             init = true;
@@ -473,9 +493,11 @@
         void switchState() {
             minimized = !minimized;
         }
+
         boolean isMaximized() {
             return !minimized;
         }
+
         void minimize() {
             minimized = true;
         }
@@ -485,30 +507,39 @@
         }
 
         int getHeight() {
-            if(minimized) return minHeight;
-            else
+            if (minimized) {
+                return minHeight;
+            } else {
                 return (int) maxRenderer.getComponent().
-                    getPreferredSize().getHeight() ;
+                        getPreferredSize().getHeight();
+            }
         }
 
         TableCellRenderer getRenderer() {
-            if(minimized) return minRenderer;
-            else return maxRenderer;
+            if (minimized) {
+                return minRenderer;
+            } else {
+                return maxRenderer;
+            }
         }
     }
 
     class NotifMouseListener extends MouseAdapter {
 
+        @Override
         public void mousePressed(MouseEvent e) {
-            if(e.getButton() == MouseEvent.BUTTON1) {
-                if(e.getClickCount() >= 2) {
+            if (e.getButton() == MouseEvent.BUTTON1) {
+                if (e.getClickCount() >= 2) {
                     int row = XMBeanNotifications.this.getSelectedRow();
                     int col = XMBeanNotifications.this.getSelectedColumn();
-                    if(col != 2) return;
-                    if(col == -1 || row == -1) return;
+                    if (col != 2) {
+                        return;
+                    }
+                    if (col == -1 || row == -1) {
+                        return;
+                    }
 
-                    XMBeanNotifications.this.updateUserDataCell(row,
-                                                                col);
+                    XMBeanNotifications.this.updateUserDataCell(row, col);
                 }
             }
         }
@@ -516,20 +547,21 @@
 
     class UserDataCellEditor extends XTextFieldEditor {
         // implements javax.swing.table.TableCellEditor
-        public Component getTableCellEditorComponent(JTable table,
-                                                     Object value,
-                                                     boolean isSelected,
-                                                     int row,
-                                                     int column) {
+        @Override
+        public Component getTableCellEditorComponent(
+                JTable table,
+                Object value,
+                boolean isSelected,
+                int row,
+                int column) {
             Object val = value;
-            if(column == 2) {
-                Object obj = getModel().getValueAt(row,
-                                                   column);
-                if(obj instanceof UserDataCell) {
+            if (column == 2) {
+                Object obj = getModel().getValueAt(row, column);
+                if (obj instanceof UserDataCell) {
                     UserDataCell cell = (UserDataCell) obj;
-                    if(cell.getRenderer() instanceof UserDataCellRenderer) {
+                    if (cell.getRenderer() instanceof UserDataCellRenderer) {
                         UserDataCellRenderer zr =
-                            (UserDataCellRenderer) cell.getRenderer();
+                                (UserDataCellRenderer) cell.getRenderer();
                         return zr.getComponent();
                     }
                 } else {
@@ -539,12 +571,14 @@
                     return comp;
                 }
             }
-            return super.getTableCellEditorComponent(table,
-                                                     val,
-                                                     isSelected,
-                                                     row,
-                                                     column);
+            return super.getTableCellEditorComponent(
+                    table,
+                    val,
+                    isSelected,
+                    row,
+                    column);
         }
+
         @Override
         public boolean stopCellEditing() {
             int editingRow = getEditingRow();
@@ -554,7 +588,7 @@
                 if (obj instanceof UserDataCell) {
                     UserDataCell cell = (UserDataCell) obj;
                     if (cell.isMaximized()) {
-                        this.cancelCellEditing();
+                        cancelCellEditing();
                         return true;
                     }
                 }
@@ -564,17 +598,20 @@
     }
 
     class XMBeanNotificationsListener implements NotificationListener {
+
         private String[] columnNames;
         private XMBean xmbean;
         private DefaultMutableTreeNode node;
-        private long received;
+        private volatile long received;
         private XMBeanNotifications notifications;
-        private boolean unregistered;
+        private volatile boolean unregistered;
         private ArrayList<Object[]> data = new ArrayList<Object[]>();
-        public XMBeanNotificationsListener(XMBeanNotifications notifications,
-                                           XMBean xmbean,
-                                           DefaultMutableTreeNode node,
-                                           String[] columnNames) {
+
+        public XMBeanNotificationsListener(
+                XMBeanNotifications notifications,
+                XMBean xmbean,
+                DefaultMutableTreeNode node,
+                String[] columnNames) {
             this.notifications = notifications;
             this.xmbean = xmbean;
             this.node = node;
@@ -591,22 +628,24 @@
             received = 0;
         }
 
-        public boolean isRegistered() {
+        public synchronized boolean isRegistered() {
             return !unregistered;
         }
 
         public synchronized void unregister() {
             try {
-                xmbean.getMBeanServerConnection().
-                    removeNotificationListener(xmbean.getObjectName(),this,null,null);
-            }catch(Exception e) {
-                System.out.println("Error removing listener :"+
-                                   e.getMessage());
+                xmbean.getMBeanServerConnection().removeNotificationListener(
+                        xmbean.getObjectName(), this, null, null);
+            } catch (Exception e) {
+                if (JConsole.isDebug()) {
+                    System.err.println("Error removing listener:");
+                    e.printStackTrace();
+                }
             }
             unregistered = true;
         }
 
-        public long getReceivedNotifications() {
+        public synchronized long getReceivedNotifications() {
             return received;
         }
 
@@ -614,52 +653,62 @@
             clear();
             this.node = node;
             try {
-                xmbean.getMBeanServerConnection().
-                    addNotificationListener(xmbean.getObjectName(),this,null,null);
+                xmbean.getMBeanServerConnection().addNotificationListener(
+                        xmbean.getObjectName(), this, null, null);
                 unregistered = false;
-            }catch(Exception e) {
-                System.out.println("Error adding listener :"+
-                                   e.getMessage());
+            } catch (Exception e) {
+                if (JConsole.isDebug()) {
+                    System.err.println("Error adding listener:");
+                    e.printStackTrace();
+                }
             }
         }
 
-        public synchronized void handleNotification(Notification e,
-                                                    Object handback) {
-            try {
-                if(unregistered) return;
-                Date receivedDate = new Date(e.getTimeStamp());
-                String time = timeFormater.format(receivedDate);
+        public synchronized void handleNotification(
+                final Notification n, Object hb) {
+            EventQueue.invokeLater(new Runnable() {
 
-                Object userData = e.getUserData();
-                Component comp = null;
-                UserDataCell cell = null;
-                if((comp = XDataViewer.createNotificationViewer(userData))
-                   != null) {
-                    XDataViewer.registerForMouseEvent(comp, mouseListener);
-                    cell = new UserDataCell(userData, comp);
+                public void run() {
+                    synchronized (XMBeanNotificationsListener.this) {
+                        try {
+                            if (unregistered) {
+                                return;
+                            }
+                            Date receivedDate = new Date(n.getTimeStamp());
+                            String time = timeFormater.format(receivedDate);
+
+                            Object userData = n.getUserData();
+                            Component comp = null;
+                            UserDataCell cell = null;
+                            if ((comp = XDataViewer.createNotificationViewer(userData)) != null) {
+                                XDataViewer.registerForMouseEvent(comp, mouseListener);
+                                cell = new UserDataCell(userData, comp);
+                            }
+
+                            Object[] rowData = {
+                                time,
+                                n.getType(),
+                                (cell == null ? userData : cell),
+                                n.getSequenceNumber(),
+                                n.getMessage(),
+                                n,
+                                n.getSource()
+                            };
+                            received++;
+                            data.add(0, rowData);
+
+                            notifications.fireNotificationReceived(
+                                    XMBeanNotificationsListener.this,
+                                    xmbean, node, rowData, received);
+                        } catch (Exception e) {
+                            if (JConsole.isDebug()) {
+                                System.err.println("Error handling notification:");
+                                e.printStackTrace();
+                            }
+                        }
+                    }
                 }
-
-                Object[] rowData = {time,
-                                    e.getType(),
-                                    (cell == null ? userData : cell),
-                                    new Long(e.getSequenceNumber()),
-                                    e.getMessage(),
-                                    e,
-                                    e.getSource()};
-                received++;
-                data.add(0, rowData);
-
-                notifications.fireNotificationReceived(this,
-                                                       xmbean,
-                                                       node,
-                                                       rowData,
-                                                       received);
-            }
-            catch (Exception ex) {
-                ex.printStackTrace();
-                System.out.println("Error when handling notification :"+
-                                   ex.toString());
-            }
+            });
         }
     }
 }
--- a/src/share/classes/sun/tools/jconsole/inspector/XMBeanOperations.java	Mon Mar 10 23:51:13 2008 +0100
+++ b/src/share/classes/sun/tools/jconsole/inspector/XMBeanOperations.java	Tue Mar 11 01:20:55 2008 +0100
@@ -22,6 +22,7 @@
  * CA 95054 USA or visit www.sun.com if you need additional information or
  * have any questions.
  */
+
 package sun.tools.jconsole.inspector;
 
 import javax.management.*;
--- a/src/share/classes/sun/tools/jconsole/inspector/XObject.java	Mon Mar 10 23:51:13 2008 +0100
+++ b/src/share/classes/sun/tools/jconsole/inspector/XObject.java	Tue Mar 11 01:20:55 2008 +0100
@@ -22,6 +22,7 @@
  * CA 95054 USA or visit www.sun.com if you need additional information or
  * have any questions.
  */
+
 package sun.tools.jconsole.inspector;
 
 // java import
--- a/src/share/classes/sun/tools/jconsole/inspector/XOperations.java	Mon Mar 10 23:51:13 2008 +0100
+++ b/src/share/classes/sun/tools/jconsole/inspector/XOperations.java	Tue Mar 11 01:20:55 2008 +0100
@@ -33,10 +33,7 @@
 import java.awt.GridLayout;
 import java.awt.FlowLayout;
 import java.awt.Component;
-import java.awt.EventQueue;
 import java.awt.event.*;
-import java.awt.Insets;
-import java.awt.Dimension;
 import java.util.*;
 import java.io.*;
 
@@ -49,29 +46,30 @@
 public abstract class XOperations extends JPanel implements ActionListener {
 
     public final static String OPERATION_INVOCATION_EVENT =
-        "jam.xoperations.invoke.result";
+            "jam.xoperations.invoke.result";
     private java.util.List<NotificationListener> notificationListenersList;
-
     private Hashtable<JButton, OperationEntry> operationEntryTable;
-
     private XMBean mbean;
     private MBeanInfo mbeanInfo;
     private MBeansTab mbeansTab;
+
     public XOperations(MBeansTab mbeansTab) {
-        super(new GridLayout(1,1));
+        super(new GridLayout(1, 1));
         this.mbeansTab = mbeansTab;
         operationEntryTable = new Hashtable<JButton, OperationEntry>();
         ArrayList<NotificationListener> l =
-            new ArrayList<NotificationListener>(1);
+                new ArrayList<NotificationListener>(1);
         notificationListenersList =
-            Collections.synchronizedList(l);
+                Collections.synchronizedList(l);
     }
 
+    // Call on EDT
     public void removeOperations() {
         removeAll();
     }
 
-    public void loadOperations(XMBean mbean,MBeanInfo mbeanInfo) {
+    // Call on EDT
+    public void loadOperations(XMBean mbean, MBeanInfo mbeanInfo) {
         this.mbean = mbean;
         this.mbeanInfo = mbeanInfo;
         // add operations information
@@ -80,131 +78,149 @@
 
         // remove listeners, if any
         Component listeners[] = getComponents();
-        for (int i = 0; i < listeners.length; i++)
-            if (listeners[i] instanceof JButton)
-                ((JButton)listeners[i]).removeActionListener(this);
+        for (int i = 0; i < listeners.length; i++) {
+            if (listeners[i] instanceof JButton) {
+                ((JButton) listeners[i]).removeActionListener(this);
+            }
+        }
 
         removeAll();
         setLayout(new BorderLayout());
 
         JButton methodButton;
         JLabel methodLabel;
-        JPanel innerPanelLeft,innerPanelRight;
-        JPanel outerPanelLeft,outerPanelRight;
-        outerPanelLeft  = new JPanel(new GridLayout(operations.length,1));
-        outerPanelRight = new JPanel(new GridLayout(operations.length,1));
+        JPanel innerPanelLeft, innerPanelRight;
+        JPanel outerPanelLeft, outerPanelRight;
+        outerPanelLeft = new JPanel(new GridLayout(operations.length, 1));
+        outerPanelRight = new JPanel(new GridLayout(operations.length, 1));
 
-        for (int i=0;i<operations.length;i++) {
-            innerPanelLeft  = new JPanel(new FlowLayout(FlowLayout.RIGHT));
+        for (int i = 0; i < operations.length; i++) {
+            innerPanelLeft = new JPanel(new FlowLayout(FlowLayout.RIGHT));
             innerPanelRight = new JPanel(new FlowLayout(FlowLayout.LEFT));
-            innerPanelLeft.add(methodLabel =
-                               new JLabel(Utils.
-                                          getReadableClassName(operations[i].
-                                                             getReturnType()),
-                                          JLabel.RIGHT));
-            if (methodLabel.getText().length()>20) {
+            String returnType = operations[i].getReturnType();
+            if (returnType == null) {
+                methodLabel = new JLabel("null", JLabel.RIGHT);
+                if (JConsole.isDebug()) {
+                    System.err.println(
+                            "WARNING: The operation's return type " +
+                            "shouldn't be \"null\". Check how the " +
+                            "MBeanOperationInfo for the \"" +
+                            operations[i].getName() + "\" operation has " +
+                            "been defined in the MBean's implementation code.");
+                }
+            } else {
+                methodLabel = new JLabel(
+                        Utils.getReadableClassName(returnType), JLabel.RIGHT);
+            }
+            innerPanelLeft.add(methodLabel);
+            if (methodLabel.getText().length() > 20) {
                 methodLabel.setText(methodLabel.getText().
-                                    substring(methodLabel.getText().
-                                              lastIndexOf(".")+1,
-                                             methodLabel.getText().length()));
+                        substring(methodLabel.getText().
+                        lastIndexOf(".") + 1,
+                        methodLabel.getText().length()));
             }
 
             methodButton = new JButton(operations[i].getName());
             methodButton.setToolTipText(operations[i].getDescription());
             boolean callable = isCallable(operations[i].getSignature());
-            if(callable)
+            if (callable) {
                 methodButton.addActionListener(this);
-            else
+            } else {
                 methodButton.setEnabled(false);
+            }
 
             MBeanParameterInfo[] signature = operations[i].getSignature();
             OperationEntry paramEntry = new OperationEntry(operations[i],
-                                                           callable,
-                                                           methodButton,
-                                                           this);
+                    callable,
+                    methodButton,
+                    this);
             operationEntryTable.put(methodButton, paramEntry);
             innerPanelRight.add(methodButton);
-                if(signature.length==0)
-                    innerPanelRight.add(new JLabel("( )",JLabel.CENTER));
-                else
-                    innerPanelRight.add(paramEntry);
+            if (signature.length == 0) {
+                innerPanelRight.add(new JLabel("( )", JLabel.CENTER));
+            } else {
+                innerPanelRight.add(paramEntry);
+            }
 
-            outerPanelLeft.add(innerPanelLeft,BorderLayout.WEST);
-            outerPanelRight.add(innerPanelRight,BorderLayout.CENTER);
+            outerPanelLeft.add(innerPanelLeft, BorderLayout.WEST);
+            outerPanelRight.add(innerPanelRight, BorderLayout.CENTER);
         }
-        add(outerPanelLeft,BorderLayout.WEST);
-        add(outerPanelRight,BorderLayout.CENTER);
+        add(outerPanelLeft, BorderLayout.WEST);
+        add(outerPanelRight, BorderLayout.CENTER);
         validate();
     }
 
     private boolean isCallable(MBeanParameterInfo[] signature) {
-        for(int i = 0; i < signature.length; i++) {
-            if(!Utils.isEditableType(signature[i].getType()))
+        for (int i = 0; i < signature.length; i++) {
+            if (!Utils.isEditableType(signature[i].getType())) {
                 return false;
+            }
         }
         return true;
     }
 
+    // Call on EDT
     public void actionPerformed(final ActionEvent e) {
-        performInvokeRequest((JButton)e.getSource());
+        performInvokeRequest((JButton) e.getSource());
     }
 
     void performInvokeRequest(final JButton button) {
-        mbeansTab.workerAdd(new Runnable() {
-            public void run() {
+        final OperationEntry entryIf = operationEntryTable.get(button);
+        new SwingWorker<Object, Void>() {
+            @Override
+            public Object doInBackground() throws Exception {
+                return mbean.invoke(button.getText(),
+                        entryIf.getParameters(), entryIf.getSignature());
+            }
+            @Override
+            protected void done() {
                 try {
-                    OperationEntry entryIf = operationEntryTable.get(button);
-                    Object result = null;
-                    result = mbean.invoke(button.getText(),
-                                          entryIf.getParameters(),
-                                          entryIf.getSignature());
+                    Object result = get();
                     // sends result notification to upper level if
                     // there is a return value
                     if (entryIf.getReturnType() != null &&
-                        !entryIf.getReturnType().equals(Void.TYPE.getName()) &&
-                        !entryIf.getReturnType().equals(Void.class.getName()))
-                        fireChangedNotification(OPERATION_INVOCATION_EVENT,
-                                                button,
-                                                result);
-                    else
-                        EventQueue.invokeLater(new ThreadDialog(
+                            !entryIf.getReturnType().equals(Void.TYPE.getName()) &&
+                            !entryIf.getReturnType().equals(Void.class.getName())) {
+                        fireChangedNotification(OPERATION_INVOCATION_EVENT, button, result);
+                    } else {
+                        new ThreadDialog(
+                                button,
+                                Resources.getText("Method successfully invoked"),
+                                Resources.getText("Info"),
+                                JOptionPane.INFORMATION_MESSAGE).run();
+                    }
+                } catch (Throwable t) {
+                    t = Utils.getActualException(t);
+                    if (JConsole.isDebug()) {
+                        t.printStackTrace();
+                    }
+                    new ThreadDialog(
                             button,
-                            Resources.getText("Method successfully invoked"),
-                            Resources.getText("Info"),
-                            JOptionPane.INFORMATION_MESSAGE));
-                } catch (Throwable ex) {
-                    if (JConsole.isDebug()) {
-                        ex.printStackTrace();
-                    }
-                    ex = Utils.getActualException(ex);
-                    String message = ex.toString();
-                    EventQueue.invokeLater(new ThreadDialog(
-                        button,
-                        Resources.getText("Problem invoking") + " " +
-                        button.getText() + " : " + message,
-                        Resources.getText("Error"),
-                        JOptionPane.ERROR_MESSAGE));
+                            Resources.getText("Problem invoking") + " " +
+                            button.getText() + " : " + t.toString(),
+                            Resources.getText("Error"),
+                            JOptionPane.ERROR_MESSAGE).run();
                 }
             }
-        });
+        }.execute();
     }
 
     public void addOperationsListener(NotificationListener nl) {
-            notificationListenersList.add(nl);
-        }
+        notificationListenersList.add(nl);
+    }
 
     public void removeOperationsListener(NotificationListener nl) {
         notificationListenersList.remove(nl);
     }
 
-    private void fireChangedNotification(String type,
-                                         Object source,
-                                         Object handback) {
-        Notification e = new Notification(type,source,0);
-        for(NotificationListener nl : notificationListenersList)
-            nl.handleNotification(e,handback);
+    // Call on EDT
+    private void fireChangedNotification(
+            String type, Object source, Object handback) {
+        Notification n = new Notification(type, source, 0);
+        for (NotificationListener nl : notificationListenersList) {
+            nl.handleNotification(n, handback);
+        }
     }
 
-    protected abstract MBeanOperationInfo[]
-        updateOperations(MBeanOperationInfo[] operations);
+    protected abstract MBeanOperationInfo[] updateOperations(MBeanOperationInfo[] operations);
 }
--- a/src/share/classes/sun/tools/jconsole/inspector/XPlotter.java	Mon Mar 10 23:51:13 2008 +0100
+++ b/src/share/classes/sun/tools/jconsole/inspector/XPlotter.java	Tue Mar 11 01:20:55 2008 +0100
@@ -22,7 +22,9 @@
  * CA 95054 USA or visit www.sun.com if you need additional information or
  * have any questions.
  */
+
 package sun.tools.jconsole.inspector;
+
 import sun.tools.jconsole.Plotter;
 import javax.swing.JTable;
 import java.awt.Graphics;
--- a/src/share/classes/sun/tools/jconsole/inspector/XPlottingViewer.java	Mon Mar 10 23:51:13 2008 +0100
+++ b/src/share/classes/sun/tools/jconsole/inspector/XPlottingViewer.java	Tue Mar 11 01:20:55 2008 +0100
@@ -22,6 +22,7 @@
  * CA 95054 USA or visit www.sun.com if you need additional information or
  * have any questions.
  */
+
 package sun.tools.jconsole.inspector;
 
 import java.awt.*;
--- a/src/share/classes/sun/tools/jconsole/inspector/XSheet.java	Mon Mar 10 23:51:13 2008 +0100
+++ b/src/share/classes/sun/tools/jconsole/inspector/XSheet.java	Tue Mar 11 01:20:55 2008 +0100
@@ -1,5 +1,5 @@
 /*
- * Copyright 2004-2006 Sun Microsystems, Inc.  All Rights Reserved.
+ * Copyright 2004-2007 Sun Microsystems, Inc.  All Rights Reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -28,7 +28,6 @@
 import java.awt.*;
 import java.awt.event.*;
 import java.io.*;
-import java.util.Enumeration;
 import javax.management.*;
 import javax.swing.*;
 import javax.swing.border.*;
@@ -45,31 +44,22 @@
 
     private JPanel mainPanel;
     private JPanel southPanel;
-
     // Node being currently displayed
-    private DefaultMutableTreeNode node;
-
+    private volatile DefaultMutableTreeNode currentNode;
     // MBean being currently displayed
-    private XMBean mbean;
-
+    private volatile XMBean mbean;
     // XMBeanAttributes container
     private XMBeanAttributes mbeanAttributes;
-
     // XMBeanOperations container
     private XMBeanOperations mbeanOperations;
-
     // XMBeanNotifications container
     private XMBeanNotifications mbeanNotifications;
-
     // XMBeanInfo container
     private XMBeanInfo mbeanInfo;
-
     // Refresh JButton (mbean attributes case)
     private JButton refreshButton;
-
     // Subscribe/Unsubscribe/Clear JButton (mbean notifications case)
-    private JButton clearButton, subscribeButton, unsubscribeButton;
-
+    private JButton clearButton,  subscribeButton,  unsubscribeButton;
     // Reference to MBeans tab
     private MBeansTab mbeansTab;
 
@@ -86,6 +76,7 @@
 
     private void setupScreen() {
         setLayout(new BorderLayout());
+        setBorder(BorderFactory.createLineBorder(Color.GRAY));
         // add main panel to XSheet
         mainPanel = new JPanel();
         mainPanel.setLayout(new BorderLayout());
@@ -129,17 +120,32 @@
         mbeanInfo = new XMBeanInfo();
     }
 
-    public boolean isMBeanNode(DefaultMutableTreeNode node) {
-        XNodeInfo uo = (XNodeInfo) node.getUserObject();
-        return uo.getType().equals(Type.MBEAN);
+    private boolean isSelectedNode(DefaultMutableTreeNode n, DefaultMutableTreeNode cn) {
+        return (cn == n);
     }
 
-    public void displayNode(DefaultMutableTreeNode node) {
+    // Call on EDT
+    private void showErrorDialog(Object message, String title) {
+        new ThreadDialog(this, message, title, JOptionPane.ERROR_MESSAGE).run();
+    }
+
+    public boolean isMBeanNode(DefaultMutableTreeNode node) {
+        Object userObject = node.getUserObject();
+        if (userObject instanceof XNodeInfo) {
+            XNodeInfo uo = (XNodeInfo) userObject;
+            return uo.getType().equals(Type.MBEAN);
+        }
+        return false;
+    }
+
+    // Call on EDT
+    public synchronized void displayNode(DefaultMutableTreeNode node) {
         clear();
+        displayEmptyNode();
         if (node == null) {
-            displayEmptyNode();
             return;
         }
+        currentNode = node;
         Object userObject = node.getUserObject();
         if (userObject instanceof XNodeInfo) {
             XNodeInfo uo = (XNodeInfo) userObject;
@@ -173,27 +179,28 @@
         }
     }
 
+    // Call on EDT
     private void displayMBeanNode(final DefaultMutableTreeNode node) {
         final XNodeInfo uo = (XNodeInfo) node.getUserObject();
         if (!uo.getType().equals(Type.MBEAN)) {
             return;
         }
-        mbeansTab.workerAdd(new Runnable() {
-            public void run() {
+        mbean = (XMBean) uo.getData();
+        SwingWorker<MBeanInfo, Void> sw = new SwingWorker<MBeanInfo, Void>() {
+            @Override
+            public MBeanInfo doInBackground() throws InstanceNotFoundException,
+                    IntrospectionException, ReflectionException, IOException {
+                return mbean.getMBeanInfo();
+            }
+            @Override
+            protected void done() {
                 try {
-                    XSheet.this.node = node;
-                    XSheet.this.mbean = (XMBean) uo.getData();
-                    mbeanInfo.addMBeanInfo(mbean, mbean.getMBeanInfo());
-                } catch (Throwable ex) {
-                    EventQueue.invokeLater(new ThreadDialog(
-                            XSheet.this,
-                            ex.getMessage(),
-                            Resources.getText("Problem displaying MBean"),
-                            JOptionPane.ERROR_MESSAGE));
-                    return;
-                }
-                EventQueue.invokeLater(new Runnable() {
-                    public void run() {
+                    MBeanInfo mbi = get();
+                    if (mbi != null) {
+                        if (!isSelectedNode(node, currentNode)) {
+                            return;
+                        }
+                        mbeanInfo.addMBeanInfo(mbean, mbi);
                         invalidate();
                         mainPanel.removeAll();
                         mainPanel.add(mbeanInfo, BorderLayout.CENTER);
@@ -202,9 +209,19 @@
                         validate();
                         repaint();
                     }
-                });
+                } catch (Exception e) {
+                    Throwable t = Utils.getActualException(e);
+                    if (JConsole.isDebug()) {
+                        System.err.println("Couldn't get MBeanInfo for MBean [" +
+                                mbean.getObjectName() + "]");
+                        t.printStackTrace();
+                    }
+                    showErrorDialog(t.toString(),
+                            Resources.getText("Problem displaying MBean"));
+                }
             }
-        });
+        };
+        sw.execute();
     }
 
     // Call on EDT
@@ -213,90 +230,90 @@
         final XMBeanInfo mbi = mbeanInfo;
         switch (uo.getType()) {
             case ATTRIBUTE:
-                mbeansTab.workerAdd(new Runnable() {
-                    public void run() {
-                        Object attrData = uo.getData();
-                        XSheet.this.mbean = (XMBean) ((Object[]) attrData)[0];
-                        final MBeanAttributeInfo mbai =
-                                (MBeanAttributeInfo) ((Object[]) attrData)[1];
-                        final XMBeanAttributes mba = mbeanAttributes;
-                        try {
-                            mba.loadAttributes(mbean, new MBeanInfo(
-                                    null, null, new MBeanAttributeInfo[] {mbai},
-                                    null, null, null));
-                        } catch (Exception e) {
-                            EventQueue.invokeLater(new ThreadDialog(
-                                    XSheet.this,
-                                    e.getMessage(),
-                                    Resources.getText("Problem displaying MBean"),
-                                    JOptionPane.ERROR_MESSAGE));
-                            return;
-                        }
-                        EventQueue.invokeLater(new Runnable() {
-                            public void run() {
-                                invalidate();
-                                mainPanel.removeAll();
-                                JPanel attributePanel =
-                                        new JPanel(new BorderLayout());
-                                JPanel attributeBorderPanel =
-                                        new JPanel(new BorderLayout());
-                                attributeBorderPanel.setBorder(
-                                        BorderFactory.createTitledBorder(
-                                        Resources.getText("Attribute value")));
-                                JPanel attributeValuePanel =
-                                        new JPanel(new BorderLayout());
-                                attributeValuePanel.setBorder(
-                                        LineBorder.createGrayLineBorder());
-                                attributeValuePanel.add(mba.getTableHeader(),
-                                        BorderLayout.PAGE_START);
-                                attributeValuePanel.add(mba,
-                                        BorderLayout.CENTER);
-                                attributeBorderPanel.add(attributeValuePanel,
-                                        BorderLayout.CENTER);
-                                JPanel refreshButtonPanel = new JPanel();
-                                refreshButtonPanel.add(refreshButton);
-                                attributeBorderPanel.add(refreshButtonPanel,
-                                        BorderLayout.SOUTH);
-                                refreshButton.setEnabled(true);
-                                attributePanel.add(attributeBorderPanel,
-                                        BorderLayout.NORTH);
-                                mbi.addMBeanAttributeInfo(mbai);
-                                attributePanel.add(mbi, BorderLayout.CENTER);
-                                mainPanel.add(attributePanel,
-                                        BorderLayout.CENTER);
-                                southPanel.setVisible(false);
-                                southPanel.removeAll();
-                                validate();
-                                repaint();
+                SwingWorker<MBeanAttributeInfo, Void> sw =
+                        new SwingWorker<MBeanAttributeInfo, Void>() {
+                            @Override
+                            public MBeanAttributeInfo doInBackground() {
+                                Object attrData = uo.getData();
+                                mbean = (XMBean) ((Object[]) attrData)[0];
+                                MBeanAttributeInfo mbai =
+                                        (MBeanAttributeInfo) ((Object[]) attrData)[1];
+                                mbeanAttributes.loadAttributes(mbean, new MBeanInfo(
+                                        null, null, new MBeanAttributeInfo[]{mbai},
+                                        null, null, null));
+                                return mbai;
                             }
-                        });
-                    }
-                });
+                            @Override
+                            protected void done() {
+                                try {
+                                    MBeanAttributeInfo mbai = get();
+                                    if (!isSelectedNode(node, currentNode)) {
+                                        return;
+                                    }
+                                    invalidate();
+                                    mainPanel.removeAll();
+                                    JPanel attributePanel =
+                                            new JPanel(new BorderLayout());
+                                    JPanel attributeBorderPanel =
+                                            new JPanel(new BorderLayout());
+                                    attributeBorderPanel.setBorder(
+                                            BorderFactory.createTitledBorder(
+                                            Resources.getText("Attribute value")));
+                                    JPanel attributeValuePanel =
+                                            new JPanel(new BorderLayout());
+                                    attributeValuePanel.setBorder(
+                                            LineBorder.createGrayLineBorder());
+                                    attributeValuePanel.add(mbeanAttributes.getTableHeader(),
+                                            BorderLayout.PAGE_START);
+                                    attributeValuePanel.add(mbeanAttributes,
+                                            BorderLayout.CENTER);
+                                    attributeBorderPanel.add(attributeValuePanel,
+                                            BorderLayout.CENTER);
+                                    JPanel refreshButtonPanel = new JPanel();
+                                    refreshButtonPanel.add(refreshButton);
+                                    attributeBorderPanel.add(refreshButtonPanel,
+                                            BorderLayout.SOUTH);
+                                    refreshButton.setEnabled(true);
+                                    attributePanel.add(attributeBorderPanel,
+                                            BorderLayout.NORTH);
+                                    mbi.addMBeanAttributeInfo(mbai);
+                                    attributePanel.add(mbi, BorderLayout.CENTER);
+                                    mainPanel.add(attributePanel,
+                                            BorderLayout.CENTER);
+                                    southPanel.setVisible(false);
+                                    southPanel.removeAll();
+                                    validate();
+                                    repaint();
+                                } catch (Exception e) {
+                                    Throwable t = Utils.getActualException(e);
+                                    if (JConsole.isDebug()) {
+                                        System.err.println("Problem displaying MBean " +
+                                                "attribute for MBean [" +
+                                                mbean.getObjectName() + "]");
+                                        t.printStackTrace();
+                                    }
+                                    showErrorDialog(t.toString(),
+                                            Resources.getText("Problem displaying MBean"));
+                                }
+                            }
+                        };
+                sw.execute();
                 break;
             case OPERATION:
                 Object operData = uo.getData();
-                XSheet.this.mbean = (XMBean) ((Object[]) operData)[0];
+                mbean = (XMBean) ((Object[]) operData)[0];
                 MBeanOperationInfo mboi =
                         (MBeanOperationInfo) ((Object[]) operData)[1];
-                XMBeanOperations mbo = mbeanOperations;
-                try {
-                    mbo.loadOperations(mbean, new MBeanInfo(null, null, null,
-                            null, new MBeanOperationInfo[] {mboi}, null));
-                } catch (Exception e) {
-                    EventQueue.invokeLater(new ThreadDialog(
-                            XSheet.this,
-                            e.getMessage(),
-                            Resources.getText("Problem displaying MBean"),
-                            JOptionPane.ERROR_MESSAGE));
-                    return;
-                }
+                mbeanOperations.loadOperations(mbean,
+                        new MBeanInfo(null, null, null, null,
+                        new MBeanOperationInfo[]{mboi}, null));
                 invalidate();
                 mainPanel.removeAll();
                 JPanel operationPanel = new JPanel(new BorderLayout());
                 JPanel operationBorderPanel = new JPanel(new BorderLayout());
                 operationBorderPanel.setBorder(BorderFactory.createTitledBorder(
                         Resources.getText("Operation invocation")));
-                operationBorderPanel.add(new JScrollPane(mbo));
+                operationBorderPanel.add(new JScrollPane(mbeanOperations));
                 operationPanel.add(operationBorderPanel, BorderLayout.NORTH);
                 mbi.addMBeanOperationInfo(mboi);
                 operationPanel.add(mbi, BorderLayout.CENTER);
@@ -320,134 +337,134 @@
         }
     }
 
+    // Call on EDT
     private void displayMBeanAttributesNode(final DefaultMutableTreeNode node) {
         final XNodeInfo uo = (XNodeInfo) node.getUserObject();
         if (!uo.getType().equals(Type.ATTRIBUTES)) {
             return;
         }
-        final XMBeanAttributes mba = mbeanAttributes;
-        mbeansTab.workerAdd(new Runnable() {
-            public void run() {
+        mbean = (XMBean) uo.getData();
+        SwingWorker<Void, Void> sw = new SwingWorker<Void, Void>() {
+            @Override
+            public Void doInBackground() throws InstanceNotFoundException,
+                    IntrospectionException, ReflectionException, IOException {
+                mbeanAttributes.loadAttributes(mbean, mbean.getMBeanInfo());
+                return null;
+            }
+            @Override
+            protected void done() {
                 try {
-                    XSheet.this.node = node;
-                    XSheet.this.mbean = (XMBean) uo.getData();
-                    mba.loadAttributes(mbean, mbean.getMBeanInfo());
-                } catch (Throwable ex) {
-                    EventQueue.invokeLater(new ThreadDialog(
-                            XSheet.this,
-                            ex.getMessage(),
-                            Resources.getText("Problem displaying MBean"),
-                            JOptionPane.ERROR_MESSAGE));
-                    return;
+                    get();
+                    if (!isSelectedNode(node, currentNode)) {
+                        return;
+                    }
+                    invalidate();
+                    mainPanel.removeAll();
+                    JPanel borderPanel = new JPanel(new BorderLayout());
+                    borderPanel.setBorder(BorderFactory.createTitledBorder(
+                            Resources.getText("Attribute values")));
+                    borderPanel.add(new JScrollPane(mbeanAttributes));
+                    mainPanel.add(borderPanel, BorderLayout.CENTER);
+                    // add the refresh button to the south panel
+                    southPanel.removeAll();
+                    southPanel.add(refreshButton, BorderLayout.SOUTH);
+                    southPanel.setVisible(true);
+                    refreshButton.setEnabled(true);
+                    validate();
+                    repaint();
+                } catch (Exception e) {
+                    Throwable t = Utils.getActualException(e);
+                    if (JConsole.isDebug()) {
+                        System.err.println("Problem displaying MBean " +
+                                "attributes for MBean [" +
+                                mbean.getObjectName() + "]");
+                        t.printStackTrace();
+                    }
+                    showErrorDialog(t.toString(),
+                            Resources.getText("Problem displaying MBean"));
                 }
-                EventQueue.invokeLater(new Runnable() {
-                    public void run() {
-                        invalidate();
-                        mainPanel.removeAll();
-                        JPanel borderPanel = new JPanel(new BorderLayout());
-                        borderPanel.setBorder(BorderFactory.createTitledBorder(
-                                Resources.getText("Attribute values")));
-                        borderPanel.add(new JScrollPane(mba));
-                        mainPanel.add(borderPanel, BorderLayout.CENTER);
-                        // add the refresh button to the south panel
-                        southPanel.removeAll();
-                        southPanel.add(refreshButton, BorderLayout.SOUTH);
-                        southPanel.setVisible(true);
-                        refreshButton.setEnabled(true);
-                        validate();
-                        repaint();
-                    }
-                });
             }
-        });
+        };
+        sw.execute();
     }
 
+    // Call on EDT
     private void displayMBeanOperationsNode(final DefaultMutableTreeNode node) {
         final XNodeInfo uo = (XNodeInfo) node.getUserObject();
         if (!uo.getType().equals(Type.OPERATIONS)) {
             return;
         }
-        final XMBeanOperations mbo = mbeanOperations;
-        mbeansTab.workerAdd(new Runnable() {
-            public void run() {
+        mbean = (XMBean) uo.getData();
+        SwingWorker<MBeanInfo, Void> sw = new SwingWorker<MBeanInfo, Void>() {
+            @Override
+            public MBeanInfo doInBackground() throws InstanceNotFoundException,
+                    IntrospectionException, ReflectionException, IOException {
+                return mbean.getMBeanInfo();
+            }
+            @Override
+            protected void done() {
                 try {
-                    XSheet.this.node = node;
-                    XSheet.this.mbean = (XMBean) uo.getData();
-                    mbo.loadOperations(mbean, mbean.getMBeanInfo());
-                } catch (Throwable ex) {
-                    EventQueue.invokeLater(new ThreadDialog(
-                            XSheet.this,
-                            ex.getMessage(),
-                            Resources.getText("Problem displaying MBean"),
-                            JOptionPane.ERROR_MESSAGE));
-                    return;
-                }
-                EventQueue.invokeLater(new Runnable() {
-                    public void run() {
+                    MBeanInfo mbi = get();
+                    if (mbi != null) {
+                        if (!isSelectedNode(node, currentNode)) {
+                            return;
+                        }
+                        mbeanOperations.loadOperations(mbean, mbi);
                         invalidate();
                         mainPanel.removeAll();
                         JPanel borderPanel = new JPanel(new BorderLayout());
                         borderPanel.setBorder(BorderFactory.createTitledBorder(
                                 Resources.getText("Operation invocation")));
-                        borderPanel.add(new JScrollPane(mbo));
+                        borderPanel.add(new JScrollPane(mbeanOperations));
                         mainPanel.add(borderPanel, BorderLayout.CENTER);
                         southPanel.setVisible(false);
                         southPanel.removeAll();
                         validate();
                         repaint();
                     }
-                });
+                } catch (Exception e) {
+                    Throwable t = Utils.getActualException(e);
+                    if (JConsole.isDebug()) {
+                        System.err.println("Problem displaying MBean " +
+                                "operations for MBean [" +
+                                mbean.getObjectName() + "]");
+                        t.printStackTrace();
+                    }
+                    showErrorDialog(t.toString(),
+                            Resources.getText("Problem displaying MBean"));
+                }
             }
-        });
+        };
+        sw.execute();
     }
 
-    private void displayMBeanNotificationsNode(
-            final DefaultMutableTreeNode node) {
+    // Call on EDT
+    private void displayMBeanNotificationsNode(DefaultMutableTreeNode node) {
         final XNodeInfo uo = (XNodeInfo) node.getUserObject();
         if (!uo.getType().equals(Type.NOTIFICATIONS)) {
             return;
         }
-        final XMBeanNotifications mbn = mbeanNotifications;
-        mbeansTab.workerAdd(new Runnable() {
-            public void run() {
-                try {
-                    XSheet.this.node = node;
-                    XSheet.this.mbean = (XMBean) uo.getData();
-                    mbn.loadNotifications(mbean);
-                    updateNotifications();
-                } catch (Throwable ex) {
-                    EventQueue.invokeLater(new ThreadDialog(
-                            XSheet.this,
-                            ex.getMessage(),
-                            Resources.getText("Problem displaying MBean"),
-                            JOptionPane.ERROR_MESSAGE));
-                    return;
-                }
-                EventQueue.invokeLater(new Runnable() {
-                    public void run() {
-                        invalidate();
-                        mainPanel.removeAll();
-                        JPanel borderPanel = new JPanel(new BorderLayout());
-                        borderPanel.setBorder(BorderFactory.createTitledBorder(
-                                Resources.getText("Notification buffer")));
-                        borderPanel.add(new JScrollPane(mbn));
-                        mainPanel.add(borderPanel, BorderLayout.CENTER);
-                        // add the subscribe/unsubscribe/clear buttons to
-                        // the south panel
-                        southPanel.removeAll();
-                        southPanel.add(subscribeButton, BorderLayout.WEST);
-                        southPanel.add(unsubscribeButton, BorderLayout.CENTER);
-                        southPanel.add(clearButton, BorderLayout.EAST);
-                        southPanel.setVisible(true);
-                        subscribeButton.setEnabled(true);
-                        unsubscribeButton.setEnabled(true);
-                        clearButton.setEnabled(true);
-                        validate();
-                        repaint();
-                    }
-                });
-            }
-        });
+        mbean = (XMBean) uo.getData();
+        mbeanNotifications.loadNotifications(mbean);
+        updateNotifications();
+        invalidate();
+        mainPanel.removeAll();
+        JPanel borderPanel = new JPanel(new BorderLayout());
+        borderPanel.setBorder(BorderFactory.createTitledBorder(
+                Resources.getText("Notification buffer")));
+        borderPanel.add(new JScrollPane(mbeanNotifications));
+        mainPanel.add(borderPanel, BorderLayout.CENTER);
+        // add the subscribe/unsubscribe/clear buttons to the south panel
+        southPanel.removeAll();
+        southPanel.add(subscribeButton, BorderLayout.WEST);
+        southPanel.add(unsubscribeButton, BorderLayout.CENTER);
+        southPanel.add(clearButton, BorderLayout.EAST);
+        southPanel.setVisible(true);
+        subscribeButton.setEnabled(true);
+        unsubscribeButton.setEnabled(true);
+        clearButton.setEnabled(true);
+        validate();
+        repaint();
     }
 
     // Call on EDT
@@ -462,21 +479,60 @@
     /**
      * Subscribe button action.
      */
-    private void registerListener() throws InstanceNotFoundException,
-            IOException {
-        mbeanNotifications.registerListener(node);
-        updateNotifications();
-        validate();
+    private void registerListener() {
+        new SwingWorker<Void, Void>() {
+            @Override
+            public Void doInBackground()
+                    throws InstanceNotFoundException, IOException {
+                mbeanNotifications.registerListener(currentNode);
+                return null;
+            }
+            @Override
+            protected void done() {
+                try {
+                    get();
+                    updateNotifications();
+                    validate();
+                } catch (Exception e) {
+                    Throwable t = Utils.getActualException(e);
+                    if (JConsole.isDebug()) {
+                        System.err.println("Problem adding listener");
+                        t.printStackTrace();
+                    }
+                    showErrorDialog(t.getMessage(),
+                            Resources.getText("Problem adding listener"));
+                }
+            }
+        }.execute();
     }
 
     /**
      * Unsubscribe button action.
      */
     private void unregisterListener() {
-        if (mbeanNotifications.unregisterListener(node)) {
-            clearNotifications();
-            validate();
-        }
+        new SwingWorker<Boolean, Void>() {
+            @Override
+            public Boolean doInBackground() {
+                return mbeanNotifications.unregisterListener(currentNode);
+            }
+            @Override
+            protected void done() {
+                try {
+                    if (get()) {
+                        updateNotifications();
+                        validate();
+                    }
+                } catch (Exception e) {
+                    Throwable t = Utils.getActualException(e);
+                    if (JConsole.isDebug()) {
+                        System.err.println("Problem removing listener");
+                        t.printStackTrace();
+                    }
+                    showErrorDialog(t.getMessage(),
+                            Resources.getText("Problem removing listener"));
+                }
+            }
+        }.execute();
     }
 
     /**
@@ -486,15 +542,11 @@
         mbeanAttributes.refreshAttributes();
     }
 
+    // Call on EDT
     private void updateNotifications() {
-        if (mbean.isBroadcaster()) {
-            if (mbeanNotifications.isListenerRegistered(mbean)) {
-                long received =
-                        mbeanNotifications.getReceivedNotifications(mbean);
-                updateReceivedNotifications(node, received, false);
-            } else {
-                clearNotifications();
-            }
+        if (mbeanNotifications.isListenerRegistered(mbean)) {
+            long received = mbeanNotifications.getReceivedNotifications(mbean);
+            updateReceivedNotifications(currentNode, received, false);
         } else {
             clearNotifications();
         }
@@ -503,11 +555,11 @@
     /**
      * Update notification node label in MBean tree: "Notifications[received]".
      */
+    // Call on EDT
     private void updateReceivedNotifications(
             DefaultMutableTreeNode emitter, long received, boolean bold) {
         String text = Resources.getText("Notifications") + "[" + received + "]";
-        DefaultMutableTreeNode selectedNode = (DefaultMutableTreeNode)
-        mbeansTab.getTree().getLastSelectedPathComponent();
+        DefaultMutableTreeNode selectedNode = (DefaultMutableTreeNode) mbeansTab.getTree().getLastSelectedPathComponent();
         if (bold && emitter != selectedNode) {
             text = "<html><b>" + text + "</b></html>";
         }
@@ -517,41 +569,40 @@
     /**
      * Update notification node label in MBean tree: "Notifications".
      */
+    // Call on EDT
     private void clearNotifications() {
-        updateNotificationsNodeLabel(node,
+        updateNotificationsNodeLabel(currentNode,
                 Resources.getText("Notifications"));
     }
 
     /**
      * Update notification node label in MBean tree: "Notifications[0]".
      */
+    // Call on EDT
     private void clearNotifications0() {
-        updateNotificationsNodeLabel(node,
+        updateNotificationsNodeLabel(currentNode,
                 Resources.getText("Notifications") + "[0]");
     }
 
     /**
      * Update the label of the supplied MBean tree node.
      */
+    // Call on EDT
     private void updateNotificationsNodeLabel(
-            final DefaultMutableTreeNode node, final String label) {
-        EventQueue.invokeLater(new Runnable() {
-            public void run() {
-                synchronized (mbeansTab.getTree()) {
-                    invalidate();
-                    XNodeInfo oldUserObject = (XNodeInfo) node.getUserObject();
-                    XNodeInfo newUserObject = new XNodeInfo(
-                            oldUserObject.getType(), oldUserObject.getData(),
-                            label, oldUserObject.getToolTipText());
-                    node.setUserObject(newUserObject);
-                    DefaultTreeModel model =
-                            (DefaultTreeModel) mbeansTab.getTree().getModel();
-                    model.nodeChanged(node);
-                    validate();
-                    repaint();
-                }
-            }
-        });
+            DefaultMutableTreeNode node, String label) {
+        synchronized (mbeansTab.getTree()) {
+            invalidate();
+            XNodeInfo oldUserObject = (XNodeInfo) node.getUserObject();
+            XNodeInfo newUserObject = new XNodeInfo(
+                    oldUserObject.getType(), oldUserObject.getData(),
+                    label, oldUserObject.getToolTipText());
+            node.setUserObject(newUserObject);
+            DefaultTreeModel model =
+                    (DefaultTreeModel) mbeansTab.getTree().getModel();
+            model.nodeChanged(node);
+            validate();
+            repaint();
+        }
     }
 
     /**
@@ -577,6 +628,7 @@
         }
     }
 
+    // Call on EDT
     private void clear() {
         mbeanAttributes.stopCellEditing();
         mbeanAttributes.emptyTable();
@@ -586,13 +638,14 @@
         mbeanNotifications.emptyTable();
         mbeanNotifications.disableNotifications();
         mbean = null;
-        node = null;
+        currentNode = null;
     }
 
     /**
      * Notification listener: handles asynchronous reception
      * of MBean operation results and MBean notifications.
      */
+    // Call on EDT
     public void handleNotification(Notification e, Object handback) {
         // Operation result
         if (e.getType().equals(XOperations.OPERATION_INVOCATION_EVENT)) {
@@ -628,13 +681,12 @@
                     message = comp;
                 }
             }
-            EventQueue.invokeLater(new ThreadDialog(
+            new ThreadDialog(
                     (Component) e.getSource(),
                     message,
                     Resources.getText("Operation return value"),
-                    JOptionPane.INFORMATION_MESSAGE));
-        }
-        // Got notification
+                    JOptionPane.INFORMATION_MESSAGE).run();
+        } // Got notification
         else if (e.getType().equals(
                 XMBeanNotifications.NOTIFICATION_RECEIVED_EVENT)) {
             DefaultMutableTreeNode emitter = (DefaultMutableTreeNode) handback;
@@ -646,16 +698,19 @@
     /**
      * Action listener: handles actions in panel buttons
      */
+    // Call on EDT
     public void actionPerformed(ActionEvent e) {
         if (e.getSource() instanceof JButton) {
             JButton button = (JButton) e.getSource();
             // Refresh button
             if (button == refreshButton) {
-                mbeansTab.workerAdd(new Runnable() {
-                    public void run() {
+                new SwingWorker<Void, Void>() {
+                    @Override
+                    public Void doInBackground() {
                         refreshAttributes();
+                        return null;
                     }
-                });
+                }.execute();
                 return;
             }
             // Clear button
@@ -665,38 +720,12 @@
             }
             // Subscribe button
             if (button == subscribeButton) {
-                mbeansTab.workerAdd(new Runnable() {
-                    public void run() {
-                        try {
-                            registerListener();
-                        } catch (Throwable ex) {
-                            ex = Utils.getActualException(ex);
-                            EventQueue.invokeLater(new ThreadDialog(
-                                    XSheet.this,
-                                    ex.getMessage(),
-                                    Resources.getText("Problem adding listener"),
-                                    JOptionPane.ERROR_MESSAGE));
-                        }
-                    }
-                });
+                registerListener();
                 return;
             }
             // Unsubscribe button
             if (button == unsubscribeButton) {
-                mbeansTab.workerAdd(new Runnable() {
-                    public void run() {
-                        try {
-                            unregisterListener();
-                        } catch (Throwable ex) {
-                            ex = Utils.getActualException(ex);
-                            EventQueue.invokeLater(new ThreadDialog(
-                                    XSheet.this,
-                                    ex.getMessage(),
-                                    Resources.getText("Problem removing listener"),
-                                    JOptionPane.ERROR_MESSAGE));
-                        }
-                    }
-                });
+                unregisterListener();
                 return;
             }
         }
--- a/src/share/classes/sun/tools/jconsole/inspector/XTable.java	Mon Mar 10 23:51:13 2008 +0100
+++ b/src/share/classes/sun/tools/jconsole/inspector/XTable.java	Tue Mar 11 01:20:55 2008 +0100
@@ -22,6 +22,7 @@
  * CA 95054 USA or visit www.sun.com if you need additional information or
  * have any questions.
  */
+
 package sun.tools.jconsole.inspector;
 
 import javax.swing.*;
--- a/src/share/classes/sun/tools/jconsole/inspector/XTextField.java	Mon Mar 10 23:51:13 2008 +0100
+++ b/src/share/classes/sun/tools/jconsole/inspector/XTextField.java	Tue Mar 11 01:20:55 2008 +0100
@@ -22,6 +22,7 @@
  * CA 95054 USA or visit www.sun.com if you need additional information or
  * have any questions.
  */
+
 package sun.tools.jconsole.inspector;
 
 import java.awt.*;
--- a/src/share/classes/sun/tools/jconsole/inspector/XTextFieldEditor.java	Mon Mar 10 23:51:13 2008 +0100
+++ b/src/share/classes/sun/tools/jconsole/inspector/XTextFieldEditor.java	Tue Mar 11 01:20:55 2008 +0100
@@ -22,6 +22,7 @@
  * CA 95054 USA or visit www.sun.com if you need additional information or
  * have any questions.
  */
+
 package sun.tools.jconsole.inspector;
 
 import java.awt.Component;
--- a/src/share/classes/sun/tools/jconsole/inspector/XTree.java	Mon Mar 10 23:51:13 2008 +0100
+++ b/src/share/classes/sun/tools/jconsole/inspector/XTree.java	Tue Mar 11 01:20:55 2008 +0100
@@ -1,5 +1,5 @@
 /*
- * Copyright 2004-2006 Sun Microsystems, Inc.  All Rights Reserved.
+ * Copyright 2004-2007 Sun Microsystems, Inc.  All Rights Reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -25,7 +25,7 @@
 
 package sun.tools.jconsole.inspector;
 
-import java.awt.EventQueue;
+import java.io.IOException;
 import java.util.*;
 import javax.management.*;
 import javax.swing.*;
@@ -34,13 +34,14 @@
 import sun.tools.jconsole.MBeansTab;
 import sun.tools.jconsole.Resources;
 import sun.tools.jconsole.inspector.XNodeInfo;
-import sun.tools.jconsole.inspector.XNodeInfo.Type;
+import static sun.tools.jconsole.inspector.XNodeInfo.Type;
 
 @SuppressWarnings("serial")
 public class XTree extends JTree {
 
     private static final List<String> orderedKeyPropertyList =
             new ArrayList<String>();
+
     static {
         String keyPropertyList =
                 System.getProperty("com.sun.tools.jconsole.mbeans.keyPropertyList");
@@ -54,9 +55,7 @@
             }
         }
     }
-
     private MBeansTab mbeansTab;
-
     private Map<String, DefaultMutableTreeNode> nodes =
             new HashMap<String, DefaultMutableTreeNode>();
 
@@ -65,7 +64,7 @@
     }
 
     public XTree(TreeNode root, MBeansTab mbeansTab) {
-        super(root);
+        super(root, true);
         this.mbeansTab = mbeansTab;
         setRootVisible(false);
         setShowsRootHandles(true);
@@ -90,15 +89,8 @@
             DefaultMutableTreeNode parent,
             DefaultMutableTreeNode child,
             int index) {
-        // Tree does not show up when there is only the root node
-        //
         DefaultTreeModel model = (DefaultTreeModel) getModel();
-        DefaultMutableTreeNode root = (DefaultMutableTreeNode) model.getRoot();
-        boolean rootLeaf = root.isLeaf();
         model.insertNodeInto(child, parent, index);
-        if (rootLeaf) {
-            model.nodeStructureChanged(root);
-        }
     }
 
     /**
@@ -114,32 +106,29 @@
         int childCount = parent.getChildCount();
         if (childCount == 0) {
             addChildNode(parent, child, 0);
-        } else if (child instanceof ComparableDefaultMutableTreeNode) {
+            return;
+        }
+        if (child instanceof ComparableDefaultMutableTreeNode) {
             ComparableDefaultMutableTreeNode comparableChild =
-                (ComparableDefaultMutableTreeNode)child;
-            int i = 0;
-            for (; i < childCount; i++) {
+                    (ComparableDefaultMutableTreeNode) child;
+            for (int i = childCount - 1; i >= 0; i--) {
                 DefaultMutableTreeNode brother =
                         (DefaultMutableTreeNode) parent.getChildAt(i);
-                //child < brother
-                if (comparableChild.compareTo(brother) < 0) {
-                    addChildNode(parent, child, i);
-                    break;
-                }
-                //child = brother
-                else if (comparableChild.compareTo(brother) == 0) {
-                    addChildNode(parent, child, i);
-                    break;
+                // expr1: child node must be inserted after metadata nodes
+                // - OR -
+                // expr2: "child >= brother"
+                if ((i <= 2 && isMetadataNode(brother)) ||
+                        comparableChild.compareTo(brother) >= 0) {
+                    addChildNode(parent, child, i + 1);
+                    return;
                 }
             }
-            //child < all brothers
-            if (i == childCount) {
-                addChildNode(parent, child, childCount);
-            }
-        } else {
-            //not comparable, add at the end
-            addChildNode(parent, child, childCount);
+            // "child < all brothers", add at the beginning
+            addChildNode(parent, child, 0);
+            return;
         }
+        // "child not comparable", add at the end
+        addChildNode(parent, child, childCount);
     }
 
     /**
@@ -147,6 +136,7 @@
      * but does not affect actual MBeanServer contents.
      */
     // Call on EDT
+    @Override
     public synchronized void removeAll() {
         DefaultTreeModel model = (DefaultTreeModel) getModel();
         DefaultMutableTreeNode root = (DefaultMutableTreeNode) model.getRoot();
@@ -155,51 +145,56 @@
         nodes.clear();
     }
 
-    public void delMBeanFromView(final ObjectName mbean) {
-        EventQueue.invokeLater(new Runnable() {
-            public void run() {
-                // We assume here that MBeans are removed one by one (on MBean
-                // unregistered notification). Deletes the tree node associated
-                // with the given MBean and recursively all the node parents
-                // which are leaves and non XMBean.
-                //
-                synchronized (XTree.this) {
-                    DefaultMutableTreeNode node = null;
-                    Dn dn = buildDn(mbean);
-                    if (dn.size() > 0) {
-                        DefaultTreeModel model = (DefaultTreeModel) getModel();
-                        Token token = dn.getToken(0);
-                        String hashKey = dn.getHashKey(token);
-                        node = nodes.get(hashKey);
-                        if ((node != null) && (!node.isRoot())) {
-                            if (hasMBeanChildren(node)) {
-                                removeNonMBeanChildren(node);
-                                String label = token.getValue().toString();
-                                XNodeInfo userObject = new XNodeInfo(
-                                        Type.NONMBEAN, label,
-                                        label, token.toString());
-                                changeNodeValue(node, userObject);
-                            } else {
-                                DefaultMutableTreeNode parent =
-                                        (DefaultMutableTreeNode) node.getParent();
-                                model.removeNodeFromParent(node);
-                                nodes.remove(hashKey);
-                                delParentFromView(dn, 1, parent);
-                            }
-                        }
-                    }
+    // Call on EDT
+    public synchronized void removeMBeanFromView(ObjectName mbean) {
+        // We assume here that MBeans are removed one by one (on MBean
+        // unregistered notification). Deletes the tree node associated
+        // with the given MBean and recursively all the node parents
+        // which are leaves and non XMBean.
+        //
+        DefaultMutableTreeNode node = null;
+        Dn dn = new Dn(mbean);
+        if (dn.getTokenCount() > 0) {
+            DefaultTreeModel model = (DefaultTreeModel) getModel();
+            Token token = dn.getToken(0);
+            String hashKey = dn.getHashKey(token);
+            node = nodes.get(hashKey);
+            if ((node != null) && (!node.isRoot())) {
+                if (hasNonMetadataNodes(node)) {
+                    removeMetadataNodes(node);
+                    String label = token.getValue();
+                    XNodeInfo userObject = new XNodeInfo(
+                            Type.NONMBEAN, label,
+                            label, token.getTokenValue());
+                    changeNodeValue(node, userObject);
+                } else {
+                    DefaultMutableTreeNode parent =
+                            (DefaultMutableTreeNode) node.getParent();
+                    model.removeNodeFromParent(node);
+                    nodes.remove(hashKey);
+                    removeParentFromView(dn, 1, parent);
                 }
             }
-        });
+        }
     }
 
     /**
-     * Returns true if any of the children nodes is an MBean.
+     * Returns true if any of the children nodes is a non MBean metadata node.
      */
-    private boolean hasMBeanChildren(DefaultMutableTreeNode node) {
-        for (Enumeration e = node.children(); e.hasMoreElements(); ) {
+    private boolean hasNonMetadataNodes(DefaultMutableTreeNode node) {
+        for (Enumeration e = node.children(); e.hasMoreElements();) {
             DefaultMutableTreeNode n = (DefaultMutableTreeNode) e.nextElement();
-            if (((XNodeInfo) n.getUserObject()).getType().equals(Type.MBEAN)) {
+            Object uo = n.getUserObject();
+            if (uo instanceof XNodeInfo) {
+                switch (((XNodeInfo) uo).getType()) {
+                    case ATTRIBUTES:
+                    case NOTIFICATIONS:
+                    case OPERATIONS:
+                        break;
+                    default:
+                        return true;
+                }
+            } else {
                 return true;
             }
         }
@@ -207,16 +202,68 @@
     }
 
     /**
-     * Remove all the children nodes which are not MBean.
+     * Returns true if any of the children nodes is an MBean metadata node.
      */
-    private void removeNonMBeanChildren(DefaultMutableTreeNode node) {
+    public boolean hasMetadataNodes(DefaultMutableTreeNode node) {
+        for (Enumeration e = node.children(); e.hasMoreElements();) {
+            DefaultMutableTreeNode n = (DefaultMutableTreeNode) e.nextElement();
+            Object uo = n.getUserObject();
+            if (uo instanceof XNodeInfo) {
+                switch (((XNodeInfo) uo).getType()) {
+                    case ATTRIBUTES:
+                    case NOTIFICATIONS:
+                    case OPERATIONS:
+                        return true;
+                    default:
+                        break;
+                }
+            } else {
+                return false;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Returns true if the given node is an MBean metadata node.
+     */
+    public boolean isMetadataNode(DefaultMutableTreeNode node) {
+        Object uo = node.getUserObject();
+        if (uo instanceof XNodeInfo) {
+            switch (((XNodeInfo) uo).getType()) {
+                case ATTRIBUTES:
+                case NOTIFICATIONS:
+                case OPERATIONS:
+                    return true;
+                default:
+                    return false;
+            }
+        } else {
+            return false;
+        }
+    }
+
+    /**
+     * Remove the metadata nodes associated with a given MBean node.
+     */
+    // Call on EDT
+    private void removeMetadataNodes(DefaultMutableTreeNode node) {
         Set<DefaultMutableTreeNode> metadataNodes =
                 new HashSet<DefaultMutableTreeNode>();
         DefaultTreeModel model = (DefaultTreeModel) getModel();
-        for (Enumeration e = node.children(); e.hasMoreElements(); ) {
+        for (Enumeration e = node.children(); e.hasMoreElements();) {
             DefaultMutableTreeNode n = (DefaultMutableTreeNode) e.nextElement();
-            if (!((XNodeInfo) n.getUserObject()).getType().equals(Type.MBEAN)) {
-                metadataNodes.add(n);
+            Object uo = n.getUserObject();
+            if (uo instanceof XNodeInfo) {
+                switch (((XNodeInfo) uo).getType()) {
+                    case ATTRIBUTES:
+                    case NOTIFICATIONS:
+                    case OPERATIONS:
+                        metadataNodes.add(n);
+                        break;
+                    default:
+                        break;
+                }
             }
         }
         for (DefaultMutableTreeNode n : metadataNodes) {
@@ -228,7 +275,8 @@
      * Removes only the parent nodes which are non MBean and leaf.
      * This method assumes the child nodes have been removed before.
      */
-    private DefaultMutableTreeNode delParentFromView(
+    // Call on EDT
+    private DefaultMutableTreeNode removeParentFromView(
             Dn dn, int index, DefaultMutableTreeNode node) {
         if ((!node.isRoot()) && node.isLeaf() &&
                 (!(((XNodeInfo) node.getUserObject()).getType().equals(Type.MBEAN)))) {
@@ -237,115 +285,100 @@
             removeChildNode(node);
             String hashKey = dn.getHashKey(dn.getToken(index));
             nodes.remove(hashKey);
-            delParentFromView(dn, index + 1, parent);
+            removeParentFromView(dn, index + 1, parent);
         }
         return node;
     }
 
-    public synchronized void addMBeanToView(final ObjectName mbean) {
-        final XMBean xmbean;
-        try {
-            xmbean = new XMBean(mbean, mbeansTab);
-            if (xmbean == null) {
-                return;
-            }
-        } catch (Exception e) {
-            // Got exception while trying to retrieve the
-            // given MBean from the underlying MBeanServer
+    // Call on EDT
+    public synchronized void addMBeansToView(Set<ObjectName> mbeans) {
+        Set<Dn> dns = new TreeSet<Dn>();
+        for (ObjectName mbean : mbeans) {
+            Dn dn = new Dn(mbean);
+            dns.add(dn);
+        }
+        for (Dn dn : dns) {
+            ObjectName mbean = dn.getObjectName();
+            XMBean xmbean = new XMBean(mbean, mbeansTab);
+            addMBeanToView(mbean, xmbean, dn);
+        }
+    }
+
+    // Call on EDT
+    public synchronized void addMBeanToView(ObjectName mbean) {
+        // Build XMBean for the given MBean
+        //
+        XMBean xmbean = new XMBean(mbean, mbeansTab);
+        // Build Dn for the given MBean
+        //
+        Dn dn = new Dn(mbean);
+        // Add the new nodes to the MBean tree from leaf to root
+        //
+        addMBeanToView(mbean, xmbean, dn);
+    }
+
+    // Call on EDT
+    private synchronized void addMBeanToView(
+            ObjectName mbean, XMBean xmbean, Dn dn) {
+
+        DefaultMutableTreeNode childNode = null;
+        DefaultMutableTreeNode parentNode = null;
+
+        // Add the node or replace its user object if already added
+        //
+        Token token = dn.getToken(0);
+        String hashKey = dn.getHashKey(token);
+        if (nodes.containsKey(hashKey)) {
+            // Found existing node previously created when adding another node
             //
-            if (JConsole.isDebug()) {
-                e.printStackTrace();
-            }
+            childNode = nodes.get(hashKey);
+            // Replace user object to reflect that this node is an MBean
+            //
+            Object data = createNodeValue(xmbean, token);
+            String label = data.toString();
+            XNodeInfo userObject =
+                    new XNodeInfo(Type.MBEAN, data, label, mbean.toString());
+            changeNodeValue(childNode, userObject);
             return;
         }
-        EventQueue.invokeLater(new Runnable() {
-            public void run() {
-                synchronized (XTree.this) {
-                    // Add the new nodes to the MBean tree from leaf to root
 
-                    Dn dn = buildDn(mbean);
-                    if (dn.size() == 0) return;
-                    Token token = dn.getToken(0);
-                    DefaultMutableTreeNode node = null;
-                    boolean nodeCreated = true;
+        // Create new leaf node
+        //
+        childNode = createDnNode(dn, token, xmbean);
+        nodes.put(hashKey, childNode);
 
-                    //
-                    // Add the node or replace its user object if already added
-                    //
-
-                    String hashKey = dn.getHashKey(token);
-                    if (nodes.containsKey(hashKey)) {
-                        //already in the tree, means it has been created previously
-                        //when adding another node
-                        node = nodes.get(hashKey);
-                        //sets the user object
-                        final Object data = createNodeValue(xmbean, token);
-                        final String label = data.toString();
-                        final XNodeInfo userObject =
-                                new XNodeInfo(Type.MBEAN, data, label, mbean.toString());
-                        changeNodeValue(node, userObject);
-                        nodeCreated = false;
-                    } else {
-                        //create a new node
-                        node = createDnNode(dn, token, xmbean);
-                        if (node != null) {
-                            nodes.put(hashKey, node);
-                            nodeCreated = true;
-                        } else {
-                            return;
-                        }
-                    }
-
-                    //
-                    // Add (virtual) nodes without user object if necessary
-                    //
-
-                    for (int i = 1; i < dn.size(); i++) {
-                        DefaultMutableTreeNode currentNode = null;
-                        token = dn.getToken(i);
-                        hashKey = dn.getHashKey(token);
-                        if (nodes.containsKey(hashKey)) {
-                            //node already present
-                            if (nodeCreated) {
-                                //previous node created, link to do
-                                currentNode = nodes.get(hashKey);
-                                addChildNode(currentNode, node);
-                                return;
-                            } else {
-                                //both nodes already present
-                                return;
-                            }
-                        } else {
-                            //creates the node that can be a virtual one
-                            if (token.getKeyDn().equals("domain")) {
-                                //better match on keyDn that on Dn
-                                currentNode = createDomainNode(dn, token);
-                                if (currentNode != null) {
-                                    final DefaultMutableTreeNode root =
-                                            (DefaultMutableTreeNode) getModel().getRoot();
-                                    addChildNode(root, currentNode);
-                                }
-                            } else {
-                                currentNode = createSubDnNode(dn, token);
-                                if (currentNode == null) {
-                                    //skip
-                                    continue;
-                                }
-                            }
-                            nodes.put(hashKey, currentNode);
-                            addChildNode(currentNode, node);
-                            nodeCreated = true;
-                        }
-                        node = currentNode;
-                    }
+        // Add intermediate non MBean nodes
+        //
+        for (int i = 1; i < dn.getTokenCount(); i++) {
+            token = dn.getToken(i);
+            hashKey = dn.getHashKey(token);
+            if (nodes.containsKey(hashKey)) {
+                // Intermediate node already present, add new node as child
+                //
+                parentNode = nodes.get(hashKey);
+                addChildNode(parentNode, childNode);
+                return;
+            } else {
+                // Create new intermediate node
+                //
+                if ("domain".equals(token.getTokenType())) {
+                    parentNode = createDomainNode(dn, token);
+                    DefaultMutableTreeNode root =
+                            (DefaultMutableTreeNode) getModel().getRoot();
+                    addChildNode(root, parentNode);
+                } else {
+                    parentNode = createSubDnNode(dn, token);
                 }
+                nodes.put(hashKey, parentNode);
+                addChildNode(parentNode, childNode);
             }
-        });
+            childNode = parentNode;
+        }
     }
 
     // Call on EDT
     private synchronized void changeNodeValue(
-            final DefaultMutableTreeNode node, XNodeInfo nodeValue) {
+            DefaultMutableTreeNode node, XNodeInfo nodeValue) {
         if (node instanceof ComparableDefaultMutableTreeNode) {
             // should it stay at the same place?
             DefaultMutableTreeNode clone =
@@ -373,9 +406,12 @@
         }
         // Load the MBean metadata if type is MBEAN
         if (nodeValue.getType().equals(Type.MBEAN)) {
-            XMBeanInfo.loadInfo(node);
-            DefaultTreeModel model = (DefaultTreeModel) getModel();
-            model.nodeStructureChanged(node);
+            removeMetadataNodes(node);
+            TreeNode[] treeNodes = node.getPath();
+            TreePath path = new TreePath(treeNodes);
+            if (isExpanded(path)) {
+                addMetadataNodes(node);
+            }
         }
         // Clear the current selection and set it
         // again so valueChanged() gets called
@@ -386,7 +422,9 @@
         }
     }
 
-    //creates the domain node, called on a domain token
+    /**
+     * Creates the domain node.
+     */
     private DefaultMutableTreeNode createDomainNode(Dn dn, Token token) {
         DefaultMutableTreeNode node = new ComparableDefaultMutableTreeNode();
         String label = dn.getDomain();
@@ -396,7 +434,9 @@
         return node;
     }
 
-    //creates the node corresponding to the whole Dn
+    /**
+     * Creates the node corresponding to the whole Dn, i.e. an MBean.
+     */
     private DefaultMutableTreeNode createDnNode(
             Dn dn, Token token, XMBean xmbean) {
         DefaultMutableTreeNode node = new ComparableDefaultMutableTreeNode();
@@ -405,38 +445,36 @@
         XNodeInfo userObject = new XNodeInfo(Type.MBEAN, data, label,
                 xmbean.getObjectName().toString());
         node.setUserObject(userObject);
-        XMBeanInfo.loadInfo(node);
         return node;
     }
 
-    //creates a node with the token value, call for each non domain sub
-    //dn token
+    /**
+     * Creates the node corresponding to a subDn, i.e. a non-MBean
+     * intermediate node.
+     */
     private DefaultMutableTreeNode createSubDnNode(Dn dn, Token token) {
         DefaultMutableTreeNode node = new ComparableDefaultMutableTreeNode();
-        String label = isKeyValueView() ? token.toString() :
-            token.getValue().toString();
+        String label = isKeyValueView() ? token.getTokenValue() : token.getValue();
         XNodeInfo userObject =
-                new XNodeInfo(Type.NONMBEAN, label, label, token.toString());
+                new XNodeInfo(Type.NONMBEAN, label, label, token.getTokenValue());
         node.setUserObject(userObject);
         return node;
     }
 
     private Object createNodeValue(XMBean xmbean, Token token) {
-        String label = isKeyValueView() ? token.toString() :
-            token.getValue().toString();
+        String label = isKeyValueView() ? token.getTokenValue() : token.getValue();
         xmbean.setText(label);
         return xmbean;
     }
 
     /**
-     * Parses MBean ObjectName comma-separated properties string and put the
-     * individual key/value pairs into the map. Key order in the properties
+     * Parses the MBean ObjectName comma-separated properties string and puts
+     * the individual key/value pairs into the map. Key order in the properties
      * string is preserved by the map.
      */
-    private Map<String,String> extractKeyValuePairs(
-            String properties, ObjectName mbean) {
-        String props = properties;
-        Map<String,String> map = new LinkedHashMap<String,String>();
+    private static Map<String, String> extractKeyValuePairs(
+            String props, ObjectName mbean) {
+        Map<String, String> map = new LinkedHashMap<String, String>();
         int eq = props.indexOf("=");
         while (eq != -1) {
             String key = props.substring(0, eq);
@@ -461,9 +499,9 @@
      * in the comma-separated key property list does not apply to the given
      * MBean then it will be discarded.
      */
-    private String getKeyPropertyListString(ObjectName mbean) {
+    private static String getKeyPropertyListString(ObjectName mbean) {
         String props = mbean.getKeyPropertyListString();
-        Map<String,String> map = extractKeyValuePairs(props, mbean);
+        Map<String, String> map = extractKeyValuePairs(props, mbean);
         StringBuilder sb = new StringBuilder();
         // Add the key/value pairs to the buffer following the
         // key order defined by the "orderedKeyPropertyList"
@@ -474,7 +512,7 @@
             }
         }
         // Add the remaining key/value pairs to the buffer
-        for (Map.Entry<String,String> entry : map.entrySet()) {
+        for (Map.Entry<String, String> entry : map.entrySet()) {
             sb.append(entry.getKey() + "=" + entry.getValue() + ",");
         }
         String orderedKeyPropertyListString = sb.toString();
@@ -483,67 +521,158 @@
         return orderedKeyPropertyListString;
     }
 
-    /**
-     * Builds the Dn for the given MBean.
-     */
-    private Dn buildDn(ObjectName mbean) {
-
-        String domain = mbean.getDomain();
-        String globalDn = getKeyPropertyListString(mbean);
-
-        Dn dn = buildDn(domain, globalDn, mbean);
-
-        //update the Dn tokens to add the domain
-        dn.updateDn();
-
-        //reverse the Dn (from leaf to root)
-        dn.reverseOrder();
-
-        //compute the hashDn
-        dn.computeHashDn();
-
-        return dn;
-    }
-
-    /**
-     * Builds the Dn for the given MBean.
-     */
-    private Dn buildDn(String domain, String globalDn, ObjectName mbean) {
-        Dn dn = new Dn(domain, globalDn);
-        String keyDn = "no_key";
-        if (isTreeView()) {
-            String props = globalDn;
-            Map<String,String> map = extractKeyValuePairs(props, mbean);
-            for (Map.Entry<String,String> entry : map.entrySet()) {
-                dn.addToken(new Token(keyDn,
-                        entry.getKey() + "=" + entry.getValue()));
-            }
-        } else {
-            //flat view
-            dn.addToken(new Token(keyDn, "properties=" + globalDn));
-        }
-        return dn;
-    }
-
-    //
-    //utility objects
-    //
-
-    public static class ComparableDefaultMutableTreeNode
-            extends DefaultMutableTreeNode
-            implements Comparable<DefaultMutableTreeNode> {
-        public int compareTo(DefaultMutableTreeNode node) {
-            return (this.toString().compareTo(node.toString()));
+    // Call on EDT
+    public void addMetadataNodes(DefaultMutableTreeNode node) {
+        XMBean mbean = (XMBean) ((XNodeInfo) node.getUserObject()).getData();
+        DefaultTreeModel model = (DefaultTreeModel) getModel();
+        MBeanInfoNodesSwingWorker sw =
+                new MBeanInfoNodesSwingWorker(model, node, mbean);
+        if (sw != null) {
+            sw.execute();
         }
     }
 
+    private static class MBeanInfoNodesSwingWorker
+            extends SwingWorker<Object[], Void> {
+
+        private final DefaultTreeModel model;
+        private final DefaultMutableTreeNode node;
+        private final XMBean mbean;
+
+        public MBeanInfoNodesSwingWorker(
+                DefaultTreeModel model,
+                DefaultMutableTreeNode node,
+                XMBean mbean) {
+            this.model = model;
+            this.node = node;
+            this.mbean = mbean;
+        }
+
+        @Override
+        public Object[] doInBackground() throws InstanceNotFoundException,
+                IntrospectionException, ReflectionException, IOException {
+            Object result[] = new Object[2];
+            // Retrieve MBeanInfo for this MBean
+            result[0] = mbean.getMBeanInfo();
+            // Check if this MBean is a notification emitter
+            result[1] = mbean.isBroadcaster();
+            return result;
+        }
+
+        @Override
+        protected void done() {
+            try {
+                Object result[] = get();
+                MBeanInfo mbeanInfo = (MBeanInfo) result[0];
+                Boolean isBroadcaster = (Boolean) result[1];
+                if (mbeanInfo != null) {
+                    addMBeanInfoNodes(model, node, mbean, mbeanInfo, isBroadcaster);
+                }
+            } catch (Exception e) {
+                Throwable t = Utils.getActualException(e);
+                if (JConsole.isDebug()) {
+                    t.printStackTrace();
+                }
+            }
+        }
+
+        // Call on EDT
+        private void addMBeanInfoNodes(
+                DefaultTreeModel tree, DefaultMutableTreeNode node,
+                XMBean mbean, MBeanInfo mbeanInfo, Boolean isBroadcaster) {
+            MBeanAttributeInfo[] ai = mbeanInfo.getAttributes();
+            MBeanOperationInfo[] oi = mbeanInfo.getOperations();
+            MBeanNotificationInfo[] ni = mbeanInfo.getNotifications();
+
+            // Insert the Attributes/Operations/Notifications metadata nodes as
+            // the three first children of this MBean node. This is only useful
+            // when this MBean node denotes an MBean but it's not a leaf in the
+            // MBean tree
+            //
+            int childIndex = 0;
+
+            // MBeanAttributeInfo node
+            //
+            if (ai != null && ai.length > 0) {
+                DefaultMutableTreeNode attributes = new DefaultMutableTreeNode();
+                XNodeInfo attributesUO = new XNodeInfo(Type.ATTRIBUTES, mbean,
+                        Resources.getText("Attributes"), null);
+                attributes.setUserObject(attributesUO);
+                node.insert(attributes, childIndex++);
+                for (MBeanAttributeInfo mbai : ai) {
+                    DefaultMutableTreeNode attribute = new DefaultMutableTreeNode();
+                    XNodeInfo attributeUO = new XNodeInfo(Type.ATTRIBUTE,
+                            new Object[]{mbean, mbai}, mbai.getName(), null);
+                    attribute.setUserObject(attributeUO);
+                    attribute.setAllowsChildren(false);
+                    attributes.add(attribute);
+                }
+            }
+            // MBeanOperationInfo node
+            //
+            if (oi != null && oi.length > 0) {
+                DefaultMutableTreeNode operations = new DefaultMutableTreeNode();
+                XNodeInfo operationsUO = new XNodeInfo(Type.OPERATIONS, mbean,
+                        Resources.getText("Operations"), null);
+                operations.setUserObject(operationsUO);
+                node.insert(operations, childIndex++);
+                for (MBeanOperationInfo mboi : oi) {
+                    // Compute the operation's tool tip text:
+                    // "operationname(param1type,param2type,...)"
+                    //
+                    StringBuilder sb = new StringBuilder();
+                    for (MBeanParameterInfo mbpi : mboi.getSignature()) {
+                        sb.append(mbpi.getType() + ",");
+                    }
+                    String signature = sb.toString();
+                    if (signature.length() > 0) {
+                        // Remove the trailing ','
+                        //
+                        signature = signature.substring(0, signature.length() - 1);
+                    }
+                    String toolTipText = mboi.getName() + "(" + signature + ")";
+                    // Create operation node
+                    //
+                    DefaultMutableTreeNode operation = new DefaultMutableTreeNode();
+                    XNodeInfo operationUO = new XNodeInfo(Type.OPERATION,
+                            new Object[]{mbean, mboi}, mboi.getName(), toolTipText);
+                    operation.setUserObject(operationUO);
+                    operation.setAllowsChildren(false);
+                    operations.add(operation);
+                }
+            }
+            // MBeanNotificationInfo node
+            //
+            if (isBroadcaster != null && isBroadcaster.booleanValue()) {
+                DefaultMutableTreeNode notifications = new DefaultMutableTreeNode();
+                XNodeInfo notificationsUO = new XNodeInfo(Type.NOTIFICATIONS, mbean,
+                        Resources.getText("Notifications"), null);
+                notifications.setUserObject(notificationsUO);
+                node.insert(notifications, childIndex++);
+                if (ni != null && ni.length > 0) {
+                    for (MBeanNotificationInfo mbni : ni) {
+                        DefaultMutableTreeNode notification =
+                                new DefaultMutableTreeNode();
+                        XNodeInfo notificationUO = new XNodeInfo(Type.NOTIFICATION,
+                                mbni, mbni.getName(), null);
+                        notification.setUserObject(notificationUO);
+                        notification.setAllowsChildren(false);
+                        notifications.add(notification);
+                    }
+                }
+            }
+            // Update tree model
+            //
+            model.reload(node);
+        }
+    }
     //
-    //tree preferences
+    // Tree preferences
     //
+    private static boolean treeView;
+    private static boolean treeViewInit = false;
 
-    private boolean treeView;
-    private boolean treeViewInit = false;
-    public boolean isTreeView() {
+    private static boolean isTreeView() {
         if (!treeViewInit) {
             treeView = getTreeViewValue();
             treeViewInit = true;
@@ -551,170 +680,136 @@
         return treeView;
     }
 
-    private boolean getTreeViewValue() {
-        String treeView = System.getProperty("treeView");
-        return ((treeView == null) ? true : !(treeView.equals("false")));
+    private static boolean getTreeViewValue() {
+        String tv = System.getProperty("treeView");
+        return ((tv == null) ? true : !(tv.equals("false")));
     }
+    //
+    // MBean key-value preferences
+    //
+    private boolean keyValueView = Boolean.getBoolean("keyValueView");
 
-    //
-    //MBean key-value preferences
-    //
-
-    private boolean keyValueView = Boolean.getBoolean("keyValueView");
-    public boolean isKeyValueView() {
+    private boolean isKeyValueView() {
         return keyValueView;
     }
 
     //
-    //utility classes
+    // Utility classes
     //
+    private static class ComparableDefaultMutableTreeNode
+            extends DefaultMutableTreeNode
+            implements Comparable<DefaultMutableTreeNode> {
 
-    public static class Dn {
+        public int compareTo(DefaultMutableTreeNode node) {
+            return (this.toString().compareTo(node.toString()));
+        }
+    }
 
+    private static class Dn implements Comparable<Dn> {
+
+        private ObjectName mbean;
         private String domain;
-        private String dn;
+        private String keyPropertyList;
         private String hashDn;
-        private ArrayList<Token> tokens = new ArrayList<Token>();
+        private List<Token> tokens = new ArrayList<Token>();
 
-        public Dn(String domain, String dn) {
-            this.domain = domain;
-            this.dn = dn;
+        public Dn(ObjectName mbean) {
+            this.mbean = mbean;
+            this.domain = mbean.getDomain();
+            this.keyPropertyList = getKeyPropertyListString(mbean);
+
+            if (isTreeView()) {
+                // Tree view
+                Map<String, String> map =
+                        extractKeyValuePairs(keyPropertyList, mbean);
+                for (Map.Entry<String, String> entry : map.entrySet()) {
+                    tokens.add(new Token("key", entry.getKey() + "=" + entry.getValue()));
+                }
+            } else {
+                // Flat view
+                tokens.add(new Token("key", "properties=" + keyPropertyList));
+            }
+
+            // Add the domain as the first token in the Dn
+            tokens.add(0, new Token("domain", "domain=" + domain));
+
+            // Reverse the Dn (from leaf to root)
+            Collections.reverse(tokens);
+
+            // Compute hash for Dn
+            computeHashDn();
         }
 
-        public void clearTokens() {
-            tokens.clear();
+        public ObjectName getObjectName() {
+            return mbean;
         }
 
-        public void addToken(Token token) {
-            tokens.add(token);
+        public String getDomain() {
+            return domain;
         }
 
-        public void addToken(int index, Token token) {
-            tokens.add(index, token);
-        }
-
-        public void setToken(int index, Token token) {
-            tokens.set(index, token);
-        }
-
-        public void removeToken(int index) {
-            tokens.remove(index);
+        public String getKeyPropertyList() {
+            return keyPropertyList;
         }
 
         public Token getToken(int index) {
             return tokens.get(index);
         }
 
-        public void reverseOrder() {
-            ArrayList<Token> newOrder = new ArrayList<Token>(tokens.size());
-            for (int i = tokens.size() - 1; i >= 0; i--) {
-                newOrder.add(tokens.get(i));
-            }
-            tokens = newOrder;
-        }
-
-        public int size() {
+        public int getTokenCount() {
             return tokens.size();
         }
 
-        public String getDomain() {
-            return domain;
-        }
-
-        public String getDn() {
-            return dn;
-        }
-
         public String getHashDn() {
             return hashDn;
         }
 
         public String getHashKey(Token token) {
-            final int begin = getHashDn().indexOf(token.getHashToken());
-            return  getHashDn().substring(begin, getHashDn().length());
+            final int begin = hashDn.indexOf(token.getTokenValue());
+            return hashDn.substring(begin, hashDn.length());
         }
 
-        public void computeHashDn() {
-            final StringBuilder hashDn = new StringBuilder();
-            final int tokensSize = tokens.size();
-            for (int i = 0; i < tokensSize; i++) {
-                Token token = tokens.get(i);
-                String hashToken = token.getHashToken();
-                if (hashToken == null) {
-                    hashToken = token.getToken() + (tokensSize - i);
-                    token.setHashToken(hashToken);
-                }
-                hashDn.append(hashToken);
-                hashDn.append(",");
+        private void computeHashDn() {
+            if (tokens.isEmpty()) {
+                return;
             }
-            if (tokensSize > 0) {
-                this.hashDn = hashDn.substring(0, hashDn.length() - 1);
-            } else {
-                this.hashDn = "";
+            final StringBuilder hdn = new StringBuilder();
+            for (int i = 0; i < tokens.size(); i++) {
+                hdn.append(tokens.get(i).getTokenValue());
+                hdn.append(",");
             }
+            hashDn = hdn.substring(0, hdn.length() - 1);
         }
 
-        /**
-         * Adds the domain as the first token in the Dn.
-         */
-        public void updateDn() {
-            addToken(0, new Token("domain", "domain=" + getDomain()));
+        @Override
+        public String toString() {
+            return domain + ":" + keyPropertyList;
         }
 
-        public String toString() {
-            return tokens.toString();
+        public int compareTo(Dn dn) {
+            return this.toString().compareTo(dn.toString());
         }
     }
 
-    public static class Token {
+    private static class Token {
 
-        private String keyDn;
-        private String token;
-        private String hashToken;
+        private String tokenType;
+        private String tokenValue;
         private String key;
         private String value;
 
-        public Token(String keyDn, String token) {
-            this.keyDn = keyDn;
-            this.token = token;
+        public Token(String tokenType, String tokenValue) {
+            this.tokenType = tokenType;
+            this.tokenValue = tokenValue;
             buildKeyValue();
         }
 
-        public Token(String keyDn, String token, String hashToken) {
-            this.keyDn = keyDn;
-            this.token = token;
-            this.hashToken = hashToken;
-            buildKeyValue();
+        public String getTokenType() {
+            return tokenType;
         }
 
-        public String getKeyDn() {
-            return keyDn;
-        }
-
-        public String getToken() {
-            return token;
-        }
-
-        public void setValue(String value) {
-            this.value = value;
-            this.token = key + "=" + value;
-        }
-
-        public void setKey(String key) {
-            this.key = key;
-            this.token = key + "=" + value;
-        }
-
-        public void setKeyDn(String keyDn) {
-            this.keyDn = keyDn;
-        }
-
-        public  void setHashToken(String hashToken) {
-            this.hashToken = hashToken;
-        }
-
-        public String getHashToken() {
-            return hashToken;
+        public String getTokenValue() {
+            return tokenValue;
         }
 
         public String getKey() {
@@ -725,26 +820,14 @@
             return value;
         }
 
-        public String toString(){
-            return getToken();
-        }
-
-        public boolean equals(Object object) {
-            if (object instanceof Token) {
-                return token.equals(((Token) object));
+        private void buildKeyValue() {
+            int index = tokenValue.indexOf("=");
+            if (index < 0) {
+                key = tokenValue;
+                value = tokenValue;
             } else {
-                return false;
-            }
-        }
-
-        private void buildKeyValue() {
-            int index = token.indexOf("=");
-            if (index < 0) {
-                key = token;
-                value = token;
-            } else {
-                key = token.substring(0, index);
-                value = token.substring(index + 1, token.length());
+                key = tokenValue.substring(0, index);
+                value = tokenValue.substring(index + 1, tokenValue.length());
             }
         }
     }