Development Class Java

/*
 * @(#)JTop.java  1.5 06/05/08
 *
 * Copyright (c) 2006 Sun Microsystems, Inc. All Rights Reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * -Redistribution of source code must retain the above copyright notice, this
 *  list of conditions and the following disclaimer.
 *
 * -Redistribution in binary form must reproduce the above copyright notice,
 *  this list of conditions and the following disclaimer in the documentation
 *  and/or other materials provided with the distribution.
 *
 * Neither the name of Sun Microsystems, Inc. or the names of contributors may
 * be used to endorse or promote products derived from this software without
 * specific prior written permission.
 *
 * This software is provided "AS IS," without a warranty of any kind. ALL
 * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING
 * ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
 * OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN MIDROSYSTEMS, INC. ("SUN")
 * AND ITS LICENSORS SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE
 * AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
 * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST
 * REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL,
 * INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY
 * OF LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE,
 * EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
 *
 * You acknowledge that this software is not designed, licensed or intended
 * for use in the design, construction, operation or maintenance of any
 * nuclear facility.
 */
/*
 * @(#)JTop.java  1.5 06/05/08
 *
 * Example of using the java.lang.management API to sort threads
 * by CPU usage.
 *
 * JTop class can be run as a standalone application.
 * It first establishs a connection to a target VM specified
 * by the given hostname and port number where the JMX agent
 * to be connected.  It then polls for the thread information
 * and the CPU consumption of each thread to display every 2 
 * seconds.
 *
 * It is also used by JTopPlugin which is a JConsolePlugin
 * that can be used with JConsole (see README.txt). The JTop
 * GUI will be added as a JConsole tab by the JTop plugin.
 *
 * @see com.sun.tools.jconsole.JConsolePlugin
 *
 * @author Mandy Chung
 */
import static java.lang.management.ManagementFactory.THREAD_MXBEAN_NAME;
import static java.lang.management.ManagementFactory.newPlatformMXBeanProxy;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.io.IOException;
import java.lang.management.ThreadInfo;
import java.lang.management.ThreadMXBean;
import java.net.MalformedURLException;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.SortedMap;
import java.util.Timer;
import java.util.TimerTask;
import java.util.TreeMap;
import java.util.concurrent.ExecutionException;
import javax.management.MBeanServerConnection;
import javax.management.remote.JMXConnector;
import javax.management.remote.JMXConnectorFactory;
import javax.management.remote.JMXServiceURL;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;
import javax.swing.border.EmptyBorder;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.DefaultTableCellRenderer;
/**
 * JTop is a JPanel to display thread's name, CPU time, and its state in a
 * table.
 */
