changeset 2002:8658e633a5a2

Profiler table/treemap displaying optimization PR3073 Reviewed-by: jkang Review-thread: http://icedtea.classpath.org/pipermail/thermostat/2016-June/020119.html Original-thread: http://icedtea.classpath.org/pipermail/thermostat/2015-December/017352.html
author James Aziz <jaziz@redhat.com>
date Thu, 30 Jun 2016 15:48:32 -0400
parents 1f5dc3abcf16
children 7e6a52dfd303
files vm-profiler/client-swing/src/main/java/com/redhat/thermostat/vm/profiler/client/swing/internal/SwingVmProfileView.java vm-profiler/client-swing/src/main/java/com/redhat/thermostat/vm/profiler/client/swing/internal/VmProfileController.java vm-profiler/client-swing/src/main/java/com/redhat/thermostat/vm/profiler/client/swing/internal/VmProfileTreeMapView.java vm-profiler/client-swing/src/main/java/com/redhat/thermostat/vm/profiler/client/swing/internal/VmProfileView.java vm-profiler/client-swing/src/test/java/com/redhat/thermostat/vm/profiler/client/swing/internal/SwingVmProfileViewTest.java vm-profiler/client-swing/src/test/java/com/redhat/thermostat/vm/profiler/client/swing/internal/VmProfileControllerTest.java
diffstat 6 files changed, 213 insertions(+), 37 deletions(-) [+]
line wrap: on
line diff
--- a/vm-profiler/client-swing/src/main/java/com/redhat/thermostat/vm/profiler/client/swing/internal/SwingVmProfileView.java	Wed Jun 29 15:01:50 2016 -0400
+++ b/vm-profiler/client-swing/src/main/java/com/redhat/thermostat/vm/profiler/client/swing/internal/SwingVmProfileView.java	Thu Jun 30 15:48:32 2016 -0400
@@ -36,6 +36,9 @@
 
 package com.redhat.thermostat.vm.profiler.client.swing.internal;
 
+import static com.redhat.thermostat.vm.profiler.client.swing.internal.VmProfileView.TabbedPaneAction.TABLE_TAB_SELECTED;
+import static com.redhat.thermostat.vm.profiler.client.swing.internal.VmProfileView.TabbedPaneAction.TREEMAP_TAB_SELECTED;
+
 import java.awt.BorderLayout;
 import java.awt.Color;
 import java.awt.Component;
@@ -58,16 +61,20 @@
 import javax.swing.JLabel;
 import javax.swing.JList;
 import javax.swing.JPanel;
+import javax.swing.JScrollPane;
 import javax.swing.JSplitPane;
 import javax.swing.JTabbedPane;
 import javax.swing.JTable;
 import javax.swing.JToggleButton;
 import javax.swing.ListSelectionModel;
 import javax.swing.RowSorter;
+import javax.swing.SingleSelectionModel;
 import javax.swing.SortOrder;
 import javax.swing.SwingUtilities;
 import javax.swing.SwingWorker;
 import javax.swing.WindowConstants;
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
 import javax.swing.event.ListSelectionEvent;
 import javax.swing.event.ListSelectionListener;
 import javax.swing.table.DefaultTableModel;
@@ -111,6 +118,7 @@
 
     private HeaderPanel mainContainer;
     private JTabbedPane tabPane;
+    private TabbedPaneAction lastTabSelection;
 
     private ActionToggleButton toggleButton;
 
@@ -123,6 +131,8 @@
     private JLabel currentStatusLabel;
     private boolean viewControlsEnabled = true;
 
