# HG changeset patch # User Elliott Baron # Date 1375380300 14400 # Node ID 9cb09d6aeddbe6f1d95f2887b13ba1fbd22d0fc8 # Parent 74c7131a0bb99eca66ef5b7dcfdd9dcfb6c9d39d Support web storage in JMX plugin This commit fixes the JMX plugin to work with web storage. Aside from needing a command channel action and associated role, there were a few other issues this commit addresses. Most importantly, I noticed the GUI would hang when trying to enable JMX notifications without proper permissions after the third attempt. This seems to be due to another issue where the web service stops responding to the client's requests. I traced the hang down to waiting on the network for AgentInfoDAO.getAgentInformation. I've fixed the GUI hang by making the toggle notifications action asynchronous, running in an executor provided by ApplicationService. Another fix in this commit is provided feedback to the user when authentication fails when trying to toggle JMX notifications. This is done using a dialog box similar to the thread plugin. One final small fix is to add the jmx-common bundle to the webservice command in thermostat-plugin.xml. Without this, the webservice cannot find the JMX storage entities it needs. Reviewed-by: vanaltj Review-thread: http://icedtea.classpath.org/pipermail/thermostat/2013-July/007712.html diff -r 74c7131a0bb9 -r 9cb09d6aeddb vm-jmx/client-core/src/main/java/com/redhat/thermostat/vm/jmx/client/core/JmxNotificationsView.java --- a/vm-jmx/client-core/src/main/java/com/redhat/thermostat/vm/jmx/client/core/JmxNotificationsView.java Thu Aug 01 13:40:31 2013 -0400 +++ b/vm-jmx/client-core/src/main/java/com/redhat/thermostat/vm/jmx/client/core/JmxNotificationsView.java Thu Aug 01 14:05:00 2013 -0400 @@ -39,6 +39,7 @@ import com.redhat.thermostat.client.core.views.BasicView; import com.redhat.thermostat.client.core.views.UIComponent; import com.redhat.thermostat.common.ActionListener; +import com.redhat.thermostat.shared.locale.LocalizedString; import com.redhat.thermostat.vm.jmx.common.JmxNotification; public abstract class JmxNotificationsView extends BasicView implements UIComponent { @@ -53,5 +54,6 @@ public abstract void setNotificationsEnabled(boolean enabled); public abstract void clearNotifications(); public abstract void addNotification(JmxNotification data); + public abstract void displayWarning(LocalizedString warning); } diff -r 74c7131a0bb9 -r 9cb09d6aeddb vm-jmx/client-core/src/main/java/com/redhat/thermostat/vm/jmx/client/core/LocaleResources.java --- a/vm-jmx/client-core/src/main/java/com/redhat/thermostat/vm/jmx/client/core/LocaleResources.java Thu Aug 01 13:40:31 2013 -0400 +++ b/vm-jmx/client-core/src/main/java/com/redhat/thermostat/vm/jmx/client/core/LocaleResources.java Thu Aug 01 14:05:00 2013 -0400 @@ -49,6 +49,9 @@ NOTIFICATIONS_ENABLE_DESCRIPTION, NOTIFICATIONS_DISABLE, NOTIFICATIONS_DISABLE_DESCRIPTION, + + NOTIFICATIONS_CANNOT_ENABLE, + NOTIFICATIONS_CANNOT_DISABLE, ; static final String RESOURCE_BUNDLE = "com.redhat.thermostat.vm.jmx.client.core.strings"; diff -r 74c7131a0bb9 -r 9cb09d6aeddb vm-jmx/client-core/src/main/java/com/redhat/thermostat/vm/jmx/client/core/internal/Activator.java --- a/vm-jmx/client-core/src/main/java/com/redhat/thermostat/vm/jmx/client/core/internal/Activator.java Thu Aug 01 13:40:31 2013 -0400 +++ b/vm-jmx/client-core/src/main/java/com/redhat/thermostat/vm/jmx/client/core/internal/Activator.java Thu Aug 01 14:05:00 2013 -0400 @@ -65,6 +65,7 @@ public void start(final BundleContext context) throws Exception { final Class[] deps = new Class[] { + ApplicationService.class, AgentInfoDAO.class, JmxNotificationDAO.class, ApplicationService.class, @@ -75,13 +76,14 @@ depsTracker = new MultipleServiceTracker(context, deps, new Action() { @Override public void dependenciesAvailable(Map services) { + ApplicationService appSvc = (ApplicationService) services.get(ApplicationService.class.getName()); AgentInfoDAO agentDao = (AgentInfoDAO) services.get(AgentInfoDAO.class.getName()); JmxNotificationDAO notificationDao = (JmxNotificationDAO) services.get(JmxNotificationDAO.class.getName()); JmxNotificationsViewProvider viewProvider = (JmxNotificationsViewProvider) services.get(JmxNotificationsViewProvider.class.getName()); TimerFactory tf = ((ApplicationService) services.get(ApplicationService.class.getName())).getTimerFactory(); RequestQueue queue = (RequestQueue) services.get(RequestQueue.class.getName()); - JmxNotificationsViewServiceImpl notificationsView = new JmxNotificationsViewServiceImpl(agentDao, notificationDao, queue, tf, viewProvider); + JmxNotificationsViewServiceImpl notificationsView = new JmxNotificationsViewServiceImpl(appSvc, agentDao, notificationDao, queue, tf, viewProvider); Dictionary props = new Hashtable(); props.put(Constants.GENERIC_SERVICE_CLASSNAME, VmRef.class.getName()); diff -r 74c7131a0bb9 -r 9cb09d6aeddb vm-jmx/client-core/src/main/java/com/redhat/thermostat/vm/jmx/client/core/internal/JmxNotificationsViewController.java --- a/vm-jmx/client-core/src/main/java/com/redhat/thermostat/vm/jmx/client/core/internal/JmxNotificationsViewController.java Thu Aug 01 13:40:31 2013 -0400 +++ b/vm-jmx/client-core/src/main/java/com/redhat/thermostat/vm/jmx/client/core/internal/JmxNotificationsViewController.java Thu Aug 01 14:05:00 2013 -0400 @@ -47,6 +47,7 @@ import com.redhat.thermostat.client.core.views.UIComponent; import com.redhat.thermostat.common.ActionEvent; import com.redhat.thermostat.common.ActionListener; +import com.redhat.thermostat.common.ApplicationService; import com.redhat.thermostat.common.Timer; import com.redhat.thermostat.common.TimerFactory; import com.redhat.thermostat.shared.locale.LocalizedString; @@ -66,21 +67,57 @@ private final JmxNotificationsView view; private final Timer timer; private final JmxNotificationDAO dao; - private final AgentInfoDAO agentDAO; private final VmRef vm; private final Translate t = LocaleResources.createLocalizer(); + private final JmxToggleNotificationRequest toggleReq; private final AtomicBoolean notificationsEnabled = new AtomicBoolean(false); - - public JmxNotificationsViewController(AgentInfoDAO agent, JmxNotificationDAO notification, - TimerFactory timerFactory, final RequestQueue queue, - JmxNotificationsViewProvider viewProvider, - VmRef vmId) { + + public JmxNotificationsViewController(ApplicationService appSvc, + AgentInfoDAO agent, JmxNotificationDAO notification, + TimerFactory timerFactory, RequestQueue queue, + JmxNotificationsViewProvider viewProvider, VmRef vmId) { + this(appSvc, agent, notification, timerFactory, queue, viewProvider, vmId, + new JmxToggleNotificationRequestFactory()); + } + + JmxNotificationsViewController(final ApplicationService appSvc, + AgentInfoDAO agent, JmxNotificationDAO notification, + TimerFactory timerFactory, RequestQueue queue, + JmxNotificationsViewProvider viewProvider, VmRef vmId, + JmxToggleNotificationRequestFactory reqFactory) { this.dao = notification; - this.agentDAO = agent; this.view = viewProvider.createView(); this.timer = timerFactory.createTimer(); this.vm = vmId; + + // Callbacks for toggle notifications + final Runnable successAction = new Runnable() { + + @Override + public void run() { + notificationsEnabled.set(!notificationsEnabled.get()); + view.setNotificationsEnabled(notificationsEnabled.get()); + } + }; + + final Runnable failureAction = new Runnable() { + + @Override + public void run() { + LocalizedString warning; + if (notificationsEnabled.get()) { + warning = t.localize(LocaleResources.NOTIFICATIONS_CANNOT_DISABLE); + } + else { + warning = t.localize(LocaleResources.NOTIFICATIONS_CANNOT_ENABLE); + } + view.displayWarning(warning); + view.setNotificationsEnabled(notificationsEnabled.get()); + } + }; + + this.toggleReq = reqFactory.createRequest(queue, agent, successAction, failureAction); initializeTimer(); @@ -103,11 +140,13 @@ @Override public void actionPerformed(ActionEvent actionEvent) { if (actionEvent.getActionId() == NotificationAction.TOGGLE_NOTIFICATIONS) { - notificationsEnabled.set(!notificationsEnabled.get()); - - new JmxToggleNotificationRequest(queue).sendEnableNotificationsRequestToAgent(vm, agentDAO, notificationsEnabled.get(), null); - - view.setNotificationsEnabled(notificationsEnabled.get()); + // This can block on network, do outside EDT/UI thread + appSvc.getApplicationExecutor().execute(new Runnable() { + @Override + public void run() { + toggleReq.sendEnableNotificationsRequestToAgent(vm, !notificationsEnabled.get()); + } + }); } } }); @@ -156,5 +195,14 @@ public LocalizedString getLocalizedName() { return t.localize(LocaleResources.NOTIFICATIONS_TITLE); } + + static class JmxToggleNotificationRequestFactory { + + JmxToggleNotificationRequest createRequest(RequestQueue queue, AgentInfoDAO agentDAO, + Runnable successAction, Runnable failureAction) { + return new JmxToggleNotificationRequest(queue, agentDAO, successAction, failureAction); + } + + } } diff -r 74c7131a0bb9 -r 9cb09d6aeddb vm-jmx/client-core/src/main/java/com/redhat/thermostat/vm/jmx/client/core/internal/JmxNotificationsViewServiceImpl.java --- a/vm-jmx/client-core/src/main/java/com/redhat/thermostat/vm/jmx/client/core/internal/JmxNotificationsViewServiceImpl.java Thu Aug 01 13:40:31 2013 -0400 +++ b/vm-jmx/client-core/src/main/java/com/redhat/thermostat/vm/jmx/client/core/internal/JmxNotificationsViewServiceImpl.java Thu Aug 01 14:05:00 2013 -0400 @@ -40,6 +40,7 @@ import com.redhat.thermostat.client.core.Filter; import com.redhat.thermostat.client.core.NameMatchingRefFilter; import com.redhat.thermostat.client.core.controllers.InformationServiceController; +import com.redhat.thermostat.common.ApplicationService; import com.redhat.thermostat.common.Ordered; import com.redhat.thermostat.common.TimerFactory; import com.redhat.thermostat.storage.core.VmRef; @@ -52,14 +53,18 @@ private final Filter FILTER = new NameMatchingRefFilter<>(); + private final ApplicationService appSvc; private final JmxNotificationsViewProvider viewProvider; private final JmxNotificationDAO notificationDao; private final AgentInfoDAO agentDao; private final RequestQueue requestQueue; private final TimerFactory timerFactory; - public JmxNotificationsViewServiceImpl(AgentInfoDAO agentDao, JmxNotificationDAO notificationDao, - RequestQueue requestQueue, TimerFactory timerFactory, JmxNotificationsViewProvider viewProvider) { + public JmxNotificationsViewServiceImpl(ApplicationService appSvc, + AgentInfoDAO agentDao, JmxNotificationDAO notificationDao, + RequestQueue requestQueue, TimerFactory timerFactory, + JmxNotificationsViewProvider viewProvider) { + this.appSvc = appSvc; this.agentDao = agentDao; this.notificationDao = notificationDao; this.timerFactory = timerFactory; @@ -74,7 +79,8 @@ @Override public InformationServiceController getInformationServiceController(VmRef ref) { - return new JmxNotificationsViewController(agentDao, notificationDao, timerFactory, requestQueue, viewProvider, ref); + return new JmxNotificationsViewController(appSvc, agentDao, notificationDao, timerFactory, + requestQueue, viewProvider, ref); } @Override diff -r 74c7131a0bb9 -r 9cb09d6aeddb vm-jmx/client-core/src/main/java/com/redhat/thermostat/vm/jmx/client/core/internal/JmxToggleNotificationRequest.java --- a/vm-jmx/client-core/src/main/java/com/redhat/thermostat/vm/jmx/client/core/internal/JmxToggleNotificationRequest.java Thu Aug 01 13:40:31 2013 -0400 +++ b/vm-jmx/client-core/src/main/java/com/redhat/thermostat/vm/jmx/client/core/internal/JmxToggleNotificationRequest.java Thu Aug 01 14:05:00 2013 -0400 @@ -40,43 +40,89 @@ import com.redhat.thermostat.client.command.RequestQueue; import com.redhat.thermostat.common.command.Request; +import com.redhat.thermostat.common.command.Request.RequestType; import com.redhat.thermostat.common.command.RequestResponseListener; -import com.redhat.thermostat.common.command.Request.RequestType; +import com.redhat.thermostat.common.command.Response; import com.redhat.thermostat.storage.core.HostRef; import com.redhat.thermostat.storage.core.VmRef; import com.redhat.thermostat.storage.dao.AgentInfoDAO; import com.redhat.thermostat.vm.jmx.common.JmxCommand; public class JmxToggleNotificationRequest { + + static final String CMD_CHANNEL_ACTION_NAME = "jmx-toggle-notifications"; private RequestQueue queue; + private AgentInfoDAO agentDAO; + private Runnable successAction; + private Runnable failureAction; + private JmxToggleResponseListenerFactory factory; - public JmxToggleNotificationRequest(RequestQueue queue) { + public JmxToggleNotificationRequest(RequestQueue queue, AgentInfoDAO agentDAO, + Runnable successAction, Runnable failureAction) { + this(queue, agentDAO, successAction, failureAction, new JmxToggleResponseListenerFactory()); + } + + JmxToggleNotificationRequest(RequestQueue queue, AgentInfoDAO agentDAO, Runnable successAction, + Runnable failureAction, JmxToggleResponseListenerFactory factory) { this.queue = queue; + this.agentDAO = agentDAO; + this.successAction = successAction; + this.failureAction = failureAction; + this.factory = factory; } - public void sendEnableNotificationsRequestToAgent(VmRef vm, AgentInfoDAO agentDAO, boolean enable, RequestResponseListener responseListener) { - + public void sendEnableNotificationsRequestToAgent(VmRef vm, boolean enable) { HostRef targetHostRef = vm.getHostRef(); String address = agentDAO.getAgentInformation(targetHostRef).getConfigListenAddress(); String[] host = address.split(":"); InetSocketAddress target = new InetSocketAddress(host[0], Integer.parseInt(host[1])); - Request gcRequest = createRequest(target); + Request req = new Request(RequestType.RESPONSE_EXPECTED, target); + + req.setReceiver(JmxCommand.RECEIVER); - gcRequest.setReceiver(JmxCommand.RECEIVER); + req.setParameter(Request.ACTION, CMD_CHANNEL_ACTION_NAME); + req.setParameter(JmxCommand.class.getName(), enable ? JmxCommand.ENABLE_JMX_NOTIFICATIONS.name() : JmxCommand.DISABLE_JMX_NOTIFICATIONS.name()); + req.setParameter(JmxCommand.VM_PID, String.valueOf(vm.getPid())); - gcRequest.setParameter(JmxCommand.class.getName(), enable ? JmxCommand.ENABLE_JMX_NOTIFICATIONS.name() : JmxCommand.DISABLE_JMX_NOTIFICATIONS.name()); - gcRequest.setParameter(JmxCommand.VM_PID, String.valueOf(vm.getPid())); + JmxToggleResponseListener listener = factory.createListener(successAction, failureAction); + req.addListener(listener); - gcRequest.addListener(responseListener); - - queue.putRequest(gcRequest); + queue.putRequest(req); } + + static class JmxToggleResponseListener implements RequestResponseListener { + + private Runnable successAction; + private Runnable failureAction; + + public JmxToggleResponseListener(Runnable successAction, Runnable failureAction) { + this.successAction = successAction; + this.failureAction = failureAction; + } - // for testing - Request createRequest(InetSocketAddress target) { - return new Request(RequestType.RESPONSE_EXPECTED, target); + @Override + public void fireComplete(Request request, Response response) { + switch (response.getType()) { + case OK: + successAction.run(); + break; + default: + failureAction.run(); + break; + } + } + + } + + static class JmxToggleResponseListenerFactory { + + JmxToggleResponseListener createListener(Runnable successAction, + Runnable failureAction) { + return new JmxToggleResponseListener(successAction, failureAction); + } + } } diff -r 74c7131a0bb9 -r 9cb09d6aeddb vm-jmx/client-core/src/main/resources/com/redhat/thermostat/vm/jmx/client/core/strings.properties --- a/vm-jmx/client-core/src/main/resources/com/redhat/thermostat/vm/jmx/client/core/strings.properties Thu Aug 01 13:40:31 2013 -0400 +++ b/vm-jmx/client-core/src/main/resources/com/redhat/thermostat/vm/jmx/client/core/strings.properties Thu Aug 01 14:05:00 2013 -0400 @@ -7,3 +7,6 @@ NOTIFICATIONS_ENABLE_DESCRIPTION = Start Monitoring Jmx Notifications NOTIFICATIONS_DISABLE = Stop Monitoring NOTIFICATIONS_DISABLE_DESCRIPTION = Stop Monitoring Jmx Notifications + +NOTIFICATIONS_CANNOT_ENABLE = Could not start monitoring JMX Notifications +NOTIFICATIONS_CANNOT_DISABLE = Could not stop monitoring JMX Notifications diff -r 74c7131a0bb9 -r 9cb09d6aeddb vm-jmx/client-core/src/test/java/com/redhat/thermostat/vm/jmx/client/core/internal/ActivatorTest.java --- a/vm-jmx/client-core/src/test/java/com/redhat/thermostat/vm/jmx/client/core/internal/ActivatorTest.java Thu Aug 01 13:40:31 2013 -0400 +++ b/vm-jmx/client-core/src/test/java/com/redhat/thermostat/vm/jmx/client/core/internal/ActivatorTest.java Thu Aug 01 14:05:00 2013 -0400 @@ -56,6 +56,7 @@ public void testActivatorRegistersService() throws Exception { StubBundleContext bundleContext = new StubBundleContext(); + bundleContext.registerService(ApplicationService.class, mock(ApplicationService.class), null); bundleContext.registerService(AgentInfoDAO.class, mock(AgentInfoDAO.class), null); bundleContext.registerService(JmxNotificationDAO.class, mock(JmxNotificationDAO.class), null); bundleContext.registerService(ApplicationService.class, mock(ApplicationService.class), null); diff -r 74c7131a0bb9 -r 9cb09d6aeddb vm-jmx/client-core/src/test/java/com/redhat/thermostat/vm/jmx/client/core/internal/JmxNotificationsViewControllerTest.java --- a/vm-jmx/client-core/src/test/java/com/redhat/thermostat/vm/jmx/client/core/internal/JmxNotificationsViewControllerTest.java Thu Aug 01 13:40:31 2013 -0400 +++ b/vm-jmx/client-core/src/test/java/com/redhat/thermostat/vm/jmx/client/core/internal/JmxNotificationsViewControllerTest.java Thu Aug 01 14:05:00 2013 -0400 @@ -37,40 +37,51 @@ package com.redhat.thermostat.vm.jmx.client.core.internal; import static org.junit.Assert.assertEquals; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import java.net.InetSocketAddress; import java.util.Arrays; +import java.util.concurrent.ExecutorService; import java.util.concurrent.TimeUnit; import org.junit.Before; import org.junit.Test; import org.mockito.ArgumentCaptor; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; import com.redhat.thermostat.client.command.RequestQueue; import com.redhat.thermostat.client.core.views.BasicView.Action; import com.redhat.thermostat.common.ActionEvent; import com.redhat.thermostat.common.ActionListener; +import com.redhat.thermostat.common.ApplicationService; import com.redhat.thermostat.common.Timer; import com.redhat.thermostat.common.Timer.SchedulingType; import com.redhat.thermostat.common.TimerFactory; -import com.redhat.thermostat.common.command.Request; +import com.redhat.thermostat.shared.locale.LocalizedString; +import com.redhat.thermostat.shared.locale.Translate; import com.redhat.thermostat.storage.core.HostRef; import com.redhat.thermostat.storage.core.VmRef; import com.redhat.thermostat.storage.dao.AgentInfoDAO; -import com.redhat.thermostat.storage.model.AgentInformation; import com.redhat.thermostat.vm.jmx.client.core.JmxNotificationsView; import com.redhat.thermostat.vm.jmx.client.core.JmxNotificationsView.NotificationAction; import com.redhat.thermostat.vm.jmx.client.core.JmxNotificationsViewProvider; +import com.redhat.thermostat.vm.jmx.client.core.LocaleResources; +import com.redhat.thermostat.vm.jmx.client.core.internal.JmxNotificationsViewController.JmxToggleNotificationRequestFactory; import com.redhat.thermostat.vm.jmx.common.JmxNotification; import com.redhat.thermostat.vm.jmx.common.JmxNotificationDAO; import com.redhat.thermostat.vm.jmx.common.JmxNotificationStatus; public class JmxNotificationsViewControllerTest { - private AgentInfoDAO agentDao; + private static final Translate translator = LocaleResources.createLocalizer(); + private JmxNotificationDAO notificationDao; private JmxNotificationsView view; private JmxNotificationsViewProvider viewProvider; @@ -80,10 +91,28 @@ private RequestQueue queue; private JmxNotificationsViewController controller; private HostRef host; + private JmxToggleNotificationRequest toggleReq; + + private Runnable successAction; + + private Runnable failureAction; @Before public void setUp() { - agentDao = mock(AgentInfoDAO.class); + ApplicationService appSvc = mock(ApplicationService.class); + ExecutorService execSvc = mock(ExecutorService.class); + when(appSvc.getApplicationExecutor()).thenReturn(execSvc); + // Run task immediately + doAnswer(new Answer() { + @Override + public Void answer(InvocationOnMock invocation) throws Throwable { + Runnable runnable = (Runnable) invocation.getArguments()[0]; + runnable.run(); + return null; + } + }).when(execSvc).execute(any(Runnable.class)); + + AgentInfoDAO agentDao = mock(AgentInfoDAO.class); notificationDao = mock(JmxNotificationDAO.class); queue = mock(RequestQueue.class); view = mock(JmxNotificationsView.class); @@ -96,8 +125,20 @@ host = mock(HostRef.class); vm = mock(VmRef.class); when(vm.getHostRef()).thenReturn(host); + + JmxToggleNotificationRequestFactory reqFactory = mock(JmxToggleNotificationRequestFactory.class); + toggleReq = mock(JmxToggleNotificationRequest.class); + when(reqFactory.createRequest(eq(queue), eq(agentDao), any(Runnable.class), + any(Runnable.class))).thenReturn(toggleReq); - controller = new JmxNotificationsViewController(agentDao, notificationDao, timerFactory, queue, viewProvider, vm); + controller = new JmxNotificationsViewController(appSvc, agentDao, notificationDao, timerFactory, + queue, viewProvider, vm, reqFactory); + ArgumentCaptor successCaptor = ArgumentCaptor.forClass(Runnable.class); + ArgumentCaptor failureCaptor = ArgumentCaptor.forClass(Runnable.class); + verify(reqFactory).createRequest(eq(queue), eq(agentDao), successCaptor.capture(), failureCaptor.capture()); + + successAction = successCaptor.getValue(); + failureAction = failureCaptor.getValue(); } @Test @@ -169,17 +210,91 @@ ArgumentCaptor listenerCaptor = ArgumentCaptor.forClass(ActionListener.class); verify(view).addNotificationActionListener(listenerCaptor.capture()); + answerSuccess(true); - AgentInformation agentInfo = mock(AgentInformation.class); - when(agentInfo.getConfigListenAddress()).thenReturn("example.com:0"); - when(agentDao.getAgentInformation(host)).thenReturn(agentInfo); + listenerCaptor.getValue().actionPerformed(new ActionEvent<>(view, NotificationAction.TOGGLE_NOTIFICATIONS)); + + verify(toggleReq).sendEnableNotificationsRequestToAgent(eq(vm), eq(true)); + verify(view).setNotificationsEnabled(true); + } + + @Test + public void enableNotificationsFails() { + ArgumentCaptor listenerCaptor = ArgumentCaptor.forClass(ActionListener.class); + + verify(view).addNotificationActionListener(listenerCaptor.capture()); + answerFailure(true); listenerCaptor.getValue().actionPerformed(new ActionEvent<>(view, NotificationAction.TOGGLE_NOTIFICATIONS)); - ArgumentCaptor requestCaptor = ArgumentCaptor.forClass(Request.class); - verify(queue).putRequest(requestCaptor.capture()); + verify(toggleReq).sendEnableNotificationsRequestToAgent(vm, true); + verify(view, never()).setNotificationsEnabled(true); + verify(view).setNotificationsEnabled(false); + + ArgumentCaptor warningCaptor = ArgumentCaptor.forClass(LocalizedString.class); + verify(view).displayWarning(warningCaptor.capture()); + assertEquals(translator.localize(LocaleResources.NOTIFICATIONS_CANNOT_ENABLE).getContents(), + warningCaptor.getValue().getContents()); + } + + @Test + public void disableNotificationsWhenViewFiresEvent() { + ArgumentCaptor listenerCaptor = ArgumentCaptor.forClass(ActionListener.class); + + verify(view).addNotificationActionListener(listenerCaptor.capture()); + answerSuccess(true); + answerSuccess(false); + + // Enable, then disable + listenerCaptor.getValue().actionPerformed(new ActionEvent<>(view, NotificationAction.TOGGLE_NOTIFICATIONS)); + listenerCaptor.getValue().actionPerformed(new ActionEvent<>(view, NotificationAction.TOGGLE_NOTIFICATIONS)); + + verify(toggleReq).sendEnableNotificationsRequestToAgent(vm, false); + verify(view).setNotificationsEnabled(false); + } + + private void answerSuccess(boolean enable) { + doAnswer(new Answer() { - Request req = requestCaptor.getValue(); - assertEquals(new InetSocketAddress("example.com", 0), req.getTarget()); + @Override + public Void answer(InvocationOnMock invocation) throws Throwable { + successAction.run(); + return null; + } + }).when(toggleReq).sendEnableNotificationsRequestToAgent(vm, enable); + } + + private void answerFailure(boolean enable) { + doAnswer(new Answer() { + + @Override + public Void answer(InvocationOnMock invocation) throws Throwable { + failureAction.run(); + return null; + } + }).when(toggleReq).sendEnableNotificationsRequestToAgent(vm, enable); } + + @Test + public void disableNotificationsFails() { + ArgumentCaptor listenerCaptor = ArgumentCaptor.forClass(ActionListener.class); + + verify(view).addNotificationActionListener(listenerCaptor.capture()); + answerSuccess(true); + answerFailure(false); + + listenerCaptor.getValue().actionPerformed(new ActionEvent<>(view, NotificationAction.TOGGLE_NOTIFICATIONS)); + listenerCaptor.getValue().actionPerformed(new ActionEvent<>(view, NotificationAction.TOGGLE_NOTIFICATIONS)); + + verify(toggleReq).sendEnableNotificationsRequestToAgent(vm, false); + verify(view, never()).setNotificationsEnabled(false); + verify(view, times(2)).setNotificationsEnabled(true); + + ArgumentCaptor warningCaptor = ArgumentCaptor.forClass(LocalizedString.class); + verify(view).displayWarning(warningCaptor.capture()); + assertEquals(translator.localize(LocaleResources.NOTIFICATIONS_CANNOT_DISABLE).getContents(), + warningCaptor.getValue().getContents()); + } + + } diff -r 74c7131a0bb9 -r 9cb09d6aeddb vm-jmx/client-core/src/test/java/com/redhat/thermostat/vm/jmx/client/core/internal/JmxNotificationsViewServiceImplTest.java --- a/vm-jmx/client-core/src/test/java/com/redhat/thermostat/vm/jmx/client/core/internal/JmxNotificationsViewServiceImplTest.java Thu Aug 01 13:40:31 2013 -0400 +++ b/vm-jmx/client-core/src/test/java/com/redhat/thermostat/vm/jmx/client/core/internal/JmxNotificationsViewServiceImplTest.java Thu Aug 01 14:05:00 2013 -0400 @@ -45,6 +45,7 @@ import org.junit.Test; import com.redhat.thermostat.client.command.RequestQueue; +import com.redhat.thermostat.common.ApplicationService; import com.redhat.thermostat.common.Ordered; import com.redhat.thermostat.common.Timer; import com.redhat.thermostat.common.TimerFactory; @@ -67,6 +68,7 @@ @Before public void setUp() { + ApplicationService appSvc = mock(ApplicationService.class); agentDao = mock(AgentInfoDAO.class); notificationDao = mock(JmxNotificationDAO.class); timer = mock(Timer.class); @@ -79,7 +81,8 @@ viewProvider = mock(JmxNotificationsViewProvider.class); when(viewProvider.createView()).thenReturn(view); - service = new JmxNotificationsViewServiceImpl(agentDao, notificationDao, queue, timerFactory, viewProvider); + service = new JmxNotificationsViewServiceImpl(appSvc, agentDao, notificationDao, + queue, timerFactory, viewProvider); } @Test diff -r 74c7131a0bb9 -r 9cb09d6aeddb vm-jmx/client-core/src/test/java/com/redhat/thermostat/vm/jmx/client/core/internal/JmxToggleNotificationRequestTest.java --- a/vm-jmx/client-core/src/test/java/com/redhat/thermostat/vm/jmx/client/core/internal/JmxToggleNotificationRequestTest.java Thu Aug 01 13:40:31 2013 -0400 +++ b/vm-jmx/client-core/src/test/java/com/redhat/thermostat/vm/jmx/client/core/internal/JmxToggleNotificationRequestTest.java Thu Aug 01 14:05:00 2013 -0400 @@ -37,23 +37,33 @@ package com.redhat.thermostat.vm.jmx.client.core.internal; import static org.junit.Assert.assertEquals; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import java.net.InetSocketAddress; +import java.util.Collection; import org.junit.Before; import org.junit.Test; import org.mockito.ArgumentCaptor; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; import com.redhat.thermostat.client.command.RequestQueue; import com.redhat.thermostat.common.command.Request; import com.redhat.thermostat.common.command.RequestResponseListener; +import com.redhat.thermostat.common.command.Response; +import com.redhat.thermostat.common.command.Response.ResponseType; import com.redhat.thermostat.storage.core.HostRef; import com.redhat.thermostat.storage.core.VmRef; import com.redhat.thermostat.storage.dao.AgentInfoDAO; import com.redhat.thermostat.storage.model.AgentInformation; +import com.redhat.thermostat.vm.jmx.client.core.internal.JmxToggleNotificationRequest.JmxToggleResponseListener; +import com.redhat.thermostat.vm.jmx.client.core.internal.JmxToggleNotificationRequest.JmxToggleResponseListenerFactory; import com.redhat.thermostat.vm.jmx.common.JmxCommand; public class JmxToggleNotificationRequestTest { @@ -66,8 +76,12 @@ private HostRef host; private VmRef vm; private AgentInfoDAO agentDAO; - private RequestResponseListener responseListener; private AgentInformation agentInfo; + private JmxToggleResponseListenerFactory factory; + private JmxToggleResponseListener listener; + private Runnable successAction; + private Runnable failureAction; + private JmxToggleNotificationRequest toggleReq; @Before public void setUp() { @@ -83,37 +97,101 @@ agentInfo = mock(AgentInformation.class); when(agentInfo.getConfigListenAddress()).thenReturn(HOST + ":" + PORT); when(agentDAO.getAgentInformation(host)).thenReturn(agentInfo); - - responseListener = mock(RequestResponseListener.class); + + factory = mock(JmxToggleResponseListenerFactory.class); + listener = mock(JmxToggleResponseListener.class); + successAction = mock(Runnable.class); + failureAction = mock(Runnable.class); + when(factory.createListener(successAction, failureAction)).thenReturn(listener); + + toggleReq = new JmxToggleNotificationRequest(queue, agentDAO, successAction, failureAction); } @Test - public void testEnableNotificationMessage() { - new JmxToggleNotificationRequest(queue).sendEnableNotificationsRequestToAgent(vm, agentDAO, true, responseListener); + public void testEnableNotificationsSuccess() { + answerSuccess(); + toggleReq.sendEnableNotificationsRequestToAgent(vm, true); + + verify(queue).putRequest(requestCaptor.capture()); + + Request req = requestCaptor.getValue(); + + assertEquals(new InetSocketAddress(HOST, PORT), req.getTarget()); + assertEquals(JmxToggleNotificationRequest.CMD_CHANNEL_ACTION_NAME, req.getParameter(Request.ACTION)); + assertEquals(JmxCommand.RECEIVER, req.getReceiver()); + assertEquals(String.valueOf(vm.getPid()), req.getParameter(JmxCommand.VM_PID)); + + assertEquals(JmxCommand.ENABLE_JMX_NOTIFICATIONS.name(), req.getParameter(JmxCommand.class.getName())); + + verify(successAction).run(); + verify(failureAction, never()).run(); + } + + @Test + public void testEnableNotificationsFailure() { + answerFailure(); + toggleReq.sendEnableNotificationsRequestToAgent(vm, true); + + verify(successAction, never()).run(); + verify(failureAction).run(); + } + + @Test + public void testDisableNotificationsSuccess() { + answerSuccess(); + toggleReq.sendEnableNotificationsRequestToAgent(vm, false); verify(queue).putRequest(requestCaptor.capture()); Request req = requestCaptor.getValue(); assertEquals(new InetSocketAddress(HOST, PORT), req.getTarget()); - assertEquals(JmxCommand.RECEIVER, req.getReceiver()); - assertEquals(String.valueOf(vm.getPid()), req.getParameter(JmxCommand.VM_PID)); - - assertEquals(JmxCommand.ENABLE_JMX_NOTIFICATIONS.name(), req.getParameter(JmxCommand.class.getName())); - } - - @Test - public void testDisableNotificationMessage() { - new JmxToggleNotificationRequest(queue).sendEnableNotificationsRequestToAgent(vm, agentDAO, false, responseListener); - - verify(queue).putRequest(requestCaptor.capture()); - - Request req = requestCaptor.getValue(); - - assertEquals(new InetSocketAddress(HOST, PORT), req.getTarget()); + assertEquals(JmxToggleNotificationRequest.CMD_CHANNEL_ACTION_NAME, req.getParameter(Request.ACTION)); assertEquals(JmxCommand.RECEIVER, req.getReceiver()); assertEquals(String.valueOf(vm.getPid()), req.getParameter(JmxCommand.VM_PID)); assertEquals(JmxCommand.DISABLE_JMX_NOTIFICATIONS.name(), req.getParameter(JmxCommand.class.getName())); + + verify(successAction).run(); + verify(failureAction, never()).run(); + } + + @Test + public void testDisableNotificationsFailure() { + answerFailure(); + toggleReq.sendEnableNotificationsRequestToAgent(vm, false); + + verify(successAction, never()).run(); + verify(failureAction).run(); + } + + private void answerSuccess() { + doAnswer(new Answer() { + @Override + public Void answer(InvocationOnMock invocation) throws Throwable { + // Fire complete OK + Request req = (Request) invocation.getArguments()[0]; + Collection listeners = req.getListeners(); + assertEquals(1, listeners.size()); + RequestResponseListener listener = listeners.iterator().next(); + listener.fireComplete(req, new Response(ResponseType.OK)); + return null; + } + }).when(queue).putRequest(any(Request.class)); + } + + private void answerFailure() { + doAnswer(new Answer() { + @Override + public Void answer(InvocationOnMock invocation) throws Throwable { + // Fire complete ERROR + Request req = (Request) invocation.getArguments()[0]; + Collection listeners = req.getListeners(); + assertEquals(1, listeners.size()); + RequestResponseListener listener = listeners.iterator().next(); + listener.fireComplete(req, new Response(ResponseType.ERROR)); + return null; + } + }).when(queue).putRequest(any(Request.class)); } } diff -r 74c7131a0bb9 -r 9cb09d6aeddb vm-jmx/client-swing/src/main/java/com/redhat/thermostat/vm/jmx/client/swing/internal/JmxNotificationsSwingView.java --- a/vm-jmx/client-swing/src/main/java/com/redhat/thermostat/vm/jmx/client/swing/internal/JmxNotificationsSwingView.java Thu Aug 01 13:40:31 2013 -0400 +++ b/vm-jmx/client-swing/src/main/java/com/redhat/thermostat/vm/jmx/client/swing/internal/JmxNotificationsSwingView.java Thu Aug 01 14:05:00 2013 -0400 @@ -47,6 +47,7 @@ import javax.swing.ButtonModel; import javax.swing.DefaultListModel; import javax.swing.JList; +import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.SwingUtilities; @@ -61,6 +62,7 @@ import com.redhat.thermostat.client.swing.components.HeaderPanel; import com.redhat.thermostat.common.ActionEvent; import com.redhat.thermostat.common.ActionListener; +import com.redhat.thermostat.shared.locale.LocalizedString; import com.redhat.thermostat.shared.locale.Translate; import com.redhat.thermostat.vm.jmx.client.core.JmxNotificationsView; import com.redhat.thermostat.vm.jmx.client.core.LocaleResources; @@ -190,4 +192,14 @@ return visiblePanel; } + @Override + public void displayWarning(final LocalizedString warning) { + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + JOptionPane.showMessageDialog(visiblePanel.getParent(), warning.getContents(), "", JOptionPane.WARNING_MESSAGE); + } + }); + } + } diff -r 74c7131a0bb9 -r 9cb09d6aeddb vm-jmx/distribution/thermostat-plugin.xml --- a/vm-jmx/distribution/thermostat-plugin.xml Thu Aug 01 13:40:31 2013 -0400 +++ b/vm-jmx/distribution/thermostat-plugin.xml Thu Aug 01 14:05:00 2013 -0400 @@ -55,5 +55,12 @@ com.redhat.thermostat.vm.jmx.agent${project.version} + + + webservice + + com.redhat.thermostat.vm.jmx.common${project.version} + +