# HG changeset patch # User James Aziz # Date 1467316112 14400 # Node ID 8658e633a5a2d39b995d80ec5fbb1e1fc7bb5a0b # Parent 1f5dc3abcf1627dbf8207b5c016ffec605d758a0 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 diff -r 1f5dc3abcf16 -r 8658e633a5a2 vm-profiler/client-swing/src/main/java/com/redhat/thermostat/vm/profiler/client/swing/internal/SwingVmProfileView.java --- 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 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 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 listener) { + this.tabListener = listener; + } + + private void fireTabbedPaneAction(final TabbedPaneAction action) { + new SwingWorker() { + @Override + protected Void doInBackground() throws Exception { + try { + ActionEvent event = + new ActionEvent<>(this, action); + tabListener.actionPerformed(event); + } catch (Throwable t) { + t.printStackTrace(); + } + return null; + } + }.execute(); + } + + @Override public void addProfileActionListener(ActionListener listener) { listeners.add(listener); } diff -r 1f5dc3abcf16 -r 8658e633a5a2 vm-profiler/client-swing/src/main/java/com/redhat/thermostat/vm/profiler/client/swing/internal/VmProfileController.java --- 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 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() { + @Override + public void actionPerformed(ActionEvent 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 diff -r 1f5dc3abcf16 -r 8658e633a5a2 vm-profiler/client-swing/src/main/java/com/redhat/thermostat/vm/profiler/client/swing/internal/VmProfileTreeMapView.java --- 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); + } diff -r 1f5dc3abcf16 -r 8658e633a5a2 vm-profiler/client-swing/src/main/java/com/redhat/thermostat/vm/profiler/client/swing/internal/VmProfileView.java --- 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 listener); + public abstract void setTabbedPaneActionListener(ActionListener listener); + /* * Because of the latency between asking for starting profiling and actually * starting profiling, we use a lot more states than 'enabled/disabled' for diff -r 1f5dc3abcf16 -r 8658e633a5a2 vm-profiler/client-swing/src/test/java/com/redhat/thermostat/vm/profiler/client/swing/internal/SwingVmProfileViewTest.java --- 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())); + view.setProfilingDetailData(new ProfilingResult(new ArrayList())); 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 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 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())); + 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(); diff -r 1f5dc3abcf16 -r 8658e633a5a2 vm-profiler/client-swing/src/test/java/com/redhat/thermostat/vm/profiler/client/swing/internal/VmProfileControllerTest.java --- 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 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 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 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)); }