Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/share/demo/management/JTop/JTop.java
38829 views
/*1* Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved.2*3* Redistribution and use in source and binary forms, with or without4* modification, are permitted provided that the following conditions5* are met:6*7* - Redistributions of source code must retain the above copyright8* notice, this list of conditions and the following disclaimer.9*10* - Redistributions in binary form must reproduce the above copyright11* notice, this list of conditions and the following disclaimer in the12* documentation and/or other materials provided with the distribution.13*14* - Neither the name of Oracle nor the names of its15* contributors may be used to endorse or promote products derived16* from this software without specific prior written permission.17*18* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS19* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,20* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR21* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR22* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,23* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,24* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR25* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF26* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING27* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS28* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.29*/3031/*32* This source code is provided to illustrate the usage of a given feature33* or technique and has been deliberately simplified. Additional steps34* required for a production-quality application, such as security checks,35* input validation and proper error handling, might not be present in36* this sample code.37*/383940/*41*42* Example of using the java.lang.management API to sort threads43* by CPU usage.44*45* JTop class can be run as a standalone application.46* It first establishs a connection to a target VM specified47* by the given hostname and port number where the JMX agent48* to be connected. It then polls for the thread information49* and the CPU consumption of each thread to display every 250* seconds.51*52* It is also used by JTopPlugin which is a JConsolePlugin53* that can be used with JConsole (see README.txt). The JTop54* GUI will be added as a JConsole tab by the JTop plugin.55*56* @see com.sun.tools.jconsole.JConsolePlugin57*58* @author Mandy Chung59*/60import java.lang.management.*;61import javax.management.*;62import javax.management.remote.*;63import java.io.IOException;64import java.util.ArrayList;65import java.util.Collections;66import java.util.List;67import java.util.Map;68import java.util.Set;69import java.util.SortedMap;70import java.util.Timer;71import java.util.TimerTask;72import java.util.TreeMap;73import java.util.concurrent.ExecutionException;74import java.text.NumberFormat;75import java.net.MalformedURLException;76import static java.lang.management.ManagementFactory.*;7778import java.awt.*;79import javax.swing.*;80import javax.swing.border.*;81import javax.swing.table.*;8283/**84* JTop is a JPanel to display thread's name, CPU time, and its state85* in a table.86*/87public class JTop extends JPanel {8889private static class StatusBar extends JPanel {90private static final long serialVersionUID = -6483392381797633018L;91private final JLabel statusText;9293public StatusBar(boolean defaultVisible) {94super(new GridLayout(1, 1));95statusText = new JLabel();96statusText.setVisible(defaultVisible);97add(statusText);98}99100@Override101public Dimension getMaximumSize() {102Dimension maximum = super.getMaximumSize();103Dimension minimum = getMinimumSize();104return new Dimension(maximum.width, minimum.height);105}106107public void setMessage(String text) {108statusText.setText(text);109statusText.setVisible(true);110}111}112private static final long serialVersionUID = -1499762160973870696L;113private MBeanServerConnection server;114private ThreadMXBean tmbean;115private MyTableModel tmodel;116private final StatusBar statusBar;117public JTop() {118super(new GridBagLayout());119120tmodel = new MyTableModel();121JTable table = new JTable(tmodel);122table.setPreferredScrollableViewportSize(new Dimension(500, 300));123124// Set the renderer to format Double125table.setDefaultRenderer(Double.class, new DoubleRenderer());126// Add some space127table.setIntercellSpacing(new Dimension(6,3));128table.setRowHeight(table.getRowHeight() + 4);129130// Create the scroll pane and add the table to it.131JScrollPane scrollPane = new JScrollPane(table);132133// Add the scroll pane to this panel.134GridBagConstraints c1 = new GridBagConstraints();135c1.fill = GridBagConstraints.BOTH;136c1.gridy = 0;137c1.gridx = 0;138c1.weightx = 1;139c1.weighty = 1;140add(scrollPane, c1);141142statusBar = new StatusBar(false);143GridBagConstraints c2 = new GridBagConstraints();144c2.fill = GridBagConstraints.HORIZONTAL;145c2.gridy = 1;146c2.gridx = 0;147c2.weightx = 1.0;148c2.weighty = 0.0;149add(statusBar, c2);150}151152// Set the MBeanServerConnection object for communicating153// with the target VM154public void setMBeanServerConnection(MBeanServerConnection mbs) {155this.server = mbs;156try {157this.tmbean = newPlatformMXBeanProxy(server,158THREAD_MXBEAN_NAME,159ThreadMXBean.class);160} catch (IOException e) {161e.printStackTrace();162}163if (!tmbean.isThreadCpuTimeSupported()) {164statusBar.setMessage("Monitored VM does not support thread CPU time measurement");165} else {166try {167tmbean.setThreadCpuTimeEnabled(true);168} catch (SecurityException e) {169statusBar.setMessage("Monitored VM does not have permission for enabling thread cpu time measurement");170}171}172}173174class MyTableModel extends AbstractTableModel {175private static final long serialVersionUID = -7877310288576779514L;176private String[] columnNames = {"ThreadName",177"CPU(sec)",178"State"};179// List of all threads. The key of each entry is the CPU time180// and its value is the ThreadInfo object with no stack trace.181private List<Map.Entry<Long, ThreadInfo>> threadList =182Collections.emptyList();183184public MyTableModel() {185}186187@Override188public int getColumnCount() {189return columnNames.length;190}191192@Override193public int getRowCount() {194return threadList.size();195}196197@Override198public String getColumnName(int col) {199return columnNames[col];200}201202@Override203public Object getValueAt(int row, int col) {204Map.Entry<Long, ThreadInfo> me = threadList.get(row);205switch (col) {206case 0 :207// Column 0 shows the thread name208return me.getValue().getThreadName();209case 1 :210// Column 1 shows the CPU usage211long ns = me.getKey().longValue();212double sec = ns / 1000000000;213return new Double(sec);214case 2 :215// Column 2 shows the thread state216return me.getValue().getThreadState();217default:218return null;219}220}221222@Override223public Class<?> getColumnClass(int c) {224return getValueAt(0, c).getClass();225}226227void setThreadList(List<Map.Entry<Long, ThreadInfo>> list) {228threadList = list;229}230}231232/**233* Get the thread list with CPU consumption and the ThreadInfo234* for each thread sorted by the CPU time.235*/236private List<Map.Entry<Long, ThreadInfo>> getThreadList() {237// Get all threads and their ThreadInfo objects238// with no stack trace239long[] tids = tmbean.getAllThreadIds();240ThreadInfo[] tinfos = tmbean.getThreadInfo(tids);241242// build a map with key = CPU time and value = ThreadInfo243SortedMap<Long, ThreadInfo> map = new TreeMap<Long, ThreadInfo>();244for (int i = 0; i < tids.length; i++) {245long cpuTime = tmbean.getThreadCpuTime(tids[i]);246// filter out threads that have been terminated247if (cpuTime != -1 && tinfos[i] != null) {248map.put(new Long(cpuTime), tinfos[i]);249}250}251252// build the thread list and sort it with CPU time253// in decreasing order254Set<Map.Entry<Long, ThreadInfo>> set = map.entrySet();255List<Map.Entry<Long, ThreadInfo>> list =256new ArrayList<Map.Entry<Long, ThreadInfo>>(set);257Collections.reverse(list);258return list;259}260261262/**263* Format Double with 4 fraction digits264*/265class DoubleRenderer extends DefaultTableCellRenderer {266private static final long serialVersionUID = 1704639497162584382L;267NumberFormat formatter;268public DoubleRenderer() {269super();270setHorizontalAlignment(JLabel.RIGHT);271}272273@Override274public void setValue(Object value) {275if (formatter==null) {276formatter = NumberFormat.getInstance();277formatter.setMinimumFractionDigits(4);278}279setText((value == null) ? "" : formatter.format(value));280}281}282283// SwingWorker responsible for updating the GUI284//285// It first gets the thread and CPU usage information as a286// background task done by a worker thread so that287// it will not block the event dispatcher thread.288//289// When the worker thread finishes, the event dispatcher290// thread will invoke the done() method which will update291// the UI.292class Worker extends SwingWorker<List<Map.Entry<Long, ThreadInfo>>,Object> {293private MyTableModel tmodel;294Worker(MyTableModel tmodel) {295this.tmodel = tmodel;296}297298// Get the current thread info and CPU time299@Override300public List<Map.Entry<Long, ThreadInfo>> doInBackground() {301return getThreadList();302}303304// fire table data changed to trigger GUI update305// when doInBackground() is finished306@Override307protected void done() {308try {309// Set table model with the new thread list310tmodel.setThreadList(get());311// refresh the table model312tmodel.fireTableDataChanged();313} catch (InterruptedException e) {314} catch (ExecutionException e) {315}316}317}318319// Return a new SwingWorker for UI update320public SwingWorker<?,?> newSwingWorker() {321return new Worker(tmodel);322}323324public static void main(String[] args) throws Exception {325// Validate the input arguments326if (args.length != 1) {327usage();328}329330String[] arg2 = args[0].split(":");331if (arg2.length != 2) {332usage();333}334String hostname = arg2[0];335int port = -1;336try {337port = Integer.parseInt(arg2[1]);338} catch (NumberFormatException x) {339usage();340}341if (port < 0) {342usage();343}344345// Create the JTop Panel346final JTop jtop = new JTop();347// Set up the MBeanServerConnection to the target VM348MBeanServerConnection server = connect(hostname, port);349jtop.setMBeanServerConnection(server);350351// A timer task to update GUI per each interval352TimerTask timerTask = new TimerTask() {353@Override354public void run() {355// Schedule the SwingWorker to update the GUI356jtop.newSwingWorker().execute();357}358};359360// Create the standalone window with JTop panel361// by the event dispatcher thread362SwingUtilities.invokeAndWait(new Runnable() {363@Override364public void run() {365createAndShowGUI(jtop);366}367});368369// refresh every 2 seconds370Timer timer = new Timer("JTop Sampling thread");371timer.schedule(timerTask, 0, 2000);372373}374375// Establish a connection with the remote application376//377// You can modify the urlPath to the address of the JMX agent378// of your application if it has a different URL.379//380// You can also modify the following code to take381// username and password for client authentication.382private static MBeanServerConnection connect(String hostname, int port) {383// Create an RMI connector client and connect it to384// the RMI connector server385String urlPath = "/jndi/rmi://" + hostname + ":" + port + "/jmxrmi";386MBeanServerConnection server = null;387try {388JMXServiceURL url = new JMXServiceURL("rmi", "", 0, urlPath);389JMXConnector jmxc = JMXConnectorFactory.connect(url);390server = jmxc.getMBeanServerConnection();391} catch (MalformedURLException e) {392// should not reach here393} catch (IOException e) {394System.err.println("\nCommunication error: " + e.getMessage());395System.exit(1);396}397return server;398}399400private static void usage() {401System.out.println("Usage: java JTop <hostname>:<port>");402System.exit(1);403}404/**405* Create the GUI and show it. For thread safety,406* this method should be invoked from the407* event-dispatching thread.408*/409private static void createAndShowGUI(JPanel jtop) {410// Create and set up the window.411JFrame frame = new JFrame("JTop");412frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);413414// Create and set up the content pane.415JComponent contentPane = (JComponent) frame.getContentPane();416contentPane.add(jtop, BorderLayout.CENTER);417contentPane.setOpaque(true); //content panes must be opaque418contentPane.setBorder(new EmptyBorder(12, 12, 12, 12));419frame.setContentPane(contentPane);420421// Display the window.422frame.pack();423frame.setVisible(true);424}425426}427428429