changeset 1833:2649ea55de87

Profiler controller correctly restores state after changing selected VM Reviewed-by: omajid Review-thread: http://icedtea.classpath.org/pipermail/thermostat/2015-October/016758.html PR2672
author Andrew Azores <aazores@redhat.com>
date Tue, 13 Oct 2015 17:16:38 -0400
parents ffd37dfffbdd
children 7db129297a73
files vm-profiler/client-swing/src/main/java/com/redhat/thermostat/vm/profiler/client/swing/internal/VmProfileController.java vm-profiler/client-swing/src/test/java/com/redhat/thermostat/vm/profiler/client/swing/internal/VmProfileControllerTest.java
diffstat 2 files changed, 244 insertions(+), 18 deletions(-) [+]
line wrap: on
line diff
--- a/vm-profiler/client-swing/src/main/java/com/redhat/thermostat/vm/profiler/client/swing/internal/VmProfileController.java	Tue Oct 13 12:48:05 2015 -0400
+++ b/vm-profiler/client-swing/src/main/java/com/redhat/thermostat/vm/profiler/client/swing/internal/VmProfileController.java	Tue Oct 13 17:16:38 2015 -0400
@@ -41,7 +41,9 @@
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Comparator;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 import java.util.concurrent.TimeUnit;
 
 import com.redhat.thermostat.client.command.RequestQueue;
@@ -82,7 +84,9 @@
 
 public class VmProfileController implements InformationServiceController<VmRef> {
 
+    static final String STATE_MAP_KEY = "vmProfileControllerRestoreBundle";
     private static final Translate<LocaleResources> translator = LocaleResources.createLocalizer();
+    private static final Map<VmRef, SaveState> STATE_BUNDLE_MAP = new HashMap<>();
 
     private final ApplicationService service;
     private final ProgressNotifier notifier;
@@ -99,8 +103,8 @@
     private Clock clock;
 
     private boolean profilingStartOrStopRequested = false;
-
     private ProfileStatusChange previousStatus;
+    private ProfilingState profilingState = ProfilingState.STOPPED;
 
     private ProgressHandle progressDisplay;
 
@@ -112,7 +116,7 @@
         this(service, notifier, agentInfoDao, vmInfoDao, dao, queue, new SystemClock(), new SwingVmProfileView(), vm);
     }
 
