1 /*
   2  * Copyright (c) 2004, 2006, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package sun.tools.jconsole.inspector;
  27 
  28 import java.awt.BorderLayout;
  29 import java.awt.Color;
  30 import java.awt.Component;
  31 import java.awt.GridLayout;
  32 import java.util.*;
  33 import javax.management.*;
  34 import javax.swing.*;
  35 import javax.swing.border.TitledBorder;
  36 import javax.swing.event.*;
  37 import javax.swing.table.*;
  38 import javax.swing.tree.*;
  39 import sun.tools.jconsole.JConsole;
  40 import sun.tools.jconsole.Resources;
  41 import sun.tools.jconsole.inspector.XNodeInfo.Type;
  42 
  43 import static sun.tools.jconsole.Utilities.*;
  44 
  45 @SuppressWarnings("serial")
  46 public class XMBeanInfo extends JPanel {
  47 
  48     private static final Color lightYellow = new Color(255, 255, 128);
  49 
  50     private final int NAME_COLUMN = 0;
  51     private final int VALUE_COLUMN = 1;
  52 
  53     private final String[] columnNames = {
  54         Resources.getText("Name"),
  55         Resources.getText("Value")
  56     };
  57 
  58     private JTable infoTable = new JTable();
  59     private JTable descTable = new JTable();
  60     private JPanel infoBorderPanel = new JPanel(new BorderLayout());
  61     private JPanel descBorderPanel = new JPanel(new BorderLayout());
  62 
  63     private static class ReadOnlyDefaultTableModel extends DefaultTableModel {
  64         public void setValueAt(Object value, int row, int col) {
  65         }
  66     }
  67 
  68     private static class TableRowDivider {
  69 
  70         private String tableRowDividerText;
  71 
  72         public TableRowDivider(String tableRowDividerText) {
  73             this.tableRowDividerText = tableRowDividerText;
  74         }
  75 
  76         public String toString() {
  77             return tableRowDividerText;
  78         }
  79     }
  80 
  81     private static MBeanInfoTableCellRenderer renderer =
  82             new MBeanInfoTableCellRenderer();
  83 
  84     private static class MBeanInfoTableCellRenderer
  85             extends DefaultTableCellRenderer {
  86 
  87         public Component getTableCellRendererComponent(
  88                 JTable table, Object value, boolean isSelected,
  89                 boolean hasFocus, int row, int column) {
  90             Component comp = super.getTableCellRendererComponent(
  91                     table, value, isSelected, hasFocus, row, column);
  92             if (value instanceof TableRowDivider) {
  93                 JLabel label = new JLabel(value.toString());
  94                 label.setBackground(ensureContrast(lightYellow,
  95                                                    label.getForeground()));
  96                 label.setOpaque(true);
  97                 return label;
  98             }
  99             return comp;
 100         }
 101     }
 102 
 103     private static TableCellEditor editor =
 104             new MBeanInfoTableCellEditor(new JTextField());
 105 
 106     private static class MBeanInfoTableCellEditor
 107             extends Utils.ReadOnlyTableCellEditor {
 108         public MBeanInfoTableCellEditor(JTextField tf) {
 109             super(tf);
 110         }
 111         public Component getTableCellEditorComponent(
 112                 JTable table, Object value, boolean isSelected,
 113                 int row, int column) {
 114             Component comp = super.getTableCellEditorComponent(
 115                     table, value, isSelected, row, column);
 116             if (value instanceof TableRowDivider) {
 117                 JLabel label = new JLabel(value.toString());
 118                 label.setBackground(ensureContrast(lightYellow,
 119                                                    label.getForeground()));
 120                 label.setOpaque(true);
 121                 return label;
 122             }
 123             return comp;
 124         }
 125     }
 126 
 127     public XMBeanInfo() {
 128         // Use the grid layout to display the two tables
 129         //
 130         super(new GridLayout(2, 1));
 131         // MBean*Info table
 132         //
 133         infoTable.setModel(new ReadOnlyDefaultTableModel());
 134         infoTable.setRowSelectionAllowed(false);
 135         infoTable.setColumnSelectionAllowed(false);
 136         infoTable.getTableHeader().setReorderingAllowed(false);
 137         ((DefaultTableModel) infoTable.getModel()).setColumnIdentifiers(columnNames);
 138         infoTable.getColumnModel().getColumn(NAME_COLUMN).setPreferredWidth(140);
 139         infoTable.getColumnModel().getColumn(NAME_COLUMN).setMaxWidth(140);
 140         infoTable.getColumnModel().getColumn(NAME_COLUMN).setCellRenderer(renderer);
 141         infoTable.getColumnModel().getColumn(VALUE_COLUMN).setCellRenderer(renderer);
 142         infoTable.getColumnModel().getColumn(NAME_COLUMN).setCellEditor(editor);
 143         infoTable.getColumnModel().getColumn(VALUE_COLUMN).setCellEditor(editor);
 144         infoTable.addKeyListener(new Utils.CopyKeyAdapter());
 145         infoTable.setAutoResizeMode(JTable.AUTO_RESIZE_SUBSEQUENT_COLUMNS);
 146         JScrollPane infoTableScrollPane = new JScrollPane(infoTable);
 147         infoBorderPanel.setBorder(
 148                 BorderFactory.createTitledBorder("MBeanInfoPlaceHolder"));
 149         infoBorderPanel.add(infoTableScrollPane);
 150         // Descriptor table
 151         //
 152         descTable.setModel(new ReadOnlyDefaultTableModel());
 153         descTable.setRowSelectionAllowed(false);
 154         descTable.setColumnSelectionAllowed(false);
 155         descTable.getTableHeader().setReorderingAllowed(false);
 156         ((DefaultTableModel) descTable.getModel()).setColumnIdentifiers(columnNames);
 157         descTable.getColumnModel().getColumn(NAME_COLUMN).setPreferredWidth(140);
 158         descTable.getColumnModel().getColumn(NAME_COLUMN).setMaxWidth(140);
 159         descTable.getColumnModel().getColumn(NAME_COLUMN).setCellRenderer(renderer);
 160         descTable.getColumnModel().getColumn(VALUE_COLUMN).setCellRenderer(renderer);
 161         descTable.getColumnModel().getColumn(NAME_COLUMN).setCellEditor(editor);
 162         descTable.getColumnModel().getColumn(VALUE_COLUMN).setCellEditor(editor);
 163         descTable.addKeyListener(new Utils.CopyKeyAdapter());
 164         descTable.setAutoResizeMode(JTable.AUTO_RESIZE_SUBSEQUENT_COLUMNS);
 165         JScrollPane descTableScrollPane = new JScrollPane(descTable);
 166         descBorderPanel.setBorder(
 167                 BorderFactory.createTitledBorder(Resources.getText("Descriptor")));
 168         descBorderPanel.add(descTableScrollPane);
 169         // Add the two tables to the grid
 170         //
 171         add(infoBorderPanel);
 172         add(descBorderPanel);
 173     }
 174 
 175     public void emptyInfoTable() {
 176         DefaultTableModel tableModel = (DefaultTableModel) infoTable.getModel();
 177         while (tableModel.getRowCount() > 0) {
 178             tableModel.removeRow(0);
 179         }
 180     }
 181 
 182     public void emptyDescTable() {
 183         DefaultTableModel tableModel = (DefaultTableModel) descTable.getModel();
 184         while (tableModel.getRowCount() > 0) {
 185             tableModel.removeRow(0);
 186         }
 187     }
 188 
 189     private void addDescriptor(Descriptor desc, String text) {
 190         if (desc != null && desc.getFieldNames().length > 0) {
 191             DefaultTableModel tableModel = (DefaultTableModel) descTable.getModel();
 192             Object rowData[] = new Object[2];
 193             rowData[0] = new TableRowDivider(text);
 194             rowData[1] = new TableRowDivider("");
 195             tableModel.addRow(rowData);
 196             for (String fieldName : desc.getFieldNames()) {
 197                 rowData[0] = fieldName;
 198                 Object fieldValue = desc.getFieldValue(fieldName);
 199                 if (fieldValue instanceof boolean[]) {
 200                     rowData[1] = Arrays.toString((boolean[]) fieldValue);
 201                 } else if (fieldValue instanceof byte[]) {
 202                     rowData[1] = Arrays.toString((byte[]) fieldValue);
 203                 } else if (fieldValue instanceof char[]) {
 204                     rowData[1] = Arrays.toString((char[]) fieldValue);
 205                 } else if (fieldValue instanceof double[]) {
 206                     rowData[1] = Arrays.toString((double[]) fieldValue);
 207                 } else if (fieldValue instanceof float[]) {
 208                     rowData[1] = Arrays.toString((float[]) fieldValue);
 209                 } else if (fieldValue instanceof int[]) {
 210                     rowData[1] = Arrays.toString((int[]) fieldValue);
 211                 } else if (fieldValue instanceof long[]) {
 212                     rowData[1] = Arrays.toString((long[]) fieldValue);
 213                 } else if (fieldValue instanceof short[]) {
 214                     rowData[1] = Arrays.toString((short[]) fieldValue);
 215                 } else if (fieldValue instanceof Object[]) {
 216                     rowData[1] = Arrays.toString((Object[]) fieldValue);
 217                 } else {
 218                     rowData[1] = fieldValue;
 219                 }
 220                 tableModel.addRow(rowData);
 221             }
 222             tableModel.newDataAvailable(new TableModelEvent(tableModel));
 223         }
 224     }
 225 
 226     public void addMBeanInfo(XMBean mbean, MBeanInfo mbeanInfo) {
 227         emptyInfoTable();
 228         emptyDescTable();
 229         ((TitledBorder) infoBorderPanel.getBorder()).setTitle(
 230                 Resources.getText("MBeanInfo"));
 231         String text = Resources.getText("Info") + ":";
 232         DefaultTableModel tableModel = (DefaultTableModel) infoTable.getModel();
 233         Object rowData[] = new Object[2];
 234         rowData[0] = new TableRowDivider(text);
 235         rowData[1] = new TableRowDivider("");
 236         tableModel.addRow(rowData);
 237         rowData[0] = Resources.getText("ObjectName");
 238         rowData[1] = mbean.getObjectName();
 239         tableModel.addRow(rowData);
 240         rowData[0] = Resources.getText("ClassName");
 241         rowData[1] = mbeanInfo.getClassName();
 242         tableModel.addRow(rowData);
 243         rowData[0] = Resources.getText("Description");
 244         rowData[1] = mbeanInfo.getDescription();
 245         tableModel.addRow(rowData);
 246         addDescriptor(mbeanInfo.getDescriptor(), text);
 247         // MBeanConstructorInfo
 248         //
 249         int i = 0;
 250         for (MBeanConstructorInfo mbci : mbeanInfo.getConstructors()) {
 251             addMBeanConstructorInfo(mbci,
 252                     Resources.getText("Constructor") + "-" + i + ":");
 253             // MBeanParameterInfo
 254             //
 255             int j = 0;
 256             for (MBeanParameterInfo mbpi : mbci.getSignature()) {
 257                 addMBeanParameterInfo(mbpi,
 258                         Resources.getText("Parameter") + "-" + i + "-" + j + ":");
 259                 j++;
 260             }
 261             i++;
 262         }
 263         tableModel.newDataAvailable(new TableModelEvent(tableModel));
 264     }
 265 
 266     public void addMBeanAttributeInfo(MBeanAttributeInfo mbai) {
 267         emptyInfoTable();
 268         emptyDescTable();
 269         ((TitledBorder) infoBorderPanel.getBorder()).setTitle(
 270                 Resources.getText("MBeanAttributeInfo"));
 271         String text = Resources.getText("Attribute") + ":";
 272         DefaultTableModel tableModel = (DefaultTableModel) infoTable.getModel();
 273         Object rowData[] = new Object[2];
 274         rowData[0] = new TableRowDivider(text);
 275         rowData[1] = new TableRowDivider("");
 276         tableModel.addRow(rowData);
 277         rowData[0] = Resources.getText("Name");
 278         rowData[1] = mbai.getName();
 279         tableModel.addRow(rowData);
 280         rowData[0] = Resources.getText("Description");
 281         rowData[1] = mbai.getDescription();
 282         tableModel.addRow(rowData);
 283         rowData[0] = Resources.getText("Readable");
 284         rowData[1] = mbai.isReadable();
 285         tableModel.addRow(rowData);
 286         rowData[0] = Resources.getText("Writable");
 287         rowData[1] = mbai.isWritable();
 288         tableModel.addRow(rowData);
 289         rowData[0] = Resources.getText("Is");
 290         rowData[1] = mbai.isIs();
 291         tableModel.addRow(rowData);
 292         rowData[0] = Resources.getText("Type");
 293         rowData[1] = mbai.getType();
 294         tableModel.addRow(rowData);
 295         addDescriptor(mbai.getDescriptor(), text);
 296         tableModel.newDataAvailable(new TableModelEvent(tableModel));
 297     }
 298 
 299     public void addMBeanOperationInfo(MBeanOperationInfo mboi) {
 300         emptyInfoTable();
 301         emptyDescTable();
 302         ((TitledBorder) infoBorderPanel.getBorder()).setTitle(
 303                 Resources.getText("MBeanOperationInfo"));
 304         String text = Resources.getText("Operation") + ":";
 305         DefaultTableModel tableModel = (DefaultTableModel) infoTable.getModel();
 306         Object rowData[] = new Object[2];
 307         rowData[0] = new TableRowDivider(text);
 308         rowData[1] = new TableRowDivider("");
 309         tableModel.addRow(rowData);
 310         rowData[0] = Resources.getText("Name");
 311         rowData[1] = mboi.getName();
 312         tableModel.addRow(rowData);
 313         rowData[0] = Resources.getText("Description");
 314         rowData[1] = mboi.getDescription();
 315         tableModel.addRow(rowData);
 316         rowData[0] = Resources.getText("Impact");
 317         switch (mboi.getImpact()) {
 318             case MBeanOperationInfo.INFO:
 319                 rowData[1] = Resources.getText("INFO");
 320                 break;
 321             case MBeanOperationInfo.ACTION:
 322                 rowData[1] = Resources.getText("ACTION");
 323                 break;
 324             case MBeanOperationInfo.ACTION_INFO:
 325                 rowData[1] = Resources.getText("ACTION_INFO");
 326                 break;
 327             case MBeanOperationInfo.UNKNOWN:
 328                 rowData[1] = Resources.getText("UNKNOWN");
 329                 break;
 330         }
 331         tableModel.addRow(rowData);
 332         rowData[0] = Resources.getText("ReturnType");
 333         rowData[1] = mboi.getReturnType();
 334         tableModel.addRow(rowData);
 335         addDescriptor(mboi.getDescriptor(), text);
 336         // MBeanParameterInfo
 337         //
 338         int i = 0;
 339         for (MBeanParameterInfo mbpi : mboi.getSignature()) {
 340             addMBeanParameterInfo(mbpi,
 341                     Resources.getText("Parameter") + "-" + i++ + ":");
 342         }
 343         tableModel.newDataAvailable(new TableModelEvent(tableModel));
 344     }
 345 
 346     public void addMBeanNotificationInfo(MBeanNotificationInfo mbni) {
 347         emptyInfoTable();
 348         emptyDescTable();
 349         ((TitledBorder) infoBorderPanel.getBorder()).setTitle(
 350                 Resources.getText("MBeanNotificationInfo"));
 351         String text = Resources.getText("Notification") + ":";
 352         DefaultTableModel tableModel = (DefaultTableModel) infoTable.getModel();
 353         Object rowData[] = new Object[2];
 354         rowData[0] = new TableRowDivider(text);
 355         rowData[1] = new TableRowDivider("");
 356         tableModel.addRow(rowData);
 357         rowData[0] = Resources.getText("Name");
 358         rowData[1] = mbni.getName();
 359         tableModel.addRow(rowData);
 360         rowData[0] = Resources.getText("Description");
 361         rowData[1] = mbni.getDescription();
 362         tableModel.addRow(rowData);
 363         rowData[0] = Resources.getText("NotifTypes");
 364         rowData[1] = Arrays.toString(mbni.getNotifTypes());
 365         tableModel.addRow(rowData);
 366         addDescriptor(mbni.getDescriptor(), text);
 367         tableModel.newDataAvailable(new TableModelEvent(tableModel));
 368     }
 369 
 370     private void addMBeanConstructorInfo(MBeanConstructorInfo mbci, String text) {
 371         DefaultTableModel tableModel = (DefaultTableModel) infoTable.getModel();
 372         Object rowData[] = new Object[2];
 373         rowData[0] = new TableRowDivider(text);
 374         rowData[1] = new TableRowDivider("");
 375         tableModel.addRow(rowData);
 376         rowData[0] = Resources.getText("Name");
 377         rowData[1] = mbci.getName();
 378         tableModel.addRow(rowData);
 379         rowData[0] = Resources.getText("Description");
 380         rowData[1] = mbci.getDescription();
 381         tableModel.addRow(rowData);
 382         addDescriptor(mbci.getDescriptor(), text);
 383         tableModel.newDataAvailable(new TableModelEvent(tableModel));
 384     }
 385 
 386     private void addMBeanParameterInfo(MBeanParameterInfo mbpi, String text) {
 387         DefaultTableModel tableModel = (DefaultTableModel) infoTable.getModel();
 388         Object rowData[] = new Object[2];
 389         rowData[0] = new TableRowDivider(text);
 390         rowData[1] = new TableRowDivider("");
 391         tableModel.addRow(rowData);
 392         rowData[0] = Resources.getText("Name");
 393         rowData[1] = mbpi.getName();
 394         tableModel.addRow(rowData);
 395         rowData[0] = Resources.getText("Description");
 396         rowData[1] = mbpi.getDescription();
 397         tableModel.addRow(rowData);
 398         rowData[0] = Resources.getText("Type");
 399         rowData[1] = mbpi.getType();
 400         tableModel.addRow(rowData);
 401         addDescriptor(mbpi.getDescriptor(), text);
 402         tableModel.newDataAvailable(new TableModelEvent(tableModel));
 403     }
 404 
 405     public static void loadInfo(DefaultMutableTreeNode root) {
 406         // Retrieve XMBean from XNodeInfo
 407         //
 408         XMBean mbean = (XMBean) ((XNodeInfo) root.getUserObject()).getData();
 409         // Initialize MBean*Info
 410         //
 411         final MBeanInfo mbeanInfo;
 412         try {
 413             mbeanInfo = mbean.getMBeanInfo();
 414         } catch (Exception e) {
 415             if (JConsole.isDebug()) {
 416                 e.printStackTrace();
 417             }
 418             return;
 419         }
 420         MBeanAttributeInfo[] ai = mbeanInfo.getAttributes();
 421         MBeanOperationInfo[] oi = mbeanInfo.getOperations();
 422         MBeanNotificationInfo[] ni = mbeanInfo.getNotifications();
 423         // MBeanAttributeInfo node
 424         //
 425         if (ai != null && ai.length > 0) {
 426             DefaultMutableTreeNode attributes = new DefaultMutableTreeNode();
 427             XNodeInfo attributesUO = new XNodeInfo(Type.ATTRIBUTES, mbean,
 428                     Resources.getText("Attributes"), null);
 429             attributes.setUserObject(attributesUO);
 430             root.add(attributes);
 431             for (MBeanAttributeInfo mbai : ai) {
 432                 DefaultMutableTreeNode attribute = new DefaultMutableTreeNode();
 433                 XNodeInfo attributeUO = new XNodeInfo(Type.ATTRIBUTE,
 434                         new Object[] {mbean, mbai}, mbai.getName(), null);
 435                 attribute.setUserObject(attributeUO);
 436                 attributes.add(attribute);
 437             }
 438         }
 439         // MBeanOperationInfo node
 440         //
 441         if (oi != null && oi.length > 0) {
 442             DefaultMutableTreeNode operations = new DefaultMutableTreeNode();
 443             XNodeInfo operationsUO = new XNodeInfo(Type.OPERATIONS, mbean,
 444                     Resources.getText("Operations"), null);
 445             operations.setUserObject(operationsUO);
 446             root.add(operations);
 447             for (MBeanOperationInfo mboi : oi) {
 448                 // Compute the operation's tool tip text:
 449                 // "operationname(param1type,param2type,...)"
 450                 //
 451                 StringBuilder sb = new StringBuilder();
 452                 for (MBeanParameterInfo mbpi : mboi.getSignature()) {
 453                     sb.append(mbpi.getType() + ",");
 454                 }
 455                 String signature = sb.toString();
 456                 if (signature.length() > 0) {
 457                     // Remove the trailing ','
 458                     //
 459                     signature = signature.substring(0, signature.length() - 1);
 460                 }
 461                 String toolTipText = mboi.getName() + "(" + signature + ")";
 462                 // Create operation node
 463                 //
 464                 DefaultMutableTreeNode operation = new DefaultMutableTreeNode();
 465                 XNodeInfo operationUO = new XNodeInfo(Type.OPERATION,
 466                         new Object[] {mbean, mboi}, mboi.getName(), toolTipText);
 467                 operation.setUserObject(operationUO);
 468                 operations.add(operation);
 469             }
 470         }
 471         // MBeanNotificationInfo node
 472         //
 473         if (mbean.isBroadcaster()) {
 474             DefaultMutableTreeNode notifications = new DefaultMutableTreeNode();
 475             XNodeInfo notificationsUO = new XNodeInfo(Type.NOTIFICATIONS, mbean,
 476                     Resources.getText("Notifications"), null);
 477             notifications.setUserObject(notificationsUO);
 478             root.add(notifications);
 479             if (ni != null && ni.length > 0) {
 480                 for (MBeanNotificationInfo mbni : ni) {
 481                     DefaultMutableTreeNode notification =
 482                             new DefaultMutableTreeNode();
 483                     XNodeInfo notificationUO = new XNodeInfo(Type.NOTIFICATION,
 484                             mbni, mbni.getName(), null);
 485                     notification.setUserObject(notificationUO);
 486                     notifications.add(notification);
 487                 }
 488             }
 489         }
 490     }
 491 }