| 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 */  | 
 | 
 | 
 | 
package sun.tools.jconsole.inspector;  | 
 | 
 | 
 | 
 | 
 | 
import java.awt.Component;  | 
 | 
import java.awt.EventQueue;  | 
 | 
import java.awt.Dimension;  | 
 | 
import java.awt.event.MouseAdapter;  | 
 | 
import java.awt.event.MouseEvent;  | 
 | 
import java.io.IOException;  | 
 | 
 | 
 | 
import java.lang.reflect.Array;  | 
 | 
 | 
 | 
import java.util.EventObject;  | 
 | 
import java.util.HashMap;  | 
 | 
import java.util.WeakHashMap;  | 
 | 
 | 
 | 
import java.util.concurrent.ExecutionException;  | 
 | 
import java.util.logging.Level;  | 
 | 
import java.util.logging.Logger;  | 
 | 
import javax.management.JMException;  | 
 | 
import javax.management.MBeanInfo;  | 
 | 
import javax.management.MBeanAttributeInfo;  | 
 | 
import javax.management.AttributeList;  | 
 | 
import javax.management.Attribute;  | 
 | 
import javax.management.openmbean.CompositeData;  | 
 | 
import javax.management.openmbean.TabularData;  | 
 | 
 | 
 | 
import javax.swing.JComponent;  | 
 | 
import javax.swing.JOptionPane;  | 
 | 
import javax.swing.JTable;  | 
 | 
import javax.swing.JTextField;  | 
 | 
import javax.swing.SwingWorker;  | 
 | 
import javax.swing.event.ChangeEvent;  | 
 | 
import javax.swing.event.TableModelEvent;  | 
 | 
import javax.swing.event.TableModelListener;  | 
 | 
import javax.swing.table.DefaultTableCellRenderer;  | 
 | 
import javax.swing.table.DefaultTableModel;  | 
 | 
import javax.swing.table.TableCellEditor;  | 
 | 
import javax.swing.table.TableCellRenderer;  | 
 | 
import javax.swing.table.TableColumn;  | 
 | 
import javax.swing.table.TableColumnModel;  | 
 | 
import javax.swing.table.TableModel;  | 
 | 
 | 
 | 
import sun.tools.jconsole.MBeansTab;  | 
 | 
import sun.tools.jconsole.JConsole;  | 
 | 
import sun.tools.jconsole.Messages;  | 
 | 
import sun.tools.jconsole.ProxyClient.SnapshotMBeanServerConnection;  | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
  COMPULSORY to not call the JMX world in synchronized blocks */  | 
 | 
@SuppressWarnings("serial") | 
 | 