public class JTop extends JPanel {
  private MBeanServerConnection server;
  private ThreadMXBean tmbean;
  private MyTableModel tmodel;
  public JTop() {
    super(new GridLayout(1, 0));
    tmodel = new MyTableModel();
    JTable table = new JTable(tmodel);
    table.setPreferredScrollableViewportSize(new Dimension(500, 300));
    // Set the renderer to format Double
    table.setDefaultRenderer(Double.class, new DoubleRenderer());
    // Add some space
    table.setIntercellSpacing(new Dimension(6, 3));
    table.setRowHeight(table.getRowHeight() + 4);
    // Create the scroll pane and add the table to it.
    JScrollPane scrollPane = new JScrollPane(table);
    // Add the scroll pane to this panel.
    add(scrollPane);
  }
  // Set the MBeanServerConnection object for communicating
  // with the target VM
  public void setMBeanServerConnection(MBeanServerConnection mbs) {
    this.server = mbs;
    try {
      this.tmbean = newPlatformMXBeanProxy(server, THREAD_MXBEAN_NAME, ThreadMXBean.class);
    } catch (IOException e) {
      e.printStackTrace();
    }
    if (!tmbean.isThreadCpuTimeSupported()) {
      System.err.println("This VM does not support thread CPU time monitoring");
    } else {
      tmbean.setThreadCpuTimeEnabled(true);
    }
  }
  class MyTableModel extends AbstractTableModel {
    private String[] columnNames = { "ThreadName", "CPU(sec)", "State" };
    // List of all threads. The key of each entry is the CPU time
    // and its value is the ThreadInfo object with no stack trace.
    private List> threadList = Collections.EMPTY_LIST;
    public MyTableModel() {
    }
    public int getColumnCount() {
      return columnNames.length;
    }
    public int getRowCount() {
      return threadList.size();
    }
    public String getColumnName(int col) {
      return columnNames[col];
    }
    public Object getValueAt(int row, int col) {
      Map.Entry me = threadList.get(row);
      switch (col) {
      case 0:
        // Column 0 shows the thread name
        return me.getValue().getThreadName();
      case 1:
        // Column 1 shows the CPU usage
        long ns = me.getKey().longValue();
        double sec = ns / 1000000000;
        return new Double(sec);
      case 2:
        // Column 2 shows the thread state
        return me.getValue().getThreadState();
      default:
        return null;
      }
    }
    public Class getColumnClass(int c) {
      return getValueAt(0, c).getClass();
    }
    void setThreadList(List> list) {
      threadList = list;
    }
  }
  /**
   * Get the thread list with CPU consumption and the ThreadInfo for each thread
   * sorted by the CPU time.
   */
  private List> getThreadList() {
    // Get all threads and their ThreadInfo objects
    // with no stack trace
    long[] tids = tmbean.getAllThreadIds();
    ThreadInfo[] tinfos = tmbean.getThreadInfo(tids);
    // build a map with key = CPU time and value = ThreadInfo
    SortedMap map = new TreeMap();
    for (int i = 0; i < tids.length; i++) {
      long cpuTime = tmbean.getThreadCpuTime(tids[i]);
      // filter out threads that have been terminated
      if (cpuTime != -1 && tinfos[i] != null) {
        map.put(new Long(cpuTime), tinfos[i]);
      }
    }
    // build the thread list and sort it with CPU time
    // in decreasing order
    Set> set = map.entrySet();
    List> list = new ArrayList>(set);
    Collections.reverse(list);
    return list;
  }
  /**
   * Format Double with 4 fraction digits
   */
  class DoubleRenderer extends DefaultTableCellRenderer {
    NumberFormat formatter;
    public DoubleRenderer() {
      super();
      setHorizontalAlignment(JLabel.RIGHT);
    }
    public void setValue(Object value) {
      if (formatter == null) {
        formatter = NumberFormat.getInstance();
        formatter.setMinimumFractionDigits(4);
      }
      setText((value == null) ? "" : formatter.format(value));
    }
  }
  // SwingWorker responsible for updating the GUI
  // 
  // It first gets the thread and CPU usage information as a
  // background task done by a worker thread so that
  // it will not block the event dispatcher thread.
  //
  // When the worker thread finishes, the event dispatcher
  // thread will invoke the done() method which will update
  // the UI.
  class Worker extends SwingWorker>, Object> {
    private MyTableModel tmodel;
    Worker(MyTableModel tmodel) {
      this.tmodel = tmodel;
    }
    // Get the current thread info and CPU time
    public List> doInBackground() {
      return getThreadList();
    }
    // fire table data changed to trigger GUI update
    // when doInBackground() is finished
    protected void done() {
      try {
        // Set table model with the new thread list
        tmodel.setThreadList(get());
        // refresh the table model
        tmodel.fireTableDataChanged();
      } catch (InterruptedException e) {
      } catch (ExecutionException e) {
      }
    }
  }
  // Return a new SwingWorker for UI update
  public SwingWorker newSwingWorker() {
    return new Worker(tmodel);
  }
  public static void main(String[] args) throws Exception {
    // Validate the input arguments
    if (args.length != 1) {
      usage();
    }
    String[] arg2 = args[0].split(":");
    if (arg2.length != 2) {
      usage();
    }
    String hostname = arg2[0];
    int port = -1;
    try {
      port = Integer.parseInt(arg2[1]);
    } catch (NumberFormatException x) {
      usage();
    }
    if (port < 0) {
      usage();
    }
    // Create the JTop Panel
    final JTop jtop = new JTop();
    // Set up the MBeanServerConnection to the target VM
    MBeanServerConnection server = connect(hostname, port);
    jtop.setMBeanServerConnection(server);
    // A timer task to update GUI per each interval
    TimerTask timerTask = new TimerTask() {
      public void run() {
        // Schedule the SwingWorker to update the GUI
        jtop.newSwingWorker().execute();
      }
    };
    // Create the standalone window with JTop panel
    // by the event dispatcher thread
    SwingUtilities.invokeAndWait(new Runnable() {
      public void run() {
        createAndShowGUI(jtop);
      }
    });
    // refresh every 2 seconds
    Timer timer = new Timer("JTop Sampling thread");
    timer.schedule(timerTask, 0, 2000);
  }
  // Establish a connection with the remote application
  //
  // You can modify the urlPath to the address of the JMX agent
  // of your application if it has a different URL.
  // 
  // You can also modify the following code to take
  // username and password for client authentication.
  private static MBeanServerConnection connect(String hostname, int port) {
    // Create an RMI connector client and connect it to
    // the RMI connector server
    String urlPath = "/jndi/rmi://" + hostname + ":" + port + "/jmxrmi";
    MBeanServerConnection server = null;
    try {
      JMXServiceURL url = new JMXServiceURL("rmi", "", 0, urlPath);
      JMXConnector jmxc = JMXConnectorFactory.connect(url);
      server = jmxc.getMBeanServerConnection();
    } catch (MalformedURLException e) {
      // should not reach here
    } catch (IOException e) {
      System.err.println("\nCommunication error: " + e.getMessage());
      System.exit(1);
    }
    return server;
  }
  private static void usage() {
    System.out.println("Usage: java JTop :");
    System.exit(1);
  }
  /**
   * Create the GUI and show it. For thread safety, this method should be
   * invoked from the event-dispatching thread.
   */
  private static void createAndShowGUI(JPanel jtop) {
    // Create and set up the window.
    JFrame frame = new JFrame("JTop");
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    // Create and set up the content pane.
    JComponent contentPane = (JComponent) frame.getContentPane();
    contentPane.add(jtop, BorderLayout.CENTER);
    contentPane.setOpaque(true); // content panes must be opaque
    contentPane.setBorder(new EmptyBorder(12, 12, 12, 12));
    frame.setContentPane(contentPane);
    // Display the window.
    frame.pack();
    frame.setVisible(true);
  }
}