       1 /*
       2  * Copyright (c) 2004, 2013, 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
      43 import static sun.tools.jconsole.Formatter.*;
      44 import static sun.tools.jconsole.Utilities.*;
      45
      46 @SuppressWarnings("serial")
      47 class MemoryTab extends Tab implements ActionListener, ItemListener {
      48     JComboBox<Plotter> plotterChoice;
      49     TimeComboBox timeComboBox;
      50     JButton gcButton;
      51
      52     PlotterPanel plotterPanel;
      53     JPanel bottomPanel;
      54     HTMLPane details;
      55     PoolChart poolChart;
      56
      57     ArrayList<Plotter> plotterList;
      58     Plotter heapPlotter, nonHeapPlotter;
      59
      60     private MemoryOverviewPanel overviewPanel;
      61
      62     private static final String usedKey        = "used";
      63     private static final String committedKey   = "committed";
      64     private static final String maxKey         = "max";
      65     private static final String thresholdKey   = "threshold";
      66     private static final Color  usedColor      = Plotter.defaultColor;
      67     private static final Color  committedColor = null;
      68     private static final Color  maxColor       = null;
      69     private static final Color  thresholdColor = Color.red;
      70
      71     /*
      72       Hierarchy of panels and layouts for this tab:
      73
      74         MemoryTab (BorderLayout)
      75
      76             North:  topPanel (BorderLayout)
      77
      78                         Center: controlPanel (FlowLayout)
      79                                     plotterChoice, timeComboBox
      80
      81                         East:   topRightPanel (FlowLayout)
      82                                     gcButton
      83
      84             Center: plotterPanel
      85
      86                         Center: plotter
      87
      88             South:  bottomPanel (BorderLayout)
      89
      90                         Center: details
      91                         East:   poolChart
      92     */
      93
      94
      95     public static String getTabName() {
      96         return Messages.MEMORY;
      97     }
      98
      99     public MemoryTab(VMPanel vmPanel) {
     100         super(vmPanel, getTabName());
     101
     102         setLayout(new BorderLayout(0, 0));
     103         setBorder(new EmptyBorder(4, 4, 3, 4));
     104
     105         JPanel topPanel     = new JPanel(new BorderLayout());
     106                plotterPanel = new PlotterPanel(null);
     107                bottomPanel  = new JPanel(new BorderLayout());
     108
     109         add(topPanel,     BorderLayout.NORTH);
     110         add(plotterPanel, BorderLayout.CENTER);
     111
     112         JPanel controlPanel = new JPanel(new FlowLayout(FlowLayout.LEADING, 20, 5));
     113         topPanel.add(controlPanel, BorderLayout.CENTER);
     114
     115         // Plotter choice
     116         plotterChoice = new JComboBox<Plotter>();
     117         plotterChoice.addItemListener(this);
     118         controlPanel.add(new LabeledComponent(Messages.CHART_COLON,
     119                                               Resources.getMnemonicInt(Messages.CHART_COLON),
     120                                               plotterChoice));
     121
     122         // Range control
     123         timeComboBox = new TimeComboBox();
     124         controlPanel.add(new LabeledComponent(Messages.TIME_RANGE_COLON,
     125                                               Resources.getMnemonicInt(Messages.TIME_RANGE_COLON),
     126                                               timeComboBox));
     127
     128         gcButton = new JButton(Messages.PERFORM_GC);
     129         gcButton.setMnemonic(Resources.getMnemonicInt(Messages.PERFORM_GC));
     130         gcButton.addActionListener(this);
     131         gcButton.setToolTipText(Messages.PERFORM_GC_TOOLTIP);
     132         JPanel topRightPanel = new JPanel();
     133         topRightPanel.setBorder(new EmptyBorder(0, 65-8, 0, 70));
     134         topRightPanel.add(gcButton);
     135         topPanel.add(topRightPanel, BorderLayout.AFTER_LINE_ENDS);
     136
     137         bottomPanel.setBorder(new CompoundBorder(new TitledBorder(Messages.DETAILS),
     138                                                   new EmptyBorder(10, 10, 10, 10)));
     139
     140         details = new HTMLPane();
     141         setAccessibleName(details, Messages.DETAILS);
     142         bottomPanel.add(new JScrollPane(details), BorderLayout.CENTER);
     143
     144         poolChart = new PoolChart();
     145         bottomPanel.add(poolChart, BorderLayout.AFTER_LINE_ENDS);
     146     }
     147
     148
     149     private void createPlotters() throws IOException {
     150         plotterList = new ArrayList<Plotter>();
     151
     152         ProxyClient proxyClient = vmPanel.getProxyClient();
     153
     154         heapPlotter = new Plotter(Plotter.Unit.BYTES) {
     155             public String toString() {
     156                 return Messages.HEAP_MEMORY_USAGE;
     157             }
     158         };
     159         proxyClient.addWeakPropertyChangeListener(heapPlotter);
     160
     161         nonHeapPlotter = new Plotter(Plotter.Unit.BYTES) {
     162             public String toString() {
     163                 return Messages.NON_HEAP_MEMORY_USAGE;
     164             }
     165         };
     166
     167         setAccessibleName(heapPlotter,
     168                           Messages.MEMORY_TAB_HEAP_PLOTTER_ACCESSIBLE_NAME);
     169         setAccessibleName(nonHeapPlotter,
     170                           Messages.MEMORY_TAB_NON_HEAP_PLOTTER_ACCESSIBLE_NAME);
     171
     172         proxyClient.addWeakPropertyChangeListener(nonHeapPlotter);
     173
     174         heapPlotter.createSequence(usedKey,         Messages.USED,      usedColor,      true);
     175         heapPlotter.createSequence(committedKey,    Messages.COMMITTED, committedColor, false);
     176         heapPlotter.createSequence(maxKey,          Messages.MAX,       maxColor,       false);
     177
     178         nonHeapPlotter.createSequence(usedKey,      Messages.USED,      usedColor,      true);
     179         nonHeapPlotter.createSequence(committedKey, Messages.COMMITTED, committedColor, false);
     180         nonHeapPlotter.createSequence(maxKey,       Messages.MAX,       maxColor,       false);
     181
     182         plotterList.add(heapPlotter);
     183         plotterList.add(nonHeapPlotter);
     184
     185         // Now add memory pools
     186         Map<ObjectName, MBeanInfo> mBeanMap = proxyClient.getMBeans("java.lang");
     187         Set<ObjectName> keys = mBeanMap.keySet();
     188         ObjectName[] objectNames = keys.toArray(new ObjectName[keys.size()]);
     189         ArrayList<PoolPlotter> nonHeapPlotters = new ArrayList<PoolPlotter>(2);
     190         for (ObjectName objectName : objectNames) {
     191             String type = objectName.getKeyProperty("type");
     192             if (type.equals("MemoryPool")) {
     193                 String name = Resources.format(Messages.MEMORY_POOL_LABEL,
     194                                                objectName.getKeyProperty("name"));
     195                 // Heap or non-heap?
     196                 boolean isHeap = false;
     197                 AttributeList al =
     198                     proxyClient.getAttributes(objectName,
     199                                               new String[] { "Type" });
     200                 if (al.size() > 0) {
     201                     isHeap = MemoryType.HEAP.name().equals(((Attribute)al.get(0)).getValue());
     202                 }
     203                 PoolPlotter poolPlotter = new PoolPlotter(objectName, name, isHeap);
     204                 proxyClient.addWeakPropertyChangeListener(poolPlotter);
     205
     206                 poolPlotter.createSequence(usedKey,      Messages.USED,      usedColor,      true);
     207                 poolPlotter.createSequence(committedKey, Messages.COMMITTED, committedColor, false);
     208                 poolPlotter.createSequence(maxKey,       Messages.MAX,       maxColor,       false);
     209                 poolPlotter.createSequence(thresholdKey, Messages.THRESHOLD, thresholdColor, false);
     210                 poolPlotter.setUseDashedTransitions(thresholdKey, true);
     211
     212                 if (isHeap) {
     213                     plotterList.add(poolPlotter);
     214                 } else {
     215                     // Will be added to plotterList below
     216                     nonHeapPlotters.add(poolPlotter);
     217                 }
     218             }
     219         }
     220         // Add non-heap plotters last
     221         for (PoolPlotter poolPlotter : nonHeapPlotters) {
     222             plotterList.add(poolPlotter);
     223         }
     224     }
     225
     226
     227     public void itemStateChanged(ItemEvent ev) {
     228         if (ev.getStateChange() == ItemEvent.SELECTED) {
     229             Plotter plotter = (Plotter)plotterChoice.getSelectedItem();
     230             plotterPanel.setPlotter(plotter);
     231             plotterPanel.repaint();
     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 }