changeset 390:7a5ea4137f04

Some more heap fixes review-thread: http://icedtea.classpath.org/pipermail/thermostat/2012-June/001894.html reviewed-by: rkennke
author Mario Torre <neugens.limasoftware@gmail.com>
date Tue, 19 Jun 2012 19:11:24 +0200
parents cfbc2b43142e
children be4863c455f7 809924b921ec
files client/heapdumper/src/main/java/com/redhat/thermostat/client/heap/HeapDump.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/HeapView.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/HeapDumpControllerTest.java common/core/src/main/java/com/redhat/thermostat/common/dao/VmRef.java
diffstat 9 files changed, 327 insertions(+), 27 deletions(-) [+]
line wrap: on
line diff
--- a/client/heapdumper/src/main/java/com/redhat/thermostat/client/heap/HeapDump.java	Tue Jun 19 19:00:28 2012 +0200
+++ b/client/heapdumper/src/main/java/com/redhat/thermostat/client/heap/HeapDump.java	Tue Jun 19 19:11:24 2012 +0200
@@ -38,29 +38,26 @@
 
 import java.util.Date;
 
+import com.redhat.thermostat.common.model.HeapInfo;
+
 public class HeapDump {
 
-    private String name;
-    private Date timestamp;
+    private HeapInfo info;
     
-    void setTimestamp(long currentTimeMillis) {
-        this.timestamp = new Date(currentTimeMillis);
-    }
-
-    void setVMName(String name) {
-        this.name = name;
+    public String getName() {
+        return info.getVm().getName();
     }
     
-    public String getName() {
-        return name;
-    }
-    
-    public Date getTimestamp() {
-        return timestamp;
+    public long getTimestamp() {
+        return info.getTimestamp();
     }
     
     @Override
     public String toString() {
-        return "[" + getTimestamp() +"] ";
+        return "[" + new Date(getTimestamp()) +"] ";
+    }
+
+    public void setHeapInfo(HeapInfo info) {
+        this.info = info;
     }
 }
--- a/client/heapdumper/src/main/java/com/redhat/thermostat/client/heap/HeapDumpController.java	Tue Jun 19 19:00:28 2012 +0200
+++ b/client/heapdumper/src/main/java/com/redhat/thermostat/client/heap/HeapDumpController.java	Tue Jun 19 19:11:24 2012 +0200
@@ -41,6 +41,7 @@
 
 import java.text.DecimalFormat;
 import java.text.NumberFormat;
+import java.util.Collection;
 import java.util.List;
 import java.util.concurrent.TimeUnit;
 
@@ -54,12 +55,16 @@
 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.HeapDAO;
 import com.redhat.thermostat.common.dao.VmMemoryStatDAO;
 import com.redhat.thermostat.common.dao.VmRef;
+
+import com.redhat.thermostat.common.model.HeapInfo;
 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 {
@@ -67,6 +72,8 @@
     private final VmMemoryStatDAO vmDao;
     private final VmRef ref;
     
+    private final HeapDAO heapDAO;
+        
     private HeapView<JComponent> view;
     private final Timer timer;
     
@@ -76,6 +83,7 @@
         
         this.ref = ref;
         this.vmDao = ApplicationContext.getInstance().getDAOFactory().getVmMemoryStatDAO();
+        this.heapDAO = ApplicationContext.getInstance().getDAOFactory().getHeapDAO();
         
         model = new OverviewChart("Heap Used vs. Current Capacity Difference", "Time", "Heap");
         
@@ -88,6 +96,16 @@
         timer.setSchedulingType(SchedulingType.FIXED_RATE);
         
         view = ApplicationContext.getInstance().getViewFactory().getView(HeapView.class);
+
+        HeapDump dump = null;
+        view.clearHeapDumpList();
+        Collection<HeapInfo> infos = heapDAO.getAllHeapInfo(ref);
+        for (HeapInfo info : infos) {
+            dump = new HeapDump();
+            dump.setHeapInfo(info);
+            view.addHeapDump(dump);
+        }
+        
         view.addActionListener(new ActionListener<Action>() {            
             @Override
             public void actionPerformed(ActionEvent<Action> actionEvent) {
@@ -96,7 +114,7 @@
                     timer.stop();
                     break;
                 
-                case VISIBLE:
+                case VISIBLE:                    
                     timer.start();
                     break;
 
@@ -110,8 +128,17 @@
         view.addDumperListener(new ActionListener<HeapView.HeadDumperAction>() {
             @Override
             public void actionPerformed(ActionEvent<HeadDumperAction> actionEvent) {
-                HeapDump dump = command.execute(ref);
-                view.addHeapDump(dump);
+                HeapDump dump = null;
+                switch (actionEvent.getActionId()) {
+                case DUMP_REQUESTED:
+                    dump = command.execute(ref);
+                    view.addHeapDump(dump);
+                    break;
+                
+                case ANALYSE:
+                    view.openDumpView(dump);
+                    break;
+                }
             }
         });
     }
--- a/client/heapdumper/src/main/java/com/redhat/thermostat/client/heap/HeapDumperCommand.java	Tue Jun 19 19:00:28 2012 +0200
+++ b/client/heapdumper/src/main/java/com/redhat/thermostat/client/heap/HeapDumperCommand.java	Tue Jun 19 19:11:24 2012 +0200
@@ -37,18 +37,24 @@
 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 com.redhat.thermostat.common.appctx.ApplicationContext;
+import com.redhat.thermostat.common.dao.HeapDAO;
 import com.redhat.thermostat.common.dao.VmRef;
+import com.redhat.thermostat.common.model.HeapInfo;
 
 public class HeapDumperCommand {
     
-    private static final Logger log = Logger.getLogger(HeapDumpAction.class.getName());
+    private static final Logger log = Logger.getLogger(HeapDumperCommand.class.getName());
 
     public HeapDump execute(VmRef reference) {
+
         try {
             File tempFile = Files.createTempFile("thermostat-", "-heapdump").toFile();
             String tempFileName = tempFile.getAbsolutePath();
@@ -57,13 +63,15 @@
             try {
                 proc.waitFor();
                 log.info("Heap dump written to: " + tempFileName);
+            
             } catch (InterruptedException e) {
                 Thread.currentThread().interrupt();
             }
 
+            HeapInfo info = saveHeapDumpInfo(reference, tempFile);
+            
             HeapDump dump = new HeapDump();
-            dump.setTimestamp(System.currentTimeMillis());
-            dump.setVMName(reference.getName());
+            dump.setHeapInfo(info);
             
             return dump;
         
@@ -73,4 +81,15 @@
             return null;
         }
     }
+    
+    private HeapInfo saveHeapDumpInfo(VmRef reference, File tempFile) throws FileNotFoundException {
+    
+        HeapDAO heapDAO = ApplicationContext.getInstance().getDAOFactory().getHeapDAO();
+        HeapInfo heapInfo = new HeapInfo(reference, System.currentTimeMillis());
+        heapInfo.setHeapDumpId(reference.getStringID() + "-" + reference.getAgent().getAgentId() + "-" + heapInfo.getTimestamp());
+        
+        heapDAO.putHeapInfo(heapInfo, new FileInputStream(tempFile));
+        
+        return heapInfo;
+    }
 }
--- a/client/heapdumper/src/main/java/com/redhat/thermostat/client/heap/HeapView.java	Tue Jun 19 19:00:28 2012 +0200
+++ b/client/heapdumper/src/main/java/com/redhat/thermostat/client/heap/HeapView.java	Tue Jun 19 19:11:24 2012 +0200
@@ -40,12 +40,12 @@
 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,
+        ANALYSE,
         REQUEST_ABORTED
     }
     
@@ -66,4 +66,7 @@
 
     abstract public void updateOverview(OverviewChart model, String used, String capacity);
     abstract public void addHeapDump(HeapDump dump);
+    abstract public void clearHeapDumpList();
+    
+    abstract public void openDumpView(HeapDump dump);
 }
--- a/client/heapdumper/src/main/java/com/redhat/thermostat/client/heap/swing/HeapPanel.java	Tue Jun 19 19:00:28 2012 +0200
+++ b/client/heapdumper/src/main/java/com/redhat/thermostat/client/heap/swing/HeapPanel.java	Tue Jun 19 19:11:24 2012 +0200
@@ -67,6 +67,10 @@
         setLayout(groupLayout);
     }
 
+    void divideView() {
+        splitPane.setDividerLocation(.5d);
+    }
+    
     void hideBottom() {
         splitPane.getBottomComponent().setMinimumSize(new Dimension(0, 0));
         splitPane.setDividerLocation(1.0d);
--- a/client/heapdumper/src/main/java/com/redhat/thermostat/client/heap/swing/HeapSwingView.java	Tue Jun 19 19:00:28 2012 +0200
+++ b/client/heapdumper/src/main/java/com/redhat/thermostat/client/heap/swing/HeapSwingView.java	Tue Jun 19 19:11:24 2012 +0200
@@ -39,8 +39,13 @@
 import java.awt.Component;
 import java.awt.event.ActionEvent;
 import java.awt.event.ActionListener;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
 
+import javax.swing.BoxLayout;
 import javax.swing.JComponent;
+import javax.swing.JPanel;
+import javax.swing.JTable;
 import javax.swing.SwingUtilities;
 import javax.swing.SwingWorker;
 
@@ -50,12 +55,16 @@
 import com.redhat.thermostat.client.ui.ComponentVisibleListener;
 
 public class HeapSwingView extends HeapView<JComponent> {
+
+    private boolean heapDetailIsShowing;
     
     private StatsPanel stats;
 
-    private HeapPanel panel;
+    private HeapPanel heapDetailPanel;
     private HeaderPanel overview;
     
+    private JPanel visiblePane;
+    
     public HeapSwingView() {
         
         stats = new StatsPanel();
@@ -81,18 +90,32 @@
             }
         });
         
-        panel = new HeapPanel();
+        stats.addDumpListListener(new MouseAdapter() {
+            @Override
+            public void mouseClicked(MouseEvent e) {
+                if (e.getClickCount() == 2) { 
+                    HeapDump dump = stats.getSelectedHeapDump();
+                    heapDumperNotifier.fireAction(HeadDumperAction.ANALYSE, dump);
+                }
+            }
+        });
+        
+        visiblePane = new JPanel();
+        visiblePane.setLayout(new BoxLayout(visiblePane, BoxLayout.X_AXIS));
+        
+        heapDetailPanel = new HeapPanel();
         
         overview = new HeaderPanel("Heap Usage Overview");
         overview.setContent(stats);
         overview.addHierarchyListener(new ViewVisibleListener());
 
-        panel.setTop(overview);        
+        // at the beginning, only the overview is visible
+        visiblePane.add(overview);
     }
     
     @Override
     public JComponent getComponent() {
-        return overview;
+        return visiblePane;
     }
     
     private class ViewVisibleListener extends ComponentVisibleListener {
@@ -133,4 +156,58 @@
             }
         });
     }
