text: /*
   * Copyright (c) 2004, 2012, Oracle and/or its affiliates. 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
   * under the terms of the GNU General Public License version 2 only, as
   * published by the Free Software Foundation.  Oracle designates this
   * particular file as subject to the "Classpath" exception as provided
   * by Oracle in the LICENSE file that accompanied this code.
   *
   * This code is distributed in the hope that it will be useful, but WITHOUT
   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
   * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
   * version 2 for more details (a copy is included in the LICENSE file that
   * accompanied this code).
   *
   * You should have received a copy of the GNU General Public License version
   * 2 along with this work; if not, write to the Free Software Foundation,
   * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
   *
   * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
   * or visit www.oracle.com if you need additional information or have any
   * questions.
   */
  
  package sun.tools.jconsole.inspector;
  
  
  import java.awt.BorderLayout;
  import java.awt.Color;
  import java.awt.Component;
  import java.awt.Dimension;
  import java.awt.event.ActionEvent;
  import java.awt.event.ActionListener;
  import java.io.IOException;
  
  import javax.management.IntrospectionException;
  import javax.management.NotificationListener;
  import javax.management.MBeanInfo;
  import javax.management.InstanceNotFoundException;
  import javax.management.ReflectionException;
  import javax.management.MBeanAttributeInfo;
  import javax.management.MBeanOperationInfo;
  import javax.management.MBeanNotificationInfo;
  import javax.management.Notification;
  import javax.swing.BorderFactory;
  import javax.swing.JButton;
  import javax.swing.JOptionPane;
  import javax.swing.JPanel;
  import javax.swing.JScrollPane;
  import javax.swing.JTextArea;
  import javax.swing.SwingWorker;
  import javax.swing.border.LineBorder;
  import javax.swing.tree.DefaultMutableTreeNode;
  import javax.swing.tree.DefaultTreeModel;
  
  import sun.tools.jconsole.*;
  import sun.tools.jconsole.inspector.XNodeInfo.Type;
  
  @SuppressWarnings("serial")
  public class XSheet extends JPanel
          implements ActionListener, NotificationListener {
  
      private JPanel mainPanel;
      private JPanel southPanel;
      // Node being currently displayed
      private volatile DefaultMutableTreeNode currentNode;
      // MBean being currently displayed
      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;
      // Reference to MBeans tab
      private MBeansTab mbeansTab;
  
      public XSheet(MBeansTab mbeansTab) {
          this.mbeansTab = mbeansTab;
          setupScreen();
      }
  
      public void dispose() {
          clear();
          XDataViewer.dispose(mbeansTab);
          mbeanNotifications.dispose();
      }
  
      private void setupScreen() {
          setLayout(new BorderLayout());
          setBorder(BorderFactory.createLineBorder(Color.GRAY));
          // add main panel to XSheet
          mainPanel = new JPanel();
          mainPanel.setLayout(new BorderLayout());
          add(mainPanel, BorderLayout.CENTER);
          // add south panel to XSheet
          southPanel = new JPanel();
          add(southPanel, BorderLayout.SOUTH);
          // create the refresh button
          refreshButton = new JButton(Messages.MBEANS_TAB_REFRESH_ATTRIBUTES_BUTTON);
          refreshButton.setMnemonic(Resources.getMnemonicInt(Messages.MBEANS_TAB_REFRESH_ATTRIBUTES_BUTTON));
          refreshButton.setToolTipText(Messages.MBEANS_TAB_REFRESH_ATTRIBUTES_BUTTON_TOOLTIP);
          refreshButton.addActionListener(this);
          // create the clear button
          clearButton = new JButton(Messages.MBEANS_TAB_CLEAR_NOTIFICATIONS_BUTTON);
          clearButton.setMnemonic(Resources.getMnemonicInt(Messages.MBEANS_TAB_CLEAR_NOTIFICATIONS_BUTTON));
          clearButton.setToolTipText(Messages.MBEANS_TAB_CLEAR_NOTIFICATIONS_BUTTON_TOOLTIP);
          clearButton.addActionListener(this);
          // create the subscribe button
          subscribeButton = new JButton(Messages.MBEANS_TAB_SUBSCRIBE_NOTIFICATIONS_BUTTON);
          subscribeButton.setMnemonic(Resources.getMnemonicInt(Messages.MBEANS_TAB_SUBSCRIBE_NOTIFICATIONS_BUTTON));
          subscribeButton.setToolTipText(Messages.MBEANS_TAB_SUBSCRIBE_NOTIFICATIONS_BUTTON_TOOLTIP);
          subscribeButton.addActionListener(this);
          // create the unsubscribe button
          unsubscribeButton = new JButton(Messages.MBEANS_TAB_UNSUBSCRIBE_NOTIFICATIONS_BUTTON);
          unsubscribeButton.setMnemonic(Resources.getMnemonicInt(Messages.MBEANS_TAB_UNSUBSCRIBE_NOTIFICATIONS_BUTTON));
          unsubscribeButton.setToolTipText(Messages.MBEANS_TAB_UNSUBSCRIBE_NOTIFICATIONS_BUTTON_TOOLTIP);
          unsubscribeButton.addActionListener(this);
          // create XMBeanAttributes container
          mbeanAttributes = new XMBeanAttributes(mbeansTab);
          // create XMBeanOperations container
          mbeanOperations = new XMBeanOperations(mbeansTab);
          mbeanOperations.addOperationsListener(this);
          // create XMBeanNotifications container
          mbeanNotifications = new XMBeanNotifications();
          mbeanNotifications.addNotificationsListener(this);
          // create XMBeanInfo container
          mbeanInfo = new XMBeanInfo();
      }
  
      private boolean isSelectedNode(DefaultMutableTreeNode n, DefaultMutableTreeNode cn) {
          return (cn == n);
      }
  
      // 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) {
              return;
          }
          currentNode = node;
          Object userObject = node.getUserObject();
          if (userObject instanceof XNodeInfo) {
              XNodeInfo uo = (XNodeInfo) userObject;
              switch (uo.getType()) {
                  case MBEAN:
                      displayMBeanNode(node);
                      break;
                  case NONMBEAN:
                      displayEmptyNode();
                      break;
                  case ATTRIBUTES:
                      displayMBeanAttributesNode(node);
                      break;
                  case OPERATIONS:
                      displayMBeanOperationsNode(node);
                      break;
                  case NOTIFICATIONS:
                      displayMBeanNotificationsNode(node);
                      break;
                  case ATTRIBUTE:
                  case OPERATION:
                  case NOTIFICATION:
                      displayMetadataNode(node);
                      break;
                  default:
                      displayEmptyNode();
                      break;
              }
          } else {
              displayEmptyNode();
          }
      }
  
      // Call on EDT
      private void displayMBeanNode(final DefaultMutableTreeNode node) {
          final XNodeInfo uo = (XNodeInfo) node.getUserObject();
          if (!uo.getType().equals(Type.MBEAN)) {
              return;
          }
          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 {
                      MBeanInfo mbi = get();
                      if (mbi != null) {
                          if (!isSelectedNode(node, currentNode)) {
                              return;
                          }
                          mbeanInfo.addMBeanInfo(mbean, mbi);
                          invalidate();
                          mainPanel.removeAll();
                          mainPanel.add(mbeanInfo, BorderLayout.CENTER);
                          southPanel.setVisible(false);
                          southPanel.removeAll();
                          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(),
                              Messages.PROBLEM_DISPLAYING_MBEAN);
                  }
              }
          };
          sw.execute();
      }
  
      // Call on EDT
      private void displayMetadataNode(final DefaultMutableTreeNode node) {
          final XNodeInfo uo = (XNodeInfo) node.getUserObject();
          final XMBeanInfo mbi = mbeanInfo;
          switch (uo.getType()) {
              case ATTRIBUTE:
                  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(
                                              Messages.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(),
                                              Messages.PROBLEM_DISPLAYING_MBEAN);
                                  }
                              }
                          };
                  sw.execute();
                  break;
              case OPERATION:
                  Object operData = uo.getData();
                  mbean = (XMBean) ((Object[]) operData)[0];
                  MBeanOperationInfo mboi =
                          (MBeanOperationInfo) ((Object[]) operData)[1];
                  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(
                          Messages.OPERATION_INVOCATION));
                  operationBorderPanel.add(new JScrollPane(mbeanOperations));
                  operationPanel.add(operationBorderPanel, BorderLayout.NORTH);
                  mbi.addMBeanOperationInfo(mboi);
                  operationPanel.add(mbi, BorderLayout.CENTER);
                  mainPanel.add(operationPanel, BorderLayout.CENTER);
                  southPanel.setVisible(false);
                  southPanel.removeAll();
                  validate();
                  repaint();
                  break;
              case NOTIFICATION:
                  Object notifData = uo.getData();
                  invalidate();
                  mainPanel.removeAll();
                  mbi.addMBeanNotificationInfo((MBeanNotificationInfo) notifData);
                  mainPanel.add(mbi, BorderLayout.CENTER);
                  southPanel.setVisible(false);
                  southPanel.removeAll();
                  validate();
                  repaint();
                  break;
          }
      }
  
      // Call on EDT
      private void displayMBeanAttributesNode(final DefaultMutableTreeNode node) {
          final XNodeInfo uo = (XNodeInfo) node.getUserObject();
          if (!uo.getType().equals(Type.ATTRIBUTES)) {
              return;
          }
          mbean = (XMBean) uo.getData();
          final XMBean xmb = mbean;
          SwingWorker<MBeanInfo,Void> sw = new SwingWorker<MBeanInfo,Void>() {
              @Override
              public MBeanInfo doInBackground() throws InstanceNotFoundException,
                      IntrospectionException, ReflectionException, IOException {
                  MBeanInfo mbi = xmb.getMBeanInfo();
                  return mbi;
              }
              @Override
              protected void done() {
                  try {
                      MBeanInfo mbi = get();
                      if (mbi != null && mbi.getAttributes() != null &&
                              mbi.getAttributes().length > 0) {
  
                          mbeanAttributes.loadAttributes(xmb, mbi);
  
                          if (!isSelectedNode(node, currentNode)) {
                              return;
                          }
                          invalidate();
                          mainPanel.removeAll();
                          JPanel borderPanel = new JPanel(new BorderLayout());
                          borderPanel.setBorder(BorderFactory.createTitledBorder(
                                  Messages.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(),
                              Messages.PROBLEM_DISPLAYING_MBEAN);
                  }
              }
          };
          sw.execute();
      }
  
      // Call on EDT
      private void displayMBeanOperationsNode(final DefaultMutableTreeNode node) {
          final XNodeInfo uo = (XNodeInfo) node.getUserObject();
          if (!uo.getType().equals(Type.OPERATIONS)) {
              return;
          }
          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 {
                      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(
                                  Messages.OPERATION_INVOCATION));
                          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(),
                              Messages.PROBLEM_DISPLAYING_MBEAN);
                  }
              }
          };
          sw.execute();
      }
  
      // Call on EDT
      private void displayMBeanNotificationsNode(DefaultMutableTreeNode node) {
          final XNodeInfo uo = (XNodeInfo) node.getUserObject();
          if (!uo.getType().equals(Type.NOTIFICATIONS)) {
              return;
          }
          mbean = (XMBean) uo.getData();
          mbeanNotifications.loadNotifications(mbean);
          updateNotifications();
          invalidate();
          mainPanel.removeAll();
          JPanel borderPanel = new JPanel(new BorderLayout());
          borderPanel.setBorder(BorderFactory.createTitledBorder(
                  Messages.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
      private void displayEmptyNode() {
          invalidate();
          mainPanel.removeAll();
          southPanel.removeAll();
          validate();
          repaint();
      }
  
      /**
       * Subscribe button action.
       */
      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(),
                              Messages.PROBLEM_ADDING_LISTENER);
                  }
              }
          }.execute();
      }
  
      /**
       * Unsubscribe button action.
       */
      private void unregisterListener() {
          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(),
                              Messages.PROBLEM_REMOVING_LISTENER);
                  }
              }
          }.execute();
      }
  
      /**
       * Refresh button action.
       */
      private void refreshAttributes() {
          mbeanAttributes.refreshAttributes();
      }
  
      // Call on EDT
      private void updateNotifications() {
          if (mbeanNotifications.isListenerRegistered(mbean)) {
              long received = mbeanNotifications.getReceivedNotifications(mbean);
              updateReceivedNotifications(currentNode, received, false);
          } else {
              clearNotifications();
          }
      }
  
      /**
       * Update notification node label in MBean tree: "Notifications[received]".
       */
      // Call on EDT
      private void updateReceivedNotifications(
              DefaultMutableTreeNode emitter, long received, boolean bold) {
          String text = Messages.NOTIFICATIONS + "[" + received + "]";
          DefaultMutableTreeNode selectedNode = (DefaultMutableTreeNode) mbeansTab.getTree().getLastSelectedPathComponent();
          if (bold && emitter != selectedNode) {
              text = "<html><b>" + text + "</b></html>";
          }
          updateNotificationsNodeLabel(emitter, text);
      }
  
      /**
       * Update notification node label in MBean tree: "Notifications".
       */
      // Call on EDT
      private void clearNotifications() {
          updateNotificationsNodeLabel(currentNode,
                  Messages.NOTIFICATIONS);
      }
  
      /**
       * Update notification node label in MBean tree: "Notifications[0]".
       */
      // Call on EDT
      private void clearNotifications0() {
          updateNotificationsNodeLabel(currentNode,
                  Messages.NOTIFICATIONS + "[0]");
      }
  
      /**
       * Update the label of the supplied MBean tree node.
       */
      // Call on EDT
      private void updateNotificationsNodeLabel(
              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();
          }
      }
  
      /**
       * Clear button action.
       */
      // Call on EDT
      private void clearCurrentNotifications() {
          mbeanNotifications.clearCurrentNotifications();
          if (mbeanNotifications.isListenerRegistered(mbean)) {
              // Update notifs in MBean tree "Notifications[0]".
              //
              // Notification buffer has been cleared with a listener been
              // registered so add "[0]" at the end of the node label.
              //
              clearNotifications0();
          } else {
              // Update notifs in MBean tree "Notifications".
              //
              // Notification buffer has been cleared without a listener been
              // registered so don't add "[0]" at the end of the node label.
              //
              clearNotifications();
          }
      }
  
      // Call on EDT
      private void clear() {
          mbeanAttributes.stopCellEditing();
          mbeanAttributes.emptyTable();
          mbeanAttributes.removeAttributes();
          mbeanOperations.removeOperations();
          mbeanNotifications.stopCellEditing();
          mbeanNotifications.emptyTable();
          mbeanNotifications.disableNotifications();
          mbean = 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)) {
              final Object message;
              if (handback == null) {
                  JTextArea textArea = new JTextArea("null");
                  textArea.setEditable(false);
                  textArea.setEnabled(true);
                  textArea.setRows(textArea.getLineCount());
                  message = textArea;
              } else {
                  Component comp = mbeansTab.getDataViewer().
                          createOperationViewer(handback, mbean);
                  if (comp == null) {
                      JTextArea textArea = new JTextArea(handback.toString());
                      textArea.setEditable(false);
                      textArea.setEnabled(true);
                      textArea.setRows(textArea.getLineCount());
                      JScrollPane scrollPane = new JScrollPane(textArea);
                      Dimension d = scrollPane.getPreferredSize();
                      if (d.getWidth() > 400 || d.getHeight() > 250) {
                          scrollPane.setPreferredSize(new Dimension(400, 250));
                      }
                      message = scrollPane;
                  } else {
                      if (!(comp instanceof JScrollPane)) {
                          comp = new JScrollPane(comp);
                      }
                      Dimension d = comp.getPreferredSize();
                      if (d.getWidth() > 400 || d.getHeight() > 250) {
                          comp.setPreferredSize(new Dimension(400, 250));
                      }
                      message = comp;
                  }
              }
              new ThreadDialog(
                      (Component) e.getSource(),
                      message,
                      Messages.OPERATION_RETURN_VALUE,
                      JOptionPane.INFORMATION_MESSAGE).run();
          } // Got notification
          else if (e.getType().equals(
                  XMBeanNotifications.NOTIFICATION_RECEIVED_EVENT)) {
              DefaultMutableTreeNode emitter = (DefaultMutableTreeNode) handback;
              Long received = (Long) e.getUserData();
              updateReceivedNotifications(emitter, received.longValue(), true);
          }
      }
  
      /**
       * 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) {
                  refreshAttributes();
                  return;
              }
              // Clear button
              if (button == clearButton) {
                  clearCurrentNotifications();
                  return;
              }
              // Subscribe button
              if (button == subscribeButton) {
                  registerListener();
                  return;
              }
              // Unsubscribe button
              if (button == unsubscribeButton) {
                  unregisterListener();
                  return;
              }
          }
      }
  }
  