blob: e57c91a5e359ebd82eb6673e4893a8e717296b97 [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 2004-2006 Sun Microsystems, Inc. All Rights Reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25
26package sun.tools.jconsole;
27
28import java.awt.*;
29import java.awt.event.*;
30import java.beans.*;
31import java.io.*;
32import java.lang.reflect.*;
33import java.util.*;
34import java.util.List;
35import java.util.Timer;
36
37import javax.swing.*;
38import javax.swing.plaf.*;
39
40import com.sun.tools.jconsole.JConsolePlugin;
41import com.sun.tools.jconsole.JConsoleContext;
42import static com.sun.tools.jconsole.JConsoleContext.ConnectionState.*;
43
44import static sun.tools.jconsole.ProxyClient.*;
45
46@SuppressWarnings("serial")
47public class VMPanel extends JTabbedPane implements PropertyChangeListener {
48 private ProxyClient proxyClient;
49 private Timer timer;
50 private int updateInterval;
51 private String hostName;
52 private int port;
53 private int vmid;
54 private String userName;
55 private String password;
56 private String url;
57 private VMInternalFrame vmIF = null;
58
59 private static final String windowsLaF =
60 "com.sun.java.swing.plaf.windows.WindowsLookAndFeel";
61
62 private static ArrayList<TabInfo> tabInfos = new ArrayList<TabInfo>();
63
64 private boolean wasConnected = false;
65
66 // The everConnected flag keeps track of whether the window can be
67 // closed if the user clicks Cancel after a failed connection attempt.
68 //
69 private boolean everConnected = false;
70
71 // The initialUpdate flag is used to enable/disable tabs each time
72 // a connect or reconnect takes place. This flag avoids having to
73 // enable/disable tabs on each update call.
74 //
75 private boolean initialUpdate = true;
76
77 // Each VMPanel has its own instance of the JConsolePlugin
78 // A map of JConsolePlugin to the previous SwingWorker
79 private Map<JConsolePlugin, SwingWorker<?,?>> plugins = null;
80 private boolean pluginTabsAdded = false;
81
82 // Update these only on the EDT
83 private JOptionPane optionPane;
84 private JProgressBar progressBar;
85 private long time0;
86
87 static {
88 tabInfos.add(new TabInfo(OverviewTab.class, OverviewTab.getTabName(), true));
89 tabInfos.add(new TabInfo(MemoryTab.class, MemoryTab.getTabName(), true));
90 tabInfos.add(new TabInfo(ThreadTab.class, ThreadTab.getTabName(), true));
91 tabInfos.add(new TabInfo(ClassTab.class, ClassTab.getTabName(), true));
92 tabInfos.add(new TabInfo(SummaryTab.class, SummaryTab.getTabName(), true));
93 tabInfos.add(new TabInfo(MBeansTab.class, MBeansTab.getTabName(), true));
94 }
95
96 public static TabInfo[] getTabInfos() {
97 return tabInfos.toArray(new TabInfo[tabInfos.size()]);
98 }
99
100 VMPanel(ProxyClient proxyClient, int updateInterval) {
101 this.proxyClient = proxyClient;
102 this.updateInterval = updateInterval;
103 this.hostName = proxyClient.getHostName();
104 this.port = proxyClient.getPort();
105 this.vmid = proxyClient.getVmid();
106 this.userName = proxyClient.getUserName();
107 this.password = proxyClient.getPassword();
108 this.url = proxyClient.getUrl();
109
110 for (TabInfo tabInfo : tabInfos) {
111 if (tabInfo.tabVisible) {
112 addTab(tabInfo);
113 }
114 }
115
116 plugins = new LinkedHashMap<JConsolePlugin, SwingWorker<?,?>>();
117 for (JConsolePlugin p : JConsole.getPlugins()) {
118 p.setContext(proxyClient);
119 plugins.put(p, null);
120 }
121
122 Utilities.updateTransparency(this);
123
124 ToolTipManager.sharedInstance().registerComponent(this);
125
126 // Start listening to connection state events
127 //
128 proxyClient.addPropertyChangeListener(this);
129
130 addMouseListener(new MouseAdapter() {
131 public void mouseClicked(MouseEvent e) {
132 if (connectedIconBounds != null
133 && (e.getModifiers() & MouseEvent.BUTTON1_MASK) != 0
134 && connectedIconBounds.contains(e.getPoint())) {
135
136 if (isConnected()) {
137 disconnect();
138 wasConnected = false;
139 } else {
140 connect();
141 }
142 repaint();
143 }
144 }
145 });
146
147 }
148
149 private static Icon connectedIcon16 =
150 new ImageIcon(VMPanel.class.getResource("resources/connected16.png"));
151 private static Icon connectedIcon24 =
152 new ImageIcon(VMPanel.class.getResource("resources/connected24.png"));
153 private static Icon disconnectedIcon16 =
154 new ImageIcon(VMPanel.class.getResource("resources/disconnected16.png"));
155 private static Icon disconnectedIcon24 =
156 new ImageIcon(VMPanel.class.getResource("resources/disconnected24.png"));
157
158 private Rectangle connectedIconBounds;
159
160 // Override to increase right inset for tab area,
161 // in order to reserve space for the connect toggle.
162 public void setUI(TabbedPaneUI ui) {
163 Insets insets = (Insets)UIManager.getLookAndFeelDefaults().get("TabbedPane.tabAreaInsets");
164 insets = (Insets)insets.clone();
165 insets.right += connectedIcon24.getIconWidth() + 8;
166 UIManager.put("TabbedPane.tabAreaInsets", insets);
167 super.setUI(ui);
168 }
169
170 // Override to paint the connect toggle
171 protected void paintComponent(Graphics g) {
172 super.paintComponent(g);
173
174 Icon icon;
175 Component c0 = getComponent(0);
176 if (c0 != null && c0.getY() > 24) {
177 icon = isConnected() ? connectedIcon24 : disconnectedIcon24;
178 } else {
179 icon = isConnected() ? connectedIcon16 : disconnectedIcon16;
180 }
181 Insets insets = getInsets();
182 int x = getWidth() - insets.right - icon.getIconWidth() - 4;
183 int y = insets.top;
184 if (c0 != null) {
185 y = (c0.getY() - icon.getIconHeight()) / 2;
186 }
187 icon.paintIcon(this, g, x, y);
188 connectedIconBounds = new Rectangle(x, y, icon.getIconWidth(), icon.getIconHeight());
189 }
190
191 public String getToolTipText(MouseEvent event) {
192 if (connectedIconBounds.contains(event.getPoint())) {
193 if (isConnected()) {
194 return getText("Connected. Click to disconnect.");
195 } else {
196 return getText("Disconnected. Click to connect.");
197 }
198 } else {
199 return super.getToolTipText(event);
200 }
201 }
202
203 private synchronized void addTab(TabInfo tabInfo) {
204 Tab tab = instantiate(tabInfo);
205 if (tab != null) {
206 addTab(tabInfo.name, tab);
207 } else {
208 tabInfo.tabVisible = false;
209 }
210 }
211
212 private synchronized void insertTab(TabInfo tabInfo, int index) {
213 Tab tab = instantiate(tabInfo);
214 if (tab != null) {
215 insertTab(tabInfo.name, null, tab, null, index);
216 } else {
217 tabInfo.tabVisible = false;
218 }
219 }
220
221 public synchronized void removeTabAt(int index) {
222 super.removeTabAt(index);
223 }
224
225 private Tab instantiate(TabInfo tabInfo) {
226 try {
227 Constructor con = tabInfo.tabClass.getConstructor(VMPanel.class);
228 return (Tab)con.newInstance(this);
229 } catch (Exception ex) {
230 System.err.println(ex);
231 return null;
232 }
233 }
234
235 boolean isConnected() {
236 return proxyClient.isConnected();
237 }
238
239 public int getUpdateInterval() {
240 return updateInterval;
241 }
242
243 /**
244 * WARNING NEVER CALL THIS METHOD TO MAKE JMX REQUEST
245 * IF assertThread == false.
246 * DISPATCHER THREAD IS NOT ASSERTED.
247 * IT IS USED TO MAKE SOME LOCAL MANIPULATIONS.
248 */
249 ProxyClient getProxyClient(boolean assertThread) {
250 if(assertThread)
251 return getProxyClient();
252 else
253 return proxyClient;
254 }
255
256 public ProxyClient getProxyClient() {
257 String threadClass = Thread.currentThread().getClass().getName();
258 if (threadClass.equals("java.awt.EventDispatchThread")) {
259 String msg = "Calling VMPanel.getProxyClient() from the Event Dispatch Thread!";
260 new RuntimeException(msg).printStackTrace();
261 System.exit(1);
262 }
263 return proxyClient;
264 }
265
266 public void cleanUp() {
267 //proxyClient.disconnect();
268 for (Tab tab : getTabs()) {
269 tab.dispose();
270 }
271 for (JConsolePlugin p : plugins.keySet()) {
272 p.dispose();
273 }
274 // Cancel pending update tasks
275 //
276 if (timer != null) {
277 timer.cancel();
278 }
279 // Stop listening to connection state events
280 //
281 proxyClient.removePropertyChangeListener(this);
282 }
283
284 // Call on EDT
285 public void connect() {
286 if (isConnected()) {
287 // create plugin tabs if not done
288 createPluginTabs();
289 // Notify tabs
290 fireConnectedChange(true);
291 // Enable/disable tabs on initial update
292 initialUpdate = true;
293 // Start/Restart update timer on connect/reconnect
294 startUpdateTimer();
295 } else {
296 new Thread("VMPanel.connect") {
297 public void run() {
298 proxyClient.connect();
299 }
300 }.start();
301 }
302 }
303
304
305 // Call on EDT
306 public void disconnect() {
307 proxyClient.disconnect();
308 updateFrameTitle();
309 }
310
311
312
313 // Called on EDT
314 public void propertyChange(PropertyChangeEvent ev) {
315 String prop = ev.getPropertyName();
316
317 if (prop == CONNECTION_STATE_PROPERTY) {
318 ConnectionState oldState = (ConnectionState)ev.getOldValue();
319 ConnectionState newState = (ConnectionState)ev.getNewValue();
320 switch (newState) {
321 case CONNECTING:
322 onConnecting();
323 break;
324
325 case CONNECTED:
326 if (progressBar != null) {
327 progressBar.setIndeterminate(false);
328 progressBar.setValue(100);
329 }
330 closeOptionPane();
331 updateFrameTitle();
332 // create tabs if not done
333 createPluginTabs();
334 repaint();
335 // Notify tabs
336 fireConnectedChange(true);
337 // Enable/disable tabs on initial update
338 initialUpdate = true;
339 // Start/Restart update timer on connect/reconnect
340 startUpdateTimer();
341 break;
342
343 case DISCONNECTED:
344 if (progressBar != null) {
345 progressBar.setIndeterminate(false);
346 progressBar.setValue(0);
347 closeOptionPane();
348 }
349 vmPanelDied();
350 if (oldState == ConnectionState.CONNECTED) {
351 // Notify tabs
352 fireConnectedChange(false);
353 }
354 break;
355 }
356 }
357 }
358
359
360
361 // Called on EDT
362 private void onConnecting() {
363 time0 = System.currentTimeMillis();
364
365 final JConsole jc = (JConsole)SwingUtilities.getWindowAncestor(this);
366
367 String connectionName = getConnectionName();
368 progressBar = new JProgressBar();
369 progressBar.setIndeterminate(true);
370 JPanel progressPanel = new JPanel(new FlowLayout(FlowLayout.CENTER));
371 progressPanel.add(progressBar);
372
373 Object[] message = {
374 "<html><h3>" + getText("connectingTo1", connectionName) + "</h3></html>",
375 progressPanel,
376 "<html><b>" + getText("connectingTo2", connectionName) + "</b></html>"
377 };
378
379
380 optionPane =
381 SheetDialog.showOptionDialog(this,
382 message,
383 JOptionPane.DEFAULT_OPTION,
384 JOptionPane.INFORMATION_MESSAGE, null,
385 new String[] { getText("Cancel") },
386 0);
387
388
389 }
390
391 // Called on EDT
392 private void closeOptionPane() {
393 if (optionPane != null) {
394 new Thread("VMPanel.sleeper") {
395 public void run() {
396 long elapsed = System.currentTimeMillis() - time0;
397 if (elapsed < 2000) {
398 try {
399 sleep(2000 - elapsed);
400 } catch (InterruptedException ex) {
401 // Ignore
402 }
403 }
404 SwingUtilities.invokeLater(new Runnable() {
405 public void run() {
406 optionPane.setVisible(false);
407 progressBar = null;
408 }
409 });
410 }
411 }.start();
412 }
413 }
414
415 void updateFrameTitle() {
416 VMInternalFrame vmIF = getFrame();
417 if (vmIF != null) {
418 String displayName = getDisplayName();
419 if (!proxyClient.isConnected()) {
420 displayName = getText("ConnectionName (disconnected)", displayName);
421 }
422 vmIF.setTitle(displayName);
423 }
424 }
425
426 private VMInternalFrame getFrame() {
427 if (vmIF == null) {
428 vmIF = (VMInternalFrame)SwingUtilities.getAncestorOfClass(VMInternalFrame.class,
429 this);
430 }
431 return vmIF;
432 }
433
434 // TODO: this method is not needed when all JConsole tabs
435 // are migrated to use the new JConsolePlugin API.
436 //
437 // A thread safe clone of all JConsole tabs
438 synchronized List<Tab> getTabs() {
439 ArrayList<Tab> list = new ArrayList<Tab>();
440 int n = getTabCount();
441 for (int i = 0; i < n; i++) {
442 Component c = getComponentAt(i);
443 if (c instanceof Tab) {
444 list.add((Tab) c);
445 }
446 }
447 return list;
448 }
449
450 private void startUpdateTimer() {
451 if (timer != null) {
452 timer.cancel();
453 }
454 TimerTask timerTask = new TimerTask() {
455 public void run() {
456 update();
457 }
458 };
459 String timerName = "Timer-"+getConnectionName();
460 timer = new Timer(timerName, true);
461 timer.schedule(timerTask, 0, updateInterval);
462 }
463
464
465 // Call on EDT
466 private void vmPanelDied() {
467 disconnect();
468
469 final JConsole jc = (JConsole)SwingUtilities.getWindowAncestor(this);
470
471 JOptionPane optionPane;
472
473 final String connectStr = getText("Connect");
474 final String reconnectStr = getText("Reconnect");
475 final String cancelStr = getText("Cancel");
476
477 String msgTitle, msgExplanation, buttonStr;
478
479 if (wasConnected) {
480 wasConnected = false;
481 msgTitle = getText("connectionLost1");
482 msgExplanation = getText("connectionLost2", getConnectionName());
483 buttonStr = reconnectStr;
484 } else {
485 msgTitle = getText("connectionFailed1");
486 msgExplanation = getText("connectionFailed2", getConnectionName());
487 buttonStr = connectStr;
488 }
489
490 optionPane =
491 SheetDialog.showOptionDialog(this,
492 "<html><h3>" + msgTitle + "</h3>" +
493 "<b>" + msgExplanation + "</b>",
494 JOptionPane.DEFAULT_OPTION,
495 JOptionPane.WARNING_MESSAGE, null,
496 new String[] { buttonStr, cancelStr },
497 0);
498
499 optionPane.addPropertyChangeListener(new PropertyChangeListener() {
500 public void propertyChange(PropertyChangeEvent event) {
501 if (event.getPropertyName().equals(JOptionPane.VALUE_PROPERTY)) {
502 Object value = event.getNewValue();
503
504 if (value == reconnectStr || value == connectStr) {
505 connect();
506 } else if (!everConnected) {
507 try {
508 getFrame().setClosed(true);
509 } catch (PropertyVetoException ex) {
510 // Should not happen, but can be ignored.
511 }
512 }
513 }
514 }
515 });
516 }
517
518 // Note: This method is called on a TimerTask thread. Any GUI manipulation
519 // must be performed with invokeLater() or invokeAndWait().
520 private Object lockObject = new Object();
521 private void update() {
522 synchronized(lockObject) {
523 if (!isConnected()) {
524 if (wasConnected) {
525 EventQueue.invokeLater(new Runnable() {
526 public void run() {
527 vmPanelDied();
528 }
529 });
530 }
531 wasConnected = false;
532 return;
533 } else {
534 wasConnected = true;
535 everConnected = true;
536 }
537 proxyClient.flush();
538 List<Tab> tabs = getTabs();
539 final int n = tabs.size();
540 for (int i = 0; i < n; i++) {
541 final int index = i;
542 try {
543 if (!proxyClient.isDead()) {
544 // Update tab
545 //
546 tabs.get(index).update();
547 // Enable tab on initial update
548 //
549 if (initialUpdate) {
550 EventQueue.invokeLater(new Runnable() {
551 public void run() {
552 setEnabledAt(index, true);
553 }
554 });
555 }
556 }
557 } catch (Exception e) {
558 // Disable tab on initial update
559 //
560 if (initialUpdate) {
561 EventQueue.invokeLater(new Runnable() {
562 public void run() {
563 setEnabledAt(index, false);
564 }
565 });
566 }
567 }
568 }
569
570 // plugin GUI update
571 for (JConsolePlugin p : plugins.keySet()) {
572 SwingWorker<?,?> sw = p.newSwingWorker();
573 SwingWorker<?,?> prevSW = plugins.get(p);
574 // schedule SwingWorker to run only if the previous
575 // SwingWorker has finished its task and it hasn't started.
576 if (prevSW == null || prevSW.isDone()) {
577 if (sw == null || sw.getState() == SwingWorker.StateValue.PENDING) {
578 plugins.put(p, sw);
579 if (sw != null) {
580 sw.execute();
581 }
582 }
583 }
584 }
585
586 // Set the first enabled tab in the tabĀ“s list
587 // as the selected tab on initial update
588 //
589 if (initialUpdate) {
590 EventQueue.invokeLater(new Runnable() {
591 public void run() {
592 // Select first enabled tab if current tab isn't.
593 int index = getSelectedIndex();
594 if (index < 0 || !isEnabledAt(index)) {
595 for (int i = 0; i < n; i++) {
596 if (isEnabledAt(i)) {
597 setSelectedIndex(i);
598 break;
599 }
600 }
601 }
602 }
603 });
604 initialUpdate = false;
605 }
606 }
607 }
608
609 public String getHostName() {
610 return hostName;
611 }
612
613 public int getPort() {
614 return port;
615 }
616
617 public String getUserName() {
618 return userName;
619 }
620
621 public String getUrl() {
622 return url;
623 }
624
625
626 public String getPassword() {
627 return password;
628 }
629
630 public String getConnectionName() {
631 return proxyClient.connectionName();
632 }
633
634 public String getDisplayName() {
635 return proxyClient.getDisplayName();
636 }
637
638 static class TabInfo {
639 Class<? extends Tab> tabClass;
640 String name;
641 boolean tabVisible;
642
643 TabInfo(Class<? extends Tab> tabClass, String name, boolean tabVisible) {
644 this.tabClass = tabClass;
645 this.name = name;
646 this.tabVisible = tabVisible;
647 }
648 }
649
650 // Convenience methods
651 private static String getText(String key, Object... args) {
652 return Resources.getText(key, args);
653 }
654
655 private void createPluginTabs() {
656 // add plugin tabs if not done
657 if (!pluginTabsAdded) {
658 for (JConsolePlugin p : plugins.keySet()) {
659 Map<String, JPanel> tabs = p.getTabs();
660 for (Map.Entry<String, JPanel> e : tabs.entrySet()) {
661 addTab(e.getKey(), e.getValue());
662 }
663 }
664 pluginTabsAdded = true;
665 }
666 }
667
668 private void fireConnectedChange(boolean connected) {
669 for (Tab tab : getTabs()) {
670 tab.firePropertyChange(JConsoleContext.CONNECTION_STATE_PROPERTY, !connected, connected);
671 }
672 }
673}