mirror of
https://github.com/openjdk/jdk.git
synced 2026-03-10 08:01:54 +00:00
671 lines
22 KiB
Java
671 lines
22 KiB
Java
/*
|
|
* Copyright (c) 2004, 2013, Oracle and/or its affiliates. All rights reserved.
|
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
|
*
|
|
* This code is free software; you can redistribute it and/or modify it
|
|
* under the terms of the GNU General Public License version 2 only, as
|
|
* published by the Free Software Foundation. Oracle designates this
|
|
* particular file as subject to the "Classpath" exception as provided
|
|
* by Oracle in the LICENSE file that accompanied this code.
|
|
*
|
|
* This code is distributed in the hope that it will be useful, but WITHOUT
|
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
* version 2 for more details (a copy is included in the LICENSE file that
|
|
* accompanied this code).
|
|
*
|
|
* You should have received a copy of the GNU General Public License version
|
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
*
|
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
|
* or visit www.oracle.com if you need additional information or have any
|
|
* questions.
|
|
*/
|
|
|
|
package sun.tools.jconsole;
|
|
|
|
import java.awt.*;
|
|
import java.awt.event.*;
|
|
import java.beans.*;
|
|
import java.lang.reflect.*;
|
|
import java.util.*;
|
|
import java.util.List;
|
|
import java.util.Timer;
|
|
import javax.swing.*;
|
|
import javax.swing.plaf.*;
|
|
|
|
|
|
import com.sun.tools.jconsole.JConsolePlugin;
|
|
import com.sun.tools.jconsole.JConsoleContext;
|
|
|
|
import static sun.tools.jconsole.ProxyClient.*;
|
|
|
|
@SuppressWarnings("serial")
|
|
public class VMPanel extends JTabbedPane implements PropertyChangeListener {
|
|
|
|
private ProxyClient proxyClient;
|
|
private Timer timer;
|
|
private int updateInterval;
|
|
private String hostName;
|
|
private int port;
|
|
private String userName;
|
|
private String password;
|
|
private String url;
|
|
private VMInternalFrame vmIF = null;
|
|
private static ArrayList<TabInfo> tabInfos = new ArrayList<TabInfo>();
|
|
private boolean wasConnected = false;
|
|
private boolean userDisconnected = false;
|
|
private boolean shouldUseSSL = true;
|
|
|
|
// The everConnected flag keeps track of whether the window can be
|
|
// closed if the user clicks Cancel after a failed connection attempt.
|
|
//
|
|
private boolean everConnected = false;
|
|
|
|
// The initialUpdate flag is used to enable/disable tabs each time
|
|
// a connect or reconnect takes place. This flag avoids having to
|
|
// enable/disable tabs on each update call.
|
|
//
|
|
private boolean initialUpdate = true;
|
|
|
|
// Each VMPanel has its own instance of the JConsolePlugin
|
|
// A map of JConsolePlugin to the previous SwingWorker
|
|
private Map<ExceptionSafePlugin, SwingWorker<?, ?>> plugins = null;
|
|
private boolean pluginTabsAdded = false;
|
|
|
|
// Update these only on the EDT
|
|
private JOptionPane optionPane;
|
|
private JProgressBar progressBar;
|
|
private long time0;
|
|
|
|
static {
|
|
tabInfos.add(new TabInfo(OverviewTab.class, OverviewTab.getTabName(), true));
|
|
tabInfos.add(new TabInfo(MemoryTab.class, MemoryTab.getTabName(), true));
|
|
tabInfos.add(new TabInfo(ThreadTab.class, ThreadTab.getTabName(), true));
|
|
tabInfos.add(new TabInfo(ClassTab.class, ClassTab.getTabName(), true));
|
|
tabInfos.add(new TabInfo(SummaryTab.class, SummaryTab.getTabName(), true));
|
|
tabInfos.add(new TabInfo(MBeansTab.class, MBeansTab.getTabName(), true));
|
|
}
|
|
|
|
public static TabInfo[] getTabInfos() {
|
|
return tabInfos.toArray(new TabInfo[tabInfos.size()]);
|
|
}
|
|
|
|
VMPanel(ProxyClient proxyClient, int updateInterval) {
|
|
this.proxyClient = proxyClient;
|
|
this.updateInterval = updateInterval;
|
|
this.hostName = proxyClient.getHostName();
|
|
this.port = proxyClient.getPort();
|
|
this.userName = proxyClient.getUserName();
|
|
this.password = proxyClient.getPassword();
|
|
this.url = proxyClient.getUrl();
|
|
|
|
for (TabInfo tabInfo : tabInfos) {
|
|
if (tabInfo.tabVisible) {
|
|
addTab(tabInfo);
|
|
}
|
|
}
|
|
|
|
plugins = new LinkedHashMap<ExceptionSafePlugin, SwingWorker<?, ?>>();
|
|
for (JConsolePlugin p : JConsole.getPlugins()) {
|
|
p.setContext(proxyClient);
|
|
plugins.put(new ExceptionSafePlugin(p), null);
|
|
}
|
|
|
|
Utilities.updateTransparency(this);
|
|
|
|
ToolTipManager.sharedInstance().registerComponent(this);
|
|
|
|
// Start listening to connection state events
|
|
//
|
|
proxyClient.addPropertyChangeListener(this);
|
|
|
|
addMouseListener(new MouseAdapter() {
|
|
|
|
public void mouseClicked(MouseEvent e) {
|
|
if (connectedIconBounds != null
|
|
&& (e.getModifiersEx() & MouseEvent.BUTTON1_DOWN_MASK) != 0
|
|
&& connectedIconBounds.contains(e.getPoint())) {
|
|
|
|
if (isConnected()) {
|
|
userDisconnected = true;
|
|
disconnect();
|
|
wasConnected = false;
|
|
} else {
|
|
connect();
|
|
}
|
|
repaint();
|
|
}
|
|
}
|
|
});
|
|
|
|
}
|
|
private static Icon connectedIcon16 =
|
|
new ImageIcon(VMPanel.class.getResource("resources/connected16.png"));
|
|
private static Icon connectedIcon24 =
|
|
new ImageIcon(VMPanel.class.getResource("resources/connected24.png"));
|
|
private static Icon disconnectedIcon16 =
|
|
new ImageIcon(VMPanel.class.getResource("resources/disconnected16.png"));
|
|
private static Icon disconnectedIcon24 =
|
|
new ImageIcon(VMPanel.class.getResource("resources/disconnected24.png"));
|
|
private Rectangle connectedIconBounds;
|
|
|
|
// Override to increase right inset for tab area,
|
|
// in order to reserve space for the connect toggle.
|
|
public void setUI(TabbedPaneUI ui) {
|
|
Insets insets = (Insets) UIManager.getLookAndFeelDefaults().get("TabbedPane.tabAreaInsets");
|
|
if (insets != null) {
|
|
insets = (Insets) insets.clone();
|
|
insets.right += connectedIcon24.getIconWidth() + 8;
|
|
UIManager.put("TabbedPane.tabAreaInsets", insets);
|
|
}
|
|
super.setUI(ui);
|
|
}
|
|
|
|
// Override to paint the connect toggle
|
|
protected void paintComponent(Graphics g) {
|
|
super.paintComponent(g);
|
|
|
|
Icon icon;
|
|
Component c0 = getComponent(0);
|
|
if (c0 != null && c0.getY() > 24) {
|
|
icon = isConnected() ? connectedIcon24 : disconnectedIcon24;
|
|
} else {
|
|
icon = isConnected() ? connectedIcon16 : disconnectedIcon16;
|
|
}
|
|
Insets insets = getInsets();
|
|
int x = getWidth() - insets.right - icon.getIconWidth() - 4;
|
|
int y = insets.top;
|
|
if (c0 != null) {
|
|
y = (c0.getY() - icon.getIconHeight()) / 2;
|
|
}
|
|
icon.paintIcon(this, g, x, y);
|
|
connectedIconBounds = new Rectangle(x, y, icon.getIconWidth(), icon.getIconHeight());
|
|
}
|
|
|
|
public String getToolTipText(MouseEvent event) {
|
|
if (connectedIconBounds.contains(event.getPoint())) {
|
|
if (isConnected()) {
|
|
return Messages.CONNECTED_PUNCTUATION_CLICK_TO_DISCONNECT_;
|
|
} else {
|
|
return Messages.DISCONNECTED_PUNCTUATION_CLICK_TO_CONNECT_;
|
|
}
|
|
} else {
|
|
return super.getToolTipText(event);
|
|
}
|
|
}
|
|
|
|
private synchronized void addTab(TabInfo tabInfo) {
|
|
Tab tab = instantiate(tabInfo);
|
|
if (tab != null) {
|
|
addTab(tabInfo.name, tab);
|
|
} else {
|
|
tabInfo.tabVisible = false;
|
|
}
|
|
}
|
|
|
|
private synchronized void insertTab(TabInfo tabInfo, int index) {
|
|
Tab tab = instantiate(tabInfo);
|
|
if (tab != null) {
|
|
insertTab(tabInfo.name, null, tab, null, index);
|
|
} else {
|
|
tabInfo.tabVisible = false;
|
|
}
|
|
}
|
|
|
|
public synchronized void removeTabAt(int index) {
|
|
super.removeTabAt(index);
|
|
}
|
|
|
|
private Tab instantiate(TabInfo tabInfo) {
|
|
try {
|
|
Constructor<?> con = tabInfo.tabClass.getConstructor(VMPanel.class);
|
|
return (Tab) con.newInstance(this);
|
|
} catch (Exception ex) {
|
|
System.err.println(ex);
|
|
return null;
|
|
}
|
|
}
|
|
|
|
boolean isConnected() {
|
|
return proxyClient.isConnected();
|
|
}
|
|
|
|
public int getUpdateInterval() {
|
|
return updateInterval;
|
|
}
|
|
|
|
/**
|
|
* WARNING NEVER CALL THIS METHOD TO MAKE JMX REQUEST
|
|
* IF assertThread == false.
|
|
* DISPATCHER THREAD IS NOT ASSERTED.
|
|
* IT IS USED TO MAKE SOME LOCAL MANIPULATIONS.
|
|
*/
|
|
ProxyClient getProxyClient(boolean assertThread) {
|
|
if (assertThread) {
|
|
return getProxyClient();
|
|
} else {
|
|
return proxyClient;
|
|
}
|
|
}
|
|
|
|
public ProxyClient getProxyClient() {
|
|
String threadClass = Thread.currentThread().getClass().getName();
|
|
if (threadClass.equals("java.awt.EventDispatchThread")) {
|
|
String msg = "Calling VMPanel.getProxyClient() from the Event Dispatch Thread!";
|
|
new RuntimeException(msg).printStackTrace();
|
|
System.exit(1);
|
|
}
|
|
return proxyClient;
|
|
}
|
|
|
|
public void cleanUp() {
|
|
//proxyClient.disconnect();
|
|
for (Tab tab : getTabs()) {
|
|
tab.dispose();
|
|
}
|
|
for (JConsolePlugin p : plugins.keySet()) {
|
|
p.dispose();
|
|
}
|
|
// Cancel pending update tasks
|
|
//
|
|
if (timer != null) {
|
|
timer.cancel();
|
|
}
|
|
// Stop listening to connection state events
|
|
//
|
|
proxyClient.removePropertyChangeListener(this);
|
|
}
|
|
|
|
// Call on EDT
|
|
public void connect() {
|
|
if (isConnected()) {
|
|
// create plugin tabs if not done
|
|
createPluginTabs();
|
|
// Notify tabs
|
|
fireConnectedChange(true);
|
|
// Enable/disable tabs on initial update
|
|
initialUpdate = true;
|
|
// Start/Restart update timer on connect/reconnect
|
|
startUpdateTimer();
|
|
} else {
|
|
new Thread("VMPanel.connect") {
|
|
|
|
public void run() {
|
|
proxyClient.connect(shouldUseSSL);
|
|
}
|
|
}.start();
|
|
}
|
|
}
|
|
|
|
// Call on EDT
|
|
public void disconnect() {
|
|
proxyClient.disconnect();
|
|
updateFrameTitle();
|
|
}
|
|
|
|
// Called on EDT
|
|
public void propertyChange(PropertyChangeEvent ev) {
|
|
String prop = ev.getPropertyName();
|
|
|
|
if (prop == CONNECTION_STATE_PROPERTY) {
|
|
ConnectionState oldState = (ConnectionState) ev.getOldValue();
|
|
ConnectionState newState = (ConnectionState) ev.getNewValue();
|
|
switch (newState) {
|
|
case CONNECTING:
|
|
onConnecting();
|
|
break;
|
|
|
|
case CONNECTED:
|
|
if (progressBar != null) {
|
|
progressBar.setIndeterminate(false);
|
|
progressBar.setValue(100);
|
|
}
|
|
closeOptionPane();
|
|
updateFrameTitle();
|
|
// create tabs if not done
|
|
createPluginTabs();
|
|
repaint();
|
|
// Notify tabs
|
|
fireConnectedChange(true);
|
|
// Enable/disable tabs on initial update
|
|
initialUpdate = true;
|
|
// Start/Restart update timer on connect/reconnect
|
|
startUpdateTimer();
|
|
break;
|
|
|
|
case DISCONNECTED:
|
|
if (progressBar != null) {
|
|
progressBar.setIndeterminate(false);
|
|
progressBar.setValue(0);
|
|
closeOptionPane();
|
|
}
|
|
vmPanelDied();
|
|
if (oldState == ConnectionState.CONNECTED) {
|
|
// Notify tabs
|
|
fireConnectedChange(false);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Called on EDT
|
|
private void onConnecting() {
|
|
time0 = System.currentTimeMillis();
|
|
|
|
SwingUtilities.getWindowAncestor(this);
|
|
|
|
String connectionName = getConnectionName();
|
|
progressBar = new JProgressBar();
|
|
progressBar.setIndeterminate(true);
|
|
JPanel progressPanel = new JPanel(new FlowLayout(FlowLayout.CENTER));
|
|
progressPanel.add(progressBar);
|
|
|
|
Object[] message = {
|
|
"<html><h3>" + Resources.format(Messages.CONNECTING_TO1, connectionName) + "</h3></html>",
|
|
progressPanel,
|
|
"<html><b>" + Resources.format(Messages.CONNECTING_TO2, connectionName) + "</b></html>"
|
|
};
|
|
|
|
optionPane =
|
|
SheetDialog.showOptionDialog(this,
|
|
message,
|
|
JOptionPane.DEFAULT_OPTION,
|
|
JOptionPane.INFORMATION_MESSAGE, null,
|
|
new String[]{Messages.CANCEL},
|
|
0);
|
|
|
|
|
|
}
|
|
|
|
// Called on EDT
|
|
private void closeOptionPane() {
|
|
if (optionPane != null) {
|
|
new Thread("VMPanel.sleeper") {
|
|
public void run() {
|
|
long elapsed = System.currentTimeMillis() - time0;
|
|
if (elapsed < 2000) {
|
|
try {
|
|
sleep(2000 - elapsed);
|
|
} catch (InterruptedException ex) {
|
|
// Ignore
|
|
}
|
|
}
|
|
SwingUtilities.invokeLater(new Runnable() {
|
|
|
|
public void run() {
|
|
optionPane.setVisible(false);
|
|
progressBar = null;
|
|
}
|
|
});
|
|
}
|
|
}.start();
|
|
}
|
|
}
|
|
|
|
void updateFrameTitle() {
|
|
VMInternalFrame vmIF = getFrame();
|
|
if (vmIF != null) {
|
|
String displayName = getDisplayName();
|
|
if (!proxyClient.isConnected()) {
|
|
displayName = Resources.format(Messages.CONNECTION_NAME__DISCONNECTED_, displayName);
|
|
}
|
|
vmIF.setTitle(displayName);
|
|
}
|
|
}
|
|
|
|
private VMInternalFrame getFrame() {
|
|
if (vmIF == null) {
|
|
vmIF = (VMInternalFrame) SwingUtilities.getAncestorOfClass(VMInternalFrame.class,
|
|
this);
|
|
}
|
|
return vmIF;
|
|
}
|
|
|
|
// TODO: this method is not needed when all JConsole tabs
|
|
// are migrated to use the new JConsolePlugin API.
|
|
//
|
|
// A thread safe clone of all JConsole tabs
|
|
synchronized List<Tab> getTabs() {
|
|
ArrayList<Tab> list = new ArrayList<Tab>();
|
|
int n = getTabCount();
|
|
for (int i = 0; i < n; i++) {
|
|
Component c = getComponentAt(i);
|
|
if (c instanceof Tab) {
|
|
list.add((Tab) c);
|
|
}
|
|
}
|
|
return list;
|
|
}
|
|
|
|
private void startUpdateTimer() {
|
|
if (timer != null) {
|
|
timer.cancel();
|
|
}
|
|
TimerTask timerTask = new TimerTask() {
|
|
|
|
public void run() {
|
|
update();
|
|
}
|
|
};
|
|
String timerName = "Timer-" + getConnectionName();
|
|
timer = new Timer(timerName, true);
|
|
timer.schedule(timerTask, 0, updateInterval);
|
|
}
|
|
|
|
// Call on EDT
|
|
private void vmPanelDied() {
|
|
disconnect();
|
|
|
|
if (userDisconnected) {
|
|
userDisconnected = false;
|
|
return;
|
|
}
|
|
|
|
JOptionPane optionPane;
|
|
String msgTitle, msgExplanation, buttonStr;
|
|
|
|
if (wasConnected) {
|
|
wasConnected = false;
|
|
msgTitle = Messages.CONNECTION_LOST1;
|
|
msgExplanation = Resources.format(Messages.CONNECTING_TO2, getConnectionName());
|
|
buttonStr = Messages.RECONNECT;
|
|
} else if (shouldUseSSL) {
|
|
msgTitle = Messages.CONNECTION_FAILED_SSL1;
|
|
msgExplanation = Resources.format(Messages.CONNECTION_FAILED_SSL2, getConnectionName());
|
|
buttonStr = Messages.INSECURE;
|
|
} else {
|
|
msgTitle = Messages.CONNECTION_FAILED1;
|
|
msgExplanation = Resources.format(Messages.CONNECTION_FAILED2, getConnectionName());
|
|
buttonStr = Messages.CONNECT;
|
|
}
|
|
|
|
optionPane =
|
|
SheetDialog.showOptionDialog(this,
|
|
"<html><h3>" + msgTitle + "</h3>" +
|
|
"<b>" + msgExplanation + "</b>",
|
|
JOptionPane.DEFAULT_OPTION,
|
|
JOptionPane.WARNING_MESSAGE, null,
|
|
new String[]{buttonStr, Messages.CANCEL},
|
|
0);
|
|
|
|
optionPane.addPropertyChangeListener(new PropertyChangeListener() {
|
|
|
|
public void propertyChange(PropertyChangeEvent event) {
|
|
if (event.getPropertyName().equals(JOptionPane.VALUE_PROPERTY)) {
|
|
Object value = event.getNewValue();
|
|
|
|
if (value == Messages.RECONNECT || value == Messages.CONNECT) {
|
|
connect();
|
|
} else if (value == Messages.INSECURE) {
|
|
shouldUseSSL = false;
|
|
connect();
|
|
} else if (!everConnected) {
|
|
try {
|
|
getFrame().setClosed(true);
|
|
} catch (PropertyVetoException ex) {
|
|
// Should not happen, but can be ignored.
|
|
}
|
|
}
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
// Note: This method is called on a TimerTask thread. Any GUI manipulation
|
|
// must be performed with invokeLater() or invokeAndWait().
|
|
private Object lockObject = new Object();
|
|
|
|
private void update() {
|
|
synchronized (lockObject) {
|
|
if (!isConnected()) {
|
|
if (wasConnected) {
|
|
EventQueue.invokeLater(new Runnable() {
|
|
|
|
public void run() {
|
|
vmPanelDied();
|
|
}
|
|
});
|
|
}
|
|
wasConnected = false;
|
|
return;
|
|
} else {
|
|
wasConnected = true;
|
|
everConnected = true;
|
|
}
|
|
proxyClient.flush();
|
|
List<Tab> tabs = getTabs();
|
|
final int n = tabs.size();
|
|
for (int i = 0; i < n; i++) {
|
|
final int index = i;
|
|
try {
|
|
if (!proxyClient.isDead()) {
|
|
// Update tab
|
|
//
|
|
tabs.get(index).update();
|
|
// Enable tab on initial update
|
|
//
|
|
if (initialUpdate) {
|
|
EventQueue.invokeLater(new Runnable() {
|
|
|
|
public void run() {
|
|
setEnabledAt(index, true);
|
|
}
|
|
});
|
|
}
|
|
}
|
|
} catch (Exception e) {
|
|
// Disable tab on initial update
|
|
//
|
|
if (initialUpdate) {
|
|
EventQueue.invokeLater(new Runnable() {
|
|
public void run() {
|
|
setEnabledAt(index, false);
|
|
}
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
// plugin GUI update
|
|
for (ExceptionSafePlugin p : plugins.keySet()) {
|
|
SwingWorker<?, ?> sw = p.newSwingWorker();
|
|
SwingWorker<?, ?> prevSW = plugins.get(p);
|
|
// schedule SwingWorker to run only if the previous
|
|
// SwingWorker has finished its task and it hasn't started.
|
|
if (prevSW == null || prevSW.isDone()) {
|
|
if (sw == null || sw.getState() == SwingWorker.StateValue.PENDING) {
|
|
plugins.put(p, sw);
|
|
if (sw != null) {
|
|
p.executeSwingWorker(sw);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Set the first enabled tab in the tab's list
|
|
// as the selected tab on initial update
|
|
//
|
|
if (initialUpdate) {
|
|
EventQueue.invokeLater(new Runnable() {
|
|
public void run() {
|
|
// Select first enabled tab if current tab isn't.
|
|
int index = getSelectedIndex();
|
|
if (index < 0 || !isEnabledAt(index)) {
|
|
for (int i = 0; i < n; i++) {
|
|
if (isEnabledAt(i)) {
|
|
setSelectedIndex(i);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
});
|
|
initialUpdate = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
public String getHostName() {
|
|
return hostName;
|
|
}
|
|
|
|
public int getPort() {
|
|
return port;
|
|
}
|
|
|
|
public String getUserName() {
|
|
return userName;
|
|
}
|
|
|
|
public String getUrl() {
|
|
return url;
|
|
}
|
|
|
|
public String getPassword() {
|
|
return password;
|
|
}
|
|
|
|
public String getConnectionName() {
|
|
return proxyClient.connectionName();
|
|
}
|
|
|
|
public String getDisplayName() {
|
|
return proxyClient.getDisplayName();
|
|
}
|
|
|
|
static class TabInfo {
|
|
|
|
Class<? extends Tab> tabClass;
|
|
String name;
|
|
boolean tabVisible;
|
|
|
|
TabInfo(Class<? extends Tab> tabClass, String name, boolean tabVisible) {
|
|
this.tabClass = tabClass;
|
|
this.name = name;
|
|
this.tabVisible = tabVisible;
|
|
}
|
|
}
|
|
|
|
private void createPluginTabs() {
|
|
// add plugin tabs if not done
|
|
if (!pluginTabsAdded) {
|
|
for (JConsolePlugin p : plugins.keySet()) {
|
|
Map<String, JPanel> tabs = p.getTabs();
|
|
for (Map.Entry<String, JPanel> e : tabs.entrySet()) {
|
|
addTab(e.getKey(), e.getValue());
|
|
}
|
|
}
|
|
pluginTabsAdded = true;
|
|
}
|
|
}
|
|
|
|
private void fireConnectedChange(boolean connected) {
|
|
for (Tab tab : getTabs()) {
|
|
tab.firePropertyChange(JConsoleContext.CONNECTION_STATE_PROPERTY, !connected, connected);
|
|
}
|
|
}
|
|
}
|