Mercurial > hg > release > thermostat-0.4
changeset 563:51f768003f10
Fix regression in ServiceCommand shutdown (PR1134)
Review-thread: http://icedtea.classpath.org/pipermail/thermostat/2012-August/002879.html
Reviewed-by: omajid
PR1134
line wrap: on
line diff
--- a/agent/cli/src/main/java/com/redhat/thermostat/agent/cli/ServiceCommand.java Wed Aug 22 19:23:39 2012 +0200 +++ b/agent/cli/src/main/java/com/redhat/thermostat/agent/cli/ServiceCommand.java Wed Aug 22 16:28:44 2012 -0400 @@ -36,173 +36,83 @@ package com.redhat.thermostat.agent.cli; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.List; -import java.util.concurrent.CopyOnWriteArrayList; -import java.util.logging.Level; -import java.util.logging.Logger; - -import org.osgi.framework.BundleContext; -import org.osgi.framework.ServiceReference; +import java.util.concurrent.Semaphore; import com.redhat.thermostat.agent.cli.db.StorageAlreadyRunningException; import com.redhat.thermostat.agent.cli.impl.locale.LocaleResources; import com.redhat.thermostat.agent.cli.impl.locale.Translate; import com.redhat.thermostat.common.ActionEvent; import com.redhat.thermostat.common.ActionListener; -import com.redhat.thermostat.common.ActionNotifier; -import com.redhat.thermostat.common.NotImplementedException; import com.redhat.thermostat.common.cli.ArgumentSpec; import com.redhat.thermostat.common.cli.CommandContext; -import com.redhat.thermostat.common.cli.CommandContextImpl; import com.redhat.thermostat.common.cli.CommandException; -import com.redhat.thermostat.common.cli.OSGiContext; import com.redhat.thermostat.common.cli.SimpleArgumentSpec; -import com.redhat.thermostat.common.cli.SimpleArguments; -import com.redhat.thermostat.common.config.InvalidConfigurationException; -import com.redhat.thermostat.common.config.StartupConfiguration; -import com.redhat.thermostat.common.tools.ApplicationException; +import com.redhat.thermostat.common.cli.SimpleCommand; import com.redhat.thermostat.common.tools.ApplicationState; -import com.redhat.thermostat.common.tools.BasicCommand; +import com.redhat.thermostat.common.utils.OSGIUtils; import com.redhat.thermostat.launcher.Launcher; /** * Simple service that allows starting Agent and DB Backend * in a single step. */ -public class ServiceCommand extends BasicCommand implements ActionListener<ApplicationState>, OSGiContext { +public class ServiceCommand extends SimpleCommand implements ActionListener<ApplicationState> { private static final String NAME = "service"; - private static final String DESCRIPTION = Translate.localize(LocaleResources.COMMAND_SERVICE_DESCRIPTION); - private static final String USAGE = DESCRIPTION; - private BasicCommand database; - private AgentApplication agent; - - private ActionNotifier<ApplicationState> notifier; + private List<ActionListener<ApplicationState>> listeners; - private CommandContext context; - private BundleContext bundleContext; - - private List<Runnable> tasksOnStop = new CopyOnWriteArrayList<>(); + private Semaphore agentBarrier = new Semaphore(0); public ServiceCommand() { - database = new StorageCommand(); - agent = new AgentApplication(); - notifier = new ActionNotifier<>(this); - } - - @Override - public void setBundleContext(BundleContext context) { - this.bundleContext = context; - } - - private void addListeners() throws InvalidConfigurationException { - // Currently, all the arguments are for the db. We only configure the - // agent accordingly to the database settings. - // so nothing else is done here at this stage - database.getNotifier().addActionListener(this); - agent.getNotifier().addActionListener(this); + listeners = new ArrayList<>(); + listeners.add(this); } @Override public void run(CommandContext ctx) throws CommandException { - context = ctx; - try { - addListeners(); - // just run the database, if the database is successful, let the - // listeners start the agent for us. - database.run(ctx); - } catch (InvalidConfigurationException e) { - throw new CommandException(e); - } - } - - @Override - public ActionNotifier<ApplicationState> getNotifier() { - return notifier; - } - - @Override - public StartupConfiguration getConfiguration() { - throw new NotImplementedException("NYI"); + Launcher launcher = getLauncher(); + String[] storageStartArgs = new String[] { "storage", "--start" }; + launcher.setArgs(storageStartArgs); + launcher.run(listeners); + agentBarrier.acquireUninterruptibly(); + String[] storageStopArgs = new String[] { "storage", "--stop" }; + launcher.setArgs(storageStopArgs); + launcher.run(); } @Override public void actionPerformed(ActionEvent<ApplicationState> actionEvent) { - if (actionEvent.getSource().equals(database)) { + if (actionEvent.getSource() instanceof StorageCommand) { + StorageCommand storage = (StorageCommand) actionEvent.getSource(); + // Implementation detail: there is a single StorageCommand instance registered + // as an OSGi service. We remove ourselves as listener so that we don't get + // notified in the case that the command is invoked by some other means later. + storage.getNotifier().removeActionListener(this); switch (actionEvent.getActionId()) { - // we are only interested in starting the agent if - // we started the database ourselves case START: - - // set a bundle-stop hook if the db was started by us - tasksOnStop.add(new Runnable() { - @Override - public void run() { - String[] args = new String[] { "storage", "--stop" }; - - ServiceReference launcherRef = bundleContext.getServiceReference(Launcher.class.getName()); - if (launcherRef != null) { - Launcher launcher = (Launcher) bundleContext.getService(launcherRef); - launcher.setArgs(args); - launcher.run(); - } else { - try { - SimpleArguments arguments = new SimpleArguments(); - arguments.addArgument("stop", args[1]); - database.run(new CommandContextImpl(arguments, - ServiceCommand.this.context.getCommandContextFactory())); - - } catch (CommandException e) { - Logger.getLogger(getClass().getSimpleName()).log(Level.WARNING, "Can't stop database", e); - } - } - - } - }); - - String dbUrl = database.getConfiguration().getDBConnectionString(); - SimpleArguments args = new SimpleArguments(); - args.addArgument("dbUrl", dbUrl); - try { - System.err.println(Translate.localize(LocaleResources.STARTING_AGENT)); - agent.run(new CommandContextImpl(args, context.getCommandContextFactory())); - } catch (CommandException e) { - notifier.fireAction(ApplicationState.FAIL); - } + String dbUrl = storage.getConfiguration().getDBConnectionString(); + Launcher launcher = getLauncher(); + String[] agentArgs = new String[] {"agent", "-d", dbUrl}; + System.err.println(Translate.localize(LocaleResources.STARTING_AGENT)); + launcher.setArgs(agentArgs); + launcher.run(); + agentBarrier.release(); break; - case FAIL: System.err.println(Translate.localize(LocaleResources.ERROR_STARTING_DB)); Object payload = actionEvent.getPayload(); - if (payload instanceof ApplicationException) { - ApplicationException exception = (ApplicationException) payload; - if (exception instanceof StorageAlreadyRunningException) { - System.err.println(Translate.localize(LocaleResources.STORAGE_ALREADY_RUNNING)); - } + if (payload instanceof StorageAlreadyRunningException) { + System.err.println(Translate.localize(LocaleResources.STORAGE_ALREADY_RUNNING)); } - - notifier.fireAction(ApplicationState.FAIL); break; } - } else if (actionEvent.getSource().equals(agent)) { - // agent is running - switch (actionEvent.getActionId()) { - case START: - notifier.fireAction(ApplicationState.SUCCESS); - break; - } - } - } - - @Override - public void disable() { - for (Runnable task: tasksOnStop) { - task.run(); } } @@ -222,11 +132,25 @@ } @Override + public boolean isAvailableInShell() { + return false; + } + + @Override public Collection<ArgumentSpec> getAcceptedArguments() { ArgumentSpec start = new SimpleArgumentSpec("start", Translate.localize(LocaleResources.COMMAND_SERVICE_ARGUMENT_START_DESCRIPTION)); ArgumentSpec stop = new SimpleArgumentSpec("stop", Translate.localize(LocaleResources.COMMAND_SERVICE_ARGUMENT_STOP_DESCRIPTION)); return Arrays.asList(start, stop); } + @Override + public boolean isStorageRequired() { + return false; + } + + private Launcher getLauncher() { + OSGIUtils osgi = OSGIUtils.getInstance(); + return osgi.getService(Launcher.class); + } }
--- a/agent/cli/src/main/java/com/redhat/thermostat/agent/cli/StorageCommand.java Wed Aug 22 19:23:39 2012 +0200 +++ b/agent/cli/src/main/java/com/redhat/thermostat/agent/cli/StorageCommand.java Wed Aug 22 16:28:44 2012 -0400 @@ -104,7 +104,6 @@ if (parser.isDryRun()) return; runner = createRunner(); - try { switch (parser.getAction()) { case START: @@ -117,7 +116,6 @@ break; } getNotifier().fireAction(ApplicationState.SUCCESS); - } catch (Exception e) { getNotifier().fireAction(ApplicationState.FAIL, e); } @@ -182,11 +180,6 @@ } @Override - public void disable() { - /* NO-OP */ - } - - @Override public DBStartupConfiguration getConfiguration() { return configuration; }
--- a/client/core/src/main/java/com/redhat/thermostat/client/internal/GUIClientCommand.java Wed Aug 22 19:23:39 2012 +0200 +++ b/client/core/src/main/java/com/redhat/thermostat/client/internal/GUIClientCommand.java Wed Aug 22 16:28:44 2012 -0400 @@ -83,11 +83,6 @@ } @Override - public void disable() { - // TODO clientMain.shutdown(); - } - - @Override public String getName() { return "gui"; }
--- a/client/heapdumper/src/test/java/com/redhat/thermostat/client/heap/cli/FindObjectsCommandTest.java Wed Aug 22 19:23:39 2012 +0200 +++ b/client/heapdumper/src/test/java/com/redhat/thermostat/client/heap/cli/FindObjectsCommandTest.java Wed Aug 22 16:28:44 2012 -0400 @@ -232,9 +232,4 @@ assertEquals("Heap ID not found: " + INVALID_HEAP_ID + "\n", factory.getOutput()); } - - @Test - public void testDisable() { - cmd.disable(); // No side-effects. Hopefully... - } }
--- a/client/heapdumper/src/test/java/com/redhat/thermostat/client/heap/cli/FindRootCommandTest.java Wed Aug 22 19:23:39 2012 +0200 +++ b/client/heapdumper/src/test/java/com/redhat/thermostat/client/heap/cli/FindRootCommandTest.java Wed Aug 22 16:28:44 2012 -0400 @@ -324,10 +324,4 @@ assertEquals("Object not found: fluff", ex.getMessage()); } } - - @Test - public void testDisable() { - cmd.disable(); // No side effects... hopefully :-) - } - }
--- a/client/heapdumper/src/test/java/com/redhat/thermostat/client/heap/cli/ObjectInfoCommandTest.java Wed Aug 22 19:23:39 2012 +0200 +++ b/client/heapdumper/src/test/java/com/redhat/thermostat/client/heap/cli/ObjectInfoCommandTest.java Wed Aug 22 16:28:44 2012 -0400 @@ -181,11 +181,6 @@ } @Test - public void testDisable() { - cmd.disable(); // No side effects... hopefully :-) - } - - @Test public void testSimpleObject() throws CommandException { TestCommandContextFactory factory = new TestCommandContextFactory(); SimpleArguments args = new SimpleArguments();
--- a/common/core/src/main/java/com/redhat/thermostat/common/cli/Command.java Wed Aug 22 19:23:39 2012 +0200 +++ b/common/core/src/main/java/com/redhat/thermostat/common/cli/Command.java Wed Aug 22 16:28:44 2012 -0400 @@ -61,23 +61,6 @@ public void run(CommandContext ctx) throws CommandException; /** - * Called when the command is first installed into the system. This is a - * good point to initiate one-time initialization actions. - * <p> - * Should be idempotent. - */ - public void enable(); - - /** - * Called when the command is being removed from the system. The command - * should cancel any long-term action it has taken, such as any background - * tasks or threads it has spawned. - * <p> - * Should be idempotent. - */ - public void disable(); - - /** * Returns a name for this command. This will be used by the user to select * this command. */
--- a/common/core/src/main/java/com/redhat/thermostat/common/cli/CommandRegistryImpl.java Wed Aug 22 19:23:39 2012 +0200 +++ b/common/core/src/main/java/com/redhat/thermostat/common/cli/CommandRegistryImpl.java Wed Aug 22 16:28:44 2012 -0400 @@ -65,7 +65,6 @@ if (cmd instanceof OSGiContext) { ((OSGiContext) cmd).setBundleContext(context); } - cmd.enable(); proxy.registerService(cmd, cmd.getName()); myRegisteredCommands.add(cmd); } @@ -79,9 +78,6 @@ @Override public void unregisterCommands() { - for (Command command : myRegisteredCommands) { - command.disable(); - } proxy.unregisterAll(); }
--- a/common/core/src/main/java/com/redhat/thermostat/common/cli/SimpleCommand.java Wed Aug 22 19:23:39 2012 +0200 +++ b/common/core/src/main/java/com/redhat/thermostat/common/cli/SimpleCommand.java Wed Aug 22 16:28:44 2012 -0400 @@ -39,16 +39,6 @@ public abstract class SimpleCommand implements Command { @Override - public void enable() { - /* NO-OP */ - } - - @Override - public void disable() { - /* NO-OP */ - } - - @Override public boolean isStorageRequired() { return true; }
--- a/common/core/src/main/java/com/redhat/thermostat/common/tools/BasicCommand.java Wed Aug 22 19:23:39 2012 +0200 +++ b/common/core/src/main/java/com/redhat/thermostat/common/tools/BasicCommand.java Wed Aug 22 16:28:44 2012 -0400 @@ -68,16 +68,6 @@ } @Override - public void enable() { - /* NO-OP */ - } - - @Override - public void disable() { - /* NO-OP */ - } - - @Override public boolean isAvailableInShell() { return true; }
--- a/common/core/src/test/java/com/redhat/thermostat/common/cli/CommandRegistryImplTest.java Wed Aug 22 19:23:39 2012 +0200 +++ b/common/core/src/test/java/com/redhat/thermostat/common/cli/CommandRegistryImplTest.java Wed Aug 22 16:28:44 2012 -0400 @@ -131,9 +131,7 @@ commandRegistry.unregisterCommands(); - verify(cmd1).disable(); verify(cmd1Reg).unregister(); - verify(cmd2).disable(); verify(cmd2Reg).unregister(); verifyNoMoreInteractions(bundleContext);
--- a/common/core/src/test/java/com/redhat/thermostat/common/tools/BasicCommandTest.java Wed Aug 22 19:23:39 2012 +0200 +++ b/common/core/src/test/java/com/redhat/thermostat/common/tools/BasicCommandTest.java Wed Aug 22 16:28:44 2012 -0400 @@ -64,11 +64,6 @@ } @Override - public void disable() { - // Move along - } - - @Override public String getName() { return null; }
--- a/common/test/src/main/java/com/redhat/thermostat/test/cli/TestCommand.java Wed Aug 22 19:23:39 2012 +0200 +++ b/common/test/src/main/java/com/redhat/thermostat/test/cli/TestCommand.java Wed Aug 22 16:28:44 2012 -0400 @@ -61,7 +61,6 @@ public static interface Handle { public void run(CommandContext ctx) throws CommandException; - public void stop(); } public TestCommand(String name) { @@ -81,18 +80,6 @@ } @Override - public void enable() { - // TODO what do we do here? - } - - @Override - public void disable() { - if (handle != null) { - handle.stop(); - } - } - - @Override public String getName() { return name; }
--- a/launcher/src/main/java/com/redhat/thermostat/launcher/Launcher.java Wed Aug 22 19:23:39 2012 +0200 +++ b/launcher/src/main/java/com/redhat/thermostat/launcher/Launcher.java Wed Aug 22 16:28:44 2012 -0400 @@ -36,6 +36,11 @@ package com.redhat.thermostat.launcher; +import java.util.Collection; + +import com.redhat.thermostat.common.ActionListener; +import com.redhat.thermostat.common.tools.ApplicationState; + /** * Launcher is the main entry point for all Thermostat commands. */ @@ -43,11 +48,19 @@ /** * Invoked in order to start a command, either when Thermostat starts, or within - * the thermostat shell. + * the thermostat shell. Equivalent to calling run(null). */ void run(); /** + * Invoked in order to start a command, either when Thermostat starts, or within + * the thermostat shell. If the command being run happens to be a BasicCommand, + * and the argument is non-null, the listeners will be added to the command for + * life cycle notifications. Otherwise, the argument is ignored. + */ + void run(Collection<ActionListener<ApplicationState>> listeners); + + /** * Should be set before calling run() * @param command line arguments to the program */
--- a/launcher/src/main/java/com/redhat/thermostat/launcher/internal/LauncherImpl.java Wed Aug 22 19:23:39 2012 +0200 +++ b/launcher/src/main/java/com/redhat/thermostat/launcher/internal/LauncherImpl.java Wed Aug 22 16:28:44 2012 -0400 @@ -47,6 +47,8 @@ import org.osgi.framework.BundleException; import com.redhat.thermostat.bundles.OSGiRegistryService; +import com.redhat.thermostat.common.ActionListener; +import com.redhat.thermostat.common.ActionNotifier; import com.redhat.thermostat.common.TimerFactory; import com.redhat.thermostat.common.Version; import com.redhat.thermostat.common.appctx.ApplicationContext; @@ -60,6 +62,8 @@ import com.redhat.thermostat.common.config.ClientPreferences; import com.redhat.thermostat.common.config.InvalidConfigurationException; import com.redhat.thermostat.common.storage.ConnectionException; +import com.redhat.thermostat.common.tools.ApplicationState; +import com.redhat.thermostat.common.tools.BasicCommand; import com.redhat.thermostat.common.utils.LoggingUtils; import com.redhat.thermostat.common.utils.OSGIUtils; import com.redhat.thermostat.launcher.CommonCommandOptions; @@ -91,6 +95,11 @@ @Override public synchronized void run() { + run(null); + } + + @Override + public synchronized void run(Collection<ActionListener<ApplicationState>> listeners) { usageCount++; try { argsBarrier.acquire(); @@ -108,7 +117,7 @@ Version coreVersion = new Version(); cmdCtxFactory.getConsole().getOutput().println(coreVersion.getVersionInfo()); } else { - runCommandFromArguments(); + runCommandFromArguments(listeners); } } finally { args = null; @@ -163,22 +172,22 @@ } private void runHelpCommand() { - runCommand("help", new String[0]); + runCommand("help", new String[0], null); } - private void runCommandFromArguments() { - runCommand(args[0], Arrays.copyOfRange(args, 1, args.length)); + private void runCommandFromArguments(Collection<ActionListener<ApplicationState>> listeners) { + runCommand(args[0], Arrays.copyOfRange(args, 1, args.length), listeners); } - private void runCommand(String cmdName, String[] cmdArgs) { + private void runCommand(String cmdName, String[] cmdArgs, Collection<ActionListener<ApplicationState>> listeners) { try { - parseArgsAndRunCommand(cmdName, cmdArgs); + parseArgsAndRunCommand(cmdName, cmdArgs, listeners); } catch (CommandException e) { cmdCtxFactory.getConsole().getError().println(e.getMessage()); } } - private void parseArgsAndRunCommand(String cmdName, String[] cmdArgs) throws CommandException { + private void parseArgsAndRunCommand(String cmdName, String[] cmdArgs, Collection<ActionListener<ApplicationState>> listeners) throws CommandException { PrintStream out = cmdCtxFactory.getConsole().getOutput(); try { @@ -198,6 +207,13 @@ runHelpCommand(); return; } + if (listeners != null && cmd instanceof BasicCommand) { + BasicCommand basicCmd = (BasicCommand) cmd; + ActionNotifier<ApplicationState> notifier = basicCmd.getNotifier(); + for (ActionListener<ApplicationState> listener : listeners) { + notifier.addActionListener(listener); + } + } CommonCommandOptions commonOpts = new CommonCommandOptions(); Collection<ArgumentSpec> acceptedOptions = commonOpts.getAcceptedOptionsFor(cmd); Arguments args = parseCommandArguments(cmdArgs, acceptedOptions);
--- a/launcher/src/test/java/com/redhat/thermostat/launcher/LauncherTest.java Wed Aug 22 19:23:39 2012 +0200 +++ b/launcher/src/test/java/com/redhat/thermostat/launcher/LauncherTest.java Wed Aug 22 16:28:44 2012 -0400 @@ -43,7 +43,9 @@ import static org.mockito.Mockito.when; import java.text.MessageFormat; +import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; import java.util.logging.Level; import java.util.logging.Logger; @@ -61,6 +63,8 @@ import org.powermock.modules.junit4.PowerMockRunner; import com.redhat.thermostat.bundles.OSGiRegistryService; +import com.redhat.thermostat.common.ActionListener; +import com.redhat.thermostat.common.ActionNotifier; import com.redhat.thermostat.common.ApplicationInfo; import com.redhat.thermostat.common.Version; import com.redhat.thermostat.common.appctx.ApplicationContext; @@ -73,6 +77,8 @@ import com.redhat.thermostat.common.config.ClientPreferences; import com.redhat.thermostat.common.locale.LocaleResources; import com.redhat.thermostat.common.locale.Translate; +import com.redhat.thermostat.common.tools.ApplicationState; +import com.redhat.thermostat.common.tools.BasicCommand; import com.redhat.thermostat.launcher.internal.HelpCommand; import com.redhat.thermostat.launcher.internal.LauncherImpl; import com.redhat.thermostat.test.TestCommandContextFactory; @@ -106,10 +112,6 @@ Arguments args = ctx.getArguments(); ctx.getConsole().getOutput().print(args.getArgument("arg1") + ", " + args.getArgument("arg2")); } - - @Override - public void stop() { /* N0-OP */ } - } private static class TestCmd2 implements TestCommand.Handle { @@ -118,11 +120,6 @@ Arguments args = ctx.getArguments(); ctx.getConsole().getOutput().print(args.getArgument("arg4") + ": " + args.getArgument("arg3")); } - - @Override - public void stop() { - /* NO-OP */ - } } private TestCommandContextFactory ctxFactory; @@ -130,6 +127,7 @@ private BundleContext bundleContext; private TestTimerFactory timerFactory; private OSGiRegistryService registry; + private ActionNotifier<ApplicationState> notifier; @Before public void setUp() { @@ -158,7 +156,14 @@ cmd3.setStorageRequired(true); cmd3.setDescription("description 3"); - ctxFactory.getCommandRegistry().registerCommands(Arrays.asList(new HelpCommand(), cmd1, cmd2, cmd3)); + BasicCommand basicCmd = mock(BasicCommand.class); + when(basicCmd.getName()).thenReturn("basic"); + when(basicCmd.getDescription()).thenReturn("nothing that means anything"); + when(basicCmd.isStorageRequired()).thenReturn(false); + notifier = mock(ActionNotifier.class); + when(basicCmd.getNotifier()).thenReturn(notifier); + + ctxFactory.getCommandRegistry().registerCommands(Arrays.asList(new HelpCommand(), cmd1, cmd2, cmd3, basicCmd)); registry = mock(OSGiRegistryService.class); } @@ -197,6 +202,7 @@ public void testMainNoArgs() { String expected = "list of commands:\n\n" + " help show help for a given command or help overview\n" + + " basic nothing that means anything\n" + " test1 description 1\n" + " test2 description 2\n" + " test3 description 3\n"; @@ -215,6 +221,7 @@ String expected = "unknown command '--help'\n" + "list of commands:\n\n" + " help show help for a given command or help overview\n" + + " basic nothing that means anything\n" + " test1 description 1\n" + " test2 description 2\n" + " test3 description 3\n"; @@ -226,6 +233,7 @@ String expected = "unknown command '-help'\n" + "list of commands:\n\n" + " help show help for a given command or help overview\n" + + " basic nothing that means anything\n" + " test1 description 1\n" + " test2 description 2\n" + " test3 description 3\n"; @@ -237,6 +245,7 @@ String expected = "unknown command 'foobarbaz'\n" + "list of commands:\n\n" + " help show help for a given command or help overview\n" + + " basic nothing that means anything\n" + " test1 description 1\n" + " test2 description 2\n" + " test3 description 3\n"; @@ -248,6 +257,7 @@ String expected = "unknown command 'foo'\n" + "list of commands:\n\n" + " help show help for a given command or help overview\n" + + " basic nothing that means anything\n" + " test1 description 1\n" + " test2 description 2\n" + " test3 description 3\n"; @@ -263,8 +273,6 @@ throw new CommandException("test error"); } - @Override - public void stop() { /* NO-OP */ } }); ctxFactory.getCommandRegistry().registerCommands(Arrays.asList(errorCmd)); @@ -355,4 +363,19 @@ assertEquals(expectedVersionInfo, ctxFactory.getOutput()); assertTrue(timerFactory.isShutdown()); } + + @Test + public void verifyListenersAdded() { + ActionListener<ApplicationState> listener = mock(ActionListener.class); + Collection<ActionListener<ApplicationState>> listeners = new ArrayList<>(); + listeners.add(listener); + String[] args = new String[] {"basic"}; + LauncherImpl launcher = new LauncherImpl(bundleContext, ctxFactory, registry); + Keyring keyring = mock(Keyring.class); + launcher.setPreferences(new ClientPreferences(keyring)); + + launcher.setArgs(args); + launcher.run(listeners); + verify(notifier).addActionListener(listener); + } }