public class XMBeanAttributes extends XTable { | 
 | 
 | 
 | 
    final Logger LOGGER =  | 
 | 
            Logger.getLogger(XMBeanAttributes.class.getPackage().getName());  | 
 | 
 | 
 | 
    private final static String[] columnNames =  | 
 | 
    {Messages.NAME, | 
 | 
     Messages.VALUE};  | 
 | 
 | 
 | 
    private XMBean mbean;  | 
 | 
    private MBeanInfo mbeanInfo;  | 
 | 
    private MBeanAttributeInfo[] attributesInfo;  | 
 | 
    private HashMap<String, Object> attributes;  | 
 | 
    private HashMap<String, Object> unavailableAttributes;  | 
 | 
    private HashMap<String, Object> viewableAttributes;  | 
 | 
    private WeakHashMap<XMBean, HashMap<String, ZoomedCell>> viewersCache =  | 
 | 
            new WeakHashMap<XMBean, HashMap<String, ZoomedCell>>();  | 
 | 
    private final TableModelListener attributesListener;  | 
 | 
    private MBeansTab mbeansTab;  | 
 | 
    private TableCellEditor valueCellEditor = new ValueCellEditor();  | 
 | 
    private int rowMinHeight = -1;  | 
 | 
    private AttributesMouseListener mouseListener = new AttributesMouseListener();  | 
 | 
 | 
 | 
    private static TableCellEditor editor =  | 
 | 
            new Utils.ReadOnlyTableCellEditor(new JTextField());  | 
 | 
 | 
 | 
    public XMBeanAttributes(MBeansTab mbeansTab) { | 
 | 
        super();  | 
 | 
        this.mbeansTab = mbeansTab;  | 
 | 
        ((DefaultTableModel)getModel()).setColumnIdentifiers(columnNames);  | 
 | 
        attributesListener = new AttributesListener(this);  | 
 | 
        getModel().addTableModelListener(attributesListener);  | 
 | 
        getColumnModel().getColumn(NAME_COLUMN).setPreferredWidth(40);  | 
 | 
 | 
 | 
        addMouseListener(mouseListener);  | 
 | 
        getTableHeader().setReorderingAllowed(false);  | 
 | 
        setColumnEditors();  | 
 | 
        addKeyListener(new Utils.CopyKeyAdapter());  | 
 | 
    }  | 
 | 
 | 
 | 
    @Override  | 
 | 
    public synchronized Component prepareRenderer(TableCellRenderer renderer,  | 
 | 
                                                  int row, int column) { | 
 | 
        //In case we have a repaint thread that is in the process of  | 
 | 
        //repainting an obsolete table, just ignore the call.  | 
 | 
          | 
 | 
        if(row >= getRowCount())  | 
 | 
            return null;  | 
 | 
        else  | 
 | 
            return super.prepareRenderer(renderer, row, column);  | 
 | 
    }  | 
 | 
 | 
 | 
    void updateRowHeight(Object obj, int row) { | 
 | 
        ZoomedCell cell = null;  | 
 | 
        if(obj instanceof ZoomedCell) { | 
 | 
            cell = (ZoomedCell) obj;  | 
 | 
            if(cell.isInited())  | 
 | 
                setRowHeight(row, cell.getHeight());  | 
 | 
            else  | 
 | 
                if(rowMinHeight != - 1)  | 
 | 
                    setRowHeight(row, rowMinHeight);  | 
 | 
        } else  | 
 | 
            if(rowMinHeight != - 1)  | 
 | 
                setRowHeight(row, rowMinHeight);  | 
 | 
    }  | 
 | 
 | 
 | 
    @Override  | 
 | 
    public synchronized TableCellRenderer getCellRenderer(int row,  | 
 | 
            int column) { | 
 | 
        //In case we have a repaint thread that is in the process of  | 
 | 
        //repainting an obsolete table, just ignore the call.  | 
 | 
          | 
 | 
        if (row >= getRowCount()) { | 
 | 
            return null;  | 
 | 
        } else { | 
 | 
            if (column == VALUE_COLUMN) { | 
 | 
                Object obj = getModel().getValueAt(row, column);  | 
 | 
                if (obj instanceof ZoomedCell) { | 
 | 
                    ZoomedCell cell = (ZoomedCell) obj;  | 
 | 
                    if (cell.isInited()) { | 
 | 
                        DefaultTableCellRenderer renderer =  | 
 | 
                                (DefaultTableCellRenderer) cell.getRenderer();  | 
 | 
                        renderer.setToolTipText(getToolTip(row,column));  | 
 | 
                        return renderer;  | 
 | 
                    }  | 
 | 
                }  | 
 | 
            }  | 
 | 
            DefaultTableCellRenderer renderer = (DefaultTableCellRenderer)  | 
 | 
                super.getCellRenderer(row, column);  | 
 | 
            if (!isCellError(row, column)) { | 
 | 
                if (!(isColumnEditable(column) && isWritable(row) &&  | 
 | 
                      Utils.isEditableType(getClassName(row)))) { | 
 | 
                    renderer.setForeground(getDefaultColor());  | 
 | 
                }  | 
 | 
            }  | 
 | 
            return renderer;  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
    private void setColumnEditors() { | 
 | 
        TableColumnModel tcm = getColumnModel();  | 
 | 
        for (int i = 0; i < columnNames.length; i++) { | 
 | 
            TableColumn tc = tcm.getColumn(i);  | 
 | 
            if (isColumnEditable(i)) { | 
 | 
                tc.setCellEditor(valueCellEditor);  | 
 | 
            } else { | 
 | 
                tc.setCellEditor(editor);  | 
 | 
            }  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
    public void cancelCellEditing() { | 
 | 
        if (LOGGER.isLoggable(Level.FINER)) { | 
 | 
            LOGGER.finer("Cancel Editing Row: "+getEditingRow()); | 
 | 
        }  | 
 | 
        final TableCellEditor tableCellEditor = getCellEditor();  | 
 | 
        if (tableCellEditor != null) { | 
 | 
            tableCellEditor.cancelCellEditing();  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
    public void stopCellEditing() { | 
 | 
        if (LOGGER.isLoggable(Level.FINER)) { | 
 | 
            LOGGER.finer("Stop Editing Row: "+getEditingRow()); | 
 | 
        }  | 
 | 
        final TableCellEditor tableCellEditor = getCellEditor();  | 
 | 
        if (tableCellEditor != null) { | 
 | 
            tableCellEditor.stopCellEditing();  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
    @Override  | 
 | 
    public final boolean editCellAt(final int row, final int column, EventObject e) { | 
 | 
        if (LOGGER.isLoggable(Level.FINER)) { | 
 | 
            LOGGER.finer("editCellAt(row="+row+", col="+column+ | 
 | 
                    ", e="+e+")");  | 
 | 
        }  | 
 | 
        if (JConsole.isDebug()) { | 
 | 
            System.err.println("edit: "+getValueName(row)+"="+getValue(row)); | 
 | 
        }  | 
 | 
        boolean retVal = super.editCellAt(row, column, e);  | 
 | 
        if (retVal) { | 
 | 
            final TableCellEditor tableCellEditor =  | 
 | 
                    getColumnModel().getColumn(column).getCellEditor();  | 
 | 
            if (tableCellEditor == valueCellEditor) { | 
 | 
                ((JComponent) tableCellEditor).requestFocus();  | 
 | 
            }  | 
 | 
        }  | 
 | 
        return retVal;  | 
 | 
    }  | 
 | 
 | 
 | 
    @Override  | 
 | 
    public boolean isCellEditable(int row, int col) { | 
 | 
          | 
 | 
        if (!isColumnEditable(col)) { | 
 | 
            return true;  | 
 | 
        }  | 
 | 
          | 
 | 
        Object obj = getModel().getValueAt(row, col);  | 
 | 
        if (obj instanceof ZoomedCell) { | 
 | 
            ZoomedCell cell = (ZoomedCell) obj;  | 
 | 
            return cell.isMaximized();  | 
 | 
        }  | 
 | 
        return true;  | 
 | 
    }  | 
 | 
 | 
 | 
    @Override  | 
 | 
    public void setValueAt(Object value, int row, int column) { | 
 | 
        if (!isCellError(row, column) && isColumnEditable(column) &&  | 
 | 
            isWritable(row) && Utils.isEditableType(getClassName(row))) { | 
 | 
            if (JConsole.isDebug()) { | 
 | 
                System.err.println("validating [row="+row+", column="+column+ | 
 | 
                        "]: "+getValueName(row)+"="+value);  | 
 | 
            }  | 
 | 
            super.setValueAt(value, row, column);  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
    //Table methods  | 
 | 
 | 
 | 
    public boolean isTableEditable() { | 
 | 
        return true;  | 
 | 
    }  | 
 | 
 | 
 | 
    public void setTableValue(Object value, int row) { | 
 | 
    }  | 
 | 
 | 
 | 
    public boolean isColumnEditable(int column) { | 
 | 
        if (column < getColumnCount()) { | 
 | 
            return getColumnName(column).equals(Messages.VALUE);  | 
 | 
        }  | 
 | 
        else { | 
 | 
            return false;  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
    public String getClassName(int row) { | 
 | 
        int index = convertRowToIndex(row);  | 
 | 
        if (index != -1) { | 
 | 
            return attributesInfo[index].getType();  | 
 | 
        }  | 
 | 
        else { | 
 | 
            return null;  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
 | 
 | 
    public String getValueName(int row) { | 
 | 
        int index = convertRowToIndex(row);  | 
 | 
        if (index != -1) { | 
 | 
            return attributesInfo[index].getName();  | 
 | 
        }  | 
 | 
        else { | 
 | 
            return null;  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
    public Object getValue(int row) { | 
 | 
        final Object val = ((DefaultTableModel) getModel())  | 
 | 
                .getValueAt(row, VALUE_COLUMN);  | 
 | 
        return val;  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
    @Override  | 
 | 
    public String getToolTip(int row, int column) { | 
 | 
        if (isCellError(row, column)) { | 
 | 
            return (String) unavailableAttributes.get(getValueName(row));  | 
 | 
        }  | 
 | 
        if (isColumnEditable(column)) { | 
 | 
            Object value = getValue(row);  | 
 | 
            String tip = null;  | 
 | 
            if (value != null) { | 
 | 
                tip = value.toString();  | 
 | 
                if(isAttributeViewable(row, VALUE_COLUMN))  | 
 | 
                    tip = Messages.DOUBLE_CLICK_TO_EXPAND_FORWARD_SLASH_COLLAPSE+  | 
 | 
                        ". " + tip;  | 
 | 
            }  | 
 | 
 | 
 | 
            return tip;  | 
 | 
        }  | 
 | 
 | 
 | 
        if(column == NAME_COLUMN) { | 
 | 
            int index = convertRowToIndex(row);  | 
 | 
            if (index != -1) { | 
 | 
                return attributesInfo[index].getDescription();  | 
 | 
            }  | 
 | 
        }  | 
 | 
        return null;  | 
 | 
    }  | 
 | 
 | 
 | 
    public synchronized boolean isWritable(int row) { | 
 | 
        int index = convertRowToIndex(row);  | 
 | 
        if (index != -1) { | 
 | 
            return (attributesInfo[index].isWritable());  | 
 | 
        }  | 
 | 
        else { | 
 | 
            return false;  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    @Override  | 
 | 
    public synchronized int getRowCount() { | 
 | 
        return super.getRowCount();  | 
 | 
    }  | 
 | 
 | 
 | 
    public synchronized boolean isReadable(int row) { | 
 | 
        int index = convertRowToIndex(row);  | 
 | 
        if (index != -1) { | 
 | 
            return (attributesInfo[index].isReadable());  | 
 | 
        }  | 
 | 
        else { | 
 | 
            return false;  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
    public synchronized boolean isCellError(int row, int col) { | 
 | 
        return (isColumnEditable(col) &&  | 
 | 
                (unavailableAttributes.containsKey(getValueName(row))));  | 
 | 
    }  | 
 | 
 | 
 | 
    public synchronized boolean isAttributeViewable(int row, int col) { | 
 | 
        boolean isViewable = false;  | 
 | 
        if(col == VALUE_COLUMN) { | 
 | 
            Object obj = getModel().getValueAt(row, col);  | 
 | 
            if(obj instanceof ZoomedCell)  | 
 | 
                isViewable = true;  | 
 | 
        }  | 
 | 
 | 
 | 
        return isViewable;  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
    public void loadAttributes(final XMBean mbean, final MBeanInfo mbeanInfo) { | 
 | 
 | 
 | 
        final SwingWorker<Runnable,Void> load =  | 
 | 
                new SwingWorker<Runnable,Void>() { | 
 | 
            @Override  | 
 | 
            protected Runnable doInBackground() throws Exception { | 
 | 
                return doLoadAttributes(mbean,mbeanInfo);  | 
 | 
            }  | 
 | 
 | 
 | 
            @Override  | 
 | 
            protected void done() { | 
 | 
                try { | 
 | 
                    final Runnable updateUI = get();  | 
 | 
                    if (updateUI != null) updateUI.run();  | 
 | 
                } catch (RuntimeException x) { | 
 | 
                    throw x;  | 
 | 
                } catch (ExecutionException x) { | 
 | 
                    if(JConsole.isDebug()) { | 
 | 
                       System.err.println(  | 
 | 
                               "Exception raised while loading attributes: "  | 
 | 
                               +x.getCause());  | 
 | 
                       x.printStackTrace();  | 
 | 
                    }  | 
 | 
                } catch (InterruptedException x) { | 
 | 
                    if(JConsole.isDebug()) { | 
 | 
                       System.err.println(  | 
 | 
                            "Interrupted while loading attributes: "+x);  | 
 | 
                       x.printStackTrace();  | 
 | 
                    }  | 
 | 
                }  | 
 | 
            }  | 
 | 
 | 
 | 
        };  | 
 | 
        mbeansTab.workerAdd(load);  | 
 | 
    }  | 
 | 
 | 
 | 
    // Don't call this in EDT, but execute returned Runnable inside  | 
 | 
    // EDT - typically in the done() method of a SwingWorker  | 
 | 
      | 
 | 
    private Runnable doLoadAttributes(final XMBean mbean, MBeanInfo infoOrNull)  | 
 | 
        throws JMException, IOException { | 
 | 
        // To avoid deadlock with events coming from the JMX side,  | 
 | 
        // we retrieve all JMX stuff in a non synchronized block.  | 
 | 
 | 
 | 
        if(mbean == null) return null;  | 
 | 
        final MBeanInfo curMBeanInfo =  | 
 | 
                (infoOrNull==null)?mbean.getMBeanInfo():infoOrNull;  | 
 | 
 | 
 | 
        final MBeanAttributeInfo[] attrsInfo = curMBeanInfo.getAttributes();  | 
 | 
        final HashMap<String, Object> attrs =  | 
 | 
            new HashMap<String, Object>(attrsInfo.length);  | 
 | 
        final HashMap<String, Object> unavailableAttrs =  | 
 | 
            new HashMap<String, Object>(attrsInfo.length);  | 
 | 
        final HashMap<String, Object> viewableAttrs =  | 
 | 
            new HashMap<String, Object>(attrsInfo.length);  | 
 | 
        AttributeList list = null;  | 
 | 
 | 
 | 
        try { | 
 | 
            list = mbean.getAttributes(attrsInfo);  | 
 | 
        }catch(Exception e) { | 
 | 
            if (JConsole.isDebug()) { | 
 | 
                System.err.println("Error calling getAttributes() on MBean \"" + | 
 | 
                                   mbean.getObjectName() + "\". JConsole will " +  | 
 | 
                                   "try to get them individually calling " +  | 
 | 
                                   "getAttribute() instead. Exception:");  | 
 | 
                e.printStackTrace(System.err);  | 
 | 
            }  | 
 | 
            list = new AttributeList();  | 
 | 
              | 
 | 
            for(int i = 0; i < attrsInfo.length; i++) { | 
 | 
                String name = null;  | 
 | 
                try { | 
 | 
                    name = attrsInfo[i].getName();  | 
 | 
                    Object value =  | 
 | 
                        mbean.getMBeanServerConnection().  | 
 | 
                        getAttribute(mbean.getObjectName(), name);  | 
 | 
                    list.add(new Attribute(name, value));  | 
 | 
                }catch(Exception ex) { | 
 | 
                    if(attrsInfo[i].isReadable()) { | 
 | 
                        unavailableAttrs.put(name,  | 
 | 
                                Utils.getActualException(ex).toString());  | 
 | 
                    }  | 
 | 
                }  | 
 | 
            }  | 
 | 
        }  | 
 | 
        try { | 
 | 
            int att_length = list.size();  | 
 | 
            for (int i=0;i<att_length;i++) { | 
 | 
                Attribute attribute = (Attribute) list.get(i);  | 
 | 
                if(isViewable(attribute)) { | 
 | 
                    viewableAttrs.put(attribute.getName(),  | 
 | 
                                           attribute.getValue());  | 
 | 
                }  | 
 | 
                else  | 
 | 
                    attrs.put(attribute.getName(),attribute.getValue());  | 
 | 
 | 
 | 
            }  | 
 | 
            // if not all attributes are accessible,  | 
 | 
              | 
 | 
            if (att_length < attrsInfo.length) { | 
 | 
                for (int i=0;i<attrsInfo.length;i++) { | 
 | 
                    MBeanAttributeInfo attributeInfo = attrsInfo[i];  | 
 | 
                    if (!attrs.containsKey(attributeInfo.getName()) &&  | 
 | 
                        !viewableAttrs.containsKey(attributeInfo.  | 
 | 
                                                        getName()) &&  | 
 | 
                        !unavailableAttrs.containsKey(attributeInfo.  | 
 | 
                                                           getName())) { | 
 | 
                        if (attributeInfo.isReadable()) { | 
 | 
                            // getAttributes didn't help resolving the  | 
 | 
                            // exception.  | 
 | 
                            // We must call it again to understand what  | 
 | 
                              | 
 | 
                            try { | 
 | 
                                Object v =  | 
 | 
                                    mbean.getMBeanServerConnection().getAttribute(  | 
 | 
                                    mbean.getObjectName(), attributeInfo.getName());  | 
 | 
                                //What happens if now it is ok?  | 
 | 
                                  | 
 | 
                                attrs.put(attributeInfo.getName(),  | 
 | 
                                               v);  | 
 | 
                            }catch(Exception e) { | 
 | 
                                //Put the exception that will be displayed  | 
 | 
                                  | 
 | 
                                unavailableAttrs.put(attributeInfo.getName(),  | 
 | 
                                        Utils.getActualException(e).toString());  | 
 | 
                            }  | 
 | 
                        }  | 
 | 
                    }  | 
 | 
                }  | 
 | 
            }  | 
 | 
        }  | 
 | 
        catch(Exception e) { | 
 | 
              | 
 | 
            for (int i=0;i<attrsInfo.length;i++) { | 
 | 
                MBeanAttributeInfo attributeInfo = attrsInfo[i];  | 
 | 
                if (attributeInfo.isReadable()) { | 
 | 
                    unavailableAttrs.put(attributeInfo.getName(),  | 
 | 
                                              Utils.getActualException(e).  | 
 | 
                                              toString());  | 
 | 
                }  | 
 | 
            }  | 
 | 
        }  | 
 | 
        //end of retrieval  | 
 | 
 | 
 | 
          | 
 | 
        return new Runnable() { | 
 | 
            public void run() { | 
 | 
                synchronized (XMBeanAttributes.this) { | 
 | 
                    XMBeanAttributes.this.mbean = mbean;  | 
 | 
                    XMBeanAttributes.this.mbeanInfo = curMBeanInfo;  | 
 | 
                    XMBeanAttributes.this.attributesInfo = attrsInfo;  | 
 | 
                    XMBeanAttributes.this.attributes = attrs;  | 
 | 
                    XMBeanAttributes.this.unavailableAttributes = unavailableAttrs;  | 
 | 
                    XMBeanAttributes.this.viewableAttributes = viewableAttrs;  | 
 | 
 | 
 | 
                    DefaultTableModel tableModel =  | 
 | 
                            (DefaultTableModel) getModel();  | 
 | 
 | 
 | 
                      | 
 | 
                    emptyTable(tableModel);  | 
 | 
 | 
 | 
                    addTableData(tableModel,  | 
 | 
                            mbean,  | 
 | 
                            attrsInfo,  | 
 | 
                            attrs,  | 
 | 
                            unavailableAttrs,  | 
 | 
                            viewableAttrs);  | 
 | 
 | 
 | 
                      | 
 | 
                    tableModel.newDataAvailable(new TableModelEvent(tableModel));  | 
 | 
                      | 
 | 
                    tableModel.addTableModelListener(attributesListener);  | 
 | 
                }  | 
 | 
            }  | 
 | 
        };  | 
 | 
    }  | 
 | 
 | 
 | 
    void collapse(String attributeName, final Component c) { | 
 | 
        final int row = getSelectedRow();  | 
 | 
        Object obj = getModel().getValueAt(row, VALUE_COLUMN);  | 
 | 
        if(obj instanceof ZoomedCell) { | 
 | 
            cancelCellEditing();  | 
 | 
            ZoomedCell cell = (ZoomedCell) obj;  | 
 | 
            cell.reset();  | 
 | 
            setRowHeight(row,  | 
 | 
                         cell.getHeight());  | 
 | 
            editCellAt(row,  | 
 | 
                       VALUE_COLUMN);  | 
 | 
            invalidate();  | 
 | 
            repaint();  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
    ZoomedCell updateZoomedCell(int row,  | 
 | 
                                int col) { | 
 | 
        Object obj = getModel().getValueAt(row, VALUE_COLUMN);  | 
 | 
        ZoomedCell cell = null;  | 
 | 
        if(obj instanceof ZoomedCell) { | 
 | 
            cell = (ZoomedCell) obj;  | 
 | 
            if(!cell.isInited()) { | 
 | 
                Object elem = cell.getValue();  | 
 | 
                String attributeName =  | 
 | 
                    (String) getModel().getValueAt(row,  | 
 | 
                                                   NAME_COLUMN);  | 
 | 
                Component comp = mbeansTab.getDataViewer().  | 
 | 
                        createAttributeViewer(elem, mbean, attributeName, this);  | 
 | 
                if(comp != null){ | 
 | 
                    if(rowMinHeight == -1)  | 
 | 
                        rowMinHeight = getRowHeight(row);  | 
 | 
 | 
 | 
                    cell.init(super.getCellRenderer(row, col),  | 
 | 
                              comp,  | 
 | 
                              rowMinHeight);  | 
 | 
 | 
 | 
                    XDataViewer.registerForMouseEvent(  | 
 | 
                            comp, mouseListener);  | 
 | 
                } else  | 
 | 
                    return cell;  | 
 | 
            }  | 
 | 
 | 
 | 
            cell.switchState();  | 
 | 
            setRowHeight(row,  | 
 | 
                         cell.getHeight());  | 
 | 
 | 
 | 
            if(!cell.isMaximized()) { | 
 | 
                cancelCellEditing();  | 
 | 
                  | 
 | 
                editCellAt(row,  | 
 | 
                           VALUE_COLUMN);  | 
 | 
            }  | 
 | 
 | 
 | 
            invalidate();  | 
 | 
            repaint();  | 
 | 
        }  | 
 | 
        return cell;  | 
 | 
    }  | 
 | 
 | 
 | 
    // This is called by XSheet when the "refresh" button is pressed.  | 
 | 
    // In this case we will commit any pending attribute values by  | 
 | 
    // calling 'stopCellEditing'.  | 
 | 
      | 
 | 
    public void refreshAttributes() { | 
 | 
         refreshAttributes(true);  | 
 | 
    }  | 
 | 
 | 
 | 
    // refreshAttributes(false) is called by tableChanged().  | 
 | 
    // in this case we must not call stopCellEditing, because it's already  | 
 | 
    // been called - e.g.  | 
 | 
    // lostFocus/mousePressed -> stopCellEditing -> setValueAt -> tableChanged  | 
 | 
    //                        -> refreshAttributes(false)  | 
 | 
    //  | 
 | 
    // Can be called in EDT - as long as the implementation of  | 
 | 
    // mbeansTab.getCachedMBeanServerConnection() and mbsc.flush() doesn't  | 
 | 
    // change  | 
 | 
      | 
 | 
    private void refreshAttributes(final boolean stopCellEditing) { | 
 | 
         SwingWorker<Void,Void> sw = new SwingWorker<Void,Void>() { | 
 | 
 | 
 | 
            @Override  | 
 | 
            protected Void doInBackground() throws Exception { | 
 | 
                SnapshotMBeanServerConnection mbsc =  | 
 | 
                mbeansTab.getSnapshotMBeanServerConnection();  | 
 | 
                mbsc.flush();  | 
 | 
                return null;  | 
 | 
            }  | 
 | 
 | 
 | 
            @Override  | 
 | 
            protected void done() { | 
 | 
                try { | 
 | 
                    get();  | 
 | 
                    if (stopCellEditing) stopCellEditing();  | 
 | 
                    loadAttributes(mbean, mbeanInfo);  | 
 | 
                } catch (Exception x) { | 
 | 
                    if (JConsole.isDebug()) { | 
 | 
                        x.printStackTrace();  | 
 | 
                    }  | 
 | 
                }  | 
 | 
            }  | 
 | 
         };  | 
 | 
         mbeansTab.workerAdd(sw);  | 
 | 
     }  | 
 | 
    // We need to call stop editing here - otherwise edits are lost  | 
 | 
    // when resizing the table.  | 
 | 
      | 
 | 
    @Override  | 
 | 
    public void columnMarginChanged(ChangeEvent e) { | 
 | 
        if (isEditing()) stopCellEditing();  | 
 | 
        super.columnMarginChanged(e);  | 
 | 
    }  | 
 | 
 | 
 | 
    // We need to call stop editing here - otherwise the edited value  | 
 | 
    // is transferred to the wrong row...  | 
 | 
      | 
 | 
    @Override  | 
 | 
    void sortRequested(int column) { | 
 | 
        if (isEditing()) stopCellEditing();  | 
 | 
        super.sortRequested(column);  | 
 | 
    }  | 
 | 
 | 
 | 
 | 
 | 
    @Override  | 
 | 
    public synchronized void emptyTable() { | 
 | 
         emptyTable((DefaultTableModel)getModel());  | 
 | 
     }  | 
 | 
 | 
 | 
      | 
 | 
    private void emptyTable(DefaultTableModel model) { | 
 | 
         model.removeTableModelListener(attributesListener);  | 
 | 
         super.emptyTable();  | 
 | 
    }  | 
 | 
 | 
 | 
    private boolean isViewable(Attribute attribute) { | 
 | 
        Object data = attribute.getValue();  | 
 | 
        return XDataViewer.isViewableValue(data);  | 
 | 
 | 
 | 
    }  | 
 | 
 | 
 | 
    synchronized void removeAttributes() { | 
 | 
        if (attributes != null) { | 
 | 
            attributes.clear();  | 
 | 
        }  | 
 | 
        if (unavailableAttributes != null) { | 
 | 
            unavailableAttributes.clear();  | 
 | 
        }  | 
 | 
        if (viewableAttributes != null) { | 
 | 
            viewableAttributes.clear();  | 
 | 
        }  | 
 | 
        mbean = null;  | 
 | 
    }  | 
 | 
 | 
 | 
    private ZoomedCell getZoomedCell(XMBean mbean, String attribute, Object value) { | 
 | 
        synchronized (viewersCache) { | 
 | 
            HashMap<String, ZoomedCell> viewers;  | 
 | 
            if (viewersCache.containsKey(mbean)) { | 
 | 
                viewers = viewersCache.get(mbean);  | 
 | 
            } else { | 
 | 
                viewers = new HashMap<String, ZoomedCell>();  | 
 | 
            }  | 
 | 
            ZoomedCell cell;  | 
 | 
            if (viewers.containsKey(attribute)) { | 
 | 
                cell = viewers.get(attribute);  | 
 | 
                cell.setValue(value);  | 
 | 
                if (cell.isMaximized() && cell.getType() != XDataViewer.NUMERIC) { | 
 | 
                    // Plotters are the only viewers with auto update capabilities.  | 
 | 
                      | 
 | 
                    Component comp =  | 
 | 
                        mbeansTab.getDataViewer().createAttributeViewer(  | 
 | 
                            value, mbean, attribute, XMBeanAttributes.this);  | 
 | 
                    cell.init(cell.getMinRenderer(), comp, cell.getMinHeight());  | 
 | 
                    XDataViewer.registerForMouseEvent(comp, mouseListener);  | 
 | 
                }  | 
 | 
            } else { | 
 | 
                cell = new ZoomedCell(value);  | 
 | 
                viewers.put(attribute, cell);  | 
 | 
            }  | 
 | 
            viewersCache.put(mbean, viewers);  | 
 | 
            return cell;  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
    protected void addTableData(DefaultTableModel tableModel,  | 
 | 
                                XMBean mbean,  | 
 | 
                                MBeanAttributeInfo[] attributesInfo,  | 
 | 
                                HashMap<String, Object> attributes,  | 
 | 
                                HashMap<String, Object> unavailableAttributes,  | 
 | 
                                HashMap<String, Object> viewableAttributes) { | 
 | 
 | 
 | 
        Object rowData[] = new Object[2];  | 
 | 
        int col1Width = 0;  | 
 | 
        int col2Width = 0;  | 
 | 
        for (int i = 0; i < attributesInfo.length; i++) { | 
 | 
            rowData[0] = (attributesInfo[i].getName());  | 
 | 
            if (unavailableAttributes.containsKey(rowData[0])) { | 
 | 
                rowData[1] = Messages.UNAVAILABLE;  | 
 | 
            } else if (viewableAttributes.containsKey(rowData[0])) { | 
 | 
                rowData[1] = viewableAttributes.get(rowData[0]);  | 
 | 
                if (!attributesInfo[i].isWritable() ||  | 
 | 
                    !Utils.isEditableType(attributesInfo[i].getType())) { | 
 | 
                    rowData[1] = getZoomedCell(mbean, (String) rowData[0], rowData[1]);  | 
 | 
                }  | 
 | 
            } else { | 
 | 
                rowData[1] = attributes.get(rowData[0]);  | 
 | 
            }  | 
 | 
 | 
 | 
            tableModel.addRow(rowData);  | 
 | 
 | 
 | 
            //Update column width  | 
 | 
              | 
 | 
            String str = null;  | 
 | 
            if(rowData[0] != null) { | 
 | 
                str = rowData[0].toString();  | 
 | 
                if(str.length() > col1Width)  | 
 | 
                    col1Width = str.length();  | 
 | 
            }  | 
 | 
            if(rowData[1] != null) { | 
 | 
                str = rowData[1].toString();  | 
 | 
                if(str.length() > col2Width)  | 
 | 
                    col2Width = str.length();  | 
 | 
            }  | 
 | 
        }  | 
 | 
        updateColumnWidth(col1Width, col2Width);  | 
 | 
    }  | 
 | 
 | 
 | 
    private void updateColumnWidth(int col1Width, int col2Width) { | 
 | 
        TableColumnModel colModel = getColumnModel();  | 
 | 
 | 
 | 
          | 
 | 
        col1Width = col1Width * 7;  | 
 | 
        col2Width = col2Width * 7;  | 
 | 
        if(col1Width + col2Width <  | 
 | 
           (int) getPreferredScrollableViewportSize().getWidth())  | 
 | 
            col2Width = (int) getPreferredScrollableViewportSize().getWidth()  | 
 | 
                - col1Width;  | 
 | 
 | 
 | 
        colModel.getColumn(NAME_COLUMN).setPreferredWidth(50);  | 
 | 
    }  | 
 | 
 | 
 | 
    class AttributesMouseListener extends MouseAdapter { | 
 | 
 | 
 | 
        @Override  | 
 | 
        public void mousePressed(MouseEvent e) { | 
 | 
            if(e.getButton() == MouseEvent.BUTTON1) { | 
 | 
                if(e.getClickCount() >= 2) { | 
 | 
 | 
 | 
                    int row = XMBeanAttributes.this.getSelectedRow();  | 
 | 
                    int col = XMBeanAttributes.this.getSelectedColumn();  | 
 | 
                    if(col != VALUE_COLUMN) return;  | 
 | 
                    if(col == -1 || row == -1) return;  | 
 | 
 | 
 | 
                    XMBeanAttributes.this.updateZoomedCell(row, col);  | 
 | 
                }  | 
 | 
            }  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
    class ValueCellEditor extends XTextFieldEditor { | 
 | 
          | 
 | 
        @Override  | 
 | 
        public Component getTableCellEditorComponent(JTable table,  | 
 | 
                                                     Object value,  | 
 | 
                                                     boolean isSelected,  | 
 | 
                                                     int row,  | 
 | 
                                                     int column) { | 
 | 
            Object val = value;  | 
 | 
            if(column == VALUE_COLUMN) { | 
 | 
                Object obj = getModel().getValueAt(row,  | 
 | 
                                                   column);  | 
 | 
                if(obj instanceof ZoomedCell) { | 
 | 
                    ZoomedCell cell = (ZoomedCell) obj;  | 
 | 
                    if(cell.getRenderer() instanceof MaximizedCellRenderer) { | 
 | 
                        MaximizedCellRenderer zr =  | 
 | 
                            (MaximizedCellRenderer) cell.getRenderer();  | 
 | 
                        return zr.getComponent();  | 
 | 
                    }  | 
 | 
                } else { | 
 | 
                    Component comp = super.getTableCellEditorComponent(  | 
 | 
                            table, val, isSelected, row, column);  | 
 | 
                    if (isCellError(row, column) ||  | 
 | 
                        !isWritable(row) ||  | 
 | 
                        !Utils.isEditableType(getClassName(row))) { | 
 | 
                        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 == VALUE_COLUMN) { | 
 | 
                Object obj = getModel().getValueAt(editingRow, editingColumn);  | 
 | 
                if (obj instanceof ZoomedCell) { | 
 | 
                    ZoomedCell cell = (ZoomedCell) obj;  | 
 | 
                    if (cell.isMaximized()) { | 
 | 
                        this.cancelCellEditing();  | 
 | 
                        return true;  | 
 | 
                    }  | 
 | 
                }  | 
 | 
            }  | 
 | 
            return super.stopCellEditing();  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
    class MaximizedCellRenderer extends  DefaultTableCellRenderer { | 
 | 
        Component comp;  | 
 | 
        MaximizedCellRenderer(Component comp) { | 
 | 
            this.comp = comp;  | 
 | 
            Dimension d = comp.getPreferredSize();  | 
 | 
            if (d.getHeight() > 220) { | 
 | 
                comp.setPreferredSize(new Dimension((int) d.getWidth(), 220));  | 
 | 
            }  | 
 | 
        }  | 
 | 
        @Override  | 
 | 
        public Component getTableCellRendererComponent(JTable table,  | 
 | 
                                                       Object value,  | 
 | 
                                                       boolean isSelected,  | 
 | 
                                                       boolean hasFocus,  | 
 | 
                                                       int row,  | 
 | 
                                                       int column) { | 
 | 
            return comp;  | 
 | 
        }  | 
 | 
        public Component getComponent() { | 
 | 
            return comp;  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
    class ZoomedCell { | 
 | 
        TableCellRenderer minRenderer;  | 
 | 
        MaximizedCellRenderer maxRenderer;  | 
 | 
        int minHeight;  | 
 | 
        boolean minimized = true;  | 
 | 
        boolean init = false;  | 
 | 
        int type;  | 
 | 
        Object value;  | 
 | 
        ZoomedCell(Object value) { | 
 | 
            type = XDataViewer.getViewerType(value);  | 
 | 
            this.value = value;  | 
 | 
        }  | 
 | 
 | 
 | 
        boolean isInited() { | 
 | 
            return init;  | 
 | 
        }  | 
 | 
 | 
 | 
        Object getValue() { | 
 | 
            return value;  | 
 | 
        }  | 
 | 
 | 
 | 
        void setValue(Object value) { | 
 | 
            this.value = value;  | 
 | 
        }  | 
 | 
 | 
 | 
        void init(TableCellRenderer minRenderer,  | 
 | 
                  Component maxComponent,  | 
 | 
                  int minHeight) { | 
 | 
            this.minRenderer = minRenderer;  | 
 | 
            this.maxRenderer = new MaximizedCellRenderer(maxComponent);  | 
 | 
 | 
 | 
            this.minHeight = minHeight;  | 
 | 
            init = true;  | 
 | 
        }  | 
 | 
 | 
 | 
        int getType() { | 
 | 
            return type;  | 
 | 
        }  | 
 | 
 | 
 | 
        void reset() { | 
 | 
            init = false;  | 
 | 
            minimized = 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() ;  | 
 | 
        }  | 
 | 
 | 
 | 
        int getMinHeight() { | 
 | 
            return minHeight;  | 
 | 
        }  | 
 | 
 | 
 | 
        @Override  | 
 | 
        public String toString() { | 
 | 
 | 
 | 
            if(value == null) return null;  | 
 | 
 | 
 | 
            if(value.getClass().isArray()) { | 
 | 
                String name =  | 
 | 
                    Utils.getArrayClassName(value.getClass().getName());  | 
 | 
                int length = Array.getLength(value);  | 
 | 
                return name + "[" + length +"]";  | 
 | 
            }  | 
 | 
 | 
 | 
            if(value instanceof CompositeData ||  | 
 | 
               value instanceof TabularData)  | 
 | 
                return value.getClass().getName();  | 
 | 
 | 
 | 
            return value.toString();  | 
 | 
        }  | 
 | 
 | 
 | 
        TableCellRenderer getRenderer() { | 
 | 
            if(minimized) return minRenderer;  | 
 | 
            else return maxRenderer;  | 
 | 
        }  | 
 | 
 | 
 | 
        TableCellRenderer getMinRenderer() { | 
 | 
            return minRenderer;  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
    class AttributesListener implements  TableModelListener { | 
 | 
 | 
 | 
        private Component component;  | 
 | 
 | 
 | 
        public AttributesListener(Component component) { | 
 | 
            this.component = component;  | 
 | 
        }  | 
 | 
 | 
 | 
          | 
 | 
        public void tableChanged(final TableModelEvent e) { | 
 | 
              | 
 | 
            if (isColumnEditable(e.getColumn())) { | 
 | 
                final TableModel model = (TableModel)e.getSource();  | 
 | 
                Object tableValue = model.getValueAt(e.getFirstRow(),  | 
 | 
                                                 e.getColumn());  | 
 | 
 | 
 | 
                if (LOGGER.isLoggable(Level.FINER)) { | 
 | 
                    LOGGER.finer("tableChanged: firstRow="+e.getFirstRow()+ | 
 | 
                        ", lastRow="+e.getLastRow()+", column="+e.getColumn()+  | 
 | 
                        ", value="+tableValue);  | 
 | 
                }  | 
 | 
                // if it's a String, try construct new value  | 
 | 
                  | 
 | 
                if (tableValue instanceof String) { | 
 | 
                    try { | 
 | 
                        tableValue =  | 
 | 
                            Utils.createObjectFromString(getClassName(e.getFirstRow()),   | 
 | 
                            (String)tableValue);  | 
 | 
                    } catch (Throwable ex) { | 
 | 
                        popupAndLog(ex,"tableChanged",  | 
 | 
                                    Messages.PROBLEM_SETTING_ATTRIBUTE);  | 
 | 
                    }  | 
 | 
                }  | 
 | 
                final String attributeName = getValueName(e.getFirstRow());  | 
 | 
                final Attribute attribute =  | 
 | 
                      new Attribute(attributeName,tableValue);  | 
 | 
                setAttribute(attribute, "tableChanged");  | 
 | 
            }  | 
 | 
        }  | 
 | 
 | 
 | 
          | 
 | 
        private void setAttribute(final Attribute attribute, final String method) { | 
 | 
            final SwingWorker<Void,Void> setAttribute =  | 
 | 
                    new SwingWorker<Void,Void>() { | 
 | 
                @Override  | 
 | 
                protected Void doInBackground() throws Exception { | 
 | 
                    try { | 
 | 
                        if (JConsole.isDebug()) { | 
 | 
                            System.err.println("setAttribute("+ | 
 | 
                                    attribute.getName()+  | 
 | 
                                "="+attribute.getValue()+")");  | 
 | 
                        }  | 
 | 
                        mbean.setAttribute(attribute);  | 
 | 
                    } catch (Throwable ex) { | 
 | 
                        popupAndLog(ex,method,Messages.PROBLEM_SETTING_ATTRIBUTE);  | 
 | 
                    }  | 
 | 
                    return null;  | 
 | 
                }  | 
 | 
                @Override  | 
 | 
                protected void done() { | 
 | 
                    try { | 
 | 
                        get();  | 
 | 
                    } catch (Exception x) { | 
 | 
                        if (JConsole.isDebug())  | 
 | 
                            x.printStackTrace();  | 
 | 
                    }  | 
 | 
                    refreshAttributes(false);  | 
 | 
                }  | 
 | 
 | 
 | 
            };  | 
 | 
            mbeansTab.workerAdd(setAttribute);  | 
 | 
        }  | 
 | 
 | 
 | 
          | 
 | 
        private void popupAndLog(Throwable ex, String method, String title) { | 
 | 
            ex = Utils.getActualException(ex);  | 
 | 
            if (JConsole.isDebug()) ex.printStackTrace();  | 
 | 
 | 
 | 
            String message = (ex.getMessage() != null) ? ex.getMessage()  | 
 | 
                    : ex.toString();  | 
 | 
            EventQueue.invokeLater(  | 
 | 
                    new ThreadDialog(component, message+"\n",  | 
 | 
                                     title,  | 
 | 
                                     JOptionPane.ERROR_MESSAGE));  | 
 | 
        }  | 
 | 
    }  | 
 | 
}  |