Mercurial > hg > thermostat-ng > agent
changeset 2448:f3616b75be2c
Add facilities for implementing cli commands and CompleterServices at once
Implement this dual-functionality for BytemanControlCommand and
StoragePopulatorCommand as well.
Reviewed-by: neugens
Review-thread: http://icedtea.classpath.org/pipermail/thermostat/2016-July/020212.html
Review-thread: http://icedtea.classpath.org/pipermail/thermostat/2016-August/020597.html
Review-thread: http://icedtea.classpath.org/pipermail/thermostat/2016-September/020814.html
author | Andrew Azores <aazores@redhat.com> |
---|---|
date | Wed, 14 Sep 2016 11:05:00 -0400 |
parents | b620db651e19 |
children | bce16ab0a34a |
files | common/core/src/main/java/com/redhat/thermostat/common/cli/AbstractCompleterCommand.java common/core/src/main/java/com/redhat/thermostat/common/cli/LocaleResources.java common/core/src/main/resources/com/redhat/thermostat/common/cli/locale/strings.properties common/core/src/test/java/com/redhat/thermostat/common/cli/AbstractCompleterCommandTest.java dev/storage-populator/command/pom.xml dev/storage-populator/command/src/main/java/com/redhat/thermostat/storage/populator/StoragePopulatorCommand.java dev/storage-populator/command/src/main/java/com/redhat/thermostat/storage/populator/internal/Activator.java dev/storage-populator/command/src/main/java/com/redhat/thermostat/storage/populator/internal/StoragePopulatorCompleterService.java dev/storage-populator/command/src/test/java/com/redhat/thermostat/storage/populator/StoragePopulatorCommandTest.java dev/storage-populator/command/src/test/java/com/redhat/thermostat/storage/populator/internal/ActivatorTest.java dev/storage-populator/command/src/test/java/com/redhat/thermostat/storage/populator/internal/StoragePopulatorCompleterServiceTest.java vm-byteman/client-cli/pom.xml vm-byteman/client-cli/src/main/java/com/redhat/thermostat/vm/byteman/client/cli/BytemanControlCommand.java vm-byteman/client-cli/src/main/java/com/redhat/thermostat/vm/byteman/client/cli/internal/Activator.java vm-byteman/client-cli/src/main/java/com/redhat/thermostat/vm/byteman/client/cli/internal/BytemanCompleterService.java vm-byteman/client-cli/src/main/java/com/redhat/thermostat/vm/byteman/client/cli/internal/BytemanControlCommand.java vm-byteman/client-cli/src/test/java/com/redhat/thermostat/vm/byteman/client/cli/BytemanControlCommandTest.java vm-byteman/client-cli/src/test/java/com/redhat/thermostat/vm/byteman/client/cli/internal/ActivatorTest.java vm-byteman/client-cli/src/test/java/com/redhat/thermostat/vm/byteman/client/cli/internal/BytemanCompleterServiceTest.java vm-byteman/client-cli/src/test/java/com/redhat/thermostat/vm/byteman/client/cli/internal/BytemanControlCommandTest.java |
diffstat | 20 files changed, 1168 insertions(+), 1524 deletions(-) [+] |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/common/core/src/main/java/com/redhat/thermostat/common/cli/AbstractCompleterCommand.java Wed Sep 14 11:05:00 2016 -0400 @@ -0,0 +1,88 @@ +/* + * 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.common.cli; + +import com.redhat.thermostat.shared.locale.Translate; +import org.osgi.service.component.ComponentContext; +import org.osgi.service.component.annotations.Activate; + +import java.util.Collections; +import java.util.Objects; +import java.util.Set; + +/** + * An abstract class to help with implementing commands which also provide tab completions for arguments within + * Thermostat shell. This is most useful in cases where an argument and its completions are unique to a single + * command; if your plugin provides multiple commands which have shared options and shared arguments/completions, + * more flexibility in the implementation can be had by implementing a {@link CompleterService} directly and separately + * from the Command implementations. + * + * This class is meant for use with OSGi Declarative Services. Subclasses should be annotated with @Component, @Service, + * and @Property(name = Command.NAME, value = "command-name-here") at minimum. + */ +public abstract class AbstractCompleterCommand extends AbstractCommand implements CompleterService { + + private static final Translate<LocaleResources> t = LocaleResources.createLocalizer(); + + private String commandName = null; + + /** + * If implementation subclasses override this method, they <b>must</b> call through to this super implementation as + * well, or else tab completions will fail to be registered correctly. + */ + @Activate + protected void activate(ComponentContext context) { + commandName = validateCommandName((String) context.getProperties().get(Command.NAME)); + } + + /** + * The set of command names for which tab completions are provided. + * + * Implementations of this class only provide completions for a single command name value. + * This value is expected to be provided by an @Property annotation defining a {@link Command.NAME} property. + * @return a singleton set containing the command name + */ + @Override + public final Set<String> getCommands() { + return Collections.singleton(validateCommandName(commandName)); + } + + private String validateCommandName(String name) { + return Objects.requireNonNull(name, t.localize(LocaleResources.MISSING_COMMAND_NAME, getClass().getName()).getContents()); + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/common/core/src/main/java/com/redhat/thermostat/common/cli/LocaleResources.java Wed Sep 14 11:05:00 2016 -0400 @@ -0,0 +1,51 @@ +/* + * 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.common.cli; + +import com.redhat.thermostat.shared.locale.Translate; + +public enum LocaleResources { + MISSING_COMMAND_NAME, + ; + + public static final String RESOURCE_BUNDLE = + "com.redhat.thermostat.common.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/common/core/src/main/resources/com/redhat/thermostat/common/cli/locale/strings.properties Wed Sep 14 11:05:00 2016 -0400 @@ -0,0 +1,1 @@ +MISSING_COMMAND_NAME=The implementation class {0} does not define an OSGi property for COMMAND_NAME, which is required. \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/common/core/src/test/java/com/redhat/thermostat/common/cli/AbstractCompleterCommandTest.java Wed Sep 14 11:05:00 2016 -0400 @@ -0,0 +1,117 @@ +/* + * 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.common.cli; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.osgi.service.component.ComponentContext; + +import java.util.Collections; +import java.util.Dictionary; +import java.util.Map; + +import static org.hamcrest.CoreMatchers.equalTo; +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 AbstractCompleterCommandTest { + + public static final String COMMAND_NAME = "StubCompleterCommand"; + + @Rule + public ExpectedException thrown = ExpectedException.none(); + + private AbstractCompleterCommand command; + private ComponentContext componentContext; + + @Before + public void setup() { + command = new StubCompleterCommand(); + componentContext = mock(ComponentContext.class); + Dictionary dict = mock(Dictionary.class); + when(dict.get(Command.NAME)).thenReturn(COMMAND_NAME); + when(componentContext.getProperties()).thenReturn(dict); + } + + @Test + public void testActivationSetsCommandName() { + command.activate(componentContext); + assertThat(command.getCommands(), is(equalTo(Collections.singleton(COMMAND_NAME)))); + } + + @Test + public void testThrowsExceptionIfGetCommandsCalledBeforeActivation() { + thrown.expect(NullPointerException.class); + thrown.expectMessage("The implementation class " + + "com.redhat.thermostat.common.cli.AbstractCompleterCommandTest$StubCompleterCommand does not define an" + + " OSGi property for COMMAND_NAME, which is required."); + command.getCommands(); + } + + @Test + public void testActivationFailsIfCommandNamePropertyNotSet() { + Dictionary dict = mock(Dictionary.class); + when(componentContext.getProperties()).thenReturn(dict); + thrown.expect(NullPointerException.class); + thrown.expectMessage("The implementation class " + + "com.redhat.thermostat.common.cli.AbstractCompleterCommandTest$StubCompleterCommand does not define an" + + " OSGi property for COMMAND_NAME, which is required."); + command.activate(componentContext); + } + + private static class StubCompleterCommand extends AbstractCompleterCommand { + @Override + public void run(CommandContext ctx) throws CommandException { + // no-op + } + + @Override + public Map<CliCommandOption, ? extends TabCompleter> getOptionCompleters() { + return Collections.emptyMap(); + } + + @Override + public Map<String, Map<CliCommandOption, ? extends TabCompleter>> getSubcommandCompleters() { + return Collections.emptyMap(); + } + } + +}
--- a/dev/storage-populator/command/pom.xml Tue Sep 13 11:35:25 2016 -0400 +++ b/dev/storage-populator/command/pom.xml Wed Sep 14 11:05:00 2016 -0400 @@ -61,7 +61,6 @@ <instructions> <Bundle-SymbolicName>com.redhat.thermostat.storage.populator</Bundle-SymbolicName> <Bundle-Vendor>Red Hat, Inc.</Bundle-Vendor> - <Bundle-Activator>com.redhat.thermostat.storage.populator.internal.Activator</Bundle-Activator> <Export-Package> com.redhat.thermostat.storage.populator, </Export-Package>
--- a/dev/storage-populator/command/src/main/java/com/redhat/thermostat/storage/populator/StoragePopulatorCommand.java Tue Sep 13 11:35:25 2016 -0400 +++ b/dev/storage-populator/command/src/main/java/com/redhat/thermostat/storage/populator/StoragePopulatorCommand.java Wed Sep 14 11:05:00 2016 -0400 @@ -39,23 +39,27 @@ import java.io.File; import java.io.IOException; import java.nio.file.Files; +import java.util.Collections; import java.util.HashMap; import java.util.Map; -import java.util.logging.Logger; -import com.redhat.thermostat.common.cli.AbstractCommand; +import com.redhat.thermostat.common.cli.AbstractCompleterCommand; import com.redhat.thermostat.common.cli.Arguments; +import com.redhat.thermostat.common.cli.CliCommandOption; +import com.redhat.thermostat.common.cli.Command; import com.redhat.thermostat.common.cli.CommandContext; import com.redhat.thermostat.common.cli.CommandException; +import com.redhat.thermostat.common.cli.CompletionFinderTabCompleter; import com.redhat.thermostat.common.cli.Console; import com.redhat.thermostat.common.cli.DependencyServices; -import com.redhat.thermostat.common.utils.LoggingUtils; +import com.redhat.thermostat.common.cli.TabCompleter; import com.redhat.thermostat.shared.config.CommonPaths; import com.redhat.thermostat.shared.locale.Translate; import com.redhat.thermostat.storage.dao.AgentInfoDAO; import com.redhat.thermostat.storage.dao.HostInfoDAO; import com.redhat.thermostat.storage.dao.NetworkInterfaceInfoDAO; import com.redhat.thermostat.storage.dao.VmInfoDAO; +import com.redhat.thermostat.storage.populator.internal.StoragePopulatorConfigFinder; import com.redhat.thermostat.storage.populator.internal.config.ConfigItem; import com.redhat.thermostat.storage.populator.internal.config.PopulationConfig; import com.redhat.thermostat.storage.populator.internal.dependencies.SharedState; @@ -67,62 +71,105 @@ import com.redhat.thermostat.storage.populator.internal.VmInfoPopulator; import com.redhat.thermostat.storage.populator.internal.LocaleResources; import com.redhat.thermostat.thread.dao.ThreadDao; +import org.apache.felix.scr.annotations.Component; +import org.apache.felix.scr.annotations.Property; +import org.apache.felix.scr.annotations.Reference; +import org.apache.felix.scr.annotations.ReferenceCardinality; +import org.apache.felix.scr.annotations.ReferencePolicy; +import org.apache.felix.scr.annotations.Service; -public class StoragePopulatorCommand extends AbstractCommand { - - public static final String COMMAND_NAME = "storage-populator"; +@Component +@Service // Command and CompleterService +@Property(name = Command.NAME, value = "storage-populator") +public class StoragePopulatorCommand extends AbstractCompleterCommand { private static final Translate<LocaleResources> translator = LocaleResources.createLocalizer(); - private static final Logger logger = LoggingUtils.getLogger(StoragePopulatorCommand.class); - private static final String CONFIG_FILE_NAME = "config"; + static final CliCommandOption CONFIG_OPTION = new CliCommandOption("c", "config", true, "the json config file to use", true); private final Map<String, CollectionPopulator> populators = new HashMap<>(); private final DependencyServices dependencyServices = new DependencyServices(); + @Reference(policy = ReferencePolicy.DYNAMIC, cardinality = ReferenceCardinality.OPTIONAL_UNARY) private CommonPaths paths; + + @Reference(policy = ReferencePolicy.DYNAMIC, cardinality = ReferenceCardinality.OPTIONAL_UNARY) private HostInfoDAO hostInfoDAO; + + @Reference(policy = ReferencePolicy.DYNAMIC, cardinality = ReferenceCardinality.OPTIONAL_UNARY) private AgentInfoDAO agentInfoDAO; + + @Reference(policy = ReferencePolicy.DYNAMIC, cardinality = ReferenceCardinality.OPTIONAL_UNARY) private VmInfoDAO vmInfoDAO; + + @Reference(policy = ReferencePolicy.DYNAMIC, cardinality = ReferenceCardinality.OPTIONAL_UNARY) private NetworkInterfaceInfoDAO networkInfoDAO; - private ThreadDao threadDao; + + @Reference(policy = ReferencePolicy.DYNAMIC, cardinality = ReferenceCardinality.OPTIONAL_UNARY) + private ThreadDao threadDAO; private Console console; - public void setPaths(CommonPaths paths) { + public void bindPaths(CommonPaths paths) { dependencyServices.addService(CommonPaths.class, paths); } - public void setHostInfoDAO(HostInfoDAO dao) { + public void unbindPaths(CommonPaths paths) { + dependencyServices.removeService(CommonPaths.class); + } + + public void bindHostInfoDAO(HostInfoDAO dao) { dependencyServices.addService(HostInfoDAO.class, dao); } - public void setAgentInfoDAO(AgentInfoDAO dao) { + public void unbindHostInfoDAO(HostInfoDAO dao) { + dependencyServices.removeService(HostInfoDAO.class); + } + + public void bindAgentInfoDAO(AgentInfoDAO dao) { dependencyServices.addService(AgentInfoDAO.class, dao); } - public void setVmInfoDAO(VmInfoDAO dao) { + public void unbindAgentInfoDAO(AgentInfoDAO dao) { + dependencyServices.removeService(AgentInfoDAO.class); + } + + public void bindVmInfoDAO(VmInfoDAO dao) { dependencyServices.addService(VmInfoDAO.class, dao); } - public void setNetworkInfoDAO(NetworkInterfaceInfoDAO dao) { + public void unbindVmInfoDAO(VmInfoDAO dao) { + dependencyServices.removeService(VmInfoDAO.class); + } + + public void bindNetworkInfoDAO(NetworkInterfaceInfoDAO dao) { dependencyServices.addService(NetworkInterfaceInfoDAO.class, dao); } - public void setThreadDao(ThreadDao dao) { + public void unbindNetworkInfoDAO(NetworkInterfaceInfoDAO dao) { + dependencyServices.removeService(NetworkInterfaceInfoDAO.class); + } + + public void bindThreadDAO(ThreadDao dao) { dependencyServices.addService(ThreadDao.class, dao); } - public void setServicesUnavailable() { - dependencyServices.removeService(CommonPaths.class); - dependencyServices.removeService(HostInfoDAO.class); - dependencyServices.removeService(AgentInfoDAO.class); - dependencyServices.removeService(VmInfoDAO.class); - dependencyServices.removeService(NetworkInterfaceInfoDAO.class); + public void unbindThreadDAO(ThreadDao dao) { dependencyServices.removeService(ThreadDao.class); } @Override + public Map<CliCommandOption, ? extends TabCompleter> getOptionCompleters() { + CompletionFinderTabCompleter completer = new CompletionFinderTabCompleter(new StoragePopulatorConfigFinder(dependencyServices)); + return Collections.singletonMap(CONFIG_OPTION, completer); + } + + @Override + public Map<String, Map<CliCommandOption, ? extends TabCompleter>> getSubcommandCompleters() { + return Collections.emptyMap(); + } + + @Override public void run(CommandContext ctx) throws CommandException { console = ctx.getConsole(); @@ -144,8 +191,8 @@ requireNonNull(networkInfoDAO, translator.localize(LocaleResources.NETWORK_SERVICE_UNAVAILABLE)); - threadDao = dependencyServices.getService(ThreadDao.class); - requireNonNull(threadDao, translator.localize(LocaleResources.THREAD_SERVICE_UNAVAILABLE)); + threadDAO = dependencyServices.getService(ThreadDao.class); + requireNonNull(threadDAO, translator.localize(LocaleResources.THREAD_SERVICE_UNAVAILABLE)); HostInfoPopulator hostInfoPopulator = new HostInfoPopulator(hostInfoDAO); populators.put(hostInfoPopulator.getHandledCollection(), hostInfoPopulator); @@ -159,7 +206,7 @@ NetworkInfoPopulator networkInfoPopulator = new NetworkInfoPopulator(networkInfoDAO); populators.put(networkInfoPopulator.getHandledCollection(), networkInfoPopulator); - ThreadPopulator threadPopulator = new ThreadPopulator(threadDao); + ThreadPopulator threadPopulator = new ThreadPopulator(threadDAO); populators.put(threadPopulator.getHandledCollection(), threadPopulator); try { @@ -193,7 +240,7 @@ * Package-private to allow overriding for testing. */ File getConfigFile(Arguments args) { - return new File(getConfigFileDirectoryPath(paths) + args.getArgument(CONFIG_FILE_NAME)); + return new File(getConfigFileDirectoryPath(paths) + args.getArgument(CONFIG_OPTION.getLongOpt())); } public static String getConfigFileDirectoryPath(CommonPaths paths) {
--- a/dev/storage-populator/command/src/main/java/com/redhat/thermostat/storage/populator/internal/Activator.java Tue Sep 13 11:35:25 2016 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,110 +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.storage.populator.internal; - -import com.redhat.thermostat.common.MultipleServiceTracker; -import com.redhat.thermostat.common.cli.Command; -import com.redhat.thermostat.shared.config.CommonPaths; -import com.redhat.thermostat.storage.dao.AgentInfoDAO; -import com.redhat.thermostat.storage.dao.HostInfoDAO; -import com.redhat.thermostat.storage.dao.NetworkInterfaceInfoDAO; -import com.redhat.thermostat.storage.dao.VmInfoDAO; -import com.redhat.thermostat.storage.populator.StoragePopulatorCommand; -import com.redhat.thermostat.thread.dao.ThreadDao; -import org.osgi.framework.BundleActivator; -import org.osgi.framework.BundleContext; - -import java.util.Hashtable; -import java.util.Map; - -public class Activator implements BundleActivator { - - private MultipleServiceTracker cmdDepsTracker; - - @Override - public void start(final BundleContext context) throws Exception { - - final StoragePopulatorCommand command = new StoragePopulatorCommand(); - Hashtable<String,String> properties = new Hashtable<>(); - properties.put(Command.NAME, "storage-populator"); - context.registerService(Command.class, command, properties); - - Class<?>[] deps = new Class<?>[] { - CommonPaths.class, - HostInfoDAO.class, - AgentInfoDAO.class, - VmInfoDAO.class, - NetworkInterfaceInfoDAO.class, - ThreadDao.class, - }; - - cmdDepsTracker = new MultipleServiceTracker(context, deps, new MultipleServiceTracker.Action() { - - @Override - public void dependenciesAvailable(Map<String, Object> services) { - CommonPaths paths = (CommonPaths) services.get(CommonPaths.class.getName()); - HostInfoDAO hostInfoDAO = (HostInfoDAO) services.get(HostInfoDAO.class.getName()); - AgentInfoDAO agentInfoDAO = (AgentInfoDAO) services.get(AgentInfoDAO.class.getName()); - VmInfoDAO vmInfoDAO = (VmInfoDAO) services.get(VmInfoDAO.class.getName()); - NetworkInterfaceInfoDAO networkInfoDAO = (NetworkInterfaceInfoDAO) - services.get(NetworkInterfaceInfoDAO.class.getName()); - ThreadDao threadDao = (ThreadDao) services.get(ThreadDao.class.getName()); - - command.setPaths(paths); - command.setHostInfoDAO(hostInfoDAO); - command.setAgentInfoDAO(agentInfoDAO); - command.setVmInfoDAO(vmInfoDAO); - command.setNetworkInfoDAO(networkInfoDAO); - command.setThreadDao(threadDao); - } - - @Override - public void dependenciesUnavailable() { - command.setServicesUnavailable(); - } - }); - cmdDepsTracker.open(); - } - - @Override - public void stop(BundleContext context) throws Exception { - if (cmdDepsTracker != null) { - cmdDepsTracker.close(); - } - } - -}
--- a/dev/storage-populator/command/src/main/java/com/redhat/thermostat/storage/populator/internal/StoragePopulatorCompleterService.java Tue Sep 13 11:35:25 2016 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,80 +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.storage.populator.internal; - -import com.redhat.thermostat.common.cli.AbstractCompleterService; -import com.redhat.thermostat.common.cli.CliCommandOption; -import com.redhat.thermostat.common.cli.CompleterService; -import com.redhat.thermostat.common.cli.CompletionFinderTabCompleter; -import com.redhat.thermostat.common.cli.TabCompleter; -import com.redhat.thermostat.shared.config.CommonPaths; -import com.redhat.thermostat.storage.populator.StoragePopulatorCommand; -import org.apache.felix.scr.annotations.Component; -import org.apache.felix.scr.annotations.Reference; -import org.apache.felix.scr.annotations.Service; - -import java.util.Collections; -import java.util.Map; -import java.util.Set; - -@Component -@Service(CompleterService.class) -@Reference(name = "commonPaths", referenceInterface = CommonPaths.class) -public class StoragePopulatorCompleterService extends AbstractCompleterService { - - static final CliCommandOption CONFIG_OPTION = new CliCommandOption("c", "config", true, "the json config file to use", true); - - @Override - public Set<String> getCommands() { - return Collections.singleton(StoragePopulatorCommand.COMMAND_NAME); - } - - @Override - public Map<CliCommandOption, ? extends TabCompleter> getOptionCompleters() { - CompletionFinderTabCompleter completer = new CompletionFinderTabCompleter(new StoragePopulatorConfigFinder(dependencyServices)); - return Collections.singletonMap(CONFIG_OPTION, completer); - } - - public void bindCommonPaths(CommonPaths commonPaths) { - setService(CommonPaths.class, commonPaths); - } - - public void unbindCommonPaths(CommonPaths commonPaths) { - unsetService(CommonPaths.class); - } - -}
--- a/dev/storage-populator/command/src/test/java/com/redhat/thermostat/storage/populator/StoragePopulatorCommandTest.java Tue Sep 13 11:35:25 2016 -0400 +++ b/dev/storage-populator/command/src/test/java/com/redhat/thermostat/storage/populator/StoragePopulatorCommandTest.java Wed Sep 14 11:05:00 2016 -0400 @@ -36,7 +36,11 @@ package com.redhat.thermostat.storage.populator; +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.not; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import static org.mockito.Matchers.any; @@ -49,7 +53,11 @@ import java.io.File; import java.io.IOException; import java.io.PrintStream; +import java.util.Collections; +import java.util.Map; +import com.redhat.thermostat.common.cli.CliCommandOption; +import com.redhat.thermostat.common.cli.TabCompleter; import org.junit.Before; import org.junit.Test; @@ -110,7 +118,6 @@ @Test public void testCommandFailsWhenDependenciesUnavailable() { command = new StoragePopulatorCommand(); - command.setServicesUnavailable(); try { command.run(ctx); @@ -210,18 +217,37 @@ verify(vmInfoDAO, times(vmCount)).putVmInfo(any(VmInfo.class)); } + @Test + public void testProvidesConfigFileCompletions() { + command = new StoragePopulatorCommand(); + setUpServices(); + Map<CliCommandOption, ? extends TabCompleter> map = command.getOptionCompleters(); + assertThat(map.keySet(), is(equalTo(Collections.singleton(StoragePopulatorCommand.CONFIG_OPTION)))); + assertThat(map.get(StoragePopulatorCommand.CONFIG_OPTION), is(not(equalTo(null)))); + } + + @Test + public void testProvidesTabCompletionsEvenWhenCommonPathsUnavailable() { + command = new StoragePopulatorCommand(); + setUpServices(); + command.unbindPaths(mock(CommonPaths.class)); + Map<CliCommandOption, ? extends TabCompleter> map = command.getOptionCompleters(); + assertThat(map.keySet(), is(equalTo(Collections.singleton(StoragePopulatorCommand.CONFIG_OPTION)))); + assertThat(map.get(StoragePopulatorCommand.CONFIG_OPTION), is(not(equalTo(null)))); + } + private void setUpServices () { paths = mock(CommonPaths.class); - command.setPaths(paths); + command.bindPaths(paths); hostInfoDAO = mock(HostInfoDAO.class); - command.setHostInfoDAO(hostInfoDAO); + command.bindHostInfoDAO(hostInfoDAO); agentInfoDAO = mock(AgentInfoDAO.class); - command.setAgentInfoDAO(agentInfoDAO); + command.bindAgentInfoDAO(agentInfoDAO); vmInfoDAO = mock(VmInfoDAO.class); - command.setVmInfoDAO(vmInfoDAO); + command.bindVmInfoDAO(vmInfoDAO); networkInfoDAO = mock(NetworkInterfaceInfoDAO.class); - command.setNetworkInfoDAO(networkInfoDAO); + command.bindNetworkInfoDAO(networkInfoDAO); threadDao = mock(ThreadDao.class); - command.setThreadDao(threadDao); + command.bindThreadDAO(threadDao); } }
--- a/dev/storage-populator/command/src/test/java/com/redhat/thermostat/storage/populator/internal/ActivatorTest.java Tue Sep 13 11:35:25 2016 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,65 +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.storage.populator.internal; - -import static com.redhat.thermostat.testutils.Asserts.assertCommandIsRegistered; -import static com.redhat.thermostat.testutils.Asserts.assertServiceIsNotRegistered; -import static com.redhat.thermostat.testutils.Asserts.assertServiceIsRegistered; -import static org.junit.Assert.assertEquals; -import static org.mockito.Mockito.mock; - -import com.redhat.thermostat.common.cli.CompleterService; -import com.redhat.thermostat.shared.config.CommonPaths; -import org.junit.Test; - -import com.redhat.thermostat.storage.populator.StoragePopulatorCommand; -import com.redhat.thermostat.testutils.StubBundleContext; - -public class ActivatorTest { - @Test - public void verifyActivatorRegistersServices() throws Exception { - StubBundleContext ctx = new StubBundleContext(); - Activator activator = new Activator(); - - activator.start(ctx); - assertCommandIsRegistered(ctx, "storage-populator", StoragePopulatorCommand.class); - activator.stop(ctx); - - assertEquals(0, ctx.getServiceListeners().size()); - } - -}
--- a/dev/storage-populator/command/src/test/java/com/redhat/thermostat/storage/populator/internal/StoragePopulatorCompleterServiceTest.java Tue Sep 13 11:35:25 2016 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,85 +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.storage.populator.internal; - -import com.redhat.thermostat.common.cli.CliCommandOption; -import com.redhat.thermostat.common.cli.TabCompleter; -import com.redhat.thermostat.shared.config.CommonPaths; -import com.redhat.thermostat.storage.populator.StoragePopulatorCommand; -import org.junit.Before; -import org.junit.Test; - -import java.util.Collections; -import java.util.Map; - -import static org.hamcrest.CoreMatchers.equalTo; -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.CoreMatchers.not; -import static org.junit.Assert.assertThat; -import static org.mockito.Mockito.mock; - -public class StoragePopulatorCompleterServiceTest { - - private StoragePopulatorCompleterService service; - - @Before - public void setup() { - service = new StoragePopulatorCompleterService(); - service.bindCommonPaths(mock(CommonPaths.class)); - } - - @Test - public void testOnlyProvidesCompletionForStoragePopulatorCommand() { - assertThat(service.getCommands(), is(equalTo(Collections.singleton(StoragePopulatorCommand.COMMAND_NAME)))); - } - - @Test - public void testProvidesCompleterOnlyForConfigOption() { - Map<CliCommandOption, ? extends TabCompleter> completerMap = service.getOptionCompleters(); - assertThat(completerMap.keySet(), is(equalTo(Collections.singleton(StoragePopulatorCompleterService.CONFIG_OPTION)))); - assertThat(completerMap.get(StoragePopulatorCompleterService.CONFIG_OPTION), is(not(equalTo(null)))); - } - - @Test - public void testConfigOptionProperties() { - assertThat(StoragePopulatorCompleterService.CONFIG_OPTION.getOpt(), is("c")); - assertThat(StoragePopulatorCompleterService.CONFIG_OPTION.getLongOpt(), is("config")); - assertThat(StoragePopulatorCompleterService.CONFIG_OPTION.hasArg(), is(true)); - assertThat(StoragePopulatorCompleterService.CONFIG_OPTION.isRequired(), is(true)); - } - -}
--- a/vm-byteman/client-cli/pom.xml Tue Sep 13 11:35:25 2016 -0400 +++ b/vm-byteman/client-cli/pom.xml Wed Sep 14 11:05:00 2016 -0400 @@ -54,10 +54,11 @@ <extensions>true</extensions> <configuration> <instructions> - <Bundle-Activator>com.redhat.thermostat.vm.byteman.client.cli.internal.Activator</Bundle-Activator> <Bundle-Vendor>Red Hat, Inc.</Bundle-Vendor> <Bundle-SymbolicName>com.redhat.thermostat.vm.byteman.client.cli</Bundle-SymbolicName> - <Export-Package /> + <Export-Package> + com.redhat.thermostat.vm.byteman.client.cli + </Export-Package> <Private-Package> com.redhat.thermostat.vm.byteman.client.cli.internal </Private-Package>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vm-byteman/client-cli/src/main/java/com/redhat/thermostat/vm/byteman/client/cli/BytemanControlCommand.java Wed Sep 14 11:05:00 2016 -0400 @@ -0,0 +1,346 @@ +/* + * 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.byteman.client.cli; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.PrintStream; +import java.net.InetSocketAddress; +import java.nio.charset.Charset; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +import com.redhat.thermostat.client.cli.VmArgument; +import com.redhat.thermostat.client.command.RequestQueue; +import com.redhat.thermostat.common.cli.AbstractCompleterCommand; +import com.redhat.thermostat.common.cli.Arguments; +import com.redhat.thermostat.common.cli.CliCommandOption; +import com.redhat.thermostat.common.cli.Command; +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.common.cli.FileNameTabCompleter; +import com.redhat.thermostat.common.cli.TabCompleter; +import com.redhat.thermostat.common.command.Request; +import com.redhat.thermostat.common.model.Range; +import com.redhat.thermostat.common.utils.StreamUtils; +import com.redhat.thermostat.shared.locale.Translate; +import com.redhat.thermostat.storage.core.AgentId; +import com.redhat.thermostat.storage.core.VmId; +import com.redhat.thermostat.storage.dao.AgentInfoDAO; +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.byteman.client.cli.internal.LocaleResources; +import com.redhat.thermostat.vm.byteman.common.BytemanMetric; +import com.redhat.thermostat.vm.byteman.common.VmBytemanDAO; +import com.redhat.thermostat.vm.byteman.common.VmBytemanStatus; +import com.redhat.thermostat.vm.byteman.common.command.BytemanRequest; +import com.redhat.thermostat.vm.byteman.common.command.BytemanRequestResponseListener; +import com.redhat.thermostat.vm.byteman.common.command.BytemanRequest.RequestAction; +import org.apache.felix.scr.annotations.Component; +import org.apache.felix.scr.annotations.Property; +import org.apache.felix.scr.annotations.Reference; +import org.apache.felix.scr.annotations.ReferenceCardinality; +import org.apache.felix.scr.annotations.ReferencePolicy; +import org.apache.felix.scr.annotations.References; +import org.apache.felix.scr.annotations.Service; + +@Component +@Service +@Property(name = Command.NAME, value = "byteman") +@References({ + @Reference(name = "agentInfoDao", referenceInterface = AgentInfoDAO.class, policy = ReferencePolicy.DYNAMIC, cardinality = ReferenceCardinality.OPTIONAL_UNARY), + @Reference(name = "vmInfoDao", referenceInterface = VmInfoDAO.class, policy = ReferencePolicy.DYNAMIC, cardinality = ReferenceCardinality.OPTIONAL_UNARY), + @Reference(name = "vmBytemanDao", referenceInterface = VmBytemanDAO.class, policy = ReferencePolicy.DYNAMIC, cardinality = ReferenceCardinality.OPTIONAL_UNARY), + @Reference(name = "requestQueue", referenceInterface = RequestQueue.class, policy = ReferencePolicy.DYNAMIC, cardinality = ReferenceCardinality.OPTIONAL_UNARY), + @Reference(name = "fileNameTabCompleter", referenceInterface = FileNameTabCompleter.class, policy = ReferencePolicy.DYNAMIC, cardinality = ReferenceCardinality.OPTIONAL_UNARY) +}) +public class BytemanControlCommand extends AbstractCompleterCommand { + + static final CliCommandOption RULES_OPTION = new CliCommandOption("r", "rules", true, + "a file with Byteman rules to load into a VM", false); + + private static final Translate<LocaleResources> translator = LocaleResources.createLocalizer(); + static final String INJECT_RULE_ACTION = "load"; + static final String UNLOAD_RULE_ACTION = "unload"; + static final String STATUS_ACTION = "status"; + static final String SHOW_ACTION = "show-metrics"; + private static final String RULES_FILE_OPTION = "rules"; + private static final String NO_RULES_LOADED = "<no-loaded-rules>"; + private static final String UNSET_PORT = "<unset>"; + private static final Charset UTF_8_CHARSET = Charset.forName("UTF-8"); + + + private final DependencyServices depServices = new DependencyServices(); + + @Override + public Map<CliCommandOption, ? extends TabCompleter> getOptionCompleters() { + if (!depServices.hasService(FileNameTabCompleter.class)) { + return Collections.emptyMap(); + } + return Collections.singletonMap(RULES_OPTION, depServices.getService(FileNameTabCompleter.class)); + } + + @Override + public Map<String, Map<CliCommandOption, ? extends TabCompleter>> getSubcommandCompleters() { + return Collections.emptyMap(); + } + + @Override + public void run(CommandContext ctx) throws CommandException { + VmArgument vmArgument = VmArgument.required(ctx.getArguments()); + VmId vmId = vmArgument.getVmId(); + + VmInfoDAO vmInfoDAO = depServices.getService(VmInfoDAO.class); + + requireNonNull(vmInfoDAO, translator.localize(LocaleResources.VM_SERVICE_UNAVAILABLE)); + final VmInfo vmInfo = vmInfoDAO.getVmInfo(vmId); + requireNonNull(vmInfo, translator.localize(LocaleResources.VM_SERVICE_UNAVAILABLE)); + + AgentInfoDAO agentInfoDAO = depServices.getService(AgentInfoDAO.class); + final AgentId agentId = new AgentId(vmInfo.getAgentId()); + requireNonNull(agentInfoDAO, translator.localize(LocaleResources.AGENT_SERVICE_UNAVAILABLE)); + + AgentInformation agentInfo = agentInfoDAO.getAgentInformation(agentId); + if (agentInfo == null) { + throw new CommandException(translator.localize(LocaleResources.AGENT_NOT_FOUND, agentId.get())); + } + if (!agentInfo.isAlive()) { + throw new CommandException(translator.localize(LocaleResources.AGENT_DEAD, agentId.get())); + } + + InetSocketAddress target = agentInfo.getRequestQueueAddress(); + + List<String> nonOptionargs = ctx.getArguments().getNonOptionArguments(); + if (nonOptionargs.size() != 1) { + throw new CommandException(translator.localize(LocaleResources.COMMAND_EXPECTED)); + } + VmBytemanDAO bytemanDao = depServices.getService(VmBytemanDAO.class); + requireNonNull(bytemanDao, translator.localize(LocaleResources.BYTEMAN_METRICS_SERVICE_UNAVAILABLE)); + + String command = nonOptionargs.get(0); + + switch (command) { + case INJECT_RULE_ACTION: + injectRules(target, vmInfo, ctx, bytemanDao); + break; + case UNLOAD_RULE_ACTION: + unloadRules(target, vmInfo, ctx, bytemanDao); + break; + case STATUS_ACTION: + showStatus(ctx, vmInfo, bytemanDao); + break; + case SHOW_ACTION: + showMetrics(ctx, vmId, agentId, bytemanDao); + break; + default: + throw new CommandException(translator.localize(LocaleResources.UNKNOWN_COMMAND, command)); + } + } + + /* Unloads byteman rules */ + private void unloadRules(InetSocketAddress target, VmInfo vmInfo, CommandContext ctx, VmBytemanDAO bytemanDao) throws CommandException { + VmId vmId = new VmId(vmInfo.getVmId()); + RequestQueue requestQueue = getRequestQueue(); + VmBytemanStatus status = getVmBytemanStatus(vmId, bytemanDao); + int listenPort = status.getListenPort(); + Request unloadRequest = BytemanRequest.create(target, vmInfo, RequestAction.UNLOAD_RULES, listenPort); + submitRequest(ctx, requestQueue, unloadRequest); + } + + + /* Injects byteman rules */ + private void injectRules(InetSocketAddress target, VmInfo vmInfo, CommandContext ctx, VmBytemanDAO bytemanDao) throws CommandException { + VmId vmId = new VmId(vmInfo.getVmId()); + Arguments args = ctx.getArguments(); + if (!args.hasArgument(RULES_FILE_OPTION)) { + throw new CommandException(translator.localize(LocaleResources.NO_RULE_OPTION)); + } + String ruleFile = args.getArgument(RULES_FILE_OPTION); + + byte[] rulesBytes; + try { + rulesBytes = StreamUtils.readAll(new FileInputStream(new File(ruleFile))); + } catch (FileNotFoundException e) { + throw new CommandException(translator.localize(LocaleResources.RULE_FILE_NOT_FOUND, ruleFile)); + } catch (IOException e) { + throw new CommandException(translator.localize(LocaleResources.ERROR_READING_RULE_FILE, ruleFile)); + } + String rulesContent = new String(rulesBytes, UTF_8_CHARSET); + VmBytemanStatus status = bytemanDao.findBytemanStatus(vmId); + int listenPort = BytemanRequest.NOT_ATTACHED_PORT; + if (status != null) { + listenPort = status.getListenPort(); + } + RequestQueue requestQueue = getRequestQueue(); + Request request = BytemanRequest.create(target, vmInfo, RequestAction.LOAD_RULES, listenPort, rulesContent); + submitRequest(ctx, requestQueue, request); + } + + /* Show metrics retrieved via byteman rules */ + private void showMetrics(CommandContext ctx, VmId vmId, AgentId agentId, VmBytemanDAO bytemanDao) throws CommandException { + // TODO: Make this query configurable with arguments + long now = System.currentTimeMillis(); + long from = now - TimeUnit.MINUTES.toMillis(5); + long to = now; + Range<Long> timeRange = new Range<Long>(from, to); + List<BytemanMetric> metrics = bytemanDao.findBytemanMetrics(timeRange, vmId, agentId); + PrintStream output = ctx.getConsole().getOutput(); + PrintStream out = ctx.getConsole().getOutput(); + if (metrics.isEmpty()) { + out.println(translator.localize(LocaleResources.NO_METRICS_AVAILABLE, vmId.get()).getContents()); + } else { + for (BytemanMetric m: metrics) { + output.println(m.getDataAsJson()); + } + } + } + + /* Show status of loaded byteman rules */ + private void showStatus(CommandContext ctx, VmInfo vmInfo, VmBytemanDAO bytemanDao) throws CommandException { + // Byteman status might be null if no agent has been attached yet. Treat + // this similar to no-rules loaded + VmBytemanStatus status = bytemanDao.findBytemanStatus(new VmId(vmInfo.getVmId())); + PrintStream out = ctx.getConsole().getOutput(); + String rules; + if (status == null || status.getRule() == null || status.getRule().isEmpty()) { + rules = NO_RULES_LOADED; + } else { + rules = status.getRule(); + } + String listenPort; + if (status == null) { + listenPort = UNSET_PORT; + } else { + listenPort = Integer.toString(status.getListenPort()); + } + out.println(translator.localize(LocaleResources.BYTEMAN_STATUS_MSG, + vmInfo.getMainClass(), + listenPort, + rules).getContents()); + } + + private void submitRequest(CommandContext ctx, RequestQueue requestQueue, Request request) { + CountDownLatch latch = new CountDownLatch(1); + BytemanRequestResponseListener listener = new BytemanRequestResponseListener(latch); + request.addListener(listener); + requestQueue.putRequest(request); + waitWithTimeout(latch); + printResponse(listener, ctx); + } + + private void printResponse(BytemanRequestResponseListener listener, CommandContext ctx) { + if (listener.isError()) { + PrintStream err = ctx.getConsole().getError(); + err.println(listener.getErrorMessage()); + } else { + PrintStream out = ctx.getConsole().getOutput(); + out.println(translator.localize(LocaleResources.REQUEST_SUCCESS) + .getContents()); + } + } + + // package-private for testing + void waitWithTimeout(CountDownLatch latch) { + try { + latch.await(5, TimeUnit.SECONDS); + } catch (InterruptedException e) { + // ignore + } + } + + private RequestQueue getRequestQueue() throws CommandException { + RequestQueue requestQueue = depServices.getService(RequestQueue.class); + requireNonNull(requestQueue, translator.localize(LocaleResources.QUEUE_SERVICE_UNAVAILABLE)); + return requestQueue; + } + + private VmBytemanStatus getVmBytemanStatus(VmId vmId, VmBytemanDAO bytemanDao) throws CommandException { + VmBytemanStatus status = bytemanDao.findBytemanStatus(vmId); + if (status == null) { + throw new CommandException(translator.localize(LocaleResources.ERROR_NO_STATUS, vmId.get())); + } + return status; + } + + void bindAgentInfoDao(AgentInfoDAO agentDao) { + depServices.addService(AgentInfoDAO.class, agentDao); + } + + void unbindAgentInfoDao(AgentInfoDAO agentDao) { + depServices.removeService(AgentInfoDAO.class); + } + + void bindVmInfoDao(VmInfoDAO vmDao) { + depServices.addService(VmInfoDAO.class, vmDao); + } + + void unbindVmInfoDao(VmInfoDAO vmDao) { + depServices.removeService(VmInfoDAO.class); + } + + void bindVmBytemanDao(VmBytemanDAO metricDao) { + depServices.addService(VmBytemanDAO.class, metricDao); + } + + void unbindVmBytemanDao(VmBytemanDAO metricDao) { + depServices.removeService(VmBytemanDAO.class); + } + + void bindRequestQueue(RequestQueue queue) { + depServices.addService(RequestQueue.class, queue); + } + + void unbindRequestQueue(RequestQueue queue) { + depServices.removeService(RequestQueue.class); + } + + void bindFileNameTabCompleter(FileNameTabCompleter fileNameTabCompleter) { + depServices.addService(FileNameTabCompleter.class, fileNameTabCompleter); + } + + void unbindFileNameTabCompleter(FileNameTabCompleter fileNameTabCompleter) { + depServices.removeService(FileNameTabCompleter.class); + } + +}
--- a/vm-byteman/client-cli/src/main/java/com/redhat/thermostat/vm/byteman/client/cli/internal/Activator.java Tue Sep 13 11:35:25 2016 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,146 +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.byteman.client.cli.internal; - -import java.util.Hashtable; -import java.util.Map; - -import com.redhat.thermostat.common.cli.CompleterService; -import com.redhat.thermostat.common.cli.FileNameTabCompleter; -import org.osgi.framework.BundleActivator; -import org.osgi.framework.BundleContext; -import org.osgi.framework.ServiceReference; -import org.osgi.framework.ServiceRegistration; - -import com.redhat.thermostat.client.command.RequestQueue; -import com.redhat.thermostat.common.MultipleServiceTracker; -import com.redhat.thermostat.common.MultipleServiceTracker.Action; -import com.redhat.thermostat.common.cli.Command; -import com.redhat.thermostat.storage.dao.AgentInfoDAO; -import com.redhat.thermostat.storage.dao.VmInfoDAO; -import com.redhat.thermostat.vm.byteman.common.VmBytemanDAO; -import org.osgi.util.tracker.ServiceTracker; -import org.osgi.util.tracker.ServiceTrackerCustomizer; - -public class Activator implements BundleActivator { - - private ServiceRegistration<Command> commandReg; - private ServiceRegistration completerRegistration; - private MultipleServiceTracker commandDepsTracker; - private ServiceTracker fileNameTabCompleterTracker; - private BytemanCompleterService completerService; - - @Override - @SuppressWarnings("unchecked") - public void start(final BundleContext context) throws Exception { - Hashtable<String, String> properties = new Hashtable<>(); - properties.put(Command.NAME, BytemanControlCommand.COMMAND_NAME); - final BytemanControlCommand bytemanCommand = new BytemanControlCommand(); - commandReg = context.registerService(Command.class, bytemanCommand, properties); - Class<?>[] deps = new Class[] { - VmInfoDAO.class, - AgentInfoDAO.class, - VmBytemanDAO.class, - RequestQueue.class, - }; - commandDepsTracker = new MultipleServiceTracker(context, deps, new Action() { - - @Override - public void dependenciesUnavailable() { - bytemanCommand.unsetAgentInfoDao(); - bytemanCommand.unsetRequestQueue(); - bytemanCommand.unsetVmBytemanDao(); - bytemanCommand.unsetVmInfoDao(); - } - - @Override - public void dependenciesAvailable(Map<String, Object> services) { - VmInfoDAO vmInfo = (VmInfoDAO)services.get(VmInfoDAO.class.getName()); - AgentInfoDAO agentInfo = (AgentInfoDAO)services.get(AgentInfoDAO.class.getName()); - VmBytemanDAO vmBytemanDao = (VmBytemanDAO)services.get(VmBytemanDAO.class.getName()); - RequestQueue queue = (RequestQueue)services.get(RequestQueue.class.getName()); - bytemanCommand.setAgentInfoDao(agentInfo); - bytemanCommand.setVmInfoDao(vmInfo); - bytemanCommand.setVmBytemanDao(vmBytemanDao); - bytemanCommand.setRequestQueue(queue); - } - }); - commandDepsTracker.open(); - - completerService = new BytemanCompleterService(); - - fileNameTabCompleterTracker = new ServiceTracker(context, FileNameTabCompleter.class, new ServiceTrackerCustomizer() { - @Override - public Object addingService(ServiceReference serviceReference) { - FileNameTabCompleter fileNameTabCompleter = (FileNameTabCompleter) context.getService(serviceReference); - completerService.setFileNameTabCompleter(fileNameTabCompleter); - completerRegistration = context.registerService(CompleterService.class.getName(), completerService, null); - return context.getService(serviceReference); - } - - @Override - public void modifiedService(ServiceReference serviceReference, Object o) { - } - - @Override - public void removedService(ServiceReference serviceReference, Object o) { - completerService.setFileNameTabCompleter(null); - } - }); - fileNameTabCompleterTracker.open(); - } - - @Override - public void stop(BundleContext context) throws Exception { - if (commandDepsTracker != null) { - commandDepsTracker.close(); - } - if (commandReg != null) { - commandReg.unregister(); - } - if (fileNameTabCompleterTracker != null) { - fileNameTabCompleterTracker.close(); - } - if (completerRegistration != null) { - completerRegistration.unregister(); - } - if (completerService != null) { - completerService.setFileNameTabCompleter(null); - } - } - -}
--- a/vm-byteman/client-cli/src/main/java/com/redhat/thermostat/vm/byteman/client/cli/internal/BytemanCompleterService.java Tue Sep 13 11:35:25 2016 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,75 +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.byteman.client.cli.internal; - -import com.redhat.thermostat.common.cli.AbstractCompleterService; -import com.redhat.thermostat.common.cli.CliCommandOption; -import com.redhat.thermostat.common.cli.FileNameTabCompleter; -import com.redhat.thermostat.common.cli.TabCompleter; - -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; -import java.util.Set; - -public class BytemanCompleterService extends AbstractCompleterService { - - static final CliCommandOption RULES_OPTION = new CliCommandOption("r", "rules", true, - "a file with Byteman rules to load into a VM", false); - - @Override - public Set<String> getCommands() { - return Collections.singleton(BytemanControlCommand.COMMAND_NAME); - } - - @Override - public Map<String, Map<CliCommandOption, ? extends TabCompleter>> getSubcommandCompleters() { - if (!dependencyServices.hasService(FileNameTabCompleter.class)) { - return Collections.emptyMap(); - } - Map<CliCommandOption, ? extends TabCompleter> loadMap = - Collections.singletonMap(RULES_OPTION, dependencyServices.getService(FileNameTabCompleter.class)); - Map<String, Map<CliCommandOption, ? extends TabCompleter>> map = new HashMap<>(); - map.put(BytemanControlCommand.INJECT_RULE_ACTION, loadMap); - return map; - } - - public void setFileNameTabCompleter(FileNameTabCompleter tabCompleter) { - setService(FileNameTabCompleter.class, tabCompleter); - } - -}
--- a/vm-byteman/client-cli/src/main/java/com/redhat/thermostat/vm/byteman/client/cli/internal/BytemanControlCommand.java Tue Sep 13 11:35:25 2016 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,300 +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.byteman.client.cli.internal; - -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.io.PrintStream; -import java.net.InetSocketAddress; -import java.nio.charset.Charset; -import java.util.List; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; - -import com.redhat.thermostat.client.cli.VmArgument; -import com.redhat.thermostat.client.command.RequestQueue; -import com.redhat.thermostat.common.cli.AbstractCommand; -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.common.command.Request; -import com.redhat.thermostat.common.model.Range; -import com.redhat.thermostat.common.utils.StreamUtils; -import com.redhat.thermostat.shared.locale.Translate; -import com.redhat.thermostat.storage.core.AgentId; -import com.redhat.thermostat.storage.core.VmId; -import com.redhat.thermostat.storage.dao.AgentInfoDAO; -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.byteman.common.BytemanMetric; -import com.redhat.thermostat.vm.byteman.common.VmBytemanDAO; -import com.redhat.thermostat.vm.byteman.common.VmBytemanStatus; -import com.redhat.thermostat.vm.byteman.common.command.BytemanRequest; -import com.redhat.thermostat.vm.byteman.common.command.BytemanRequestResponseListener; -import com.redhat.thermostat.vm.byteman.common.command.BytemanRequest.RequestAction; - -public class BytemanControlCommand extends AbstractCommand { - - public static final String COMMAND_NAME = "byteman"; - - private static final Translate<LocaleResources> translator = LocaleResources.createLocalizer(); - static final String INJECT_RULE_ACTION = "load"; - static final String UNLOAD_RULE_ACTION = "unload"; - static final String STATUS_ACTION = "status"; - static final String SHOW_ACTION = "show-metrics"; - private static final String RULES_FILE_OPTION = "rules"; - private static final String NO_RULES_LOADED = "<no-loaded-rules>"; - private static final String UNSET_PORT = "<unset>"; - private static final Charset UTF_8_CHARSET = Charset.forName("UTF-8"); - - - private final DependencyServices depServices = new DependencyServices(); - - @Override - public void run(CommandContext ctx) throws CommandException { - VmArgument vmArgument = VmArgument.required(ctx.getArguments()); - VmId vmId = vmArgument.getVmId(); - - VmInfoDAO vmInfoDAO = depServices.getService(VmInfoDAO.class); - - requireNonNull(vmInfoDAO, translator.localize(LocaleResources.VM_SERVICE_UNAVAILABLE)); - final VmInfo vmInfo = vmInfoDAO.getVmInfo(vmId); - requireNonNull(vmInfo, translator.localize(LocaleResources.VM_SERVICE_UNAVAILABLE)); - - AgentInfoDAO agentInfoDAO = depServices.getService(AgentInfoDAO.class); - final AgentId agentId = new AgentId(vmInfo.getAgentId()); - requireNonNull(agentInfoDAO, translator.localize(LocaleResources.AGENT_SERVICE_UNAVAILABLE)); - - AgentInformation agentInfo = agentInfoDAO.getAgentInformation(agentId); - if (agentInfo == null) { - throw new CommandException(translator.localize(LocaleResources.AGENT_NOT_FOUND, agentId.get())); - } - if (!agentInfo.isAlive()) { - throw new CommandException(translator.localize(LocaleResources.AGENT_DEAD, agentId.get())); - } - - InetSocketAddress target = agentInfo.getRequestQueueAddress(); - - List<String> nonOptionargs = ctx.getArguments().getNonOptionArguments(); - if (nonOptionargs.size() != 1) { - throw new CommandException(translator.localize(LocaleResources.COMMAND_EXPECTED)); - } - VmBytemanDAO bytemanDao = depServices.getService(VmBytemanDAO.class); - requireNonNull(bytemanDao, translator.localize(LocaleResources.BYTEMAN_METRICS_SERVICE_UNAVAILABLE)); - - String command = nonOptionargs.get(0); - - switch (command) { - case INJECT_RULE_ACTION: - injectRules(target, vmInfo, ctx, bytemanDao); - break; - case UNLOAD_RULE_ACTION: - unloadRules(target, vmInfo, ctx, bytemanDao); - break; - case STATUS_ACTION: - showStatus(ctx, vmInfo, bytemanDao); - break; - case SHOW_ACTION: - showMetrics(ctx, vmId, agentId, bytemanDao); - break; - default: - throw new CommandException(translator.localize(LocaleResources.UNKNOWN_COMMAND, command)); - } - } - - /* Unloads byteman rules */ - private void unloadRules(InetSocketAddress target, VmInfo vmInfo, CommandContext ctx, VmBytemanDAO bytemanDao) throws CommandException { - VmId vmId = new VmId(vmInfo.getVmId()); - RequestQueue requestQueue = getRequestQueue(); - VmBytemanStatus status = getVmBytemanStatus(vmId, bytemanDao); - int listenPort = status.getListenPort(); - Request unloadRequest = BytemanRequest.create(target, vmInfo, RequestAction.UNLOAD_RULES, listenPort); - submitRequest(ctx, requestQueue, unloadRequest); - } - - - /* Injects byteman rules */ - private void injectRules(InetSocketAddress target, VmInfo vmInfo, CommandContext ctx, VmBytemanDAO bytemanDao) throws CommandException { - VmId vmId = new VmId(vmInfo.getVmId()); - Arguments args = ctx.getArguments(); - if (!args.hasArgument(RULES_FILE_OPTION)) { - throw new CommandException(translator.localize(LocaleResources.NO_RULE_OPTION)); - } - String ruleFile = args.getArgument(RULES_FILE_OPTION); - - byte[] rulesBytes; - try { - rulesBytes = StreamUtils.readAll(new FileInputStream(new File(ruleFile))); - } catch (FileNotFoundException e) { - throw new CommandException(translator.localize(LocaleResources.RULE_FILE_NOT_FOUND, ruleFile)); - } catch (IOException e) { - throw new CommandException(translator.localize(LocaleResources.ERROR_READING_RULE_FILE, ruleFile)); - } - String rulesContent = new String(rulesBytes, UTF_8_CHARSET); - VmBytemanStatus status = bytemanDao.findBytemanStatus(vmId); - int listenPort = BytemanRequest.NOT_ATTACHED_PORT; - if (status != null) { - listenPort = status.getListenPort(); - } - RequestQueue requestQueue = getRequestQueue(); - Request request = BytemanRequest.create(target, vmInfo, RequestAction.LOAD_RULES, listenPort, rulesContent); - submitRequest(ctx, requestQueue, request); - } - - /* Show metrics retrieved via byteman rules */ - private void showMetrics(CommandContext ctx, VmId vmId, AgentId agentId, VmBytemanDAO bytemanDao) throws CommandException { - // TODO: Make this query configurable with arguments - long now = System.currentTimeMillis(); - long from = now - TimeUnit.MINUTES.toMillis(5); - long to = now; - Range<Long> timeRange = new Range<Long>(from, to); - List<BytemanMetric> metrics = bytemanDao.findBytemanMetrics(timeRange, vmId, agentId); - PrintStream output = ctx.getConsole().getOutput(); - PrintStream out = ctx.getConsole().getOutput(); - if (metrics.isEmpty()) { - out.println(translator.localize(LocaleResources.NO_METRICS_AVAILABLE, vmId.get()).getContents()); - } else { - for (BytemanMetric m: metrics) { - output.println(m.getDataAsJson()); - } - } - } - - /* Show status of loaded byteman rules */ - private void showStatus(CommandContext ctx, VmInfo vmInfo, VmBytemanDAO bytemanDao) throws CommandException { - // Byteman status might be null if no agent has been attached yet. Treat - // this similar to no-rules loaded - VmBytemanStatus status = bytemanDao.findBytemanStatus(new VmId(vmInfo.getVmId())); - PrintStream out = ctx.getConsole().getOutput(); - String rules; - if (status == null || status.getRule() == null || status.getRule().isEmpty()) { - rules = NO_RULES_LOADED; - } else { - rules = status.getRule(); - } - String listenPort; - if (status == null) { - listenPort = UNSET_PORT; - } else { - listenPort = Integer.toString(status.getListenPort()); - } - out.println(translator.localize(LocaleResources.BYTEMAN_STATUS_MSG, - vmInfo.getMainClass(), - listenPort, - rules).getContents()); - } - - private void submitRequest(CommandContext ctx, RequestQueue requestQueue, Request request) { - CountDownLatch latch = new CountDownLatch(1); - BytemanRequestResponseListener listener = new BytemanRequestResponseListener(latch); - request.addListener(listener); - requestQueue.putRequest(request); - waitWithTimeout(latch); - printResponse(listener, ctx); - } - - private void printResponse(BytemanRequestResponseListener listener, CommandContext ctx) { - if (listener.isError()) { - PrintStream err = ctx.getConsole().getError(); - err.println(listener.getErrorMessage()); - } else { - PrintStream out = ctx.getConsole().getOutput(); - out.println(translator.localize(LocaleResources.REQUEST_SUCCESS) - .getContents()); - } - } - - // package-private for testing - void waitWithTimeout(CountDownLatch latch) { - try { - latch.await(5, TimeUnit.SECONDS); - } catch (InterruptedException e) { - // ignore - } - } - - private RequestQueue getRequestQueue() throws CommandException { - RequestQueue requestQueue = depServices.getService(RequestQueue.class); - requireNonNull(requestQueue, translator.localize(LocaleResources.QUEUE_SERVICE_UNAVAILABLE)); - return requestQueue; - } - - private VmBytemanStatus getVmBytemanStatus(VmId vmId, VmBytemanDAO bytemanDao) throws CommandException { - VmBytemanStatus status = bytemanDao.findBytemanStatus(vmId); - if (status == null) { - throw new CommandException(translator.localize(LocaleResources.ERROR_NO_STATUS, vmId.get())); - } - return status; - } - - void setAgentInfoDao(AgentInfoDAO agentDao) { - depServices.addService(AgentInfoDAO.class, agentDao); - } - - void unsetAgentInfoDao() { - depServices.removeService(AgentInfoDAO.class); - } - - void setVmInfoDao(VmInfoDAO vmDao) { - depServices.addService(VmInfoDAO.class, vmDao); - } - - void unsetVmInfoDao() { - depServices.removeService(VmInfoDAO.class); - } - - void setVmBytemanDao(VmBytemanDAO metricDao) { - depServices.addService(VmBytemanDAO.class, metricDao); - } - - void unsetVmBytemanDao() { - depServices.removeService(VmBytemanDAO.class); - } - - void setRequestQueue(RequestQueue queue) { - depServices.addService(RequestQueue.class, queue); - } - - void unsetRequestQueue() { - depServices.removeService(RequestQueue.class); - } - -}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vm-byteman/client-cli/src/test/java/com/redhat/thermostat/vm/byteman/client/cli/BytemanControlCommandTest.java Wed Sep 14 11:05:00 2016 -0400 @@ -0,0 +1,457 @@ +/* + * 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.byteman.client.cli; + +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.CoreMatchers.instanceOf; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.not; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.net.InetSocketAddress; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.concurrent.CountDownLatch; + +import com.redhat.thermostat.common.cli.CliCommandOption; +import com.redhat.thermostat.common.cli.FileNameTabCompleter; +import com.redhat.thermostat.common.cli.TabCompleter; +import com.redhat.thermostat.vm.byteman.client.cli.BytemanControlCommand; +import org.junit.Before; +import org.junit.Test; +import org.mockito.ArgumentCaptor; + +import com.redhat.thermostat.client.command.RequestQueue; +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.command.Request; +import com.redhat.thermostat.common.command.RequestResponseListener; +import com.redhat.thermostat.common.internal.test.TestCommandContextFactory; +import com.redhat.thermostat.common.model.Range; +import com.redhat.thermostat.common.utils.StreamUtils; +import com.redhat.thermostat.storage.core.AgentId; +import com.redhat.thermostat.storage.core.VmId; +import com.redhat.thermostat.storage.dao.AgentInfoDAO; +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.byteman.common.BytemanMetric; +import com.redhat.thermostat.vm.byteman.common.VmBytemanDAO; +import com.redhat.thermostat.vm.byteman.common.VmBytemanStatus; +import com.redhat.thermostat.vm.byteman.common.command.BytemanRequest; +import com.redhat.thermostat.vm.byteman.common.command.BytemanRequestResponseListener; +import com.redhat.thermostat.vm.byteman.common.command.BytemanRequest.RequestAction; + +public class BytemanControlCommandTest { + + private static final String RULE_OPTION = "rules"; + private static final String STATUS_ACTION = "status"; + private static final String SHOW_METRICS_ACTION = "show-metrics"; + private static final String UNLOAD_ACTION = "unload"; + private static final String LOAD_ACTION = "load"; + private static final String SOME_VM_ID = "some-vm-id"; + private static final String SOME_AGENT_ID = "some-agent-id"; + private static final String EMPTY_STRING = ""; + private static final int SOME_LISTEN_PORT = 333; + private static final InetSocketAddress REQUEST_QUEUE_ADDRESS = mock(InetSocketAddress.class); + private BytemanControlCommand command; + private TestCommandContextFactory ctxFactory; + + @Before + public void setup() { + command = new BytemanControlCommand() { + @Override + void waitWithTimeout(CountDownLatch latch) { + // return immediately + } + }; + VmInfoDAO vmInfoDAO = mock(VmInfoDAO.class); + VmInfo vmInfo = new VmInfo(); + vmInfo.setAgentId(SOME_AGENT_ID); + vmInfo.setVmId(SOME_VM_ID); + when(vmInfoDAO.getVmInfo(any(VmId.class))).thenReturn(vmInfo); + command.bindVmInfoDao(vmInfoDAO); + AgentInfoDAO agentInfoDAO = mock(AgentInfoDAO.class); + AgentInformation agentInfo = mock(AgentInformation.class); + when(agentInfo.isAlive()).thenReturn(true); + when(agentInfo.getRequestQueueAddress()).thenReturn(REQUEST_QUEUE_ADDRESS); + when(agentInfoDAO.getAgentInformation(any(AgentId.class))).thenReturn(agentInfo); + command.bindAgentInfoDao(agentInfoDAO); + command.bindVmBytemanDao(mock(VmBytemanDAO.class)); + ctxFactory = new TestCommandContextFactory(); + } + + @Test + public void testUnknownAction() { + String unknownAction = "some-action-that-doesn't-exist"; + Arguments args = getBasicArgsWithAction(unknownAction); + CommandContext ctx = ctxFactory.createContext(args); + try { + command.run(ctx); + fail("Expected failure due to unknown action"); + } catch (CommandException e) { + String msg = e.getMessage(); + assertTrue(msg.contains(unknownAction)); + assertTrue(msg.startsWith("Unknown command:")); + } + } + + @Test + public void testStatusActionNoRule() throws CommandException { + String rule = null; + String expectedLoadedRuleMsg = "<no-loaded-rules>"; + VmBytemanStatus status = new VmBytemanStatus(); + status.setVmId(SOME_VM_ID); + status.setAgentId(SOME_AGENT_ID); + status.setListenPort(SOME_LISTEN_PORT); + status.setRule(rule); + basicStatusActionTest(rule, expectedLoadedRuleMsg, status); + } + + @Test + public void testStatusActionNotAttached() throws CommandException { + String rule = null; + String expectedLoadedRuleMsg = "<no-loaded-rules>"; + basicStatusActionTest(rule, expectedLoadedRuleMsg, null); + } + + private void basicStatusActionTest(String rule, String expectedRuleMsg, VmBytemanStatus status) throws CommandException { + VmBytemanDAO dao = mock(VmBytemanDAO.class); + when(dao.findBytemanStatus(eq(new VmId(SOME_VM_ID)))).thenReturn(status); + Arguments args = getBasicArgsWithAction(STATUS_ACTION); + CommandContext ctx = ctxFactory.createContext(args); + command.unbindVmBytemanDao(dao); + command.bindVmBytemanDao(dao); + command.run(ctx); + String stdErr = ctxFactory.getError(); + String stdOut = ctxFactory.getOutput(); + assertEquals(EMPTY_STRING, stdErr); + assertTrue(stdOut.contains("Byteman status for VM:")); + String listenPort = "<unset>"; + if (status != null) { + listenPort = Integer.toString(status.getListenPort()); + } + assertTrue(stdOut.contains("Byteman agent listen port: " + listenPort)); + assertTrue(stdOut.contains("Loaded rules:")); + assertTrue(stdOut.contains(expectedRuleMsg)); + } + + @Test + public void testStatusActionWithRule() throws CommandException { + String rule = "some-rule-string\nsome more rule lines\nfurther more"; + VmBytemanStatus status = new VmBytemanStatus(); + status.setVmId(SOME_VM_ID); + status.setAgentId(SOME_AGENT_ID); + status.setListenPort(SOME_LISTEN_PORT); + status.setRule(rule); + basicStatusActionTest(rule, rule, status); + } + + @Test + public void testAgentNotAlive() throws CommandException { + AgentInfoDAO agentInfoDAO = mock(AgentInfoDAO.class); + AgentInformation agentInfo = mock(AgentInformation.class); + when(agentInfo.isAlive()).thenReturn(false); + when(agentInfoDAO.getAgentInformation(any(AgentId.class))).thenReturn(agentInfo); + command.unbindAgentInfoDao(agentInfoDAO); + command.bindAgentInfoDao(agentInfoDAO); + Arguments args = getBasicArgsWithAction("no-matter"); + CommandContext ctx = ctxFactory.createContext(args); + try { + command.run(ctx); + fail("Command should have thrown exception"); + } catch (CommandException e) { + String msg = e.getMessage(); + assertEquals("Agent with id " + SOME_AGENT_ID + " is not alive.", msg); + } + } + + @Test + public void testShowMetricsActionNoMetrics() throws CommandException { + String expectedStdOut = "No metrics available for VM " + SOME_VM_ID + ".\n"; + List<BytemanMetric> returnedList = Collections.emptyList(); + doShowMetricsTest(returnedList, expectedStdOut); + } + + @Test + public void testShowMetricsActionWithMetrics() throws CommandException { + String metricData1 = "{ \"foo\": \"bar\" }"; + String metricData2 = "{ \"foo2\": -300 }"; + String expectedStdOut = String.format("%s\n%s\n", metricData1, metricData2); + BytemanMetric metric1 = new BytemanMetric(); + metric1.setData(metricData1); + BytemanMetric metric2 = new BytemanMetric(); + metric2.setData(metricData2); + List<BytemanMetric> returnedList = Arrays.asList(metric1, metric2); + doShowMetricsTest(returnedList, expectedStdOut); + } + + @Test + public void testUnloadAction() throws CommandException { + VmBytemanDAO dao = mock(VmBytemanDAO.class); + VmBytemanStatus status = new VmBytemanStatus(); + int listenPort = 333; + status.setVmId(SOME_VM_ID); + status.setAgentId(SOME_AGENT_ID); + status.setListenPort(listenPort); + status.setRule(null); + when(dao.findBytemanStatus(eq(new VmId(SOME_VM_ID)))).thenReturn(status); + Arguments args = getBasicArgsWithAction(UNLOAD_ACTION); + CommandContext ctx = ctxFactory.createContext(args); + command.unbindVmBytemanDao(dao); + command.bindVmBytemanDao(dao); + RequestQueue rQueue = mock(RequestQueue.class); + ArgumentCaptor<Request> requestCaptor = ArgumentCaptor.forClass(Request.class); + command.bindRequestQueue(rQueue); + command.run(ctx); + verify(rQueue).putRequest(requestCaptor.capture()); + Request submittedRequest = requestCaptor.getValue(); + assertEquals(1, submittedRequest.getListeners().size()); + RequestResponseListener respListener = null; + for (RequestResponseListener l: submittedRequest.getListeners()) { + respListener = l; + break; + } + assertTrue(respListener instanceof BytemanRequestResponseListener); + String rawAction = submittedRequest.getParameter(BytemanRequest.ACTION_PARAM_NAME); + RequestAction actualAction = RequestAction.fromIntString(rawAction); + assertEquals(RequestAction.UNLOAD_RULES, actualAction); + assertEquals(Integer.toString(listenPort), submittedRequest.getParameter(BytemanRequest.LISTEN_PORT_PARAM_NAME)); + assertEquals(SOME_VM_ID, submittedRequest.getParameter(BytemanRequest.VM_ID_PARAM_NAME)); + assertNull(submittedRequest.getParameter(BytemanRequest.RULE_PARAM_NAME)); + assertSame(REQUEST_QUEUE_ADDRESS, submittedRequest.getTarget()); + } + + @Test + public void testLoadActionNoRuleFile() throws CommandException { + VmBytemanDAO dao = mock(VmBytemanDAO.class); + VmBytemanStatus status = new VmBytemanStatus(); + int listenPort = 333; + status.setVmId(SOME_VM_ID); + status.setAgentId(SOME_AGENT_ID); + status.setListenPort(listenPort); + status.setRule(null); + when(dao.findBytemanStatus(eq(new VmId(SOME_VM_ID)))).thenReturn(status); + Arguments args = getBasicArgsWithAction(LOAD_ACTION); // rule file arg missing + CommandContext ctx = ctxFactory.createContext(args); + command.unbindVmBytemanDao(dao); + command.bindVmBytemanDao(dao); + RequestQueue rQueue = mock(RequestQueue.class); + command.bindRequestQueue(rQueue); + try { + command.run(ctx); + fail("Expected cmd exception due to missing rule argument"); + } catch (CommandException e) { + String msg = e.getMessage(); + assertEquals("No rule option specified.", msg); + } + } + + @Test + public void testLoadActionRuleFileDoesNotExist() throws CommandException { + VmBytemanDAO dao = mock(VmBytemanDAO.class); + VmBytemanStatus status = new VmBytemanStatus(); + int listenPort = 333; + status.setVmId(SOME_VM_ID); + status.setAgentId(SOME_AGENT_ID); + status.setListenPort(listenPort); + status.setRule(null); + when(dao.findBytemanStatus(eq(new VmId(SOME_VM_ID)))).thenReturn(status); + Arguments args = getBasicArgsWithAction(LOAD_ACTION); + when(args.hasArgument(RULE_OPTION)).thenReturn(true); + String file = "i-do-not-exist"; + when(args.getArgument(RULE_OPTION)).thenReturn(file); + CommandContext ctx = ctxFactory.createContext(args); + command.unbindVmBytemanDao(dao); + command.bindVmBytemanDao(dao); + RequestQueue rQueue = mock(RequestQueue.class); + command.bindRequestQueue(rQueue); + try { + command.run(ctx); + fail("Expected cmd exception due to rule file not existing"); + } catch (CommandException e) { + String msg = e.getMessage(); + assertEquals("Specified rules file '" + file + "' not found.", msg); + } + } + + @Test + public void testLoadActionSuccessAgentAttached() throws CommandException, FileNotFoundException, IOException { + VmBytemanDAO dao = mock(VmBytemanDAO.class); + VmBytemanStatus status = new VmBytemanStatus(); + int listenPort = 333; + status.setVmId(SOME_VM_ID); + status.setAgentId(SOME_AGENT_ID); + status.setListenPort(listenPort); + status.setRule(null); + when(dao.findBytemanStatus(eq(new VmId(SOME_VM_ID)))).thenReturn(status); + Arguments args = getBasicArgsWithAction(LOAD_ACTION); + when(args.hasArgument(RULE_OPTION)).thenReturn(true); + String file = getClass().getResource("/testRule.btm").getFile(); + when(args.getArgument(RULE_OPTION)).thenReturn(file); + CommandContext ctx = ctxFactory.createContext(args); + command.unbindVmBytemanDao(dao); + command.bindVmBytemanDao(dao); + RequestQueue rQueue = mock(RequestQueue.class); + command.bindRequestQueue(rQueue); + ArgumentCaptor<Request> requestCaptor = ArgumentCaptor.forClass(Request.class); + command.run(ctx); + verify(rQueue).putRequest(requestCaptor.capture()); + Request submittedRequest = requestCaptor.getValue(); + assertEquals(1, submittedRequest.getListeners().size()); + RequestResponseListener respListener = null; + for (RequestResponseListener l: submittedRequest.getListeners()) { + respListener = l; + break; + } + assertTrue(respListener instanceof BytemanRequestResponseListener); + String rawAction = submittedRequest.getParameter(BytemanRequest.ACTION_PARAM_NAME); + RequestAction actualAction = RequestAction.fromIntString(rawAction); + assertEquals(RequestAction.LOAD_RULES, actualAction); + assertEquals(Integer.toString(listenPort), submittedRequest.getParameter(BytemanRequest.LISTEN_PORT_PARAM_NAME)); + assertEquals(SOME_VM_ID, submittedRequest.getParameter(BytemanRequest.VM_ID_PARAM_NAME)); + String expectedRule = new String(StreamUtils.readAll(new FileInputStream(new File(file)))); + assertEquals(expectedRule, submittedRequest.getParameter(BytemanRequest.RULE_PARAM_NAME)); + assertSame(REQUEST_QUEUE_ADDRESS, submittedRequest.getTarget()); + String out = ctxFactory.getOutput(); + assertEquals("Request submitted successfully.\n", out); + } + + @Test + public void testLoadActionSuccessAgentNotAttached() throws CommandException, FileNotFoundException, IOException { + VmBytemanDAO dao = mock(VmBytemanDAO.class); + // mimic no-agent-attached, by returning a null status + when(dao.findBytemanStatus(eq(new VmId(SOME_VM_ID)))).thenReturn(null); + Arguments args = getBasicArgsWithAction(LOAD_ACTION); + when(args.hasArgument(RULE_OPTION)).thenReturn(true); + String file = getClass().getResource("/testRule.btm").getFile(); + when(args.getArgument(RULE_OPTION)).thenReturn(file); + CommandContext ctx = ctxFactory.createContext(args); + command.unbindVmBytemanDao(dao); + command.bindVmBytemanDao(dao); + RequestQueue rQueue = mock(RequestQueue.class); + command.bindRequestQueue(rQueue); + ArgumentCaptor<Request> requestCaptor = ArgumentCaptor.forClass(Request.class); + command.run(ctx); + verify(rQueue).putRequest(requestCaptor.capture()); + Request submittedRequest = requestCaptor.getValue(); + assertEquals(1, submittedRequest.getListeners().size()); + RequestResponseListener respListener = null; + for (RequestResponseListener l: submittedRequest.getListeners()) { + respListener = l; + break; + } + assertTrue(respListener instanceof BytemanRequestResponseListener); + String rawAction = submittedRequest.getParameter(BytemanRequest.ACTION_PARAM_NAME); + RequestAction actualAction = RequestAction.fromIntString(rawAction); + assertEquals(RequestAction.LOAD_RULES, actualAction); + assertEquals(Integer.toString(BytemanRequest.NOT_ATTACHED_PORT), submittedRequest.getParameter(BytemanRequest.LISTEN_PORT_PARAM_NAME)); + assertEquals(SOME_VM_ID, submittedRequest.getParameter(BytemanRequest.VM_ID_PARAM_NAME)); + String expectedRule = new String(StreamUtils.readAll(new FileInputStream(new File(file)))); + assertEquals(expectedRule, submittedRequest.getParameter(BytemanRequest.RULE_PARAM_NAME)); + assertSame(REQUEST_QUEUE_ADDRESS, submittedRequest.getTarget()); + String out = ctxFactory.getOutput(); + assertEquals("Request submitted successfully.\n", out); + } + + @Test + public void testProvidesRulesFileCompletion() { + FileNameTabCompleter fileNameTabCompleter = mock(FileNameTabCompleter.class); + command.bindFileNameTabCompleter(fileNameTabCompleter); + Map<CliCommandOption, ? extends TabCompleter> completerMap = command.getOptionCompleters(); + assertThat(completerMap.size(), is(1)); + assertThat(completerMap.keySet(), is(equalTo(Collections.singleton(BytemanControlCommand.RULES_OPTION)))); + assertThat(BytemanControlCommand.RULES_OPTION.getLongOpt(), is("rules")); + assertThat(BytemanControlCommand.RULES_OPTION.getOpt(), is("r")); + assertThat(completerMap.get(BytemanControlCommand.RULES_OPTION), is(not(equalTo(null)))); + assertThat(completerMap.get(BytemanControlCommand.RULES_OPTION), is(instanceOf(FileNameTabCompleter.class))); + } + + @Test + public void testProvidesNoCompletionsIfFileNameTabCompleterIsUnavailable() { + FileNameTabCompleter fileNameTabCompleter = mock(FileNameTabCompleter.class); + command.unbindFileNameTabCompleter(fileNameTabCompleter); + Map<CliCommandOption, ? extends TabCompleter> completerMap = command.getOptionCompleters(); + assertThat(completerMap.size(), is(0)); + } + + @SuppressWarnings("unchecked") + private void doShowMetricsTest(List<BytemanMetric> metricsToReturn, String stdOutExpected) throws CommandException { + VmBytemanDAO dao = mock(VmBytemanDAO.class); + VmBytemanStatus status = new VmBytemanStatus(); + int listenPort = 333; + status.setVmId(SOME_VM_ID); + status.setAgentId(SOME_AGENT_ID); + status.setListenPort(listenPort); + status.setRule(null); + when(dao.findBytemanStatus(eq(new VmId(SOME_VM_ID)))).thenReturn(status); + when(dao.findBytemanMetrics(any(Range.class), any(VmId.class), any(AgentId.class))).thenReturn(metricsToReturn); + Arguments args = getBasicArgsWithAction(SHOW_METRICS_ACTION); + CommandContext ctx = ctxFactory.createContext(args); + command.unbindVmBytemanDao(dao); + command.bindVmBytemanDao(dao); + command.run(ctx); + String stdErr = ctxFactory.getError(); + String stdOut = ctxFactory.getOutput(); + assertEquals(EMPTY_STRING, stdErr); + assertEquals(stdOutExpected, stdOut); + } + + private Arguments getBasicArgsWithAction(String action) { + Arguments args = mock(Arguments.class); + when(args.getArgument("vmId")).thenReturn(SOME_VM_ID); + when(args.getNonOptionArguments()).thenReturn(Arrays.asList(action)); + return args; + } +}
--- a/vm-byteman/client-cli/src/test/java/com/redhat/thermostat/vm/byteman/client/cli/internal/ActivatorTest.java Tue Sep 13 11:35:25 2016 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,87 +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.byteman.client.cli.internal; - -import com.redhat.thermostat.common.cli.Command; -import com.redhat.thermostat.common.cli.CompleterService; -import com.redhat.thermostat.common.cli.FileNameTabCompleter; -import com.redhat.thermostat.testutils.StubBundleContext; -import org.junit.Test; - -import java.util.List; - -import static com.redhat.thermostat.testutils.Asserts.assertCommandIsRegistered; -import static com.redhat.thermostat.testutils.Asserts.assertServiceIsRegistered; -import static org.junit.Assert.assertEquals; - -public class ActivatorTest { - - @Test - public void testCommandsRegistered() throws Exception { - StubBundleContext ctx = new StubBundleContext(); - - Activator activator = new Activator(); - - activator.start(ctx); - - assertCommandIsRegistered(ctx, "byteman", BytemanControlCommand.class); - - activator.stop(ctx); - - assertEquals(0, ctx.getAllServices().size()); - } - - @Test - public void testCompleterBecomesAvailableWhenFileNameCompleterAppears() throws Exception { - StubBundleContext ctx = new StubBundleContext(); - ctx.registerService(FileNameTabCompleter.class, new StubFileNameTabCompleter(), null); - Activator activator = new Activator(); - activator.start(ctx); - assertEquals(3, ctx.getAllServices().size()); - assertServiceIsRegistered(ctx, FileNameTabCompleter.class, StubFileNameTabCompleter.class); - assertServiceIsRegistered(ctx, CompleterService.class, BytemanCompleterService.class); - assertServiceIsRegistered(ctx, Command.class, BytemanControlCommand.class); - } - - private static class StubFileNameTabCompleter implements FileNameTabCompleter { - @Override - public int complete(String buffer, int cursor, List<CharSequence> candidates) { - return 0; - } - } - -}
--- a/vm-byteman/client-cli/src/test/java/com/redhat/thermostat/vm/byteman/client/cli/internal/BytemanCompleterServiceTest.java Tue Sep 13 11:35:25 2016 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,115 +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.byteman.client.cli.internal; - -import com.redhat.thermostat.common.Pair; -import com.redhat.thermostat.common.cli.CliCommandOption; -import com.redhat.thermostat.common.cli.FileNameTabCompleter; -import com.redhat.thermostat.common.cli.TabCompleter; -import org.junit.Before; -import org.junit.Test; - -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; - -import static com.redhat.thermostat.vm.byteman.client.cli.internal.BytemanCompleterService.RULES_OPTION; -import static org.hamcrest.CoreMatchers.equalTo; -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.CoreMatchers.not; -import static org.junit.Assert.assertThat; -import static org.mockito.Mockito.mock; - -public class BytemanCompleterServiceTest { - - private BytemanCompleterService completerService; - - @Before - public void setup() { - completerService = new BytemanCompleterService(); - completerService.setFileNameTabCompleter(mock(FileNameTabCompleter.class)); - } - - @Test - public void testOnlyProvidesCompletionForBytemanCommand() { - assertThat(completerService.getCommands(), is(equalTo(Collections.singleton(BytemanControlCommand.COMMAND_NAME)))); - } - - @Test - public void testProvidesNoTopLevelOptionCompletions() { - assertThat(completerService.getOptionCompleters().size(), is(0)); - } - - @Test - public void testProvidesLoadSubcommandCompletionForRulesArgument() { - Map<String, Map<CliCommandOption, ? extends TabCompleter>> map = completerService.getSubcommandCompleters(); - assertThat(map.keySet(), is(equalTo(Collections.singleton(BytemanControlCommand.INJECT_RULE_ACTION)))); - Collection<Map<CliCommandOption, ? extends TabCompleter>> values = map.values(); - assertThat(values.size(), is(1)); - Map<CliCommandOption, ? extends TabCompleter> submap = (Map<CliCommandOption, ? extends TabCompleter>) values.toArray(new Object[1])[0]; - assertThat(submap.keySet(), is(equalTo(Collections.singleton(BytemanCompleterService.RULES_OPTION)))); - assertThat(RULES_OPTION.getLongOpt(), is("rules")); - assertThat(RULES_OPTION.getOpt(), is("r")); - assertThat(RULES_OPTION.isRequired(), is(false)); - assertThat(RULES_OPTION.hasArg(), is(true)); - } - - @Test - public void testProvidesOnlyLoadSubcommandCompletion() { - Map<String, Map<CliCommandOption, ? extends TabCompleter>> map = completerService.getSubcommandCompleters(); - assertThat(map.size(), is(1)); - for (String key : map.keySet()) { - assertThat(key, is(equalTo(BytemanControlCommand.INJECT_RULE_ACTION))); - } - } - - @Test - public void testCompleterIsNotNull() { - Map<String, Map<CliCommandOption, ? extends TabCompleter>> map = completerService.getSubcommandCompleters(); - assertThat(map.get(BytemanControlCommand.INJECT_RULE_ACTION).get(BytemanCompleterService.RULES_OPTION), - is(not(equalTo(null)))); - } - - @Test - public void testProvidesNoCompletionWhenFileNameTabCompleterNotAvailable() { - completerService.setFileNameTabCompleter(null); - Map<String, Map<CliCommandOption, ? extends TabCompleter>> map = completerService.getSubcommandCompleters(); - assertThat(map.isEmpty(), is(true)); - } - -}
--- a/vm-byteman/client-cli/src/test/java/com/redhat/thermostat/vm/byteman/client/cli/internal/BytemanControlCommandTest.java Tue Sep 13 11:35:25 2016 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,426 +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.byteman.client.cli.internal; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertSame; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.eq; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.net.InetSocketAddress; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import java.util.concurrent.CountDownLatch; - -import org.junit.Before; -import org.junit.Test; -import org.mockito.ArgumentCaptor; - -import com.redhat.thermostat.client.command.RequestQueue; -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.command.Request; -import com.redhat.thermostat.common.command.RequestResponseListener; -import com.redhat.thermostat.common.internal.test.TestCommandContextFactory; -import com.redhat.thermostat.common.model.Range; -import com.redhat.thermostat.common.utils.StreamUtils; -import com.redhat.thermostat.storage.core.AgentId; -import com.redhat.thermostat.storage.core.VmId; -import com.redhat.thermostat.storage.dao.AgentInfoDAO; -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.byteman.common.BytemanMetric; -import com.redhat.thermostat.vm.byteman.common.VmBytemanDAO; -import com.redhat.thermostat.vm.byteman.common.VmBytemanStatus; -import com.redhat.thermostat.vm.byteman.common.command.BytemanRequest; -import com.redhat.thermostat.vm.byteman.common.command.BytemanRequestResponseListener; -import com.redhat.thermostat.vm.byteman.common.command.BytemanRequest.RequestAction; - -public class BytemanControlCommandTest { - - private static final String RULE_OPTION = "rules"; - private static final String STATUS_ACTION = "status"; - private static final String SHOW_METRICS_ACTION = "show-metrics"; - private static final String UNLOAD_ACTION = "unload"; - private static final String LOAD_ACTION = "load"; - private static final String SOME_VM_ID = "some-vm-id"; - private static final String SOME_AGENT_ID = "some-agent-id"; - private static final String EMPTY_STRING = ""; - private static final int SOME_LISTEN_PORT = 333; - private static final InetSocketAddress REQUEST_QUEUE_ADDRESS = mock(InetSocketAddress.class); - private BytemanControlCommand command; - private TestCommandContextFactory ctxFactory; - - @Before - public void setup() { - command = new BytemanControlCommand() { - @Override - void waitWithTimeout(CountDownLatch latch) { - // return immediately - } - }; - VmInfoDAO vmInfoDAO = mock(VmInfoDAO.class); - VmInfo vmInfo = new VmInfo(); - vmInfo.setAgentId(SOME_AGENT_ID); - vmInfo.setVmId(SOME_VM_ID); - when(vmInfoDAO.getVmInfo(any(VmId.class))).thenReturn(vmInfo); - command.setVmInfoDao(vmInfoDAO); - AgentInfoDAO agentInfoDAO = mock(AgentInfoDAO.class); - AgentInformation agentInfo = mock(AgentInformation.class); - when(agentInfo.isAlive()).thenReturn(true); - when(agentInfo.getRequestQueueAddress()).thenReturn(REQUEST_QUEUE_ADDRESS); - when(agentInfoDAO.getAgentInformation(any(AgentId.class))).thenReturn(agentInfo); - command.setAgentInfoDao(agentInfoDAO); - command.setVmBytemanDao(mock(VmBytemanDAO.class)); - ctxFactory = new TestCommandContextFactory(); - } - - @Test - public void testUnknownAction() { - String unknownAction = "some-action-that-doesn't-exist"; - Arguments args = getBasicArgsWithAction(unknownAction); - CommandContext ctx = ctxFactory.createContext(args); - try { - command.run(ctx); - fail("Expected failure due to unknown action"); - } catch (CommandException e) { - String msg = e.getMessage(); - assertTrue(msg.contains(unknownAction)); - assertTrue(msg.startsWith("Unknown command:")); - } - } - - @Test - public void testStatusActionNoRule() throws CommandException { - String rule = null; - String expectedLoadedRuleMsg = "<no-loaded-rules>"; - VmBytemanStatus status = new VmBytemanStatus(); - status.setVmId(SOME_VM_ID); - status.setAgentId(SOME_AGENT_ID); - status.setListenPort(SOME_LISTEN_PORT); - status.setRule(rule); - basicStatusActionTest(rule, expectedLoadedRuleMsg, status); - } - - @Test - public void testStatusActionNotAttached() throws CommandException { - String rule = null; - String expectedLoadedRuleMsg = "<no-loaded-rules>"; - basicStatusActionTest(rule, expectedLoadedRuleMsg, null); - } - - private void basicStatusActionTest(String rule, String expectedRuleMsg, VmBytemanStatus status) throws CommandException { - VmBytemanDAO dao = mock(VmBytemanDAO.class); - when(dao.findBytemanStatus(eq(new VmId(SOME_VM_ID)))).thenReturn(status); - Arguments args = getBasicArgsWithAction(STATUS_ACTION); - CommandContext ctx = ctxFactory.createContext(args); - command.unsetVmBytemanDao(); - command.setVmBytemanDao(dao); - command.run(ctx); - String stdErr = ctxFactory.getError(); - String stdOut = ctxFactory.getOutput(); - assertEquals(EMPTY_STRING, stdErr); - assertTrue(stdOut.contains("Byteman status for VM:")); - String listenPort = "<unset>"; - if (status != null) { - listenPort = Integer.toString(status.getListenPort()); - } - assertTrue(stdOut.contains("Byteman agent listen port: " + listenPort)); - assertTrue(stdOut.contains("Loaded rules:")); - assertTrue(stdOut.contains(expectedRuleMsg)); - } - - @Test - public void testStatusActionWithRule() throws CommandException { - String rule = "some-rule-string\nsome more rule lines\nfurther more"; - VmBytemanStatus status = new VmBytemanStatus(); - status.setVmId(SOME_VM_ID); - status.setAgentId(SOME_AGENT_ID); - status.setListenPort(SOME_LISTEN_PORT); - status.setRule(rule); - basicStatusActionTest(rule, rule, status); - } - - @Test - public void testAgentNotAlive() throws CommandException { - AgentInfoDAO agentInfoDAO = mock(AgentInfoDAO.class); - AgentInformation agentInfo = mock(AgentInformation.class); - when(agentInfo.isAlive()).thenReturn(false); - when(agentInfoDAO.getAgentInformation(any(AgentId.class))).thenReturn(agentInfo); - command.unsetAgentInfoDao(); - command.setAgentInfoDao(agentInfoDAO); - Arguments args = getBasicArgsWithAction("no-matter"); - CommandContext ctx = ctxFactory.createContext(args); - try { - command.run(ctx); - fail("Command should have thrown exception"); - } catch (CommandException e) { - String msg = e.getMessage(); - assertEquals("Agent with id " + SOME_AGENT_ID + " is not alive.", msg); - } - } - - @Test - public void testShowMetricsActionNoMetrics() throws CommandException { - String expectedStdOut = "No metrics available for VM " + SOME_VM_ID + ".\n"; - List<BytemanMetric> returnedList = Collections.emptyList(); - doShowMetricsTest(returnedList, expectedStdOut); - } - - @Test - public void testShowMetricsActionWithMetrics() throws CommandException { - String metricData1 = "{ \"foo\": \"bar\" }"; - String metricData2 = "{ \"foo2\": -300 }"; - String expectedStdOut = String.format("%s\n%s\n", metricData1, metricData2); - BytemanMetric metric1 = new BytemanMetric(); - metric1.setData(metricData1); - BytemanMetric metric2 = new BytemanMetric(); - metric2.setData(metricData2); - List<BytemanMetric> returnedList = Arrays.asList(metric1, metric2); - doShowMetricsTest(returnedList, expectedStdOut); - } - - @Test - public void testUnloadAction() throws CommandException { - VmBytemanDAO dao = mock(VmBytemanDAO.class); - VmBytemanStatus status = new VmBytemanStatus(); - int listenPort = 333; - status.setVmId(SOME_VM_ID); - status.setAgentId(SOME_AGENT_ID); - status.setListenPort(listenPort); - status.setRule(null); - when(dao.findBytemanStatus(eq(new VmId(SOME_VM_ID)))).thenReturn(status); - Arguments args = getBasicArgsWithAction(UNLOAD_ACTION); - CommandContext ctx = ctxFactory.createContext(args); - command.unsetVmBytemanDao(); - command.setVmBytemanDao(dao); - RequestQueue rQueue = mock(RequestQueue.class); - ArgumentCaptor<Request> requestCaptor = ArgumentCaptor.forClass(Request.class); - command.setRequestQueue(rQueue); - command.run(ctx); - verify(rQueue).putRequest(requestCaptor.capture()); - Request submittedRequest = requestCaptor.getValue(); - assertEquals(1, submittedRequest.getListeners().size()); - RequestResponseListener respListener = null; - for (RequestResponseListener l: submittedRequest.getListeners()) { - respListener = l; - break; - } - assertTrue(respListener instanceof BytemanRequestResponseListener); - String rawAction = submittedRequest.getParameter(BytemanRequest.ACTION_PARAM_NAME); - RequestAction actualAction = RequestAction.fromIntString(rawAction); - assertEquals(RequestAction.UNLOAD_RULES, actualAction); - assertEquals(Integer.toString(listenPort), submittedRequest.getParameter(BytemanRequest.LISTEN_PORT_PARAM_NAME)); - assertEquals(SOME_VM_ID, submittedRequest.getParameter(BytemanRequest.VM_ID_PARAM_NAME)); - assertNull(submittedRequest.getParameter(BytemanRequest.RULE_PARAM_NAME)); - assertSame(REQUEST_QUEUE_ADDRESS, submittedRequest.getTarget()); - } - - @Test - public void testLoadActionNoRuleFile() throws CommandException { - VmBytemanDAO dao = mock(VmBytemanDAO.class); - VmBytemanStatus status = new VmBytemanStatus(); - int listenPort = 333; - status.setVmId(SOME_VM_ID); - status.setAgentId(SOME_AGENT_ID); - status.setListenPort(listenPort); - status.setRule(null); - when(dao.findBytemanStatus(eq(new VmId(SOME_VM_ID)))).thenReturn(status); - Arguments args = getBasicArgsWithAction(LOAD_ACTION); // rule file arg missing - CommandContext ctx = ctxFactory.createContext(args); - command.unsetVmBytemanDao(); - command.setVmBytemanDao(dao); - RequestQueue rQueue = mock(RequestQueue.class); - command.setRequestQueue(rQueue); - try { - command.run(ctx); - fail("Expected cmd exception due to missing rule argument"); - } catch (CommandException e) { - String msg = e.getMessage(); - assertEquals("No rule option specified.", msg); - } - } - - @Test - public void testLoadActionRuleFileDoesNotExist() throws CommandException { - VmBytemanDAO dao = mock(VmBytemanDAO.class); - VmBytemanStatus status = new VmBytemanStatus(); - int listenPort = 333; - status.setVmId(SOME_VM_ID); - status.setAgentId(SOME_AGENT_ID); - status.setListenPort(listenPort); - status.setRule(null); - when(dao.findBytemanStatus(eq(new VmId(SOME_VM_ID)))).thenReturn(status); - Arguments args = getBasicArgsWithAction(LOAD_ACTION); - when(args.hasArgument(RULE_OPTION)).thenReturn(true); - String file = "i-do-not-exist"; - when(args.getArgument(RULE_OPTION)).thenReturn(file); - CommandContext ctx = ctxFactory.createContext(args); - command.unsetVmBytemanDao(); - command.setVmBytemanDao(dao); - RequestQueue rQueue = mock(RequestQueue.class); - command.setRequestQueue(rQueue); - try { - command.run(ctx); - fail("Expected cmd exception due to rule file not existing"); - } catch (CommandException e) { - String msg = e.getMessage(); - assertEquals("Specified rules file '" + file + "' not found.", msg); - } - } - - @Test - public void testLoadActionSuccessAgentAttached() throws CommandException, FileNotFoundException, IOException { - VmBytemanDAO dao = mock(VmBytemanDAO.class); - VmBytemanStatus status = new VmBytemanStatus(); - int listenPort = 333; - status.setVmId(SOME_VM_ID); - status.setAgentId(SOME_AGENT_ID); - status.setListenPort(listenPort); - status.setRule(null); - when(dao.findBytemanStatus(eq(new VmId(SOME_VM_ID)))).thenReturn(status); - Arguments args = getBasicArgsWithAction(LOAD_ACTION); - when(args.hasArgument(RULE_OPTION)).thenReturn(true); - String file = getClass().getResource("/testRule.btm").getFile(); - when(args.getArgument(RULE_OPTION)).thenReturn(file); - CommandContext ctx = ctxFactory.createContext(args); - command.unsetVmBytemanDao(); - command.setVmBytemanDao(dao); - RequestQueue rQueue = mock(RequestQueue.class); - command.setRequestQueue(rQueue); - ArgumentCaptor<Request> requestCaptor = ArgumentCaptor.forClass(Request.class); - command.run(ctx); - verify(rQueue).putRequest(requestCaptor.capture()); - Request submittedRequest = requestCaptor.getValue(); - assertEquals(1, submittedRequest.getListeners().size()); - RequestResponseListener respListener = null; - for (RequestResponseListener l: submittedRequest.getListeners()) { - respListener = l; - break; - } - assertTrue(respListener instanceof BytemanRequestResponseListener); - String rawAction = submittedRequest.getParameter(BytemanRequest.ACTION_PARAM_NAME); - RequestAction actualAction = RequestAction.fromIntString(rawAction); - assertEquals(RequestAction.LOAD_RULES, actualAction); - assertEquals(Integer.toString(listenPort), submittedRequest.getParameter(BytemanRequest.LISTEN_PORT_PARAM_NAME)); - assertEquals(SOME_VM_ID, submittedRequest.getParameter(BytemanRequest.VM_ID_PARAM_NAME)); - String expectedRule = new String(StreamUtils.readAll(new FileInputStream(new File(file)))); - assertEquals(expectedRule, submittedRequest.getParameter(BytemanRequest.RULE_PARAM_NAME)); - assertSame(REQUEST_QUEUE_ADDRESS, submittedRequest.getTarget()); - String out = ctxFactory.getOutput(); - assertEquals("Request submitted successfully.\n", out); - } - - @Test - public void testLoadActionSuccessAgentNotAttached() throws CommandException, FileNotFoundException, IOException { - VmBytemanDAO dao = mock(VmBytemanDAO.class); - // mimic no-agent-attached, by returning a null status - when(dao.findBytemanStatus(eq(new VmId(SOME_VM_ID)))).thenReturn(null); - Arguments args = getBasicArgsWithAction(LOAD_ACTION); - when(args.hasArgument(RULE_OPTION)).thenReturn(true); - String file = getClass().getResource("/testRule.btm").getFile(); - when(args.getArgument(RULE_OPTION)).thenReturn(file); - CommandContext ctx = ctxFactory.createContext(args); - command.unsetVmBytemanDao(); - command.setVmBytemanDao(dao); - RequestQueue rQueue = mock(RequestQueue.class); - command.setRequestQueue(rQueue); - ArgumentCaptor<Request> requestCaptor = ArgumentCaptor.forClass(Request.class); - command.run(ctx); - verify(rQueue).putRequest(requestCaptor.capture()); - Request submittedRequest = requestCaptor.getValue(); - assertEquals(1, submittedRequest.getListeners().size()); - RequestResponseListener respListener = null; - for (RequestResponseListener l: submittedRequest.getListeners()) { - respListener = l; - break; - } - assertTrue(respListener instanceof BytemanRequestResponseListener); - String rawAction = submittedRequest.getParameter(BytemanRequest.ACTION_PARAM_NAME); - RequestAction actualAction = RequestAction.fromIntString(rawAction); - assertEquals(RequestAction.LOAD_RULES, actualAction); - assertEquals(Integer.toString(BytemanRequest.NOT_ATTACHED_PORT), submittedRequest.getParameter(BytemanRequest.LISTEN_PORT_PARAM_NAME)); - assertEquals(SOME_VM_ID, submittedRequest.getParameter(BytemanRequest.VM_ID_PARAM_NAME)); - String expectedRule = new String(StreamUtils.readAll(new FileInputStream(new File(file)))); - assertEquals(expectedRule, submittedRequest.getParameter(BytemanRequest.RULE_PARAM_NAME)); - assertSame(REQUEST_QUEUE_ADDRESS, submittedRequest.getTarget()); - String out = ctxFactory.getOutput(); - assertEquals("Request submitted successfully.\n", out); - } - - @SuppressWarnings("unchecked") - private void doShowMetricsTest(List<BytemanMetric> metricsToReturn, String stdOutExpected) throws CommandException { - VmBytemanDAO dao = mock(VmBytemanDAO.class); - VmBytemanStatus status = new VmBytemanStatus(); - int listenPort = 333; - status.setVmId(SOME_VM_ID); - status.setAgentId(SOME_AGENT_ID); - status.setListenPort(listenPort); - status.setRule(null); - when(dao.findBytemanStatus(eq(new VmId(SOME_VM_ID)))).thenReturn(status); - when(dao.findBytemanMetrics(any(Range.class), any(VmId.class), any(AgentId.class))).thenReturn(metricsToReturn); - Arguments args = getBasicArgsWithAction(SHOW_METRICS_ACTION); - CommandContext ctx = ctxFactory.createContext(args); - command.unsetVmBytemanDao(); - command.setVmBytemanDao(dao); - command.run(ctx); - String stdErr = ctxFactory.getError(); - String stdOut = ctxFactory.getOutput(); - assertEquals(EMPTY_STRING, stdErr); - assertEquals(stdOutExpected, stdOut); - } - - private Arguments getBasicArgsWithAction(String action) { - Arguments args = mock(Arguments.class); - when(args.getArgument("vmId")).thenReturn(SOME_VM_ID); - when(args.getNonOptionArguments()).thenReturn(Arrays.asList(action)); - return args; - } -}