Mercurial > hg > release > thermostat-0.11
changeset 1155:c70ab9ec00f5
Preliminary add export function to HeapDump
review-thread: http://icedtea.classpath.org/pipermail/thermostat/2013-June/007216.html
reviewed-by: vanaltj
line wrap: on
line diff
--- a/vm-heap-analysis/client-core/src/main/java/com/redhat/thermostat/vm/heap/analysis/client/core/HeapView.java Tue Jul 09 15:09:58 2013 +0200 +++ b/vm-heap-analysis/client-core/src/main/java/com/redhat/thermostat/vm/heap/analysis/client/core/HeapView.java Tue Jul 09 15:10:03 2013 +0200 @@ -44,6 +44,7 @@ import com.redhat.thermostat.common.ActionNotifier; import com.redhat.thermostat.shared.locale.LocalizedString; import com.redhat.thermostat.vm.heap.analysis.client.core.chart.OverviewChart; +import com.redhat.thermostat.vm.heap.analysis.common.DumpFile; import com.redhat.thermostat.vm.heap.analysis.common.HeapDump; public abstract class HeapView extends BasicView implements UIComponent { @@ -51,6 +52,8 @@ public enum HeapDumperAction { DUMP_REQUESTED, ANALYSE, + REQUEST_EXPORT, + SAVE_HEAP_DUMP, REQUEST_ABORTED, REQUEST_DISPLAY_DUMP_LIST, } @@ -92,5 +95,7 @@ public abstract void displayWarning(LocalizedString string); public abstract void openDumpListView(HeapDumpListView childView); + + public abstract void openExportDialog(DumpFile heapdDump); }
--- a/vm-heap-analysis/client-core/src/main/java/com/redhat/thermostat/vm/heap/analysis/client/core/internal/HeapDumpController.java Tue Jul 09 15:09:58 2013 +0200 +++ b/vm-heap-analysis/client-core/src/main/java/com/redhat/thermostat/vm/heap/analysis/client/core/internal/HeapDumpController.java Tue Jul 09 15:10:03 2013 +0200 @@ -36,10 +36,18 @@ package com.redhat.thermostat.vm.heap.analysis.client.core.internal; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Files; +import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Collection; +import java.util.Date; import java.util.List; import java.util.concurrent.TimeUnit; +import java.util.logging.Level; +import java.util.logging.Logger; import com.redhat.thermostat.client.core.controllers.InformationServiceController; import com.redhat.thermostat.client.core.views.BasicView.Action; @@ -67,6 +75,7 @@ import com.redhat.thermostat.vm.heap.analysis.client.core.ObjectRootsViewProvider; 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.common.DumpFile; import com.redhat.thermostat.vm.heap.analysis.common.HeapDAO; import com.redhat.thermostat.vm.heap.analysis.common.HeapDump; import com.redhat.thermostat.vm.heap.analysis.common.model.HeapInfo; @@ -201,6 +210,26 @@ dump = (HeapDump) actionEvent.getPayload(); analyseDump(dump); break; + + case REQUEST_EXPORT: { + dump = (HeapDump) actionEvent.getPayload(); + DumpFile localHeapDump = new DumpFile(); + SimpleDateFormat format = new SimpleDateFormat("yyyy_MM_dd_HH_mm_ss_SSS"); + Date date = new Date(dump.getTimestamp()); + String timeStamp = format.format(date); + String id = "heapdump-" + ref.getName() + "-" + timeStamp + "." + dump.getType(); + localHeapDump.setFile(new File(id)); + localHeapDump.setDump(dump); + view.openExportDialog(localHeapDump); + } break; + + case SAVE_HEAP_DUMP: { + // FIXME: we really need some indicator that something is + // going on here, same for dumping requests + DumpFile localHeapDump = (DumpFile) actionEvent.getPayload(); + saveHeapDump(localHeapDump); + } break; + default: break; } @@ -213,6 +242,31 @@ } } + private void saveHeapDump(final DumpFile localHeapDump) { + appService.getApplicationExecutor().execute(new Runnable() { + @Override + public void run() { + HeapDump dump = localHeapDump.getDump(); + File file = localHeapDump.getFile(); + if (dump == null || file == null) { + // this is here mainly for the tests, since we don't + // expect files or dumps to be null + return; + } + + try (InputStream in = heapDAO.getHeapDumpData(dump.getInfo())) { + Files.copy(in, file.toPath()); + + } catch (IOException e) { + LocalizedString message = translator.localize(LocaleResources.ERROR_EXPORTING_FILE); + view.displayWarning(message); + Logger.getLogger(HeapDumpController.class.getSimpleName()). + log(Level.WARNING, message.getContents(), e); + } + } + }); + } + private void openDumpList() { appService.getApplicationExecutor().execute(new Runnable() {
--- a/vm-heap-analysis/client-core/src/main/java/com/redhat/thermostat/vm/heap/analysis/client/locale/LocaleResources.java Tue Jul 09 15:09:58 2013 +0200 +++ b/vm-heap-analysis/client-core/src/main/java/com/redhat/thermostat/vm/heap/analysis/client/locale/LocaleResources.java Tue Jul 09 15:10:03 2013 +0200 @@ -66,6 +66,9 @@ HEAP_DUMP_OBJECT_BROWSE_REFERENCES, HEAP_DUMP_OBJECT_FIND_ROOT, + EXPORT_HEAP_DUMP_TO_FILE, + ERROR_EXPORTING_FILE, + OBJECT_ROOTS_VIEW_TITLE, TRIGGER_HEAP_DUMP,
--- a/vm-heap-analysis/client-core/src/main/resources/com/redhat/thermostat/vm/heap/analysis/client/locale/strings.properties Tue Jul 09 15:09:58 2013 +0200 +++ b/vm-heap-analysis/client-core/src/main/resources/com/redhat/thermostat/vm/heap/analysis/client/locale/strings.properties Tue Jul 09 15:10:03 2013 +0200 @@ -25,6 +25,9 @@ HEAP_DUMP_OBJECT_BROWSE_REFERENCES = References HEAP_DUMP_OBJECT_FIND_ROOT = Find Root +EXPORT_HEAP_DUMP_TO_FILE = Export... +ERROR_EXPORTING_FILE = Error exporting Heap dump to file... + OBJECT_ROOTS_VIEW_TITLE = Object Roots TRIGGER_HEAP_DUMP = Heap Dump
--- a/vm-heap-analysis/client-core/src/test/java/com/redhat/thermostat/vm/heap/analysis/client/core/internal/HeapDumpControllerTest.java Tue Jul 09 15:09:58 2013 +0200 +++ b/vm-heap-analysis/client-core/src/test/java/com/redhat/thermostat/vm/heap/analysis/client/core/internal/HeapDumpControllerTest.java Tue Jul 09 15:10:03 2013 +0200 @@ -37,6 +37,8 @@ package com.redhat.thermostat.vm.heap.analysis.client.core.internal; import static org.junit.Assert.assertTrue; +import static org.junit.Assert.assertEquals; + import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyLong; import static org.mockito.Matchers.isA; @@ -48,6 +50,8 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import java.io.File; +import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -63,6 +67,7 @@ import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; +import com.lowagie.text.pdf.codec.Base64.InputStream; import com.redhat.thermostat.common.ActionEvent; import com.redhat.thermostat.common.ActionListener; import com.redhat.thermostat.common.ApplicationCache; @@ -88,6 +93,7 @@ import com.redhat.thermostat.vm.heap.analysis.client.core.ObjectDetailsViewProvider; import com.redhat.thermostat.vm.heap.analysis.client.core.ObjectRootsView; import com.redhat.thermostat.vm.heap.analysis.client.core.ObjectRootsViewProvider; +import com.redhat.thermostat.vm.heap.analysis.common.DumpFile; import com.redhat.thermostat.vm.heap.analysis.common.HeapDAO; import com.redhat.thermostat.vm.heap.analysis.common.HeapDump; import com.redhat.thermostat.vm.heap.analysis.common.model.HeapInfo; @@ -350,7 +356,55 @@ verify(heapDumper).dump(); verify(view).notifyHeapDumpComplete(); } + + @Test + public void testRequestExport() throws CommandException, InterruptedException { + setUpListeners(); + HeapDump dump = mock(HeapDump.class); + when(dump.getTimestamp()).thenReturn(1l); + when(dump.getType()).thenReturn("TEST"); + + ArgumentCaptor<DumpFile> localDumpCaptor = ArgumentCaptor.forClass(DumpFile.class); + + ActionEvent<HeapDumperAction> event = new ActionEvent<>(view, HeapDumperAction.REQUEST_EXPORT); + event.setPayload(dump); + + heapDumperListener.actionPerformed(event); + + verify(view).openExportDialog(localDumpCaptor.capture()); + DumpFile localDump = localDumpCaptor.getValue(); + + assertTrue(localDump.getFile().getName().endsWith(".TEST")); + assertEquals(dump, localDump.getDump()); + } + + @Test + public void testExportLocation() throws CommandException, InterruptedException { + final CountDownLatch latch = new CountDownLatch(1); + mockExecutorService(latch); + + setUpListeners(); + + HeapInfo info = mock(HeapInfo.class); + HeapDump dump = mock(HeapDump.class); + when(dump.getInfo()).thenReturn(info); + DumpFile localDump = mock(DumpFile.class); + when(localDump.getDump()).thenReturn(dump); + + InputStream stream = mock(InputStream.class); + when(heapDao.getHeapDumpData(any(HeapInfo.class))).thenReturn(stream); + + ActionEvent<HeapDumperAction> event = new ActionEvent<>(view, HeapDumperAction.SAVE_HEAP_DUMP); + event.setPayload(localDump); + + heapDumperListener.actionPerformed(event); + latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS); + + verify(localDump).getFile(); + verify(localDump).getDump(); + } + @Test public void testRequestHeapDumpFails() throws CommandException, InterruptedException { final CountDownLatch latch = new CountDownLatch(1);
--- a/vm-heap-analysis/client-swing/src/main/java/com/redhat/thermostat/vm/heap/analysis/client/swing/internal/HeapSwingView.java Tue Jul 09 15:09:58 2013 +0200 +++ b/vm-heap-analysis/client-swing/src/main/java/com/redhat/thermostat/vm/heap/analysis/client/swing/internal/HeapSwingView.java Tue Jul 09 15:10:03 2013 +0200 @@ -41,11 +41,13 @@ import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.KeyEvent; +import java.io.File; import java.util.List; import javax.swing.AbstractAction; import javax.swing.BoxLayout; import javax.swing.JComponent; +import javax.swing.JFileChooser; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.KeyStroke; @@ -69,10 +71,13 @@ 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.ExportDumpEvent; +import com.redhat.thermostat.vm.heap.analysis.client.swing.internal.stats.ExportDumpListener; import com.redhat.thermostat.vm.heap.analysis.client.swing.internal.stats.HeapChartPanel; 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 com.redhat.thermostat.vm.heap.analysis.client.swing.internal.stats.StatsPanel; +import com.redhat.thermostat.vm.heap.analysis.common.DumpFile; import com.redhat.thermostat.vm.heap.analysis.common.HeapDump; public class HeapSwingView extends HeapView implements SwingComponent { @@ -92,6 +97,8 @@ private JPanel stack; + private JFileChooser fileChooser; + public HeapSwingView() { stats = new StatsPanel(); stats.addHeapDumperListener(new ActionListener() { @@ -109,6 +116,14 @@ } }); + stats.addExportDumpListener(new ExportDumpListener() { + @Override + public void actionPerformed(ExportDumpEvent e) { + HeapDump dump = e.getSource(); + heapDumperNotifier.fireAction(HeapDumperAction.REQUEST_EXPORT, dump); + } + }); + visiblePane = new JPanel(); visiblePane.setLayout(new BoxLayout(visiblePane, BoxLayout.X_AXIS)); visiblePane.setName(HeapSwingView.class.getName()); @@ -173,6 +188,9 @@ // at the beginning, only the overview is visible visiblePane.add(overview); + + fileChooser = new JFileChooser(); + fileChooser.setName("EXPORT_HEAP_DUMP_FILE_CHOOSER"); } private class ViewVisibleListener extends ComponentVisibleListener { @@ -376,4 +394,20 @@ }); } } + + @Override + public void openExportDialog(final DumpFile heapDump) { + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + fileChooser.setSelectedFile(heapDump.getFile()); + int result = fileChooser.showSaveDialog(HeapSwingView.this.getUiComponent()); + if (result == JFileChooser.APPROVE_OPTION) { + File file = fileChooser.getSelectedFile(); + heapDump.setFile(file); + heapDumperNotifier.fireAction(HeapDumperAction.SAVE_HEAP_DUMP, heapDump); + } + } + }); + } }
--- /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/ExportDumpEvent.java Tue Jul 09 15:10:03 2013 +0200 @@ -0,0 +1,54 @@ +/* + * 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; + +import com.redhat.thermostat.vm.heap.analysis.common.HeapDump; + +@SuppressWarnings("serial") +public class ExportDumpEvent extends EventObject { + + public ExportDumpEvent(HeapDump source) { + super(source); + } + + @Override + public HeapDump getSource() { + return (HeapDump) 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/ExportDumpListener.java Tue Jul 09 15:10:03 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 ExportDumpListener extends EventListener { + + public void actionPerformed(ExportDumpEvent 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/ExportDumpPopup.java Tue Jul 09 15:10:03 2013 +0200 @@ -0,0 +1,62 @@ +/* + * 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.shared.locale.Translate; +import com.redhat.thermostat.vm.heap.analysis.client.locale.LocaleResources; + +@SuppressWarnings("serial") +class ExportDumpPopup extends ThermostatPopupMenu { + + private static final Translate<LocaleResources> translator = LocaleResources.createLocalizer(); + private JMenuItem export; + + public ExportDumpPopup() { + setName(getClass().getName()); + export = new JMenuItem(translator.localize(LocaleResources.EXPORT_HEAP_DUMP_TO_FILE).getContents()); + add(export); + } + + public void addExportListener(ActionListener listener) { + export.addActionListener(listener); + } +}
--- a/vm-heap-analysis/client-swing/src/main/java/com/redhat/thermostat/vm/heap/analysis/client/swing/internal/stats/HeapSelectionEvent.java Tue Jul 09 15:09:58 2013 +0200 +++ b/vm-heap-analysis/client-swing/src/main/java/com/redhat/thermostat/vm/heap/analysis/client/swing/internal/stats/HeapSelectionEvent.java Tue Jul 09 15:10:03 2013 +0200 @@ -38,6 +38,7 @@ import java.util.EventObject; +@SuppressWarnings("serial") public class HeapSelectionEvent extends EventObject { public HeapSelectionEvent(OverlayComponent source) {
--- a/vm-heap-analysis/client-swing/src/main/java/com/redhat/thermostat/vm/heap/analysis/client/swing/internal/stats/OverlayComponent.java Tue Jul 09 15:09:58 2013 +0200 +++ b/vm-heap-analysis/client-swing/src/main/java/com/redhat/thermostat/vm/heap/analysis/client/swing/internal/stats/OverlayComponent.java Tue Jul 09 15:10:03 2013 +0200 @@ -36,6 +36,18 @@ package com.redhat.thermostat.vm.heap.analysis.client.swing.internal.stats; +import java.awt.Component; +import java.awt.Dimension; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.Image; +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 com.redhat.thermostat.client.swing.GraphicsUtils; import com.redhat.thermostat.client.swing.components.CompositeIcon; import com.redhat.thermostat.client.swing.components.Icon; @@ -46,21 +58,6 @@ 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; - @SuppressWarnings("serial") public class OverlayComponent extends ShadowLabel { @@ -68,7 +65,7 @@ private boolean selected; private String _text; - + public class NiceIcon extends Icon { private Icon src; @@ -115,27 +112,21 @@ super(LocalizedString.EMPTY_STRING); + this.dump = dump; + this._text = new Date(dump.getTimestamp()).toString(); + setOpaque(false); - setForeground(Palette.ROYAL_BLUE.getColor()); - setName(OverlayComponent.class.getName()); - + setFont(TimelineUtils.FONT); + setOpaque(false); + setToolTipText(_text); + 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) {
--- a/vm-heap-analysis/client-swing/src/main/java/com/redhat/thermostat/vm/heap/analysis/client/swing/internal/stats/StatsPanel.java Tue Jul 09 15:09:58 2013 +0200 +++ b/vm-heap-analysis/client-swing/src/main/java/com/redhat/thermostat/vm/heap/analysis/client/swing/internal/stats/StatsPanel.java Tue Jul 09 15:10:03 2013 +0200 @@ -37,6 +37,7 @@ 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; @@ -53,11 +54,14 @@ private HeapChartPanel heapPanel; private HeapDumperPopup popup; - + + private ExportDumpPopup export; private List<OverlayComponent> overlays; private boolean canDump; + private HeapDump selectedDump; + public StatsPanel() { setName(StatsPanel.class.getName()); @@ -67,7 +71,17 @@ overlays = new ArrayList<>(); popup = new HeapDumperPopup(); - + + export = new ExportDumpPopup(); + export.addExportListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + if (selectedDump != null) { + fireExportDumpEvent(selectedDump); + } + } + }); + setLayout(new BorderLayout()); } @@ -75,10 +89,12 @@ 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); @@ -113,6 +129,10 @@ public void addDumpListListener(HeapDumpListener listener) { listenerList.add(HeapDumpListener.class, listener); } + + public void addExportDumpListener(ExportDumpListener listener) { + listenerList.add(ExportDumpListener.class, listener); + } public void disableHeapDumperControl(DumpDisabledReason reason) { canDump = false; @@ -122,6 +142,18 @@ canDump = true; } + private void fireExportDumpEvent(HeapDump source) { + Object[] listeners = listenerList.getListenerList(); + + ExportDumpEvent event = new ExportDumpEvent(source); + + for (int i = listeners.length - 2; i >= 0; i -= 2) { + if (listeners[i] == ExportDumpListener.class) { + ((ExportDumpListener) listeners[i + 1]).actionPerformed(event); + } + } + } + private void fireHeapDumpClicked(OverlayComponent component) { Object[] listeners = listenerList.getListenerList(); @@ -134,9 +166,10 @@ } } - public void addDump(HeapDump dump) { + public void addDump(final HeapDump dump) { OverlayComponent dumpOverlay = new OverlayComponent(dump); - if (!overlays.contains(dumpOverlay)){ + if (!overlays.contains(dumpOverlay)) { + dumpOverlay.setName(String.valueOf(dump.getTimestamp())); overlays.add(dumpOverlay); dumpOverlay.addMouseListener(new MouseAdapter() { @Override @@ -150,7 +183,23 @@ fireHeapDumpClicked(sourceOverlay); } } + + public void mousePressed(MouseEvent e) { + checkPopup(e); + } + + public void mouseReleased(MouseEvent e) { + checkPopup(e); + } + + private void checkPopup(MouseEvent e) { + if (e.isPopupTrigger()) { + selectedDump = dump; + export.show(e.getComponent(), e.getX(), e.getY()); + } + } }); + if (heapPanel != null) { heapPanel.add(dumpOverlay); } @@ -184,4 +233,3 @@ } } } -
--- a/vm-heap-analysis/client-swing/src/test/java/com/redhat/thermostat/vm/heap/analysis/client/swing/internal/HeapSwingViewTest.java Tue Jul 09 15:09:58 2013 +0200 +++ b/vm-heap-analysis/client-swing/src/test/java/com/redhat/thermostat/vm/heap/analysis/client/swing/internal/HeapSwingViewTest.java Tue Jul 09 15:10:03 2013 +0200 @@ -42,17 +42,21 @@ import java.awt.Container; import java.awt.event.KeyEvent; +import java.io.File; import java.util.concurrent.CountDownLatch; import net.java.openjdk.cacio.ctc.junit.CacioFESTRunner; import org.fest.swing.annotation.GUITest; +import org.fest.swing.core.Robot; import org.fest.swing.edt.FailOnThreadViolationRepaintManager; import org.fest.swing.edt.GuiActionRunner; import org.fest.swing.edt.GuiTask; +import org.fest.swing.finder.JFileChooserFinder; import org.fest.swing.fixture.Containers; import org.fest.swing.fixture.FrameFixture; import org.fest.swing.fixture.JButtonFixture; +import org.fest.swing.fixture.JFileChooserFixture; import org.fest.swing.fixture.JLabelFixture; import org.fest.swing.fixture.JPanelFixture; import org.fest.swing.fixture.JPopupMenuFixture; @@ -63,7 +67,6 @@ import org.junit.Test; import org.junit.runner.RunWith; -import com.redhat.thermostat.client.swing.components.HeaderPanel; import com.redhat.thermostat.client.swing.components.OverlayPanel; import com.redhat.thermostat.common.ActionEvent; import com.redhat.thermostat.common.ActionListener; @@ -72,6 +75,7 @@ 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.DumpFile; import com.redhat.thermostat.vm.heap.analysis.common.HeapDump; import com.redhat.thermostat.vm.heap.analysis.common.model.HeapInfo; @@ -213,7 +217,7 @@ latch.await(); - JLabelFixture overlay = panel.label(OverlayComponent.class.getName()); + JLabelFixture overlay = panel.label(String.valueOf(dump.getTimestamp())); overlay.doubleClick(); OverlayComponent overlayComponent = (OverlayComponent) overlay.component(); @@ -232,6 +236,93 @@ @GUITest @Test + public void testExportStep1() { + + HeapInfo info = mock(HeapInfo.class); + when(info.getTimeStamp()).thenReturn(now); + HeapDump dump = new HeapDump(info, null); + + view.addHeapDump(dump); + + HeapInfo info2 = mock(HeapInfo.class); + when(info2.getTimeStamp()).thenReturn(now + 1); + HeapDump dump2 = new HeapDump(info2, null); + + view.addHeapDump(dump2); + + final HeapDump [] resultDump = new HeapDump[1]; + + view.addDumperListener(new ActionListener<HeapView.HeapDumperAction>() { + @Override + public void actionPerformed(ActionEvent<HeapDumperAction> actionEvent) { + if (actionEvent.getActionId() == HeapDumperAction.REQUEST_EXPORT) { + resultDump[0] = (HeapDump) actionEvent.getPayload(); + } + } + }); + + frame.show(); + + JLabelFixture overlayFixture = frame.label(String.valueOf(dump.getTimestamp())); + overlayFixture.requireVisible(); + + JPopupMenuFixture popupFixture = overlayFixture.showPopupMenu(); + popupFixture.click(); + + assertNotNull(resultDump[0]); + assertEquals(dump, resultDump[0]); + + overlayFixture = frame.label(String.valueOf(dump2.getTimestamp())); + overlayFixture.requireVisible(); + + popupFixture = overlayFixture.showPopupMenu(); + popupFixture.click(); + + assertNotNull(resultDump[0]); + assertEquals(dump2, resultDump[0]); + } + + @GUITest + @Test + public void testExportStep2() { + + HeapInfo info = mock(HeapInfo.class); + HeapDump dump = mock(HeapDump.class); + when(dump.getInfo()).thenReturn(info); + + DumpFile localDump = new DumpFile(); + localDump.setDump(dump); + localDump.setFile(new File("TEST-1-.hprof")); + + final DumpFile [] resultDump = new DumpFile[1]; + + view.addDumperListener(new ActionListener<HeapView.HeapDumperAction>() { + @Override + public void actionPerformed(ActionEvent<HeapDumperAction> actionEvent) { + if (actionEvent.getActionId() == HeapDumperAction.SAVE_HEAP_DUMP) { + resultDump[0] = (DumpFile) actionEvent.getPayload(); + } + } + }); + + frame.show(); + + view.openExportDialog(localDump); + + JFileChooserFixture fileChooser = + JFileChooserFinder.findFileChooser("EXPORT_HEAP_DUMP_FILE_CHOOSER").using(frame.robot); + File destination = new File("TEST-2-.hprof"); + fileChooser.selectFile(destination); + fileChooser.approve(); + + assertNotNull(resultDump[0]); + assertEquals(destination.getName(), resultDump[0].getFile().getName()); + assertEquals(dump, resultDump[0].getDump()); + + } + + @GUITest + @Test public void testHeapDumperActionFired() { final boolean [] result = new boolean[1];
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vm-heap-analysis/client-swing/src/test/java/com/redhat/thermostat/vm/heap/analysis/client/swing/internal/stats/OverlayComponentTest.java Tue Jul 09 15:10:03 2013 +0200 @@ -0,0 +1,135 @@ +/* + * 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 static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.awt.Dimension; +import java.util.Date; +import java.util.Locale; + +import javax.swing.JFrame; +import javax.swing.JPanel; + +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.FrameFixture; +import org.fest.swing.fixture.JLabelFixture; +import org.junit.After; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; + +import com.redhat.thermostat.vm.heap.analysis.common.HeapDump; + +@RunWith(CacioFESTRunner.class) +public class OverlayComponentTest { + + private OverlayComponent overlay; + + private JFrame frame; + private FrameFixture frameFixture; + + private static Locale defaultLocale; + + @BeforeClass + public static void setUpOnce() { + defaultLocale = Locale.getDefault(); + Locale.setDefault(Locale.US); + FailOnThreadViolationRepaintManager.install(); + } + + @AfterClass + public static void tearDownUpOnce() { + Locale.setDefault(defaultLocale); + } + + @Before + public void setUp() throws Exception { + + GuiActionRunner.execute(new GuiTask() { + @Override + protected void executeInEDT() throws Throwable { + + HeapDump dump = mock(HeapDump.class); + when(dump.getTimestamp()).thenReturn(0l); + + overlay = new OverlayComponent(dump); + + frame = new JFrame(); + frame.setMinimumSize(new Dimension(500, 500)); + + JPanel contentPane = new JPanel(); + contentPane.add(overlay); + + frame.add(contentPane); + } + }); + + frameFixture = new FrameFixture(frame); + } + + @After + public void tearDown() { + frameFixture.cleanUp(); + } + + @GUITest + @Test + public void testHover() { + frameFixture.show(); + + JLabelFixture overlayFixture = frameFixture.label(OverlayComponent.class.getName()); + overlayFixture.requireVisible(); + + String text = overlayFixture.text(); + assertEquals("", text); + + frameFixture.robot.moveMouse(overlayFixture.target); + + text = overlayFixture.text(); + assertEquals(new Date(1l).toString(), text); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vm-heap-analysis/common/src/main/java/com/redhat/thermostat/vm/heap/analysis/common/DumpFile.java Tue Jul 09 15:10:03 2013 +0200 @@ -0,0 +1,61 @@ +/* + * 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.common; + +import java.io.File; + +public class DumpFile { + + private HeapDump dump; + private File location; + + public File getFile() { + return location; + } + + public HeapDump getDump() { + return dump; + } + + public void setDump(HeapDump dump) { + this.dump = dump; + } + + public void setFile(File location) { + this.location = location; + } +}
--- a/vm-heap-analysis/common/src/main/java/com/redhat/thermostat/vm/heap/analysis/common/HeapDump.java Tue Jul 09 15:09:58 2013 +0200 +++ b/vm-heap-analysis/common/src/main/java/com/redhat/thermostat/vm/heap/analysis/common/HeapDump.java Tue Jul 09 15:10:03 2013 +0200 @@ -228,5 +228,9 @@ public int hashCode() { return heapInfo.hashCode(); } + + public String getType() { + return "hprof"; + } }