changeset 537:0e677821d3d4

Use configuration instead of hard-coded value for config-server listen port. This is also published as part of agent information, so that clients can know what port each agent listens on. Also some potential causes of hanging during agent shutdown are corrected here. reviewed-by: sgehwolf, neugens, omajid review-thread: http://icedtea.classpath.org/pipermail/thermostat/2012-August/002679.html PR925
author Jon VanAlten <jon.vanalten@redhat.com>
date Thu, 16 Aug 2012 17:00:03 -0400
parents 797047eb544a
children 896613f76b83
files agent/cli/pom.xml agent/cli/src/main/java/com/redhat/thermostat/agent/cli/AgentApplication.java agent/command/pom.xml agent/command/src/main/java/com/redhat/thermostat/agent/command/ConfigurationServer.java agent/command/src/main/java/com/redhat/thermostat/agent/command/internal/Activator.java agent/command/src/main/java/com/redhat/thermostat/agent/command/internal/ConfigurationServerContext.java agent/command/src/main/java/com/redhat/thermostat/agent/command/internal/ConfigurationServerImpl.java agent/command/src/test/java/com/redhat/thermostat/agent/command/internal/ConfigurationServerImplTest.java agent/core/src/main/java/com/redhat/thermostat/agent/Agent.java agent/core/src/main/java/com/redhat/thermostat/agent/config/AgentConfigsUtils.java agent/core/src/main/java/com/redhat/thermostat/agent/config/AgentProperties.java agent/core/src/main/java/com/redhat/thermostat/agent/config/AgentStartupConfiguration.java agent/core/src/main/java/com/redhat/thermostat/agent/config/ConfigurationWatcher.java client/command/src/main/java/com/redhat/thermostat/client/command/internal/ConfigurationRequestContext.java common/command/src/main/java/com/redhat/thermostat/common/command/ConfigurationCommandContext.java common/command/src/test/java/com/redhat/thermostat/common/command/ConfigurationContextTest.java common/core/src/main/java/com/redhat/thermostat/common/storage/AgentInformation.java common/core/src/main/java/com/redhat/thermostat/common/storage/MongoStorage.java common/core/src/main/java/com/redhat/thermostat/common/storage/Storage.java common/core/src/main/java/com/redhat/thermostat/common/storage/StorageConstants.java distribution/config/agent.properties distribution/config/bundles.properties distribution/pom.xml launcher/src/main/java/com/redhat/thermostat/launcher/internal/Activator.java main/src/main/java/com/redhat/thermostat/main/impl/Activator.java
diffstat 25 files changed, 125 insertions(+), 202 deletions(-) [+]
line wrap: on
line diff
--- a/agent/cli/pom.xml	Thu Aug 16 14:35:57 2012 -0400
+++ b/agent/cli/pom.xml	Thu Aug 16 17:00:03 2012 -0400
@@ -68,6 +68,11 @@
     </dependency>
     <dependency>
       <groupId>com.redhat.thermostat</groupId>
+      <artifactId>thermostat-agent-command</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>com.redhat.thermostat</groupId>
       <artifactId>thermostat-common-core</artifactId>
       <version>${project.version}</version>
     </dependency>
--- a/agent/cli/src/main/java/com/redhat/thermostat/agent/cli/AgentApplication.java	Thu Aug 16 14:35:57 2012 -0400
+++ b/agent/cli/src/main/java/com/redhat/thermostat/agent/cli/AgentApplication.java	Thu Aug 16 17:00:03 2012 -0400
@@ -42,6 +42,7 @@
 import java.util.logging.Logger;
 
 import com.redhat.thermostat.agent.Agent;
+import com.redhat.thermostat.agent.command.ConfigurationServer;
 import com.redhat.thermostat.agent.config.AgentConfigsUtils;
 import com.redhat.thermostat.agent.config.AgentOptionParser;
 import com.redhat.thermostat.agent.config.AgentStartupConfiguration;
@@ -66,6 +67,7 @@
 import com.redhat.thermostat.common.storage.StorageProvider;
 import com.redhat.thermostat.common.tools.BasicCommand;
 import com.redhat.thermostat.common.utils.LoggingUtils;
