Mercurial > hg > release > thermostat-2.0
changeset 2528:3c990cdff9ad
Implement jmx-client-cli
Reviewed-by: omajid
Review-thread: http://icedtea.classpath.org/pipermail/thermostat/2016-November/021503.html
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vm-jmx/client-cli/pom.xml Fri Oct 28 15:24:30 2016 -0400 @@ -0,0 +1,137 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + + Copyright 2012-2016 Red Hat, Inc. + + This file is part of Thermostat. + + Thermostat is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, or (at your + option) any later version. + + Thermostat is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Thermostat; see the file COPYING. If not see + <http://www.gnu.org/licenses/>. + + Linking this code with other modules is making a combined work + based on this code. Thus, the terms and conditions of the GNU + General Public License cover the whole combination. + + As a special exception, the copyright holders of this code give + you permission to link this code with independent modules to + produce an executable, regardless of the license terms of these + independent modules, and to copy and distribute the resulting + executable under terms of your choice, provided that you also + meet, for each linked independent module, the terms and conditions + of the license of that module. An independent module is a module + which is not derived from or based on this code. If you modify + this code, you may extend this exception to your version of the + library, but you are not obligated to do so. If you do not wish + to do so, delete this exception statement from your version. + +--> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + <parent> + <artifactId>thermostat-vm-jmx</artifactId> + <groupId>com.redhat.thermostat</groupId> + <version>1.99.12-SNAPSHOT</version> + </parent> + <artifactId>thermostat-vm-jmx-client-cli</artifactId> + <packaging>bundle</packaging> + <name>Thermostat VM JMX CLI plugin</name> + <build> + <plugins> + <plugin> + <groupId>org.apache.felix</groupId> + <artifactId>maven-bundle-plugin</artifactId> + <extensions>true</extensions> + <configuration> + <instructions> + <Bundle-Vendor>Red Hat, Inc.</Bundle-Vendor> + <Bundle-SymbolicName>com.redhat.thermostat.vm.jmx.client.cli</Bundle-SymbolicName> + <Bundle-Activator>com.redhat.thermostat.vm.jmx.client.cli.internal.Activator</Bundle-Activator> + <Export-Package> + com.redhat.thermostat.vm.jmx.client.cli + </Export-Package> + <Private-Package> + com.redhat.thermostat.vm.jmx.client.cli.internal, + com.redhat.thermostat.vm.jmx.client.cli.locale, + </Private-Package> + <!-- Do not autogenerate uses clauses in Manifests --> + <_nouses>true</_nouses> + </instructions> + </configuration> + </plugin> + </plugins> + </build> + <dependencies> + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.mockito</groupId> + <artifactId>mockito-core</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>com.redhat.thermostat</groupId> + <artifactId>thermostat-common-test</artifactId> + <version>${project.version}</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.osgi</groupId> + <artifactId>org.osgi.core</artifactId> + <scope>provided</scope> + </dependency> + <dependency> + <groupId>org.osgi</groupId> + <artifactId>org.osgi.compendium</artifactId> + <scope>provided</scope> + </dependency> + <dependency> + <groupId>com.redhat.thermostat</groupId> + <artifactId>thermostat-common-core</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>com.redhat.thermostat</groupId> + <artifactId>thermostat-storage-core</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>com.redhat.thermostat</groupId> + <artifactId>thermostat-client-command</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>com.redhat.thermostat</groupId> + <artifactId>thermostat-client-cli</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>com.redhat.thermostat</groupId> + <artifactId>thermostat-client-core</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>com.redhat.thermostat</groupId> + <artifactId>thermostat-vm-jmx-common</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>com.redhat.thermostat</groupId> + <artifactId>thermostat-vm-jmx-client-core</artifactId> + <version>${project.version}</version> + </dependency> + </dependencies> +</project>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vm-jmx/client-cli/src/main/java/com/redhat/thermostat/vm/jmx/client/cli/NotificationsCommand.java Fri Oct 28 15:24:30 2016 -0400 @@ -0,0 +1,358 @@ +/* + * Copyright 2012-2016 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * <http://www.gnu.org/licenses/>. + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.vm.jmx.client.cli; + +import com.redhat.thermostat.client.cli.VmArgument; +import com.redhat.thermostat.client.command.RequestQueue; +import com.redhat.thermostat.common.ApplicationService; +import com.redhat.thermostat.common.Clock; +import com.redhat.thermostat.common.SystemClock; +import com.redhat.thermostat.common.Timer; +import com.redhat.thermostat.common.TimerFactory; +import com.redhat.thermostat.common.Timers; +import com.redhat.thermostat.common.cli.AbstractCompleterCommand; +import com.redhat.thermostat.common.cli.Arguments; +import com.redhat.thermostat.common.cli.CommandContext; +import com.redhat.thermostat.common.cli.CommandException; +import com.redhat.thermostat.common.cli.DependencyServices; +import com.redhat.thermostat.shared.locale.LocalizedString; +import com.redhat.thermostat.shared.locale.Translate; +import com.redhat.thermostat.storage.core.AgentId; +import com.redhat.thermostat.storage.core.HostRef; +import com.redhat.thermostat.storage.core.VmId; +import com.redhat.thermostat.storage.core.VmRef; +import com.redhat.thermostat.storage.dao.AgentInfoDAO; +import com.redhat.thermostat.storage.dao.HostInfoDAO; +import com.redhat.thermostat.storage.dao.VmInfoDAO; +import com.redhat.thermostat.storage.model.HostInfo; +import com.redhat.thermostat.storage.model.VmInfo; +import com.redhat.thermostat.vm.jmx.client.cli.internal.NotificationsSinceParser; +import com.redhat.thermostat.vm.jmx.client.cli.locale.LocaleResources; +import com.redhat.thermostat.vm.jmx.client.core.JmxToggleNotificationRequest; +import com.redhat.thermostat.vm.jmx.common.JmxNotification; +import com.redhat.thermostat.vm.jmx.common.JmxNotificationDAO; +import com.redhat.thermostat.vm.jmx.common.JmxNotificationStatus; + +import java.io.IOException; +import java.util.Date; +import java.util.List; +import java.util.concurrent.CountDownLatch; + +public class NotificationsCommand extends AbstractCompleterCommand { + + public static final String COMMAND_NAME = "notifications"; + + private static final String STATUS_SUBCOMMAND = "status"; + private static final String SHOW_SUBCOMMAND = "show"; + private static final String ENABLE_SUBCOMMAND = "enable"; + private static final String DISABLE_SUBCOMMAND = "disable"; + private static final String FOLLOW_SUBCOMMAND = "follow"; + + private static final String ENABLE_FOLLOW_OPTION = "follow"; + private static final String SHOW_SINCE_OPTION = "since"; + + // in milliseconds + private static final long FOLLOW_LOOP_INPUT_PERIOD = 500L; + private static final long ENABLE_TO_FOLLOW_TRANSITION_DELAY = 500L; + + private static final Translate<LocaleResources> t = LocaleResources.createLocalizer(); + + private static final NotificationsSinceParser sinceParser = new NotificationsSinceParser(new SystemClock()); + + private final DependencyServices dependencyServices = new DependencyServices(); + + @Override + public void run(CommandContext ctx) throws CommandException { + Arguments args = ctx.getArguments(); + + if (args.getNonOptionArguments().size() != 1) { + throw new CommandException(t.localize(LocaleResources.EXPECTED_ONE_NONOPTION_ARG)); + } + String subcommand = args.getNonOptionArguments().get(0); + + ApplicationService applicationService = getService(ApplicationService.class); + Clock clock = getService(Clock.class); + RequestQueue queue = getService(RequestQueue.class); + HostInfoDAO hostInfoDAO = getService(HostInfoDAO.class); + AgentInfoDAO agentInfoDAO = getService(AgentInfoDAO.class); + VmInfoDAO vmInfoDAO = getService(VmInfoDAO.class); + JmxNotificationDAO jmxNotificationDAO = getService(JmxNotificationDAO.class); + + VmRef vmRef = getVmRef(args, vmInfoDAO, hostInfoDAO); + + switch (subcommand) { + case STATUS_SUBCOMMAND: + handleStatusRequest(ctx, jmxNotificationDAO, vmRef); + break; + case DISABLE_SUBCOMMAND: + handleJmxStatusChange(subcommand, ctx, queue, jmxNotificationDAO, agentInfoDAO, vmRef); + break; + case ENABLE_SUBCOMMAND: + handleJmxStatusChange(subcommand, ctx, queue, jmxNotificationDAO, agentInfoDAO, vmRef); + if (args.hasArgument(ENABLE_FOLLOW_OPTION)) { + try { + Thread.sleep(ENABLE_TO_FOLLOW_TRANSITION_DELAY); + } catch (InterruptedException e) { + break; + } + // fall through to FOLLOW_SUBCOMMAND + } else { + break; + } + case FOLLOW_SUBCOMMAND: + if (!isMonitoringEnabled(jmxNotificationDAO, vmRef)) { + ctx.getConsole().getOutput().println(t.localize(LocaleResources.JMX_NOTIFICATION_MONITORING_FOLLOW_NOT_ENABLED).getContents()); + break; + } + ctx.getConsole().getOutput().println(t.localize(LocaleResources.EXIT_FOLLOW_MODE_HINT).getContents()); + followNotificationsLoop(ctx, clock, applicationService.getTimerFactory(), jmxNotificationDAO, vmRef); + break; + case SHOW_SUBCOMMAND: + printNotifications(ctx, jmxNotificationDAO, vmRef, parseSinceOption(args)); + break; + default: + throw new CommandException(t.localize(LocaleResources.UNRECOGNIZED_SUBCOMMAND, subcommand)); + } + } + + private <T> T getService(Class<T> klazz) throws CommandException { + T service = dependencyServices.getService(klazz); + requireNonNull(service, t.localize(LocaleResources.MISSING_REQUIRED_SERVICE)); + return service; + } + + private VmRef getVmRef(Arguments arguments, VmInfoDAO vmInfoDAO, HostInfoDAO hostInfoDAO) throws CommandException { + VmId vmId = VmArgument.required(arguments).getVmId(); + VmInfo vmInfo = vmInfoDAO.getVmInfo(vmId); + if (vmInfo == null) { + throw new CommandException(t.localize(LocaleResources.UNKNOWN_VMID, vmId.get())); + } + AgentId agentId = new AgentId(vmInfo.getAgentId()); + HostInfo hostInfo = hostInfoDAO.getHostInfo(agentId); + if (hostInfo == null) { + throw new CommandException(t.localize(LocaleResources.UNKNOWN_HOST)); + } + + HostRef hostRef = new HostRef(agentId.get(), hostInfo.getHostname()); + return new VmRef(hostRef, vmInfo); + } + + private void handleStatusRequest(CommandContext ctx, JmxNotificationDAO jmxNotificationDAO, VmRef vmRef) { + boolean monitoringEnabled = isMonitoringEnabled(jmxNotificationDAO, vmRef); + LocalizedString message = t.localize(monitoringEnabled ? LocaleResources.JMX_NOTIFICATION_MONITORING_STATUS_ENABLED + : LocaleResources.JMX_NOTIFICATION_MONITORING_STATUS_DISABLED); + ctx.getConsole().getOutput().println(message.getContents()); + } + + private boolean isMonitoringEnabled(JmxNotificationDAO jmxNotificationDAO, VmRef vmRef) { + JmxNotificationStatus status = jmxNotificationDAO.getLatestNotificationStatus(vmRef); + return status != null && status.isEnabled(); + } + + private void handleJmxStatusChange(String subcommand, CommandContext ctx, RequestQueue queue, + JmxNotificationDAO jmxNotificationDAO, AgentInfoDAO agentInfoDAO, VmRef vmRef) { + if (!ENABLE_SUBCOMMAND.equals(subcommand) && !DISABLE_SUBCOMMAND.equals(subcommand)) { + throw new AssertionError("Invalid subcommand: " + subcommand); + } + + boolean enable = ENABLE_SUBCOMMAND.equals(subcommand); + boolean currentlyEnabled = isMonitoringEnabled(jmxNotificationDAO, vmRef); + if (enable == currentlyEnabled) { + LocalizedString message = t.localize(enable ? LocaleResources.ALREADY_ENABLED : LocaleResources.ALREADY_DISABLED); + ctx.getConsole().getOutput().println(message.getContents()); + return; + } + + CountDownLatch latch = new CountDownLatch(1); + RequestCompleteAction action = new RequestCompleteAction(ctx, latch, enable); + JmxToggleNotificationRequest enableRequest = + new JmxToggleNotificationRequest(queue, agentInfoDAO, action.getSuccessAction(), action.getFailureAction()); + enableRequest.sendEnableNotificationsRequestToAgent(vmRef, enable); + try { + latch.await(); + } catch (InterruptedException e) { + // ignore + } + } + + private long parseSinceOption(Arguments args) throws CommandException { + if (!args.hasArgument(SHOW_SINCE_OPTION)) { + return NotificationsSinceParser.NO_SINCE_OPTION; + } + String sinceArg = args.getArgument(SHOW_SINCE_OPTION); + return sinceParser.parse(sinceArg); + } + + private void followNotificationsLoop(final CommandContext ctx, final Clock clock, TimerFactory timerFactory, + final JmxNotificationDAO jmxNotificationDAO, final VmRef vmRef) { + + Timer pollTimer = null; + try { + // this timer asynchronously performs the DAO polling and results printing + pollTimer = createFollowModeTimer(ctx, clock, timerFactory, jmxNotificationDAO, vmRef); + pollTimer.start(); + + // loop the main thread waiting for user key input, which tells us to exit follow mode and stop the timer + while (continueFollowing(ctx, jmxNotificationDAO, vmRef)) { + try { + Thread.sleep(FOLLOW_LOOP_INPUT_PERIOD); + } catch (InterruptedException e) { + break; + } + } + } finally { + if (pollTimer != null) { + pollTimer.stop(); + } + } + } + + private Timer createFollowModeTimer(final CommandContext ctx, final Clock clock, TimerFactory timerFactory, + final JmxNotificationDAO jmxNotificationDAO, final VmRef vmRef) { + return Timers.createDataRefreshTimer(timerFactory, new Runnable() { + long lastUpdate = clock.getRealTimeMillis(); + + @Override + public void run() { + printNotifications(ctx, jmxNotificationDAO, vmRef, lastUpdate); + lastUpdate = clock.getRealTimeMillis(); + } + }); + } + + private boolean continueFollowing(CommandContext ctx, JmxNotificationDAO jmxNotificationDAO, VmRef vmRef) { + try { + if (ctx.getConsole().getInput().available() > 0) { + return false; + } + } catch (IOException e) { + return false; + } + + boolean monitoringActive = isMonitoringEnabled(jmxNotificationDAO, vmRef); + if (!monitoringActive) { + ctx.getConsole().getOutput().println(t.localize(LocaleResources.JMX_NOTIFICATION_MONITORING_FOLLOW_INTERRUPTED).getContents()); + } + return monitoringActive; + } + + private void printNotifications(CommandContext ctx, JmxNotificationDAO jmxNotificationDAO, VmRef vmRef, long since) { + List<JmxNotification> notifications = jmxNotificationDAO.getNotifications(vmRef, since); + for (JmxNotification notification : notifications) { + String timestamp = Clock.DEFAULT_DATE_FORMAT.format(new Date(notification.getTimeStamp())); + String details = notification.getSourceDetails(); + String contents = notification.getContents(); + ctx.getConsole().getOutput().println( + t.localize(LocaleResources.PRINT_NOTIFICATIONS_FORMAT, timestamp, details, contents).getContents()); + } + } + + public void bindApplicationService(ApplicationService applicationService) { + dependencyServices.addService(ApplicationService.class, applicationService); + } + + public void bindClock(Clock clock) { + dependencyServices.addService(Clock.class, clock); + } + + public void bindRequestQueue(RequestQueue requestQueue) { + dependencyServices.addService(RequestQueue.class, requestQueue); + } + + public void bindHostInfoDao(HostInfoDAO hostInfoDAO) { + dependencyServices.addService(HostInfoDAO.class, hostInfoDAO); + } + + public void bindAgentInfoDao(AgentInfoDAO agentInfoDAO) { + dependencyServices.addService(AgentInfoDAO.class, agentInfoDAO); + } + + public void bindVmInfoDao(VmInfoDAO vmInfoDAO) { + dependencyServices.addService(VmInfoDAO.class, vmInfoDAO); + } + + public void bindJmxNotificationDao(JmxNotificationDAO jmxNotificationDAO) { + dependencyServices.addService(JmxNotificationDAO.class, jmxNotificationDAO); + } + + public void dependenciesUnavailable() { + dependencyServices.removeService(ApplicationService.class); + dependencyServices.removeService(Clock.class); + dependencyServices.removeService(RequestQueue.class); + dependencyServices.removeService(HostInfoDAO.class); + dependencyServices.removeService(AgentInfoDAO.class); + dependencyServices.removeService(VmInfoDAO.class); + dependencyServices.removeService(JmxNotificationDAO.class); + } + + private static class RequestCompleteAction { + + private final CommandContext ctx; + private final CountDownLatch latch; + private final boolean enableRequest; + + RequestCompleteAction(CommandContext ctx, CountDownLatch latch, boolean enableRequest) { + this.ctx = ctx; + this.latch = latch; + this.enableRequest = enableRequest; + } + + Runnable getSuccessAction() { + return new Runnable() { + @Override + public void run() { + LocaleResources message = enableRequest ? LocaleResources.ENABLE_SUCCESS : LocaleResources.DISABLE_SUCCESS; + ctx.getConsole().getOutput().println(t.localize(message).getContents()); + latch.countDown(); + } + }; + } + + Runnable getFailureAction() { + return new Runnable() { + @Override + public void run() { + LocaleResources message = enableRequest ? LocaleResources.ENABLE_FAILURE : LocaleResources.DISABLE_FAILURE; + ctx.getConsole().getError().println(t.localize(message).getContents()); + latch.countDown(); + } + }; + } + + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vm-jmx/client-cli/src/main/java/com/redhat/thermostat/vm/jmx/client/cli/internal/Activator.java Fri Oct 28 15:24:30 2016 -0400 @@ -0,0 +1,115 @@ +/* + * Copyright 2012-2016 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * <http://www.gnu.org/licenses/>. + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.vm.jmx.client.cli.internal; + +import com.redhat.thermostat.client.command.RequestQueue; +import com.redhat.thermostat.common.ApplicationService; +import com.redhat.thermostat.common.MultipleServiceTracker; +import com.redhat.thermostat.common.SystemClock; +import com.redhat.thermostat.common.cli.Command; +import com.redhat.thermostat.storage.dao.AgentInfoDAO; +import com.redhat.thermostat.storage.dao.HostInfoDAO; +import com.redhat.thermostat.storage.dao.VmInfoDAO; +import com.redhat.thermostat.vm.jmx.client.cli.NotificationsCommand; +import com.redhat.thermostat.vm.jmx.common.JmxNotificationDAO; +import org.osgi.framework.BundleActivator; +import org.osgi.framework.BundleContext; +import org.osgi.framework.ServiceRegistration; + +import java.util.Dictionary; +import java.util.Hashtable; + +public class Activator implements BundleActivator { + + private MultipleServiceTracker depsTracker; + private ServiceRegistration registration; + + @Override + public void start(final BundleContext context) throws Exception { + final Class<?>[] deps = new Class<?>[] { + ApplicationService.class, + RequestQueue.class, + HostInfoDAO.class, + AgentInfoDAO.class, + VmInfoDAO.class, + JmxNotificationDAO.class, + }; + + final NotificationsCommand cmd = new NotificationsCommand(); + depsTracker = new MultipleServiceTracker(context, deps, new MultipleServiceTracker.Action() { + @Override + public void dependenciesAvailable(MultipleServiceTracker.DependencyProvider services) { + ApplicationService applicationService = services.get(ApplicationService.class); + RequestQueue queue = services.get(RequestQueue.class); + HostInfoDAO hostDao = services.get(HostInfoDAO.class); + AgentInfoDAO agentDao = services.get(AgentInfoDAO.class); + VmInfoDAO vmDao = services.get(VmInfoDAO.class); + JmxNotificationDAO jmxDao = services.get(JmxNotificationDAO.class); + + cmd.bindApplicationService(applicationService); + cmd.bindClock(new SystemClock()); + cmd.bindRequestQueue(queue); + cmd.bindHostInfoDao(hostDao); + cmd.bindAgentInfoDao(agentDao); + cmd.bindVmInfoDao(vmDao); + cmd.bindJmxNotificationDao(jmxDao); + } + + @Override + public void dependenciesUnavailable() { + cmd.dependenciesUnavailable(); + } + + }); + depsTracker.open(); + + Dictionary<String, String> props = new Hashtable<>(); + props.put(Command.NAME, NotificationsCommand.COMMAND_NAME); + registration = context.registerService(Command.class.getName(), cmd, props); + } + + @Override + public void stop(BundleContext context) throws Exception { + if (depsTracker != null) { + depsTracker.close(); + } + if (registration != null) { + registration.unregister(); + } + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vm-jmx/client-cli/src/main/java/com/redhat/thermostat/vm/jmx/client/cli/internal/NotificationsSinceParser.java Fri Oct 28 15:24:30 2016 -0400 @@ -0,0 +1,136 @@ +/* + * Copyright 2012-2016 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * <http://www.gnu.org/licenses/>. + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.vm.jmx.client.cli.internal; + +import com.redhat.thermostat.common.Clock; +import com.redhat.thermostat.common.cli.CommandException; +import com.redhat.thermostat.shared.locale.Translate; +import com.redhat.thermostat.vm.jmx.client.cli.locale.LocaleResources; + +import java.util.concurrent.TimeUnit; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class NotificationsSinceParser { + + public static final long NO_SINCE_OPTION = -1L; + + private static final Pattern SINCE_SECONDS_OFFSET_PATTERN = Pattern.compile("^([0-9]+)s$"); + private static final Pattern SINCE_MINUTES_OFFSET_PATTERN = Pattern.compile("^([0-9]+)m$"); + private static final Pattern SINCE_HOURS_OFFSET_PATTERN = Pattern.compile("^([0-9]+)h$"); + private static final Pattern SINCE_DAYS_OFFSET_PATTERN = Pattern.compile("^([0-9]+)d$"); + + private static final Translate<LocaleResources> t = LocaleResources.createLocalizer(); + + private final Clock clock; + + public NotificationsSinceParser(Clock clock) { + this.clock = clock; + } + + public long parse(String sinceArg) throws CommandException { + try { + if (hasSinceSecondsArgument(sinceArg)) { + return getSinceSecondsArgument(sinceArg); + } else if (hasSinceMinutesArgument(sinceArg)) { + return getSinceMinutesArgument(sinceArg); + } else if (hasSinceHoursArgument(sinceArg)) { + return getSinceHoursArgument(sinceArg); + } else if (hasSinceDaysArgument(sinceArg)) { + return getSinceDaysArgument(sinceArg); + } + long parsed = Long.parseLong(sinceArg); + if (parsed < NO_SINCE_OPTION) { + parsed = NO_SINCE_OPTION; + } + return parsed; + } catch (NumberFormatException nfe) { + throw new CommandException(t.localize(LocaleResources.UNRECOGNIZED_SINCE_FORMAT), nfe); + } + } + + private boolean hasSinceSecondsArgument(String s) { + return SINCE_SECONDS_OFFSET_PATTERN.matcher(s).matches(); + } + + private long getSinceSecondsArgument(String s) { + Matcher matcher = SINCE_SECONDS_OFFSET_PATTERN.matcher(s); + verifyMatcher(matcher); + long seconds = Long.parseLong(matcher.group(1)); + return clock.getRealTimeMillis() - TimeUnit.SECONDS.toMillis(seconds); + } + + private boolean hasSinceMinutesArgument(String s) { + return SINCE_MINUTES_OFFSET_PATTERN.matcher(s).matches(); + } + + private long getSinceMinutesArgument(String s) { + Matcher matcher = SINCE_MINUTES_OFFSET_PATTERN.matcher(s); + verifyMatcher(matcher); + long minutes = Long.parseLong(matcher.group(1)); + return clock.getRealTimeMillis() - TimeUnit.MINUTES.toMillis(minutes); + } + + private boolean hasSinceHoursArgument(String s) { + return SINCE_HOURS_OFFSET_PATTERN.matcher(s).matches(); + } + + private long getSinceHoursArgument(String s) { + Matcher matcher = SINCE_HOURS_OFFSET_PATTERN.matcher(s); + verifyMatcher(matcher); + long hours = Long.parseLong(matcher.group(1)); + return clock.getRealTimeMillis() - TimeUnit.HOURS.toMillis(hours); + } + + private boolean hasSinceDaysArgument(String s) { + return SINCE_DAYS_OFFSET_PATTERN.matcher(s).matches(); + } + + private long getSinceDaysArgument(String s) { + Matcher matcher = SINCE_DAYS_OFFSET_PATTERN.matcher(s); + verifyMatcher(matcher); + long days = Long.parseLong(matcher.group(1)); + return clock.getRealTimeMillis() - TimeUnit.DAYS.toMillis(days); + } + + private void verifyMatcher(Matcher matcher) { + if (!matcher.matches()) { + throw new AssertionError("Invalid \"since\" format"); + } + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vm-jmx/client-cli/src/main/java/com/redhat/thermostat/vm/jmx/client/cli/locale/LocaleResources.java Fri Oct 28 15:24:30 2016 -0400 @@ -0,0 +1,80 @@ +/* + * Copyright 2012-2016 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * <http://www.gnu.org/licenses/>. + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.vm.jmx.client.cli.locale; + +import com.redhat.thermostat.shared.locale.Translate; + +public enum LocaleResources { + + EXPECTED_ONE_NONOPTION_ARG, + UNRECOGNIZED_SUBCOMMAND, + + MISSING_REQUIRED_SERVICE, + + UNKNOWN_VMID, + UNKNOWN_HOST, + + JMX_NOTIFICATION_MONITORING_STATUS_ENABLED, + JMX_NOTIFICATION_MONITORING_STATUS_DISABLED, + + JMX_NOTIFICATION_MONITORING_FOLLOW_NOT_ENABLED, + JMX_NOTIFICATION_MONITORING_FOLLOW_INTERRUPTED, + + ALREADY_ENABLED, + ALREADY_DISABLED, + + ENABLE_SUCCESS, + ENABLE_FAILURE, + + DISABLE_SUCCESS, + DISABLE_FAILURE, + + EXIT_FOLLOW_MODE_HINT, + + UNRECOGNIZED_SINCE_FORMAT, + + PRINT_NOTIFICATIONS_FORMAT, + + ; + + static final String RESOURCE_BUNDLE = "com.redhat.thermostat.vm.jmx.client.cli.locale.strings"; + + public static Translate<LocaleResources> createLocalizer() { + return new Translate<>(RESOURCE_BUNDLE, LocaleResources.class); + } +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vm-jmx/client-cli/src/main/resources/com/redhat/thermostat/vm/jmx/client/cli/locale/strings.properties Fri Oct 28 15:24:30 2016 -0400 @@ -0,0 +1,28 @@ +EXPECTED_ONE_NONOPTION_ARG=Expected one non-option argument (subcommand) +UNRECOGNIZED_SUBCOMMAND=Unrecognized subcommand: {0} + +MISSING_REQUIRED_SERVICE=Required service {0} is missing + +UNKNOWN_VMID=Unknown VM ID: {0} +UNKNOWN_HOST=Unknown host + +JMX_NOTIFICATION_MONITORING_STATUS_ENABLED=JMX notification monitoring is enabled +JMX_NOTIFICATION_MONITORING_STATUS_DISABLED=JMX notification monitoring is disabled + +JMX_NOTIFICATION_MONITORING_FOLLOW_NOT_ENABLED=JMX notification monitoring is not enabled for this JVM - notifications cannot be followed +JMX_NOTIFICATION_MONITORING_FOLLOW_INTERRUPTED=JMX notification monitoring interrupted + +ALREADY_ENABLED=JMX notification monitoring is already enabled +ALREADY_DISABLED=JMX notification monitoring is not enabled + +ENABLE_SUCCESS=JMX notification monitoring enabled +ENABLE_FAILURE=Failed to enable JMX notification monitoring + +DISABLE_SUCCESS=JMX notification monitoring disabled +DISABLE_FAILURE=Failed to disable JMX notification monitoring + +EXIT_FOLLOW_MODE_HINT=Press any key to exit follow mode... + +UNRECOGNIZED_SINCE_FORMAT=Failed to parse "since" option argument + +PRINT_NOTIFICATIONS_FORMAT=[{0}] ({1}) : {2} \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vm-jmx/client-cli/src/test/java/com/redhat/thermostat/vm/jmx/client/cli/NotificationsCommandTest.java Fri Oct 28 15:24:30 2016 -0400 @@ -0,0 +1,552 @@ +/* + * Copyright 2012-2016 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * <http://www.gnu.org/licenses/>. + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.vm.jmx.client.cli; + +import com.redhat.thermostat.client.cli.VmArgument; +import com.redhat.thermostat.client.command.RequestQueue; +import com.redhat.thermostat.common.ApplicationService; +import com.redhat.thermostat.common.Clock; +import com.redhat.thermostat.common.cli.Arguments; +import com.redhat.thermostat.common.cli.CommandContext; +import com.redhat.thermostat.common.cli.CommandException; +import com.redhat.thermostat.common.cli.Console; +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.internal.test.TestTimerFactory; +import com.redhat.thermostat.storage.core.AgentId; +import com.redhat.thermostat.storage.core.VmId; +import com.redhat.thermostat.storage.core.VmRef; +import com.redhat.thermostat.storage.dao.AgentInfoDAO; +import com.redhat.thermostat.storage.dao.HostInfoDAO; +import com.redhat.thermostat.storage.dao.VmInfoDAO; +import com.redhat.thermostat.storage.model.AgentInformation; +import com.redhat.thermostat.storage.model.HostInfo; +import com.redhat.thermostat.storage.model.VmInfo; +import com.redhat.thermostat.vm.jmx.common.JmxNotification; +import com.redhat.thermostat.vm.jmx.common.JmxNotificationDAO; +import com.redhat.thermostat.vm.jmx.common.JmxNotificationStatus; +import org.junit.Before; +import org.junit.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; + +import java.io.IOException; +import java.io.InputStream; +import java.io.PrintStream; +import java.util.Collections; +import java.util.List; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.fail; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyLong; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyZeroInteractions; +import static org.mockito.Mockito.when; + +public class NotificationsCommandTest { + + private static final String NOTIFICATION_OUTPUT = "[31-Dec-1969 7:00:00 EST PM] (notification source details) : notification contents"; + private static final String FAR_FUTURE_NOTIFICATION_OUTPUT = "[17-Aug-292278994 2:12:55 EST AM] (notification source details) : notification contents"; + + private static final String JMX_NOTIFICATION_MONITORING_ENABLED = "JMX notification monitoring enabled"; + private static final String PRESS_ANY_KEY_TO_EXIT_FOLLOW_MODE = "Press any key to exit follow mode..."; + private static final String JMX_NOTIFICATION_MONITORING_INTERRUPTED = "JMX notification monitoring interrupted"; + private static final String JMX_NOTIFICATION_MONITORING_IS_ALREADY_ENABLED = "JMX notification monitoring is already enabled"; + private static final String JMX_NOTIFICATION_MONITORING_IS_NOT_ENABLED = "JMX notification monitoring is not enabled"; + private static final String JMX_NOTIFICATION_MONITORING_DISABLED = "JMX notification monitoring disabled"; + private static final String JMX_NOTIFICATION_MONITORING_IS_DISABLED = "JMX notification monitoring is disabled"; + private static final String JMX_NOTIFICATION_MONITORING_IS_ENABLED = "JMX notification monitoring is enabled"; + + private static final String FOO_AGENTID = "foo-agentid"; + private static final String FOO_HOSTNAME = "foo-hostname"; + private static final String FOO_VMID = "foo-vmid"; + + private static final String STATUS_SUBCOMMAND = "status"; + private static final String DISABLE_SUBCOMMAND = "disable"; + private static final String ENABLE_SUBCOMMAND = "enable"; + private static final String FOLLOW_SUBCOMMAND = "follow"; + private static final String SHOW_SUBCOMMAND = "show"; + + private static final String FOLLOW_OPTION = "follow"; + private static final String SINCE_OPTION = "since"; + + private ApplicationService applicationService; + private Clock clock; + private RequestQueue requestQueue; + private HostInfoDAO hostInfoDAO; + private AgentInfoDAO agentInfoDAO; + private VmInfoDAO vmInfoDAO; + private JmxNotificationDAO jmxNotificationDAO; + + private JmxNotificationStatus jmxNotificationStatus; + private JmxNotification jmxNotification; + + private Arguments args; + private PrintStream outStream; + private InputStream inStream; + private ArgumentCaptor<String> outCaptor; + private CommandContext ctx; + + private NotificationsCommand cmd; + private TestTimerFactory timerFactory; + + @Before + public void setup() { + timerFactory = new TestTimerFactory(); + applicationService = mock(ApplicationService.class); + clock = mock(Clock.class); + requestQueue = mock(RequestQueue.class); + hostInfoDAO = mock(HostInfoDAO.class); + agentInfoDAO = mock(AgentInfoDAO.class); + vmInfoDAO = mock(VmInfoDAO.class); + jmxNotificationDAO = mock(JmxNotificationDAO.class); + + Console console = mock(Console.class); + outStream = mock(PrintStream.class); + inStream = mock(InputStream.class); + outCaptor = ArgumentCaptor.forClass(String.class); + when(console.getOutput()).thenReturn(outStream); + when(console.getInput()).thenReturn(inStream); + + args = mock(Arguments.class); + when(args.hasArgument(VmArgument.ARGUMENT_NAME)).thenReturn(true); + when(args.getArgument(VmArgument.ARGUMENT_NAME)).thenReturn(FOO_VMID); + ctx = mock(CommandContext.class); + when(ctx.getArguments()).thenReturn(args); + when(ctx.getConsole()).thenReturn(console); + + cmd = new NotificationsCommand(); + cmd.bindApplicationService(applicationService); + cmd.bindClock(clock); + cmd.bindRequestQueue(requestQueue); + cmd.bindHostInfoDao(hostInfoDAO); + cmd.bindAgentInfoDao(agentInfoDAO); + cmd.bindVmInfoDao(vmInfoDAO); + cmd.bindJmxNotificationDao(jmxNotificationDAO); + + setupMocks(); + } + + private void setupMocks() { + when(applicationService.getTimerFactory()).thenReturn(timerFactory); + when(clock.getRealTimeMillis()).thenReturn(1L); + + doAnswer(new Answer<Void>() { + @Override + public Void answer(InvocationOnMock invocationOnMock) throws Throwable { + Request request = (Request) invocationOnMock.getArguments()[0]; + Response response = new Response(Response.ResponseType.OK); + for (RequestResponseListener listener : request.getListeners()) { + listener.fireComplete(request, response); + } + return null; + } + }).when(requestQueue).putRequest(any(Request.class)); + + HostInfo hostInfo = new HostInfo(); + hostInfo.setAgentId(FOO_AGENTID); + hostInfo.setHostname(FOO_HOSTNAME); + when(hostInfoDAO.getHostInfo(any(AgentId.class))).thenReturn(hostInfo); + when(hostInfoDAO.getAllHostInfos()).thenReturn(Collections.singletonList(hostInfo)); + + AgentInformation agentInfo = new AgentInformation(); + agentInfo.setAgentId(FOO_AGENTID); + agentInfo.setConfigListenAddress("127.0.0.1:22"); + when(agentInfoDAO.getAgentIds()).thenReturn(Collections.singleton(new AgentId(FOO_AGENTID))); + when(agentInfoDAO.getAliveAgentIds()).thenReturn(Collections.singleton(new AgentId(FOO_AGENTID))); + when(agentInfoDAO.getAgentInformation(any(AgentId.class))).thenReturn(agentInfo); + when(agentInfoDAO.getAliveAgents()).thenReturn(Collections.singletonList(agentInfo)); + when(agentInfoDAO.getAllAgentInformation()).thenReturn(Collections.singletonList(agentInfo)); + + VmInfo vmInfo = new VmInfo(); + vmInfo.setAgentId(FOO_AGENTID); + vmInfo.setVmId(FOO_VMID); + when(vmInfoDAO.getVmInfo(any(VmId.class))).thenReturn(vmInfo); + when(vmInfoDAO.getAllVmInfos()).thenReturn(Collections.singletonList(vmInfo)); + when(vmInfoDAO.getAllVmInfosForAgent(any(AgentId.class))).thenReturn(Collections.singletonList(vmInfo)); + when(vmInfoDAO.getVmIds(any(AgentId.class))).thenReturn(Collections.singleton(new VmId(FOO_VMID))); + when(vmInfoDAO.getVmInfo(any(VmRef.class))).thenReturn(vmInfo); + + jmxNotificationStatus = new JmxNotificationStatus(FOO_AGENTID); + jmxNotificationStatus.setVmId(FOO_VMID); + jmxNotificationStatus.setTimeStamp(100L); + jmxNotificationStatus.setEnabled(true); + jmxNotification = new JmxNotification(FOO_AGENTID); + jmxNotification.setVmId(FOO_VMID); + jmxNotification.setTimeStamp(110L); + jmxNotification.setContents("notification contents"); + jmxNotification.setSourceBackend("jmxBackend"); + jmxNotification.setSourceDetails("notification source details"); + when(jmxNotificationDAO.getLatestNotificationStatus(any(VmRef.class))).thenReturn(jmxNotificationStatus); + when(jmxNotificationDAO.getNotifications(any(VmRef.class), anyLong())).thenAnswer(new Answer<List<JmxNotification>>() { + @Override + public List<JmxNotification> answer(InvocationOnMock invocationOnMock) throws Throwable { + long timestamp = (Long) invocationOnMock.getArguments()[1]; + if (timestamp < jmxNotification.getTimeStamp()) { + return Collections.singletonList(jmxNotification); + } else { + return Collections.emptyList(); + } + } + }); + } + + @Test + public void testStatusWhenMonitoringIsEnabled() throws CommandException { + when(args.getNonOptionArguments()).thenReturn(Collections.singletonList(STATUS_SUBCOMMAND)); + assertThat(runCommandForOutput(), is(JMX_NOTIFICATION_MONITORING_IS_ENABLED)); + } + + @Test + public void testStatusWhenMonitoringIsDisabled() throws CommandException { + jmxNotificationStatus.setEnabled(false); + when(args.getNonOptionArguments()).thenReturn(Collections.singletonList(STATUS_SUBCOMMAND)); + assertThat(runCommandForOutput(), is(JMX_NOTIFICATION_MONITORING_IS_DISABLED)); + } + + @Test + public void testStatusWhenMonitoringStatusIsUnknown() throws CommandException { + when(jmxNotificationDAO.getLatestNotificationStatus(any(VmRef.class))).thenReturn(null); + when(args.getNonOptionArguments()).thenReturn(Collections.singletonList(STATUS_SUBCOMMAND)); + assertThat(runCommandForOutput(), is(JMX_NOTIFICATION_MONITORING_IS_DISABLED)); + } + + @Test + public void testDisableWhenMonitoringIsEnabled() throws CommandException { + when(args.getNonOptionArguments()).thenReturn(Collections.singletonList(DISABLE_SUBCOMMAND)); + assertThat(runCommandForOutput(), is(JMX_NOTIFICATION_MONITORING_DISABLED)); + } + + @Test + public void testDisableWhenMonitoringIsDisabled() throws CommandException { + jmxNotificationStatus.setEnabled(false); + when(args.getNonOptionArguments()).thenReturn(Collections.singletonList(DISABLE_SUBCOMMAND)); + assertThat(runCommandForOutput(), is(JMX_NOTIFICATION_MONITORING_IS_NOT_ENABLED)); + } + + @Test + public void testDisableWhenMonitoringStatusIsUnknown() throws CommandException { + when(jmxNotificationDAO.getLatestNotificationStatus(any(VmRef.class))).thenReturn(null); + when(args.getNonOptionArguments()).thenReturn(Collections.singletonList(DISABLE_SUBCOMMAND)); + assertThat(runCommandForOutput(), is(JMX_NOTIFICATION_MONITORING_IS_NOT_ENABLED)); + } + + @Test + public void testEnableWhenMonitoringIsEnabled() throws CommandException { + when(args.getNonOptionArguments()).thenReturn(Collections.singletonList(ENABLE_SUBCOMMAND)); + assertThat(runCommandForOutput(), is(JMX_NOTIFICATION_MONITORING_IS_ALREADY_ENABLED)); + } + + @Test + public void testEnableWhenMonitoringIsDisabled() throws CommandException { + jmxNotificationStatus.setEnabled(false); + when(args.getNonOptionArguments()).thenReturn(Collections.singletonList(ENABLE_SUBCOMMAND)); + assertThat(runCommandForOutput(), is(JMX_NOTIFICATION_MONITORING_ENABLED)); + } + + @Test + public void testEnableWhenMonitoringStatusIsUnknown() throws CommandException { + when(jmxNotificationDAO.getLatestNotificationStatus(any(VmRef.class))).thenReturn(null); + when(args.getNonOptionArguments()).thenReturn(Collections.singletonList(ENABLE_SUBCOMMAND)); + assertThat(runCommandForOutput(), is(JMX_NOTIFICATION_MONITORING_ENABLED)); + } + + @Test + public void testEnableWithFollowOption() throws CommandException, IOException { + jmxNotificationStatus.setEnabled(false); + jmxNotification.setTimeStamp(Long.MAX_VALUE); + when(args.getNonOptionArguments()).thenReturn(Collections.singletonList(ENABLE_SUBCOMMAND)); + when(args.hasArgument(FOLLOW_OPTION)).thenReturn(true); + + doFollowTestWithKeyboardInterrupt(); + + verify(outStream, times(3)).println(outCaptor.capture()); + String status = outCaptor.getAllValues().get(0); + String hint = outCaptor.getAllValues().get(1); + String firstNotification = outCaptor.getAllValues().get(2); + assertThat(status, is(JMX_NOTIFICATION_MONITORING_ENABLED)); + assertThat(hint, is(PRESS_ANY_KEY_TO_EXIT_FOLLOW_MODE)); + assertThat(firstNotification, is(FAR_FUTURE_NOTIFICATION_OUTPUT)); + } + + @Test + public void testEnableWithFollowOptionWhenAlreadyEnabled() throws CommandException, IOException { + jmxNotification.setTimeStamp(Long.MAX_VALUE); + when(args.getNonOptionArguments()).thenReturn(Collections.singletonList(ENABLE_SUBCOMMAND)); + when(args.hasArgument(FOLLOW_OPTION)).thenReturn(true); + + doFollowTestWithKeyboardInterrupt(); + + verify(outStream, times(3)).println(outCaptor.capture()); + String status = outCaptor.getAllValues().get(0); + String hint = outCaptor.getAllValues().get(1); + String firstNotification = outCaptor.getAllValues().get(2); + assertThat(status, is(JMX_NOTIFICATION_MONITORING_IS_ALREADY_ENABLED)); + assertThat(hint, is(PRESS_ANY_KEY_TO_EXIT_FOLLOW_MODE)); + assertThat(firstNotification, is(FAR_FUTURE_NOTIFICATION_OUTPUT)); + } + + @Test + public void testEnableWithFollowOptionWithExternalInterrupt() throws CommandException, IOException { + jmxNotificationStatus.setEnabled(false); + jmxNotification.setTimeStamp(Long.MAX_VALUE); + when(args.getNonOptionArguments()).thenReturn(Collections.singletonList(ENABLE_SUBCOMMAND)); + when(args.hasArgument(FOLLOW_OPTION)).thenReturn(true); + + doFollowTestWithExternalMonitoringInterrupt(); + + verify(outStream, times(4)).println(outCaptor.capture()); + String status = outCaptor.getAllValues().get(0); + String hint = outCaptor.getAllValues().get(1); + String firstNotification = outCaptor.getAllValues().get(2); + String interruptNotice = outCaptor.getAllValues().get(3); + assertThat(status, is(JMX_NOTIFICATION_MONITORING_ENABLED)); + assertThat(hint, is(PRESS_ANY_KEY_TO_EXIT_FOLLOW_MODE)); + assertThat(firstNotification, is(FAR_FUTURE_NOTIFICATION_OUTPUT)); + assertThat(interruptNotice, is(JMX_NOTIFICATION_MONITORING_INTERRUPTED)); + } + + @Test + public void testFollowWhenDisabled() throws CommandException { + jmxNotificationStatus.setEnabled(false); + jmxNotification.setTimeStamp(Long.MAX_VALUE); + when(args.getNonOptionArguments()).thenReturn(Collections.singletonList(FOLLOW_SUBCOMMAND)); + + assertThat(runCommandForOutput(), is("JMX notification monitoring is not enabled for this JVM - notifications cannot be followed")); + } + + @Test + public void testFollowWhenEnabled() throws CommandException, IOException { + jmxNotification.setTimeStamp(Long.MAX_VALUE); + when(args.getNonOptionArguments()).thenReturn(Collections.singletonList(FOLLOW_SUBCOMMAND)); + + doFollowTestWithKeyboardInterrupt(); + + verify(outStream, times(2)).println(outCaptor.capture()); + String hint = outCaptor.getAllValues().get(0); + String firstNotification = outCaptor.getAllValues().get(1); + assertThat(hint, is(PRESS_ANY_KEY_TO_EXIT_FOLLOW_MODE)); + assertThat(firstNotification, is(FAR_FUTURE_NOTIFICATION_OUTPUT)); + } + + @Test + public void testFollowWithExternalInterrupt() throws CommandException, IOException { + jmxNotification.setTimeStamp(Long.MAX_VALUE); + when(args.getNonOptionArguments()).thenReturn(Collections.singletonList(FOLLOW_SUBCOMMAND)); + + doFollowTestWithExternalMonitoringInterrupt(); + + verify(outStream, times(3)).println(outCaptor.capture()); + String hint = outCaptor.getAllValues().get(0); + String firstNotification = outCaptor.getAllValues().get(1); + String interruptNotice = outCaptor.getAllValues().get(2); + assertThat(hint, is(PRESS_ANY_KEY_TO_EXIT_FOLLOW_MODE)); + assertThat(firstNotification, is(FAR_FUTURE_NOTIFICATION_OUTPUT)); + assertThat(interruptNotice, is(JMX_NOTIFICATION_MONITORING_INTERRUPTED)); + } + + private void doFollowTestWithKeyboardInterrupt() throws IOException { + doAnswer(new Answer<Void>() { + @Override + public Void answer(InvocationOnMock invocationOnMock) throws Throwable { + jmxNotificationStatus.setEnabled(true); + Request request = (Request) invocationOnMock.getArguments()[0]; + Response response = new Response(Response.ResponseType.OK); + for (RequestResponseListener listener : request.getListeners()) { + listener.fireComplete(request, response); + } + return null; + } + }).when(requestQueue).putRequest(any(Request.class)); + + Thread helper = new Thread(new Runnable() { + @Override + public void run() { + try { + cmd.run(ctx); + } catch (CommandException e) { + fail(); + } + } + }); + helper.start(); + try { + Thread.sleep(750); + timerFactory.getAction().run(); + } catch (InterruptedException ignored) { + } + when(inStream.available()).thenReturn(1); + try { + helper.join(); + } catch (InterruptedException ignored) { + } + } + + private void doFollowTestWithExternalMonitoringInterrupt() throws IOException { + doAnswer(new Answer<Void>() { + @Override + public Void answer(InvocationOnMock invocationOnMock) throws Throwable { + jmxNotificationStatus.setEnabled(true); + Request request = (Request) invocationOnMock.getArguments()[0]; + Response response = new Response(Response.ResponseType.OK); + for (RequestResponseListener listener : request.getListeners()) { + listener.fireComplete(request, response); + } + return null; + } + }).when(requestQueue).putRequest(any(Request.class)); + + Thread helper = new Thread(new Runnable() { + @Override + public void run() { + try { + cmd.run(ctx); + } catch (CommandException e) { + fail(); + } + } + }); + helper.start(); + try { + Thread.sleep(750); + timerFactory.getAction().run(); + } catch (InterruptedException ignored) { + } + jmxNotificationStatus.setEnabled(false); + try { + helper.join(); + } catch (InterruptedException ignored) { + } + } + + @Test + public void testShow() throws CommandException { + when(args.getNonOptionArguments()).thenReturn(Collections.singletonList(SHOW_SUBCOMMAND)); + assertThat(runCommandForOutput(), is(NOTIFICATION_OUTPUT)); + } + + @Test + public void testShowSinceWithTimestampNewerThanData() throws CommandException { + when(args.getNonOptionArguments()).thenReturn(Collections.singletonList(SHOW_SUBCOMMAND)); + when(args.hasArgument(SINCE_OPTION)).thenReturn(true); + when(args.getArgument(SINCE_OPTION)).thenReturn("500"); + cmd.run(ctx); + verifyZeroInteractions(outStream); + } + + @Test + public void testShowSinceWithTimestampOlderThanData() throws CommandException { + when(args.getNonOptionArguments()).thenReturn(Collections.singletonList(SHOW_SUBCOMMAND)); + when(args.hasArgument(SINCE_OPTION)).thenReturn(true); + when(args.getArgument(SINCE_OPTION)).thenReturn("1"); + assertThat(runCommandForOutput(), is(NOTIFICATION_OUTPUT)); + } + + private String runCommandForOutput() throws CommandException { + cmd.run(ctx); + verify(outStream).println(outCaptor.capture()); + return outCaptor.getValue(); + } + + @Test(expected = CommandException.class) + public void testRequiresRequestQueue() throws CommandException { + when(args.getNonOptionArguments()).thenReturn(Collections.singletonList(STATUS_SUBCOMMAND)); + cmd.dependenciesUnavailable(); +// cmd.bindRequestQueue(requestQueue); + cmd.bindHostInfoDao(hostInfoDAO); + cmd.bindAgentInfoDao(agentInfoDAO); + cmd.bindVmInfoDao(vmInfoDAO); + cmd.bindJmxNotificationDao(jmxNotificationDAO); + cmd.run(ctx); + } + + @Test(expected = CommandException.class) + public void testRequiresHostInfoDao() throws CommandException { + when(args.getNonOptionArguments()).thenReturn(Collections.singletonList(STATUS_SUBCOMMAND)); + cmd.dependenciesUnavailable(); + cmd.bindRequestQueue(requestQueue); +// cmd.bindHostInfoDao(hostInfoDAO); + cmd.bindAgentInfoDao(agentInfoDAO); + cmd.bindVmInfoDao(vmInfoDAO); + cmd.bindJmxNotificationDao(jmxNotificationDAO); + cmd.run(ctx); + } + + @Test(expected = CommandException.class) + public void testRequiresAgentInfoDao() throws CommandException { + when(args.getNonOptionArguments()).thenReturn(Collections.singletonList(STATUS_SUBCOMMAND)); + cmd.dependenciesUnavailable(); + cmd.bindRequestQueue(requestQueue); + cmd.bindHostInfoDao(hostInfoDAO); +// cmd.bindAgentInfoDao(agentInfoDAO); + cmd.bindVmInfoDao(vmInfoDAO); + cmd.bindJmxNotificationDao(jmxNotificationDAO); + cmd.run(ctx); + } + + @Test(expected = CommandException.class) + public void testRequiresVmInfoDao() throws CommandException { + when(args.getNonOptionArguments()).thenReturn(Collections.singletonList(STATUS_SUBCOMMAND)); + cmd.dependenciesUnavailable(); + cmd.bindRequestQueue(requestQueue); + cmd.bindHostInfoDao(hostInfoDAO); + cmd.bindAgentInfoDao(agentInfoDAO); +// cmd.bindVmInfoDao(vmInfoDAO); + cmd.bindJmxNotificationDao(jmxNotificationDAO); + cmd.run(ctx); + } + + @Test(expected = CommandException.class) + public void testRequiresJmxNotificationDao() throws CommandException { + when(args.getNonOptionArguments()).thenReturn(Collections.singletonList(STATUS_SUBCOMMAND)); + cmd.dependenciesUnavailable(); + cmd.bindRequestQueue(requestQueue); + cmd.bindHostInfoDao(hostInfoDAO); + cmd.bindAgentInfoDao(agentInfoDAO); + cmd.bindVmInfoDao(vmInfoDAO); +// cmd.bindJmxNotificationDao(jmxNotificationDAO); + cmd.run(ctx); + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vm-jmx/client-cli/src/test/java/com/redhat/thermostat/vm/jmx/client/cli/internal/ActivatorTest.java Fri Oct 28 15:24:30 2016 -0400 @@ -0,0 +1,63 @@ +/* + * Copyright 2012-2016 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * <http://www.gnu.org/licenses/>. + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.vm.jmx.client.cli.internal; + +import com.redhat.thermostat.common.cli.Command; +import com.redhat.thermostat.testutils.StubBundleContext; +import com.redhat.thermostat.vm.jmx.client.cli.NotificationsCommand; +import org.junit.Test; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +public class ActivatorTest { + + @Test + public void testActivator() throws Exception { + StubBundleContext bundleContext = new StubBundleContext(); + Activator activator = new Activator(); + + activator.start(bundleContext); + + assertTrue(bundleContext.isServiceRegistered(Command.class.getName(), NotificationsCommand.class)); + + activator.stop(bundleContext); + + assertFalse(bundleContext.isServiceRegistered(Command.class.getName(), NotificationsCommand.class)); + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vm-jmx/client-cli/src/test/java/com/redhat/thermostat/vm/jmx/client/cli/internal/NotificationsSinceParserTest.java Fri Oct 28 15:24:30 2016 -0400 @@ -0,0 +1,175 @@ +/* + * Copyright 2012-2016 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * <http://www.gnu.org/licenses/>. + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.vm.jmx.client.cli.internal; + +import com.redhat.thermostat.common.Clock; +import com.redhat.thermostat.common.cli.CommandException; +import org.junit.Before; +import org.junit.Test; + +import java.util.concurrent.TimeUnit; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class NotificationsSinceParserTest { + + private static final long FAKE_CURRENT_TIMESTAMP = 100_000_000L; + private Clock clock; + private NotificationsSinceParser parser; + + @Before + public void setup() { + clock = mock(Clock.class); + when(clock.getRealTimeMillis()).thenReturn(FAKE_CURRENT_TIMESTAMP); + parser = new NotificationsSinceParser(clock); + } + + @Test + public void testPlainLongTimestamp() throws CommandException { + long result = parser.parse("100"); + assertThat(result, is(100L)); + } + + @Test + public void testNegativeLongTimestamp() throws CommandException { + long result = parser.parse("-5"); + assertThat(result, is(-1L)); + } + + @Test(expected = CommandException.class) + public void testInvalidOffsetSuffix() throws CommandException { + parser.parse("1y"); // we don't support "in the last year" as an offset + } + + @Test(expected = CommandException.class) + public void testLongTimestampTooLarge() throws CommandException { + String timestamp = String.valueOf(Long.MAX_VALUE) + "0"; // 10x Long.MAX_VALUE + parser.parse(timestamp); + } + + @Test + public void testSecondsOffset() throws CommandException { + long result = parser.parse("5s"); + long delta = TimeUnit.SECONDS.toMillis(5); + assertThat(result, is(FAKE_CURRENT_TIMESTAMP - delta)); + } + + @Test(expected = CommandException.class) + public void testSecondsOffsetDoesNotAllowNegativeOffsets() throws CommandException { + parser.parse("-5s"); + } + + @Test(expected = CommandException.class) + public void testSecondsOffsetRequiresIntegerOffsets() throws CommandException { + parser.parse("5.5s"); + } + + @Test(expected = CommandException.class) + public void testSecondsOffsetIsCaseSensitive() throws CommandException { + parser.parse("5S"); + } + + @Test + public void testMinutesOffset() throws CommandException { + long result = parser.parse("5m"); + long delta = TimeUnit.MINUTES.toMillis(5); + assertThat(result, is(FAKE_CURRENT_TIMESTAMP - delta)); + } + + @Test(expected = CommandException.class) + public void testMinutesOffsetDoesNotAllowNegativeOffsets() throws CommandException { + parser.parse("-5m"); + } + + @Test(expected = CommandException.class) + public void testMinutesOffsetRequiresIntegerOffsets() throws CommandException { + parser.parse("5.5m"); + } + + @Test(expected = CommandException.class) + public void testMinutesOffsetIsCaseSensitive() throws CommandException { + parser.parse("5M"); + } + + @Test + public void testHoursOffset() throws CommandException { + long result = parser.parse("5h"); + long delta = TimeUnit.HOURS.toMillis(5); + assertThat(result, is(FAKE_CURRENT_TIMESTAMP - delta)); + } + + @Test(expected = CommandException.class) + public void testHoursOffsetDoesNotAllowNegativeOffsets() throws CommandException { + parser.parse("-5h"); + } + + @Test(expected = CommandException.class) + public void testHoursOffsetRequiresIntegerOffsets() throws CommandException { + parser.parse("5.5h"); + } + + @Test(expected = CommandException.class) + public void testHoursOffsetIsCaseSensitive() throws CommandException { + parser.parse("5H"); + } + + @Test + public void testDaysOffset() throws CommandException { + long result = parser.parse("5d"); + long delta = TimeUnit.DAYS.toMillis(5); + assertThat(result, is(FAKE_CURRENT_TIMESTAMP - delta)); + } + + @Test(expected = CommandException.class) + public void testDaysOffsetDoesNotAllowNegativeOffsets() throws CommandException { + parser.parse("-5d"); + } + + @Test(expected = CommandException.class) + public void testDaysOffsetRequiresIntegerOffsets() throws CommandException { + parser.parse("5.5d"); + } + + @Test(expected = CommandException.class) + public void testDaysOffsetIsCaseSensitive() throws CommandException { + parser.parse("5D"); + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vm-jmx/client-cli/src/test/java/com/redhat/thermostat/vm/jmx/client/cli/locale/LocaleResourcesTest.java Fri Oct 28 15:24:30 2016 -0400 @@ -0,0 +1,53 @@ +/* + * Copyright 2012-2016 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * <http://www.gnu.org/licenses/>. + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.vm.jmx.client.cli.locale; + +import com.redhat.thermostat.testutils.AbstractLocaleResourcesTest; + +public class LocaleResourcesTest extends AbstractLocaleResourcesTest<LocaleResources> { + + @Override + protected Class<LocaleResources> getEnumClass() { + return LocaleResources.class; + } + + @Override + protected String getResourceBundle() { + return LocaleResources.RESOURCE_BUNDLE; + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vm-jmx/client-core/src/main/java/com/redhat/thermostat/vm/jmx/client/core/JmxToggleNotificationRequest.java Fri Oct 28 15:24:30 2016 -0400 @@ -0,0 +1,127 @@ +/* + * Copyright 2012-2016 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * <http://www.gnu.org/licenses/>. + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.vm.jmx.client.core; + +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.Response; +import com.redhat.thermostat.storage.core.AgentId; +import com.redhat.thermostat.storage.core.VmRef; +import com.redhat.thermostat.storage.dao.AgentInfoDAO; +import com.redhat.thermostat.vm.jmx.common.JmxCommand; + +import java.net.InetSocketAddress; + +public class JmxToggleNotificationRequest { + + public 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, 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, boolean enable) { + AgentId targetId = new AgentId(vm.getHostRef().getAgentId()); + + InetSocketAddress target = agentDAO.getAgentInformation(targetId).getRequestQueueAddress(); + Request req = new Request(RequestType.RESPONSE_EXPECTED, target); + + req.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())); + req.setParameter(JmxCommand.VM_ID, vm.getVmId()); + + JmxToggleResponseListener listener = factory.createListener(successAction, failureAction); + req.addListener(listener); + + 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; + } + + @Override + public void fireComplete(Request request, Response response) { + switch (response.getType()) { + case OK: + successAction.run(); + break; + default: + failureAction.run(); + break; + } + } + + } + + static class JmxToggleResponseListenerFactory { + + public JmxToggleResponseListener createListener(Runnable successAction, + Runnable failureAction) { + return new JmxToggleResponseListener(successAction, failureAction); + } + + } +} +
--- a/vm-jmx/client-core/src/main/java/com/redhat/thermostat/vm/jmx/client/core/internal/JmxNotificationsViewController.java Tue Nov 15 18:18:31 2016 -0500 +++ b/vm-jmx/client-core/src/main/java/com/redhat/thermostat/vm/jmx/client/core/internal/JmxNotificationsViewController.java Fri Oct 28 15:24:30 2016 -0400 @@ -65,6 +65,7 @@ import com.redhat.thermostat.vm.jmx.common.JmxNotification; import com.redhat.thermostat.vm.jmx.common.JmxNotificationDAO; import com.redhat.thermostat.vm.jmx.common.JmxNotificationStatus; +import com.redhat.thermostat.vm.jmx.client.core.JmxToggleNotificationRequest; public class JmxNotificationsViewController implements InformationServiceController<VmRef> {
--- a/vm-jmx/client-core/src/main/java/com/redhat/thermostat/vm/jmx/client/core/internal/JmxToggleNotificationRequest.java Tue Nov 15 18:18:31 2016 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,127 +0,0 @@ -/* - * Copyright 2012-2016 Red Hat, Inc. - * - * This file is part of Thermostat. - * - * Thermostat is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published - * by the Free Software Foundation; either version 2, or (at your - * option) any later version. - * - * Thermostat is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Thermostat; see the file COPYING. If not see - * <http://www.gnu.org/licenses/>. - * - * Linking this code with other modules is making a combined work - * based on this code. Thus, the terms and conditions of the GNU - * General Public License cover the whole combination. - * - * As a special exception, the copyright holders of this code give - * you permission to link this code with independent modules to - * produce an executable, regardless of the license terms of these - * independent modules, and to copy and distribute the resulting - * executable under terms of your choice, provided that you also - * meet, for each linked independent module, the terms and conditions - * of the license of that module. An independent module is a module - * which is not derived from or based on this code. If you modify - * this code, you may extend this exception to your version of the - * library, but you are not obligated to do so. If you do not wish - * to do so, delete this exception statement from your version. - */ - -package com.redhat.thermostat.vm.jmx.client.core.internal; - -import java.net.InetSocketAddress; - -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.Response; -import com.redhat.thermostat.storage.core.AgentId; -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, 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, boolean enable) { - AgentId targetId = new AgentId(vm.getHostRef().getAgentId()); - - InetSocketAddress target = agentDAO.getAgentInformation(targetId).getRequestQueueAddress(); - Request req = new Request(RequestType.RESPONSE_EXPECTED, target); - - req.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())); - req.setParameter(JmxCommand.VM_ID, vm.getVmId()); - - JmxToggleResponseListener listener = factory.createListener(successAction, failureAction); - req.addListener(listener); - - 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; - } - - @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); - } - - } -} -
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vm-jmx/client-core/src/test/java/com/redhat/thermostat/vm/jmx/client/core/JmxToggleNotificationRequestTest.java Fri Oct 28 15:24:30 2016 -0400 @@ -0,0 +1,204 @@ +/* + * Copyright 2012-2016 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * <http://www.gnu.org/licenses/>. + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.vm.jmx.client.core; + +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.AgentId; +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.JmxToggleNotificationRequest.JmxToggleResponseListener; +import com.redhat.thermostat.vm.jmx.client.core.JmxToggleNotificationRequest.JmxToggleResponseListenerFactory; +import com.redhat.thermostat.vm.jmx.common.JmxCommand; + +public class JmxToggleNotificationRequestTest { + + private static final String HOST = "example.com"; + private static final int PORT = 0; + + private RequestQueue queue; + private ArgumentCaptor<Request> requestCaptor; + private HostRef host; + private VmRef vm; + private AgentInfoDAO agentDAO; + private AgentInformation agentInfo; + private JmxToggleResponseListenerFactory factory; + private JmxToggleResponseListener listener; + private Runnable successAction; + private Runnable failureAction; + private JmxToggleNotificationRequest toggleReq; + + @Before + public void setUp() { + queue = mock(RequestQueue.class); + requestCaptor = ArgumentCaptor.forClass(Request.class); + + final String AGENT_ID = "some-id"; + AgentId agentId = new AgentId(AGENT_ID); + host = mock(HostRef.class); + when(host.getAgentId()).thenReturn(AGENT_ID); + vm = mock(VmRef.class); + when(vm.getHostRef()).thenReturn(host); + + agentDAO = mock(AgentInfoDAO.class); + + agentInfo = mock(AgentInformation.class); + when(agentInfo.getRequestQueueAddress()).thenReturn(new InetSocketAddress(HOST, PORT)); + when(agentDAO.getAgentInformation(agentId)).thenReturn(agentInfo); + + 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 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(vm.getVmId(), req.getParameter(JmxCommand.VM_ID)); + + 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(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(vm.getVmId(), req.getParameter(JmxCommand.VM_ID)); + + 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<Void>() { + @Override + public Void answer(InvocationOnMock invocation) throws Throwable { + // Fire complete OK + Request req = (Request) invocation.getArguments()[0]; + Collection<RequestResponseListener> 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<Void>() { + @Override + public Void answer(InvocationOnMock invocation) throws Throwable { + // Fire complete ERROR + Request req = (Request) invocation.getArguments()[0]; + Collection<RequestResponseListener> 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)); + } +} +
--- a/vm-jmx/client-core/src/test/java/com/redhat/thermostat/vm/jmx/client/core/internal/JmxNotificationsViewControllerTest.java Tue Nov 15 18:18:31 2016 -0500 +++ b/vm-jmx/client-core/src/test/java/com/redhat/thermostat/vm/jmx/client/core/internal/JmxNotificationsViewControllerTest.java Fri Oct 28 15:24:30 2016 -0400 @@ -38,7 +38,6 @@ import static org.junit.Assert.assertEquals; import static org.mockito.Matchers.any; -import static org.mockito.Matchers.anyBoolean; import static org.mockito.Matchers.eq; import static org.mockito.Matchers.isA; import static org.mockito.Mockito.atLeastOnce; @@ -57,6 +56,7 @@ import com.redhat.thermostat.storage.dao.VmInfoDAO; import com.redhat.thermostat.storage.model.AgentInformation; import com.redhat.thermostat.storage.model.VmInfo; +import com.redhat.thermostat.vm.jmx.client.core.JmxToggleNotificationRequest; import org.junit.Before; import org.junit.Test; import org.mockito.ArgumentCaptor;
--- a/vm-jmx/client-core/src/test/java/com/redhat/thermostat/vm/jmx/client/core/internal/JmxToggleNotificationRequestTest.java Tue Nov 15 18:18:31 2016 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,204 +0,0 @@ -/* - * Copyright 2012-2016 Red Hat, Inc. - * - * This file is part of Thermostat. - * - * Thermostat is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published - * by the Free Software Foundation; either version 2, or (at your - * option) any later version. - * - * Thermostat is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Thermostat; see the file COPYING. If not see - * <http://www.gnu.org/licenses/>. - * - * Linking this code with other modules is making a combined work - * based on this code. Thus, the terms and conditions of the GNU - * General Public License cover the whole combination. - * - * As a special exception, the copyright holders of this code give - * you permission to link this code with independent modules to - * produce an executable, regardless of the license terms of these - * independent modules, and to copy and distribute the resulting - * executable under terms of your choice, provided that you also - * meet, for each linked independent module, the terms and conditions - * of the license of that module. An independent module is a module - * which is not derived from or based on this code. If you modify - * this code, you may extend this exception to your version of the - * library, but you are not obligated to do so. If you do not wish - * to do so, delete this exception statement from your version. - */ - -package com.redhat.thermostat.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.AgentId; -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 { - - private static final String HOST = "example.com"; - private static final int PORT = 0; - - private RequestQueue queue; - private ArgumentCaptor<Request> requestCaptor; - private HostRef host; - private VmRef vm; - private AgentInfoDAO agentDAO; - private AgentInformation agentInfo; - private JmxToggleResponseListenerFactory factory; - private JmxToggleResponseListener listener; - private Runnable successAction; - private Runnable failureAction; - private JmxToggleNotificationRequest toggleReq; - - @Before - public void setUp() { - queue = mock(RequestQueue.class); - requestCaptor = ArgumentCaptor.forClass(Request.class); - - final String AGENT_ID = "some-id"; - AgentId agentId = new AgentId(AGENT_ID); - host = mock(HostRef.class); - when(host.getAgentId()).thenReturn(AGENT_ID); - vm = mock(VmRef.class); - when(vm.getHostRef()).thenReturn(host); - - agentDAO = mock(AgentInfoDAO.class); - - agentInfo = mock(AgentInformation.class); - when(agentInfo.getRequestQueueAddress()).thenReturn(new InetSocketAddress(HOST, PORT)); - when(agentDAO.getAgentInformation(agentId)).thenReturn(agentInfo); - - 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 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(vm.getVmId(), req.getParameter(JmxCommand.VM_ID)); - - 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(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(vm.getVmId(), req.getParameter(JmxCommand.VM_ID)); - - 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<Void>() { - @Override - public Void answer(InvocationOnMock invocation) throws Throwable { - // Fire complete OK - Request req = (Request) invocation.getArguments()[0]; - Collection<RequestResponseListener> 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<Void>() { - @Override - public Void answer(InvocationOnMock invocation) throws Throwable { - // Fire complete ERROR - Request req = (Request) invocation.getArguments()[0]; - Collection<RequestResponseListener> 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)); - } -} -
--- a/vm-jmx/distribution/pom.xml Tue Nov 15 18:18:31 2016 -0500 +++ b/vm-jmx/distribution/pom.xml Fri Oct 28 15:24:30 2016 -0400 @@ -92,6 +92,11 @@ </dependency> <dependency> <groupId>com.redhat.thermostat</groupId> + <artifactId>thermostat-vm-jmx-client-cli</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>com.redhat.thermostat</groupId> <artifactId>thermostat-vm-jmx-client-core</artifactId> <version>${project.version}</version> </dependency>
--- a/vm-jmx/distribution/thermostat-plugin.xml Tue Nov 15 18:18:31 2016 -0500 +++ b/vm-jmx/distribution/thermostat-plugin.xml Fri Oct 28 15:24:30 2016 -0400 @@ -39,6 +39,72 @@ <plugin xmlns="http://icedtea.classpath.org/thermostat/plugins/v1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://icedtea.classpath.org/thermostat/plugins/v1.0 thermostat-plugin.xsd"> + <commands> + <command> + <name>notifications</name> + <summary>manage JMX notifications on the given VM</summary> + <description>Manage JMX notification monitoring on the given JVM.</description> + <subcommands> + <subcommand> + <name>status</name> + <description>Check JMX notification monitoring status</description> + </subcommand> + <subcommand> + <name>enable</name> + <description>Enable JMX notification monitoring</description> + <options> + <option> + <long>follow</long> + <short>f</short> + <required>false</required> + <description>Start following JMX events immediately</description> + </option> + </options> + </subcommand> + <subcommand> + <name>disable</name> + <description>Disable JMX notification monitoring</description> + </subcommand> + <subcommand> + <name>follow</name> + <description>Follow JMX notification events</description> + </subcommand> + <subcommand> + <name>show</name> + <description>Show all JMX events for the JVM</description> + <options> + <option> + <long>since</long> + <argument>time</argument> + <description>only show events occurring since the given instant. Dates must be expressed 1) in milliseconds + since the Unix epoch, or 2) as an integer offset from present in seconds, minutes, hours, or days, of + the form "30s" for the last 30 seconds, or "1d" for the last day, for example.</description> + </option> + </options> + </subcommand> + </subcommands> + <options> + <option> + <long>vmId</long> + <short>v</short> + <argument>id</argument> + <required>true</required> + </option> + </options> + <environments> + <environment>cli</environment> + <environment>shell</environment> + </environments> + <bundles> + <bundle><symbolic-name>com.redhat.thermostat.vm.jmx.client.cli</symbolic-name><version>${project.version}</version></bundle> + <bundle><symbolic-name>com.redhat.thermostat.vm.jmx.common</symbolic-name><version>${project.version}</version></bundle> + <bundle><symbolic-name>com.redhat.thermostat.vm.jmx.client.core</symbolic-name><version>${project.version}</version></bundle> + <bundle><symbolic-name>com.redhat.thermostat.web.common</symbolic-name><version>${project.version}</version></bundle> + <bundle><symbolic-name>com.redhat.thermostat.web.client</symbolic-name><version>${project.version}</version></bundle> + </bundles> + </command> + </commands> + <extensions> <extension> <name>gui</name>