changeset 380:adbe46431675

Preliminary heap dumper view reviewed-by: rkennke review-thread: http://icedtea.classpath.org/pipermail/thermostat/2012-June/001857.html PR 944 PR 1033
author Mario Torre <neugens.limasoftware@gmail.com>
date Mon, 18 Jun 2012 17:04:47 +0200
parents 3fad23440030
children e78dfab194a5
files client/core/src/main/java/com/redhat/thermostat/client/internal/MainWindowControllerImpl.java client/core/src/main/java/com/redhat/thermostat/client/internal/RegistryFactory.java client/core/src/main/java/com/redhat/thermostat/client/internal/VMInformationRegistry.java client/core/src/test/java/com/redhat/thermostat/client/internal/MainWindowControllerImplTest.java client/heapdumper/pom.xml client/heapdumper/src/main/java/com/redhat/thermostat/client/heap/Activator.java client/heapdumper/src/main/java/com/redhat/thermostat/client/heap/HeapDump.java client/heapdumper/src/main/java/com/redhat/thermostat/client/heap/HeapDumpAction.java client/heapdumper/src/main/java/com/redhat/thermostat/client/heap/HeapDumpController.java client/heapdumper/src/main/java/com/redhat/thermostat/client/heap/HeapDumperCommand.java client/heapdumper/src/main/java/com/redhat/thermostat/client/heap/HeapDumperService.java client/heapdumper/src/main/java/com/redhat/thermostat/client/heap/HeapView.java client/heapdumper/src/main/java/com/redhat/thermostat/client/heap/chart/Chart.java client/heapdumper/src/main/java/com/redhat/thermostat/client/heap/chart/OverviewChart.java client/heapdumper/src/main/java/com/redhat/thermostat/client/heap/swing/ChartPanel.java client/heapdumper/src/main/java/com/redhat/thermostat/client/heap/swing/HeaderPanel.java client/heapdumper/src/main/java/com/redhat/thermostat/client/heap/swing/HeapPanel.java client/heapdumper/src/main/java/com/redhat/thermostat/client/heap/swing/HeapSwingView.java client/heapdumper/src/main/java/com/redhat/thermostat/client/heap/swing/StatsPanel.java client/heapdumper/src/test/java/com/redhat/thermostat/client/heap/HeapDumpActionTest.java common/core/src/main/java/com/redhat/thermostat/common/BasicView.java
diffstat 21 files changed, 1397 insertions(+), 32 deletions(-) [+]
line wrap: on
line diff
--- a/client/core/src/main/java/com/redhat/thermostat/client/internal/MainWindowControllerImpl.java	Mon Jun 18 16:17:37 2012 +0200
+++ b/client/core/src/main/java/com/redhat/thermostat/client/internal/MainWindowControllerImpl.java	Mon Jun 18 17:04:47 2012 +0200
@@ -167,7 +167,18 @@
             }
         };
     };
-    
+
+    private VMInformationRegistry vmInfoRegistry;
+    private ActionListener<ThermostatExtensionRegistry.Action> vmInfoRegistryListener =
+            new ActionListener<ThermostatExtensionRegistry.Action> ()
+    {
+        public void actionPerformed(com.redhat.thermostat.common.ActionEvent<ThermostatExtensionRegistry.Action>
+                                    actionEvent)
+        {
+            updateView();
+        };
+    };
+            
     private boolean showHistory;
 
     private VmInformationControllerProvider vmInfoControllerProvider;
@@ -178,6 +189,7 @@
             filterRegistry = registryFactory.createVMTreeFilterRegistry();
             decoratorRegistry = registryFactory.createVMTreeDecoratorRegistry();
             menuRegistry = registryFactory.createMenuRegistry();