+    private ActionListener<TabbedPaneAction> tabListener;
+
     static class ProfileItemRenderer extends DefaultListCellRenderer {
         @Override
         public Component getListCellRendererComponent(JList<?> list,
@@ -139,7 +149,32 @@
 
     public SwingVmProfileView() {
         listModel = new DefaultListModel<>();
+
         tabPane = new JTabbedPane();
+        tabPane.getModel().addChangeListener(new ChangeListener() {
+            @Override
+            public void stateChanged(ChangeEvent e) {
+                SingleSelectionModel model = (SingleSelectionModel) e.getSource();
+
+                switch (model.getSelectedIndex()) {
+                    case 0:
+                        if (lastTabSelection != TABLE_TAB_SELECTED) {
+                            fireTabbedPaneAction(TABLE_TAB_SELECTED);
+                            lastTabSelection = TABLE_TAB_SELECTED;
+                        }
+                        break;
+                    case 1:
+                        if (lastTabSelection != TREEMAP_TAB_SELECTED) {
+                            fireTabbedPaneAction(TREEMAP_TAB_SELECTED);
+                            lastTabSelection = TREEMAP_TAB_SELECTED;
+                        }
+                        break;
+                    default:
+                        throw new AssertionError("Unexpected index " + model.getSelectedIndex());
+                }
+            }
+        });
+        lastTabSelection = TABLE_TAB_SELECTED;
 
         toggleButton = new ActionToggleButton(START_ICON, STOP_ICON, translator.localize(
                 LocaleResources.START_PROFILING));
@@ -222,6 +257,7 @@
             }
         });
         ThermostatScrollPane profileListPane = new ThermostatScrollPane(profileList);
+        profileListPane.setName("PROFILE_LIST_PANE");
 
         Vector<String> columnNames = new Vector<>();
         columnNames.add(translator.localize(LocaleResources.PROFILER_RESULTS_METHOD).getContents());
@@ -266,10 +302,11 @@
         sortKeys.add(new RowSorter.SortKey(COLUMN_METHOD_TIME, SortOrder.DESCENDING));
         profileTable.getRowSorter().setSortKeys(sortKeys);
 
-        profileTable.setName("METHOD_TABLE");
+        JScrollPane scrollPaneProfileTable = profileTable.wrap();
+        scrollPaneProfileTable.setName("METHOD_TABLE");
 
         tabPane.addTab(translator.localize(LocaleResources.PROFILER_RESULTS_TABLE).getContents(),
-                profileTable.wrap());
+                scrollPaneProfileTable);
 
         JSplitPane splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, profileListPane, tabPane);
         splitPane.setDividerLocation(SPLIT_PANE_RATIO);
@@ -279,6 +316,27 @@
     }
 
     @Override
+    public void setTabbedPaneActionListener(ActionListener<TabbedPaneAction> listener) {
+        this.tabListener = listener;
+    }
+
+    private void fireTabbedPaneAction(final TabbedPaneAction action) {
+        new SwingWorker<Void, Void>() {
+            @Override
+            protected Void doInBackground() throws Exception {
+                try {
+                    ActionEvent<TabbedPaneAction> event =
+                            new ActionEvent<>(this, action);
+                    tabListener.actionPerformed(event);
+                } catch (Throwable t) {
+                    t.printStackTrace();
+                }
+                return null;
+            }
+        }.execute();
+    }
+
+    @Override
     public void addProfileActionListener(ActionListener<ProfileAction> listener) {
         listeners.add(listener);
     }
--- a/vm-profiler/client-swing/src/main/java/com/redhat/thermostat/vm/profiler/client/swing/internal/VmProfileController.java	Wed Jun 29 15:01:50 2016 -0400
+++ b/vm-profiler/client-swing/src/main/java/com/redhat/thermostat/vm/profiler/client/swing/internal/VmProfileController.java	Thu Jun 30 15:48:32 2016 -0400
@@ -36,9 +36,12 @@
 
 package com.redhat.thermostat.vm.profiler.client.swing.internal;
 
+import static com.redhat.thermostat.vm.profiler.client.swing.internal.VmProfileView.TabbedPaneAction.TABLE_TAB_SELECTED;
+
 import java.io.InputStream;
 import java.net.InetSocketAddress;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.HashMap;
@@ -76,6 +79,7 @@
 import com.redhat.thermostat.storage.model.VmInfo.AliveStatus;
 import com.redhat.thermostat.vm.profiler.client.core.ProfilingResult;
 import com.redhat.thermostat.vm.profiler.client.core.ProfilingResultParser;