+    
+    @Override
+    public void clearHeapDumpList() {
+        SwingUtilities.invokeLater(new Runnable() {
+            @Override
+            public void run() {
+                stats.clearDumpList();
+            }
+        });
+    }
+    
+    @Override
+    public void openDumpView(final HeapDump dump) {
+        SwingUtilities.invokeLater(new Runnable() {
+            @Override
+            public void run() {
+                if (!heapDetailIsShowing) {
+                    visiblePane.removeAll();
+                    
+                    heapDetailIsShowing = true;
+                    
+                    heapDetailPanel.divideView();
+                    heapDetailPanel.setTop(overview);
+                    
+                    String[] columnNames = {"First Name",
+                            "Last Name",
+                            "Sport",
+                            "# of Years",
+                            "Vegetarian"};
+                    Object[][] data = {
+                            {"Kathy", "Smith",
+                             "Snowboarding", new Integer(5), new Boolean(false)},
+                            {"John", "Doe",
+                             "Rowing", new Integer(3), new Boolean(true)},
+                            {"Sue", "Black",
+                             "Knitting", new Integer(2), new Boolean(false)},
+                            {"Jane", "White",
+                             "Speed reading", new Integer(20), new Boolean(true)},
+                            {"Joe", "Brown",
+                             "Pool", new Integer(10), new Boolean(false)}
+                        };
+                    JTable table = new JTable(data, columnNames);
+                    JPanel bottom = new JPanel();
+                    bottom.setLayout(new BoxLayout(bottom, BoxLayout.X_AXIS));
+                    bottom.add(table);
+                    heapDetailPanel.setBottom(bottom);
+                    
+                    visiblePane.add(heapDetailPanel);
+                    
+                    visiblePane.revalidate();                    
+                }
+            }
+        });
+    }
 }
