# HG changeset patch # User Joshua Matsuoka # Date 1502474618 14400 # Node ID 08726e4b6d43a06f824f37331460af4a2f659512 # Parent 90361ef466fb5b3b3e376023c4759ccdb28e78ab Convert Agent-Core to Declarative Services This patch also merges the agent-cli and agent-core bundles. Reviewed-by: jerboaa, neugens Review-Thread: http://icedtea.classpath.org/pipermail/thermostat/2017-August/024462.html diff -r 90361ef466fb -r 08726e4b6d43 agent/cli/pom.xml --- a/agent/cli/pom.xml Thu Aug 10 09:41:24 2017 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,135 +0,0 @@ - - - - 4.0.0 - - - com.redhat.thermostat - thermostat-agent - 1.99.12-SNAPSHOT - - - thermostat-agent-cli - bundle - - Thermostat Agent Command Line - - - - junit - junit - test - - - org.mockito - mockito-core - test - - - org.powermock - powermock-api-mockito - test - - - org.powermock - powermock-module-junit4 - test - - - com.redhat.thermostat - thermostat-agent-core - ${project.version} - - - com.redhat.thermostat - thermostat-common-core - ${project.version} - - - com.redhat.thermostat - thermostat-launcher - ${project.version} - - - com.redhat.thermostat - thermostat-process-handler - ${project.version} - - - org.osgi - org.osgi.core - - - com.redhat.thermostat - thermostat-common-test - ${project.version} - test - - - com.redhat.thermostat - thermostat-storage-core - ${project.version} - - - - - - - org.apache.felix - maven-bundle-plugin - true - - - Red Hat, Inc. - com.redhat.thermostat.agent.cli.internal.Activator - com.redhat.thermostat.agent.cli - - com.redhat.thermostat.agent.cli.internal, - com.redhat.thermostat.agent.cli.internal.locale, - - - <_nouses>true - - - - - - - - diff -r 90361ef466fb -r 08726e4b6d43 agent/cli/src/main/java/com/redhat/thermostat/agent/cli/internal/Activator.java --- a/agent/cli/src/main/java/com/redhat/thermostat/agent/cli/internal/Activator.java Thu Aug 10 09:41:24 2017 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,100 +0,0 @@ -/* - * Copyright 2012-2017 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 - * . - * - * 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.agent.cli.internal; - -import com.redhat.thermostat.agent.dao.AgentInfoDAO; -import com.redhat.thermostat.agent.dao.BackendInfoDAO; -import org.osgi.framework.BundleActivator; -import org.osgi.framework.BundleContext; - -import com.redhat.thermostat.common.ExitStatus; -import com.redhat.thermostat.common.MultipleServiceTracker; -import com.redhat.thermostat.common.MultipleServiceTracker.Action; -import com.redhat.thermostat.common.MultipleServiceTracker.DependencyProvider; -import com.redhat.thermostat.common.cli.CommandRegistry; -import com.redhat.thermostat.common.cli.CommandRegistryImpl; -import com.redhat.thermostat.shared.config.SSLConfiguration; -import com.redhat.thermostat.storage.core.WriterID; - -public class Activator implements BundleActivator { - - private CommandRegistry reg; - private AgentApplication agentApplication; - private MultipleServiceTracker tracker; - - @Override - public void start(final BundleContext context) throws Exception { - reg = new CommandRegistryImpl(context); - - Class[] deps = new Class[] { - ExitStatus.class, - WriterID.class, - SSLConfiguration.class, - AgentInfoDAO.class, - BackendInfoDAO.class, - }; - tracker = new MultipleServiceTracker(context, deps, new Action() { - - @Override - public void dependenciesAvailable(DependencyProvider services) { - ExitStatus exitStatus = services.get(ExitStatus.class); - WriterID writerID = services.get(WriterID.class); - agentApplication = new AgentApplication(context, exitStatus, writerID); - reg.registerCommand("agent", agentApplication); - } - - @Override - public void dependenciesUnavailable() { - agentApplication.shutdown(ExitStatus.EXIT_SUCCESS); - reg.unregisterCommands(); - } - }); - tracker.open(); - } - - @Override - public void stop(BundleContext context) throws Exception { - if (agentApplication != null) { - // Bundle may be shut down *before* deps become available and - // app is set. - agentApplication.shutdown(ExitStatus.EXIT_SUCCESS); - } - reg.unregisterCommands(); - tracker.close(); - } -} - diff -r 90361ef466fb -r 08726e4b6d43 agent/cli/src/main/java/com/redhat/thermostat/agent/cli/internal/AgentApplication.java --- a/agent/cli/src/main/java/com/redhat/thermostat/agent/cli/internal/AgentApplication.java Thu Aug 10 09:41:24 2017 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,275 +0,0 @@ -/* - * Copyright 2012-2017 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 - * . - * - * 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.agent.cli.internal; - -import java.util.concurrent.CountDownLatch; -import java.util.logging.Level; -import java.util.logging.Logger; - -import com.redhat.thermostat.agent.dao.AgentInfoDAO; -import com.redhat.thermostat.agent.dao.BackendInfoDAO; -import org.osgi.framework.BundleContext; -import org.osgi.util.tracker.ServiceTracker; - -import com.redhat.thermostat.agent.Agent; -import com.redhat.thermostat.agent.config.AgentConfigsUtils; -import com.redhat.thermostat.agent.config.AgentOptionParser; -import com.redhat.thermostat.agent.config.AgentStartupConfiguration; -import com.redhat.thermostat.backend.BackendRegistry; -import com.redhat.thermostat.backend.BackendService; -import com.redhat.thermostat.common.ExitStatus; -import com.redhat.thermostat.common.LaunchException; -import com.redhat.thermostat.common.MultipleServiceTracker; -import com.redhat.thermostat.common.MultipleServiceTracker.Action; -import com.redhat.thermostat.common.MultipleServiceTracker.DependencyProvider; -import com.redhat.thermostat.common.cli.AbstractStateNotifyingCommand; -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.tools.ApplicationState; -import com.redhat.thermostat.common.utils.LoggingUtils; -import com.redhat.thermostat.shared.config.InvalidConfigurationException; -import com.redhat.thermostat.storage.core.WriterID; -import sun.misc.Signal; -import sun.misc.SignalHandler; - -@SuppressWarnings("restriction") -public final class AgentApplication extends AbstractStateNotifyingCommand { - - /** - * Property for turning on verbose mode. This is there so as to be able to - * run integration tests independent of log levels. - */ - private static final String VERBOSE_MODE_PROPERTY = "thermostat.agent.verbose"; - // Messages printed in verbose mode. Integration tests use this. Be careful - // when you change those! - private static final String VERBOSE_MODE_AGENT_STOPPED_MSG = "Agent stopped."; - private static final String VERBOSE_MODE_AGENT_STARTED_MSG = "Agent started."; - - private static final String SIGINT_NAME = "INT"; - private static final String SIGTERM_NAME = "TERM"; - - private static final Logger logger = LoggingUtils.getLogger(AgentApplication.class); - - private final BundleContext bundleContext; - private final ConfigurationCreator configurationCreator; - - private AgentStartupConfiguration configuration; - private AgentOptionParser parser; - @SuppressWarnings("rawtypes") - private ServiceTracker configServerTracker; - private MultipleServiceTracker depTracker; - private final ExitStatus exitStatus; - private final WriterID writerId; - private CountDownLatch shutdownLatch; - - private CustomSignalHandler handler; - - public AgentApplication(BundleContext bundleContext, ExitStatus exitStatus, WriterID writerId) { - this(bundleContext, exitStatus, writerId, new ConfigurationCreator()); - } - - AgentApplication(BundleContext bundleContext, ExitStatus exitStatus, WriterID writerId, ConfigurationCreator configurationCreator) { - this.bundleContext = bundleContext; - this.configurationCreator = configurationCreator; - this.exitStatus = exitStatus; - this.writerId = writerId; - } - - private void parseArguments(Arguments args) throws InvalidConfigurationException { - parser = new AgentOptionParser(configuration, args); - parser.parse(); - } - - private void runAgent(CommandContext ctx) throws CommandException { - long startTime = System.currentTimeMillis(); - configuration.setStartTime(startTime); - - shutdownLatch = new CountDownLatch(1); - prepareAgent(); - - try { - // Wait for either SIGINT or SIGTERM - shutdownLatch.await(); - logger.fine("terminating agent cmd"); - } catch (InterruptedException e) { - // Ensure proper shutdown if interrupted - handler.handle(new Signal(SIGINT_NAME)); - return; - } - } - - @Override - public void run(CommandContext ctx) throws CommandException { - configuration = configurationCreator.create(); - - parseArguments(ctx.getArguments()); - if (!parser.isHelp()) { - runAgent(ctx); - } - } - - public void shutdown(int shutDownStatus) { - // Exit application - if (shutdownLatch != null) { - shutdownLatch.countDown(); - } - - if (depTracker != null) { - depTracker.close(); - } - if (configServerTracker != null) { - configServerTracker.close(); - } - this.exitStatus.setExitStatus(shutDownStatus); - if (shutDownStatus == ExitStatus.EXIT_SUCCESS) { - getNotifier().fireAction(ApplicationState.STOP); - } else { - getNotifier().fireAction(ApplicationState.FAIL); - } - } - - private class CustomSignalHandler implements SignalHandler { - - private Agent agent; - - public CustomSignalHandler(Agent agent) { - this.agent = agent; - } - - @Override - public void handle(Signal arg0) { - try { - agent.stop(); - } catch (Exception ex) { - // We don't want any exception to hold back the signal handler, otherwise - // there will be no way to actually stop Thermostat. - ex.printStackTrace(); - } - logger.fine("Agent stopped."); - // Hook for integration tests. Print a well known message to stdout - // if verbose mode is turned on via the system property. - if (Boolean.getBoolean(VERBOSE_MODE_PROPERTY)) { - System.out.println(VERBOSE_MODE_AGENT_STOPPED_MSG); - } - shutdown(ExitStatus.EXIT_SUCCESS); - } - - } - - Agent startAgent(AgentInfoDAO agentInfoDAO, BackendInfoDAO backendInfoDAO) { - BackendRegistry backendRegistry = null; - try { - backendRegistry = new BackendRegistry(bundleContext); - - } catch (Exception e) { - logger.log(Level.SEVERE, "Could not get BackendRegistry instance.", e); - shutdown(ExitStatus.EXIT_ERROR); - // Since this would throw NPE's down the line if we continue in this - // method, let's fail right and early :) - throw new RuntimeException(e); - } - - final Agent agent = new Agent(backendRegistry, configuration, agentInfoDAO, backendInfoDAO, writerId); - try { - logger.fine("Starting agent."); - agent.start(); - - bundleContext.registerService(BackendService.class, new BackendService(), null); - - } catch (LaunchException le) { - logger.log(Level.SEVERE, - "Agent could not start, probably because a configured backend could not be activated.", - le); - shutdown(ExitStatus.EXIT_ERROR); - } - logger.fine("Agent started."); - // Hook for integration tests. Print a well known message to stdout - // if verbose mode is turned on via the system property. - if (Boolean.getBoolean(VERBOSE_MODE_PROPERTY)) { - System.out.println(VERBOSE_MODE_AGENT_STARTED_MSG); - } - - logger.info("Agent id: " + agent.getId()); - getNotifier().fireAction(ApplicationState.START, agent.getId()); - return agent; - } - - private void prepareAgent() { - Class[] deps = new Class[] { - AgentInfoDAO.class, - BackendInfoDAO.class - }; - depTracker = new MultipleServiceTracker(bundleContext, deps, new Action() { - - @Override - public void dependenciesAvailable(DependencyProvider services) { - AgentInfoDAO agentInfoDAO = services.get(AgentInfoDAO.class); - BackendInfoDAO backendInfoDAO = services.get(BackendInfoDAO.class); - - Agent agent = startAgent(agentInfoDAO, backendInfoDAO); - handler = new CustomSignalHandler(agent); - Signal.handle(new Signal(SIGINT_NAME), handler); - Signal.handle(new Signal(SIGTERM_NAME), handler); - } - - @Override - public void dependenciesUnavailable() { - if (shutdownLatch.getCount() > 0) { - // In the rare case we lose one of our deps, gracefully shutdown - logger.severe("Dependencies unexpectedly became unavailable"); - shutdown(ExitStatus.EXIT_ERROR); - } - } - - }); - depTracker.open(); - } - - static class ConfigurationCreator { - public AgentStartupConfiguration create() throws InvalidConfigurationException { - return AgentConfigsUtils.createAgentConfigs(); - } - } - - @Override - public boolean isStorageRequired() { - return false; - } - -} - diff -r 90361ef466fb -r 08726e4b6d43 agent/cli/src/main/java/com/redhat/thermostat/agent/cli/internal/locale/LocaleResources.java --- a/agent/cli/src/main/java/com/redhat/thermostat/agent/cli/internal/locale/LocaleResources.java Thu Aug 10 09:41:24 2017 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,56 +0,0 @@ -/* - * Copyright 2012-2017 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 - * . - * - * 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.agent.cli.internal.locale; - -import com.redhat.thermostat.shared.locale.Translate; - -public enum LocaleResources { - - SERVICE_FAILED_TO_START_DB, - LAUNCHER_UNAVAILABLE, - UNEXPECTED_RESULT_STORAGE, - STARTING_AGENT_FAILED, - ; - - static final String RESOURCE_BUNDLE = "com.redhat.thermostat.agent.cli.internal.strings"; - - public static Translate createLocalizer() { - return new Translate<>(RESOURCE_BUNDLE, LocaleResources.class); - } - -} - diff -r 90361ef466fb -r 08726e4b6d43 agent/cli/src/main/resources/com/redhat/thermostat/agent/cli/internal/strings.properties --- a/agent/cli/src/main/resources/com/redhat/thermostat/agent/cli/internal/strings.properties Thu Aug 10 09:41:24 2017 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,4 +0,0 @@ -SERVICE_FAILED_TO_START_DB = Service failed to start due to error starting storage. -LAUNCHER_UNAVAILABLE = Launcher is not available -UNEXPECTED_RESULT_STORAGE = Unexpected result from storage. -STARTING_AGENT_FAILED = Thermostat agent failed to start. See logs for details. diff -r 90361ef466fb -r 08726e4b6d43 agent/cli/src/test/java/com/redhat/thermostat/agent/cli/internal/ActivatorTest.java --- a/agent/cli/src/test/java/com/redhat/thermostat/agent/cli/internal/ActivatorTest.java Thu Aug 10 09:41:24 2017 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,82 +0,0 @@ -/* - * Copyright 2012-2017 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 - * . - * - * 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.agent.cli.internal; - -import static com.redhat.thermostat.testutils.Asserts.assertCommandIsRegistered; -import static org.junit.Assert.assertEquals; -import static org.mockito.Mockito.mock; - -import com.redhat.thermostat.agent.dao.AgentInfoDAO; -import com.redhat.thermostat.agent.dao.BackendInfoDAO; -import org.junit.Test; - -import com.redhat.thermostat.common.ExitStatus; -import com.redhat.thermostat.shared.config.SSLConfiguration; -import com.redhat.thermostat.storage.core.WriterID; -import com.redhat.thermostat.testutils.StubBundleContext; - -public class ActivatorTest { - - @Test - public void verifyActivatorRegistersCommands() throws Exception { - StubBundleContext bundleContext = new StubBundleContext(); - - ExitStatus exitStatus = mock(ExitStatus.class); - WriterID writerID = mock(WriterID.class); - bundleContext.registerService(WriterID.class, writerID, null); - bundleContext.registerService(ExitStatus.class, exitStatus, null); - bundleContext.registerService(SSLConfiguration.class, mock(SSLConfiguration.class), null); - bundleContext.registerService(AgentInfoDAO.class, mock(AgentInfoDAO.class), null); - bundleContext.registerService(BackendInfoDAO.class, mock(BackendInfoDAO.class), null); - - Activator activator = new Activator(); - - assertEquals(0, bundleContext.getServiceListeners().size()); - - activator.start(bundleContext); - - assertEquals(5, bundleContext.getServiceListeners().size()); - - assertCommandIsRegistered(bundleContext, "agent", AgentApplication.class); - - activator.stop(bundleContext); - - assertEquals(0, bundleContext.getServiceListeners().size()); - assertEquals(5, bundleContext.getAllServices().size()); - } -} - diff -r 90361ef466fb -r 08726e4b6d43 agent/cli/src/test/java/com/redhat/thermostat/agent/cli/internal/AgentApplicationTest.java --- a/agent/cli/src/test/java/com/redhat/thermostat/agent/cli/internal/AgentApplicationTest.java Thu Aug 10 09:41:24 2017 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,228 +0,0 @@ -/* - * Copyright 2012-2017 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 - * . - * - * 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.agent.cli.internal; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.fail; -import static org.mockito.Matchers.any; -import static org.mockito.Mockito.doThrow; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; -import static org.powermock.api.mockito.PowerMockito.whenNew; - -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.osgi.framework.Bundle; -import org.osgi.framework.BundleContext; -import org.osgi.framework.Filter; -import org.osgi.framework.FrameworkUtil; -import org.osgi.framework.InvalidSyntaxException; -import org.powermock.api.mockito.PowerMockito; -import org.powermock.core.classloader.annotations.PrepareForTest; -import org.powermock.modules.junit4.PowerMockRunner; - -import com.redhat.thermostat.agent.Agent; -import com.redhat.thermostat.agent.cli.internal.AgentApplication.ConfigurationCreator; -import com.redhat.thermostat.agent.config.AgentStartupConfiguration; -import com.redhat.thermostat.agent.dao.AgentInfoDAO; -import com.redhat.thermostat.agent.dao.BackendInfoDAO; -import com.redhat.thermostat.backend.BackendRegistry; -import com.redhat.thermostat.common.ExitStatus; -import com.redhat.thermostat.common.LaunchException; -import com.redhat.thermostat.common.Version; -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.shared.config.InvalidConfigurationException; -import com.redhat.thermostat.storage.core.WriterID; -import com.redhat.thermostat.testutils.StubBundleContext; - -@RunWith(PowerMockRunner.class) -public class AgentApplicationTest { - - private StubBundleContext context; - - private ConfigurationCreator configCreator; - private ExitStatus exitStatus; - private WriterID writerId; - - @Before - public void setUp() throws InvalidConfigurationException { - - context = new StubBundleContext(); - - AgentStartupConfiguration config = mock(AgentStartupConfiguration.class); - when(config.getDBConnectionString()).thenReturn("test string; please ignore"); - - configCreator = mock(ConfigurationCreator.class); - when(configCreator.create()).thenReturn(config); - - AgentInfoDAO agentInfoDAO = mock(AgentInfoDAO.class); - context.registerService(AgentInfoDAO.class.getName(), agentInfoDAO, null); - BackendInfoDAO backendInfoDAO = mock(BackendInfoDAO.class); - context.registerService(BackendInfoDAO.class.getName(), backendInfoDAO, null); - writerId = mock(WriterID.class); - - exitStatus = mock(ExitStatus.class); - } - - @After - public void tearDown() { - context = null; - configCreator = null; - exitStatus = null; - } - - @PrepareForTest({ FrameworkUtil.class, Agent.class }) - @Test - public void testAgentStartup() throws CommandException, InterruptedException, InvalidSyntaxException { - final AgentApplication agent = new AgentApplication(context, exitStatus, writerId, configCreator); - final CountDownLatch latch = new CountDownLatch(1); - final CommandException[] ce = new CommandException[1]; - final long timeoutMillis = 5000L; - - Bundle mockBundle = createBundle(); - PowerMockito.mockStatic(FrameworkUtil.class); - when(FrameworkUtil.getBundle(Agent.class)).thenReturn(mockBundle); - when(FrameworkUtil.createFilter(any(String.class))).thenReturn(mock(Filter.class)); - - startAgentRunThread(timeoutMillis, agent, ce, latch); - - boolean ret = latch.await(timeoutMillis, TimeUnit.MILLISECONDS); - if (ce[0] != null) { - throw ce[0]; - } - if (!ret) { - fail("Timeout expired!"); - } - } - - private Bundle createBundle() { - String qualifier = "201207241700"; - Bundle sysBundle = mock(Bundle.class); - org.osgi.framework.Version ver = org.osgi.framework.Version - .parseVersion(String.format(Version.VERSION_NUMBER_FORMAT, - 1, 2, 3) + "." + qualifier); - when(sysBundle.getVersion()).thenReturn(ver); - when(sysBundle.getBundleContext()).thenReturn(context); - return sysBundle; - } - - /* - * Having the PrepareForTest annotation on method level does not seem to - * deadlock the test, which seems to be more or less reliably reproducible - * if this annotation is at class level instead. Steps to reproduce the - * deadlock is: - * 1. Attach the PrepareForTest annotation to the class (over the test - * method) - * 2. Run the test multiple times. 5-20 times seemed sufficient for me to - * make the deadlock show up. This deadlock does not seem to happen - * otherwise (can run up to 30 times head-to-head without deadlock). - * - */ - @PrepareForTest({ AgentApplication.class }) - @SuppressWarnings("unchecked") - @Test - public void verifyBackendRegistryProblemsSetsExitStatus() throws Exception { - whenNew(BackendRegistry.class).withParameterTypes(BundleContext.class) - .withArguments(any(BundleContext.class)) - .thenThrow(InvalidSyntaxException.class); - final AgentApplication agent = new AgentApplication(context, - exitStatus, writerId, configCreator); - try { - agent.startAgent(null, null); - } catch (RuntimeException e) { - assertEquals(InvalidSyntaxException.class, e.getCause().getClass()); - } - verify(exitStatus).setExitStatus(ExitStatus.EXIT_ERROR); - } - - @PrepareForTest({ AgentApplication.class }) - @Test - public void verifyAgentLaunchExceptionSetsExitStatus() throws Exception { - whenNew(BackendRegistry.class).withParameterTypes(BundleContext.class) - .withArguments(any(BundleContext.class)) - .thenReturn(mock(BackendRegistry.class)); - Agent mockAgent = mock(Agent.class); - whenNew(Agent.class).withParameterTypes(BackendRegistry.class, - AgentStartupConfiguration.class, - AgentInfoDAO.class, BackendInfoDAO.class, WriterID.class).withArguments( - any(BackendRegistry.class), - any(AgentStartupConfiguration.class), - any(AgentInfoDAO.class), any(BackendInfoDAO.class), - any(WriterID.class)).thenReturn(mockAgent); - doThrow(LaunchException.class).when(mockAgent).start(); - final AgentApplication agent = new AgentApplication(context, - exitStatus, writerId, configCreator); - try { - agent.startAgent(null, null); - } catch (RuntimeException e) { - fail("Should not have thrown RuntimeException"); - } - verify(exitStatus).setExitStatus(ExitStatus.EXIT_ERROR); - } - - private void startAgentRunThread(final long timoutMillis, final AgentApplication agent, final CommandException[] ce, final CountDownLatch latch) throws InterruptedException { - Arguments args = mock(Arguments.class); - final CommandContext commandContext = mock(CommandContext.class); - when(commandContext.getArguments()).thenReturn(args); - - // Run agent in a new thread so we can timeout on failure - Thread t = new Thread(new Runnable() { - - @Override - public void run() { - try { - latch.countDown(); - agent.run(commandContext); - } catch (CommandException e) { - ce[0] = e; - } - } - }); - - t.start(); - } - -} - diff -r 90361ef466fb -r 08726e4b6d43 agent/cli/src/test/java/com/redhat/thermostat/agent/cli/internal/locale/TranslateTest.java --- a/agent/cli/src/test/java/com/redhat/thermostat/agent/cli/internal/locale/TranslateTest.java Thu Aug 10 09:41:24 2017 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,78 +0,0 @@ -/* - * Copyright 2012-2017 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 - * . - * - * 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.agent.cli.internal.locale; - -import java.io.IOException; -import java.util.Locale; -import java.util.Properties; - -import org.junit.After; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; - -public class TranslateTest { - - private Locale lang; - - @Before - public void setUp() { - this.lang = Locale.getDefault(); - Locale.setDefault(Locale.US); - } - - @After - public void tearDown() { - Locale.setDefault(lang); - } - - @Test - public void testLocalizedStringsArePresent() throws IOException { - - String stringsResource = "/" + LocaleResources.RESOURCE_BUNDLE.replace(".", "/") + ".properties"; - - Properties props = new Properties(); - props.load(getClass().getResourceAsStream(stringsResource)); - - Assert.assertEquals(LocaleResources.values().length, props.values().size()); - for (LocaleResources resource : LocaleResources.values()) { - Assert.assertTrue("missing property from resource bound file: " + resource, - props.containsKey(resource.name())); - } - } -} - diff -r 90361ef466fb -r 08726e4b6d43 agent/core/pom.xml --- a/agent/core/pom.xml Thu Aug 10 09:41:24 2017 -0400 +++ b/agent/core/pom.xml Fri Aug 11 14:03:38 2017 -0400 @@ -62,6 +62,16 @@ test + org.powermock + powermock-api-mockito + test + + + org.powermock + powermock-module-junit4 + test + + com.redhat.thermostat thermostat-common-test ${project.version} @@ -117,7 +127,6 @@ Red Hat, Inc. com.redhat.thermostat.agent.core - com.redhat.thermostat.agent.internal.Activator com.redhat.thermostat.agent, com.redhat.thermostat.agent.http, diff -r 90361ef466fb -r 08726e4b6d43 agent/core/src/main/java/com/redhat/thermostat/agent/http/HttpRequestService.java --- a/agent/core/src/main/java/com/redhat/thermostat/agent/http/HttpRequestService.java Thu Aug 10 09:41:24 2017 -0400 +++ b/agent/core/src/main/java/com/redhat/thermostat/agent/http/HttpRequestService.java Fri Aug 11 14:03:38 2017 -0400 @@ -61,6 +61,7 @@ import com.redhat.thermostat.agent.config.AgentStartupConfiguration; import com.redhat.thermostat.agent.http.internal.keycloak.KeycloakAccessToken; import com.redhat.thermostat.common.utils.LoggingUtils; +import com.redhat.thermostat.shared.config.CommonPaths; import com.redhat.thermostat.shared.config.SSLConfiguration; @Component @@ -73,6 +74,7 @@ private static final String KEYCLOAK_CONTENT_TYPE = "application/x-www-form-urlencoded"; private final HttpClientCreator httpClientCreator; + private final ConfigCreator configCreator; private Gson gson = new GsonBuilder().create(); private HttpClientFacade client; private AgentStartupConfiguration agentStartupConfiguration; @@ -80,19 +82,22 @@ private KeycloakAccessToken keycloakAccessToken; @Reference private SSLConfiguration sslConfig; + @Reference + private CommonPaths commonPaths; public HttpRequestService() { - this(new HttpClientCreator(), AgentConfigsUtils.createAgentConfigs()); + this(new HttpClientCreator(), new ConfigCreator()); } - HttpRequestService(HttpClientCreator clientCreator, AgentStartupConfiguration agentStartupConfiguration) { + HttpRequestService(HttpClientCreator clientCreator, ConfigCreator configCreator) { this.httpClientCreator = clientCreator; - this.agentStartupConfiguration = agentStartupConfiguration; + this.configCreator = configCreator; } @Activate public void activate() { try { + agentStartupConfiguration = configCreator.create(commonPaths); client = httpClientCreator.create(sslConfig); client.start(); logger.log(Level.FINE, "HttpRequestService activated"); @@ -205,7 +210,12 @@ return "grant_type=refresh_token&client_id=" + agentStartupConfiguration.getKeycloakClient() + "&refresh_token=" + keycloakAccessToken.getRefreshToken(); } - + + // Package-private for testing + void setConfiguration(AgentStartupConfiguration configuration) { + this.agentStartupConfiguration = configuration; + } + static class HttpClientCreator { HttpClientFacade create(SSLConfiguration config) { @@ -213,6 +223,14 @@ } } + + static class ConfigCreator { + AgentStartupConfiguration create(CommonPaths commonPaths) { + AgentConfigsUtils.setConfigFiles(commonPaths.getSystemAgentConfigurationFile(), + commonPaths.getUserAgentConfigurationFile()); + return AgentConfigsUtils.createAgentConfigs(); + } + } @SuppressWarnings("serial") public static class RequestFailedException extends Exception { diff -r 90361ef466fb -r 08726e4b6d43 agent/core/src/main/java/com/redhat/thermostat/agent/internal/Activator.java --- a/agent/core/src/main/java/com/redhat/thermostat/agent/internal/Activator.java Thu Aug 10 09:41:24 2017 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,122 +0,0 @@ -/* - * Copyright 2012-2017 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 - * . - * - * 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.agent.internal; - -import java.io.File; -import java.util.logging.Level; -import java.util.logging.Logger; - -import org.osgi.framework.BundleActivator; -import org.osgi.framework.BundleContext; -import org.osgi.framework.ServiceReference; -import org.osgi.util.tracker.ServiceTracker; -import org.osgi.util.tracker.ServiceTrackerCustomizer; - -import com.redhat.thermostat.agent.config.AgentConfigsUtils; -import com.redhat.thermostat.agent.dao.AgentInfoDAO; -import com.redhat.thermostat.agent.dao.BackendInfoDAO; -import com.redhat.thermostat.common.utils.LoggingUtils; -import com.redhat.thermostat.shared.config.CommonPaths; -import com.redhat.thermostat.shared.config.InvalidConfigurationException; - -public class Activator implements BundleActivator { - - private static final Logger logger = LoggingUtils.getLogger(Activator.class); - - private final AgentConfigSetter configSetter; - private ServiceTracker commonPathsTracker; - - public Activator() { - this(new AgentConfigSetter()); - } - - Activator(AgentConfigSetter configSetter) { - this.configSetter = configSetter; - } - - @Override - public void start(final BundleContext context) throws Exception { - AgentInfoDAO agentInfoDAO = new AgentInfoDAOImpl(); - context.registerService(AgentInfoDAO.class, agentInfoDAO, null); - - BackendInfoDAO backendInfoDAO = new BackendInfoDAOImpl(); - context.registerService(BackendInfoDAO.class, backendInfoDAO, null); - - // Track common paths separately and register storage credentials quickly - // We need to do this since otherwise no storage credentials will be - // available by the time they're used in DbService - commonPathsTracker = new ServiceTracker<>(context, CommonPaths.class, new ServiceTrackerCustomizer() { - - @Override - public CommonPaths addingService(ServiceReference ref) { - CommonPaths paths = context.getService(ref); - try { - configSetter.setConfigFiles(paths.getSystemAgentConfigurationFile(), paths.getUserAgentConfigurationFile()); - } catch (InvalidConfigurationException e) { - logger.log(Level.SEVERE, "Failed to start agent services", e); - } - return paths; - } - - @Override - public void modifiedService(ServiceReference arg0, CommonPaths arg1) { - // nothing - } - - @Override - public void removedService(ServiceReference ref, CommonPaths service) { - context.ungetService(ref); - } - }); - - commonPathsTracker.open(); - } - - @Override - public void stop(BundleContext context) throws Exception { - commonPathsTracker.close(); - } - - // For testing purposes - static class AgentConfigSetter { - void setConfigFiles(File systemConfigFile, File userConfigFile) { - AgentConfigsUtils.setConfigFiles(systemConfigFile, userConfigFile); - } - } - -} - diff -r 90361ef466fb -r 08726e4b6d43 agent/core/src/main/java/com/redhat/thermostat/agent/internal/AgentApplication.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/agent/core/src/main/java/com/redhat/thermostat/agent/internal/AgentApplication.java Fri Aug 11 14:03:38 2017 -0400 @@ -0,0 +1,282 @@ +/* + * Copyright 2012-2017 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 + * . + * + * 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.agent.internal; + +import java.util.concurrent.CountDownLatch; +import java.util.logging.Level; +import java.util.logging.Logger; + +import com.redhat.thermostat.agent.dao.AgentInfoDAO; +import com.redhat.thermostat.agent.dao.BackendInfoDAO; + +import org.apache.felix.scr.annotations.Activate; +import org.apache.felix.scr.annotations.Component; +import org.apache.felix.scr.annotations.Deactivate; +import org.apache.felix.scr.annotations.Reference; +import org.apache.felix.scr.annotations.Service; +import org.osgi.framework.BundleContext; + +import com.redhat.thermostat.agent.Agent; +import com.redhat.thermostat.agent.config.AgentConfigsUtils; +import com.redhat.thermostat.agent.config.AgentOptionParser; +import com.redhat.thermostat.agent.config.AgentStartupConfiguration; +import com.redhat.thermostat.backend.BackendRegistry; +import com.redhat.thermostat.backend.BackendService; +import com.redhat.thermostat.common.ExitStatus; +import com.redhat.thermostat.common.LaunchException; +import com.redhat.thermostat.common.cli.AbstractStateNotifyingCommand; +import com.redhat.thermostat.common.cli.Arguments; +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.CommandRegistry; +import com.redhat.thermostat.common.cli.CommandRegistryImpl; +import com.redhat.thermostat.common.tools.ApplicationState; +import com.redhat.thermostat.common.utils.LoggingUtils; +import com.redhat.thermostat.shared.config.InvalidConfigurationException; +import com.redhat.thermostat.storage.core.WriterID; +import sun.misc.Signal; +import sun.misc.SignalHandler; + +@Component(immediate = true) +@Service(value = Command.class) +@SuppressWarnings("restriction") +public final class AgentApplication extends AbstractStateNotifyingCommand { + + /** + * Property for turning on verbose mode. This is there so as to be able to + * run integration tests independent of log levels. + */ + private static final String VERBOSE_MODE_PROPERTY = "thermostat.agent.verbose"; + // Messages printed in verbose mode. Integration tests use this. Be careful + // when you change those! + private static final String VERBOSE_MODE_AGENT_STOPPED_MSG = "Agent stopped."; + private static final String VERBOSE_MODE_AGENT_STARTED_MSG = "Agent started."; + + private static final String SIGINT_NAME = "INT"; + private static final String SIGTERM_NAME = "TERM"; + + private static final Logger logger = LoggingUtils.getLogger(AgentApplication.class); + + private final ConfigurationCreator configurationCreator; + + private AgentStartupConfiguration configuration; + private AgentOptionParser parser; + + private BundleContext context; + + @Reference(bind = "bindExitStatus") + private ExitStatus exitStatus; + @Reference(bind = "bindWriterId") + private WriterID writerId; + @Reference + private AgentInfoDAO agentInfoDAO; + @Reference + private BackendInfoDAO backendInfoDAO; + + private CommandRegistry reg; + + private CountDownLatch shutdownLatch; + + private CustomSignalHandler handler; + + private AgentApplication instance; + + public AgentApplication() { + this(new ConfigurationCreator()); + } + + AgentApplication(ConfigurationCreator configurationCreator) { + this.configurationCreator = configurationCreator; + } + + @Activate + public void activate(BundleContext context) { + this.context = context; + reg = new CommandRegistryImpl(context); + instance = this; + reg.registerCommand("agent", instance); + } + + @Deactivate + public void deactivate(BundleContext context) { + if (instance != null) { + // Bundle may be shut down *before* deps become available and + // app is set. + instance.shutdown(ExitStatus.EXIT_SUCCESS); + } + reg.unregisterCommands(); + } + + private void parseArguments(Arguments args) throws InvalidConfigurationException { + parser = new AgentOptionParser(configuration, args); + parser.parse(); + } + + private void runAgent(CommandContext ctx) throws CommandException { + long startTime = System.currentTimeMillis(); + configuration.setStartTime(startTime); + + shutdownLatch = new CountDownLatch(1); + Agent agent = startAgent(agentInfoDAO, backendInfoDAO); + handler = new CustomSignalHandler(agent); + Signal.handle(new Signal(SIGINT_NAME), handler); + Signal.handle(new Signal(SIGTERM_NAME), handler); + + try { + // Wait for either SIGINT or SIGTERM + shutdownLatch.await(); + logger.fine("terminating agent cmd"); + } catch (InterruptedException e) { + // Ensure proper shutdown if interrupted + handler.handle(new Signal(SIGINT_NAME)); + return; + } + } + + @Override + public void run(CommandContext ctx) throws CommandException { + configuration = configurationCreator.create(); + + parseArguments(ctx.getArguments()); + if (!parser.isHelp()) { + runAgent(ctx); + } + } + + public void shutdown(int shutDownStatus) { + // Exit application + if (shutdownLatch != null) { + shutdownLatch.countDown(); + } + this.exitStatus.setExitStatus(shutDownStatus); + if (shutDownStatus == ExitStatus.EXIT_SUCCESS) { + getNotifier().fireAction(ApplicationState.STOP); + } else { + getNotifier().fireAction(ApplicationState.FAIL); + } + } + + private class CustomSignalHandler implements SignalHandler { + + private Agent agent; + + public CustomSignalHandler(Agent agent) { + this.agent = agent; + } + + @Override + public void handle(Signal arg0) { + try { + agent.stop(); + } catch (Exception ex) { + // We don't want any exception to hold back the signal handler, otherwise + // there will be no way to actually stop Thermostat. + ex.printStackTrace(); + } + logger.fine("Agent stopped."); + // Hook for integration tests. Print a well known message to stdout + // if verbose mode is turned on via the system property. + if (Boolean.getBoolean(VERBOSE_MODE_PROPERTY)) { + System.out.println(VERBOSE_MODE_AGENT_STOPPED_MSG); + } + shutdown(ExitStatus.EXIT_SUCCESS); + } + + } + + Agent startAgent(AgentInfoDAO agentInfoDAO, BackendInfoDAO backendInfoDAO) { + BackendRegistry backendRegistry = null; + try { + backendRegistry = new BackendRegistry(context); + + } catch (Exception e) { + logger.log(Level.SEVERE, "Could not get BackendRegistry instance.", e); + shutdown(ExitStatus.EXIT_ERROR); + // Since this would throw NPE's down the line if we continue in this + // method, let's fail right and early :) + throw new RuntimeException(e); + } + + final Agent agent = new Agent(backendRegistry, configuration, agentInfoDAO, backendInfoDAO, writerId); + try { + logger.fine("Starting agent."); + agent.start(); + + context.registerService(BackendService.class, new BackendService(), null); + + } catch (LaunchException le) { + logger.log(Level.SEVERE, + "Agent could not start, probably because a configured backend could not be activated.", + le); + shutdown(ExitStatus.EXIT_ERROR); + } + logger.fine("Agent started."); + // Hook for integration tests. Print a well known message to stdout + // if verbose mode is turned on via the system property. + if (Boolean.getBoolean(VERBOSE_MODE_PROPERTY)) { + System.out.println(VERBOSE_MODE_AGENT_STARTED_MSG); + } + + logger.info("Agent id: " + agent.getId()); + getNotifier().fireAction(ApplicationState.START, agent.getId()); + return agent; + } + + static class ConfigurationCreator { + public AgentStartupConfiguration create() throws InvalidConfigurationException { + return AgentConfigsUtils.createAgentConfigs(); + } + } + + @Override + public boolean isStorageRequired() { + return false; + } + + // DS runtime bind methods + protected void bindExitStatus(ExitStatus status) { + this.exitStatus = status; + } + + protected void bindWriterId(WriterID id) { + this.writerId = id; + } + +} + + diff -r 90361ef466fb -r 08726e4b6d43 agent/core/src/main/java/com/redhat/thermostat/agent/internal/AgentInfoDAOImpl.java --- a/agent/core/src/main/java/com/redhat/thermostat/agent/internal/AgentInfoDAOImpl.java Thu Aug 10 09:41:24 2017 -0400 +++ b/agent/core/src/main/java/com/redhat/thermostat/agent/internal/AgentInfoDAOImpl.java Fri Aug 11 14:03:38 2017 -0400 @@ -49,6 +49,9 @@ import com.redhat.thermostat.agent.dao.AgentInfoDAO; import com.redhat.thermostat.agent.internal.AgentInformationTypeAdapter.AgentInformationUpdateTypeAdapter; + +import org.apache.felix.scr.annotations.Component; +import org.apache.felix.scr.annotations.Service; import org.eclipse.jetty.client.HttpClient; import org.eclipse.jetty.client.HttpContentResponse; import org.eclipse.jetty.client.HttpRequest; @@ -62,6 +65,8 @@ import com.redhat.thermostat.storage.core.AgentId; import com.redhat.thermostat.storage.model.AgentInformation; +@Component +@Service(value = AgentInfoDAO.class) public class AgentInfoDAOImpl implements AgentInfoDAO { private static final Logger logger = LoggingUtils.getLogger(AgentInfoDAOImpl.class); diff -r 90361ef466fb -r 08726e4b6d43 agent/core/src/main/java/com/redhat/thermostat/agent/internal/BackendInfoDAOImpl.java --- a/agent/core/src/main/java/com/redhat/thermostat/agent/internal/BackendInfoDAOImpl.java Thu Aug 10 09:41:24 2017 -0400 +++ b/agent/core/src/main/java/com/redhat/thermostat/agent/internal/BackendInfoDAOImpl.java Fri Aug 11 14:03:38 2017 -0400 @@ -48,6 +48,9 @@ import java.util.logging.Logger; import com.redhat.thermostat.agent.dao.BackendInfoDAO; + +import org.apache.felix.scr.annotations.Component; +import org.apache.felix.scr.annotations.Service; import org.eclipse.jetty.client.HttpClient; import org.eclipse.jetty.client.HttpContentResponse; import org.eclipse.jetty.client.HttpRequest; @@ -61,6 +64,8 @@ import com.redhat.thermostat.storage.model.BackendInformation; +@Component +@Service(value = BackendInfoDAO.class) public class BackendInfoDAOImpl implements BackendInfoDAO { private static final Logger logger = LoggingUtils.getLogger(BackendInfoDAOImpl.class); diff -r 90361ef466fb -r 08726e4b6d43 agent/core/src/main/java/com/redhat/thermostat/agent/internal/locale/LocaleResources.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/agent/core/src/main/java/com/redhat/thermostat/agent/internal/locale/LocaleResources.java Fri Aug 11 14:03:38 2017 -0400 @@ -0,0 +1,56 @@ +/* + * Copyright 2012-2017 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 + * . + * + * 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.agent.internal.locale; + +import com.redhat.thermostat.shared.locale.Translate; + +public enum LocaleResources { + + SERVICE_FAILED_TO_START_DB, + LAUNCHER_UNAVAILABLE, + UNEXPECTED_RESULT_STORAGE, + STARTING_AGENT_FAILED, + ; + + static final String RESOURCE_BUNDLE = "com.redhat.thermostat.agent.internal.strings"; + + public static Translate createLocalizer() { + return new Translate<>(RESOURCE_BUNDLE, LocaleResources.class); + } + +} + diff -r 90361ef466fb -r 08726e4b6d43 agent/core/src/main/resources/com/redhat/thermostat/agent/internal/strings.properties --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/agent/core/src/main/resources/com/redhat/thermostat/agent/internal/strings.properties Fri Aug 11 14:03:38 2017 -0400 @@ -0,0 +1,4 @@ +SERVICE_FAILED_TO_START_DB = Service failed to start due to error starting storage. +LAUNCHER_UNAVAILABLE = Launcher is not available +UNEXPECTED_RESULT_STORAGE = Unexpected result from storage. +STARTING_AGENT_FAILED = Thermostat agent failed to start. See logs for details. diff -r 90361ef466fb -r 08726e4b6d43 agent/core/src/test/java/com/redhat/thermostat/agent/http/HttpRequestServiceTest.java --- a/agent/core/src/test/java/com/redhat/thermostat/agent/http/HttpRequestServiceTest.java Thu Aug 10 09:41:24 2017 -0400 +++ b/agent/core/src/test/java/com/redhat/thermostat/agent/http/HttpRequestServiceTest.java Fri Aug 11 14:03:38 2017 -0400 @@ -63,8 +63,10 @@ import org.mockito.ArgumentCaptor; import com.redhat.thermostat.agent.config.AgentStartupConfiguration; +import com.redhat.thermostat.agent.http.HttpRequestService.ConfigCreator; import com.redhat.thermostat.agent.http.HttpRequestService.HttpClientCreator; import com.redhat.thermostat.agent.http.HttpRequestService.RequestFailedException; +import com.redhat.thermostat.shared.config.CommonPaths; import com.redhat.thermostat.shared.config.SSLConfiguration; import org.eclipse.jetty.http.HttpMethod; @@ -75,6 +77,7 @@ private static final String keycloakUrl = "http://127.0.0.1:31000/keycloak"; private HttpClientCreator clientCreator; + private ConfigCreator configCreator; private HttpClientFacade client; private Request httpRequest; @@ -88,6 +91,7 @@ when(httpRequest.send()).thenReturn(response); clientCreator = mock(HttpClientCreator.class); when(clientCreator.create(any(SSLConfiguration.class))).thenReturn(client); + configCreator = mock(ConfigCreator.class); } @Test @@ -176,7 +180,8 @@ } private HttpRequestService createAndActivateRequestService(AgentStartupConfiguration configuration) throws Exception { - HttpRequestService service = new HttpRequestService(clientCreator, configuration); + when(configCreator.create(any(CommonPaths.class))).thenReturn(configuration); + HttpRequestService service = new HttpRequestService(clientCreator, configCreator); service.activate(); verify(client).start(); return service; @@ -189,7 +194,9 @@ HttpClientFacade getClient = setupHttpClient(creator, getContent); AgentStartupConfiguration configuration = createNoKeycloakConfig(); - HttpRequestService service = new HttpRequestService(creator, configuration); + ConfigCreator configCreator = mock(ConfigCreator.class); + when(configCreator.create(any(CommonPaths.class))).thenReturn(configuration); + HttpRequestService service = new HttpRequestService(creator, configCreator); service.activate(); String content = service.sendHttpRequest(null, GET_URI, com.redhat.thermostat.agent.http.HttpRequestService.Method.GET); verify(getClient).newRequest(GET_URI); @@ -203,7 +210,9 @@ HttpClientFacade getClient = setupHttpClient(creator, getContent); AgentStartupConfiguration configuration = createNoKeycloakConfig(); - HttpRequestService service = new HttpRequestService(creator, configuration); + ConfigCreator configCreator = mock(ConfigCreator.class); + when(configCreator.create(any(CommonPaths.class))).thenReturn(configuration); + HttpRequestService service = new HttpRequestService(creator, configCreator); service.activate(); // Add extra slashes to URI diff -r 90361ef466fb -r 08726e4b6d43 agent/core/src/test/java/com/redhat/thermostat/agent/internal/ActivatorTest.java --- a/agent/core/src/test/java/com/redhat/thermostat/agent/internal/ActivatorTest.java Thu Aug 10 09:41:24 2017 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,83 +0,0 @@ -/* - * Copyright 2012-2017 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 - * . - * - * 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.agent.internal; - -import static org.junit.Assert.assertTrue; -import static org.mockito.Mockito.*; - -import java.io.File; - -import org.junit.Test; - -import com.redhat.thermostat.agent.dao.AgentInfoDAO; -import com.redhat.thermostat.agent.dao.BackendInfoDAO; -import com.redhat.thermostat.agent.internal.Activator.AgentConfigSetter; -import com.redhat.thermostat.shared.config.CommonPaths; -import com.redhat.thermostat.testutils.StubBundleContext; - -public class ActivatorTest { - - @Test - public void verifyServiceIsRegistered() throws Exception { - StubBundleContext context = new StubBundleContext(); - Activator activator = new Activator(); - activator.start(context); - - assertTrue(context.isServiceRegistered(AgentInfoDAO.class.getName(), AgentInfoDAOImpl.class)); - assertTrue(context.isServiceRegistered(BackendInfoDAO.class.getName(), BackendInfoDAOImpl.class)); - } - - @Test - public void verifyAgentConfig() throws Exception { - StubBundleContext context = new StubBundleContext(); - - CommonPaths paths = mock(CommonPaths.class); - File sysPropFile = mock(File.class); - when(paths.getSystemAgentConfigurationFile()).thenReturn(sysPropFile); - File userPropFile = mock(File.class); - when(paths.getUserAgentConfigurationFile()).thenReturn(userPropFile); - context.registerService(CommonPaths.class.getName(), paths, null); - - AgentConfigSetter configSetter = mock(AgentConfigSetter.class); - Activator activator = new Activator(configSetter); - activator.start(context); - - verify(configSetter).setConfigFiles(sysPropFile, userPropFile); - } - -} - diff -r 90361ef466fb -r 08726e4b6d43 agent/core/src/test/java/com/redhat/thermostat/agent/internal/AgentApplicationTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/agent/core/src/test/java/com/redhat/thermostat/agent/internal/AgentApplicationTest.java Fri Aug 11 14:03:38 2017 -0400 @@ -0,0 +1,245 @@ +/* + * Copyright 2012-2017 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 + * . + * + * 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.agent.internal; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.osgi.framework.Bundle; +import org.osgi.framework.BundleContext; +import org.osgi.framework.Filter; +import org.osgi.framework.FrameworkUtil; +import org.osgi.framework.InvalidSyntaxException; +import org.powermock.api.mockito.PowerMockito; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; + +import static com.redhat.thermostat.testutils.Asserts.assertCommandIsRegistered; + +import com.redhat.thermostat.agent.Agent; +import com.redhat.thermostat.agent.internal.AgentApplication.ConfigurationCreator; +import com.redhat.thermostat.agent.config.AgentStartupConfiguration; +import com.redhat.thermostat.agent.dao.AgentInfoDAO; +import com.redhat.thermostat.agent.dao.BackendInfoDAO; +import com.redhat.thermostat.backend.BackendRegistry; +import com.redhat.thermostat.common.ExitStatus; +import com.redhat.thermostat.common.LaunchException; +import com.redhat.thermostat.common.Version; +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.shared.config.InvalidConfigurationException; +import com.redhat.thermostat.storage.core.WriterID; +import com.redhat.thermostat.testutils.StubBundleContext; + +@RunWith(PowerMockRunner.class) +public class AgentApplicationTest { + + private StubBundleContext context; + + private ConfigurationCreator configCreator; + private ExitStatus exitStatus; + private WriterID writerId; + + @Before + public void setUp() throws InvalidConfigurationException { + + context = new StubBundleContext(); + + AgentStartupConfiguration config = mock(AgentStartupConfiguration.class); + when(config.getDBConnectionString()).thenReturn("test string; please ignore"); + + configCreator = mock(ConfigurationCreator.class); + when(configCreator.create()).thenReturn(config); + + AgentInfoDAO agentInfoDAO = mock(AgentInfoDAO.class); + context.registerService(AgentInfoDAO.class.getName(), agentInfoDAO, null); + BackendInfoDAO backendInfoDAO = mock(BackendInfoDAO.class); + context.registerService(BackendInfoDAO.class.getName(), backendInfoDAO, null); + writerId = mock(WriterID.class); + + exitStatus = mock(ExitStatus.class); + } + + @After + public void tearDown() { + context = null; + configCreator = null; + exitStatus = null; + } + + @PrepareForTest({ FrameworkUtil.class, Agent.class }) + @Test + public void testAgentStartup() throws CommandException, InterruptedException, InvalidSyntaxException { + final AgentApplication agent = new AgentApplication(configCreator); + agent.bindExitStatus(exitStatus); + agent.bindWriterId(writerId); + agent.activate(context); + final CountDownLatch latch = new CountDownLatch(1); + final CommandException[] ce = new CommandException[1]; + final long timeoutMillis = 5000L; + + Bundle mockBundle = createBundle(); + PowerMockito.mockStatic(FrameworkUtil.class); + when(FrameworkUtil.getBundle(Agent.class)).thenReturn(mockBundle); + when(FrameworkUtil.createFilter(any(String.class))).thenReturn(mock(Filter.class)); + + startAgentRunThread(timeoutMillis, agent, ce, latch); + + boolean ret = latch.await(timeoutMillis, TimeUnit.MILLISECONDS); + if (ce[0] != null) { + throw ce[0]; + } + if (!ret) { + fail("Timeout expired!"); + } + } + + private Bundle createBundle() { + String qualifier = "201207241700"; + Bundle sysBundle = mock(Bundle.class); + org.osgi.framework.Version ver = org.osgi.framework.Version + .parseVersion(String.format(Version.VERSION_NUMBER_FORMAT, + 1, 2, 3) + "." + qualifier); + when(sysBundle.getVersion()).thenReturn(ver); + when(sysBundle.getBundleContext()).thenReturn(context); + return sysBundle; + } + + /* + * Having the PrepareForTest annotation on method level does not seem to + * deadlock the test, which seems to be more or less reliably reproducible + * if this annotation is at class level instead. Steps to reproduce the + * deadlock is: + * 1. Attach the PrepareForTest annotation to the class (over the test + * method) + * 2. Run the test multiple times. 5-20 times seemed sufficient for me to + * make the deadlock show up. This deadlock does not seem to happen + * otherwise (can run up to 30 times head-to-head without deadlock). + * + */ + @PrepareForTest({ AgentApplication.class }) + @SuppressWarnings("unchecked") + @Test + public void verifyBackendRegistryProblemsSetsExitStatus() throws Exception { + PowerMockito.whenNew(BackendRegistry.class).withParameterTypes(BundleContext.class) + .withArguments(any(BundleContext.class)) + .thenThrow(InvalidSyntaxException.class); + final AgentApplication agent = new AgentApplication(configCreator); + agent.bindExitStatus(exitStatus); + agent.bindWriterId(writerId); + agent.activate(context); + try { + agent.startAgent(null, null); + } catch (RuntimeException e) { + assertEquals(InvalidSyntaxException.class, e.getCause().getClass()); + } + verify(exitStatus).setExitStatus(ExitStatus.EXIT_ERROR); + } + + @PrepareForTest({ AgentApplication.class }) + @Test + public void verifyAgentLaunchExceptionSetsExitStatus() throws Exception { + PowerMockito.whenNew(BackendRegistry.class).withParameterTypes(BundleContext.class) + .withArguments(any(BundleContext.class)) + .thenReturn(mock(BackendRegistry.class)); + Agent mockAgent = mock(Agent.class); + PowerMockito.whenNew(Agent.class).withParameterTypes(BackendRegistry.class, + AgentStartupConfiguration.class, + AgentInfoDAO.class, BackendInfoDAO.class, WriterID.class).withArguments( + any(BackendRegistry.class), + any(AgentStartupConfiguration.class), + any(AgentInfoDAO.class), any(BackendInfoDAO.class), + any(WriterID.class)).thenReturn(mockAgent); + doThrow(LaunchException.class).when(mockAgent).start(); + final AgentApplication agent = new AgentApplication(configCreator); + agent.bindExitStatus(exitStatus); + agent.bindWriterId(writerId); + agent.activate(context); + try { + agent.startAgent(null, null); + } catch (RuntimeException e) { + fail("Should not have thrown RuntimeException"); + } + verify(exitStatus).setExitStatus(ExitStatus.EXIT_ERROR); + } + + private void startAgentRunThread(final long timoutMillis, final AgentApplication agent, final CommandException[] ce, final CountDownLatch latch) throws InterruptedException { + Arguments args = mock(Arguments.class); + final CommandContext commandContext = mock(CommandContext.class); + when(commandContext.getArguments()).thenReturn(args); + + // Run agent in a new thread so we can timeout on failure + Thread t = new Thread(new Runnable() { + + @Override + public void run() { + try { + latch.countDown(); + agent.run(commandContext); + } catch (CommandException e) { + ce[0] = e; + } + } + }); + + t.start(); + } + + @PrepareForTest({ AgentApplication.class }) + @Test + public void verifyAgentCommandIsRegistered() { + final AgentApplication agent = new AgentApplication(configCreator); + agent.bindExitStatus(exitStatus); + agent.bindWriterId(writerId); + agent.activate(context); + assertCommandIsRegistered(context, "agent", AgentApplication.class); + } + +} \ No newline at end of file diff -r 90361ef466fb -r 08726e4b6d43 agent/core/src/test/java/com/redhat/thermostat/agent/internal/locale/TranslateTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/agent/core/src/test/java/com/redhat/thermostat/agent/internal/locale/TranslateTest.java Fri Aug 11 14:03:38 2017 -0400 @@ -0,0 +1,78 @@ +/* + * Copyright 2012-2017 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 + * . + * + * 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.agent.internal.locale; + +import java.io.IOException; +import java.util.Locale; +import java.util.Properties; + +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +public class TranslateTest { + + private Locale lang; + + @Before + public void setUp() { + this.lang = Locale.getDefault(); + Locale.setDefault(Locale.US); + } + + @After + public void tearDown() { + Locale.setDefault(lang); + } + + @Test + public void testLocalizedStringsArePresent() throws IOException { + + String stringsResource = "/" + LocaleResources.RESOURCE_BUNDLE.replace(".", "/") + ".properties"; + + Properties props = new Properties(); + props.load(getClass().getResourceAsStream(stringsResource)); + + Assert.assertEquals(LocaleResources.values().length, props.values().size()); + for (LocaleResources resource : LocaleResources.values()) { + Assert.assertTrue("missing property from resource bound file: " + resource, + props.containsKey(resource.name())); + } + } +} + diff -r 90361ef466fb -r 08726e4b6d43 agent/pom.xml --- a/agent/pom.xml Thu Aug 10 09:41:24 2017 -0400 +++ b/agent/pom.xml Fri Aug 11 14:03:38 2017 -0400 @@ -59,7 +59,6 @@ - cli core ipc diff -r 90361ef466fb -r 08726e4b6d43 distribution/assembly/core-assembly-macosx.xml --- a/distribution/assembly/core-assembly-macosx.xml Thu Aug 10 09:41:24 2017 -0400 +++ b/distribution/assembly/core-assembly-macosx.xml Fri Aug 11 14:03:38 2017 -0400 @@ -52,7 +52,6 @@ com.redhat.thermostat:thermostat-main com.redhat.thermostat:thermostat-launcher com.redhat.thermostat:thermostat-agent-core - com.redhat.thermostat:thermostat-agent-cli com.redhat.thermostat:thermostat-agent-command com.redhat.thermostat:thermostat-agent-command-server com.redhat.thermostat:thermostat-agent-proxy-server diff -r 90361ef466fb -r 08726e4b6d43 distribution/assembly/core-assembly-windows.xml --- a/distribution/assembly/core-assembly-windows.xml Thu Aug 10 09:41:24 2017 -0400 +++ b/distribution/assembly/core-assembly-windows.xml Fri Aug 11 14:03:38 2017 -0400 @@ -52,7 +52,6 @@ com.redhat.thermostat:thermostat-main com.redhat.thermostat:thermostat-launcher com.redhat.thermostat:thermostat-agent-core - com.redhat.thermostat:thermostat-agent-cli com.redhat.thermostat:thermostat-agent-command com.redhat.thermostat:thermostat-agent-command-server com.redhat.thermostat:thermostat-agent-proxy-server diff -r 90361ef466fb -r 08726e4b6d43 distribution/assembly/core-assembly.xml --- a/distribution/assembly/core-assembly.xml Thu Aug 10 09:41:24 2017 -0400 +++ b/distribution/assembly/core-assembly.xml Fri Aug 11 14:03:38 2017 -0400 @@ -52,7 +52,6 @@ com.redhat.thermostat:thermostat-main com.redhat.thermostat:thermostat-launcher com.redhat.thermostat:thermostat-agent-core - com.redhat.thermostat:thermostat-agent-cli com.redhat.thermostat:thermostat-agent-proxy-server com.redhat.thermostat:thermostat-agent-ipc-tcpsocket-server com.redhat.thermostat:thermostat-agent-ipc-tcpsocket-client diff -r 90361ef466fb -r 08726e4b6d43 distribution/config/commands/agent.properties --- a/distribution/config/commands/agent.properties Thu Aug 10 09:41:24 2017 -0400 +++ b/distribution/config/commands/agent.properties Fri Aug 11 14:03:38 2017 -0400 @@ -1,4 +1,4 @@ -bundles = com.redhat.thermostat.agent.cli=@project.version@, \ +bundles = com.redhat.thermostat.agent.core=@project.version@, \ com.redhat.thermostat.agent.ipc.tcpsocket.server=@project.version@, \ com.redhat.thermostat.process=@project.version@ \ @agent_extra_bundles@ diff -r 90361ef466fb -r 08726e4b6d43 distribution/pom.xml --- a/distribution/pom.xml Thu Aug 10 09:41:24 2017 -0400 +++ b/distribution/pom.xml Fri Aug 11 14:03:38 2017 -0400 @@ -418,11 +418,6 @@ com.redhat.thermostat - thermostat-agent-cli - ${project.version} - - - com.redhat.thermostat thermostat-agent-ipc-tcpsocket-server ${project.version}