+import com.redhat.thermostat.common.utils.OSGIUtils;
 
 import sun.misc.Signal;
 import sun.misc.SignalHandler;
@@ -136,6 +138,9 @@
         connection.connect();
         logger.fine("Connecting to storage...");
 
+        final ConfigurationServer configServer = OSGIUtils.getInstance().getService(ConfigurationServer.class);
+        configServer.startListening(configuration.getConfigListenPort());
+
         BackendRegistry backendRegistry = null;
         try {
             backendRegistry = new BackendRegistry(configuration);
@@ -163,6 +168,7 @@
         final CountDownLatch shutdownLatch = new CountDownLatch(1);
         Signal.handle(new Signal("INT"), new SignalHandler() {
             public void handle(sun.misc.Signal sig) {
+                configServer.stopListening();
                 agent.stop();
                 logger.fine("Agent stopped.");       
                 shutdownLatch.countDown();
@@ -170,7 +176,7 @@
         });
         try {
             shutdownLatch.await();
-            logger.fine("terimating agent cmd");
+            logger.fine("terminating agent cmd");
         } catch (InterruptedException e) {
             return;
         }
--- a/agent/command/pom.xml	Thu Aug 16 14:35:57 2012 -0400
+++ b/agent/command/pom.xml	Thu Aug 16 17:00:03 2012 -0400
@@ -46,7 +46,7 @@
   </parent>
 
   <artifactId>thermostat-agent-command</artifactId>
-  <packaging>jar</packaging>
+  <packaging>bundle</packaging>
 
   <name>Thermostat Command Channel Server</name>
 
@@ -78,14 +78,12 @@
     <dependency>
       <groupId>org.osgi</groupId>
       <artifactId>org.osgi.core</artifactId>
-      <scope>provided</scope>
     </dependency>
 
     <dependency>
       <groupId>com.redhat.thermostat</groupId>
       <artifactId>thermostat-common-command</artifactId>
       <version>${project.version}</version>
-      <type>jar</type>
     </dependency>
     
   </dependencies>
@@ -99,13 +97,15 @@
         <configuration>
           <instructions>
             <Bundle-Vendor>Red Hat, Inc.</Bundle-Vendor>
-            <Bundle-Activator>com.redhat.thermostat.agent.config.command.internal.Activator</Bundle-Activator>
+            <Bundle-Activator>com.redhat.thermostat.agent.command.internal.Activator</Bundle-Activator>
             <Export-Package>
               com.redhat.thermostat.agent.command
             </Export-Package>
             <Private-Package>
               com.redhat.thermostat.agent.command.internal
             </Private-Package>
+            <!-- Do not autogenerate uses clauses in Manifests -->
+            <_nouses>true</_nouses>
           </instructions>
         </configuration>
       </plugin>
--- a/agent/command/src/main/java/com/redhat/thermostat/agent/command/ConfigurationServer.java	Thu Aug 16 14:35:57 2012 -0400
+++ b/agent/command/src/main/java/com/redhat/thermostat/agent/command/ConfigurationServer.java	Thu Aug 16 17:00:03 2012 -0400
@@ -38,4 +38,8 @@
 
 public interface ConfigurationServer {
 
+    void startListening(int configListenPort);
+
+    void stopListening();
+
 }
--- a/agent/command/src/main/java/com/redhat/thermostat/agent/command/internal/Activator.java	Thu Aug 16 14:35:57 2012 -0400
+++ b/agent/command/src/main/java/com/redhat/thermostat/agent/command/internal/Activator.java	Thu Aug 16 17:00:03 2012 -0400
@@ -41,16 +41,16 @@
 
 import org.osgi.framework.BundleActivator;
 import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceRegistration;
 
 import com.redhat.thermostat.agent.command.ConfigurationServer;
 
-
-
 public class Activator implements BundleActivator {
 
     private static final Logger logger = Logger.getLogger(Activator.class.getSimpleName());
 
     private ConfigurationServerImpl confServer;
+    private ServiceRegistration registration;
 
     public Activator() {
         confServer = new ConfigurationServerImpl(new ConfigurationServerContext());
@@ -59,13 +59,14 @@
     @Override
     public void start(BundleContext context) throws Exception {    
         logger.log(Level.INFO, "activating thermostat-agent-confserver");
-        context.registerService(ConfigurationServer.class.getName(), confServer, null);
-        confServer.startup();
+        registration = context.registerService(ConfigurationServer.class.getName(), confServer, null);
     }
 
     @Override
     public void stop(BundleContext context) throws Exception {
-        confServer.shutdown();
+        if (registration != null) {
+            registration.unregister();
+        }
     }
 
 }
--- a/agent/command/src/main/java/com/redhat/thermostat/agent/command/internal/ConfigurationServerContext.java	Thu Aug 16 14:35:57 2012 -0400
+++ b/agent/command/src/main/java/com/redhat/thermostat/agent/command/internal/ConfigurationServerContext.java	Thu Aug 16 17:00:03 2012 -0400
@@ -49,10 +49,10 @@
 
 import com.redhat.thermostat.common.command.ConfigurationCommandContext;
 
-class ConfigurationServerContext extends ConfigurationCommandContext {
+class ConfigurationServerContext implements ConfigurationCommandContext {
 
-    final ServerBootstrap bootstrap;
-    final ChannelGroup channels;
+    private final ServerBootstrap bootstrap;
+    private final ChannelGroup channels;
 
     ConfigurationServerContext() {
         bootstrap = createBootstrap();
--- a/agent/command/src/main/java/com/redhat/thermostat/agent/command/internal/ConfigurationServerImpl.java	Thu Aug 16 14:35:57 2012 -0400
+++ b/agent/command/src/main/java/com/redhat/thermostat/agent/command/internal/ConfigurationServerImpl.java	Thu Aug 16 17:00:03 2012 -0400
@@ -36,27 +36,32 @@
 
 package com.redhat.thermostat.agent.command.internal;
 
+import java.net.InetSocketAddress;
+
 import org.jboss.netty.bootstrap.ServerBootstrap;
 
 import com.redhat.thermostat.agent.command.ConfigurationServer;
 
 class ConfigurationServerImpl implements ConfigurationServer {
 
+    private static final String HOST = "127.0.0.1";
     private final ConfigurationServerContext ctx;
 
     ConfigurationServerImpl(ConfigurationServerContext ctx) {
         this.ctx = ctx;
     }
 
-    void startup() {
-
+    @Override
+    public void startListening(int configListenPort) {
         ServerBootstrap bootstrap = (ServerBootstrap) ctx.getBootstrap();
 
         // Bind and start to accept incoming connections.
-        bootstrap.bind(ctx.getAddress());
+        bootstrap.bind(new InetSocketAddress(HOST, configListenPort));
     }
 
-    void shutdown() {
+    @Override
+    public void stopListening() {
         ctx.getChannelGroup().close().awaitUninterruptibly();
+        ctx.getBootstrap().releaseExternalResources();
     }
 }
--- a/agent/command/src/test/java/com/redhat/thermostat/agent/command/internal/ConfigurationServerImplTest.java	Thu Aug 16 14:35:57 2012 -0400
+++ b/agent/command/src/test/java/com/redhat/thermostat/agent/command/internal/ConfigurationServerImplTest.java	Thu Aug 16 17:00:03 2012 -0400
@@ -58,7 +58,6 @@
     private ConfigurationServerContext ctx;
     ChannelGroup cg;
     ServerBootstrap bootstrap;
-    InetSocketAddress addr;
 
     @Before
     public void setUp() {
@@ -66,18 +65,16 @@
         ChannelGroupFuture future = mock(ChannelGroupFuture.class);
         when(cg.close()).thenReturn(future);
         bootstrap = mock(ServerBootstrap.class);
-        addr = new InetSocketAddress(123);
         ctx = mock(ConfigurationServerContext.class);
         when(ctx.getBootstrap()).thenReturn(bootstrap);
         when(ctx.getChannelGroup()).thenReturn(cg);
-        when(ctx.getAddress()).thenReturn(addr);
     }
 
     @Test
-    public void testStartup() {
+    public void testStartListening() {
+        InetSocketAddress addr = new InetSocketAddress("127.0.0.1", 123);
         ConfigurationServerImpl server = new ConfigurationServerImpl(ctx);
-        server.startup();
-
+        server.startListening(addr.getPort());
 
         ArgumentCaptor<InetSocketAddress> argument = ArgumentCaptor.forClass(InetSocketAddress.class);
         verify(bootstrap).bind(argument.capture());
@@ -85,9 +82,9 @@
     }
 
     @Test
-    public void testShutdown() {
+    public void testStopListening() {
         ConfigurationServerImpl server = new ConfigurationServerImpl(ctx);
-        server.shutdown();
+        server.stopListening();
 
         verify(cg).close();
     }
--- a/agent/core/src/main/java/com/redhat/thermostat/agent/Agent.java	Thu Aug 16 14:35:57 2012 -0400
+++ b/agent/core/src/main/java/com/redhat/thermostat/agent/Agent.java	Thu Aug 16 17:00:03 2012 -0400
@@ -40,7 +40,6 @@
 import java.util.logging.Logger;
 
 import com.redhat.thermostat.agent.config.AgentStartupConfiguration;
-import com.redhat.thermostat.agent.config.ConfigurationWatcher;
 import com.redhat.thermostat.backend.Backend;
 import com.redhat.thermostat.backend.BackendRegistry;
 import com.redhat.thermostat.common.LaunchException;
@@ -64,7 +63,7 @@
     private AgentInformation agentInfo;
     
     private Storage storage;
-    private Thread configWatcherThread = null;
+    private boolean started = false;
 
     public Agent(BackendRegistry backendRegistry, AgentStartupConfiguration config, DAOFactory daos) {
         this(backendRegistry, UUID.randomUUID(), config, daos);
@@ -101,12 +100,11 @@
     }
 
     public synchronized void start() throws LaunchException {
-        if (configWatcherThread == null) {
+        if (!started) {
             startBackends();
             agentInfo = createAgentInformation();
             storage.addAgentInformation(agentInfo);
-            configWatcherThread = new Thread(new ConfigurationWatcher(storage, backendRegistry), "Configuration Watcher");
-            configWatcherThread.start();
+            started = true;
         } else {
             logger.warning("Attempt to start agent when already started.");
         }
@@ -123,20 +121,12 @@
             agentInfo.addBackend(backendInfo);
         }
         agentInfo.setAlive(true);
+        agentInfo.setConfigListenPort(config.getConfigListenPort());
         return agentInfo;
     }
 
     public synchronized void stop() {
-        if (configWatcherThread != null) {
-            configWatcherThread.interrupt(); // This thread checks for its own interrupted state and ends if interrupted.
-            while (configWatcherThread.isAlive()) {
-                try {
-                    configWatcherThread.join();
-                } catch (InterruptedException e) {
-                    logger.fine("Interrupted while waiting for ConfigurationWatcher to die.");
-                }
-            }
-            configWatcherThread = null;
+        if (started) {
 
             stopBackends();
             if (config.purge()) {
@@ -149,7 +139,7 @@
                 agentInfo.setAlive(false);
                 storage.updateAgentInformation(agentInfo);
             }
-            
+            started = false;
         } else {
             logger.warning("Attempt to stop agent which is not active");
         }
--- a/agent/core/src/main/java/com/redhat/thermostat/agent/config/AgentConfigsUtils.java	Thu Aug 16 14:35:57 2012 -0400
+++ b/agent/core/src/main/java/com/redhat/thermostat/agent/config/AgentConfigsUtils.java	Thu Aug 16 17:00:03 2012 -0400
@@ -86,6 +86,12 @@
             String purge = (String) properties.get(AgentProperties.SAVE_ON_EXIT.name());
             configuration.setPurge(!Boolean.parseBoolean(purge));
         }
+
+        configuration.setConfigListenPort(12000); // Needs some default value.
+        if (properties.containsKey(AgentProperties.CONFIG_LISTEN_PORT.name())) {
+            String port = properties.getProperty(AgentProperties.CONFIG_LISTEN_PORT.name());
+            configuration.setConfigListenPort(Integer.parseInt(port));
+        }
     }
     
 }
--- a/agent/core/src/main/java/com/redhat/thermostat/agent/config/AgentProperties.java	Thu Aug 16 14:35:57 2012 -0400
+++ b/agent/core/src/main/java/com/redhat/thermostat/agent/config/AgentProperties.java	Thu Aug 16 17:00:03 2012 -0400
@@ -42,5 +42,6 @@
     BACKENDS,
     DEBUG_CONSOLE,
     DB_URL,
-    SAVE_ON_EXIT
+    SAVE_ON_EXIT,
+    CONFIG_LISTEN_PORT,
 }
--- a/agent/core/src/main/java/com/redhat/thermostat/agent/config/AgentStartupConfiguration.java	Thu Aug 16 14:35:57 2012 -0400
+++ b/agent/core/src/main/java/com/redhat/thermostat/agent/config/AgentStartupConfiguration.java	Thu Aug 16 17:00:03 2012 -0400
@@ -62,6 +62,7 @@
     private String password;
 
     private long startTime;
+    private int configListenPort;
     
     AgentStartupConfiguration() {
         this.backends = new ArrayList<>();
@@ -153,4 +154,12 @@
     public String getPassword() {
         return password;
     }
+
+    public void setConfigListenPort(int port) {
+        configListenPort = port;
+    }
+
+    public int getConfigListenPort() {
+        return configListenPort;
+    }
 }
--- a/agent/core/src/main/java/com/redhat/thermostat/agent/config/ConfigurationWatcher.java	Thu Aug 16 14:35:57 2012 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,75 +0,0 @@
-/*
- * Copyright 2012 Red Hat, Inc.
- *
- * This file is part of Thermostat.
- *
- * Thermostat is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published
- * by the Free Software Foundation; either version 2, or (at your
- * option) any later version.
- *
- * Thermostat is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Thermostat; see the file COPYING.  If not see
- * <http://www.gnu.org/licenses/>.
- *
- * Linking this code with other modules is making a combined work
- * based on this code.  Thus, the terms and conditions of the GNU
- * General Public License cover the whole combination.
- *
- * As a special exception, the copyright holders of this code give
- * you permission to link this code with independent modules to
- * produce an executable, regardless of the license terms of these
- * independent modules, and to copy and distribute the resulting
- * executable under terms of your choice, provided that you also
- * meet, for each linked independent module, the terms and conditions
- * of the license of that module.  An independent module is a module
- * which is not derived from or based on this code.  If you modify
- * this code, you may extend this exception to your version of the
- * library, but you are not obligated to do so.  If you do not wish
- * to do so, delete this exception statement from your version.
- */
-
-package com.redhat.thermostat.agent.config;
-
-import java.util.logging.Logger;
-
-import com.redhat.thermostat.backend.BackendRegistry;
-import com.redhat.thermostat.common.storage.Storage;
-import com.redhat.thermostat.common.utils.LoggingUtils;
-
-public class ConfigurationWatcher implements Runnable {
-
-    private static final Logger logger = LoggingUtils.getLogger(ConfigurationWatcher.class);
-
-    private Storage storage;
-    private BackendRegistry backends;
-
-    public ConfigurationWatcher(Storage storage, BackendRegistry backends) {
-        this.storage = storage;
-        this.backends = backends;
-    }
-
-    @Override
-    public void run() {
-        logger.fine("Watching for configuration changes.");
-        while (!Thread.interrupted()) {
-            checkConfigUpdates();
-        }
-        logger.fine("No longer watching for configuration changes.");
-    }
-
-    // TODO It would be best to develop this algorithm when we have a client that can initiate changes, so that it can be tested.
-    private void checkConfigUpdates() {
-        try { // THIS IS ONLY TEMPORARY.  Until we do implement this algorithm, we don't want this thread busy hogging CPU.
-            Thread.sleep(2000);
-        } catch (InterruptedException ignore) {
-            Thread.currentThread().interrupt();
-        }
-    }
-
-}
--- a/client/command/src/main/java/com/redhat/thermostat/client/command/internal/ConfigurationRequestContext.java	Thu Aug 16 14:35:57 2012 -0400
+++ b/client/command/src/main/java/com/redhat/thermostat/client/command/internal/ConfigurationRequestContext.java	Thu Aug 16 17:00:03 2012 -0400
@@ -47,7 +47,7 @@
 
 import com.redhat.thermostat.common.command.ConfigurationCommandContext;
 
-public class ConfigurationRequestContext extends ConfigurationCommandContext {
+public class ConfigurationRequestContext implements ConfigurationCommandContext {
 
     private final ClientBootstrap bootstrap;
 
--- a/common/command/src/main/java/com/redhat/thermostat/common/command/ConfigurationCommandContext.java	Thu Aug 16 14:35:57 2012 -0400
+++ b/common/command/src/main/java/com/redhat/thermostat/common/command/ConfigurationCommandContext.java	Thu Aug 16 17:00:03 2012 -0400
@@ -36,19 +36,10 @@
 
 package com.redhat.thermostat.common.command;
 
-import java.net.InetSocketAddress;
-
 import org.jboss.netty.bootstrap.Bootstrap;
 
-public abstract class ConfigurationCommandContext {
-
-    private static final String HOST = "127.0.0.1";
-    private static final int PORT = 12000;
+public interface ConfigurationCommandContext {
 
-    public abstract Bootstrap getBootstrap();
-
-    public InetSocketAddress getAddress() {
-        return new InetSocketAddress(HOST, PORT);
-    }
+    public Bootstrap getBootstrap();
 
 }
--- a/common/command/src/test/java/com/redhat/thermostat/common/command/ConfigurationContextTest.java	Thu Aug 16 14:35:57 2012 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,67 +0,0 @@
-/*
- * Copyright 2012 Red Hat, Inc.
- *
- * This file is part of Thermostat.
- *
- * Thermostat is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published
- * by the Free Software Foundation; either version 2, or (at your
- * option) any later version.
- *
- * Thermostat is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Thermostat; see the file COPYING.  If not see
- * <http://www.gnu.org/licenses/>.
- *
- * Linking this code with other modules is making a combined work
- * based on this code.  Thus, the terms and conditions of the GNU
- * General Public License cover the whole combination.
- *
- * As a special exception, the copyright holders of this code give
- * you permission to link this code with independent modules to
- * produce an executable, regardless of the license terms of these
- * independent modules, and to copy and distribute the resulting
- * executable under terms of your choice, provided that you also
- * meet, for each linked independent module, the terms and conditions
- * of the license of that module.  An independent module is a module
- * which is not derived from or based on this code.  If you modify
- * this code, you may extend this exception to your version of the
- * library, but you are not obligated to do so.  If you do not wish
- * to do so, delete this exception statement from your version.
- */
-
-package com.redhat.thermostat.common.command;
-
-import static org.junit.Assert.assertEquals;
-
-import java.net.InetSocketAddress;
-
-import org.jboss.netty.bootstrap.Bootstrap;
-import org.junit.Test;
-
-import com.redhat.thermostat.common.command.ConfigurationCommandContext;
-
-public class ConfigurationContextTest {
-
-    @Test
-    public void testGetAddress() {
-        ConfigurationCommandContext ctx = new ConfigurationCommandContext() {
-
-            @Override
-            public Bootstrap getBootstrap() {
-                return null;
-            }
-            
-        };
-        InetSocketAddress addr = ctx.getAddress();
-        int port = addr.getPort();
-        String address = addr.getAddress().getHostAddress();
-
-        assertEquals(12000, port);
-        assertEquals("127.0.0.1", address);
-    }
-}
--- a/common/core/src/main/java/com/redhat/thermostat/common/storage/AgentInformation.java	Thu Aug 16 14:35:57 2012 -0400
+++ b/common/core/src/main/java/com/redhat/thermostat/common/storage/AgentInformation.java	Thu Aug 16 17:00:03 2012 -0400
@@ -51,7 +51,8 @@
     private long stopTime;
 
     private boolean alive;
-    
+    private int configListenPort;
+
     private List<BackendInformation> backends = new ArrayList<BackendInformation>();
     
     public long getStartTime() {
@@ -85,4 +86,12 @@
     public void addBackend(BackendInformation backend) {
         backends.add(backend);
     }
+
+    public int getConfigListenPort() {
+        return configListenPort;
+    }
+
+    public void setConfigListenPort(int configListenPort) {
+        this.configListenPort = configListenPort;
+    }
 }
--- a/common/core/src/main/java/com/redhat/thermostat/common/storage/MongoStorage.java	Thu Aug 16 14:35:57 2012 -0400
+++ b/common/core/src/main/java/com/redhat/thermostat/common/storage/MongoStorage.java	Thu Aug 16 17:00:03 2012 -0400
@@ -55,6 +55,7 @@
 import com.mongodb.gridfs.GridFSDBFile;
 import com.mongodb.gridfs.GridFSInputFile;
 import com.redhat.thermostat.common.config.StartupConfiguration;
+import com.redhat.thermostat.common.dao.HostRef;
 import com.redhat.thermostat.common.storage.Connection.ConnectionListener;
 import com.redhat.thermostat.common.storage.Connection.ConnectionStatus;
 
@@ -255,6 +256,7 @@
         result.put(StorageConstants.KEY_AGENT_CONFIG_AGENT_START_TIME, agentInfo.getStartTime());
         result.put(StorageConstants.KEY_AGENT_CONFIG_AGENT_STOP_TIME, agentInfo.getStopTime());
         result.put(StorageConstants.KEY_AGENT_CONFIG_AGENT_ALIVE, agentInfo.isAlive());
+        result.put(StorageConstants.KEY_AGENT_CONFIG_LISTEN_PORT, agentInfo.getConfigListenPort());
         
         BasicDBObject backends = new BasicDBObject();
         for (BackendInformation backend : agentInfo.getBackends()) {
@@ -389,6 +391,23 @@
     }
 
     @Override
+    public int getConfigListenPort(HostRef ref) {
+        return getConfigListenPort(ref.getAgentId());
+    }
+
+    private int getConfigListenPort(String id) {
+        int port = -1;
+        DBCollection configCollection = db.getCollection(StorageConstants.CATEGORY_AGENT_CONFIG);
+        BasicDBObject query = new BasicDBObject(KEY_AGENT_ID, id);
+        DBObject config = configCollection.findOne(query);
+        Object value = config.get(StorageConstants.KEY_AGENT_CONFIG_LISTEN_PORT);
+        if (value instanceof Integer) {
+            port = (int) value;
+        }
+        return port;
+    }
+
+    @Override
     public void saveFile(String filename, InputStream data) {
         GridFS gridFS = new GridFS(db);
         GridFSInputFile inputFile = gridFS.createFile(data, filename);
--- a/common/core/src/main/java/com/redhat/thermostat/common/storage/Storage.java	Thu Aug 16 14:35:57 2012 -0400
+++ b/common/core/src/main/java/com/redhat/thermostat/common/storage/Storage.java	Thu Aug 16 17:00:03 2012 -0400
@@ -39,6 +39,8 @@
 import java.io.InputStream;
 import java.util.UUID;
 
+import com.redhat.thermostat.common.dao.HostRef;
+
 
 public abstract class Storage {
 
@@ -85,6 +87,8 @@
      */
     public abstract String getBackendConfig(String backendName, String configurationKey);
 
+    public abstract int getConfigListenPort(HostRef ref);
+
     public abstract void saveFile(String filename, InputStream data);
 
     public abstract InputStream loadFile(String filename);
--- a/common/core/src/main/java/com/redhat/thermostat/common/storage/StorageConstants.java	Thu Aug 16 14:35:57 2012 -0400
+++ b/common/core/src/main/java/com/redhat/thermostat/common/storage/StorageConstants.java	Thu Aug 16 17:00:03 2012 -0400
@@ -51,4 +51,6 @@
 
     public static final String KEY_AGENT_CONFIG_AGENT_ALIVE = "alive";
     public static final String KEY_AGENT_CONFIG_AGENT_STOP_TIME = "stop-time";
+
+    public static final String KEY_AGENT_CONFIG_LISTEN_PORT = "config-listen-port";
 }
--- a/distribution/config/agent.properties	Thu Aug 16 14:35:57 2012 -0400
+++ b/distribution/config/agent.properties	Thu Aug 16 17:00:03 2012 -0400
@@ -7,3 +7,9 @@
 # indicates if this agent will save its data to the database on exit
 # or rather will purge the db
 SAVE_ON_EXIT=false
+
+# A netty-based side channel for accepting configuration/tuning
+# requests from the client will listen for connections on the port
+# configured here.
+# If this is removed or commented out, the default port is 12000
+CONFIG_LISTEN_PORT=12000
--- a/distribution/config/bundles.properties	Thu Aug 16 14:35:57 2012 -0400
+++ b/distribution/config/bundles.properties	Thu Aug 16 17:00:03 2012 -0400
@@ -20,11 +20,12 @@
 service = thermostat-agent-core-@project.version@.jar, \
           thermostat-osgi-process-handler-@project.version@.jar, \
           thermostat-common-core-@project.version@.jar, \
-          thermostat-agent-cli-@project.version@.jar, \
           thermostat-common-command-@project.version@.jar, \
-          thermostat-agent-command-@project.version@.jar
+          thermostat-agent-command-@project.version@.jar, \
+          thermostat-agent-cli-@project.version@.jar
 
 agent = thermostat-agent-core-@project.version@.jar, \
+        thermostat-osgi-process-handler-@project.version@.jar, \
         thermostat-common-core-@project.version@.jar, \
         thermostat-agent-cli-@project.version@.jar, \
         thermostat-common-command-@project.version@.jar, \
--- a/distribution/pom.xml	Thu Aug 16 14:35:57 2012 -0400
+++ b/distribution/pom.xml	Thu Aug 16 17:00:03 2012 -0400
@@ -289,6 +289,11 @@
       <version>${project.version}</version>
     </dependency>
     <dependency>
+      <groupId>com.redhat.thermostat</groupId>
+      <artifactId>thermostat-common-command</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
     	<groupId>com.redhat.thermostat</groupId>
     	<artifactId>thermostat-osgi-process-handler</artifactId>
     	<version>${project.version}</version>
--- a/launcher/src/main/java/com/redhat/thermostat/launcher/internal/Activator.java	Thu Aug 16 14:35:57 2012 -0400
+++ b/launcher/src/main/java/com/redhat/thermostat/launcher/internal/Activator.java	Thu Aug 16 17:00:03 2012 -0400
@@ -73,7 +73,6 @@
 
     @SuppressWarnings("rawtypes")
     private ServiceRegistration launcherServiceRegistration;
-    @SuppressWarnings("rawtypes")
     private MultipleServiceTracker tracker;
 
     @Override
--- a/main/src/main/java/com/redhat/thermostat/main/impl/Activator.java	Thu Aug 16 14:35:57 2012 -0400
+++ b/main/src/main/java/com/redhat/thermostat/main/impl/Activator.java	Thu Aug 16 17:00:03 2012 -0400
@@ -46,16 +46,21 @@
 
 public class Activator implements BundleActivator {
 
+    ExecutorService e;
+
+    public Activator() {
+        e = Executors.newSingleThreadExecutor();
+    }
+
     @Override
     public void start(BundleContext context) throws Exception {
         Thermostat thermostat = new Thermostat(context);
-        ExecutorService e = Executors.newSingleThreadExecutor();
         e.execute(thermostat);
     }
 
     @Override
     public void stop(BundleContext context) throws Exception {
-        // nothing to do here
+        e.shutdownNow();
     }
 
 }