+import com.redhat.thermostat.vm.profiler.client.swing.internal.VmProfileView.TabbedPaneAction;
 import com.redhat.thermostat.vm.profiler.client.swing.internal.VmProfileView.Profile;
 import com.redhat.thermostat.vm.profiler.client.swing.internal.VmProfileView.ProfileAction;
 import com.redhat.thermostat.vm.profiler.client.swing.internal.VmProfileView.ProfilingState;
@@ -101,6 +105,9 @@
     private VmProfileView view;
     private VmProfileTreeMapView treeMapView;
 
+    private TabbedPaneAction lastTabSelection;
+    private ProfilingResult selectedResult;
+
     private Timer updater;
 
     private Clock clock;
@@ -140,6 +147,7 @@
         this.treeMapView = Objects.requireNonNull(treeMapViewProvider.createView());
         view.addTabToTabbedPane(translator.localize(LocaleResources.PROFILER_RESULTS_TREEMAP),
                 ((SwingComponent) this.treeMapView).getUiComponent());
+        this.lastTabSelection = TABLE_TAB_SELECTED;
 
         // TODO dispose the timer when done
         updater = service.getTimerFactory().createTimer();
@@ -180,20 +188,38 @@
             public void actionPerformed(ActionEvent<ProfileAction> actionEvent) {
                 ProfileAction id = actionEvent.getActionId();
                 switch (id) {
-                case START_PROFILING:
-                    startProfiling();
-                    break;
-                case STOP_PROFILING:
-                    stopProfiling();
-                    break;
-                case PROFILE_SELECTED:
-                    updateViewWithProfileRunData();
-                    break;
-                default:
+                    case START_PROFILING:
+                        startProfiling();
+                        break;
+                    case STOP_PROFILING:
+                        stopProfiling();
+                        break;
+                    case PROFILE_SELECTED:
+                        loadSelectedProfileRunData();
+                        updateViewWithSelectedProfileRunData();
+                        break;
+                    default:
+                        throw new AssertionError("Unknown event: " + id);
+                }
+            }
+
+        });
+
+        view.setTabbedPaneActionListener(new ActionListener<TabbedPaneAction>() {
+            @Override
+            public void actionPerformed(ActionEvent<TabbedPaneAction> actionEvent) {
+                TabbedPaneAction id = actionEvent.getActionId();
+                boolean validTabSelection = Arrays.asList(VmProfileView.TabbedPaneAction.values()).contains(id);
+
+                if (validTabSelection && selectedResult != null) {
+                    lastTabSelection = id;
+                    updateViewWithSelectedProfileRunData();
+                } else if (validTabSelection) {
+                    lastTabSelection = id;
+                } else {
                     throw new AssertionError("Unknown event: " + id);
                 }
             }
-
         });
 
         view.setViewControlsEnabled(isAlive());
@@ -342,14 +368,23 @@
         view.setAvailableProfilingRuns(profiles);
     }
 
-    private void updateViewWithProfileRunData() {
-        Profile selectedProfile = view.getSelectedProfile();
-        String profileId = selectedProfile.name;
+    private void loadSelectedProfileRunData() {
+        String profileId = view.getSelectedProfile().name;
         InputStream in = profileDao.loadProfileDataById(vm, profileId);
-        ProfilingResult result = new ProfilingResultParser().parse(in);
+        selectedResult = new ProfilingResultParser().parse(in);
+    }
 
-        view.setProfilingDetailData(result);
-        treeMapView.display(result);
+    private void updateViewWithSelectedProfileRunData() {
+        switch (lastTabSelection) {
+            case TABLE_TAB_SELECTED:
+                view.setProfilingDetailData(selectedResult);
+                break;
+            case TREEMAP_TAB_SELECTED:
+                treeMapView.display(selectedResult);
+                break;
+            default:
+                throw new AssertionError("Unknown selection: " + lastTabSelection);
+        }
     }
 
     @Override
--- a/vm-profiler/client-swing/src/main/java/com/redhat/thermostat/vm/profiler/client/swing/internal/VmProfileTreeMapView.java	Wed Jun 29 15:01:50 2016 -0400
+++ b/vm-profiler/client-swing/src/main/java/com/redhat/thermostat/vm/profiler/client/swing/internal/VmProfileTreeMapView.java	Thu Jun 30 15:48:32 2016 -0400
@@ -43,4 +43,5 @@
 public abstract class VmProfileTreeMapView extends BasicView implements UIComponent{
 
     public abstract void display(ProfilingResult result);
+
 }
--- a/vm-profiler/client-swing/src/main/java/com/redhat/thermostat/vm/profiler/client/swing/internal/VmProfileView.java	Wed Jun 29 15:01:50 2016 -0400
+++ b/vm-profiler/client-swing/src/main/java/com/redhat/thermostat/vm/profiler/client/swing/internal/VmProfileView.java	Thu Jun 30 15:48:32 2016 -0400
@@ -77,6 +77,11 @@
 
     }
 