--- a/client/heapdumper/src/main/java/com/redhat/thermostat/client/heap/swing/StatsPanel.java	Tue Jun 19 19:00:28 2012 +0200
+++ b/client/heapdumper/src/main/java/com/redhat/thermostat/client/heap/swing/StatsPanel.java	Tue Jun 19 19:11:24 2012 +0200
@@ -37,6 +37,7 @@
 package com.redhat.thermostat.client.heap.swing;
 
 import java.awt.event.ActionListener;
+import java.awt.event.MouseListener;
 
 import javax.swing.BoxLayout;
 import javax.swing.DefaultListModel;
@@ -46,10 +47,12 @@
 import javax.swing.JLabel;
 import javax.swing.JPanel;
 import javax.swing.LayoutStyle.ComponentPlacement;
+import javax.swing.ListSelectionModel;
 import javax.swing.SwingConstants;
 
 import com.redhat.thermostat.client.heap.HeapDump;
 import javax.swing.JList;
+import javax.swing.event.ListSelectionListener;
 
 public class StatsPanel extends JPanel {
     
@@ -98,6 +101,7 @@
         
         dumpList = new JList<>();
         listModel = new DefaultListModel<>();
+        dumpList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
         dumpList.setModel(listModel);
         
         GroupLayout gl_rightPanel = new GroupLayout(rightPanel);
@@ -161,6 +165,10 @@
         heapDumpButton.addActionListener(listener);
     }
 
