0N/A/*
2362N/A * Copyright (c) 2004, 2007, Oracle and/or its affiliates. All rights reserved.
0N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0N/A *
0N/A * This code is free software; you can redistribute it and/or modify it
0N/A * under the terms of the GNU General Public License version 2 only, as
2362N/A * published by the Free Software Foundation. Oracle designates this
0N/A * particular file as subject to the "Classpath" exception as provided
2362N/A * by Oracle in the LICENSE file that accompanied this code.
0N/A *
0N/A * This code is distributed in the hope that it will be useful, but WITHOUT
0N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0N/A * version 2 for more details (a copy is included in the LICENSE file that
0N/A * accompanied this code).
0N/A *
0N/A * You should have received a copy of the GNU General Public License version
0N/A * 2 along with this work; if not, write to the Free Software Foundation,
0N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0N/A *
2362N/A * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
2362N/A * or visit www.oracle.com if you need additional information or have any
2362N/A * questions.
0N/A */
0N/A
0N/Apackage sun.tools.jconsole;
0N/A
0N/Aimport java.awt.*;
0N/Aimport java.awt.event.*;
0N/Aimport java.io.*;
0N/Aimport java.lang.management.*;
0N/Aimport java.lang.reflect.*;
0N/A
0N/Aimport javax.swing.*;
0N/Aimport javax.swing.border.*;
0N/Aimport javax.swing.event.*;
0N/A
5335N/A
0N/Aimport java.util.*;
0N/Aimport java.util.concurrent.*;
0N/Aimport java.util.List;
0N/A
0N/Aimport static sun.tools.jconsole.Utilities.*;
0N/A
0N/A
0N/A@SuppressWarnings("serial")
0N/Aclass ThreadTab extends Tab implements ActionListener, DocumentListener, ListSelectionListener {
0N/A PlotterPanel threadMeter;
0N/A TimeComboBox timeComboBox;
0N/A JTabbedPane threadListTabbedPane;
5335N/A DefaultListModel<Long> listModel;
0N/A JTextField filterTF;
0N/A JLabel messageLabel;
0N/A JSplitPane threadsSplitPane;
0N/A HashMap<Long, String> nameCache = new HashMap<Long, String>();
0N/A
0N/A private ThreadOverviewPanel overviewPanel;
0N/A private boolean plotterListening = false;
0N/A
0N/A
0N/A private static final String threadCountKey = "threadCount";
0N/A private static final String peakKey = "peak";
0N/A
0N/A private static final Color threadCountColor = Plotter.defaultColor;
0N/A private static final Color peakColor = Color.red;
0N/A
0N/A private static final Border thinEmptyBorder = new EmptyBorder(2, 2, 2, 2);
0N/A
0N/A private static final String infoLabelFormat = "ThreadTab.infoLabelFormat";
0N/A
0N/A
0N/A /*
0N/A Hierarchy of panels and layouts for this tab:
0N/A
0N/A ThreadTab (BorderLayout)
0N/A
0N/A North: topPanel (BorderLayout)
0N/A
0N/A Center: controlPanel (FlowLayout)
0N/A timeComboBox
0N/A
0N/A Center: plotterPanel (BorderLayout)
0N/A
0N/A Center: plotter
0N/A
0N/A */
0N/A
0N/A
0N/A public static String getTabName() {
5335N/A return Messages.THREADS;
0N/A }
0N/A
0N/A public ThreadTab(VMPanel vmPanel) {
0N/A super(vmPanel, getTabName());
0N/A
0N/A setLayout(new BorderLayout(0, 0));
0N/A setBorder(new EmptyBorder(4, 4, 3, 4));
0N/A
0N/A JPanel topPanel = new JPanel(new BorderLayout());
0N/A JPanel plotterPanel = new JPanel(new VariableGridLayout(0, 1, 4, 4, true, true));
0N/A
0N/A add(topPanel, BorderLayout.NORTH);
0N/A add(plotterPanel, BorderLayout.CENTER);
0N/A
0N/A JPanel controlPanel = new JPanel(new FlowLayout(FlowLayout.CENTER, 20, 5));
0N/A topPanel.add(controlPanel, BorderLayout.CENTER);
0N/A
5335N/A threadMeter = new PlotterPanel(Messages.NUMBER_OF_THREADS,
0N/A Plotter.Unit.NONE, true);
5335N/A threadMeter.plotter.createSequence(threadCountKey, Messages.LIVE_THREADS, threadCountColor, true);
5335N/A threadMeter.plotter.createSequence(peakKey, Messages.PEAK, peakColor, true);
0N/A setAccessibleName(threadMeter.plotter,
5335N/A Messages.THREAD_TAB_THREAD_PLOTTER_ACCESSIBLE_NAME);
0N/A
0N/A plotterPanel.add(threadMeter);
0N/A
0N/A timeComboBox = new TimeComboBox(threadMeter.plotter);
5335N/A controlPanel.add(new LabeledComponent(Messages.TIME_RANGE_COLON,
5335N/A Resources.getMnemonicInt(Messages.TIME_RANGE_COLON),
0N/A timeComboBox));
0N/A
5335N/A listModel = new DefaultListModel<Long>();
0N/A
0N/A JTextArea textArea = new JTextArea();
0N/A textArea.setBorder(thinEmptyBorder);
0N/A textArea.setEditable(false);
0N/A setAccessibleName(textArea,
5335N/A Messages.THREAD_TAB_THREAD_INFO_ACCESSIBLE_NAME);
5335N/A ThreadJList list = new ThreadJList(listModel, textArea);
0N/A
0N/A Dimension di = new Dimension(super.getPreferredSize());
0N/A di.width = Math.min(di.width, 200);
0N/A
0N/A JScrollPane threadlistSP = new JScrollPane(list);
0N/A threadlistSP.setPreferredSize(di);
0N/A threadlistSP.setBorder(null);
0N/A
0N/A JScrollPane textAreaSP = new JScrollPane(textArea);
0N/A textAreaSP.setBorder(null);
0N/A
0N/A threadListTabbedPane = new JTabbedPane(JTabbedPane.TOP);
0N/A threadsSplitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT,
0N/A threadlistSP, textAreaSP);
0N/A threadsSplitPane.setOneTouchExpandable(true);
0N/A threadsSplitPane.setBorder(null);
0N/A
0N/A JPanel firstTabPanel = new JPanel(new BorderLayout());
0N/A firstTabPanel.setOpaque(false);
0N/A
0N/A JPanel firstTabToolPanel = new JPanel(new FlowLayout(FlowLayout.LEFT, 5, 2));
0N/A firstTabToolPanel.setOpaque(false);
0N/A
0N/A filterTF = new PromptingTextField("Filter", 20);
0N/A filterTF.getDocument().addDocumentListener(this);
0N/A firstTabToolPanel.add(filterTF);
0N/A
0N/A JSeparator separator = new JSeparator(JSeparator.VERTICAL);
0N/A separator.setPreferredSize(new Dimension(separator.getPreferredSize().width,
0N/A filterTF.getPreferredSize().height));
0N/A firstTabToolPanel.add(separator);
0N/A
5335N/A JButton detectDeadlockButton = new JButton(Messages.DETECT_DEADLOCK);
5335N/A detectDeadlockButton.setMnemonic(Resources.getMnemonicInt(Messages.DETECT_DEADLOCK));
0N/A detectDeadlockButton.setActionCommand("detectDeadlock");
0N/A detectDeadlockButton.addActionListener(this);
5335N/A detectDeadlockButton.setToolTipText(Messages.DETECT_DEADLOCK_TOOLTIP);
0N/A firstTabToolPanel.add(detectDeadlockButton);
0N/A
0N/A messageLabel = new JLabel();
0N/A firstTabToolPanel.add(messageLabel);
0N/A
0N/A firstTabPanel.add(threadsSplitPane, BorderLayout.CENTER);
0N/A firstTabPanel.add(firstTabToolPanel, BorderLayout.SOUTH);
5335N/A threadListTabbedPane.addTab(Messages.THREADS, firstTabPanel);
0N/A
0N/A plotterPanel.add(threadListTabbedPane);
0N/A }
0N/A
0N/A private long oldThreads[] = new long[0];
0N/A
0N/A public SwingWorker<?, ?> newSwingWorker() {
0N/A final ProxyClient proxyClient = vmPanel.getProxyClient();
0N/A
0N/A if (!plotterListening) {
0N/A proxyClient.addWeakPropertyChangeListener(threadMeter.plotter);
0N/A plotterListening = true;
0N/A }
0N/A
0N/A return new SwingWorker<Boolean, Object>() {
0N/A private int tlCount;
0N/A private int tpCount;
0N/A private long ttCount;
0N/A private long[] threads;
0N/A private long timeStamp;
0N/A
0N/A public Boolean doInBackground() {
0N/A try {
0N/A ThreadMXBean threadMBean = proxyClient.getThreadMXBean();
0N/A
0N/A tlCount = threadMBean.getThreadCount();
0N/A tpCount = threadMBean.getPeakThreadCount();
0N/A if (overviewPanel != null) {
0N/A ttCount = threadMBean.getTotalStartedThreadCount();
0N/A } else {
0N/A ttCount = 0L;
0N/A }
0N/A
0N/A threads = threadMBean.getAllThreadIds();
0N/A for (long newThread : threads) {
0N/A if (nameCache.get(newThread) == null) {
0N/A ThreadInfo ti = threadMBean.getThreadInfo(newThread);
0N/A if (ti != null) {
0N/A String name = ti.getThreadName();
0N/A if (name != null) {
0N/A nameCache.put(newThread, name);
0N/A }
0N/A }
0N/A }
0N/A }
0N/A timeStamp = System.currentTimeMillis();
0N/A return true;
0N/A } catch (IOException e) {
0N/A return false;
0N/A } catch (UndeclaredThrowableException e) {
0N/A return false;
0N/A }
0N/A }
0N/A
0N/A protected void done() {
0N/A try {
0N/A if (!get()) {
0N/A return;
0N/A }
0N/A } catch (InterruptedException ex) {
0N/A return;
0N/A } catch (ExecutionException ex) {
0N/A if (JConsole.isDebug()) {
0N/A ex.printStackTrace();
0N/A }
0N/A return;
0N/A }
0N/A
0N/A threadMeter.plotter.addValues(timeStamp, tlCount, tpCount);
0N/A threadMeter.setValueLabel(tlCount+"");
0N/A
0N/A if (overviewPanel != null) {
0N/A overviewPanel.updateThreadsInfo(tlCount, tpCount, ttCount, timeStamp);
0N/A }
0N/A
0N/A String filter = filterTF.getText().toLowerCase(Locale.ENGLISH);
0N/A boolean doFilter = (filter.length() > 0);
0N/A
0N/A ArrayList<Long> l = new ArrayList<Long>();
0N/A for (long t : threads) {
0N/A l.add(t);
0N/A }
0N/A Iterator<Long> iterator = l.iterator();
0N/A while (iterator.hasNext()) {
0N/A long newThread = iterator.next();
0N/A String name = nameCache.get(newThread);
0N/A if (doFilter && name != null &&
0N/A name.toLowerCase(Locale.ENGLISH).indexOf(filter) < 0) {
0N/A
0N/A iterator.remove();
0N/A }
0N/A }
0N/A long[] newThreads = threads;
0N/A if (l.size() < threads.length) {
0N/A newThreads = new long[l.size()];
0N/A for (int i = 0; i < newThreads.length; i++) {
0N/A newThreads[i] = l.get(i);
0N/A }
0N/A }
0N/A
0N/A
0N/A for (long oldThread : oldThreads) {
0N/A boolean found = false;
0N/A for (long newThread : newThreads) {
0N/A if (newThread == oldThread) {
0N/A found = true;
0N/A break;
0N/A }
0N/A }
0N/A if (!found) {
0N/A listModel.removeElement(oldThread);
0N/A if (!doFilter) {
0N/A nameCache.remove(oldThread);
0N/A }
0N/A }
0N/A }
0N/A
0N/A // Threads are in reverse chronological order
0N/A for (int i = newThreads.length - 1; i >= 0; i--) {
0N/A long newThread = newThreads[i];
0N/A boolean found = false;
0N/A for (long oldThread : oldThreads) {
0N/A if (newThread == oldThread) {
0N/A found = true;
0N/A break;
0N/A }
0N/A }
0N/A if (!found) {
0N/A listModel.addElement(newThread);
0N/A }
0N/A }
0N/A oldThreads = newThreads;
0N/A }
0N/A };
0N/A }
0N/A
0N/A long lastSelected = -1;
0N/A
0N/A public void valueChanged(ListSelectionEvent ev) {
0N/A ThreadJList list = (ThreadJList)ev.getSource();
0N/A final JTextArea textArea = list.textArea;
0N/A
0N/A Long selected = (Long)list.getSelectedValue();
0N/A if (selected == null) {
0N/A if (lastSelected != -1) {
0N/A selected = lastSelected;
0N/A }
0N/A } else {
0N/A lastSelected = selected;
0N/A }
0N/A textArea.setText("");
0N/A if (selected != null) {
0N/A final long threadID = selected;
0N/A workerAdd(new Runnable() {
0N/A public void run() {
0N/A ProxyClient proxyClient = vmPanel.getProxyClient();
0N/A StringBuilder sb = new StringBuilder();
0N/A try {
0N/A ThreadMXBean threadMBean = proxyClient.getThreadMXBean();
0N/A ThreadInfo ti = null;
0N/A MonitorInfo[] monitors = null;
0N/A if (proxyClient.isLockUsageSupported() &&
0N/A threadMBean.isObjectMonitorUsageSupported()) {
0N/A // VMs that support the monitor usage monitoring
0N/A ThreadInfo[] infos = threadMBean.dumpAllThreads(true, false);
0N/A for (ThreadInfo info : infos) {
0N/A if (info.getThreadId() == threadID) {
0N/A ti = info;
0N/A monitors = info.getLockedMonitors();
0N/A break;
0N/A }
0N/A }
0N/A } else {
0N/A // VM doesn't support monitor usage monitoring
0N/A ti = threadMBean.getThreadInfo(threadID, Integer.MAX_VALUE);
0N/A }
0N/A if (ti != null) {
0N/A if (ti.getLockName() == null) {
5335N/A sb.append(Resources.format(Messages.NAME_STATE,
0N/A ti.getThreadName(),
0N/A ti.getThreadState().toString()));
0N/A } else if (ti.getLockOwnerName() == null) {
5335N/A sb.append(Resources.format(Messages.NAME_STATE_LOCK_NAME,
0N/A ti.getThreadName(),
0N/A ti.getThreadState().toString(),
0N/A ti.getLockName()));
0N/A } else {
5335N/A sb.append(Resources.format(Messages.NAME_STATE_LOCK_NAME_LOCK_OWNER,
0N/A ti.getThreadName(),
0N/A ti.getThreadState().toString(),
0N/A ti.getLockName(),
0N/A ti.getLockOwnerName()));
0N/A }
5335N/A sb.append(Resources.format(Messages.BLOCKED_COUNT_WAITED_COUNT,
0N/A ti.getBlockedCount(),
0N/A ti.getWaitedCount()));
5335N/A sb.append(Messages.STACK_TRACE);
0N/A int index = 0;
0N/A for (StackTraceElement e : ti.getStackTrace()) {
0N/A sb.append(e.toString()+"\n");
0N/A if (monitors != null) {
0N/A for (MonitorInfo mi : monitors) {
0N/A if (mi.getLockedStackDepth() == index) {
5335N/A sb.append(Resources.format(Messages.MONITOR_LOCKED, mi.toString()));
0N/A }
0N/A }
0N/A }
0N/A index++;
0N/A }
0N/A }
0N/A } catch (IOException ex) {
0N/A // Ignore
0N/A } catch (UndeclaredThrowableException e) {
0N/A proxyClient.markAsDead();
0N/A }
0N/A final String text = sb.toString();
0N/A SwingUtilities.invokeLater(new Runnable() {
0N/A public void run() {
0N/A textArea.setText(text);
0N/A textArea.setCaretPosition(0);
0N/A }
0N/A });
0N/A }
0N/A });
0N/A }
0N/A }
0N/A
0N/A private void doUpdate() {
0N/A workerAdd(new Runnable() {
0N/A public void run() {
0N/A update();
0N/A }
0N/A });
0N/A }
0N/A
0N/A
0N/A private void detectDeadlock() {
0N/A workerAdd(new Runnable() {
0N/A public void run() {
0N/A try {
0N/A final Long[][] deadlockedThreads = getDeadlockedThreadIds();
0N/A
0N/A if (deadlockedThreads == null || deadlockedThreads.length == 0) {
0N/A // Display message for 30 seconds. Do it on a separate thread so
0N/A // the sleep won't hold up the worker queue.
0N/A // This will be replaced later by separate statusbar logic.
0N/A new Thread() {
0N/A public void run() {
0N/A try {
0N/A SwingUtilities.invokeAndWait(new Runnable() {
0N/A public void run() {
5335N/A String msg = Messages.NO_DEADLOCK_DETECTED;
0N/A messageLabel.setText(msg);
0N/A threadListTabbedPane.revalidate();
0N/A }
0N/A });
0N/A sleep(30 * 1000);
0N/A } catch (InterruptedException ex) {
0N/A // Ignore
0N/A } catch (InvocationTargetException ex) {
0N/A // Ignore
0N/A }
0N/A SwingUtilities.invokeLater(new Runnable() {
0N/A public void run() {
0N/A messageLabel.setText("");
0N/A }
0N/A });
0N/A }
0N/A }.start();
0N/A return;
0N/A }
0N/A
0N/A SwingUtilities.invokeLater(new Runnable() {
0N/A public void run() {
0N/A // Remove old deadlock tabs
0N/A while (threadListTabbedPane.getTabCount() > 1) {
0N/A threadListTabbedPane.removeTabAt(1);
0N/A }
0N/A
0N/A if (deadlockedThreads != null) {
0N/A for (int i = 0; i < deadlockedThreads.length; i++) {
5335N/A DefaultListModel<Long> listModel = new DefaultListModel<Long>();
0N/A JTextArea textArea = new JTextArea();
0N/A textArea.setBorder(thinEmptyBorder);
0N/A textArea.setEditable(false);
0N/A setAccessibleName(textArea,
5335N/A Messages.THREAD_TAB_THREAD_INFO_ACCESSIBLE_NAME);
5335N/A ThreadJList list = new ThreadJList(listModel, textArea);
0N/A JScrollPane threadlistSP = new JScrollPane(list);
0N/A JScrollPane textAreaSP = new JScrollPane(textArea);
0N/A threadlistSP.setBorder(null);
0N/A textAreaSP.setBorder(null);
0N/A JSplitPane splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT,
0N/A threadlistSP, textAreaSP);
0N/A splitPane.setOneTouchExpandable(true);
0N/A splitPane.setBorder(null);
0N/A splitPane.setDividerLocation(threadsSplitPane.getDividerLocation());
0N/A String tabName;
0N/A if (deadlockedThreads.length > 1) {
5335N/A tabName = Resources.format(Messages.DEADLOCK_TAB_N, i+1);
0N/A } else {
5335N/A tabName = Messages.DEADLOCK_TAB;
0N/A }
0N/A threadListTabbedPane.addTab(tabName, splitPane);
0N/A
0N/A for (long t : deadlockedThreads[i]) {
0N/A listModel.addElement(t);
0N/A }
0N/A }
0N/A threadListTabbedPane.setSelectedIndex(1);
0N/A }
0N/A }
0N/A });
0N/A } catch (IOException e) {
0N/A // Ignore
0N/A } catch (UndeclaredThrowableException e) {
0N/A vmPanel.getProxyClient().markAsDead();
0N/A }
0N/A }
0N/A });
0N/A }
0N/A
0N/A
0N/A // Return deadlocked threads or null
0N/A public Long[][] getDeadlockedThreadIds() throws IOException {
0N/A ProxyClient proxyClient = vmPanel.getProxyClient();
0N/A ThreadMXBean threadMBean = proxyClient.getThreadMXBean();
0N/A
0N/A long[] ids = proxyClient.findDeadlockedThreads();
0N/A if (ids == null) {
0N/A return null;
0N/A }
0N/A ThreadInfo[] infos = threadMBean.getThreadInfo(ids, Integer.MAX_VALUE);
0N/A
0N/A List<Long[]> dcycles = new ArrayList<Long[]>();
0N/A List<Long> cycle = new ArrayList<Long>();
0N/A
0N/A // keep track of which thread is visited
0N/A // one thread can only be in one cycle
0N/A boolean[] visited = new boolean[ids.length];
0N/A
0N/A int deadlockedThread = -1; // Index into arrays
0N/A while (true) {
0N/A if (deadlockedThread < 0) {
0N/A if (cycle.size() > 0) {
0N/A // a cycle found
0N/A dcycles.add(cycle.toArray(new Long[0]));
0N/A cycle = new ArrayList<Long>();
0N/A }
0N/A // start a new cycle from a non-visited thread
0N/A for (int j = 0; j < ids.length; j++) {
0N/A if (!visited[j]) {
0N/A deadlockedThread = j;
0N/A visited[j] = true;
0N/A break;
0N/A }
0N/A }
0N/A if (deadlockedThread < 0) {
0N/A // done
0N/A break;
0N/A }
0N/A }
0N/A
0N/A cycle.add(ids[deadlockedThread]);
0N/A long nextThreadId = infos[deadlockedThread].getLockOwnerId();
0N/A for (int j = 0; j < ids.length; j++) {
0N/A ThreadInfo ti = infos[j];
0N/A if (ti.getThreadId() == nextThreadId) {
0N/A if (visited[j]) {
0N/A deadlockedThread = -1;
0N/A } else {
0N/A deadlockedThread = j;
0N/A visited[j] = true;
0N/A }
0N/A break;
0N/A }
0N/A }
0N/A }
0N/A return dcycles.toArray(new Long[0][0]);
0N/A }
0N/A
0N/A
0N/A
0N/A
0N/A
0N/A // ActionListener interface
0N/A public void actionPerformed(ActionEvent evt) {
0N/A String cmd = ((AbstractButton)evt.getSource()).getActionCommand();
0N/A
0N/A if (cmd == "detectDeadlock") {
0N/A messageLabel.setText("");
0N/A detectDeadlock();
0N/A }
0N/A }
0N/A
0N/A
0N/A
0N/A // DocumentListener interface
0N/A
0N/A public void insertUpdate(DocumentEvent e) {
0N/A doUpdate();
0N/A }
0N/A
0N/A public void removeUpdate(DocumentEvent e) {
0N/A doUpdate();
0N/A }
0N/A
0N/A public void changedUpdate(DocumentEvent e) {
0N/A doUpdate();
0N/A }
0N/A
0N/A
0N/A
5335N/A private class ThreadJList extends JList<Long> {
0N/A private JTextArea textArea;
0N/A
5335N/A ThreadJList(DefaultListModel<Long> listModel, JTextArea textArea) {
0N/A super(listModel);
0N/A
0N/A this.textArea = textArea;
0N/A
0N/A setBorder(thinEmptyBorder);
0N/A
0N/A addListSelectionListener(ThreadTab.this);
0N/A setCellRenderer(new DefaultListCellRenderer() {
5335N/A public Component getListCellRendererComponent(JList<?> list, Object value, int index,
0N/A boolean isSelected, boolean cellHasFocus) {
0N/A super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
0N/A
0N/A if (value != null) {
0N/A String name = nameCache.get(value);
0N/A if (name == null) {
0N/A name = value.toString();
0N/A }
0N/A setText(name);
0N/A }
0N/A return this;
0N/A }
0N/A });
0N/A }
0N/A
0N/A public Dimension getPreferredSize() {
0N/A Dimension d = super.getPreferredSize();
0N/A d.width = Math.max(d.width, 100);
0N/A return d;
0N/A }
0N/A }
0N/A
0N/A private class PromptingTextField extends JTextField implements FocusListener {
0N/A private String prompt;
0N/A boolean promptRemoved = false;
0N/A Color fg;
0N/A
0N/A public PromptingTextField(String prompt, int columns) {
0N/A super(prompt, columns);
0N/A
0N/A this.prompt = prompt;
0N/A updateForeground();
0N/A addFocusListener(this);
0N/A setAccessibleName(this, prompt);
0N/A }
0N/A
0N/A @Override
0N/A public void revalidate() {
0N/A super.revalidate();
0N/A updateForeground();
0N/A }
0N/A
0N/A private void updateForeground() {
0N/A this.fg = UIManager.getColor("TextField.foreground");
0N/A if (promptRemoved) {
0N/A setForeground(fg);
0N/A } else {
0N/A setForeground(Color.gray);
0N/A }
0N/A }
0N/A
0N/A public String getText() {
0N/A if (!promptRemoved) {
0N/A return "";
0N/A } else {
0N/A return super.getText();
0N/A }
0N/A }
0N/A
0N/A public void focusGained(FocusEvent e) {
0N/A if (!promptRemoved) {
0N/A setText("");
0N/A setForeground(fg);
0N/A promptRemoved = true;
0N/A }
0N/A }
0N/A
0N/A public void focusLost(FocusEvent e) {
0N/A if (promptRemoved && getText().equals("")) {
0N/A setText(prompt);
0N/A setForeground(Color.gray);
0N/A promptRemoved = false;
0N/A }
0N/A }
0N/A
0N/A }
0N/A
0N/A OverviewPanel[] getOverviewPanels() {
0N/A if (overviewPanel == null) {
0N/A overviewPanel = new ThreadOverviewPanel();
0N/A }
0N/A return new OverviewPanel[] { overviewPanel };
0N/A }
0N/A
0N/A
0N/A private static class ThreadOverviewPanel extends OverviewPanel {
0N/A ThreadOverviewPanel() {
5335N/A super(Messages.THREADS, threadCountKey, Messages.LIVE_THREADS, null);
0N/A }
0N/A
0N/A private void updateThreadsInfo(long tlCount, long tpCount, long ttCount, long timeStamp) {
0N/A getPlotter().addValues(timeStamp, tlCount);
5335N/A getInfoLabel().setText(Resources.format(infoLabelFormat, tlCount, tpCount, ttCount));
0N/A }
0N/A }
0N/A}