diff -r 4ebc2e2fb97c -r 71c04702a3d5 src/jdk.jconsole/share/classes/sun/tools/jconsole/inspector/XSheet.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.jconsole/share/classes/sun/tools/jconsole/inspector/XSheet.java Tue Sep 12 19:03:39 2017 +0200 @@ -0,0 +1,749 @@ +/* + * 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 sw = new SwingWorker() { + @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 sw = + new SwingWorker() { + @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 sw = new SwingWorker() { + @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 sw = new SwingWorker() { + @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() { + @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() { + @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 = "" + text + ""; + } + 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; + } + } + } +}