+    void addDumpListListener(MouseListener listener) {
+        dumpList.addMouseListener(listener);
+    }
+    
     public void disableHeapDumperControl() {
         heapDumpButton.setText("dumping...");
         heapDumpButton.setEnabled(false);
@@ -178,4 +186,15 @@
             dumpList.setVisible(true);
         }
     }
+
+    public void clearDumpList() {
+        listModel.clear();
+        if (dumpList.isVisible()) {
+            dumpList.setVisible(false);
+        }
+    }
+    
+    public HeapDump getSelectedHeapDump() {
+        return dumpList.getSelectedValue();
+    }
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/client/heapdumper/src/test/java/com/redhat/thermostat/client/heap/HeapDumpControllerTest.java	Tue Jun 19 19:11:24 2012 +0200
@@ -0,0 +1,151 @@
+/*
+ * 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 static org.junit.Assert.*;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.swing.JComponent;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import org.mockito.ArgumentCaptor;
+
+import com.redhat.thermostat.common.ActionEvent;
+import com.redhat.thermostat.common.ActionListener;
+import com.redhat.thermostat.common.Timer;
+import com.redhat.thermostat.common.TimerFactory;
+import com.redhat.thermostat.common.ViewFactory;
+import com.redhat.thermostat.common.appctx.ApplicationContext;
+import com.redhat.thermostat.common.appctx.ApplicationContextUtil;
+import com.redhat.thermostat.common.dao.DAOFactory;
+import com.redhat.thermostat.common.dao.HeapDAO;
+import com.redhat.thermostat.common.dao.MongoDAOFactory;
+import com.redhat.thermostat.common.dao.VmClassStatDAO;
+import com.redhat.thermostat.common.dao.VmRef;
+import com.redhat.thermostat.common.model.VmClassStat;
+
+public class HeapDumpControllerTest {
+
+    private ActionListener<HeapView.Action> actionListener;
+    private ActionListener<HeapView.HeadDumperAction> heapDumperListener;
+    
+    private Timer timer;
+    
+    private HeapView<JComponent> view;
+    
+    private HeapDumpController controller;
+    
+    @Before
+    public void setUp() {
+
+        VmClassStatDAO vmClassStatDAO = mock(VmClassStatDAO.class);
+        
+        DAOFactory daoFactory = mock(MongoDAOFactory.class);
+        when(daoFactory.getVmClassStatsDAO()).thenReturn(vmClassStatDAO);
+        
+        HeapDAO heapDao = mock(HeapDAO.class);
+        when(daoFactory.getHeapDAO()).thenReturn(heapDao);
+        
+        ApplicationContext.getInstance().setDAOFactory(daoFactory);
+
+        setUpTimers();
+        setUpView();
+
+        setUpListeners();
+    }
+    
+    private void setUpTimers() {
+        timer = mock(Timer.class);
+        ArgumentCaptor<Runnable> timerActionCaptor = ArgumentCaptor.forClass(Runnable.class);
+        doNothing().when(timer).setAction(timerActionCaptor.capture());
+
+        TimerFactory timerFactory = mock(TimerFactory.class);
+        when(timerFactory.createTimer()).thenReturn(timer);
+        ApplicationContext.getInstance().setTimerFactory(timerFactory);
+    }
+    
+    private void setUpView() {
+        ViewFactory viewFactory = mock(ViewFactory.class);
+
+        view = mock(HeapView.class);
+        when(viewFactory.getView(eq(HeapView.class))).thenReturn(view);
+        
+        ApplicationContext.getInstance().setViewFactory(viewFactory);
+    }
+    
+    private void setUpListeners() {
+        ArgumentCaptor<ActionListener> viewArgumentCaptor1 = ArgumentCaptor.forClass(ActionListener.class);
+        doNothing().when(view).addActionListener(viewArgumentCaptor1.capture());
+        
+        ArgumentCaptor<ActionListener> viewArgumentCaptor2 = ArgumentCaptor.forClass(ActionListener.class);
+        doNothing().when(view).addDumperListener(viewArgumentCaptor2.capture());
+
+        VmRef ref = mock(VmRef.class); 
+        controller = new HeapDumpController(ref);
+        
+        actionListener = viewArgumentCaptor1.getValue();
+        heapDumperListener = viewArgumentCaptor2.getValue();
+    }
+    
+    @Test
+    public void testTimerStartOnViewVisible() {
+        actionListener.actionPerformed(new ActionEvent<>(view, HeapView.Action.VISIBLE));
+        verify(timer).start();
+    }
+
+    @Test
+    public void testTimerStopsOnViewHidden() {
+        actionListener.actionPerformed(new ActionEvent<>(view, HeapView.Action.HIDDEN));
+        verify(timer).stop();
+    }
+    
+    @After
+    public void tearDown() {
+        ApplicationContextUtil.resetApplicationContext();
+    }
+}
--- a/common/core/src/main/java/com/redhat/thermostat/common/dao/VmRef.java	Tue Jun 19 19:00:28 2012 +0200
+++ b/common/core/src/main/java/com/redhat/thermostat/common/dao/VmRef.java	Tue Jun 19 19:11:24 2012 +0200
@@ -63,6 +63,9 @@
         return uid;
     }
 
+    /**
+     * Equivalent to {@link #getStringID()}.
+     */
     public String getIdString() {
         return uidString;
     }