Mercurial > hg > release > thermostat-1.0
changeset 1085:6c9d61f870b3
Refactor Heap dumper, first part
review-thread: http://icedtea.classpath.org/pipermail/thermostat/2013-May/006577.html
reviewed-by: jerboaa
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/client/swing/src/main/java/com/redhat/thermostat/client/swing/components/CompositeIcon.java Tue May 14 21:14:43 2013 +0200 @@ -0,0 +1,93 @@ +/* + * Copyright 2012, 2013 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * <http://www.gnu.org/licenses/>. + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.client.swing.components; + +import java.awt.AlphaComposite; +import java.awt.Component; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.image.BufferedImage; + +import com.redhat.thermostat.client.swing.GraphicsUtils; +import com.redhat.thermostat.client.ui.IconDescriptor; + +/** + * + */ +@SuppressWarnings("serial") +public class CompositeIcon extends Icon { + + private Icon mask; + + public CompositeIcon(IconDescriptor mask, IconDescriptor thisIcon) { + this(mask, new Icon(thisIcon)); + } + + public CompositeIcon(IconDescriptor mask, Icon thisIcon) { + this(new Icon(mask), thisIcon); + } + + public CompositeIcon(Icon mask, Icon thisIcon) { + super(thisIcon.getImage()); + this.mask = mask; + } + + @Override + public synchronized void paintIcon(Component c, Graphics g, int x, int y) { + GraphicsUtils utils = GraphicsUtils.getInstance(); + + Graphics2D graphics = utils.createAAGraphics(g); + + int iconW = getIconWidth(); + int iconH = getIconHeight(); + + BufferedImage imageBuffer = new BufferedImage(iconW, iconH, BufferedImage.TYPE_INT_ARGB); + Graphics2D buffer = imageBuffer.createGraphics(); + + mask.paintIcon(null, buffer, 0, 0); + + AlphaComposite ac = AlphaComposite.getInstance(AlphaComposite.SRC_IN); + buffer.setComposite(ac); + + super.paintIcon(null, buffer, 0, 0); + + buffer.dispose(); + + graphics.drawImage(imageBuffer, x, y, null); + graphics.dispose(); + } +}
--- a/client/swing/src/main/java/com/redhat/thermostat/client/swing/components/Icon.java Mon May 13 12:39:57 2013 -0400 +++ b/client/swing/src/main/java/com/redhat/thermostat/client/swing/components/Icon.java Tue May 14 21:14:43 2013 +0200 @@ -38,6 +38,8 @@ import com.redhat.thermostat.client.ui.IconDescriptor; import javax.swing.ImageIcon; +import java.awt.Dimension; +import java.awt.Image; /** */ @@ -47,4 +49,24 @@ public Icon(IconDescriptor descriptor) { super(descriptor.getData().array()); } + + public Icon() { + super(); + } + + public Icon(byte[] data) { + super(data); + } + + public Icon(Image image) { + super(image); + } + + /** + * Returns a {@link Dimension} object with this {@link Icon} + * {@code width} and {@code height}. + */ + public Dimension getDimension() { + return new Dimension(getIconWidth(), getIconHeight()); + } }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/client/swing/src/test/java/com/redhat/thermostat/client/swing/components/CompositeIconTest.java Tue May 14 21:14:43 2013 +0200 @@ -0,0 +1,152 @@ +package com.redhat.thermostat.client.swing.components; + +import static org.junit.Assert.assertEquals; + +import java.awt.Color; +import java.awt.Component; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.Image; +import java.awt.image.BufferedImage; +import java.beans.Transient; + +import org.junit.Test; + +import com.redhat.thermostat.client.swing.GraphicsUtils; +import com.redhat.thermostat.client.ui.Palette; + +public class CompositeIconTest { + + @Test + public void testDisplayIcon() { + + CompositeIcon icon = new CompositeIcon(new MaskIcon(), new SourceIcon()); + BufferedImage image = new BufferedImage(icon.getIconWidth(), icon.getIconHeight(), BufferedImage.TYPE_INT_ARGB); + Graphics2D graphics = (Graphics2D) image.getGraphics(); + icon.paintIcon(null, graphics, 0, 0); + graphics.dispose(); + + int pixel = image.getRGB(0, 0); + + int a = (pixel >> 24) & 0xFF; + int r = (pixel >> 16) & 0xFF; + int g = (pixel >> 8) & 0xFF; + int b = pixel & 0xFF; + + assertEquals(0, a); + assertEquals(0, r); + assertEquals(0, g); + assertEquals(0, b); + + pixel = image.getRGB(image.getWidth() - 1, 0); + + a = (pixel >> 24) & 0xFF; + r = (pixel >> 16) & 0xFF; + g = (pixel >> 8) & 0xFF; + b = pixel & 0xFF; + + assertEquals(0, a); + assertEquals(0, r); + assertEquals(0, g); + assertEquals(0, b); + + pixel = image.getRGB(image.getWidth() - 1, image.getHeight() - 1); + + a = (pixel >> 24) & 0xFF; + r = (pixel >> 16) & 0xFF; + g = (pixel >> 8) & 0xFF; + b = pixel & 0xFF; + + assertEquals(0, a); + assertEquals(0, r); + assertEquals(0, g); + assertEquals(0, b); + + pixel = image.getRGB(0, image.getHeight()/2); + + a = (pixel >> 24) & 0xFF; + r = (pixel >> 16) & 0xFF; + g = (pixel >> 8) & 0xFF; + b = pixel & 0xFF; + + // alpha of the source image is 200 + assertEquals(200, a); + assertEquals(0, r); + assertEquals(0, g); + assertEquals(0, b); + + pixel = image.getRGB(image.getWidth() - 1, image.getHeight()/2); + + a = (pixel >> 24) & 0xFF; + r = (pixel >> 16) & 0xFF; + g = (pixel >> 8) & 0xFF; + b = pixel & 0xFF; + + // alpha of the source image is 200 + assertEquals(200, a); + assertEquals(255, r); + assertEquals(255, g); + assertEquals(255, b); + } + + private static class SourceIcon extends Icon { + @Override + public synchronized void paintIcon(Component c, Graphics g, int x, int y) { + + GraphicsUtils utils = GraphicsUtils.getInstance(); + + Color left = utils.deriveWithAlpha(Palette.BLACK.getColor(), 200); + Color right = utils.deriveWithAlpha(Palette.WHITE.getColor(), 200); + + g.setColor(left); + g.fillRect(x, y, getIconWidth()/2, getIconHeight()); + + g.setColor(right); + g.fillRect(x + getIconWidth()/2, y, getIconWidth()/2, getIconHeight()); + } + + @Override + public int getIconHeight() { + return 48; + } + + @Override + public int getIconWidth() { + return 48; + } + + @Override + @Transient + public Image getImage() { + BufferedImage image = new BufferedImage(getIconWidth(), getIconHeight(), BufferedImage.TYPE_INT_ARGB); + Graphics2D g = (Graphics2D) image.getGraphics(); + paintIcon(null, g, 0, 0); + g.dispose(); + return image; + } + } + + private static class MaskIcon extends Icon { + @Override + public synchronized void paintIcon(Component c, Graphics g, int x, int y) { + + // draw a black line in the middle + int w = getIconWidth()/2; + int h = getIconHeight()/2; + y = y + h; + + g.setColor(Color.BLACK); + g.drawLine(x, y, x + getIconWidth() - 1, y); + } + + @Override + public int getIconHeight() { + return 48; + } + + @Override + public int getIconWidth() { + return 48; + } + } +}
--- a/distribution/config/osgi-export.properties Mon May 13 12:39:57 2013 -0400 +++ b/distribution/config/osgi-export.properties Tue May 14 21:14:43 2013 +0200 @@ -38,6 +38,7 @@ org.jfree.chart.event org.jfree.chart.labels org.jfree.chart.plot +org.jfree.chart.panel org.jfree.chart.renderer.xy org.jfree.data org.jfree.data.general
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vm-heap-analysis/client-core/src/main/java/com/redhat/thermostat/vm/heap/analysis/client/core/HeapIconResources.java Tue May 14 21:14:43 2013 +0200 @@ -0,0 +1,65 @@ +/* + * Copyright 2012, 2013 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * <http://www.gnu.org/licenses/>. + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.vm.heap.analysis.client.core; + +import com.redhat.thermostat.client.ui.IconDescriptor; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; +import java.util.logging.Level; +import java.util.logging.Logger; + +public class HeapIconResources { + + public static final String PIN_MASK = "com/redhat/thermostat/vm/heap/analysis/client/core/pin_mask.png"; + + private static Map<String, IconDescriptor> icons = new HashMap<>(); + + public synchronized static IconDescriptor getIcon(String path) { + if (!icons.containsKey(path)) { + try { + IconDescriptor icon = IconDescriptor.loadIcon(HeapIconResources.class.getClassLoader(), path); + icons.put(path, icon); + + } catch (IOException ex) { + Logger.getLogger(HeapIconResources.class.getName()).log(Level.SEVERE, ex.getMessage(), ex); + } + } + return icons.get(path); + } +}
--- a/vm-heap-analysis/client-core/src/main/java/com/redhat/thermostat/vm/heap/analysis/client/core/HeapView.java Mon May 13 12:39:57 2013 -0400 +++ b/vm-heap-analysis/client-core/src/main/java/com/redhat/thermostat/vm/heap/analysis/client/core/HeapView.java Tue May 14 21:14:43 2013 +0200 @@ -46,7 +46,7 @@ import com.redhat.thermostat.vm.heap.analysis.common.HeapDump; public abstract class HeapView extends BasicView implements UIComponent { - + public enum HeapDumperAction { DUMP_REQUESTED, ANALYSE, @@ -84,6 +84,7 @@ public abstract void openDumpView(); public abstract void setChildView(BasicView childView); + public abstract void setActiveDump(HeapDump dump); public abstract void notifyHeapDumpComplete(); public abstract void displayWarning(String string);
--- a/vm-heap-analysis/client-core/src/main/java/com/redhat/thermostat/vm/heap/analysis/client/core/chart/OverviewChart.java Mon May 13 12:39:57 2013 -0400 +++ b/vm-heap-analysis/client-core/src/main/java/com/redhat/thermostat/vm/heap/analysis/client/core/chart/OverviewChart.java Tue May 14 21:14:43 2013 +0200 @@ -69,6 +69,8 @@ private String title; private String xAxis; private String yAxis; + + private JFreeChart chart; public OverviewChart(String title, String xAxis, String yAxis, String mainSeries, String secondarySeries) { @@ -83,7 +85,11 @@ used.setDescription(secondarySeries); } - public JFreeChart createChart(int height, Color bgColor) { + public JFreeChart getChart() { + return chart; + } + + public void createChart(int height, Color bgColor) { TimeSeriesCollection dataset = new TimeSeriesCollection(); @@ -92,7 +98,7 @@ dataset.addSeries(used); } - JFreeChart chart = ChartFactory.createTimeSeriesChart( + chart = ChartFactory.createTimeSeriesChart( title, xAxis, yAxis, @@ -139,7 +145,6 @@ yAxis.setRangeType(RangeType.POSITIVE); yAxis.setAutoRangeMinimumSize(10); yAxis.setAutoRange(true); - return chart; } public void addData(long timeStamp, long used, long total) {
--- a/vm-heap-analysis/client-core/src/main/java/com/redhat/thermostat/vm/heap/analysis/client/core/internal/HeapDumpController.java Mon May 13 12:39:57 2013 -0400 +++ b/vm-heap-analysis/client-core/src/main/java/com/redhat/thermostat/vm/heap/analysis/client/core/internal/HeapDumpController.java Tue May 14 21:14:43 2013 +0200 @@ -94,27 +94,30 @@ public HeapDumpController(final VmMemoryStatDAO vmMemoryStatDao, - final VmInfoDAO vmInfoDao, - final HeapDAO heapDao, final VmRef ref, - final ApplicationService appService, HeapViewProvider viewProvider, - HeapDumpDetailsViewProvider detailsViewProvider, - HeapHistogramViewProvider histogramProvider, - ObjectDetailsViewProvider objectDetailsProvider, - ObjectRootsViewProvider objectRootsProvider) { + final VmInfoDAO vmInfoDao, + final HeapDAO heapDao, final VmRef ref, + final ApplicationService appService, HeapViewProvider viewProvider, + HeapDumpDetailsViewProvider detailsViewProvider, + HeapHistogramViewProvider histogramProvider, + ObjectDetailsViewProvider objectDetailsProvider, + ObjectRootsViewProvider objectRootsProvider) + { this(vmMemoryStatDao, vmInfoDao, heapDao, ref, appService, viewProvider, - detailsViewProvider, histogramProvider, objectDetailsProvider, - objectRootsProvider, new HeapDumper(ref)); + detailsViewProvider, histogramProvider, objectDetailsProvider, + objectRootsProvider, new HeapDumper(ref)); } HeapDumpController(final VmMemoryStatDAO vmMemoryStatDao, - final VmInfoDAO vmInfoDao, - final HeapDAO heapDao, final VmRef ref, - final ApplicationService appService, HeapViewProvider viewProvider, - HeapDumpDetailsViewProvider detailsViewProvider, - HeapHistogramViewProvider histogramProvider, - ObjectDetailsViewProvider objectDetailsProvider, - ObjectRootsViewProvider objectRootsProvider, - final HeapDumper heapDumper) { + final VmInfoDAO vmInfoDao, + final HeapDAO heapDao, final VmRef ref, + final ApplicationService appService, + HeapViewProvider viewProvider, + HeapDumpDetailsViewProvider detailsViewProvider, + HeapHistogramViewProvider histogramProvider, + ObjectDetailsViewProvider objectDetailsProvider, + ObjectRootsViewProvider objectRootsProvider, + final HeapDumper heapDumper) + { this.objectDetailsViewProvider = objectDetailsProvider; this.objectRootsViewProvider = objectRootsProvider; this.histogramViewProvider = histogramProvider; @@ -136,7 +139,7 @@ timer.setInitialDelay(0); timer.setDelay(1000); - model.setRange(3600); + model.setRange(1800); timer.setTimeUnit(TimeUnit.MILLISECONDS); timer.setSchedulingType(SchedulingType.FIXED_RATE); @@ -228,17 +231,20 @@ } private void showHeapDumpDetails(HeapDump dump) { - HeapDumpDetailsController controller = new HeapDumpDetailsController( - appService, detailsViewProvider, histogramViewProvider, - objectDetailsViewProvider, objectRootsViewProvider); + HeapDumpDetailsController controller = + new HeapDumpDetailsController(appService, detailsViewProvider, + histogramViewProvider, + objectDetailsViewProvider, + objectRootsViewProvider); controller.setDump(dump); + view.setActiveDump(dump); view.setChildView(controller.getView()); view.openDumpView(); } @Override public UIComponent getView() { - return (UIComponent) view; + return view; } @Override @@ -297,8 +303,6 @@ model.notifyListenersOfModelChange(); } - } - }
Binary file vm-heap-analysis/client-core/src/main/resources/com/redhat/thermostat/vm/heap/analysis/client/core/pin_mask.png has changed
--- a/vm-heap-analysis/client-swing/pom.xml Mon May 13 12:39:57 2013 -0400 +++ b/vm-heap-analysis/client-swing/pom.xml Tue May 14 21:14:43 2013 +0200 @@ -58,7 +58,8 @@ <Bundle-Activator>com.redhat.thermostat.vm.heap.analysis.client.swing.internal.Activator</Bundle-Activator> <Bundle-SymbolicName>com.redhat.thermostat.vm.heap.analysis.client.swing</Bundle-SymbolicName> <Private-Package> - com.redhat.thermostat.vm.heap.analysis.client.swing.internal + com.redhat.thermostat.vm.heap.analysis.client.swing.internal, + com.redhat.thermostat.vm.heap.analysis.client.swing.internal.stats, </Private-Package> <!-- Do not autogenerate uses clauses in Manifests --> <_nouses>true</_nouses>
--- a/vm-heap-analysis/client-swing/src/main/java/com/redhat/thermostat/vm/heap/analysis/client/swing/internal/HeapSwingView.java Mon May 13 12:39:57 2013 -0400 +++ b/vm-heap-analysis/client-swing/src/main/java/com/redhat/thermostat/vm/heap/analysis/client/swing/internal/HeapSwingView.java Tue May 14 21:14:43 2013 +0200 @@ -46,10 +46,14 @@ import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.SwingUtilities; -import javax.swing.event.ListSelectionEvent; -import javax.swing.event.ListSelectionListener; + +import com.redhat.thermostat.vm.heap.analysis.client.swing.internal.stats.HeapDumpListener; +import com.redhat.thermostat.vm.heap.analysis.client.swing.internal.stats.HeapSelectionEvent; -import org.jfree.chart.ChartPanel; +import org.jfree.chart.axis.DateAxis; +import org.jfree.chart.event.AxisChangeEvent; +import org.jfree.chart.event.AxisChangeListener; +import org.jfree.chart.plot.XYPlot; import com.redhat.thermostat.client.core.views.BasicView; import com.redhat.thermostat.client.swing.ComponentVisibleListener; @@ -59,6 +63,8 @@ import com.redhat.thermostat.vm.heap.analysis.client.core.HeapView; import com.redhat.thermostat.vm.heap.analysis.client.core.chart.OverviewChart; import com.redhat.thermostat.vm.heap.analysis.client.locale.LocaleResources; +import com.redhat.thermostat.vm.heap.analysis.client.swing.internal.stats.HeapChartPanel; +import com.redhat.thermostat.vm.heap.analysis.client.swing.internal.stats.StatsPanel; import com.redhat.thermostat.vm.heap.analysis.common.HeapDump; public class HeapSwingView extends HeapView implements SwingComponent { @@ -80,11 +86,11 @@ heapDumperNotifier.fireAction(HeapDumperAction.DUMP_REQUESTED); } }); - - stats.addDumpListListener(new ListSelectionListener() { + + stats.addDumpListListener(new HeapDumpListener() { @Override - public void valueChanged(ListSelectionEvent arg0) { - HeapDump dump = stats.getSelectedHeapDump(); + public void actionPerformed(HeapSelectionEvent e) { + HeapDump dump = e.getSource().getHeapDump(); heapDumperNotifier.fireAction(HeapDumperAction.ANALYSE, dump); } }); @@ -119,10 +125,25 @@ SwingUtilities.invokeLater(new Runnable() { @Override public void run() { - ChartPanel charts = new ChartPanel(model.createChart(stats.getWidth(), stats.getBackground())); + + // TODO, move to controller + model.createChart(stats.getWidth(), stats.getBackground()); + + final HeapChartPanel charts = new HeapChartPanel(model.getChart()); + + XYPlot plot = model.getChart().getXYPlot(); + DateAxis domainAxis = (DateAxis) plot.getDomainAxis(); + domainAxis.addChangeListener(new AxisChangeListener() { + @Override + public void axisChanged(AxisChangeEvent event) { + // somehow the chart panel doesn't see this + charts.revalidate(); + } + }); + /* * By default, ChartPanel scales itself instead of redrawing things when - * it's resized. To have it resize automatically, we need to set minimum + * it's re-sized. To have it resize automatically, we need to set minimum * and maximum sizes. Lets constrain the minimum, but not the maximum * size. */ @@ -220,7 +241,17 @@ }); } } - + + @Override + public void setActiveDump(final HeapDump dump) { + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + stats.selectOverlay(dump); + } + }); + } + @Override public Component getUiComponent() { return visiblePane;
--- a/vm-heap-analysis/client-swing/src/main/java/com/redhat/thermostat/vm/heap/analysis/client/swing/internal/StatsPanel.java Mon May 13 12:39:57 2013 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,225 +0,0 @@ -/* - * Copyright 2012, 2013 Red Hat, Inc. - * - * This file is part of Thermostat. - * - * Thermostat is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published - * by the Free Software Foundation; either version 2, or (at your - * option) any later version. - * - * Thermostat is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Thermostat; see the file COPYING. If not see - * <http://www.gnu.org/licenses/>. - * - * Linking this code with other modules is making a combined work - * based on this code. Thus, the terms and conditions of the GNU - * General Public License cover the whole combination. - * - * As a special exception, the copyright holders of this code give - * you permission to link this code with independent modules to - * produce an executable, regardless of the license terms of these - * independent modules, and to copy and distribute the resulting - * executable under terms of your choice, provided that you also - * meet, for each linked independent module, the terms and conditions - * of the license of that module. An independent module is a module - * which is not derived from or based on this code. If you modify - * this code, you may extend this exception to your version of the - * library, but you are not obligated to do so. If you do not wish - * to do so, delete this exception statement from your version. - */ - -package com.redhat.thermostat.vm.heap.analysis.client.swing.internal; - -import java.awt.event.ActionListener; -import java.util.List; - -import javax.swing.BoxLayout; -import javax.swing.DefaultListModel; -import javax.swing.GroupLayout; -import javax.swing.GroupLayout.Alignment; -import javax.swing.JButton; -import javax.swing.JLabel; -import javax.swing.JList; -import javax.swing.JPanel; -import javax.swing.JScrollPane; -import javax.swing.LayoutStyle.ComponentPlacement; -import javax.swing.ListSelectionModel; -import javax.swing.SwingConstants; -import javax.swing.event.ListSelectionListener; - -import com.redhat.thermostat.common.locale.Translate; -import com.redhat.thermostat.vm.heap.analysis.client.core.HeapView.DumpDisabledReason; -import com.redhat.thermostat.vm.heap.analysis.client.locale.LocaleResources; -import com.redhat.thermostat.vm.heap.analysis.common.HeapDump; - -@SuppressWarnings("serial") -public class StatsPanel extends JPanel { - - private static final Translate<LocaleResources> translator = LocaleResources.createLocalizer(); - - private JPanel leftPanel; - - private JButton heapDumpButton; - private JList<HeapDump> dumpList; - private DefaultListModel<HeapDump> listModel; - - private JLabel max; - private JLabel current; - - public StatsPanel() { - - leftPanel = new JPanel(); - leftPanel.setLayout(new BoxLayout(leftPanel, BoxLayout.X_AXIS)); - - JPanel rightPanel = new JPanel(); - GroupLayout groupLayout = new GroupLayout(this); - groupLayout.setHorizontalGroup( - groupLayout.createParallelGroup(Alignment.LEADING) - .addGroup(Alignment.TRAILING, groupLayout.createSequentialGroup() - .addComponent(leftPanel, GroupLayout.DEFAULT_SIZE, 343, Short.MAX_VALUE) - .addPreferredGap(ComponentPlacement.UNRELATED) - .addComponent(rightPanel, GroupLayout.PREFERRED_SIZE, 252, GroupLayout.PREFERRED_SIZE)) - ); - groupLayout.setVerticalGroup( - groupLayout.createParallelGroup(Alignment.LEADING) - .addComponent(rightPanel, GroupLayout.DEFAULT_SIZE, 293, Short.MAX_VALUE) - .addComponent(leftPanel, GroupLayout.DEFAULT_SIZE, 293, Short.MAX_VALUE) - ); - - JLabel currentLabel = new JLabel("used:"); - currentLabel.setHorizontalAlignment(SwingConstants.LEFT); - - JLabel maxLabel = new JLabel("capacity:"); - maxLabel.setHorizontalAlignment(SwingConstants.LEFT); - - current = new JLabel("-"); - current.setHorizontalAlignment(SwingConstants.RIGHT); - - max = new JLabel("-"); - max.setHorizontalAlignment(SwingConstants.RIGHT); - - heapDumpButton = new JButton(translator.localize(LocaleResources.TRIGGER_HEAP_DUMP)); - heapDumpButton.setName("heapDumpButton"); - - JScrollPane dumpListScrollPane = new JScrollPane(); - dumpList = new JList<>(); - dumpList.setName("heapDumpList"); - listModel = new DefaultListModel<>(); - dumpList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); - dumpList.setModel(listModel); - - GroupLayout gl_rightPanel = new GroupLayout(rightPanel); - gl_rightPanel.setHorizontalGroup( - gl_rightPanel.createParallelGroup(Alignment.TRAILING) - .addGroup(gl_rightPanel.createSequentialGroup() - .addContainerGap() - .addGroup(gl_rightPanel.createParallelGroup(Alignment.TRAILING) - .addComponent(dumpListScrollPane, Alignment.LEADING, GroupLayout.DEFAULT_SIZE, 173, Short.MAX_VALUE) - .addComponent(heapDumpButton, GroupLayout.DEFAULT_SIZE, 173, Short.MAX_VALUE) - .addGroup(gl_rightPanel.createSequentialGroup() - .addGroup(gl_rightPanel.createParallelGroup(Alignment.TRAILING, false) - .addComponent(maxLabel, Alignment.LEADING, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addComponent(currentLabel, Alignment.LEADING, GroupLayout.DEFAULT_SIZE, 48, Short.MAX_VALUE)) - .addPreferredGap(ComponentPlacement.RELATED) - .addGroup(gl_rightPanel.createParallelGroup(Alignment.TRAILING) - .addComponent(current, GroupLayout.DEFAULT_SIZE, 119, Short.MAX_VALUE) - .addComponent(max, GroupLayout.PREFERRED_SIZE, 119, GroupLayout.PREFERRED_SIZE)))) - .addContainerGap()) - ); - gl_rightPanel.setVerticalGroup( - gl_rightPanel.createParallelGroup(Alignment.LEADING) - .addGroup(gl_rightPanel.createSequentialGroup() - .addContainerGap() - .addGroup(gl_rightPanel.createParallelGroup(Alignment.BASELINE) - .addComponent(currentLabel, GroupLayout.PREFERRED_SIZE, 15, GroupLayout.PREFERRED_SIZE) - .addComponent(current)) - .addPreferredGap(ComponentPlacement.RELATED) - .addGroup(gl_rightPanel.createParallelGroup(Alignment.BASELINE) - .addComponent(maxLabel) - .addComponent(max)) - .addGap(18) - .addComponent(heapDumpButton) - .addGap(18) - .addComponent(dumpListScrollPane, GroupLayout.DEFAULT_SIZE, 172, Short.MAX_VALUE) - .addContainerGap()) - ); - rightPanel.setLayout(gl_rightPanel); - setLayout(groupLayout); - - dumpListScrollPane.setViewportView(dumpList); - } - - void setChartPanel(JPanel panel) { - leftPanel.removeAll(); - leftPanel.add(panel); - leftPanel.revalidate(); - repaint(); - } - - public void setMax(String capacity) { - max.setText(capacity); - } - - public void setUsed(String used) { - current.setText(used); - } - - void addHeapDumperListener(ActionListener listener) { - heapDumpButton.addActionListener(listener); - } - - void addDumpListListener(ListSelectionListener listener) { - dumpList.addListSelectionListener(listener); - } - - public void disableHeapDumperControl(DumpDisabledReason reason) { - String text = null; - switch (reason) { - case DUMP_IN_PROGRESS: - text = translator.localize(LocaleResources.HEAP_DUMP_IN_PROGRESS); - break; - case PROCESS_DEAD: - text = translator.localize(LocaleResources.PROCESS_EXITED); - break; - } - heapDumpButton.setText(text); - heapDumpButton.setEnabled(false); - } - - public void enableHeapDumperControl() { - heapDumpButton.setText(translator.localize(LocaleResources.TRIGGER_HEAP_DUMP)); - heapDumpButton.setEnabled(true); - } - - public void addDump(HeapDump dump) { - - listModel.addElement(dump); - } - - public void clearDumpList() { - listModel.clear(); - } - - public HeapDump getSelectedHeapDump() { - return dumpList.getSelectedValue(); - } - - public void updateHeapDumpList(List<HeapDump> heapDumps) { - int numItemsBefore = listModel.getSize(); - for (HeapDump heapDump : heapDumps) { - if (! listModel.contains(heapDump)) { - listModel.addElement(heapDump); - } - } - if (numItemsBefore == 0 && listModel.getSize() > 0) { - dumpList.repaint(); - } - } -} -
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vm-heap-analysis/client-swing/src/main/java/com/redhat/thermostat/vm/heap/analysis/client/swing/internal/stats/HeapChartPanel.java Tue May 14 21:14:43 2013 +0200 @@ -0,0 +1,76 @@ +/* + * Copyright 2012, 2013 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * <http://www.gnu.org/licenses/>. + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.vm.heap.analysis.client.swing.internal.stats; + +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.geom.Rectangle2D; + +import org.jfree.chart.ChartPanel; +import org.jfree.chart.JFreeChart; +import org.jfree.chart.event.ChartChangeEvent; +import org.jfree.chart.event.ChartChangeListener; + +@SuppressWarnings("serial") +public class HeapChartPanel extends ChartPanel { + + public HeapChartPanel(JFreeChart chart) { + + super(chart); + + setName(HeapChartPanel.class.getName()); + + setLayout(new HeapChartPanelLayout()); + chart.addChangeListener(new ChartChangeListener() { + @Override + public void chartChanged(ChartChangeEvent event) { + doLayout(); + } + }); + } + + @Override + protected void paintChildren(Graphics g) { + Rectangle2D area = getScreenDataArea(); + Graphics2D graphics = (Graphics2D) g.create(); + graphics.setClip(area); + + super.paintChildren(graphics); + + graphics.dispose(); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vm-heap-analysis/client-swing/src/main/java/com/redhat/thermostat/vm/heap/analysis/client/swing/internal/stats/HeapChartPanelLayout.java Tue May 14 21:14:43 2013 +0200 @@ -0,0 +1,169 @@ +/* + * Copyright 2012, 2013 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * <http://www.gnu.org/licenses/>. + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.vm.heap.analysis.client.swing.internal.stats; + +import java.awt.Component; +import java.awt.Container; +import java.awt.Dimension; +import java.awt.LayoutManager2; +import java.awt.Rectangle; +import java.awt.geom.Rectangle2D; + +import org.jfree.chart.axis.DateAxis; +import org.jfree.chart.plot.XYPlot; + +import com.redhat.thermostat.common.model.LongRange; +import com.redhat.thermostat.common.model.LongRangeNormalizer; + +/** + * + */ +public class HeapChartPanelLayout implements LayoutManager2 { + + @Override + public void addLayoutComponent(String name, Component comp) { + } + + @Override + public void removeLayoutComponent(Component comp) { + } + + @Override + public Dimension preferredLayoutSize(Container parent) { + return new Dimension(5, 5); + } + + @Override + public Dimension minimumLayoutSize(Container parent) { + return new Dimension(5, 5); + } + + @Override + public void layoutContainer(Container parent) { + + HeapChartPanel chartPanel = (HeapChartPanel) parent; + synchronized (chartPanel.getTreeLock()) { + + Rectangle2D area = chartPanel.getScreenDataArea(); + + XYPlot plot = (XYPlot) chartPanel.getChart().getPlot(); + DateAxis domainAxis = (DateAxis) plot.getDomainAxis(); + + // need first and last value + + long max = domainAxis.getMaximumDate().getTime(); + long min = domainAxis.getMinimumDate().getTime(); + + LongRange offset = new LongRange(min, max); + + LongRangeNormalizer normaliser = new LongRangeNormalizer(offset); + chartPanel.getScreenDataArea(); + + normaliser.setMaxNormalized((int) (area.getX() + area.getWidth())); + normaliser.setMinNormalized((int) area.getX()); + + int y = (int) (area.getHeight()/2); + int bound = y; + + boolean moveUp = false; + int delta = 0; + int x = 0; + + Component[] children = chartPanel.getComponents(); + for (Component _child : children) { + + if (!(_child instanceof OverlayComponent)) { + continue; + } + + OverlayComponent child = (OverlayComponent) _child; + + if (!child.isVisible()) { + continue; + } + + Dimension preferredSize = child.getIconCenter(); + + normaliser.setValue(child.getTimestamp()); + x = (int) normaliser.getValueNormalized() - preferredSize.width; + + preferredSize = child.getPreferredSize(); + Rectangle bounds = new Rectangle(x, y, preferredSize.width, preferredSize.height); + + if (delta > bound) { + delta = 0; + } + + if (moveUp) { + bounds.y = bounds.y - delta; + } else { + bounds.y = bounds.y + delta; + delta += bounds.height; + } + moveUp = !moveUp; + + child.setSize(preferredSize); + child.setBounds(bounds); + } + } + } + + @Override + public void addLayoutComponent(Component comp, Object constraints) { + + } + + @Override + public Dimension maximumLayoutSize(Container target) { + return new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE); + } + + @Override + public float getLayoutAlignmentX(Container target) { + return 0.5f; + } + + @Override + public float getLayoutAlignmentY(Container target) { + return 0.5f; + } + + @Override + public void invalidateLayout(Container target) { + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vm-heap-analysis/client-swing/src/main/java/com/redhat/thermostat/vm/heap/analysis/client/swing/internal/stats/HeapDumpListener.java Tue May 14 21:14:43 2013 +0200 @@ -0,0 +1,44 @@ +/* + * Copyright 2012, 2013 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * <http://www.gnu.org/licenses/>. + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.vm.heap.analysis.client.swing.internal.stats; + +import java.util.EventListener; + +public interface HeapDumpListener extends EventListener { + + public void actionPerformed(HeapSelectionEvent e); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vm-heap-analysis/client-swing/src/main/java/com/redhat/thermostat/vm/heap/analysis/client/swing/internal/stats/HeapDumperPopup.java Tue May 14 21:14:43 2013 +0200 @@ -0,0 +1,60 @@ +/* + * Copyright 2012, 2013 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * <http://www.gnu.org/licenses/>. + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.vm.heap.analysis.client.swing.internal.stats; + +import java.awt.event.ActionListener; + +import javax.swing.JMenuItem; + +import com.redhat.thermostat.client.swing.components.ThermostatPopupMenu; +import com.redhat.thermostat.common.locale.Translate; +import com.redhat.thermostat.vm.heap.analysis.client.locale.LocaleResources; + +@SuppressWarnings("serial") +public class HeapDumperPopup extends ThermostatPopupMenu { + private static final Translate<LocaleResources> translator = LocaleResources.createLocalizer(); + private JMenuItem dumper; + + public HeapDumperPopup() { + dumper = new JMenuItem(translator.localize(LocaleResources.TRIGGER_HEAP_DUMP)); + add(dumper); + } + + public void addDumperListener(ActionListener listener) { + dumper.addActionListener(listener); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vm-heap-analysis/client-swing/src/main/java/com/redhat/thermostat/vm/heap/analysis/client/swing/internal/stats/HeapSelectionEvent.java Tue May 14 21:14:43 2013 +0200 @@ -0,0 +1,51 @@ +/* + * Copyright 2012, 2013 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * <http://www.gnu.org/licenses/>. + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.vm.heap.analysis.client.swing.internal.stats; + +import java.util.EventObject; + +public class HeapSelectionEvent extends EventObject { + + public HeapSelectionEvent(OverlayComponent source) { + super(source); + } + + @Override + public OverlayComponent getSource() { + return (OverlayComponent) super.getSource(); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vm-heap-analysis/client-swing/src/main/java/com/redhat/thermostat/vm/heap/analysis/client/swing/internal/stats/OverlayComponent.java Tue May 14 21:14:43 2013 +0200 @@ -0,0 +1,208 @@ +/* + * Copyright 2012, 2013 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * <http://www.gnu.org/licenses/>. + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.vm.heap.analysis.client.swing.internal.stats; + +import com.redhat.thermostat.client.swing.GraphicsUtils; +import com.redhat.thermostat.client.swing.components.CompositeIcon; +import com.redhat.thermostat.client.swing.components.Icon; +import com.redhat.thermostat.client.swing.components.ShadowLabel; +import com.redhat.thermostat.client.swing.components.timeline.TimelineUtils; +import com.redhat.thermostat.client.ui.Palette; +import com.redhat.thermostat.vm.heap.analysis.client.core.HeapIconResources; +import com.redhat.thermostat.vm.heap.analysis.common.HeapDump; + +import java.awt.Color; +import java.awt.Component; +import java.awt.Dimension; +import java.awt.GradientPaint; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.Image; +import java.awt.Paint; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.awt.image.BufferedImage; +import java.beans.Transient; +import java.util.Date; +import java.util.Objects; + +import javax.swing.ButtonModel; + +@SuppressWarnings("serial") +public class OverlayComponent extends ShadowLabel { + + private HeapDump dump; + + private boolean selected; + private String _text; + + public class NiceIcon extends Icon { + + private Icon src; + + public NiceIcon(Icon src) { + this.src = src; + } + + @Override + public int getIconHeight() { + return src.getIconHeight(); + } + + @Override + public int getIconWidth() { + return src.getIconWidth(); + } + + @Override + public synchronized void paintIcon(Component c, Graphics g, int x, int y) { + + GraphicsUtils utils = GraphicsUtils.getInstance(); + Graphics2D graphics = utils.createAAGraphics(g); + + Color up = utils.deriveWithAlpha(Palette.DARK_GRAY.getColor(), 200); + Color bottom = utils.deriveWithAlpha(Palette.BLACK.getColor(), 200); + + Paint gradient = new GradientPaint(0, 0, up, 0, getIconHeight(), bottom); + graphics.setPaint(gradient); + + graphics.fillRect(x, y, getIconWidth(), getIconHeight()); + + graphics.dispose(); + } + + @Override + @Transient + public Image getImage() { + BufferedImage image = new BufferedImage(getIconWidth(), getIconHeight(), BufferedImage.TYPE_INT_ARGB); + Graphics2D g = (Graphics2D) image.getGraphics(); + paintIcon(null, g, 0, 0); + g.dispose(); + return image; + } + } + + public OverlayComponent(HeapDump dump) { + + super(""); + + setName(OverlayComponent.class.getName()); + + Icon mask = new Icon(HeapIconResources.getIcon(HeapIconResources.PIN_MASK)); + Icon source = new NiceIcon(mask); + + setIcon(new CompositeIcon(mask, source)); + + this.dump = dump; + + this._text = new Date(dump.getTimestamp()).toString(); + + setFont(TimelineUtils.FONT); + + setOpaque(false); + + setToolTipText(_text); + + addMouseListener(new MouseAdapter() { + @Override + public void mouseEntered(MouseEvent e) { + if (!isSelected()) { + setText(_text); + repaint(); + } + } + + @Override + public void mouseExited(MouseEvent e) { + if (!isSelected()) { + setText(""); + repaint(); + } + } + }); + } + + public HeapDump getHeapDump() { + return this.dump; + } + + public long getTimestamp() { + return dump.getTimestamp(); + } + + @Transient + public Dimension getIconCenter() { + Dimension preferredSize = ((Icon) getIcon()).getDimension(); + + preferredSize.width = (preferredSize.width / 2); + preferredSize.height = (preferredSize.height/2); + + return preferredSize; + } + + @Override + public int hashCode() { + return Objects.hash(Long.valueOf(dump.getTimestamp())); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + OverlayComponent other = (OverlayComponent) obj; + if (getTimestamp() != other.getTimestamp()) + return false; + return true; + } + + public void setSelected(boolean selected) { + this.selected = selected; + if (selected) { + setText(_text); + } else { + setText(""); + } + } + + public boolean isSelected() { + return selected; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vm-heap-analysis/client-swing/src/main/java/com/redhat/thermostat/vm/heap/analysis/client/swing/internal/stats/StatsPanel.java Tue May 14 21:14:43 2013 +0200 @@ -0,0 +1,192 @@ +/* + * Copyright 2012, 2013 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * <http://www.gnu.org/licenses/>. + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.vm.heap.analysis.client.swing.internal.stats; + +import java.awt.BorderLayout; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.util.ArrayList; +import java.util.List; + +import javax.swing.JPanel; + +import com.redhat.thermostat.common.locale.Translate; +import com.redhat.thermostat.vm.heap.analysis.client.core.HeapView.DumpDisabledReason; +import com.redhat.thermostat.vm.heap.analysis.client.locale.LocaleResources; +import com.redhat.thermostat.vm.heap.analysis.common.HeapDump; + +@SuppressWarnings("serial") +public class StatsPanel extends JPanel { + + private static final Translate<LocaleResources> translator = LocaleResources.createLocalizer(); + + private HeapChartPanel heapPanel; + private HeapDumperPopup popup; + + private List<OverlayComponent> overlays; + + private boolean canDump; + + public StatsPanel() { + + setName(StatsPanel.class.getName()); + + canDump = true; + + overlays = new ArrayList<>(); + + popup = new HeapDumperPopup(); + + setLayout(new BorderLayout()); + } + + public void setChartPanel(HeapChartPanel panel) { + if (heapPanel != null) { + remove(heapPanel); + } + heapPanel = panel; + for (OverlayComponent overlay : overlays) { + heapPanel.add(overlay); + } + heapPanel.addMouseListener(new MouseAdapter() { + public void mousePressed(MouseEvent e) { + checkPopup(e); + } + + public void mouseReleased(MouseEvent e) { + checkPopup(e); + } + + private void checkPopup(MouseEvent e) { + if (canDump && e.isPopupTrigger()) { + popup.show(e.getComponent(), e.getX(), e.getY()); + } + } + }); + + add(heapPanel, BorderLayout.CENTER); + } + + public void setMax(String capacity) { + //max.setText(capacity); + } + + public void setUsed(String used) { + //current.setText(used); + } + + public void addHeapDumperListener(ActionListener listener) { + popup.addDumperListener(listener); + } + + public void addDumpListListener(HeapDumpListener listener) { + listenerList.add(HeapDumpListener.class, listener); + } + + public void disableHeapDumperControl(DumpDisabledReason reason) { + canDump = false; + } + + public void enableHeapDumperControl() { + canDump = true; + } + + private void fireHeapDumpClicked(OverlayComponent component) { + Object[] listeners = listenerList.getListenerList(); + + HeapSelectionEvent event = new HeapSelectionEvent(component); + + for (int i = listeners.length - 2; i >= 0; i -= 2) { + if (listeners[i] == HeapDumpListener.class) { + ((HeapDumpListener) listeners[i + 1]).actionPerformed(event); + } + } + } + + public void addDump(HeapDump dump) { + OverlayComponent dumpOverlay = new OverlayComponent(dump); + if (!overlays.contains(dumpOverlay)){ + overlays.add(dumpOverlay); + dumpOverlay.addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent e) { + if (e.getClickCount() > 1) { + OverlayComponent sourceOverlay = (OverlayComponent) e.getSource(); + for (OverlayComponent overlay : overlays) { + overlay.setSelected(false); + } + sourceOverlay.setSelected(true); + fireHeapDumpClicked(sourceOverlay); + } + } + }); + if (heapPanel != null) { + heapPanel.add(dumpOverlay); + } + } + } + + public void clearDumpList() { + if (heapPanel != null) { + for (OverlayComponent overlay : overlays) { + heapPanel.remove(overlay); + } + } + overlays.clear(); + } + + public void selectOverlay(HeapDump heapDump) { + OverlayComponent dumpOverlay = new OverlayComponent(heapDump); + + for (OverlayComponent overlay : overlays) { + if (overlay.equals(dumpOverlay)) { + overlay.setSelected(true); + } else { + overlay.setSelected(false); + } + } + } + + public void updateHeapDumpList(List<HeapDump> heapDumps) { + for (HeapDump heapDump : heapDumps) { + addDump(heapDump); + } + } +} +
--- a/vm-heap-analysis/client-swing/src/test/java/com/redhat/thermostat/vm/heap/analysis/client/swing/internal/HeapSwingViewTest.java Mon May 13 12:39:57 2013 -0400 +++ b/vm-heap-analysis/client-swing/src/test/java/com/redhat/thermostat/vm/heap/analysis/client/swing/internal/HeapSwingViewTest.java Tue May 14 21:14:43 2013 +0200 @@ -36,23 +36,23 @@ package com.redhat.thermostat.vm.heap.analysis.client.swing.internal; +import static org.junit.Assert.*; import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; import java.awt.Container; -import java.util.Arrays; -import java.util.List; +import java.util.concurrent.CountDownLatch; import net.java.openjdk.cacio.ctc.junit.CacioFESTRunner; -import org.fest.swing.annotation.GUITest; import org.fest.swing.edt.FailOnThreadViolationRepaintManager; import org.fest.swing.edt.GuiActionRunner; import org.fest.swing.edt.GuiTask; import org.fest.swing.fixture.Containers; import org.fest.swing.fixture.FrameFixture; -import org.fest.swing.fixture.JButtonFixture; -import org.fest.swing.fixture.JListFixture; +import org.fest.swing.fixture.JLabelFixture; +import org.fest.swing.fixture.JPanelFixture; +import org.fest.swing.fixture.JPopupMenuFixture; import org.junit.After; import org.junit.Before; import org.junit.BeforeClass; @@ -62,8 +62,11 @@ import com.redhat.thermostat.common.ActionEvent; import com.redhat.thermostat.common.ActionListener; import com.redhat.thermostat.vm.heap.analysis.client.core.HeapView.HeapDumperAction; -import com.redhat.thermostat.vm.heap.analysis.client.swing.internal.HeapSwingView; +import com.redhat.thermostat.vm.heap.analysis.client.core.chart.OverviewChart; +import com.redhat.thermostat.vm.heap.analysis.client.swing.internal.stats.HeapChartPanel; +import com.redhat.thermostat.vm.heap.analysis.client.swing.internal.stats.OverlayComponent; import com.redhat.thermostat.vm.heap.analysis.common.HeapDump; +import com.redhat.thermostat.vm.heap.analysis.common.model.HeapInfo; @RunWith(CacioFESTRunner.class) public class HeapSwingViewTest { @@ -72,19 +75,30 @@ private FrameFixture frame; + private OverviewChart model; + + private long now; + @BeforeClass public static void setUpOnce() { FailOnThreadViolationRepaintManager.install(); } - @Before public void setUp() throws Exception { + + now = 1000; + GuiActionRunner.execute(new GuiTask() { @Override protected void executeInEDT() throws Throwable { view = new HeapSwingView(); + + model = new OverviewChart("fluff", "fluff", "fluff", "fluff", "fluff"); + model.addData(now, 1, 1000); + model.addData(now + 10000, 1, 1000); + view.setModel(model); } }); frame = Containers.showInFrame((Container) view.getUiComponent()); @@ -98,51 +112,113 @@ } @Test - @GUITest - public void testActivateHeapDump() { - @SuppressWarnings("unchecked") - ActionListener<HeapDumperAction> l = mock(ActionListener.class); - view.addDumperListener(l); - JButtonFixture heapDumpButton = frame.button("heapDumpButton"); - heapDumpButton.click(); - verify(l).actionPerformed(new ActionEvent<HeapDumperAction>(view, HeapDumperAction.DUMP_REQUESTED)); - } - - @Test - @GUITest - public void testNotifyHeapDumpComplete() { - final JButtonFixture heapDumpButton = frame.button("heapDumpButton"); + public void testAddHeapDump() { + final boolean [] result = new boolean[1]; + final int [] resultTimes = new int[1]; + view.addDumperListener(new ActionListener<HeapDumperAction>() { + @Override + public void actionPerformed(ActionEvent<HeapDumperAction> actionEvent) { + if (actionEvent.getActionId() == HeapDumperAction.DUMP_REQUESTED) { + result[0] = true; + resultTimes[0]++; + } + } + }); + + frame.show(); + GuiActionRunner.execute(new GuiTask() { - @Override protected void executeInEDT() throws Throwable { - heapDumpButton.component().setEnabled(false); + model.addData(now + 20000, 1, 1000); + } + }); + + assertFalse(result[0]); + + JPanelFixture panel = frame.panel(HeapChartPanel.class.getName()); + JPopupMenuFixture popup = panel.showPopupMenu(); + popup.click(); + + GuiActionRunner.execute(new GuiTask() { + @Override + protected void executeInEDT() throws Throwable { + model.addData(now + 30000, 1, 1000); } }); - - view.notifyHeapDumpComplete(); - frame.robot.waitForIdle(); - - heapDumpButton.requireEnabled(); + + assertTrue(result[0]); + assertEquals(1, resultTimes[0]); } - + @Test - @GUITest - public void testUpdateHeapDumpList() { - JListFixture heapDumpList = frame.list("heapDumpList"); - heapDumpList.requireItemCount(0); - - HeapDump heapDump = mock(HeapDump.class); - List<HeapDump> heapDumps = Arrays.asList(heapDump); + public void testActivateHeapDump() throws InterruptedException { + final boolean [] result = new boolean[1]; + final int [] resultTimes = new int[1]; + + final CountDownLatch latch = new CountDownLatch(1); + + HeapInfo info = mock(HeapInfo.class); + when(info.getTimeStamp()).thenReturn(now + 10000); + final HeapDump dump = new HeapDump(info, null); + + view.addDumperListener(new ActionListener<HeapDumperAction>() { + @Override + public void actionPerformed(ActionEvent<HeapDumperAction> actionEvent) { + if (actionEvent.getActionId() == HeapDumperAction.ANALYSE) { + result[0] = true; + resultTimes[0]++; + } else { + view.addHeapDump(dump); + latch.countDown(); + } + } + }); + + frame.show(); - view.updateHeapDumpList(heapDumps); - frame.robot.waitForIdle(); - heapDumpList.requireItemCount(1); - - view.updateHeapDumpList(heapDumps); - frame.robot.waitForIdle(); - heapDumpList.requireItemCount(1); - + // really same as previous test, this time we get the overlay component + // though + + GuiActionRunner.execute(new GuiTask() { + @Override + protected void executeInEDT() throws Throwable { + model.addData(now + 20000, 1, 1000); + } + }); + + assertFalse(result[0]); + + final JPanelFixture panel = frame.panel(HeapChartPanel.class.getName()); + JPopupMenuFixture popup = panel.showPopupMenu(); + popup.click(); + + GuiActionRunner.execute(new GuiTask() { + @Override + protected void executeInEDT() throws Throwable { + // needs this because OverlayComponent and HeapChartPanel are + // bound by a special layout manager + panel.component().doLayout(); + } + }); + + latch.await(); + + JLabelFixture overlay = panel.label(OverlayComponent.class.getName()); + overlay.doubleClick(); + + OverlayComponent overlayComponent = (OverlayComponent) overlay.component(); + assertEquals(dump, overlayComponent.getHeapDump()); + + GuiActionRunner.execute(new GuiTask() { + @Override + protected void executeInEDT() throws Throwable { + panel.component().doLayout(); + } + }); + + assertTrue(result[0]); + assertEquals(1, resultTimes[0]); } }