All Downloads are FREE. Search and download functionalities are using the official Maven repository.

linoleum.console.JTop Maven / Gradle / Ivy

The newest version!
package linoleum.console;

import java.lang.management.*;
import javax.management.*;
import javax.management.remote.*;
import java.io.IOException;
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 java.text.NumberFormat;
import java.net.MalformedURLException;
import static java.lang.management.ManagementFactory.*;

import java.awt.*;
import javax.swing.*;
import javax.swing.border.*;
import javax.swing.table.*;

public class JTop extends JPanel {

	private static class StatusBar extends JPanel {
		private static final long serialVersionUID = -6483392381797633018L;
		private final JLabel statusText;

		public StatusBar(boolean defaultVisible) {
			super(new GridLayout(1, 1));
			statusText = new JLabel();
			statusText.setVisible(defaultVisible);
			add(statusText);
		}

		@Override
		public Dimension getMaximumSize() {
			Dimension maximum = super.getMaximumSize();
			Dimension minimum = getMinimumSize();
			return new Dimension(maximum.width, minimum.height);
		}

		public void setMessage(String text) {
			statusText.setText(text);
			statusText.setVisible(true);
		}
	}
	private static final long serialVersionUID = -1499762160973870696L;
	private MBeanServerConnection server;
	private ThreadMXBean tmbean;
	private MyTableModel tmodel;
	private final StatusBar statusBar;
	public JTop() {
		super(new GridBagLayout());

		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.
		GridBagConstraints c1 = new GridBagConstraints();
		c1.fill = GridBagConstraints.BOTH;
		c1.gridy = 0;
		c1.gridx = 0;
		c1.weightx = 1;
		c1.weighty = 1;
		add(scrollPane, c1);

		statusBar = new StatusBar(false);
		GridBagConstraints c2 = new GridBagConstraints();
		c2.fill = GridBagConstraints.HORIZONTAL;
		c2.gridy = 1;
		c2.gridx = 0;
		c2.weightx = 1.0;
		c2.weighty = 0.0;
		add(statusBar, c2);
	}

	// 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()) {
			statusBar.setMessage("Monitored VM does not support thread CPU time measurement");
		} else {
			try {
				tmbean.setThreadCpuTimeEnabled(true);
			} catch (SecurityException e) {
				statusBar.setMessage("Monitored VM does not have permission for enabling thread cpu time measurement");
			}
		}
	}

	class MyTableModel extends AbstractTableModel {
		private static final long serialVersionUID = -7877310288576779514L;
		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.emptyList();

		public MyTableModel() {
		}

		@Override
		public int getColumnCount() {
			return columnNames.length;
		}

		@Override
		public int getRowCount() {
			return threadList.size();
		}

		@Override
		public String getColumnName(int col) {
			return columnNames[col];
		}

		@Override
		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;
			}
		}

		@Override
		public Class getColumnClass(int c) {
			return getValueAt(0, c).getClass();
		}

		void setThreadList(List> list) {
			threadList = list;
		}
	}

	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;
	}


	class DoubleRenderer extends DefaultTableCellRenderer {
		private static final long serialVersionUID = 1704639497162584382L;
		NumberFormat formatter;
		public DoubleRenderer() {
			super();
			setHorizontalAlignment(JLabel.RIGHT);
		}

		@Override
		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
		@Override
		public List> doInBackground() {
			return getThreadList();
		}

		// fire table data changed to trigger GUI update
		// when doInBackground() is finished
		@Override
		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() {
			@Override
			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() {
			@Override
			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);
	}

	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);
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy