1 /*
   2  * Copyright (c) 2004, 2006, Oracle and/or its affiliates. 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.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package sun.tools.jconsole;
  27 
  28 import java.util.List;
  29 import java.awt.*;
  30 import java.awt.event.*;
  31 import java.util.*;
  32 import java.net.MalformedURLException;
  33 import java.io.IOException;
  34 
  35 import javax.accessibility.*;
  36 import javax.swing.*;
  37 import javax.swing.Timer;
  38 import javax.swing.border.*;
  39 import javax.swing.event.*;
  40 import javax.swing.plaf.basic.BasicRadioButtonUI;
  41 import javax.swing.table.*;
  42 
  43 import javax.management.remote.JMXServiceURL;
  44 import javax.management.remote.JMXConnector;
  45 
  46 import static java.awt.BorderLayout.*;
  47 import static javax.swing.ListSelectionModel.*;
  48 import static sun.tools.jconsole.Resources.*;
  49 import static sun.tools.jconsole.Utilities.*;
  50 
  51 @SuppressWarnings("serial")
  52 public class ConnectDialog extends InternalDialog
  53                 implements DocumentListener, FocusListener,
  54                            ItemListener, ListSelectionListener, KeyListener {
  55 
  56     private static final int COL_NAME = 0;
  57     private static final int COL_PID  = 1;
  58 
  59 
  60     JConsole jConsole;
  61     JTextField userNameTF, passwordTF;
  62     JRadioButton localRadioButton, remoteRadioButton;
  63     JLabel localMessageLabel, remoteMessageLabel;
  64     JTextField remoteTF;
  65     JButton connectButton, cancelButton;
  66     JPanel radioButtonPanel;
  67 
  68     private Icon mastheadIcon =
  69         new MastheadIcon(getText("ConnectDialog.masthead.title"));
  70     private Color hintTextColor, disabledTableCellColor;
  71 
  72     // The table of managed VM (local process)
  73     JTable vmTable;
  74     ManagedVmTableModel vmModel = null;
  75 
  76     JScrollPane localTableScrollPane = null;
  77 
  78     private Action connectAction, cancelAction;
  79 
  80 
  81     public ConnectDialog(JConsole jConsole) {
  82         super(jConsole, Resources.getText("ConnectDialog.title"), true);
  83 
  84         this.jConsole = jConsole;
  85         setAccessibleDescription(this,
  86                                  getText("ConnectDialog.accessibleDescription"));
  87         setDefaultCloseOperation(HIDE_ON_CLOSE);
  88         setResizable(false);
  89         Container cp = (JComponent)getContentPane();
  90 
  91         radioButtonPanel = new JPanel(new BorderLayout(0, 12));
  92         radioButtonPanel.setBorder(new EmptyBorder(6, 12, 12, 12));
  93         ButtonGroup radioButtonGroup = new ButtonGroup();
  94         JPanel bottomPanel = new JPanel(new BorderLayout());
  95 
  96         statusBar = new JLabel(" ", JLabel.CENTER);
  97         setAccessibleName(statusBar,
  98                           getText("ConnectDialog.statusBar.accessibleName"));
  99 
 100         Font normalLabelFont = statusBar.getFont();
 101         Font boldLabelFont = normalLabelFont.deriveFont(Font.BOLD);
 102         Font smallLabelFont = normalLabelFont.deriveFont(normalLabelFont.getSize2D() - 1);
 103 
 104         JLabel mastheadLabel = new JLabel(mastheadIcon);
 105         setAccessibleName(mastheadLabel,
 106                           getText("ConnectDialog.masthead.accessibleName"));
 107 
 108         cp.add(mastheadLabel, NORTH);
 109         cp.add(radioButtonPanel, CENTER);
 110         cp.add(bottomPanel, SOUTH);
 111 
 112         createActions();
 113 
 114         remoteTF = new JTextField();
 115         remoteTF.addActionListener(connectAction);
 116         remoteTF.getDocument().addDocumentListener(this);
 117         remoteTF.addFocusListener(this);
 118         remoteTF.setPreferredSize(remoteTF.getPreferredSize());
 119         setAccessibleName(remoteTF,
 120                           getText("Remote Process.textField.accessibleName"));
 121 
 122         //
 123         // If the VM supports the local attach mechanism (is: Sun
 124         // implementation) then the Local Process panel is created.
 125         //
 126         if (JConsole.isLocalAttachAvailable()) {
 127             vmModel = new ManagedVmTableModel();
 128             vmTable = new LocalTabJTable(vmModel);
 129             vmTable.setSelectionMode(SINGLE_SELECTION);
 130             vmTable.setPreferredScrollableViewportSize(new Dimension(400, 250));
 131             vmTable.setColumnSelectionAllowed(false);
 132             vmTable.addFocusListener(this);
 133             vmTable.getSelectionModel().addListSelectionListener(this);
 134 
 135             TableColumnModel columnModel = vmTable.getColumnModel();
 136 
 137             TableColumn pidColumn = columnModel.getColumn(COL_PID);
 138             pidColumn.setMaxWidth(getLabelWidth("9999999"));
 139             pidColumn.setResizable(false);
 140 
 141             TableColumn cmdLineColumn = columnModel.getColumn(COL_NAME);
 142             cmdLineColumn.setResizable(false);
 143 
 144             localRadioButton = new JRadioButton(getText("Local Process:"));
 145             localRadioButton.setMnemonic(getMnemonicInt("Local Process:"));
 146             localRadioButton.setFont(boldLabelFont);
 147             localRadioButton.addItemListener(this);
 148             radioButtonGroup.add(localRadioButton);
 149 
 150             JPanel localPanel = new JPanel(new BorderLayout());
 151 
 152             JPanel localTablePanel = new JPanel(new BorderLayout());
 153 
 154             radioButtonPanel.add(localPanel, NORTH);
 155 
 156             localPanel.add(localRadioButton, NORTH);
 157             localPanel.add(new Padder(localRadioButton), LINE_START);
 158             localPanel.add(localTablePanel, CENTER);
 159 
 160             localTableScrollPane = new JScrollPane(vmTable);
 161 
 162             localTablePanel.add(localTableScrollPane, NORTH);
 163 
 164             localMessageLabel = new JLabel(" ");
 165             localMessageLabel.setFont(smallLabelFont);
 166             localMessageLabel.setForeground(hintTextColor);
 167             localTablePanel.add(localMessageLabel, SOUTH);
 168         }
 169 
 170         remoteRadioButton = new JRadioButton(getText("Remote Process:"));
 171         remoteRadioButton.setMnemonic(getMnemonicInt("Remote Process:"));
 172         remoteRadioButton.setFont(boldLabelFont);
 173         radioButtonGroup.add(remoteRadioButton);
 174 
 175         JPanel remotePanel = new JPanel(new BorderLayout());
 176         if (localRadioButton != null) {
 177             remotePanel.add(remoteRadioButton, NORTH);
 178             remotePanel.add(new Padder(remoteRadioButton), LINE_START);
 179 
 180             Action nextRadioButtonAction =
 181                 new AbstractAction("nextRadioButton") {
 182                     public void actionPerformed(ActionEvent ev) {
 183                         JRadioButton rb =
 184                             (ev.getSource() == localRadioButton) ? remoteRadioButton
 185                                                                  : localRadioButton;
 186                         rb.doClick();
 187                         rb.requestFocus();
 188                     }
 189                 };
 190 
 191             localRadioButton.getActionMap().put("nextRadioButton", nextRadioButtonAction);
 192             remoteRadioButton.getActionMap().put("nextRadioButton", nextRadioButtonAction);
 193 
 194             localRadioButton.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0),
 195                                                "nextRadioButton");
 196             remoteRadioButton.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0),
 197                                                 "nextRadioButton");
 198         } else {
 199             JLabel remoteLabel = new JLabel(remoteRadioButton.getText());
 200             remoteLabel.setFont(boldLabelFont);
 201             remotePanel.add(remoteLabel, NORTH);
 202         }
 203         radioButtonPanel.add(remotePanel, SOUTH);
 204 
 205         JPanel remoteTFPanel = new JPanel(new BorderLayout());
 206         remotePanel.add(remoteTFPanel, CENTER);
 207 
 208         remoteTFPanel.add(remoteTF, NORTH);
 209 
 210         remoteMessageLabel = new JLabel("<html>" + getText("remoteTF.usage"));
 211         remoteMessageLabel.setFont(smallLabelFont);
 212         remoteMessageLabel.setForeground(hintTextColor);
 213         remoteTFPanel.add(remoteMessageLabel, CENTER);
 214 
 215         JPanel userPwdPanel = new JPanel(new FlowLayout(FlowLayout.LEADING, 0, 0));
 216         userPwdPanel.setBorder(new EmptyBorder(12, 0, 0, 0)); // top padding
 217 
 218         int tfWidth = JConsole.IS_WIN ? 12 : 8;
 219 
 220         userNameTF = new JTextField(tfWidth);
 221         userNameTF.addActionListener(connectAction);
 222         userNameTF.getDocument().addDocumentListener(this);
 223         userNameTF.addFocusListener(this);
 224         setAccessibleName(userNameTF,
 225                           getText("Username.accessibleName"));
 226         String labelKey = "Username: ";
 227         LabeledComponent lc;
 228         lc = new LabeledComponent(getText(labelKey),
 229                                   getMnemonicInt(labelKey),
 230                                   userNameTF);
 231         lc.label.setFont(boldLabelFont);
 232         userPwdPanel.add(lc);
 233 
 234         passwordTF = new JPasswordField(tfWidth);
 235         // Heights differ, so fix here
 236         passwordTF.setPreferredSize(userNameTF.getPreferredSize());
 237         passwordTF.addActionListener(connectAction);
 238         passwordTF.getDocument().addDocumentListener(this);
 239         passwordTF.addFocusListener(this);
 240         setAccessibleName(passwordTF,
 241                           getText("Password.accessibleName"));
 242         labelKey = "Password: ";
 243         lc = new LabeledComponent(getText(labelKey),
 244                                   getMnemonicInt(labelKey),
 245                                   passwordTF);
 246         lc.setBorder(new EmptyBorder(0, 12, 0, 0)); // Left padding
 247         lc.label.setFont(boldLabelFont);
 248         userPwdPanel.add(lc);
 249 
 250         remoteTFPanel.add(userPwdPanel, SOUTH);
 251 
 252         String connectButtonToolTipText =
 253             getText("ConnectDialog.connectButton.toolTip");
 254         connectButton = new JButton(connectAction);
 255         connectButton.setToolTipText(connectButtonToolTipText);
 256 
 257         cancelButton = new JButton(cancelAction);
 258 
 259         JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.TRAILING));
 260         buttonPanel.setBorder(new EmptyBorder(12, 12, 2, 12));
 261         if (JConsole.IS_GTK) {
 262             buttonPanel.add(cancelButton);
 263             buttonPanel.add(connectButton);
 264         } else {
 265             buttonPanel.add(connectButton);
 266             buttonPanel.add(cancelButton);
 267         }
 268         bottomPanel.add(buttonPanel, NORTH);
 269 
 270         bottomPanel.add(statusBar, SOUTH);
 271 
 272         updateButtonStates();
 273         Utilities.updateTransparency(this);
 274     }
 275 
 276     public void revalidate() {
 277         // Adjust some colors
 278         hintTextColor =
 279             ensureContrast(UIManager.getColor("Label.disabledForeground"),
 280                            UIManager.getColor("Panel.background"));
 281         disabledTableCellColor =
 282             ensureContrast(new Color(0x808080),
 283                            UIManager.getColor("Table.background"));
 284 
 285         if (remoteMessageLabel != null) {
 286             remoteMessageLabel.setForeground(hintTextColor);
 287             // Update html color setting
 288             String colorStr =
 289                 String.format("%06x", hintTextColor.getRGB() & 0xFFFFFF);
 290             remoteMessageLabel.setText("<html><font color=#" + colorStr + ">" +
 291                                        getText("remoteTF.usage"));
 292         }
 293         if (localMessageLabel != null) {
 294             localMessageLabel.setForeground(hintTextColor);
 295             // Update html color setting
 296             valueChanged(null);
 297         }
 298 
 299         super.revalidate();
 300     }
 301 
 302     private void createActions() {
 303         connectAction = new AbstractAction(getText("Connect")) {
 304             /* init */ {
 305                 putValue(Action.MNEMONIC_KEY, getMnemonicInt("Connect"));
 306             }
 307 
 308             public void actionPerformed(ActionEvent ev) {
 309                 if (!isEnabled() || !isVisible()) {
 310                     return;
 311                 }
 312                 setVisible(false);
 313                 statusBar.setText("");
 314 
 315                 if (remoteRadioButton.isSelected()) {
 316                     String txt = remoteTF.getText().trim();
 317                     String userName = userNameTF.getText().trim();
 318                     userName = userName.equals("") ? null : userName;
 319                     String password = passwordTF.getText();
 320                     password = password.equals("") ? null : password;
 321                     try {
 322                         if (txt.startsWith(JConsole.ROOT_URL)) {
 323                             String url = txt;
 324                             String msg = null;
 325                             jConsole.addUrl(url, userName, password, false);
 326                             remoteTF.setText(JConsole.ROOT_URL);
 327                             return;
 328                         } else {
 329                             String host = remoteTF.getText().trim();
 330                             String port = "0";
 331                             int index = host.lastIndexOf(":");
 332                             if (index >= 0) {
 333                                 port = host.substring(index + 1);
 334                                 host = host.substring(0, index);
 335                             }
 336                             if (host.length() > 0 && port.length() > 0) {
 337                                 int p = Integer.parseInt(port.trim());
 338                                 jConsole.addHost(host, p, userName, password);
 339                                 remoteTF.setText("");
 340                                 userNameTF.setText("");
 341                                 passwordTF.setText("");
 342                                 return;
 343                             }
 344                         }
 345                     } catch (Exception ex) {
 346                         statusBar.setText(ex.toString());
 347                     }
 348                     setVisible(true);
 349                 } else if (localRadioButton != null && localRadioButton.isSelected()) {
 350                     // Try to connect to selected VM. If a connection
 351                     // cannot be established for some reason (the process has
 352                     // terminated for example) then keep the dialog open showing
 353                     // the connect error.
 354                     //
 355                     int row = vmTable.getSelectedRow();
 356                     if (row >= 0) {
 357                         jConsole.addVmid(vmModel.vmAt(row));
 358                     }
 359                     refresh();
 360                 }
 361             }
 362         };
 363 
 364         cancelAction = new AbstractAction(getText("Cancel")) {
 365             public void actionPerformed(ActionEvent ev) {
 366                 setVisible(false);
 367                 statusBar.setText("");
 368             }
 369         };
 370     }
 371 
 372 
 373     // a label used solely for calculating the width
 374     private static JLabel tmpLabel = new JLabel();
 375     public static int getLabelWidth(String text) {
 376         tmpLabel.setText(text);
 377         return (int) tmpLabel.getPreferredSize().getWidth() + 1;
 378     }
 379 
 380     private class LocalTabJTable extends JTable {
 381         ManagedVmTableModel vmModel;
 382         Border rendererBorder = new EmptyBorder(0, 6, 0, 6);
 383 
 384         public LocalTabJTable(ManagedVmTableModel model) {
 385             super(model);
 386             this.vmModel = model;
 387 
 388             // Remove vertical lines, expect for GTK L&F.
 389             // (because GTK doesn't show header dividers)
 390             if (!JConsole.IS_GTK) {
 391                 setShowVerticalLines(false);
 392                 setIntercellSpacing(new Dimension(0, 1));
 393             }
 394 
 395             // Double-click handler
 396             addMouseListener(new MouseAdapter() {
 397                 public void mouseClicked(MouseEvent evt) {
 398                     if (evt.getClickCount() == 2) {
 399                         connectButton.doClick();
 400                     }
 401                 }
 402             });
 403 
 404             // Enter should call default action
 405             getActionMap().put("connect", connectAction);
 406             InputMap inputMap = getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
 407             inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0), "connect");
 408         }
 409 
 410         public String getToolTipText(MouseEvent e) {
 411             String tip = null;
 412             java.awt.Point p = e.getPoint();
 413             int rowIndex = rowAtPoint(p);
 414             int colIndex = columnAtPoint(p);
 415             int realColumnIndex = convertColumnIndexToModel(colIndex);
 416 
 417             if (realColumnIndex == COL_NAME) {
 418                 LocalVirtualMachine vmd = vmModel.vmAt(rowIndex);
 419                 tip = vmd.toString();
 420             }
 421             return tip;
 422         }
 423 
 424         public TableCellRenderer getCellRenderer(int row, int column) {
 425             return new DefaultTableCellRenderer() {
 426                 public Component getTableCellRendererComponent(JTable table,
 427                                                                Object value,
 428                                                                boolean isSelected,
 429                                                                boolean hasFocus,
 430                                                                int row,
 431                                                                int column) {
 432                     Component comp =
 433                         super.getTableCellRendererComponent(table, value, isSelected,
 434                                                             hasFocus, row, column);
 435 
 436                     if (!isSelected) {
 437                         LocalVirtualMachine lvm = vmModel.vmAt(row);
 438                         if (!lvm.isManageable() && !lvm.isAttachable()) {
 439                             comp.setForeground(disabledTableCellColor);
 440                         }
 441                     }
 442 
 443                     if (comp instanceof JLabel) {
 444                         JLabel label = (JLabel)comp;
 445                         label.setBorder(rendererBorder);
 446 
 447                         if (value instanceof Integer) {
 448                             label.setHorizontalAlignment(JLabel.RIGHT);
 449                         }
 450                     }
 451 
 452                     return comp;
 453                 }
 454             };
 455         }
 456     }
 457 
 458     public void setConnectionParameters(String url,
 459                                         String host,
 460                                         int port,
 461                                         String userName,
 462                                         String password,
 463                                         String msg) {
 464         if ((url != null && url.length() > 0) ||
 465             (host != null && host.length() > 0 && port > 0)) {
 466 
 467             remoteRadioButton.setSelected(true);
 468             if (url != null && url.length() > 0) {
 469                 remoteTF.setText(url);
 470             } else {
 471                 remoteTF.setText(host+":"+port);
 472             }
 473             userNameTF.setText((userName != null) ? userName : "");
 474             passwordTF.setText((password != null) ? password : "");
 475 
 476             statusBar.setText((msg != null) ? msg : "");
 477             if (getPreferredSize().width > getWidth()) {
 478                 pack();
 479             }
 480             remoteTF.requestFocus();
 481             remoteTF.selectAll();
 482         }
 483     }
 484 
 485 
 486     public void itemStateChanged(ItemEvent ev) {
 487         if (!localRadioButton.isSelected()) {
 488             vmTable.getSelectionModel().clearSelection();
 489         }
 490         updateButtonStates();
 491     }
 492 
 493     private void updateButtonStates() {
 494         boolean connectEnabled = false;
 495 
 496         if (remoteRadioButton.isSelected()) {
 497             connectEnabled = JConsole.isValidRemoteString(remoteTF.getText());
 498         } else if (localRadioButton != null && localRadioButton.isSelected()) {
 499             int row = vmTable.getSelectedRow();
 500             if (row >= 0) {
 501                 LocalVirtualMachine lvm = vmModel.vmAt(row);
 502                 connectEnabled = (lvm.isManageable() || lvm.isAttachable());
 503             }
 504         }
 505 
 506         connectAction.setEnabled(connectEnabled);
 507     }
 508 
 509     public void insertUpdate(DocumentEvent e) {
 510         updateButtonStates();
 511     }
 512 
 513     public void removeUpdate(DocumentEvent e) {
 514         updateButtonStates();
 515     }
 516 
 517     public void changedUpdate(DocumentEvent e) {
 518         updateButtonStates();
 519     }
 520 
 521     public void focusGained(FocusEvent e) {
 522         Object source = e.getSource();
 523         Component opposite = e.getOppositeComponent();
 524 
 525         if (!e.isTemporary() &&
 526             source instanceof JTextField &&
 527             opposite instanceof JComponent &&
 528             SwingUtilities.getRootPane(opposite) == getRootPane()) {
 529 
 530             ((JTextField)source).selectAll();
 531         }
 532 
 533         if (source == remoteTF) {
 534             remoteRadioButton.setSelected(true);
 535         } else if (source == vmTable) {
 536             localRadioButton.setSelected(true);
 537             if (vmModel.getRowCount() == 1) {
 538                 // if there's only one process then select the row
 539                 vmTable.setRowSelectionInterval(0, 0);
 540             }
 541         }
 542         updateButtonStates();
 543     }
 544 
 545     public void focusLost(FocusEvent e) {
 546     }
 547 
 548     public void keyTyped(KeyEvent e) {
 549         char c = e.getKeyChar();
 550         if (c == KeyEvent.VK_ESCAPE) {
 551             setVisible(false);
 552         } else if (!(Character.isDigit(c) ||
 553                      c == KeyEvent.VK_BACK_SPACE ||
 554                      c == KeyEvent.VK_DELETE)) {
 555             getToolkit().beep();
 556             e.consume();
 557         }
 558     }
 559 
 560     public void setVisible(boolean b) {
 561         boolean wasVisible = isVisible();
 562         super.setVisible(b);
 563         if (b && !wasVisible) {
 564             SwingUtilities.invokeLater(new Runnable() {
 565                 public void run() {
 566                     if (remoteRadioButton.isSelected()) {
 567                         remoteTF.requestFocus();
 568                         remoteTF.selectAll();
 569                     }
 570                 }
 571             });
 572         }
 573     }
 574 
 575     public void keyPressed(KeyEvent e) {
 576     }
 577 
 578     public void keyReleased(KeyEvent e) {
 579     }
 580 
 581 
 582     // ListSelectionListener interface
 583     public void valueChanged(ListSelectionEvent e) {
 584         updateButtonStates();
 585         String labelText = " "; // Non-empty to reserve vertical space
 586         int row = vmTable.getSelectedRow();
 587         if (row >= 0) {
 588             LocalVirtualMachine lvm = vmModel.vmAt(row);
 589             if (!lvm.isManageable()) {
 590                 if (lvm.isAttachable()) {
 591                     labelText = getText("Management Will Be Enabled");
 592                 } else {
 593                     labelText = getText("Management Not Enabled");
 594                 }
 595             }
 596         }
 597         String colorStr =
 598             String.format("%06x", hintTextColor.getRGB() & 0xFFFFFF);
 599         localMessageLabel.setText("<html><font color=#" + colorStr + ">" + labelText);
 600     }
 601     // ----
 602 
 603 
 604     // Refresh the list of managed VMs
 605     public void refresh() {
 606         if (vmModel != null) {
 607             // Remember selection
 608             LocalVirtualMachine selected = null;
 609             int row = vmTable.getSelectedRow();
 610             if (row >= 0) {
 611                 selected = vmModel.vmAt(row);
 612             }
 613 
 614             vmModel.refresh();
 615 
 616             int selectRow = -1;
 617             int n = vmModel.getRowCount();
 618             if (selected != null) {
 619                 for (int i = 0; i < n; i++) {
 620                     LocalVirtualMachine lvm = vmModel.vmAt(i);
 621                     if (selected.vmid() == lvm.vmid() &&
 622                         selected.toString().equals(lvm.toString())) {
 623 
 624                         selectRow = i;
 625                         break;
 626                     }
 627                 }
 628             }
 629             if (selectRow > -1) {
 630                 vmTable.setRowSelectionInterval(selectRow, selectRow);
 631             } else {
 632                 vmTable.getSelectionModel().clearSelection();
 633             }
 634 
 635             Dimension dim = vmTable.getPreferredSize();
 636 
 637             // Tricky. Reduce height by one to avoid double line at bottom,
 638             // but that causes a scroll bar to appear, so remove it.
 639             dim.height = Math.min(dim.height-1, 100);
 640             localTableScrollPane.setVerticalScrollBarPolicy((dim.height < 100)
 641                                                 ? JScrollPane.VERTICAL_SCROLLBAR_NEVER
 642                                                 : JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
 643             localTableScrollPane.getViewport().setMinimumSize(dim);
 644             localTableScrollPane.getViewport().setPreferredSize(dim);
 645         }
 646         pack();
 647         setLocationRelativeTo(jConsole);
 648     }
 649 
 650     // Represents the list of managed VMs as a tabular data model.
 651     private static class ManagedVmTableModel extends AbstractTableModel {
 652         private static String[] columnNames = {
 653             Resources.getText("Column.Name"),
 654             Resources.getText("Column.PID"),
 655         };
 656 
 657         private List<LocalVirtualMachine> vmList;
 658 
 659         public int getColumnCount() {
 660             return columnNames.length;
 661         }
 662 
 663         public String getColumnName(int col) {
 664             return columnNames[col];
 665         }
 666 
 667         public synchronized int getRowCount() {
 668             return vmList.size();
 669         }
 670 
 671         public synchronized Object getValueAt(int row, int col) {
 672             assert col >= 0 && col <= columnNames.length;
 673             LocalVirtualMachine vm = vmList.get(row);
 674             switch (col) {
 675                 case COL_NAME: return vm.displayName();
 676                 case COL_PID:  return vm.vmid();
 677                 default: return null;
 678             }
 679         }
 680 
 681         public Class getColumnClass(int column) {
 682             switch (column) {
 683                 case COL_NAME: return String.class;
 684                 case COL_PID:  return Integer.class;
 685                 default: return super.getColumnClass(column);
 686             }
 687         }
 688 
 689         public ManagedVmTableModel() {
 690             refresh();
 691         }
 692 
 693 
 694         public synchronized LocalVirtualMachine vmAt(int pos) {
 695             return vmList.get(pos);
 696         }
 697 
 698         public synchronized void refresh() {
 699             Map<Integer, LocalVirtualMachine> map =
 700                 LocalVirtualMachine.getAllVirtualMachines();
 701             vmList = new ArrayList<LocalVirtualMachine>();
 702             vmList.addAll(map.values());
 703 
 704             // data has changed
 705             fireTableDataChanged();
 706         }
 707     }
 708 
 709 
 710     // Convenience method
 711     private static String getText(String key) {
 712         return Resources.getText(key);
 713     }
 714 
 715 
 716     // A blank component that takes up as much space as the
 717     // button part of a JRadioButton.
 718     private static class Padder extends JPanel {
 719         JRadioButton radioButton;
 720 
 721         Padder(JRadioButton radioButton) {
 722             this.radioButton = radioButton;
 723 
 724             setAccessibleName(this, getText("Blank"));
 725         }
 726 
 727         public Dimension getPreferredSize() {
 728             Rectangle r = getTextRectangle(radioButton);
 729             int w = (r != null && r.x > 8) ? r.x : 22;
 730 
 731             return new Dimension(w, 0);
 732         }
 733 
 734         private static Rectangle getTextRectangle(AbstractButton button) {
 735             String text = button.getText();
 736             Icon icon = (button.isEnabled()) ? button.getIcon() : button.getDisabledIcon();
 737 
 738             if (icon == null && button.getUI() instanceof BasicRadioButtonUI) {
 739                 icon = ((BasicRadioButtonUI)button.getUI()).getDefaultIcon();
 740             }
 741 
 742             if ((icon == null) && (text == null)) {
 743                 return null;
 744             }
 745 
 746             Rectangle paintIconR = new Rectangle();
 747             Rectangle paintTextR = new Rectangle();
 748             Rectangle paintViewR = new Rectangle();
 749             Insets paintViewInsets = new Insets(0, 0, 0, 0);
 750 
 751             paintViewInsets = button.getInsets(paintViewInsets);
 752             paintViewR.x = paintViewInsets.left;
 753             paintViewR.y = paintViewInsets.top;
 754             paintViewR.width = button.getWidth() - (paintViewInsets.left + paintViewInsets.right);
 755             paintViewR.height = button.getHeight() - (paintViewInsets.top + paintViewInsets.bottom);
 756 
 757             Graphics g = button.getGraphics();
 758             if (g == null) {
 759                 return null;
 760             }
 761             String clippedText =
 762                 SwingUtilities.layoutCompoundLabel(button,
 763                                                    g.getFontMetrics(),
 764                                                    text,
 765                                                    icon,
 766                                                    button.getVerticalAlignment(),
 767                                                    button.getHorizontalAlignment(),
 768                                                    button.getVerticalTextPosition(),
 769                                                    button.getHorizontalTextPosition(),
 770                                                    paintViewR,
 771                                                    paintIconR,
 772                                                    paintTextR,
 773                                                    button.getIconTextGap());
 774 
 775             return paintTextR;
 776         }
 777     }
 778 
 779 }