-    VmProfileController(ApplicationService service, ProgressNotifier notifier,
+    VmProfileController(final ApplicationService service, ProgressNotifier notifier,
             AgentInfoDAO agentInfoDao, VmInfoDAO vmInfoDao, ProfileDAO dao,
             RequestQueue queue, Clock clock,
             final VmProfileView view, VmRef vm) {
@@ -126,6 +130,10 @@
         this.view = view;
         this.vm = vm;
 
+        if (service.getApplicationCache().getAttribute(STATE_MAP_KEY) == null) {
+            service.getApplicationCache().addAttribute(STATE_MAP_KEY, STATE_BUNDLE_MAP);
+        }
+
         // TODO dispose the timer when done
         updater = service.getTimerFactory().createTimer();
         updater.setSchedulingType(SchedulingType.FIXED_DELAY);
@@ -146,9 +154,11 @@
             public void actionPerformed(ActionEvent<Action> actionEvent) {
                 switch (actionEvent.getActionId()) {
                     case HIDDEN:
+                        saveState();
                         updater.stop();
                         break;
                     case VISIBLE:
+                        restoreState();
                         view.setViewControlsEnabled(isAlive());
                         updater.start();
                         break;
@@ -182,11 +192,33 @@
         view.setViewControlsEnabled(isAlive());
     }
 
+    private void saveState() {
+        SaveState bundle = new SaveState(previousStatus, profilingStartOrStopRequested, profilingState);
+        Map<VmRef, SaveState> map = ((Map<VmRef, SaveState>) service.getApplicationCache().getAttribute(STATE_MAP_KEY));
+        map.put(vm, bundle);
+        service.getApplicationCache().addAttribute(STATE_MAP_KEY, map);
+    }
+
+    private void restoreState() {
+        SaveState bundle = ((Map<VmRef, SaveState>) service.getApplicationCache().getAttribute(STATE_MAP_KEY)).get(vm);
+        profilingStartOrStopRequested = bundle != null && bundle.isProfilingStartOrStopRequested();
+        previousStatus = bundle == null ? null : bundle.getProfileStatusChange();
+        profilingState = bundle == null ? ProfilingState.STOPPED : bundle.getProfilingState();
+        if (previousStatus != null) {
+            profilingState = getProfilingState(previousStatus, profilingStartOrStopRequested);
+        }
+        view.setProfilingState(profilingState);
+    }
+
     private void startProfiling() {
+        profilingState = ProfilingState.STARTING;
+        view.setProfilingState(profilingState);
         setProgressNotificationAndSendRequest(true);
     }
 
     private void stopProfiling() {
+        profilingState = ProfilingState.STOPPING;
+        view.setProfilingState(profilingState);
         setProgressNotificationAndSendRequest(false);
     }
 
@@ -211,6 +243,9 @@
 
                         hideProgressNotificationIfVisible();
                         profilingStartOrStopRequested = false;
+                        profilingState = ProfilingState.DISABLED;
+                        view.setProfilingState(profilingState);
+                        view.setViewControlsEnabled(false);
                         break;
                 }
             }
@@ -220,23 +255,11 @@
     }
 
     private void updateViewWithCurrentProfilingStatus() {
-        ProfilingState profilingState = ProfilingState.STOPPED;
-
         ProfileStatusChange currentStatus = profileDao.getLatestStatus(vm);
         if (currentStatus != null) {
-            boolean currentlyActive = currentStatus.isStarted();
-            if (currentlyActive && profilingStartOrStopRequested) {
-                profilingState = ProfilingState.STARTING;
-            } else if (currentlyActive) {
-                profilingState = ProfilingState.STARTED;
-            } else if (profilingStartOrStopRequested) {
-                profilingState = ProfilingState.STOPPING;
-            } else {
-                profilingState = ProfilingState.STOPPED;
-            }
+            profilingState = getProfilingState(currentStatus, profilingStartOrStopRequested);
         }
 
-        view.setViewControlsEnabled(isAlive());
         if (!isAlive()) {
             view.setProfilingState(ProfilingState.DISABLED);
         } else if (profilingStartOrStopRequested) {
@@ -254,12 +277,25 @@
         previousStatus = currentStatus;
     }
 
+    private ProfilingState getProfilingState(ProfileStatusChange profileStatusChange, boolean profilingStartOrStopRequested) {
+        ProfilingState profilingState;
+        boolean currentlyActive = profileStatusChange.isStarted();
+        if (currentlyActive && profilingStartOrStopRequested) {
+            profilingState = ProfilingState.STARTING;
+        } else if (currentlyActive) {
+            profilingState = ProfilingState.STARTED;
+        } else if (profilingStartOrStopRequested) {
+            profilingState = ProfilingState.STOPPING;
+        } else {
+            profilingState = ProfilingState.STOPPED;
+        }
+        return profilingState;
+    }
+
     private void showProgressNotification(boolean start) {
         if (start) {
-            view.setProfilingState(ProfilingState.STARTING);
             progressDisplay = new ProgressHandle(translator.localize(LocaleResources.STARTING_PROFILING));
         } else {
-            view.setProfilingState(ProfilingState.STOPPING);
             progressDisplay = new ProgressHandle(translator.localize(LocaleResources.STOPPING_PROFILING));
         }
         progressDisplay.setIndeterminate(true);
@@ -323,4 +359,28 @@
             return Long.compare(o1.timeStamp, o2.timeStamp);
         }
     }