+            vmInfoRegistry = registryFactory.createVMInformationRegistry();
             
         } catch (InvalidSyntaxException e) {
             throw new RuntimeException(e);
@@ -214,6 +226,9 @@
         
         decoratorRegistry.addActionListener(decoratorListener);
         decoratorRegistry.start();
+        
+        vmInfoRegistry.addActionListener(vmInfoRegistryListener);
+        vmInfoRegistry.start();
     }
 
     private class HostsVMsLoaderImpl implements HostsVMsLoader {
--- a/client/core/src/main/java/com/redhat/thermostat/client/internal/RegistryFactory.java	Mon Jun 18 16:17:37 2012 +0200
+++ b/client/core/src/main/java/com/redhat/thermostat/client/internal/RegistryFactory.java	Mon Jun 18 17:04:47 2012 +0200
@@ -57,4 +57,8 @@
     MenuRegistry createMenuRegistry() throws InvalidSyntaxException {
         return new MenuRegistry(context);
     }
+    
+    VMInformationRegistry createVMInformationRegistry() throws InvalidSyntaxException {
+        return new VMInformationRegistry(context);
+    }
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/client/core/src/main/java/com/redhat/thermostat/client/internal/VMInformationRegistry.java	Mon Jun 18 17:04:47 2012 +0200
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2012 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.internal;
+
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.framework.InvalidSyntaxException;
+
+import com.redhat.thermostat.client.osgi.service.VmInformationService;
+
+class VMInformationRegistry extends ThermostatExtensionRegistry<VmInformationService> {
+
+    private static final String FILTER = "(&(" + Constants.OBJECTCLASS + "=" + VmInformationService.class.getName() + "))";
+    
+    public VMInformationRegistry(BundleContext context) throws InvalidSyntaxException {
+        super(context, FILTER, VmInformationService.class);
+    }
+}
--- a/client/core/src/test/java/com/redhat/thermostat/client/internal/MainWindowControllerImplTest.java	Mon Jun 18 16:17:37 2012 +0200
+++ b/client/core/src/test/java/com/redhat/thermostat/client/internal/MainWindowControllerImplTest.java	Mon Jun 18 17:04:47 2012 +0200
@@ -118,6 +118,7 @@
 
     private VMTreeFilterRegistry filters;
     private VMTreeDecoratorRegistry decorators;
+    private VMInformationRegistry vmInfoRegistry;
     private MenuRegistry menues;
     
     private ActionListener<ThermostatExtensionRegistry.Action> filtersListener;
@@ -155,11 +156,13 @@
         RegistryFactory registryFactory = mock(RegistryFactory.class);
         filters = mock(VMTreeFilterRegistry.class);
         decorators = mock(VMTreeDecoratorRegistry.class);
+        vmInfoRegistry = mock(VMInformationRegistry.class);
         menues = mock(MenuRegistry.class);
 
         when(registryFactory.createMenuRegistry()).thenReturn(menues);
         when(registryFactory.createVMTreeDecoratorRegistry()).thenReturn(decorators);
         when(registryFactory.createVMTreeFilterRegistry()).thenReturn(filters);
+        when(registryFactory.createVMInformationRegistry()).thenReturn(vmInfoRegistry);
         
         ArgumentCaptor<ActionListener> grabFiltersListener = ArgumentCaptor.forClass(ActionListener.class);
         doNothing().when(filters).addActionListener(grabFiltersListener.capture());
@@ -167,6 +170,9 @@
         ArgumentCaptor<ActionListener> grabDecoratorsListener = ArgumentCaptor.forClass(ActionListener.class);
         doNothing().when(decorators).addActionListener(grabDecoratorsListener.capture());
         
+        ArgumentCaptor<ActionListener> grabInfoRegistry = ArgumentCaptor.forClass(ActionListener.class);
+        doNothing().when(vmInfoRegistry).addActionListener(grabInfoRegistry.capture());
+        
         // TODO remove this asap. the main window has a hard dependency on summary controller/view
         ViewFactory viewFactory = mock(ViewFactory.class);
         SummaryView summaryView = mock(SummaryView.class);
--- a/client/heapdumper/pom.xml	Mon Jun 18 16:17:37 2012 +0200
+++ b/client/heapdumper/pom.xml	Mon Jun 18 17:04:47 2012 +0200
@@ -57,7 +57,11 @@
           <instructions>
             <Bundle-Vendor>Red Hat, Inc.</Bundle-Vendor>
             <Bundle-Activator>com.redhat.thermostat.client.heap.Activator</Bundle-Activator>
-            <Private-Package>com.redhat.thermostat.client.heap</Private-Package>
+            <Private-Package>
+               com.redhat.thermostat.client.heap,
+               com.redhat.thermostat.client.heap.swing,
+               com.redhat.thermostat.client.heap.chart
+            </Private-Package>
           </instructions>
         </configuration>
       </plugin>
@@ -92,5 +96,11 @@
       <version>${project.version}</version>
       <type>bundle</type>
     </dependency>
+    
+    <dependency>
+      <groupId>org.jfree</groupId>
+      <artifactId>jfreechart</artifactId>
+    </dependency>
+    
   </dependencies>
 </project>
--- a/client/heapdumper/src/main/java/com/redhat/thermostat/client/heap/Activator.java	Mon Jun 18 16:17:37 2012 +0200
+++ b/client/heapdumper/src/main/java/com/redhat/thermostat/client/heap/Activator.java	Mon Jun 18 17:04:47 2012 +0200
@@ -54,7 +54,6 @@
     @Override
     public void start(final BundleContext context) throws Exception {
 
-        // FIXME: there should be a better way than this
         ServiceListener listener = new ServiceListener() {
             
             private ApplicationService appService;
@@ -82,7 +81,7 @@
                 
                 if (contextActionServiceLoaded && applicationServiceLoaded) {
                     contextServiceReg = context.registerService(VMContextAction.class.getName(),
-                                            new HeapDumpAction(appService.getDAOFactory()), null);
+                                            new HeapDumpAction(appService.getDAOFactory(), context), null);
                 }
             }
         };
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/client/heapdumper/src/main/java/com/redhat/thermostat/client/heap/HeapDump.java	Mon Jun 18 17:04:47 2012 +0200
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2012 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.heap;
+
+import java.util.Date;
+
+public class HeapDump {
+
+    private String name;
+    private Date timestamp;
+    
+    void setTimestamp(long currentTimeMillis) {
+        this.timestamp = new Date(currentTimeMillis);
+    }
+
+    void setVMName(String name) {
+        this.name = name;
+    }
+    
+    public String getName() {
+        return name;
+    }
+    
+    public Date getTimestamp() {
+        return timestamp;
+    }
+    
+    @Override
+    public String toString() {
+        return "[" + getTimestamp() +"] ";
+    }
+}
--- a/client/heapdumper/src/main/java/com/redhat/thermostat/client/heap/HeapDumpAction.java	Mon Jun 18 16:17:37 2012 +0200
+++ b/client/heapdumper/src/main/java/com/redhat/thermostat/client/heap/HeapDumpAction.java	Mon Jun 18 17:04:47 2012 +0200
@@ -36,21 +36,18 @@
 
 package com.redhat.thermostat.client.heap;
 
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.nio.file.Files;
-import java.util.logging.Level;
 import java.util.logging.Logger;
 
