1 /*
   2  * Copyright (c) 2004, 2007, 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.awt.*;
  29 import java.awt.event.*;
  30 import java.io.*;
  31 import java.lang.management.*;
  32 import java.lang.reflect.*;
  33 import java.util.*;
  34 import java.util.concurrent.*;
  35 
  36 import javax.accessibility.*;
  37 import javax.management.*;
  38 import javax.management.openmbean.CompositeData;
  39 import javax.swing.*;
  40 import javax.swing.border.*;
  41 
  42 import sun.tools.jconsole.resources.Messages;
  43 
  44 import static sun.tools.jconsole.Formatter.*;
  45 import static sun.tools.jconsole.Utilities.*;
  46 
  47 @SuppressWarnings("serial")
  48 class MemoryTab extends Tab implements ActionListener, ItemListener {
  49     JComboBox<Plotter> plotterChoice;
  50     TimeComboBox timeComboBox;
  51     JButton gcButton;
  52 
  53     PlotterPanel plotterPanel;
  54     JPanel bottomPanel;
  55     HTMLPane details;
  56     PoolChart poolChart;
  57 
  58     ArrayList<Plotter> plotterList;
  59     Plotter heapPlotter, nonHeapPlotter;
  60 
  61     private MemoryOverviewPanel overviewPanel;
  62 
  63     private static final String usedKey        = "used";
  64     private static final String committedKey   = "committed";
  65     private static final String maxKey         = "max";
  66     private static final String thresholdKey   = "threshold";
  67     private static final Color  usedColor      = Plotter.defaultColor;
  68     private static final Color  committedColor = null;
  69     private static final Color  maxColor       = null;
  70     private static final Color  thresholdColor = Color.red;
  71 
  72     /*
  73       Hierarchy of panels and layouts for this tab:
  74 
  75         MemoryTab (BorderLayout)
  76 
  77             North:  topPanel (BorderLayout)
  78 
  79                         Center: controlPanel (FlowLayout)
  80                                     plotterChoice, timeComboBox
  81 
  82                         East:   topRightPanel (FlowLayout)
  83                                     gcButton
  84 
  85             Center: plotterPanel
  86 
  87                         Center: plotter
  88 
  89             South:  bottomPanel (BorderLayout)
  90 
  91                         Center: details
  92                         East:   poolChart
  93     */
  94 
  95 
  96     public static String getTabName() {
  97         return Messages.MEMORY;
  98     }
  99 
 100     public MemoryTab(VMPanel vmPanel) {
 101         super(vmPanel, getTabName());
 102 
 103         setLayout(new BorderLayout(0, 0));
 104         setBorder(new EmptyBorder(4, 4, 3, 4));
 105 
 106         JPanel topPanel     = new JPanel(new BorderLayout());
 107                plotterPanel = new PlotterPanel(null);
 108                bottomPanel  = new JPanel(new BorderLayout());
 109 
 110         add(topPanel,     BorderLayout.NORTH);
 111         add(plotterPanel, BorderLayout.CENTER);
 112 
 113         JPanel controlPanel = new JPanel(new FlowLayout(FlowLayout.LEADING, 20, 5));
 114         topPanel.add(controlPanel, BorderLayout.CENTER);
 115 
 116         // Plotter choice
 117         plotterChoice = new JComboBox<Plotter>();
 118         plotterChoice.addItemListener(this);
 119         controlPanel.add(new LabeledComponent(Messages.CHART_COLON,
 120                 Resources.getMnemonicInt(Messages.CHART_COLON),
 121                                               plotterChoice));
 122 
 123         // Range control
 124         timeComboBox = new TimeComboBox();
 125         controlPanel.add(new LabeledComponent(Messages.TIME_RANGE_COLON,
 126                 Resources.getMnemonicInt(Messages.TIME_RANGE_COLON),
 127                                               timeComboBox));
 128 
 129         gcButton = new JButton(Messages.PERFORM_GC);
 130         gcButton.setMnemonic(Resources.getMnemonicInt(Messages.PERFORM_GC));
 131         gcButton.addActionListener(this);
 132         gcButton.setToolTipText(Messages.PERFORM_GC_TOOLTIP);
 133         JPanel topRightPanel = new JPanel();
 134         topRightPanel.setBorder(new EmptyBorder(0, 65-8, 0, 70));
 135         topRightPanel.add(gcButton);
 136         topPanel.add(topRightPanel, BorderLayout.AFTER_LINE_ENDS);
 137 
 138         bottomPanel.setBorder(new CompoundBorder(new TitledBorder(Messages.DETAILS),
 139                                                   new EmptyBorder(10, 10, 10, 10)));
 140 
 141         details = new HTMLPane();
 142         setAccessibleName(details, Messages.DETAILS);
 143         bottomPanel.add(new JScrollPane(details), BorderLayout.CENTER);
 144 
 145         poolChart = new PoolChart();
 146         bottomPanel.add(poolChart, BorderLayout.AFTER_LINE_ENDS);
 147     }
 148 
 149 
 150     private void createPlotters() throws IOException {
 151         plotterList = new ArrayList<Plotter>();
 152 
 153         ProxyClient proxyClient = vmPanel.getProxyClient();
 154 
 155         heapPlotter = new Plotter(Plotter.Unit.BYTES) {
 156             public String toString() {
 157                 return Messages.HEAP_MEMORY_USAGE;
 158             }
 159         };
 160         proxyClient.addWeakPropertyChangeListener(heapPlotter);
 161 
 162         nonHeapPlotter = new Plotter(Plotter.Unit.BYTES) {
 163             public String toString() {
 164                 return Messages.NON_HEAP_MEMORY_USAGE;
 165             }
 166         };
 167 
 168         setAccessibleName(heapPlotter,
 169                 Messages.MEMORY_TAB_HEAP_PLOTTER_ACCESSIBLE_NAME);
 170         setAccessibleName(nonHeapPlotter,
 171                 Messages.MEMORY_TAB_NON_HEAP_PLOTTER_ACCESSIBLE_NAME);
 172 
 173         proxyClient.addWeakPropertyChangeListener(nonHeapPlotter);
 174 
 175         heapPlotter.createSequence(usedKey,         Messages.USED,      usedColor,      true);
 176         heapPlotter.createSequence(committedKey,    Messages.COMMITTED, committedColor, false);
 177         heapPlotter.createSequence(maxKey,          Messages.MAX,       maxColor,       false);
 178 
 179         nonHeapPlotter.createSequence(usedKey,      Messages.USED,      usedColor,      true);
 180         nonHeapPlotter.createSequence(committedKey, Messages.COMMITTED, committedColor, false);
 181         nonHeapPlotter.createSequence(maxKey,       Messages.MAX,       maxColor,       false);
 182 
 183         plotterList.add(heapPlotter);
 184         plotterList.add(nonHeapPlotter);
 185 
 186         // Now add memory pools
 187         Map<ObjectName, MBeanInfo> mBeanMap = proxyClient.getMBeans("java.lang");
 188         Set<ObjectName> keys = mBeanMap.keySet();
 189         ObjectName[] objectNames = keys.toArray(new ObjectName[keys.size()]);
 190         ArrayList<PoolPlotter> nonHeapPlotters = new ArrayList<PoolPlotter>(2);
 191         for (ObjectName objectName : objectNames) {
 192             String type = objectName.getKeyProperty("type");
 193             if (type.equals("MemoryPool")) {
 194                 String name = Resources.format(Messages.MEMORY_POOL_LABEL,
 195                                       objectName.getKeyProperty("name"));
 196                 // Heap or non-heap?
 197                 boolean isHeap = false;
 198                 AttributeList al =
 199                     proxyClient.getAttributes(objectName,
 200                                               new String[] { "Type" });
 201                 if (al.size() > 0) {
 202                     isHeap = MemoryType.HEAP.name().equals(((Attribute)al.get(0)).getValue());
 203                 }
 204                 PoolPlotter poolPlotter = new PoolPlotter(objectName, name, isHeap);
 205                 proxyClient.addWeakPropertyChangeListener(poolPlotter);
 206 
 207                 poolPlotter.createSequence(usedKey,      Messages.USED,      usedColor,      true);
 208                 poolPlotter.createSequence(committedKey, Messages.COMMITTED, committedColor, false);
 209                 poolPlotter.createSequence(maxKey,       Messages.MAX,       maxColor,       false);
 210                 poolPlotter.createSequence(thresholdKey, Messages.THRESHOLD, thresholdColor, false);
 211                 poolPlotter.setUseDashedTransitions(thresholdKey, true);
 212 
 213                 if (isHeap) {
 214                     plotterList.add(poolPlotter);
 215                 } else {
 216                     // Will be added to plotterList below
 217                     nonHeapPlotters.add(poolPlotter);
 218                 }
 219             }
 220         }
 221         // Add non-heap plotters last
 222         for (PoolPlotter poolPlotter : nonHeapPlotters) {
 223             plotterList.add(poolPlotter);
 224         }
 225     }
 226 
 227 
 228     public void itemStateChanged(ItemEvent ev) {
 229         if (ev.getStateChange() == ItemEvent.SELECTED) {
 230             Plotter plotter = (Plotter)plotterChoice.getSelectedItem();
 231             plotterPanel.setPlotter(plotter);
 232         }
 233     }
 234 
 235     public void gc() {
 236         new Thread("MemoryPanel.gc") {
 237             public void run() {
 238                 ProxyClient proxyClient = vmPanel.getProxyClient();
 239                 try {
 240                     proxyClient.getMemoryMXBean().gc();
 241                 } catch (UndeclaredThrowableException e) {
 242                     proxyClient.markAsDead();
 243                 } catch (IOException e) {
 244                     // Ignore
 245                 }
 246             }
 247         }.start();
 248     }
 249 
 250     public SwingWorker<?, ?> newSwingWorker() {
 251         return new SwingWorker<Boolean, Object>() {
 252             private long[] used, committed, max, threshold;
 253             private long timeStamp;
 254             private String detailsStr;
 255             private boolean initialRun = false;
 256 
 257             public Boolean doInBackground() {
 258                 ProxyClient proxyClient = vmPanel.getProxyClient();
 259 
 260                 if (plotterList == null) {
 261                     try {
 262                         createPlotters();
 263                     } catch (UndeclaredThrowableException e) {
 264                         proxyClient.markAsDead();
 265                         return false;
 266                     } catch (final IOException ex) {
 267                         return false;
 268                     }
 269                     initialRun = true;
 270                 }
 271 
 272                 int n = plotterList.size();
 273                 used      = new long[n];
 274                 committed = new long[n];
 275                 max       = new long[n];
 276                 threshold = new long[n];
 277                 timeStamp = System.currentTimeMillis();
 278 
 279                 for (int i = 0; i < n; i++) {
 280                     Plotter plotter = plotterList.get(i);
 281                     MemoryUsage mu = null;
 282                     used[i] = -1L;
 283                     threshold[i] = -1L;
 284 
 285                     try {
 286                         if (plotter instanceof PoolPlotter) {
 287                             PoolPlotter poolPlotter = (PoolPlotter)plotter;
 288                             ObjectName objectName = poolPlotter.objectName;
 289                             AttributeList al =
 290                                 proxyClient.getAttributes(objectName,
 291                                                           new String[] { "Usage", "UsageThreshold" });
 292                             if (al.size() > 0) {
 293                                 CompositeData cd = (CompositeData)((Attribute)al.get(0)).getValue();
 294                                 mu = MemoryUsage.from(cd);
 295 
 296                                 if (al.size() > 1) {
 297                                     threshold[i] = (Long)((Attribute)al.get(1)).getValue();
 298                                 }
 299                             }
 300                         } else if (plotter == heapPlotter) {
 301                             mu = proxyClient.getMemoryMXBean().getHeapMemoryUsage();
 302                         } else if (plotter == nonHeapPlotter) {
 303                             mu = proxyClient.getMemoryMXBean().getNonHeapMemoryUsage();
 304                         }
 305                     } catch (UndeclaredThrowableException e) {
 306                         proxyClient.markAsDead();
 307                         return false;
 308                     } catch (IOException ex) {
 309                         // Skip this plotter
 310                     }
 311 
 312                     if (mu != null) {
 313                         used[i]      = mu.getUsed();
 314                         committed[i] = mu.getCommitted();
 315                         max[i]       = mu.getMax();
 316                     }
 317                 }
 318                 detailsStr = formatDetails();
 319 
 320                 return true;
 321             }
 322 
 323             protected void done() {
 324                 try {
 325                     if (!get()) {
 326                         return;
 327                     }
 328                 } catch (InterruptedException ex) {
 329                     return;
 330                 } catch (ExecutionException ex) {
 331                     if (JConsole.isDebug()) {
 332                         ex.printStackTrace();
 333                     }
 334                     return;
 335                 }
 336 
 337                 if (initialRun) {
 338                     // Add Memory Pools
 339                     for (Plotter p : plotterList) {
 340                         plotterChoice.addItem(p);
 341                         timeComboBox.addPlotter(p);
 342                     }
 343                     add(bottomPanel,  BorderLayout.SOUTH);
 344                 }
 345 
 346 
 347                 int n = plotterList.size();
 348                 int poolCount = 0;
 349 
 350                 for (int i = 0; i < n; i++) {
 351                     Plotter plotter = plotterList.get(i);
 352                     if (used[i] >= 0L) {
 353                         if (plotter instanceof PoolPlotter) {
 354                             plotter.addValues(timeStamp, used[i], committed[i], max[i], threshold[i]);
 355                             if (threshold[i] > 0L) {
 356                                 plotter.setIsPlotted(thresholdKey, true);
 357                             }
 358                             poolChart.setValue(poolCount++, (PoolPlotter)plotter,
 359                                                used[i], threshold[i], max[i]);
 360                         } else {
 361                             plotter.addValues(timeStamp, used[i], committed[i], max[i]);
 362                         }
 363 
 364                         if (plotter == heapPlotter && overviewPanel != null) {
 365                             overviewPanel.getPlotter().addValues(timeStamp, used[i]);
 366                             overviewPanel.updateMemoryInfo(used[i], committed[i], max[i]);
 367                         }
 368                     }
 369                 }
 370                 details.setText(detailsStr);
 371             }
 372         };
 373     }
 374 
 375     private String formatDetails() {
 376         ProxyClient proxyClient = vmPanel.getProxyClient();
 377         if (proxyClient.isDead()) {
 378             return "";
 379         }
 380 
 381         String text = "<table cellspacing=0 cellpadding=0>";
 382 
 383         Plotter plotter = (Plotter)plotterChoice.getSelectedItem();
 384         if (plotter == null) {
 385             return "";
 386         }
 387 
 388         //long time = plotter.getLastTimeStamp();
 389         long time = System.currentTimeMillis();
 390         String timeStamp = formatDateTime(time);
 391         text += newRow(Messages.TIME, timeStamp);
 392 
 393         long used = plotter.getLastValue(usedKey);
 394         long committed = plotter.getLastValue(committedKey);
 395         long max = plotter.getLastValue(maxKey);
 396         long threshold = plotter.getLastValue(thresholdKey);
 397 
 398         text += newRow(Messages.USED, formatKBytes(used));
 399         if (committed > 0L) {
 400             text += newRow(Messages.COMMITTED, formatKBytes(committed));
 401         }
 402         if (max > 0L) {
 403             text += newRow(Messages.MAX, formatKBytes(max));
 404         }
 405         if (threshold > 0L) {
 406             text += newRow(Messages.USAGE_THRESHOLD, formatKBytes(threshold));
 407         }
 408 
 409         try {
 410             Collection<GarbageCollectorMXBean> garbageCollectors =
 411                 proxyClient.getGarbageCollectorMXBeans();
 412 
 413             boolean descPrinted = false;
 414             for (GarbageCollectorMXBean garbageCollectorMBean : garbageCollectors) {
 415                 String gcName = garbageCollectorMBean.getName();
 416                 long gcCount = garbageCollectorMBean.getCollectionCount();
 417                 long gcTime = garbageCollectorMBean.getCollectionTime();
 418                 String str = Resources.format(Messages.GC_TIME_DETAILS, justify(formatTime(gcTime), 14),
 419                                      gcName,
 420                                      String.format("%,d",gcCount));
 421                 if (!descPrinted) {
 422                     text += newRow(Messages.GC_TIME, str);
 423                     descPrinted = true;
 424                 } else {
 425                     text += newRow(null, str);
 426                 }
 427            }
 428         } catch (IOException e) {
 429         }
 430 
 431         return text;
 432     }
 433 
 434     public void actionPerformed(ActionEvent ev) {
 435         Object src = ev.getSource();
 436         if (src == gcButton) {
 437             gc();
 438         }
 439     }
 440 
 441     private class PoolPlotter extends Plotter {
 442         ObjectName objectName;
 443         String name;
 444         boolean isHeap;
 445         long value, threshold, max;
 446         int barX;
 447 
 448         public PoolPlotter(ObjectName objectName, String name, boolean isHeap) {
 449             super(Plotter.Unit.BYTES);
 450 
 451             this.objectName = objectName;
 452             this.name       = name;
 453             this.isHeap     = isHeap;
 454 
 455             setAccessibleName(this,
 456                               Resources.format(Messages.MEMORY_TAB_POOL_PLOTTER_ACCESSIBLE_NAME,
 457                                       name));
 458         }
 459 
 460 
 461         public String toString() {
 462             return name;
 463         }
 464     }
 465 
 466     private class PoolChart extends BorderedComponent
 467                             implements Accessible, MouseListener {
 468         final int height       = 150;
 469         final int leftMargin   =  50;
 470         final int rightMargin  =  23;
 471         final int bottomMargin =  35;
 472         final int barWidth     =  22;
 473         final int barGap       =   3;
 474         final int groupGap     =   8;
 475         final int barHeight    = height * 2 / 3;
 476 
 477         final Color greenBar           = new Color(100, 255, 100);
 478         final Color greenBarBackground = new Color(210, 255, 210);
 479         final Color redBarBackground   = new Color(255, 210, 210);
 480 
 481         Font smallFont = null;
 482 
 483         ArrayList<PoolPlotter> poolPlotters = new ArrayList<PoolPlotter>(5);
 484 
 485         int nHeapPools    = 0;
 486         int nNonHeapPools = 0;
 487         Rectangle heapRect    = new Rectangle(leftMargin,            height - bottomMargin + 6, barWidth, 20);
 488         Rectangle nonHeapRect = new Rectangle(leftMargin + groupGap, height - bottomMargin + 6, barWidth, 20);
 489 
 490         public PoolChart() {
 491             super(null, null);
 492 
 493             setFocusable(true);
 494             addMouseListener(this);
 495             ToolTipManager.sharedInstance().registerComponent(this);
 496         }
 497 
 498         public void setValue(int poolIndex, PoolPlotter poolPlotter,
 499                              long value, long threshold, long max) {
 500             poolPlotter.value = value;
 501             poolPlotter.threshold = threshold;
 502             poolPlotter.max = max;
 503 
 504             if (poolIndex == poolPlotters.size()) {
 505                 poolPlotters.add(poolPlotter);
 506                 if (poolPlotter.isHeap) {
 507                     poolPlotter.barX = nHeapPools * (barWidth + barGap);
 508                     nHeapPools++;
 509                     heapRect.width = nHeapPools * barWidth + (nHeapPools - 1) * barGap;
 510                     nonHeapRect.x  = leftMargin + heapRect.width + groupGap;
 511                 } else {
 512                     poolPlotter.barX = nonHeapRect.x - leftMargin + nNonHeapPools * (barWidth + barGap);
 513                     nNonHeapPools++;
 514                     nonHeapRect.width = nNonHeapPools * barWidth + (nNonHeapPools - 1) * barGap;
 515                 }
 516             } else {
 517                 poolPlotters.set(poolIndex, poolPlotter);
 518             }
 519             repaint();
 520         }
 521 
 522         private void paintPoolBar(Graphics g, PoolPlotter poolPlotter) {
 523             Rectangle barRect = getBarRect(poolPlotter);
 524             g.setColor(Color.gray);
 525             g.drawRect(barRect.x, barRect.y, barRect.width, barRect.height);
 526 
 527             long value = poolPlotter.value;
 528             long max   = poolPlotter.max;
 529             if (max > 0L) {
 530                 g.translate(barRect.x, barRect.y);
 531 
 532                 // Paint green background
 533                 g.setColor(greenBarBackground);
 534                 g.fillRect(1, 1, barRect.width - 1, barRect.height - 1);
 535 
 536                 int greenHeight = (int)(value * barRect.height / max);
 537                 long threshold = poolPlotter.threshold;
 538                 if (threshold > 0L) {
 539                     int redHeight = (int)(threshold * barRect.height / max);
 540 
 541                     // Paint red background
 542                     g.setColor(redBarBackground);
 543                     g.fillRect(1, 1, barRect.width - 1, barRect.height - redHeight);
 544 
 545                     if (value > threshold) {
 546                         // Over threshold, paint red bar
 547                         g.setColor(thresholdColor);
 548                         g.fillRect(1, barRect.height - greenHeight,
 549                                    barRect.width - 1, greenHeight - redHeight);
 550                         greenHeight = redHeight;
 551                     }
 552                 }
 553 
 554                 // Paint green bar
 555                 g.setColor(greenBar);
 556                 g.fillRect(1, barRect.height - greenHeight,
 557                            barRect.width - 1, greenHeight);
 558 
 559                 g.translate(-barRect.x, -barRect.y);
 560             }
 561         }
 562 
 563         public void paintComponent(Graphics g) {
 564             super.paintComponent(g);
 565 
 566             if (poolPlotters.size() == 0) {
 567                 return;
 568             }
 569 
 570             if (smallFont == null) {
 571                 smallFont = g.getFont().deriveFont(9.0F);
 572             }
 573 
 574             // Paint background for chart area
 575             g.setColor(getBackground());
 576             Rectangle r = g.getClipBounds();
 577             g.fillRect(r.x, r.y, r.width, r.height);
 578 
 579             g.setFont(smallFont);
 580             FontMetrics fm = g.getFontMetrics();
 581             int fontDescent = fm.getDescent();
 582 
 583             // Paint percentage axis
 584             g.setColor(getForeground());
 585             for (int pc : new int[] { 0, 25, 50, 75, 100 }) {
 586                 String str = pc + "% --";
 587                 g.drawString(str,
 588                              leftMargin - fm.stringWidth(str) - 4,
 589                              height - bottomMargin - (pc * barHeight / 100) + fontDescent + 1);
 590             }
 591 
 592             for (PoolPlotter poolPlotter : poolPlotters) {
 593                 paintPoolBar(g, poolPlotter);
 594             }
 595 
 596             g.setColor(Color.gray);
 597             g.drawRect(heapRect.x,    heapRect.y,    heapRect.width,    heapRect.height);
 598             g.drawRect(nonHeapRect.x, nonHeapRect.y, nonHeapRect.width, nonHeapRect.height);
 599 
 600             Color heapColor    = greenBar;
 601             Color nonHeapColor = greenBar;
 602 
 603 
 604             for (PoolPlotter poolPlotter : poolPlotters) {
 605                 if (poolPlotter.threshold > 0L && poolPlotter.value > poolPlotter.threshold) {
 606                     if (poolPlotter.isHeap) {
 607                         heapColor = thresholdColor;
 608                     } else {
 609                         nonHeapColor = thresholdColor;
 610                     }
 611                 }
 612             }
 613             g.setColor(heapColor);
 614             g.fillRect(heapRect.x + 1,    heapRect.y + 1,    heapRect.width - 1,    heapRect.height - 1);
 615             g.setColor(nonHeapColor);
 616             g.fillRect(nonHeapRect.x + 1, nonHeapRect.y + 1, nonHeapRect.width - 1, nonHeapRect.height - 1);
 617 
 618             String str = Messages.HEAP;
 619             int stringWidth = fm.stringWidth(str);
 620             int x = heapRect.x + (heapRect.width - stringWidth) / 2;
 621             int y = heapRect.y + heapRect.height - 6;
 622             g.setColor(Color.white);
 623             g.drawString(str, x-1, y-1);
 624             g.drawString(str, x+1, y-1);
 625             g.drawString(str, x-1, y+1);
 626             g.drawString(str, x+1, y+1);
 627             g.setColor(Color.black);
 628             g.drawString(str, x, y);
 629 
 630             str = Messages.NON_HEAP;
 631             stringWidth = fm.stringWidth(str);
 632             x = nonHeapRect.x + (nonHeapRect.width - stringWidth) / 2;
 633             y = nonHeapRect.y + nonHeapRect.height - 6;
 634             g.setColor(Color.white);
 635             g.drawString(str, x-1, y-1);
 636             g.drawString(str, x+1, y-1);
 637             g.drawString(str, x-1, y+1);
 638             g.drawString(str, x+1, y+1);
 639             g.setColor(Color.black);
 640             g.drawString(str, x, y);
 641 
 642             // Highlight current plotter
 643             g.setColor(Color.blue);
 644             r = null;
 645             Plotter plotter = (Plotter)plotterChoice.getSelectedItem();
 646             if (plotter == heapPlotter) {
 647                 r = heapRect;
 648             } else if (plotter == nonHeapPlotter) {
 649                 r = nonHeapRect;
 650             } else if (plotter instanceof PoolPlotter) {
 651                 r = getBarRect((PoolPlotter)plotter);
 652             }
 653             if (r != null) {
 654                 g.drawRect(r.x - 1, r.y - 1, r.width + 2, r.height + 2);
 655             }
 656         }
 657 
 658         private Rectangle getBarRect(PoolPlotter poolPlotter) {
 659             return new Rectangle(leftMargin + poolPlotter.barX,
 660                                  height - bottomMargin - barHeight,
 661                                  barWidth, barHeight);
 662         }
 663 
 664         public Dimension getPreferredSize() {
 665             return new Dimension(nonHeapRect.x + nonHeapRect.width + rightMargin,
 666                                  height);
 667         }
 668 
 669         public void mouseClicked(MouseEvent e) {
 670             requestFocusInWindow();
 671             Plotter plotter = getPlotter(e);
 672 
 673             if (plotter != null && plotter != plotterChoice.getSelectedItem()) {
 674                 plotterChoice.setSelectedItem(plotter);
 675                 repaint();
 676             }
 677         }
 678 
 679         public String getToolTipText(MouseEvent e) {
 680             Plotter plotter = getPlotter(e);
 681 
 682             return (plotter != null) ? plotter.toString() : null;
 683         }
 684 
 685         private Plotter getPlotter(MouseEvent e) {
 686             Point p = e.getPoint();
 687             Plotter plotter = null;
 688 
 689             if (heapRect.contains(p)) {
 690                 plotter = heapPlotter;
 691             } else if (nonHeapRect.contains(p)) {
 692                 plotter = nonHeapPlotter;
 693             } else {
 694                 for (PoolPlotter poolPlotter : poolPlotters) {
 695                     if (getBarRect(poolPlotter).contains(p)) {
 696                         plotter = poolPlotter;
 697                         break;
 698                     }
 699                 }
 700             }
 701             return plotter;
 702         }
 703 
 704         public void mousePressed(MouseEvent e) {}
 705         public void mouseReleased(MouseEvent e) {}
 706         public void mouseEntered(MouseEvent e) {}
 707         public void mouseExited(MouseEvent e) {}
 708 
 709 
 710         public AccessibleContext getAccessibleContext() {
 711             if (accessibleContext == null) {
 712                 accessibleContext = new AccessiblePoolChart();
 713             }
 714             return accessibleContext;
 715         }
 716 
 717         protected class AccessiblePoolChart extends AccessibleJPanel {
 718             public String getAccessibleName() {
 719                 String name = Messages.MEMORY_TAB_POOL_CHART_ACCESSIBLE_NAME;
 720 
 721                 String keyValueList = "";
 722                 for (PoolPlotter poolPlotter : poolPlotters) {
 723                     String value = (poolPlotter.value * 100 / poolPlotter.max) + "%";
 724                     // Assume format string ends with newline
 725                     keyValueList +=
 726                         Resources.format(Messages.PLOTTER_ACCESSIBLE_NAME_KEY_AND_VALUE,
 727                                 poolPlotter.toString(), value);
 728                     if (poolPlotter.threshold > 0L) {
 729                         String threshold =
 730                             (poolPlotter.threshold * 100 / poolPlotter.max) + "%";
 731                         if (poolPlotter.value > poolPlotter.threshold) {
 732                             keyValueList +=
 733                                Resources.format(Messages.MEMORY_TAB_POOL_CHART_ABOVE_THRESHOLD,
 734                                         threshold);
 735                         } else {
 736                             keyValueList +=
 737                                     Resources.format(Messages.MEMORY_TAB_POOL_CHART_BELOW_THRESHOLD,
 738                                         threshold);
 739                         }
 740                     }
 741                 }
 742 
 743                 return name + "\n" + keyValueList + ".";
 744             }
 745         }
 746     }
 747 
 748 
 749     OverviewPanel[] getOverviewPanels() {
 750         if (overviewPanel == null) {
 751             overviewPanel = new MemoryOverviewPanel();
 752         }
 753         return new OverviewPanel[] { overviewPanel };
 754     }
 755 
 756     private static class MemoryOverviewPanel extends OverviewPanel {
 757         MemoryOverviewPanel() {
 758             super(Messages.HEAP_MEMORY_USAGE, usedKey, Messages.USED, Plotter.Unit.BYTES);
 759         }
 760 
 761         private void updateMemoryInfo(long used, long committed, long max) {
 762             getInfoLabel().setText(Resources.format(Messages.MEMORY_TAB_INFO_LABEL_FORMAT,
 763                                            formatBytes(used, true),
 764                                            formatBytes(committed, true),
 765                                            formatBytes(max, true)));
 766         }
 767     }
 768 }