+
+    static class SaveState {
+        private final ProfileStatusChange profileStatusChange;
+        private final boolean profilingStartOrStopRequested;
+        private final ProfilingState profilingState;
+
+        public SaveState(ProfileStatusChange profileStatusChange, boolean profilingStartOrStopRequested, ProfilingState profilingState) {
+            this.profileStatusChange = profileStatusChange;
+            this.profilingStartOrStopRequested = profilingStartOrStopRequested;
+            this.profilingState = profilingState;
+        }
+
+        public ProfileStatusChange getProfileStatusChange() {
+            return profileStatusChange;
+        }
+
+        public boolean isProfilingStartOrStopRequested() {
+            return profilingStartOrStopRequested;
+        }
+
+        public ProfilingState getProfilingState() {
+            return profilingState;
+        }
+    }
 }
--- a/vm-profiler/client-swing/src/test/java/com/redhat/thermostat/vm/profiler/client/swing/internal/VmProfileControllerTest.java	Tue Oct 13 12:48:05 2015 -0400
+++ b/vm-profiler/client-swing/src/test/java/com/redhat/thermostat/vm/profiler/client/swing/internal/VmProfileControllerTest.java	Tue Oct 13 17:16:38 2015 -0400
@@ -36,9 +36,16 @@
 
 package com.redhat.thermostat.vm.profiler.client.swing.internal;
 
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.notNullValue;
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
 import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyBoolean;
+import static org.mockito.Matchers.anyMap;
 import static org.mockito.Matchers.isA;
+import static org.mockito.Mockito.atLeastOnce;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.times;
@@ -49,9 +56,14 @@
 import java.net.InetSocketAddress;
 import java.nio.charset.StandardCharsets;
 import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 import java.util.concurrent.TimeUnit;
 
+import com.redhat.thermostat.common.ApplicationCache;
+import org.hamcrest.core.IsNull;
 import org.junit.Before;
 import org.junit.Test;
 import org.mockito.ArgumentCaptor;
@@ -96,6 +108,7 @@
 
     private Timer timer;
     private ApplicationService appService;
+    private ApplicationCache appCache;
     private ProgressNotifier notifier;
     private AgentInfoDAO agentInfoDao;
     private VmInfoDAO vmInfoDao;
@@ -119,6 +132,9 @@
         appService = mock(ApplicationService.class);
         when(appService.getTimerFactory()).thenReturn(timerFactory);
 
+        appCache = mock(ApplicationCache.class);
+        when(appService.getApplicationCache()).thenReturn(appCache);
+
         notifier = mock(ProgressNotifier.class);
 
         agentInfoDao = mock(AgentInfoDAO.class);
@@ -147,6 +163,7 @@
 
     @Test
     public void timerRunsWhenVisible() throws Exception {
+        when(appCache.getAttribute(any(String.class))).thenReturn(new HashMap<>());
         controller = createController();
 
         verify(timer, never()).start();
@@ -161,6 +178,7 @@
 
     @Test
     public void timerStopsWhenHidden() throws Exception {
+        when(appCache.getAttribute(any(String.class))).thenReturn(new HashMap<>());
         controller = createController();
 
         verify(timer, never()).start();
@@ -198,7 +216,7 @@
         assertEquals(1, resultList.size());
         assertEquals(PROFILE_TIMESTAMP, resultList.get(0).timeStamp);
 
-        verify(view, times(2)).setViewControlsEnabled(true);
+        verify(view, atLeastOnce()).setViewControlsEnabled(true);
     }
 
     @Test
@@ -315,6 +333,154 @@
         verify(view).setProfilingDetailData(isA(ProfilingResult.class));
     }
 
