| /* |
| * 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 |
| * under the terms of the GNU General Public License version 2 only, as |
| * published by the Free Software Foundation. Sun designates this |
| * particular file as subject to the "Classpath" exception as provided |
| * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, |
| * 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.*; |
| 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.*; |
| import java.lang.reflect.Array; |
| |
| import javax.management.*; |
| import javax.management.openmbean.CompositeData; |
| import javax.management.openmbean.TabularData; |
| |
| import sun.tools.jconsole.Resources; |
| |
| @SuppressWarnings("serial") |
| public class XMBeanNotifications extends JTable implements NotificationListener { |
| |
| 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")}; |
| |
| private HashMap<ObjectName, XMBeanNotificationsListener> listeners = |
| new HashMap<ObjectName, XMBeanNotificationsListener>(); |
| private boolean subscribed; |
| private XMBeanNotificationsListener currentListener; |
| public final static String NOTIFICATION_RECEIVED_EVENT = |
| "jconsole.xnotification.received"; |
| |
| private List<NotificationListener> notificationListenersList; |
| private 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)); |
| setColumnSelectionAllowed(false); |
| setRowSelectionAllowed(false); |
| getTableHeader().setReorderingAllowed(false); |
| ArrayList<NotificationListener> l = |
| new ArrayList<NotificationListener>(1); |
| notificationListenersList = Collections.synchronizedList(l); |
| |
| addMouseListener(mouseListener); |
| |
| TableColumnModel colModel = getColumnModel(); |
| colModel.getColumn(0).setPreferredWidth(45); |
| colModel.getColumn(1).setPreferredWidth(50); |
| colModel.getColumn(2).setPreferredWidth(50); |
| colModel.getColumn(3).setPreferredWidth(40); |
| colModel.getColumn(4).setPreferredWidth(50); |
| colModel.getColumn(5).setPreferredWidth(50); |
| setColumnEditors(); |
| addKeyListener(new Utils.CopyKeyAdapter()); |
| } |
| |
| public void cancelCellEditing() { |
| TableCellEditor editor = getCellEditor(); |
| if (editor != null) { |
| editor.cancelCellEditing(); |
| } |
| } |
| |
| public void stopCellEditing() { |
| TableCellEditor editor = getCellEditor(); |
| if (editor != null) { |
| editor.stopCellEditing(); |
| } |
| } |
| |
| public boolean isCellEditable(int row, int col) { |
| UserDataCell cell = getUserDataCell(row, col); |
| if (cell != null) { |
| return cell.isMaximized(); |
| } |
| return true; |
| } |
| |
| public void setValueAt(Object value, int row, int column) { |
| } |
| |
| 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()) |
| return null; |
| |
| Component comp = super.prepareRenderer(renderer, row, column); |
| |
| if (normalFont == null) { |
| normalFont = comp.getFont(); |
| boldFont = normalFont.deriveFont(Font.BOLD); |
| } |
| UserDataCell cell = getUserDataCell(row, 2); |
| if (column == 2 && cell != null) { |
| comp.setFont(boldFont); |
| int size = cell.getHeight(); |
| if(size > 0) { |
| if(getRowHeight(row) != size) |
| setRowHeight(row, size); |
| } |
| } else { |
| comp.setFont(normalFont); |
| } |
| |
| return comp; |
| } |
| |
| 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()) |
| return null; |
| |
| DefaultTableCellRenderer renderer; |
| String toolTip = null; |
| UserDataCell cell = getUserDataCell(row, column); |
| if(cell != null && cell.isInited()) { |
| renderer = (DefaultTableCellRenderer) cell.getRenderer(); |
| } |
| else { |
| renderer = (DefaultTableCellRenderer) |
| super.getCellRenderer(row, |
| column); |
| } |
| |
| if(cell != null) |
| toolTip = Resources.getText("Double click to expand/collapse")+". " |
| + cell.toString(); |
| else { |
| Object val = |
| ((DefaultTableModel) getModel()).getValueAt(row,column); |
| if(val != null) |
| toolTip = val.toString(); |
| } |
| |
| renderer.setToolTipText(toolTip); |
| |
| return renderer; |
| } |
| |
| private UserDataCell getUserDataCell(int row, int column) { |
| Object obj = ((DefaultTableModel) getModel()).getValueAt(row,column); |
| if(obj instanceof UserDataCell) return (UserDataCell) obj; |
| return null; |
| } |
| |
| synchronized void dispose() { |
| listeners.clear(); |
| } |
| |
| public long getReceivedNotifications(XMBean mbean) { |
| XMBeanNotificationsListener listener = |
| listeners.get(mbean.getObjectName()); |
| if(listener == null) return 0; |
| else |
| return listener.getReceivedNotifications(); |
| } |
| |
| public synchronized boolean clearCurrentNotifications() { |
| emptyTable(); |
| if(currentListener != null) { |
| currentListener.clear(); |
| return true; |
| } else |
| return false; |
| } |
| |
| public synchronized boolean unregisterListener(DefaultMutableTreeNode node) { |
| XMBean mbean = (XMBean) ((XNodeInfo) node.getUserObject()).getData(); |
| return unregister(mbean.getObjectName()); |
| } |
| |
| public synchronized void registerListener(DefaultMutableTreeNode node) |
| throws InstanceNotFoundException, IOException { |
| XMBean mbean = (XMBean) ((XNodeInfo) node.getUserObject()).getData(); |
| if(!subscribed) { |
| try { |
| mbean.getMBeanServerConnection(). |
| addNotificationListener(new ObjectName("JMImplementation:type=MBeanServerDelegate"), |
| this, |
| null, |
| null); |
| subscribed = true; |
| }catch(Exception e) { |
| System.out.println("Error adding listener for delegate :"+ |
| e.getMessage()); |
| } |
| } |
| |
| XMBeanNotificationsListener listener = |
| listeners.get(mbean.getObjectName()); |
| if (listener == null) { |
| listener = new XMBeanNotificationsListener(this, |
| mbean, |
| node, |
| columnNames); |
| listeners.put(mbean.getObjectName(), listener); |
| } else { |
| if (!listener.isRegistered()) { |
| emptyTable(); |
| listener.register(node); |
| } |
| } |
| enabled = true; |
| currentListener = listener; |
| } |
| |
| 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){ |
| unregister(mbean); |
| } |
| } |
| } catch(Exception e) { |
| System.out.println("Error unregistering notification:"+ |
| e.getMessage()); |
| } |
| } |
| |
| public synchronized void disableNotifications() { |
| emptyTable(); |
| currentListener = null; |
| enabled = false; |
| } |
| |
| private synchronized boolean unregister(ObjectName mbean) { |
| XMBeanNotificationsListener listener = listeners.get(mbean); |
| if(listener != null && listener.isRegistered()) { |
| listener.unregister(); |
| return true; |
| } else |
| return false; |
| } |
| |
| public void addNotificationsListener(NotificationListener 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) { |
| DefaultTableModel tableModel = (DefaultTableModel) getModel(); |
| if(listener == currentListener) { |
| |
| //tableModel.addRow(rowData); |
| 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); |
| } |
| |
| private void updateModel(List<Object[]> data) { |
| emptyTable(); |
| DefaultTableModel tableModel = (DefaultTableModel) getModel(); |
| for(Object[] rowData : data) |
| tableModel.addRow(rowData); |
| } |
| |
| public synchronized boolean isListenerRegistered(XMBean mbean) { |
| XMBeanNotificationsListener listener = |
| listeners.get(mbean.getObjectName()); |
| if(listener == null) return false; |
| return listener.isRegistered(); |
| } |
| |
| public synchronized void loadNotifications(XMBean mbean) { |
| XMBeanNotificationsListener listener = |
| listeners.get(mbean.getObjectName()); |
| emptyTable(); |
| if(listener != null ) { |
| enabled = true; |
| List<Object[]> data = listener.getData(); |
| updateModel(data); |
| currentListener = listener; |
| validate(); |
| repaint(); |
| } else |
| enabled = false; |
| } |
| |
| private void setColumnEditors() { |
| TableColumnModel tcm = getColumnModel(); |
| for (int i = 0; i < columnNames.length; i++) { |
| TableColumn tc = tcm.getColumn(i); |
| if (i == 2) { |
| tc.setCellEditor(userDataEditor); |
| } else { |
| tc.setCellEditor(editor); |
| } |
| } |
| } |
| |
| public boolean isTableEditable() { |
| return true; |
| } |
| |
| public synchronized void emptyTable() { |
| DefaultTableModel model = (DefaultTableModel)getModel(); |
| //invalidate(); |
| while (model.getRowCount()>0) |
| model.removeRow(0); |
| validate(); |
| } |
| |
| synchronized void updateUserDataCell(int row, |
| int col) { |
| Object obj = getModel().getValueAt(row, 2); |
| if(obj instanceof UserDataCell) { |
| UserDataCell cell = (UserDataCell) obj; |
| if(!cell.isInited()) { |
| if(rowMinHeight == -1) |
| rowMinHeight = getRowHeight(row); |
| |
| cell.init(super.getCellRenderer(row, col), |
| rowMinHeight); |
| } |
| |
| cell.switchState(); |
| setRowHeight(row, |
| cell.getHeight()); |
| |
| if(!cell.isMaximized()) { |
| cancelCellEditing(); |
| //Back to simple editor. |
| editCellAt(row, |
| 2); |
| } |
| |
| invalidate(); |
| repaint(); |
| } |
| } |
| |
| class UserDataCellRenderer extends DefaultTableCellRenderer { |
| Component comp; |
| UserDataCellRenderer(Component comp) { |
| this.comp = comp; |
| Dimension d = comp.getPreferredSize(); |
| if (d.getHeight() > 200) { |
| comp.setPreferredSize(new Dimension((int) d.getWidth(), 200)); |
| } |
| } |
| |
| 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); |
| |
| } |
| |
| 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) { |
| this.minRenderer = minRenderer; |
| this.minHeight = minHeight; |
| init = true; |
| } |
| |
| void switchState() { |
| minimized = !minimized; |
| } |
| boolean isMaximized() { |
| return !minimized; |
| } |
| void minimize() { |
| minimized = true; |
| } |
| |
| void maximize() { |
| minimized = false; |
| } |
| |
| int getHeight() { |
| if(minimized) return minHeight; |
| else |
| return (int) maxRenderer.getComponent(). |
| getPreferredSize().getHeight() ; |
| } |
| |
| TableCellRenderer getRenderer() { |
| if(minimized) return minRenderer; |
| else return maxRenderer; |
| } |
| } |
| |
| class NotifMouseListener extends MouseAdapter { |
| |
| public void mousePressed(MouseEvent e) { |
| 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; |
| |
| XMBeanNotifications.this.updateUserDataCell(row, |
| col); |
| } |
| } |
| } |
| } |
| |
| class UserDataCellEditor extends XTextFieldEditor { |
| // implements javax.swing.table.TableCellEditor |
| 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) { |
| UserDataCell cell = (UserDataCell) obj; |
| if(cell.getRenderer() instanceof UserDataCellRenderer) { |
| UserDataCellRenderer zr = |
| (UserDataCellRenderer) cell.getRenderer(); |
| return zr.getComponent(); |
| } |
| } else { |
| Component comp = super.getTableCellEditorComponent( |
| table, val, isSelected, row, column); |
| textField.setEditable(false); |
| return comp; |
| } |
| } |
| return super.getTableCellEditorComponent(table, |
| val, |
| isSelected, |
| row, |
| column); |
| } |
| @Override |
| public boolean stopCellEditing() { |
| int editingRow = getEditingRow(); |
| int editingColumn = getEditingColumn(); |
| if (editingColumn == 2) { |
| Object obj = getModel().getValueAt(editingRow, editingColumn); |
| if (obj instanceof UserDataCell) { |
| UserDataCell cell = (UserDataCell) obj; |
| if (cell.isMaximized()) { |
| this.cancelCellEditing(); |
| return true; |
| } |
| } |
| } |
| return super.stopCellEditing(); |
| } |
| } |
| |
| class XMBeanNotificationsListener implements NotificationListener { |
| private String[] columnNames; |
| private XMBean xmbean; |
| private DefaultMutableTreeNode node; |
| private long received; |
| private XMBeanNotifications notifications; |
| private boolean unregistered; |
| private ArrayList<Object[]> data = new ArrayList<Object[]>(); |
| public XMBeanNotificationsListener(XMBeanNotifications notifications, |
| XMBean xmbean, |
| DefaultMutableTreeNode node, |
| String[] columnNames) { |
| this.notifications = notifications; |
| this.xmbean = xmbean; |
| this.node = node; |
| this.columnNames = columnNames; |
| register(node); |
| } |
| |
| public synchronized List<Object[]> getData() { |
| return data; |
| } |
| |
| public synchronized void clear() { |
| data.clear(); |
| received = 0; |
| } |
| |
| public 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()); |
| } |
| unregistered = true; |
| } |
| |
| public long getReceivedNotifications() { |
| return received; |
| } |
| |
| public synchronized void register(DefaultMutableTreeNode node) { |
| clear(); |
| this.node = node; |
| try { |
| xmbean.getMBeanServerConnection(). |
| addNotificationListener(xmbean.getObjectName(),this,null,null); |
| unregistered = false; |
| }catch(Exception e) { |
| System.out.println("Error adding listener :"+ |
| e.getMessage()); |
| } |
| } |
| |
| public synchronized void handleNotification(Notification e, |
| Object handback) { |
| try { |
| if(unregistered) return; |
| Date receivedDate = new Date(e.getTimeStamp()); |
| String time = timeFormater.format(receivedDate); |
| |
| 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); |
| } |
| |
| 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()); |
| } |
| } |
| } |
| } |