+    enum TabbedPaneAction {
+        TABLE_TAB_SELECTED,
+        TREEMAP_TAB_SELECTED,
+    }
+
     enum ProfileAction {
         START_PROFILING,
         STOP_PROFILING,
@@ -122,6 +127,8 @@
 
     public abstract void removeProfileActionlistener(ActionListener<ProfileAction> listener);
 
+    public abstract void setTabbedPaneActionListener(ActionListener<TabbedPaneAction> listener);
+
     /*
      * Because of the latency between asking for starting profiling and actually
      * starting profiling, we use a lot more states than 'enabled/disabled' for
--- a/vm-profiler/client-swing/src/test/java/com/redhat/thermostat/vm/profiler/client/swing/internal/SwingVmProfileViewTest.java	Wed Jun 29 15:01:50 2016 -0400
+++ b/vm-profiler/client-swing/src/test/java/com/redhat/thermostat/vm/profiler/client/swing/internal/SwingVmProfileViewTest.java	Thu Jun 30 15:48:32 2016 -0400
@@ -36,13 +36,14 @@
 
 package com.redhat.thermostat.vm.profiler.client.swing.internal;
 
-import com.redhat.thermostat.client.swing.components.ThermostatTable;
 import net.java.openjdk.cacio.ctc.junit.CacioFESTRunner;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
 
 import java.awt.Color;
 import java.awt.Component;
@@ -69,7 +70,7 @@
 import org.fest.swing.fixture.JLabelFixture;
 import org.fest.swing.fixture.JListFixture;
 import org.fest.swing.fixture.JScrollPaneFixture;
-import org.fest.swing.fixture.JTableFixture;
+import org.fest.swing.fixture.JTabbedPaneFixture;
 import org.fest.swing.fixture.JToggleButtonFixture;
 import org.junit.After;
 import org.junit.Before;
@@ -81,10 +82,14 @@
 import com.redhat.thermostat.annotations.internal.CacioTest;
 import com.redhat.thermostat.client.swing.components.ActionToggleButton;
 import com.redhat.thermostat.client.swing.components.Icon;
+import com.redhat.thermostat.client.swing.components.ThermostatTable;
 import com.redhat.thermostat.client.swing.components.ThermostatTableRenderer;
+import com.redhat.thermostat.common.ActionListener;
 import com.redhat.thermostat.common.utils.MethodDescriptorConverter;
+import com.redhat.thermostat.shared.locale.LocalizedString;
 import com.redhat.thermostat.shared.locale.Translate;
 import com.redhat.thermostat.vm.profiler.client.core.ProfilingResult;
+import com.redhat.thermostat.vm.profiler.client.core.ProfilingResult.MethodInfo;
 
 @Category(CacioTest.class)
 @RunWith(CacioFESTRunner.class)
@@ -255,14 +260,15 @@
 
         frame.show();
 
-        final JTableFixture profileTable = frame.table("METHOD_TABLE");
+        final JScrollPaneFixture scrollPane = frame.scrollPane("METHOD_TABLE");
 
-        view.setProfilingDetailData(new ProfilingResult(new ArrayList<ProfilingResult.MethodInfo>()));
+        view.setProfilingDetailData(new ProfilingResult(new ArrayList<MethodInfo>()));
 
         SwingUtilities.invokeAndWait(new Runnable() {
             @Override
             public void run() {
-                TableModel model =  profileTable.component().getModel();
+                TableModel model =
+                        ((ThermostatTable) scrollPane.component().getViewport().getView()).getModel();
                 assertEquals(1, model.getRowCount());
                 assertEquals(translator.localize(LocaleResources.PROFILER_NO_RESULTS).getContents(),
                         model.getValueAt(0, 0));
@@ -311,7 +317,7 @@
 
         frame.show();
 
-        final JTableFixture profileTable = frame.table("METHOD_TABLE");
+        final JScrollPaneFixture scrollPane = frame.scrollPane("METHOD_TABLE");
 
         List<ProfilingResult.MethodInfo> data = new ArrayList<>();
         data.add(new ProfilingResult.MethodInfo(new MethodDescriptorConverter.MethodDeclaration(
@@ -325,7 +331,8 @@
         SwingUtilities.invokeAndWait(new Runnable() {
             @Override
             public void run() {
-                TableModel model =  profileTable.component().getModel();
+                TableModel model =
+                        ((ThermostatTable) scrollPane.component().getViewport().getView()).getModel();
                 assertEquals(2, model.getRowCount());
 
                 ArrayList<String> methodNames = new ArrayList<>();
@@ -337,6 +344,57 @@
         });
     }
 
+    @GUITest
+    @Test
+    public void testTabAddingAndSwitching() throws InvocationTargetException, InterruptedException {
+        frame.show();
+        final JTabbedPaneFixture tabPaneFixture = frame.tabbedPane();
+
+        view.setTabbedPaneActionListener(mock(ActionListener.class));
+
+        view.setProfilingDetailData(new ProfilingResult(new ArrayList<MethodInfo>()));
+        Component testComp = mock(Component.class);
+        final String testCompName = "testComp";
+        when(testComp.getName()).thenReturn(testCompName);
+        final LocalizedString testTabName = new LocalizedString("testTab");
+        view.addTabToTabbedPane(testTabName, testComp);
+        final LocalizedString tableTabName =
+                translator.localize(LocaleResources.PROFILER_RESULTS_TABLE);
+        tabPaneFixture.requireTabTitles(tableTabName.getContents(), testTabName.getContents());
+
+        final Component selected1 = tabPaneFixture.selectedComponent();
+
+        SwingUtilities.invokeAndWait(new Runnable() {
+            @Override
+            public void run() {
+                assertNotNull(selected1);
+                assertEquals("METHOD_TABLE", selected1.getName());
+            }
+        });
+
+        tabPaneFixture.selectTab(testTabName.getContents());
+        final Component selected2 = tabPaneFixture.selectedComponent();
+
+        SwingUtilities.invokeAndWait(new Runnable() {
+            @Override
+            public void run() {
+                assertNotNull(selected2);
+                assertEquals(testCompName, selected2.getName());
+            }
+        });
+
+        tabPaneFixture.selectTab(tableTabName.getContents());
+        final Component selected3 = tabPaneFixture.selectedComponent();
+
+        SwingUtilities.invokeAndWait(new Runnable() {
+            @Override
+            public void run() {
+                assertNotNull(selected3);
+                assertEquals("METHOD_TABLE", selected3.getName());
+            }
+        });
+    }
+
     @Test
     public void testProfileItemRendererFailsWithNonProfileValue() {
         SwingVmProfileView.ProfileItemRenderer renderer = new SwingVmProfileView.ProfileItemRenderer();
--- a/vm-profiler/client-swing/src/test/java/com/redhat/thermostat/vm/profiler/client/swing/internal/VmProfileControllerTest.java	Wed Jun 29 15:01:50 2016 -0400
+++ b/vm-profiler/client-swing/src/test/java/com/redhat/thermostat/vm/profiler/client/swing/internal/VmProfileControllerTest.java	Thu Jun 30 15:48:32 2016 -0400
@@ -52,6 +52,7 @@
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
 import static org.mockito.Mockito.when;
 
 import java.awt.Component;
@@ -119,6 +120,7 @@
     private RequestQueue queue;
     private Clock clock;
     private VmProfileView view;
+    private SwingVmProfileTreeMapView treeMapView;
     private VmRef vm;
     private VmProfileTreeMapViewProvider treeMapViewProvider;
 
@@ -151,6 +153,7 @@
 
         clock = mock(Clock.class);
         view = mock(VmProfileView.class);
+        treeMapView = mock(SwingVmProfileTreeMapView.class);
 
         agent = mock(HostRef.class);
         when(agent.getAgentId()).thenReturn(AGENT_ID);
@@ -160,9 +163,8 @@
         when(vm.getVmId()).thenReturn(VM_ID);
 
         treeMapViewProvider = mock(VmProfileTreeMapViewProvider.class);
-        VmProfileTreeMapView vmProfileTreeMapView = mock(SwingVmProfileTreeMapView.class);
-        when(treeMapViewProvider.createView()).thenReturn(vmProfileTreeMapView);
-        when(((SwingComponent) vmProfileTreeMapView).getUiComponent()).thenReturn(mock(Component.class));
+        when(treeMapViewProvider.createView()).thenReturn(treeMapView);
+        when(((SwingComponent) treeMapView).getUiComponent()).thenReturn(mock(Component.class));
 
         AgentInformation agentInfo = new AgentInformation();
         agentInfo.setAlive(true);
@@ -342,20 +344,35 @@
     }
 
     @Test
-    public void selectingAProfileShowsDetails() throws Exception {
-        final String PROFILE_DATA = "1 foo";
-
+    public void testSelectingAndDisplayingAProfileAsTableAndTreeMap() throws Exception {
         controller = createController();
 
-        ArgumentCaptor<ActionListener> listenerCaptor = ArgumentCaptor.forClass(ActionListener.class);
-        verify(view).addProfileActionListener(listenerCaptor.capture());
-
+        final String PROFILE_DATA = "1 foo";
         Profile PROFILE = new Profile(PROFILE_ID, 10);
 
         when(view.getSelectedProfile()).thenReturn(PROFILE);
-        when(profileDao.loadProfileDataById(vm, PROFILE_ID)).thenReturn(new ByteArrayInputStream(PROFILE_DATA.getBytes(StandardCharsets.UTF_8)));
+        when(profileDao.loadProfileDataById(vm, PROFILE_ID)).thenReturn(
+                new ByteArrayInputStream(PROFILE_DATA.getBytes(StandardCharsets.UTF_8)));
+
+        ArgumentCaptor<ActionListener> tabListenerCaptor =
+                ArgumentCaptor.forClass(ActionListener.class);
+        verify(view).setTabbedPaneActionListener(tabListenerCaptor.capture());
+        tabListenerCaptor.getValue().actionPerformed(
+                new ActionEvent<>(view, VmProfileView.TabbedPaneAction.TREEMAP_TAB_SELECTED));
 
-        listenerCaptor.getValue().actionPerformed(new ActionEvent<>(view, ProfileAction.PROFILE_SELECTED));
+        verify(treeMapView).getUiComponent();
+        verifyNoMoreInteractions(treeMapView);
+
+        ArgumentCaptor<ActionListener> profileListenerCaptor =
+                ArgumentCaptor.forClass(ActionListener.class);
+        verify(view).addProfileActionListener(profileListenerCaptor.capture());
+        profileListenerCaptor.getValue().actionPerformed(
+                new ActionEvent<>(view, ProfileAction.PROFILE_SELECTED));
+
+        verify(treeMapView).display(isA(ProfilingResult.class));
+
+        tabListenerCaptor.getValue().actionPerformed(
+                new ActionEvent<>(view, VmProfileView.TabbedPaneAction.TABLE_TAB_SELECTED));
 
         verify(view).setProfilingDetailData(isA(ProfilingResult.class));
     }