+    @Test
+    public void testSavesStateOnViewHidden() throws Exception {
+        Map<VmRef, VmProfileController.SaveState> map = new HashMap<>();
+        when(appCache.getAttribute(any(String.class))).thenReturn(map);
+        controller = createController();
+
+        ArgumentCaptor<ActionListener> actionListenerCaptor = ArgumentCaptor.forClass(ActionListener.class);
+        verify(view).addActionListener(actionListenerCaptor.capture());
+
+        actionListenerCaptor.getValue().actionPerformed(new ActionEvent(view, Action.HIDDEN));
+
+        verify(appCache).addAttribute(VmProfileController.STATE_MAP_KEY, any(anyMap().getClass()));
+        assertThat(map.keySet(), is(equalTo(Collections.singleton(vm))));
+        VmProfileController.SaveState state = map.get(vm);
+        assertThat(state, is(notNullValue()));
+        assertThat(state.isProfilingStartOrStopRequested(), is(false));
+        assertThat(state.getProfilingState(), is(VmProfileView.ProfilingState.STOPPED));
+        assertThat(state.getProfileStatusChange(), is(equalTo(null)));
+    }
+
+    @Test
+    public void testRestoresStateOnViewVisible() throws Exception {
+        when(appCache.getAttribute(any(String.class))).thenReturn(new HashMap<>());
+        controller = createController();
+
+        ArgumentCaptor<ActionListener> actionListenerCaptor = ArgumentCaptor.forClass(ActionListener.class);
+        verify(view).addActionListener(actionListenerCaptor.capture());
+
+        actionListenerCaptor.getValue().actionPerformed(new ActionEvent(view, Action.VISIBLE));
+
+        verify(appCache, atLeastOnce()).getAttribute(VmProfileController.STATE_MAP_KEY);
+        verify(view, atLeastOnce()).setProfilingState(any(VmProfileView.ProfilingState.class));
+        verify(view, atLeastOnce()).setViewControlsEnabled(anyBoolean());
+    }
+
+    @Test
+    public void testRestoresStateCorrectlyWhenPreviousStateSetStarting() throws Exception {
+        ProfileStatusChange psc = mock(ProfileStatusChange.class);
+        when(psc.isStarted()).thenReturn(true);
+        // should ignore the DISABLED argument because a previous state (psc) is available
+        VmProfileController.SaveState state = new VmProfileController.SaveState(psc, true, VmProfileView.ProfilingState.DISABLED);
+        doRestoreTest(state);
+
+        verify(view, atLeastOnce()).setProfilingState(VmProfileView.ProfilingState.STARTING);
+        verify(view, atLeastOnce()).setViewControlsEnabled(true);
+    }
+
+    @Test
+    public void testRestoresStateCorrectlyWhenPreviousStateSetStarted() throws Exception {
+        ProfileStatusChange psc = mock(ProfileStatusChange.class);
+        when(psc.isStarted()).thenReturn(true);
+        // should ignore the DISABLED argument because a previous state (psc) is available
+        VmProfileController.SaveState state = new VmProfileController.SaveState(psc, false, VmProfileView.ProfilingState.DISABLED);
+        doRestoreTest(state);
+
+        verify(view, atLeastOnce()).setProfilingState(VmProfileView.ProfilingState.STARTED);
+        verify(view, atLeastOnce()).setViewControlsEnabled(true);
+    }
+
+    @Test
+    public void testRestoresStateCorrectlyWhenPreviousStateSetStopping() throws Exception {
+        ProfileStatusChange psc = mock(ProfileStatusChange.class);
+        when(psc.isStarted()).thenReturn(false);
+        // should ignore the DISABLED argument because a previous state (psc) is available
+        VmProfileController.SaveState state = new VmProfileController.SaveState(psc, true, VmProfileView.ProfilingState.DISABLED);
+        doRestoreTest(state);
+
+        verify(view, atLeastOnce()).setProfilingState(VmProfileView.ProfilingState.STOPPING);
+        verify(view, atLeastOnce()).setViewControlsEnabled(true);
+    }
+
+    @Test
+    public void testRestoresStateCorrectlyWhenPreviousStateSetStopped() throws Exception {
+        ProfileStatusChange psc = mock(ProfileStatusChange.class);
+        when(psc.isStarted()).thenReturn(false);
+        // should ignore the DISABLED argument because a previous state (psc) is available
+        VmProfileController.SaveState state = new VmProfileController.SaveState(psc, false, VmProfileView.ProfilingState.DISABLED);
+        doRestoreTest(state);
+
+        verify(view, atLeastOnce()).setProfilingState(VmProfileView.ProfilingState.STOPPED);
+        verify(view, atLeastOnce()).setViewControlsEnabled(true);
+    }
+
+    @Test
+    public void testRestoresStateCorrectlyWhenPreviousStateUnsetProfilingDisabled() throws Exception {
+        // no ProfilingStatusChange to rely on so fall back on the ProfilingState
+        VmProfileController.SaveState state = new VmProfileController.SaveState(null, false, VmProfileView.ProfilingState.DISABLED);
+        doRestoreTest(state);
+
+        verify(view, atLeastOnce()).setProfilingState(VmProfileView.ProfilingState.DISABLED);
+        verify(view, atLeastOnce()).setViewControlsEnabled(true);
+    }
+
+    @Test
+    public void testRestoresStateCorrectlyWhenPreviousStateUnsetProfilingStarting() throws Exception {
+        // no ProfilingStatusChange to rely on so fall back on the ProfilingState
+        VmProfileController.SaveState state = new VmProfileController.SaveState(null, false, VmProfileView.ProfilingState.STARTING);
+        doRestoreTest(state);
+
+        verify(view, atLeastOnce()).setProfilingState(VmProfileView.ProfilingState.STARTING);
+        verify(view, atLeastOnce()).setViewControlsEnabled(true);
+    }
+
+    @Test
+    public void testRestoresStateCorrectlyWhenPreviousStateUnsetProfilingStarted() throws Exception {
+        // no ProfilingStatusChange to rely on so fall back on the ProfilingState
+        VmProfileController.SaveState state = new VmProfileController.SaveState(null, false, VmProfileView.ProfilingState.STARTED);
+        doRestoreTest(state);
+
+        verify(view, atLeastOnce()).setProfilingState(VmProfileView.ProfilingState.STARTED);
+        verify(view, atLeastOnce()).setViewControlsEnabled(true);
+    }
+
+    @Test
+    public void testRestoresStateCorrectlyWhenPreviousStateUnsetProfilingStopping() throws Exception {
+        // no ProfilingStatusChange to rely on so fall back on the ProfilingState
+        VmProfileController.SaveState state = new VmProfileController.SaveState(null, false, VmProfileView.ProfilingState.STOPPING);
+        doRestoreTest(state);
+
+        verify(view, atLeastOnce()).setProfilingState(VmProfileView.ProfilingState.STOPPING);
+        verify(view, atLeastOnce()).setViewControlsEnabled(true);
+    }
+
+    @Test
+    public void testRestoresStateCorrectlyWhenPreviousStateUnsetProfilingStopped() throws Exception {
+        // no ProfilingStatusChange to rely on so fall back on the ProfilingState
+        VmProfileController.SaveState state = new VmProfileController.SaveState(null, false, VmProfileView.ProfilingState.STOPPED);
+        doRestoreTest(state);
+
+        verify(view, atLeastOnce()).setProfilingState(VmProfileView.ProfilingState.STOPPED);
+        verify(view, atLeastOnce()).setViewControlsEnabled(true);
+    }
+
+    private void doRestoreTest(VmProfileController.SaveState saveState) {
+        Map<VmRef, VmProfileController.SaveState> map = new HashMap<>();
+
+        map.put(vm, saveState);
+        when(appCache.getAttribute(any(String.class))).thenReturn(map);
+        controller = createController();
+
+        ArgumentCaptor<ActionListener> actionListenerCaptor = ArgumentCaptor.forClass(ActionListener.class);
+        verify(view).addActionListener(actionListenerCaptor.capture());
+
+        actionListenerCaptor.getValue().actionPerformed(new ActionEvent(view, Action.VISIBLE));
+
+        verify(appCache, atLeastOnce()).getAttribute(VmProfileController.STATE_MAP_KEY);
+    }
+
     private VmProfileController createController() {
         return new VmProfileController(appService, notifier, agentInfoDao, vmInfoDao, profileDao, queue, clock, view, vm);
     }