+import org.osgi.framework.BundleContext;
+
+import com.redhat.thermostat.client.heap.swing.HeapSwingView;
 import com.redhat.thermostat.client.osgi.service.Filter;
 import com.redhat.thermostat.client.osgi.service.VMContextAction;
+import com.redhat.thermostat.client.osgi.service.VmInformationService;
+import com.redhat.thermostat.common.appctx.ApplicationContext;
 import com.redhat.thermostat.common.dao.DAOFactory;
-import com.redhat.thermostat.common.dao.HeapDAO;
 import com.redhat.thermostat.common.dao.Ref;
 import com.redhat.thermostat.common.dao.VmRef;
-import com.redhat.thermostat.common.model.HeapInfo;
 import com.redhat.thermostat.common.model.VmInfo;
 
 /**
@@ -62,37 +59,27 @@
     private static final Logger log = Logger.getLogger(HeapDumpAction.class.getName());
 
     private final DAOFactory dao;
+    private final BundleContext context;
 
-    public HeapDumpAction(DAOFactory dao) {
+    public HeapDumpAction(DAOFactory dao, BundleContext context) {
         this.dao = dao;
+        this.context = context;
     }
 
     @Override
     public String getName() {
-        return "Heap Dump";
+        return "Heap Analysis";
     }
 
     @Override
     public String getDescription() {
-        return "Generate a heap dump";
+        return "Heap View";
     }
 
     @Override
     public void execute(VmRef reference) {
-        try {
-            File tempFile = Files.createTempFile("thermostat-", "-heapdump").toFile();
-            String tempFileName = tempFile.getAbsolutePath();
-            tempFile.delete(); // Need to delete before dumping heap, jmap does not override existing file and stop with an error.
-            Process proc = Runtime.getRuntime().exec(new String[] {"jmap", "-dump:format=b,file=" + tempFileName, reference.getIdString()});
-            try {
-                proc.waitFor();
-                log.info("Heap dump written to: " + tempFileName);
-            } catch (InterruptedException e) {
-                Thread.currentThread().interrupt();
-            }
-        } catch (IOException e) {
-            log.log(Level.SEVERE, "Unexpected IO problem while writing heap dump", e);
-        }
+        ApplicationContext.getInstance().getViewFactory().setViewClass(HeapView.class, HeapSwingView.class);
+        context.registerService(VmInformationService.class.getName(), new HeapDumperService(), null);
     }
 
     @Override
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/client/heapdumper/src/main/java/com/redhat/thermostat/client/heap/HeapDumpController.java	Mon Jun 18 17:04:47 2012 +0200
@@ -0,0 +1,169 @@
+/*
+ * Copyright 2012 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.heap;
+
+import java.awt.Component;
+import javax.swing.JComponent;
+
+import java.text.DecimalFormat;
+import java.text.NumberFormat;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+import com.redhat.thermostat.client.heap.HeapView.HeadDumperAction;
+import com.redhat.thermostat.client.heap.chart.OverviewChart;
+import com.redhat.thermostat.client.osgi.service.VmInformationServiceController;
+import com.redhat.thermostat.common.ActionEvent;
+import com.redhat.thermostat.common.ActionListener;
+import com.redhat.thermostat.common.NotImplementedException;
+import com.redhat.thermostat.common.Timer;
+import com.redhat.thermostat.common.BasicView.Action;
+import com.redhat.thermostat.common.Timer.SchedulingType;
+import com.redhat.thermostat.common.appctx.ApplicationContext;
+import com.redhat.thermostat.common.dao.VmMemoryStatDAO;
+import com.redhat.thermostat.common.dao.VmRef;
+import com.redhat.thermostat.common.model.VmMemoryStat;
+import com.redhat.thermostat.common.model.VmMemoryStat.Generation;
+import com.redhat.thermostat.common.model.VmMemoryStat.Space;
+import com.redhat.thermostat.common.utils.DisplayableValues;
+import com.redhat.thermostat.common.utils.DisplayableValues.Scale;
+
+public class HeapDumpController implements VmInformationServiceController {
+
+    private final VmMemoryStatDAO vmDao;
+    private final VmRef ref;
+    
+    private HeapView<JComponent> view;
+    private final Timer timer;
+    
+    private OverviewChart model;
+    
+    public HeapDumpController(final VmRef ref) {
+        
+        this.ref = ref;
+        this.vmDao = ApplicationContext.getInstance().getDAOFactory().getVmMemoryStatDAO();
+        
+        model = new OverviewChart("Heap Used vs. Current Capacity Difference", "Time", "Heap");
+        
+        timer = ApplicationContext.getInstance().getTimerFactory().createTimer();
+        timer.setAction(new HeapOverviewDataCollector());
+        
+        timer.setInitialDelay(0);
+        timer.setDelay(1000);
+        timer.setTimeUnit(TimeUnit.MILLISECONDS);
+        timer.setSchedulingType(SchedulingType.FIXED_RATE);
+        
+        view = ApplicationContext.getInstance().getViewFactory().getView(HeapView.class);
+        view.addActionListener(new ActionListener<Action>() {            
+            @Override
+            public void actionPerformed(ActionEvent<Action> actionEvent) {
+                switch (actionEvent.getActionId()) {
+                case HIDDEN:
+                    timer.stop();
+                    break;
+                
+                case VISIBLE:
+                    timer.start();
+                    break;
+
+                default:
+                    throw new NotImplementedException("unknown event: " + actionEvent.getActionId());
+                }
+            }
+        });
+        
+        final HeapDumperCommand command = new HeapDumperCommand();
+        view.addDumperListener(new ActionListener<HeapView.HeadDumperAction>() {
+            @Override
+            public void actionPerformed(ActionEvent<HeadDumperAction> actionEvent) {
+                HeapDump dump = command.execute(ref);
+                view.addHeapDump(dump);
+            }
+        });
+    }
+    
+    @Override
+    public Component getComponent() {
+        return view.getComponent();
+    }
+
+    @Override
+    public String getLocalizedName() {
+        return "Memory Analyzer";
+    }
+
+    class HeapOverviewDataCollector implements Runnable {
+        @Override
+        public void run() {
+            long used = 0l;
+            long capacity = 0l;
+            long max = 0l;
+            
+            List<VmMemoryStat> vmInfo = vmDao.getLatestVmMemoryStats(ref);
+            for (VmMemoryStat memoryStats: vmInfo) {
+                List<Generation> generations = memoryStats.getGenerations();
+                for (Generation generation : generations) {
+                    
+                    // non heap
+                    if (generation.name.equals("perm")) {
+                        continue;
+                    }
+                    
+                    List<Space> spaces = generation.spaces;
+                    for (Space space: spaces) {
+                        used += space.used;
+                        capacity += space.capacity;
+                        
+                        // TODO
+                        max =+ space.maxCapacity;
+                    }
+                }
+                model.addData(memoryStats.getTimeStamp(), used, capacity);
+                
+                NumberFormat formatter = DecimalFormat.getInstance();
+
+                double res = Scale.convertTo(Scale.B, used);
+                String _used = formatter.format(res) + " " + Scale.B;
+                
+                res = Scale.convertTo(Scale.B, capacity);
+                String _capacity= formatter.format(capacity) + " " + Scale.B;
+                
+                view.updateOverview(model, _used, _capacity);
+            }
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/client/heapdumper/src/main/java/com/redhat/thermostat/client/heap/HeapDumperCommand.java	Mon Jun 18 17:04:47 2012 +0200
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2012 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.heap;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import com.redhat.thermostat.common.dao.VmRef;
+
+public class HeapDumperCommand {
+    
+    private static final Logger log = Logger.getLogger(HeapDumpAction.class.getName());
+
+    public HeapDump execute(VmRef reference) {
+        try {
+            File tempFile = Files.createTempFile("thermostat-", "-heapdump").toFile();
+            String tempFileName = tempFile.getAbsolutePath();
+            tempFile.delete(); // Need to delete before dumping heap, jmap does not override existing file and stop with an error.
+            Process proc = Runtime.getRuntime().exec(new String[] {"jmap", "-dump:format=b,file=" + tempFileName, reference.getIdString()});
+            try {
+                proc.waitFor();
+                log.info("Heap dump written to: " + tempFileName);
+            } catch (InterruptedException e) {
+                Thread.currentThread().interrupt();
+            }
+
+            HeapDump dump = new HeapDump();
+            dump.setTimestamp(System.currentTimeMillis());
+            dump.setVMName(reference.getName());
+            
+            return dump;
+        
+        } catch (IOException e) {
+            
+            log.log(Level.SEVERE, "Unexpected IO problem while writing heap dump", e);
+            return null;
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/client/heapdumper/src/main/java/com/redhat/thermostat/client/heap/HeapDumperService.java	Mon Jun 18 17:04:47 2012 +0200
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2012 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.heap;
+
+import com.redhat.thermostat.client.osgi.service.VmInformationService;
+import com.redhat.thermostat.client.osgi.service.VmInformationServiceController;
+import com.redhat.thermostat.common.dao.VmRef;
+
+public class HeapDumperService implements VmInformationService {
+
+    @Override
+    public VmInformationServiceController getInformationServiceController(VmRef ref) {
+        return new HeapDumpController(ref);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/client/heapdumper/src/main/java/com/redhat/thermostat/client/heap/HeapView.java	Mon Jun 18 17:04:47 2012 +0200
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2012 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.heap;
+
+import com.redhat.thermostat.client.heap.chart.OverviewChart;
+import com.redhat.thermostat.common.ActionListener;
+import com.redhat.thermostat.common.ActionNotifier;
+import com.redhat.thermostat.common.BasicView;
+import com.redhat.thermostat.common.BasicView.Action;
+
+public abstract class HeapView<E> extends BasicView {
+    
+    public enum HeadDumperAction {
+        DUMP_REQUESTED,
+        REQUEST_ABORTED
+    }
+    
+    protected final ActionNotifier<HeadDumperAction> heapDumperNotifier;
+    protected HeapView() {
+        heapDumperNotifier = new ActionNotifier<HeadDumperAction>(this);
+    }
+    
+    public void addDumperListener(ActionListener<HeadDumperAction> listener) {
+        heapDumperNotifier.addActionListener(listener);
+    }
+    
+    public void removeDumperListener(ActionListener<HeadDumperAction> listener) {
+        heapDumperNotifier.removeActionListener(listener);
+    }
+        
+    public abstract E getComponent();
+
+    abstract public void updateOverview(OverviewChart model, String used, String capacity);
+    abstract public void addHeapDump(HeapDump dump);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/client/heapdumper/src/main/java/com/redhat/thermostat/client/heap/chart/Chart.java	Mon Jun 18 17:04:47 2012 +0200
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2012 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.heap.chart;
+
+import java.awt.Color;
+import java.awt.Graphics2D;
+import java.awt.geom.Rectangle2D;
+import java.awt.image.BufferedImage;
+
+import org.jfree.chart.JFreeChart;
+
+public abstract class Chart {
+    
+    public BufferedImage getChart(int width, int height, Color bgColor) {
+        JFreeChart chart = createChart(width, height, bgColor);
+        
+        BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
+        chart.draw((Graphics2D) image.getGraphics(), new Rectangle2D.Double(0, 0, width, height), null);
+        
+        return image;
+    }
+    
+    protected abstract JFreeChart createChart(int width, int height, Color bgColor);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/client/heapdumper/src/main/java/com/redhat/thermostat/client/heap/chart/OverviewChart.java	Mon Jun 18 17:04:47 2012 +0200
@@ -0,0 +1,132 @@
+/*
+ * Copyright 2012 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.heap.chart;
+
+import java.awt.Color;
+import java.awt.GradientPaint;
+import java.awt.Graphics2D;
+import java.awt.Paint;
+import java.awt.geom.Rectangle2D;
+import java.awt.image.BufferedImage;
+import java.util.Date;
+
+import javax.swing.plaf.ColorUIResource;
+
+import org.jfree.chart.ChartFactory;
+import org.jfree.chart.JFreeChart;
+import org.jfree.chart.axis.DateAxis;
+import org.jfree.chart.axis.ValueAxis;
+import org.jfree.chart.plot.XYPlot;
+import org.jfree.chart.renderer.xy.XYDifferenceRenderer;
+import org.jfree.data.time.Millisecond;
+import org.jfree.data.time.TimeSeries;
+import org.jfree.data.time.TimeSeriesCollection;
+
+public class OverviewChart extends Chart {
+        
+    private static final ColorUIResource MAIN_BAR_BASE_COLOR = new ColorUIResource(0x4A90D9);
+
+    
+    private TimeSeries total;
+    private TimeSeries used;
+    private String title;
+    private String xAxis;
+    private String yAxis;
+    
+    public OverviewChart(String title, String xAxis, String yAxis) {
+        
+        this.title = title;
+        this.xAxis = xAxis;
+        this.yAxis = yAxis;
+        
+        total = new TimeSeries("");
+        total.setDescription("total");
+        used = new TimeSeries("");
+        used.setDescription("used");
+    }
+    
+    @Override
+    protected JFreeChart createChart(int width, int height, Color bgColor) {
+        
+        TimeSeriesCollection dataset = new TimeSeriesCollection();
+        dataset.addSeries(total);
+        dataset.addSeries(used);
+        
+        JFreeChart chart = ChartFactory.createTimeSeriesChart(
+                title,
+                xAxis,
+                yAxis,
+                dataset,
+                false,  // legend
+                false,  // tool tips
+                false   // URLs
+        );
+
+        Paint paint = new GradientPaint(0, 0, MAIN_BAR_BASE_COLOR, 0, height, bgColor);
+        Paint paint2 = new GradientPaint(0, 0, Color.GREEN, 0, height, bgColor);
+
+        XYPlot plot = (XYPlot) chart.getPlot();
+        plot.setDomainPannable(true);
+        XYDifferenceRenderer r = new XYDifferenceRenderer(paint, paint2, false);
+        r.setRoundXCoordinates(true);
+        plot.setDomainCrosshairLockedOnData(true);
+        plot.setRangeCrosshairLockedOnData(true);
+        plot.setDomainCrosshairVisible(true);
+        plot.setRangeCrosshairVisible(true);
+        plot.setRenderer(r);
+
+        ValueAxis domainAxis = new DateAxis(xAxis);
+        domainAxis.setLowerMargin(0.0);
+        domainAxis.setUpperMargin(0.0);
+        plot.setDomainAxis(domainAxis);
+        plot.setForegroundAlpha(0.5f);
+        
+        return chart;
+    }
+
+    public void addData(long timeStamp, long used, long total) {
+        Millisecond millisecond = new Millisecond(new Date(timeStamp));
+        if (this.total.getValue(millisecond) == null) {
+            this.total.add(millisecond, total);
+            this.total.removeAgedItems(true);
+        }
+        if (this.used.getValue(millisecond) == null) {
+            this.used.add(millisecond, used);
+            this.used.removeAgedItems(true);
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/client/heapdumper/src/main/java/com/redhat/thermostat/client/heap/swing/ChartPanel.java	Mon Jun 18 17:04:47 2012 +0200
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2012 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.heap.swing;
+
+import java.awt.Graphics;
+import java.awt.image.BufferedImage;
+
+import javax.swing.JPanel;
+
+import com.redhat.thermostat.client.heap.chart.Chart;
+
+public class ChartPanel extends JPanel {
+
+    private Chart chart;
+    
+    public ChartPanel(Chart chart) {
+        this.chart = chart;
+    }
+    
+    @Override
+    protected void paintComponent(Graphics g) {
+        BufferedImage image = chart.getChart(getWidth(), getHeight(), getBackground());
+        g.drawImage(image, 0, 0, null);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/client/heapdumper/src/main/java/com/redhat/thermostat/client/heap/swing/HeaderPanel.java	Mon Jun 18 17:04:47 2012 +0200
@@ -0,0 +1,146 @@
+/*
+ * Copyright 2012 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.heap.swing;
+
+import java.awt.Graphics;
+import java.awt.Graphics2D;
+import java.awt.RenderingHints;
+import java.lang.reflect.InvocationTargetException;
+
+import javax.swing.BoxLayout;
+import javax.swing.JComponent;
+import javax.swing.JFrame;
+import javax.swing.JPanel;
+import javax.swing.GroupLayout;
+import javax.swing.GroupLayout.Alignment;
+import javax.swing.LayoutStyle.ComponentPlacement;
+import javax.swing.SwingUtilities;
+
+import sun.swing.SwingUtilities2;
+
+/**
+ * A component that hot a panel
+ */
+public class HeaderPanel extends JPanel {
+
+    private String header;
+    private boolean open;
+    
+    private JPanel contentPanel;
+    
+    public HeaderPanel(String header) {
+        this();
+        this.header = header;
+    }
+    
+    public HeaderPanel() {
+        
+        this.open = true;
+        
+        JPanel headerPanel = new TopPanel();
+        
+        contentPanel = new JPanel();
+        contentPanel.setLayout(new BoxLayout(contentPanel, BoxLayout.X_AXIS));
+        
+        GroupLayout groupLayout = new GroupLayout(this);
+        groupLayout.setHorizontalGroup(
+            groupLayout.createParallelGroup(Alignment.LEADING)
+                .addComponent(headerPanel, GroupLayout.DEFAULT_SIZE, 450, Short.MAX_VALUE)
+                .addComponent(contentPanel, GroupLayout.DEFAULT_SIZE, 450, Short.MAX_VALUE)
+        );
+        groupLayout.setVerticalGroup(
+            groupLayout.createParallelGroup(Alignment.LEADING)
+                .addGroup(groupLayout.createSequentialGroup()
+                    .addComponent(headerPanel, GroupLayout.PREFERRED_SIZE, 30, GroupLayout.PREFERRED_SIZE)
+                    .addPreferredGap(ComponentPlacement.RELATED)
+                    .addComponent(contentPanel, GroupLayout.DEFAULT_SIZE, 260, Short.MAX_VALUE))
+        );
+        setLayout(groupLayout);
+    }
+    
+    public String getHeader() {
+        return header;
+    }
+    
+    public void setHeader(String header) {
+        this.header = header;
+    }
+    
+    public void setContent(JComponent content) {
+        contentPanel.removeAll();
+        contentPanel.add(content);
+        contentPanel.revalidate();
+        repaint();
+    }
+    
+    @SuppressWarnings({ "restriction", "serial" })
+    private class TopPanel extends JPanel {
+        @Override
+        protected void paintComponent(Graphics g) {
+            
+            Graphics2D graphics = (Graphics2D) g.create();
+            
+            graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
+            
+            graphics.setColor(getForeground());
+            graphics.drawRect(0, 0, getWidth() - 1, getHeight() - 1);
+
+            if (header != null) {
+                int currentHeight = getHeight();
+                int height = SwingUtilities2.getFontMetrics(this, getFont()).getAscent()/2 + currentHeight/2 - 1;
+                SwingUtilities2.drawString(this, graphics, header, 5, height);
+            }
+            
+            graphics.dispose();
+        }
+    }
+    
+    public static void main(String[] args) throws InvocationTargetException, InterruptedException {
+        SwingUtilities.invokeAndWait(new Runnable() {
+            
+            @Override
+            public void run() {
+               JFrame frame = new JFrame();
+               HeaderPanel header = new HeaderPanel();
+               header.setHeader("Test");
+               frame.add(header);
+               frame.setSize(500, 500);
+               frame.setVisible(true);
+            }
+        });
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/client/heapdumper/src/main/java/com/redhat/thermostat/client/heap/swing/HeapPanel.java	Mon Jun 18 17:04:47 2012 +0200
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2012 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.heap.swing;
+
+import java.awt.Dimension;
+
+import javax.swing.JPanel;
+import javax.swing.JSplitPane;
+import javax.swing.GroupLayout;
+import javax.swing.GroupLayout.Alignment;
+
+public class HeapPanel extends JPanel {
+
+    private JSplitPane splitPane;
+    
+    public HeapPanel() {
+        
+        splitPane = new JSplitPane();
+        splitPane.setOrientation(JSplitPane.VERTICAL_SPLIT);
+        GroupLayout groupLayout = new GroupLayout(this);
+        groupLayout.setHorizontalGroup(
+            groupLayout.createParallelGroup(Alignment.LEADING)
+                .addComponent(splitPane, Alignment.TRAILING, GroupLayout.DEFAULT_SIZE, 450, Short.MAX_VALUE)
+        );
+        groupLayout.setVerticalGroup(
+            groupLayout.createParallelGroup(Alignment.LEADING)
+                .addGroup(groupLayout.createSequentialGroup()
+                    .addGap(5)
+                    .addComponent(splitPane, GroupLayout.DEFAULT_SIZE, 295, Short.MAX_VALUE))
+        );
+
+        splitPane.setOneTouchExpandable(true);
+        setLayout(groupLayout);
+    }
+
+    void hideBottom() {
+        splitPane.getBottomComponent().setMinimumSize(new Dimension(0, 0));
+        splitPane.setDividerLocation(1.0d);
+    }
+    
+    void hideTop() {
+        splitPane.getTopComponent().setMinimumSize(new Dimension(0, 0));
+        splitPane.setDividerLocation(0.0d);
+    }
+    
+    void setTop(JPanel panel) {
+        splitPane.setTopComponent(panel);
+    }
+    
+    void setBottom(JPanel panel) {
+        splitPane.setBottomComponent(panel);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/client/heapdumper/src/main/java/com/redhat/thermostat/client/heap/swing/HeapSwingView.java	Mon Jun 18 17:04:47 2012 +0200
@@ -0,0 +1,136 @@
+/*
+ * Copyright 2012 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.heap.swing;
+
+import java.awt.Component;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+
+import javax.swing.JComponent;
+import javax.swing.SwingUtilities;
+import javax.swing.SwingWorker;
+
+import com.redhat.thermostat.client.heap.HeapDump;
+import com.redhat.thermostat.client.heap.HeapView;
+import com.redhat.thermostat.client.heap.chart.OverviewChart;
+import com.redhat.thermostat.client.ui.ComponentVisibleListener;
+
+public class HeapSwingView extends HeapView<JComponent> {
+    
+    private StatsPanel stats;
+
+    private HeapPanel panel;
+    private HeaderPanel overview;
+    
+    public HeapSwingView() {
+        
+        stats = new StatsPanel();
+        stats.addHeapDumperListener(new ActionListener() {
+            @Override
+            public void actionPerformed(ActionEvent e) {
+                
+                stats.disableHeapDumperControl();
+                
+                SwingWorker<Void, Void> worker = new SwingWorker<Void, Void>() {
+                    @Override
+                    protected Void doInBackground() throws Exception {
+                        heapDumperNotifier.fireAction(HeadDumperAction.DUMP_REQUESTED);
+                        return null;
+                    }
+                    
+                    @Override
+                    protected void done() {
+                        stats.enableHeapDumperControl();
+                    }
+                };
+                worker.execute();
+            }
+        });
+        
+        panel = new HeapPanel();
+        
+        overview = new HeaderPanel("Heap Usage Overview");
+        overview.setContent(stats);
+        overview.addHierarchyListener(new ViewVisibleListener());
+
+        panel.setTop(overview);        
+    }
+    
+    @Override
+    public JComponent getComponent() {
+        return overview;
+    }
+    
+    private class ViewVisibleListener extends ComponentVisibleListener {
+        @Override
+        public void componentShown(Component component) {
+            HeapSwingView.this.notify(Action.VISIBLE);
+        }
+
+        @Override
+        public void componentHidden(Component component) {
+            HeapSwingView.this.notify(Action.HIDDEN);
+        }
+    }
+
+    @Override
+    public void updateOverview(final OverviewChart model, final String used, final String capacity) {
+        
+        SwingUtilities.invokeLater(new Runnable() {
+            
+            @Override
+            public void run() {
+
+                ChartPanel charts = new ChartPanel(model);
+                stats.setChartPanel(charts);
+                
+                stats.setMax(capacity);
+                stats.setUsed(used);
+            }
+        });
+    }
+
+    @Override
+    public void addHeapDump(final HeapDump dump) {
+        SwingUtilities.invokeLater(new Runnable() {
+            @Override
+            public void run() {
+                stats.addDump(dump);
+            }
+        });
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/client/heapdumper/src/main/java/com/redhat/thermostat/client/heap/swing/StatsPanel.java	Mon Jun 18 17:04:47 2012 +0200
@@ -0,0 +1,181 @@
+/*
+ * Copyright 2012 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.heap.swing;
+
+import java.awt.event.ActionListener;
+
+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.JPanel;
+import javax.swing.LayoutStyle.ComponentPlacement;
+import javax.swing.SwingConstants;
+
+import com.redhat.thermostat.client.heap.HeapDump;
+import javax.swing.JList;
+
+public class StatsPanel extends JPanel {
+    
+    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("size:");
+        currentLabel.setHorizontalAlignment(SwingConstants.LEFT);
+        
+        JLabel maxLabel = new JLabel("max:");
+        maxLabel.setHorizontalAlignment(SwingConstants.LEFT);
+        
+        current = new JLabel("12");
+        current.setHorizontalAlignment(SwingConstants.RIGHT);
+        
+        max = new JLabel("12");
+        max.setHorizontalAlignment(SwingConstants.RIGHT);
+        
+        heapDumpButton = new JButton("Heap Dump");
+        
+        dumpList = new JList<>();
+        listModel = new DefaultListModel<>();
+        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(dumpList, 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(dumpList, GroupLayout.DEFAULT_SIZE, 172, Short.MAX_VALUE)
+                    .addContainerGap())
+        );
+        rightPanel.setLayout(gl_rightPanel);
+        setLayout(groupLayout);
+
+        // initially invisible
+        dumpList.setVisible(false);
+    }
+    
+    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);
+    }
+
+    public void disableHeapDumperControl() {
+        heapDumpButton.setText("dumping...");
+        heapDumpButton.setEnabled(false);
+    }
+
+    public void enableHeapDumperControl() {
+        heapDumpButton.setText("Heap Dump");
+        heapDumpButton.setEnabled(true);
+    }
+
+    public void addDump(HeapDump dump) {
+        
+        listModel.addElement(dump);
+        if (!dumpList.isVisible()) {
+            dumpList.setVisible(true);
+        }
+    }
+}
--- a/client/heapdumper/src/test/java/com/redhat/thermostat/client/heap/HeapDumpActionTest.java	Mon Jun 18 16:17:37 2012 +0200
+++ b/client/heapdumper/src/test/java/com/redhat/thermostat/client/heap/HeapDumpActionTest.java	Mon Jun 18 17:04:47 2012 +0200
@@ -47,6 +47,7 @@
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
+import org.osgi.framework.BundleContext;
 
 import com.redhat.thermostat.client.osgi.service.Filter;
 import com.redhat.thermostat.common.dao.DAOFactory;
@@ -78,7 +79,9 @@
         deadVmRef = mock(VmRef.class);
         when(vmInfoDAO.getVmInfo(deadVmRef)).thenReturn(vmInfo2);
 
-        heapDumpAction = new HeapDumpAction(dao);
+        
+        BundleContext bundleContext = mock(BundleContext.class);
+        heapDumpAction = new HeapDumpAction(dao, bundleContext);
     }
 
     @After
@@ -91,12 +94,12 @@
 
     @Test
     public void testName() {
-        assertEquals("Heap Dump", heapDumpAction.getName());
+        assertEquals("Heap Analysis", heapDumpAction.getName());
     }
 
     @Test
     public void testDescription() {
-        assertEquals("Generate a heap dump", heapDumpAction.getDescription());
+        assertEquals("Heap View", heapDumpAction.getDescription());
     }
 
     @Test
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/common/core/src/main/java/com/redhat/thermostat/common/BasicView.java	Mon Jun 18 17:04:47 2012 +0200
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2012 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.common;
+
+public class BasicView implements View {
+    public enum Action {
+        VISIBLE,
+        HIDDEN,
+    }
+    private final ActionNotifier<Action> notifier;
+    
+    protected BasicView() {
+        notifier = new ActionNotifier<Action>(this);
+    }
+    
+    public void addActionListener(ActionListener<Action> listener) {
+        notifier.addActionListener(listener);
+    }
+    
+    public void removeActionListener(ActionListener<Action> listener) {
+        notifier.removeActionListener(listener);
+    }
+    
+    protected void notify(Action action) {
+        notifier.fireAction(action);
+    }
+}