changeset 2691:c4ea301ff07b

Remove old netty-based command channel. Reviewed-by: ebaron Review-thread: http://icedtea.classpath.org/pipermail/thermostat/2017-June/023634.html
author Severin Gehwolf <sgehwolf@redhat.com>
date Mon, 12 Jun 2017 20:50:03 +0200
parents eaa1ff0bf5b9
children a5425acc4260
files agent/cli/pom.xml agent/cli/src/main/java/com/redhat/thermostat/agent/cli/internal/AgentApplication.java agent/cli/src/test/java/com/redhat/thermostat/agent/cli/internal/AgentApplicationTest.java agent/command-server/pom.xml agent/command-server/src/main/java/com/redhat/thermostat/agent/command/server/internal/CommandChannelConstants.java agent/command-server/src/main/java/com/redhat/thermostat/agent/command/server/internal/CommandChannelRequestDecoder.java agent/command-server/src/main/java/com/redhat/thermostat/agent/command/server/internal/CommandChannelSSLConfiguration.java agent/command-server/src/main/java/com/redhat/thermostat/agent/command/server/internal/CommandChannelServerContext.java agent/command-server/src/main/java/com/redhat/thermostat/agent/command/server/internal/CommandChannelServerImpl.java agent/command-server/src/main/java/com/redhat/thermostat/agent/command/server/internal/CommandChannelServerMain.java agent/command-server/src/main/java/com/redhat/thermostat/agent/command/server/internal/JsonRequestEncoder.java agent/command-server/src/main/java/com/redhat/thermostat/agent/command/server/internal/JsonResponseParser.java agent/command-server/src/main/java/com/redhat/thermostat/agent/command/server/internal/ResponseEncoder.java agent/command-server/src/main/java/com/redhat/thermostat/agent/command/server/internal/SSLConfigurationParser.java agent/command-server/src/main/java/com/redhat/thermostat/agent/command/server/internal/ServerHandler.java agent/command-server/src/test/java/com/redhat/thermostat/agent/command/server/internal/CommandChannelRequestDecoderTest.java agent/command-server/src/test/java/com/redhat/thermostat/agent/command/server/internal/CommandChannelServerContextTest.java agent/command-server/src/test/java/com/redhat/thermostat/agent/command/server/internal/CommandChannelServerImplTest.java agent/command-server/src/test/java/com/redhat/thermostat/agent/command/server/internal/CommandChannelServerMainTest.java agent/command-server/src/test/java/com/redhat/thermostat/agent/command/server/internal/JsonResponseParserTest.java agent/command-server/src/test/java/com/redhat/thermostat/agent/command/server/internal/ResponseEncoderTest.java agent/command-server/src/test/java/com/redhat/thermostat/agent/command/server/internal/SSLConfigurationParserTest.java agent/command-server/src/test/java/com/redhat/thermostat/agent/command/server/internal/ServerHandlerTest.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/AgentRequestDecoder.java agent/command/src/main/java/com/redhat/thermostat/agent/command/internal/AgentResponseEncoder.java agent/command/src/main/java/com/redhat/thermostat/agent/command/internal/CommandChannelConstants.java agent/command/src/main/java/com/redhat/thermostat/agent/command/internal/CommandChannelDelegate.java agent/command/src/main/java/com/redhat/thermostat/agent/command/internal/ProcessUserInfoBuilder.java agent/command/src/main/java/com/redhat/thermostat/agent/command/internal/SSLConfigurationEncoder.java agent/command/src/main/java/com/redhat/thermostat/agent/command/package-info.java agent/command/src/test/java/com/redhat/thermostat/agent/command/internal/AgentRequestDecoderTest.java agent/command/src/test/java/com/redhat/thermostat/agent/command/internal/AgentResponseEncoderTest.java agent/command/src/test/java/com/redhat/thermostat/agent/command/internal/CommandChannelDelegateTest.java agent/command/src/test/java/com/redhat/thermostat/agent/command/internal/SSLConfigurationEncoderTest.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/test/java/com/redhat/thermostat/agent/AgentTest.java agent/core/src/test/java/com/redhat/thermostat/agent/config/AgentConfigsUtilsTest.java agent/core/src/test/java/com/redhat/thermostat/agent/config/AgentStartupConfigurationTest.java agent/pom.xml common/command/pom.xml common/command/src/main/java/com/redhat/thermostat/common/command/InvalidMessageException.java common/command/src/main/java/com/redhat/thermostat/common/command/Message.java common/command/src/main/java/com/redhat/thermostat/common/command/Messages.java common/command/src/main/java/com/redhat/thermostat/common/command/Request.java common/command/src/main/java/com/redhat/thermostat/common/command/RequestResponseListener.java common/command/src/main/java/com/redhat/thermostat/common/command/Response.java common/command/src/main/java/com/redhat/thermostat/common/command/noapi/ConfigurationCommandContext.java common/command/src/main/java/com/redhat/thermostat/common/command/noapi/DecodingHelper.java common/command/src/main/java/com/redhat/thermostat/common/command/noapi/EncodingHelper.java common/command/src/main/java/com/redhat/thermostat/common/command/noapi/MessageEncoder.java common/command/src/main/java/com/redhat/thermostat/common/command/noapi/ParameterDecodingContext.java common/command/src/main/java/com/redhat/thermostat/common/command/noapi/ParameterDecodingState.java common/command/src/main/java/com/redhat/thermostat/common/command/noapi/RequestEncoder.java common/command/src/main/java/com/redhat/thermostat/common/command/noapi/StringDecodingContext.java common/command/src/main/java/com/redhat/thermostat/common/command/noapi/StringDecodingState.java common/command/src/test/java/com/redhat/thermostat/common/command/MessagesTest.java common/command/src/test/java/com/redhat/thermostat/common/command/RequestTest.java common/command/src/test/java/com/redhat/thermostat/common/command/ResponseTest.java common/command/src/test/java/com/redhat/thermostat/common/command/noapi/DecodingHelperTest.java common/command/src/test/java/com/redhat/thermostat/common/command/noapi/EncodingHelperTest.java common/command/src/test/java/com/redhat/thermostat/common/command/noapi/RequestEncoderTest.java common/pom.xml distribution/assembly/core-assembly.xml distribution/config/agent.properties distribution/pom.xml plugins/killvm/agent/pom.xml plugins/killvm/agent/src/main/java/com/redhat/thermostat/killvm/agent/internal/Activator.java plugins/killvm/agent/src/main/java/com/redhat/thermostat/killvm/agent/internal/KillVmReceiver.java plugins/killvm/agent/src/test/java/com/redhat/thermostat/killvm/agent/internal/ActivatorTest.java plugins/killvm/agent/src/test/java/com/redhat/thermostat/killvm/agent/internal/KillVmReceiverTest.java plugins/vm-gc/remote-collector-command/pom.xml plugins/vm-gc/remote-collector-command/src/main/java/com/redhat/thermostat/gc/remote/command/GCRequestReceiver.java plugins/vm-gc/remote-collector-command/src/main/java/com/redhat/thermostat/gc/remote/command/internal/Activator.java plugins/vm-gc/remote-collector-command/src/test/java/com/redhat/thermostat/gc/remote/command/internal/ActivatorTest.java pom.xml
diffstat 81 files changed, 158 insertions(+), 8241 deletions(-) [+]
line wrap: on
line diff
--- a/agent/cli/pom.xml	Tue Jun 13 12:02:12 2017 +0200
+++ b/agent/cli/pom.xml	Mon Jun 12 20:50:03 2017 +0200
@@ -78,11 +78,6 @@
     </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/internal/AgentApplication.java	Tue Jun 13 12:02:12 2017 +0200
+++ b/agent/cli/src/main/java/com/redhat/thermostat/agent/cli/internal/AgentApplication.java	Mon Jun 12 20:50:03 2017 +0200
@@ -36,17 +36,14 @@
 
 package com.redhat.thermostat.agent.cli.internal;
 
-import java.io.IOException;
 import java.util.concurrent.CountDownLatch;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 
 import org.osgi.framework.BundleContext;
-import org.osgi.framework.ServiceReference;
 import org.osgi.util.tracker.ServiceTracker;
 
 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;
@@ -62,7 +59,6 @@
 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.HostPortPair;
 import com.redhat.thermostat.common.utils.LoggingUtils;
 import com.redhat.thermostat.shared.config.InvalidConfigurationException;
 import com.redhat.thermostat.storage.core.WriterID;
@@ -120,45 +116,12 @@
         parser.parse();
     }
     
-    @SuppressWarnings({ "rawtypes", "unchecked" })
     private void runAgent(CommandContext ctx) throws CommandException {
         long startTime = System.currentTimeMillis();
         configuration.setStartTime(startTime);
 
         shutdownLatch = new CountDownLatch(1);
-        
-        configServerTracker = new ServiceTracker(bundleContext, ConfigurationServer.class.getName(), null) {
-            @Override
-            public Object addingService(ServiceReference reference) {
-                final ConfigurationServer configServer = (ConfigurationServer) super.addingService(reference);
-                final HostPortPair hostPort = configuration.getConfigListenAddress();
-
-                try {
-                    configServer.startListening(hostPort.getHost(), hostPort.getPort());
-                    prepareAgent(configServer);
-                } catch (IOException e) {
-                    logger.log(Level.SEVERE, e.getMessage());
-                    // log stack trace as info only
-                    logger.log(Level.INFO, e.getMessage(), e);
-                    shutdown(ExitStatus.EXIT_ERROR);
-                }
-                
-                return configServer;
-            }
-            
-            @Override
-            public void removedService(ServiceReference reference, Object service) {
-                if (shutdownLatch.getCount() > 0) {
-                    // Lost config server while still running
-                    logger.warning("ConfigurationServer unexpectedly became unavailable");
-                }
-                // Stop listening on command channel
-                ConfigurationServer server = (ConfigurationServer) service;
-                server.stopListening();
-                super.removedService(reference, service);
-            }
-        };
-        configServerTracker.open();
+        prepareAgent();
         
         try {
             // Wait for either SIGINT or SIGTERM
@@ -204,16 +167,13 @@
     private class CustomSignalHandler implements SignalHandler {
         
         private Agent agent;
-        private ConfigurationServer configServer;
 
-        public CustomSignalHandler(Agent agent, ConfigurationServer configServer) {
+        public CustomSignalHandler(Agent agent) {
             this.agent = agent;
-            this.configServer = configServer;
         }
         
         @Override
         public void handle(Signal arg0) {
-            configServer.stopListening();
             try {
                 agent.stop();
             } catch (Exception ex) {
@@ -270,7 +230,7 @@
         return agent;
     }
     
-    private void prepareAgent(final ConfigurationServer configServer) {
+    private void prepareAgent() {
         Class<?>[] deps = new Class<?>[] {
                 AgentInfoDAO.class,
                 BackendInfoDAO.class
@@ -283,7 +243,7 @@
                 BackendInfoDAO backendInfoDAO = services.get(BackendInfoDAO.class);
 
                 Agent agent = startAgent(agentInfoDAO, backendInfoDAO);
-                handler = new CustomSignalHandler(agent, configServer);
+                handler = new CustomSignalHandler(agent);
                 Signal.handle(new Signal(SIGINT_NAME), handler);
                 Signal.handle(new Signal(SIGTERM_NAME), handler);
             }
--- a/agent/cli/src/test/java/com/redhat/thermostat/agent/cli/internal/AgentApplicationTest.java	Tue Jun 13 12:02:12 2017 +0200
+++ b/agent/cli/src/test/java/com/redhat/thermostat/agent/cli/internal/AgentApplicationTest.java	Mon Jun 12 20:50:03 2017 +0200
@@ -39,14 +39,12 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.fail;
 import static org.mockito.Matchers.any;
-import static org.mockito.Mockito.doAnswer;
 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.io.IOException;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 
@@ -54,39 +52,37 @@
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.mockito.invocation.InvocationOnMock;
-import org.mockito.stubbing.Answer;
+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.command.ConfigurationServer;
 import com.redhat.thermostat.agent.config.AgentStartupConfiguration;
 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.common.utils.HostPortPair;
 import com.redhat.thermostat.shared.config.InvalidConfigurationException;
 import com.redhat.thermostat.storage.core.WriterID;
 import com.redhat.thermostat.storage.dao.AgentInfoDAO;
 import com.redhat.thermostat.storage.dao.BackendInfoDAO;
 import com.redhat.thermostat.testutils.StubBundleContext;
+import com.redhat.thermostat.utils.management.internal.MXBeanConnectionPoolControl;
 
 @RunWith(PowerMockRunner.class)
 public class AgentApplicationTest {
 
-    private static final String COMMAND_CHANNLE_BIND_HOST = "test";
-    private static final int COMMAND_CHANNEL_BIND_PORT = 10101;
-
     private StubBundleContext context;
 
-    private ConfigurationServer configServer;
     private ConfigurationCreator configCreator;
     private ExitStatus exitStatus;
     private WriterID writerId;
@@ -98,8 +94,6 @@
         
         AgentStartupConfiguration config = mock(AgentStartupConfiguration.class);
         when(config.getDBConnectionString()).thenReturn("test string; please ignore");
-        HostPortPair hostPort = new HostPortPair(COMMAND_CHANNLE_BIND_HOST, COMMAND_CHANNEL_BIND_PORT);
-        when(config.getConfigListenAddress()).thenReturn(hostPort);
 
         configCreator = mock(ConfigurationCreator.class);
         when(configCreator.create()).thenReturn(config);
@@ -108,8 +102,7 @@
         context.registerService(AgentInfoDAO.class.getName(), agentInfoDAO, null);
         BackendInfoDAO backendInfoDAO = mock(BackendInfoDAO.class);
         context.registerService(BackendInfoDAO.class.getName(), backendInfoDAO, null);
-        configServer = mock(ConfigurationServer.class);
-        context.registerService(ConfigurationServer.class.getName(), configServer, null);
+        context.registerService(MXBeanConnectionPoolControl.class, mock(MXBeanConnectionPoolControl.class), null);
         writerId = mock(WriterID.class);
 
         exitStatus = mock(ExitStatus.class);
@@ -118,18 +111,23 @@
     @After
     public void tearDown() {
         context = null;
-        configServer = null;
         configCreator = null;
         exitStatus = null;
     }
 
+    @PrepareForTest({ FrameworkUtil.class, Agent.class })
     @Test
-    public void testAgentStartup() throws CommandException, InterruptedException {
+    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);
@@ -141,6 +139,17 @@
         }
     }
     
+    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
@@ -195,7 +204,7 @@
         verify(exitStatus).setExitStatus(ExitStatus.EXIT_ERROR);
     }
 
-    private void startAgentRunThread(final long timoutMillis, final AgentApplication agent, final CommandException[] ce, final CountDownLatch latch) {
+    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);
@@ -205,21 +214,8 @@
             
             @Override
             public void run() {
-                // Finish when config server starts listening
                 try {
-                    doAnswer(new Answer<Void>() {
-
-                        @Override
-                        public Void answer(InvocationOnMock invocation) throws Throwable {
-                            latch.countDown();
-                            return null;
-                        }
-                    }).when(configServer).startListening(COMMAND_CHANNLE_BIND_HOST, COMMAND_CHANNEL_BIND_PORT);
-                } catch (IOException e1) {
-                    fail("a mock should not throw an exception");
-                }
-                
-                try {
+                    latch.countDown();
                     agent.run(commandContext);
                 } catch (CommandException e) {
                     ce[0] = e;
--- a/agent/command-server/pom.xml	Tue Jun 13 12:02:12 2017 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,122 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-
- 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
- <http://www.gnu.org/licenses/>.
-
- Linking this code with other modules is making a combined work
- based on this code.  Thus, the terms and conditions of the GNU
- General Public License cover the whole combination.
-
- As a special exception, the copyright holders of this code give
- you permission to link this code with independent modules to
- produce an executable, regardless of the license terms of these
- independent modules, and to copy and distribute the resulting
- executable under terms of your choice, provided that you also
- meet, for each linked independent module, the terms and conditions
- of the license of that module.  An independent module is a module
- which is not derived from or based on this code.  If you modify
- this code, you may extend this exception to your version of the
- library, but you are not obligated to do so.  If you do not wish
- to do so, delete this exception statement from your version.
-
--->
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
-  <modelVersion>4.0.0</modelVersion>
-
-  <parent>
-    <groupId>com.redhat.thermostat</groupId>
-    <artifactId>thermostat-agent</artifactId>
-    <version>1.99.12-SNAPSHOT</version>
-  </parent>
-
-  <artifactId>thermostat-agent-command-server</artifactId>
-  <packaging>bundle</packaging>
-
-  <name>Thermostat Command Channel Server (Process)</name>
-
-  <dependencies>
-    <dependency>
-      <groupId>junit</groupId>
-      <artifactId>junit</artifactId>
-      <scope>test</scope>
-    </dependency>
-    <dependency>
-      <groupId>org.mockito</groupId>
-      <artifactId>mockito-core</artifactId>
-      <scope>test</scope>
-    </dependency>
-    <dependency>
-      <groupId>org.powermock</groupId>
-      <artifactId>powermock-api-mockito</artifactId>
-      <scope>test</scope>
-    </dependency>
-    <dependency>
-      <groupId>org.powermock</groupId>
-      <artifactId>powermock-module-junit4</artifactId>
-      <scope>test</scope>
-    </dependency>
-    <dependency>
-      <groupId>io.netty</groupId>
-      <artifactId>netty-handler</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>commons-codec</groupId>
-      <artifactId>commons-codec</artifactId>
-      <version>${commons-codec.version}</version>
-    </dependency>
-	<dependency>
-      <groupId>com.google.code.gson</groupId>
-      <artifactId>gson</artifactId>
-    </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-agent-ipc-client</artifactId>
-      <version>${project.version}</version>
-    </dependency>
-  </dependencies>
-  
-  <build>
-    <plugins>
-      <plugin>
-        <groupId>org.apache.felix</groupId>
-        <artifactId>maven-bundle-plugin</artifactId>
-        <extensions>true</extensions>
-        <configuration>
-          <instructions>
-            <Bundle-Vendor>Red Hat, Inc.</Bundle-Vendor>
-            <Bundle-SymbolicName>com.redhat.thermostat.agent.command.server</Bundle-SymbolicName>
-            <Private-Package>
-              com.redhat.thermostat.agent.command.server.internal
-            </Private-Package>
-            <!-- Do not autogenerate uses clauses in Manifests -->
-            <_nouses>true</_nouses>
-          </instructions>
-        </configuration>
-      </plugin>
-    </plugins>
-  </build>
-
-</project>
-
--- a/agent/command-server/src/main/java/com/redhat/thermostat/agent/command/server/internal/CommandChannelConstants.java	Tue Jun 13 12:02:12 2017 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,66 +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
- * <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.command.server.internal;
-
-import java.nio.charset.Charset;
-
-interface CommandChannelConstants {
-
-    // Server startup state tokens
-    byte[] SERVER_STARTED_TOKEN = "<SERVER STARTED>".getBytes(Charset.forName("UTF-8"));
-    byte[] SERVER_READY_TOKEN = "<SERVER READY>".getBytes(Charset.forName("UTF-8"));
-    
-    // SSLConfiguration JSON members
-    String SSL_JSON_ROOT = "sslConfiguration";
-    String SSL_JSON_KEYSTORE_FILE = "keystoreFile";
-    String SSL_JSON_KEYSTORE_PASS = "keystorePass";
-    String SSL_JSON_COMMAND_CHANNEL = "enabledCommandChannel";
-    String SSL_JSON_BACKING_STORAGE = "enabledBackingStorage";
-    String SSL_JSON_HOSTNAME_VERIFICATION = "disableHostnameVerification";
-    
-    // Request JSON members
-    String REQUEST_JSON_TOP = "request";
-    String REQUEST_JSON_TYPE = "type";
-    String REQUEST_JSON_HOST = "targetHost";
-    String REQUEST_JSON_PORT = "targetPort";
-    String REQUEST_JSON_PARAMS = "parameters";
-    
-    // Response JSON members
-    String RESPONSE_JSON_TOP = "response";
-    String RESPONSE_JSON_TYPE = "type";
-
-}
--- a/agent/command-server/src/main/java/com/redhat/thermostat/agent/command/server/internal/CommandChannelRequestDecoder.java	Tue Jun 13 12:02:12 2017 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,140 +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
- * <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.command.server.internal;
-
-import java.net.InetSocketAddress;
-import java.util.List;
-import java.util.Map.Entry;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-import com.redhat.thermostat.common.command.InvalidMessageException;
-import com.redhat.thermostat.common.command.Request;
-import com.redhat.thermostat.common.command.Request.RequestType;
-import com.redhat.thermostat.common.command.noapi.DecodingHelper;
-import com.redhat.thermostat.common.command.noapi.ParameterDecodingContext;
-import com.redhat.thermostat.common.command.noapi.ParameterDecodingState;
-import com.redhat.thermostat.common.command.noapi.StringDecodingContext;
-import com.redhat.thermostat.common.command.noapi.StringDecodingState;
-import com.redhat.thermostat.common.utils.LoggingUtils;
-
-import io.netty.buffer.ByteBuf;
-import io.netty.buffer.ByteBufUtil;
-import io.netty.channel.ChannelHandlerContext;
-import io.netty.handler.codec.ByteToMessageDecoder;
-
-/**
- * <p>
- * {@link Request} objects are serialized over the command channel in the
- * following format:
- * <pre>
- * -------------------------
- * | A | TYPE | B | PARAMS |
- * -------------------------
- * 
- * A is an 32 bit integer representing the length - in bytes - of TYPE. TYPE
- * is a byte array representing the string of the request type (e.g.
- * "RESPONSE_EXPECTED") B is a 32 bit integer representing the number of
- * request parameters which follow.
- * 
- * PARAMS (if B > 0) is a variable length stream of the following format:
- * 
- * It is a simple encoding of name => value pairs.
- * 
- * -----------------------------------------------------------------------------------------------
- * | I_1 | K_1 | P_1 | V_1 | ... | I_(n-1) | K_(n-1) | P_(n-1) | V_(n-1) | I_n | K_n | P_n | V_n |
- * -----------------------------------------------------------------------------------------------
- * 
- * I_n  A 32 bit integer representing the length - in bytes - of the n'th
- *      parameter name.
- * K_n  A 32 bit integer representing the length - in bytes - of the n'th
- *      parameter value.
- * P_n  A byte array representing the string of the n'th parameter name.
- * V_n  A byte array representing the string of the n'th parameter value.
- * </pre>
- * </p>
- */
-class CommandChannelRequestDecoder extends ByteToMessageDecoder {
-    
-    private static final Logger logger = LoggingUtils.getLogger(CommandChannelRequestDecoder.class);
-    
-    @Override
-    protected void decode(ChannelHandlerContext ctx, ByteBuf buf, List<Object> out) throws Exception {
-        logger.log(Level.FINEST, "Command channel server: decoding Request object");
-        StringDecodingContext stringDecCtx = DecodingHelper.decodeString(buf);
-        if (stringDecCtx.getState() != StringDecodingState.VALUE_READ) {
-            // insufficient data, return
-            return;
-        }
-        String typeAsString = stringDecCtx.getValue();
-        if (typeAsString == null) {
-            throw new InvalidMessageException("Could not decode message: " + ByteBufUtil.hexDump(buf));
-        }
-        // Netty javadoc tells us it's safe to downcast to more concrete type.
-        InetSocketAddress addr = (InetSocketAddress)ctx.channel().remoteAddress();
-        Request request = new Request(RequestType.valueOf(typeAsString), addr);
-        int remainingLength = buf.readableBytes() - stringDecCtx.getBytesRead();
-        ByteBuf adjustedBuffer = buf.slice(stringDecCtx.getBytesRead(), remainingLength);
-        ParameterDecodingContext paramCtx = DecodingHelper.decodeParameters(adjustedBuffer);
-        if (paramCtx.getState() != ParameterDecodingState.ALL_PARAMETERS_READ) {
-            // insufficient data
-            return;
-        }
-        // clean up resources from the request type + parameters
-        int totalBytesRead = stringDecCtx.getBytesRead() +
-                                paramCtx.getBytesRead();
-        buf.readerIndex(buf.readerIndex() + totalBytesRead);
-        buf.discardReadBytes();
-        for (Entry<String, String> kv: paramCtx.getValues().entrySet()) {
-            request.setParameter(kv.getKey(), kv.getValue());
-        }
-        out.add(request);
-    }
-    
-    @Override
-    public void channelActive(ChannelHandlerContext ctx) throws Exception {
-        logger.log(Level.FINEST, "Channel active!");
-    }
-    
-    @Override
-    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
-        logger.log(Level.WARNING, "Exception caught", cause);
-        ctx.close();
-    }
-
-}
-
--- a/agent/command-server/src/main/java/com/redhat/thermostat/agent/command/server/internal/CommandChannelSSLConfiguration.java	Tue Jun 13 12:02:12 2017 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,85 +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
- * <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.command.server.internal;
-
-import java.io.File;
-
-import com.redhat.thermostat.shared.config.SSLConfiguration;
-
-class CommandChannelSSLConfiguration implements SSLConfiguration {
-    
-    private File keystoreFile;
-    private String keystorePass;
-    private boolean cmdChannel;
-    private boolean backingStorage;
-    private boolean disableVerification;
-    
-    public CommandChannelSSLConfiguration(File keystoreFile, String keystorePass, boolean cmdChannel, 
-            boolean backingStorage, boolean disableVerification) {
-        this.keystoreFile = keystoreFile;
-        this.keystorePass = keystorePass;
-        this.cmdChannel = cmdChannel;
-        this.backingStorage = backingStorage;
-        this.disableVerification = disableVerification;
-    }
-
-    @Override
-    public File getKeystoreFile() {
-        return keystoreFile;
-    }
-
-    @Override
-    public String getKeyStorePassword() {
-        return keystorePass;
-    }
-
-    @Override
-    public boolean enableForCmdChannel() {
-        return cmdChannel;
-    }
-
-    @Override
-    public boolean enableForBackingStorage() {
-        return backingStorage;
-    }
-
-    @Override
-    public boolean disableHostnameVerification() {
-        return disableVerification;
-    }
-
-}
--- a/agent/command-server/src/main/java/com/redhat/thermostat/agent/command/server/internal/CommandChannelServerContext.java	Tue Jun 13 12:02:12 2017 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,147 +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
- * <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.command.server.internal;
-
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-import javax.net.ssl.SSLContext;
-import javax.net.ssl.SSLEngine;
-
-import com.redhat.thermostat.agent.ipc.client.IPCMessageChannel;
-import com.redhat.thermostat.common.command.noapi.ConfigurationCommandContext;
-import com.redhat.thermostat.common.ssl.SSLContextFactory;
-import com.redhat.thermostat.common.ssl.SslInitException;
-import com.redhat.thermostat.common.utils.LoggingUtils;
-import com.redhat.thermostat.shared.config.InvalidConfigurationException;
-import com.redhat.thermostat.shared.config.SSLConfiguration;
-
-import io.netty.bootstrap.AbstractBootstrap;
-import io.netty.bootstrap.ServerBootstrap;
-import io.netty.channel.ChannelInitializer;
-import io.netty.channel.ChannelOption;
-import io.netty.channel.ChannelPipeline;
-import io.netty.channel.EventLoopGroup;
-import io.netty.channel.nio.NioEventLoopGroup;
-import io.netty.channel.socket.SocketChannel;
-import io.netty.channel.socket.nio.NioServerSocketChannel;
-import io.netty.handler.ssl.SslHandler;
-
-class CommandChannelServerContext implements ConfigurationCommandContext {
-
-    private static final Logger logger = LoggingUtils.getLogger(CommandChannelServerContext.class);
-    
-    private final ServerBootstrap bootstrap;
-    private final SSLConfiguration sslConf;
-
-
-    CommandChannelServerContext(SSLConfiguration sslConf, IPCMessageChannel agentChannel) {
-        this(sslConf, agentChannel, new ServerChannelPipelineInitializerCreator());
-    }
-    
-    CommandChannelServerContext(SSLConfiguration sslConf, IPCMessageChannel agentChannel, ServerChannelPipelineInitializerCreator initCreator) {
-        this.sslConf = sslConf;
-        bootstrap = createBootstrap(sslConf, agentChannel, initCreator);
-    }
-
-    @Override
-    public AbstractBootstrap<?, ?> getBootstrap() {
-        return bootstrap;
-    }
-
-    @Override
-    public SSLConfiguration getSSLConfiguration() {
-        return sslConf;
-    }
-
-    private ServerBootstrap createBootstrap(SSLConfiguration conf, IPCMessageChannel agentChannel, ServerChannelPipelineInitializerCreator initCreator) {
-        ServerBootstrap bootstrap = new ServerBootstrap();
-        EventLoopGroup bossGroup = new NioEventLoopGroup();
-        EventLoopGroup workerGroup = new NioEventLoopGroup();
-        bootstrap.group(bossGroup, workerGroup)
-            .channel(NioServerSocketChannel.class)
-            .childHandler(initCreator.createInitializer(conf, agentChannel))
-            .childOption(ChannelOption.TCP_NODELAY, true)
-            .childOption(ChannelOption.SO_KEEPALIVE, true)
-            .childOption(ChannelOption.SO_REUSEADDR, true);
-        
-        return bootstrap;
-    }
-
-    static class ServerChannelInitializer extends ChannelInitializer<SocketChannel> {
-
-        private final SSLConfiguration sslConf;
-        private final IPCMessageChannel agentChannel;
-        
-        ServerChannelInitializer(SSLConfiguration sslConf, IPCMessageChannel agentChannel) {
-            this.sslConf = sslConf;
-            this.agentChannel = agentChannel;
-        }
-        
-        @Override
-        public void initChannel(SocketChannel ch) throws Exception {
-            ChannelPipeline pipeline = ch.pipeline();
-            if (sslConf.enableForCmdChannel()) {
-                SSLEngine engine = null;
-                try {
-                    SSLContext ctxt = SSLContextFactory.getServerContext(sslConf);
-                    engine = ctxt.createSSLEngine();
-                    engine.setUseClientMode(false);
-                } catch (SslInitException | InvalidConfigurationException e) {
-                    logger.log(Level.SEVERE,
-                            "Failed to initiate command channel endpoint", e);
-                }
-                pipeline.addLast("ssl", new SslHandler(engine));
-                logger.log(Level.FINE, "Added SSL handler for command channel endpoint");
-            }
-            pipeline.addLast("decoder", new CommandChannelRequestDecoder());
-            pipeline.addLast("encoder", new ResponseEncoder());
-            pipeline.addLast("handler", new ServerHandler(sslConf, agentChannel));
-        }
-        
-    }
-    
-    // Testing hook
-    static class ServerChannelPipelineInitializerCreator {
-        
-        ServerChannelInitializer createInitializer(SSLConfiguration sslConf, IPCMessageChannel agentChannel) {
-            return new ServerChannelInitializer(sslConf, agentChannel);
-        }
-    }
-
-}
-
--- a/agent/command-server/src/main/java/com/redhat/thermostat/agent/command/server/internal/CommandChannelServerImpl.java	Tue Jun 13 12:02:12 2017 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,80 +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
- * <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.command.server.internal;
-
-import java.io.IOException;
-import java.net.InetSocketAddress;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-import com.redhat.thermostat.agent.command.ConfigurationServer;
-import com.redhat.thermostat.common.utils.LoggingUtils;
-
-import io.netty.bootstrap.ServerBootstrap;
-
-class CommandChannelServerImpl implements ConfigurationServer {
-
-    private static final Logger logger = LoggingUtils.getLogger(CommandChannelServerImpl.class);
-    private final CommandChannelServerContext ctx;
-    
-    CommandChannelServerImpl(CommandChannelServerContext ctx) {
-        this.ctx = ctx;
-    }
-
-    @Override
-    public void startListening(String hostname, int port) throws IOException {
-        ServerBootstrap bootstrap = (ServerBootstrap) ctx.getBootstrap();
-
-        InetSocketAddress addr = new InetSocketAddress(hostname, port);
-        
-        logger.log(Level.FINE, "Starting command channel server on " + addr.toString());
-        // Bind and start to accept incoming connections.
-        try {
-            bootstrap.bind(addr).sync();
-        } catch (InterruptedException e) {
-            logger.log(Level.WARNING, "Cmd channel server bind was interrupted!");
-        }
-        logger.log(Level.FINEST, "Bound command channel server to " + addr.toString());
-    }
-
-    @Override
-    public void stopListening() {
-        logger.log(Level.FINE, "Stopping command channel server");
-        ctx.getBootstrap().group().shutdownGracefully();
-    }
-}
-
--- a/agent/command-server/src/main/java/com/redhat/thermostat/agent/command/server/internal/CommandChannelServerMain.java	Tue Jun 13 12:02:12 2017 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,202 +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
- * <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.command.server.internal;
-
-import java.io.File;
-import java.io.IOException;
-import java.nio.ByteBuffer;
-
-import com.redhat.thermostat.agent.ipc.client.ClientIPCService;
-import com.redhat.thermostat.agent.ipc.client.ClientIPCServiceFactory;
-import com.redhat.thermostat.agent.ipc.client.IPCMessageChannel;
-import com.redhat.thermostat.common.portability.ProcessWatcher;
-import com.redhat.thermostat.shared.config.NativeLibraryResolver;
-import com.redhat.thermostat.shared.config.OS;
-import com.redhat.thermostat.shared.config.SSLConfiguration;
-import com.redhat.thermostat.shared.config.internal.CommonPathsImpl;
-
-public class CommandChannelServerMain {
-    
-    static final String IPC_SERVER_NAME = "command-channel";
-    static final String CONFIG_FILE_PROP = "ipcConfigFile";
-
-    private static final int HOSTNAME_ARG_POS = 0;
-    private static final int HOSTPORT_ARG_POS = 1;
-    private static final int PARENT_PID_ARG_POS = 2;
-
-    private static SSLConfigurationParser sslConfParser = new SSLConfigurationParser();
-    private static ServerCreator serverCreator = new ServerCreator();
-    private static ShutdownHookHandler shutdownHandler = new ShutdownHookHandler();
-    private static Sleeper sleeper = new Sleeper();
-    private static CommandChannelServerImpl impl = null;
-    private static ClientIPCService ipcService = null;
-
-    // TODO Add some keep alive check
-    public static void main(String[] args) throws IOException {
-        if (args.length != 2 && args.length != 3) {
-            throw new IOException("usage: thermostat-command-channel <hostname> <port> [<parent pid>]");
-        }
-        String hostname = args[HOSTNAME_ARG_POS];
-        Integer port;
-        try {
-            port = Integer.valueOf(args[HOSTPORT_ARG_POS]);
-        } catch (NumberFormatException e) {
-            throw new IOException("Port number must be a valid integer");
-        }
-
-        // Windows named pipes has some native code - must set paths to find the DLL
-        if (OS.IS_WINDOWS) {
-            NativeLibraryResolver.setCommonPaths(new CommonPathsImpl());
-        }
-        
-        // Get IPC configuration file location from system property
-        String configFileStr = System.getProperty(CONFIG_FILE_PROP);
-        if (configFileStr == null) {
-            throw new IOException("Unknown IPC configuration file location");
-        }
-        File configFile = new File(configFileStr);
-        if (ipcService == null) { // Only non-null for testing
-            ipcService = ClientIPCServiceFactory.getIPCService(configFile);
-        }
-        // Connect to IPC server
-        IPCMessageChannel channel = ipcService.connectToServer(IPC_SERVER_NAME);
-
-
-        // if there's a parent pid, watch for it to exit and then shutdown.
-        final int parentPid = (args.length == 3) ? Integer.parseInt(args[PARENT_PID_ARG_POS]) : 0;
-        final int SLEEP_TIME_MS = 5000; // 5 seconds between checks
-        if (parentPid > 0) {
-            final ProcessWatcher watcher = new ProcessWatcher(parentPid, SLEEP_TIME_MS) {
-                @Override
-                public void onProcessExit() {
-                    // tell myself to exit
-                    System.exit(1);
-                }
-            };
-            watcher.start();
-        }
-
-        try {
-            // Notify server has started
-            sendMessage(channel, CommandChannelConstants.SERVER_STARTED_TOKEN);
-        
-            SSLConfiguration config = sslConfParser.parseSSLConfiguration(channel);
-            
-            impl = serverCreator.createServer(config, channel);
-            
-            // Start listening on server
-            impl.startListening(hostname, port);
-            // Notify server is ready to accept requests
-            sendMessage(channel, CommandChannelConstants.SERVER_READY_TOKEN);
-            
-            shutdownHandler.addShutdownHook(new Thread(new Runnable() {
-                @Override
-                public void run() {
-                    impl.stopListening();
-                }
-            }));
-            
-            sleeper.sleepWait();
-        } catch (IOException e) {
-            // Shut down server
-            if (impl != null) {
-                impl.stopListening();
-            }
-            throw new IOException("Failed to start command channel server", e);
-        } finally {
-            channel.close();
-        }
-    }
-    
-    private static void sendMessage(IPCMessageChannel channel, byte[] message) throws IOException {
-        // Don't interleave with other messages or requests
-        synchronized (channel) {
-            ByteBuffer buf = ByteBuffer.wrap(message);
-            channel.writeMessage(buf);
-        }
-    }
-    
-    static class ServerCreator {
-        CommandChannelServerImpl createServer(SSLConfiguration sslConf, IPCMessageChannel agentChannel) {
-            CommandChannelServerContext ctx = new CommandChannelServerContext(sslConf, agentChannel);
-            return new CommandChannelServerImpl(ctx);
-        }
-    }
-    
-    static class ShutdownHookHandler {
-        void addShutdownHook(Thread hook) {
-            Runtime.getRuntime().addShutdownHook(hook);
-        }
-    }
-    
-    static class Sleeper {
-        void sleepWait() {
-            while (!Thread.interrupted()) {
-                try {
-                    Thread.sleep(1000);
-                } catch (InterruptedException e) {
-                    Thread.currentThread().interrupt();
-                }
-            }
-        }
-    }
-    
-    /* For testing purposes only */
-    static void setSSLConfigurationParser(SSLConfigurationParser parser) {
-        CommandChannelServerMain.sslConfParser = parser;
-    }
-    
-    /* For testing purposes only */
-    static void setServerCreator(ServerCreator creator) {
-        CommandChannelServerMain.serverCreator = creator;
-    }
-    
-    /* For testing purposes only */
-    static void setShutdownHookHandler(ShutdownHookHandler handler) {
-        CommandChannelServerMain.shutdownHandler = handler;
-    }
-    
-    /* For testing purposes only */
-    static void setSleeper(Sleeper sleeper) {
-        CommandChannelServerMain.sleeper = sleeper;
-    }
-    
-    /* For testing purposes only */
-    static void setIPCService(ClientIPCService ipcService) {
-        CommandChannelServerMain.ipcService = ipcService;
-    }
-}
--- a/agent/command-server/src/main/java/com/redhat/thermostat/agent/command/server/internal/JsonRequestEncoder.java	Tue Jun 13 12:02:12 2017 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,89 +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
- * <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.command.server.internal;
-
-import java.io.IOException;
-import java.net.InetSocketAddress;
-import java.nio.ByteBuffer;
-import java.nio.charset.Charset;
-
-import com.google.gson.Gson;
-import com.google.gson.GsonBuilder;
-import com.google.gson.JsonObject;
-import com.redhat.thermostat.agent.ipc.client.IPCMessageChannel;
-import com.redhat.thermostat.common.command.Request;
-import com.redhat.thermostat.common.command.Request.RequestType;
-
-// Encodes a Request as JSON and sends the result to the agent
-class JsonRequestEncoder {
-
-    void encodeRequestAndSend(IPCMessageChannel agentChannel, Request request) throws IOException {
-        GsonBuilder builder = new GsonBuilder();
-        builder.serializeNulls(); // In case null parameter values are permitted
-        Gson gson = builder.create();
-        
-        JsonObject requestObj = new JsonObject();
-        InetSocketAddress addr = request.getTarget();
-        requestObj.addProperty(CommandChannelConstants.REQUEST_JSON_HOST, addr.getHostString());
-        requestObj.addProperty(CommandChannelConstants.REQUEST_JSON_PORT, addr.getPort());
-        
-        // CommandChannelRequestDecoder ensures this is a safe cast
-        RequestType type = (RequestType) request.getType();
-        requestObj.addProperty(CommandChannelConstants.REQUEST_JSON_TYPE, type.name());
-        
-        JsonObject paramsObj = new JsonObject();
-        for (String param : request.getParameterNames()) {
-            // Don't allow null parameter names
-            if (param == null) {
-                throw new IOException("Null parameter names are not allowed");
-            }
-            String value = request.getParameter(param);
-            paramsObj.addProperty(param, value);
-        }
-        requestObj.add(CommandChannelConstants.REQUEST_JSON_PARAMS, paramsObj);
-        JsonObject requestRoot = new JsonObject();
-        requestRoot.add(CommandChannelConstants.REQUEST_JSON_TOP, requestObj);
-        
-        String jsonRequest = gson.toJson(requestRoot);
-        byte[] jsonRequestBytes = jsonRequest.getBytes(Charset.forName("UTF-8"));
-        
-        // Write request
-        ByteBuffer buf = ByteBuffer.wrap(jsonRequestBytes);
-        agentChannel.writeMessage(buf);
-    }
-}
-
--- a/agent/command-server/src/main/java/com/redhat/thermostat/agent/command/server/internal/JsonResponseParser.java	Tue Jun 13 12:02:12 2017 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,107 +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
- * <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.command.server.internal;
-
-import java.io.IOException;
-import java.nio.ByteBuffer;
-import java.nio.charset.Charset;
-
-import com.google.gson.Gson;
-import com.google.gson.GsonBuilder;
-import com.google.gson.JsonElement;
-import com.google.gson.JsonObject;
-import com.google.gson.JsonParseException;
-import com.google.gson.JsonParser;
-import com.redhat.thermostat.agent.ipc.client.IPCMessageChannel;
-import com.redhat.thermostat.common.command.Response;
-import com.redhat.thermostat.common.command.Response.ResponseType;
-
-// Parses a Response encoded as JSON received from the agent
-class JsonResponseParser {
-    
-    Response parseResponse(IPCMessageChannel agentChannel) throws IOException {
-        ByteBuffer buf = agentChannel.readMessage();
-        byte[] jsonResponse = new byte[buf.remaining()];
-        buf.get(jsonResponse);
-                
-        GsonBuilder builder = new GsonBuilder();
-        Gson gson = builder.create();
-        JsonParser parser = new JsonParser();
-        
-        try {
-            // Get root of JsonObject tree
-            String jsonResponseString = new String(jsonResponse, Charset.forName("UTF-8"));
-            JsonElement rootElement = parser.parse(jsonResponseString);
-            requireNonNull(rootElement, "Entire response missing");
-            JsonObject rootObj = toJsonObject(rootElement);
-            
-            JsonElement responseElement = rootObj.get(CommandChannelConstants.RESPONSE_JSON_TOP);
-            requireNonNull(responseElement, "Response data missing");
-            JsonObject responseObj = toJsonObject(responseElement);
-            
-            // Get response type
-            JsonElement typeElement = responseObj.get(CommandChannelConstants.RESPONSE_JSON_TYPE);
-            requireNonNull(typeElement, "Response type missing");
-            String typeString = gson.fromJson(typeElement, String.class);
-            
-            // Get enum value
-            ResponseType type;
-            try {
-                type = ResponseType.valueOf(typeString);
-            } catch (IllegalArgumentException e) {
-                throw new IOException("Invalid ResponseType: " + typeString);
-            }
-            
-            return new Response(type);
-        } catch (JsonParseException e) {
-            throw new IOException("Failed to parse response from agent", e);
-        }
-    }
-
-    private void requireNonNull(JsonElement element, String errorMessage) throws IOException {
-        if (element == null || element.isJsonNull()) {
-            throw new IOException(errorMessage);
-        }
-    }
-    
-    private JsonObject toJsonObject(JsonElement element) throws IOException {
-        if (!element.isJsonObject()) {
-            throw new IOException("Malformed data received");
-        }
-        return element.getAsJsonObject();
-    }
-}
--- a/agent/command-server/src/main/java/com/redhat/thermostat/agent/command/server/internal/ResponseEncoder.java	Tue Jun 13 12:02:12 2017 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,74 +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
- * <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.command.server.internal;
-
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-import com.redhat.thermostat.common.command.Message;
-import com.redhat.thermostat.common.command.Response;
-import com.redhat.thermostat.common.command.noapi.EncodingHelper;
-import com.redhat.thermostat.common.command.noapi.MessageEncoder;
-import com.redhat.thermostat.common.utils.LoggingUtils;
-
-import io.netty.buffer.ByteBuf;
-
-
-class ResponseEncoder extends MessageEncoder {
-
-    private static final Logger logger = LoggingUtils.getLogger(ResponseEncoder.class);
-    
-    /*
-     * See javadoc of Response for a description of the encoding.
-     */
-    @Override
-    protected ByteBuf encode(Message msg) {
-        // At this point we are only getting Messages. Since our only
-        // registered MessageEncoder is the one for Responses a cast
-        // to Response should be safe.
-        logger.log(Level.FINEST, "Command channel server: encoding Response object");
-        Response response = (Response) msg;
-
-        // Response Type
-        String responseType = EncodingHelper.trimType(response.getType().toString());
-        ByteBuf typeBuffer = EncodingHelper.encode(responseType);
-
-        return typeBuffer;
-    }
-
-}
-
--- a/agent/command-server/src/main/java/com/redhat/thermostat/agent/command/server/internal/SSLConfigurationParser.java	Tue Jun 13 12:02:12 2017 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,125 +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
- * <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.command.server.internal;
-
-import java.io.File;
-import java.io.IOException;
-import java.nio.ByteBuffer;
-import java.nio.charset.Charset;
-
-import com.google.gson.Gson;
-import com.google.gson.GsonBuilder;
-import com.google.gson.JsonElement;
-import com.google.gson.JsonObject;
-import com.google.gson.JsonParseException;
-import com.google.gson.JsonParser;
-import com.redhat.thermostat.agent.ipc.client.IPCMessageChannel;
-import com.redhat.thermostat.shared.config.SSLConfiguration;
-
-class SSLConfigurationParser {
-    
-    SSLConfiguration parseSSLConfiguration(IPCMessageChannel channel) throws IOException {
-        ByteBuffer buf = channel.readMessage();
-        byte[] jsonSslConf = new byte[buf.remaining()];
-        buf.get(jsonSslConf);
-        
-        GsonBuilder builder = new GsonBuilder();
-        Gson gson = builder.create();
-        JsonParser parser = new JsonParser();
-        
-        try {
-            String jsonSslConfString = new String(jsonSslConf, Charset.forName("UTF-8"));
-            JsonElement parsed = parser.parse(jsonSslConfString);
-            requireNonNull(parsed, "Entire SSL configuration missing");
-            JsonObject root = toJsonObject(parsed);
-            
-            JsonElement sslConfigElement = root.get(CommandChannelConstants.SSL_JSON_ROOT);
-            requireNonNull(sslConfigElement, "SSL configuration parameters missing");
-            JsonObject sslConfigObj = toJsonObject(sslConfigElement);
-            
-            // Construct SSLConfiguration from parameters
-            String keystorePath = getStringOrNull(gson, sslConfigObj, CommandChannelConstants.SSL_JSON_KEYSTORE_FILE);
-            File keystoreFile = null;
-            if (keystorePath != null) {
-                keystoreFile = new File(keystorePath);
-            }
-            String keystorePass = getStringOrNull(gson, sslConfigObj, CommandChannelConstants.SSL_JSON_KEYSTORE_PASS);
-            boolean cmdChannel = getBoolean(gson, sslConfigObj, CommandChannelConstants.SSL_JSON_COMMAND_CHANNEL);
-            boolean backingStorage = getBoolean(gson, sslConfigObj, CommandChannelConstants.SSL_JSON_BACKING_STORAGE);
-            boolean disableVerification = getBoolean(gson, sslConfigObj, CommandChannelConstants.SSL_JSON_HOSTNAME_VERIFICATION);
-            
-            return new CommandChannelSSLConfiguration(keystoreFile, keystorePass, cmdChannel, 
-                    backingStorage, disableVerification);
-        } catch (JsonParseException e) {
-            throw new IOException("Invalid encoded SSL configuration", e);
-        }
-    }
-    
-    private String getStringOrNull(Gson gson, JsonObject sslConfigObj, String member) throws IOException {
-        String value = null;
-        // May be null value, but parameter must be specified
-        if (!sslConfigObj.has(member)) {
-            throw new IOException(member + " parameter missing");
-        }
-        JsonElement element = sslConfigObj.get(member);
-        if (element != null) {
-            value = gson.fromJson(element, String.class);
-        }
-        return value;
-    }
-
-    private boolean getBoolean(Gson gson, JsonObject sslConfigObj, String member) throws IOException {
-        JsonElement element = sslConfigObj.get(member);
-        requireNonNull(element, member + " parameter missing");
-        boolean value = gson.fromJson(element, Boolean.class);
-        return value;
-    }
-    
-    private void requireNonNull(JsonElement element, String errorMessage) throws IOException {
-        if (element == null || element.isJsonNull()) {
-            throw new IOException(errorMessage);
-        }
-    }
-    
-    private JsonObject toJsonObject(JsonElement element) throws IOException {
-        if (!element.isJsonObject()) {
-            throw new IOException("Malformed data received");
-        }
-        return element.getAsJsonObject();
-    }
-
-}
--- a/agent/command-server/src/main/java/com/redhat/thermostat/agent/command/server/internal/ServerHandler.java	Tue Jun 13 12:02:12 2017 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,162 +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
- * <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.command.server.internal;
-
-import java.io.IOException;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-import com.redhat.thermostat.agent.ipc.client.IPCMessageChannel;
-import com.redhat.thermostat.common.command.Message.MessageType;
-import com.redhat.thermostat.common.command.Request;
-import com.redhat.thermostat.common.command.Response;
-import com.redhat.thermostat.common.command.Response.ResponseType;
-import com.redhat.thermostat.common.utils.LoggingUtils;
-import com.redhat.thermostat.shared.config.SSLConfiguration;
-
-import io.netty.channel.Channel;
-import io.netty.channel.ChannelHandlerContext;
-import io.netty.channel.SimpleChannelInboundHandler;
-import io.netty.handler.ssl.SslHandler;
-import io.netty.util.concurrent.Future;
-import io.netty.util.concurrent.GenericFutureListener;
-
-class ServerHandler extends SimpleChannelInboundHandler<Request> {
-
-    private static final Logger logger = LoggingUtils.getLogger(ServerHandler.class);
-    
-    private final SSLConfiguration sslConf;
-    private final IPCMessageChannel agentChannel;
-    private final JsonRequestEncoder requestEncoder;
-    private final JsonResponseParser responseParser;
-    
-    ServerHandler(SSLConfiguration sslConf, IPCMessageChannel agentChannel) {
-        this(sslConf, agentChannel, new JsonRequestEncoder(), new JsonResponseParser());
-    }
-    
-    ServerHandler(SSLConfiguration sslConf, IPCMessageChannel agentChannel, JsonRequestEncoder requestEncoder, JsonResponseParser responseParser) {
-        this.sslConf = sslConf;
-        this.agentChannel = agentChannel;
-        this.requestEncoder = requestEncoder;
-        this.responseParser = responseParser;
-    }
-    
-    @Override
-    public void channelActive(ChannelHandlerContext ctx) throws Exception {
-        logger.log(Level.FINEST, "Channel active!");
-        if (sslConf.enableForCmdChannel()) {
-            // Get the SslHandler in the current pipeline.
-            // We added it in ConfigurationServerContext$ServerPipelineFactory.
-            final SslHandler sslHandler = ctx.pipeline().get(
-                    SslHandler.class);
-
-            // Get notified when SSL handshake is done.
-            Future<Channel> handshakeFuture = sslHandler.handshakeFuture();
-            handshakeFuture.addListener(new SSLHandshakeDoneListener(ctx));
-        }
-    }
-    
-    /*
-     * 1. Read request from command channel
-     * 2. Write request to agent encoded as JSON
-     * 3. Wait on agent for response (maybe add timeout here)
-     * 4. Read JSON-encoded response from agent
-     * 5. Write response to command channel
-     */
-    @Override
-    protected void channelRead0(ChannelHandlerContext ctx, Request request)
-            throws Exception {
-        Response response;
-        String receiverName = request.getReceiver();
-        MessageType requestType = request.getType();
-        if (requestType == null || receiverName == null) {
-            logger.warning("Invalid Request");
-            response = new Response(ResponseType.ERROR);
-        } else {
-            // Reading/writing to agent should be synchronized
-            logger.info("Request received: '" + requestType + "' for '" + receiverName + "'");
-            synchronized (agentChannel) {
-                try {
-                    // Ensure channel is still open
-                    if (!agentChannel.isOpen()) {
-                        throw new IOException("Communication channel with agent is closed");
-                    }
-                    requestEncoder.encodeRequestAndSend(agentChannel, request);
-                    response = responseParser.parseResponse(agentChannel);
-                } catch (IOException ex) {
-                    logger.log(Level.WARNING, "Failed to communicate with agent", ex);
-                    response = new Response(ResponseType.ERROR);
-                }
-            }
-        }
-        
-        Channel channel = ctx.channel();
-        logger.info("Sending response: " + response.getType().toString());
-        channel.pipeline().writeAndFlush(response);
-        ctx.close();
-    }
-
-    @Override
-    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
-        logger.log(Level.WARNING, "Unexpected exception from downstream.", cause);
-        ctx.close();
-    }
-    
-    /*
-     * Only registered if SSL is enabled
-     */
-    static final class SSLHandshakeDoneListener implements GenericFutureListener<Future<Channel>> {
-
-        private final ChannelHandlerContext ctx;
-        
-        SSLHandshakeDoneListener(ChannelHandlerContext ctx) {
-            this.ctx = ctx;
-        }
-        
-        @Override
-        public void operationComplete(Future<Channel> future) throws Exception {
-            if (future.isSuccess()) {
-                logger.log(Level.FINE, "Finished SSL handshake.");
-            } else {
-                logger.log(Level.WARNING, "SSL handshake failed!");
-                ctx.close();
-            }
-        }
-    }
-
-}
-
--- a/agent/command-server/src/test/java/com/redhat/thermostat/agent/command/server/internal/CommandChannelRequestDecoderTest.java	Tue Jun 13 12:02:12 2017 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,580 +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
- * <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.command.server.internal;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-import java.net.InetSocketAddress;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.List;
-
-import org.junit.Before;
-import org.junit.Test;
-
-import com.redhat.thermostat.common.command.Message;
-import com.redhat.thermostat.common.command.Messages;
-import com.redhat.thermostat.common.command.Request;
-import com.redhat.thermostat.common.command.Request.RequestType;
-
-import io.netty.buffer.ByteBuf;
-import io.netty.buffer.Unpooled;
-import io.netty.channel.Channel;
-import io.netty.channel.ChannelHandlerContext;
-
-public class CommandChannelRequestDecoderTest {
-    
-    private static final String LONG_PARAM_VALUE = "RULE count sleep method invocations\n" +
-            "CLASS com.redhat.thermostat.byteman.test.InfiniteLoop\n" +
-            "METHOD sleep\n" +
-            "AT ENTRY\n" +
-            "IF true\n" +
-            "DO\n" +
-            "incrementCounter(\"sleep-counter\")\n" +
-            "ENDRULE\n" +
-            "\n" +
-            "RULE send sleep method invocations to thermostat\n" +
-            "CLASS com.redhat.thermostat.byteman.test.InfiniteLoop\n" +
-            "METHOD sleep\n" +
-            "HELPER org.jboss.byteman.thermostat.helper.ThermostatHelper\n" +
-            "AT ENTRY\n" +
-            "BIND counterValue: int = readCounter(\"sleep-counter\")\n" +
-            "IF counterValue % 10 == 0\n" +
-            "DO\n" +
-            "send(\"com.redhat.thermostat.byteman.test.InfiniteLoop\",  \"method sleep() count\", counterValue);\n" +
-            "ENDRULE\n";
-    
-    /*
-     * This is serialized format for
-     * req = new Request(RequestType.RESPONSE_EXPECTED, blah);
-     * req.setParameter("param1", "value1");
-     * req.setParameter("param2", "value2");
-     */
-    private static final byte[] ENCODED_REQUEST_WITH_PARAMS = new byte[] {
-        0x00, 0x00, 0x00, 0x11, 0x52, 0x45, 0x53, 0x50, 0x4f, 0x4e, 0x53, 0x45,
-        0x5f, 0x45, 0x58, 0x50, 0x45, 0x43, 0x54, 0x45, 0x44, 0x00, 0x00, 0x00,
-        0x02, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x70, 0x61, 0x72,
-        0x61, 0x6d, 0x31, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x31, 0x00, 0x00, 0x00,
-        0x06, 0x00, 0x00, 0x00, 0x06, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x32, 0x76,
-        0x61, 0x6c, 0x75, 0x65, 0x32
-    };
-    
-    /*
-     * This is serialized format for:
-     * 
-     * Request req = new Request(RequestType.RESPONSE_EXPECTED, blah);
-     * req.setReceiver("com.redhat.thermostat.vm.byteman.agent.internal.BytemanRequestReceiver");
-     * req.setParameter("action-name", "vm-byteman-instrument");
-     * req.setParameter("vm-id", "f5b4cc44-06f2-4847-97d3-b5b0b198f843");
-     * req.setParameter("byteman-action", Integer.toString(0));
-     * req.setParameter("listen-port", Integer.toString(listenPort));
-     * req.setParameter("auth-token", "/UFDv9PT3nLMW1lEJUyTxfIHfU8bRxcAw4IdVYL7JzHROruzNvIpuvqrLUN01f0hGIGMmzc58iylrbpgY1wkUA04YL5WOF66ysfwLWwDA8M2R7khrK0xY6+DLSA1nOUT+ihmvGsrlk51x5aqH7Vvqqj+TDyj8Z3Gl3dMp6KEngtBxxhxO/pI9euQGUiwWGcY7CdTAoTSx2wh8bOsmDUllwOE7978ZhTRsSN0bd57eynDaae23TwInhez5iHOxuWlByjEv4RyDPSc0fASoKwH7IAj7VYMzuj72/c9xLOA54XuyruaMdqi0zlA4v/af7xJ90XVMWdW96EGBGkIlo33fA==");
-     * req.setParameter("client-token", "MdJ08lQ4+4H9asOQaYxV6XDXbICi3njI7JmZ9W8WgN7LW8RIYZ0aCFpaHQIHsG38mOLvZRzLNwI45NG/qoxVu+BA8ekEBpLlHCotxLk+1F7yZonmkEfXq6o4imsVU/8EU36Dh7YRd5qOnerJ7NItrw/kBuVaAlsVL2Bbj7ryvVVE01GgMJlBLQjnjb21eO2yg2w4AKDOggHMwcCAaZl1wW1pKQJAvlT0FYHrwSUJQ51JrZEUJjFxZ38xxYHA0HMffUiDczqkFj08w59G/652JY/hWpoM9uzwZP+tV4sCWXFF5IuBB8PFwaeqgXDwrbxgYMjCgk9nNZuvdsdaV1bGRQ==");
-     * req.setParameter("byteman-rule", LONG_PARAM_VALUE);
-     */
-    private static final byte[] ENCODED_REQUEST_WITH_MANY_PARAMS = new byte[] {
-        0x00, 0x00, 0x00, 0x11, 0x52, 0x45, 0x53, 0x50, 0x4f, 0x4e, 0x53, 0x45,
-        0x5f, 0x45, 0x58, 0x50, 0x45, 0x43, 0x54, 0x45, 0x44,
-        0x00, 0x00, 0x00, 0x08, // 8 parameters, slice A end
-        0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x15, 0x61, 0x63, 0x74, 0x69,
-        0x6f, 0x6e, 0x2d, 0x6e, 0x61, 0x6d, 0x65, 0x76, 0x6d, 0x2d, 0x62, 0x79,
-        0x74, 0x65, 0x6d, 0x61, 0x6e, 0x2d, 0x69, 0x6e, 0x73, 0x74, 0x72, 0x75,
-        0x6d, 0x65, 0x6e, 0x74, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x01, 0x58,
-        0x61, 0x75, 0x74, 0x68, 0x2d, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x2f, 0x55,
-        0x46, 0x44, 0x76, 0x39, 0x50, 0x54, 0x33, 0x6e, 0x4c, 0x4d, 0x57, 0x31,
-        0x6c, 0x45, 0x4a, 0x55, 0x79, 0x54, 0x78, 0x66, 0x49, 0x48, 0x66, 0x55,
-        0x38, 0x62, 0x52, 0x78, 0x63, 0x41, 0x77, 0x34, 0x49, 0x64, 0x56, 0x59,
-        0x4c, 0x37, 0x4a, 0x7a, 0x48, 0x52, 0x4f, 0x72, 0x75, 0x7a, 0x4e, 0x76,
-        0x49, 0x70, 0x75, 0x76, 0x71, 0x72, 0x4c, 0x55, 0x4e, 0x30, 0x31, 0x66,
-        0x30, 0x68, 0x47, 0x49, 0x47, 0x4d, 0x6d, 0x7a, 0x63, 0x35, 0x38, 0x69,
-        0x79, 0x6c, 0x72, 0x62, 0x70, 0x67, 0x59, 0x31, 0x77, 0x6b, 0x55, 0x41,
-        0x30, 0x34, 0x59, 0x4c, 0x35, 0x57, 0x4f, 0x46, 0x36, 0x36, 0x79, 0x73,
-        0x66, 0x77, 0x4c, 0x57, 0x77, 0x44, 0x41, 0x38, 0x4d, 0x32, 0x52, 0x37,
-        0x6b, 0x68, 0x72, 0x4b, 0x30, 0x78, 0x59, 0x36, 0x2b, 0x44, 0x4c, 0x53,
-        0x41, 0x31, 0x6e, 0x4f, 0x55, 0x54, 0x2b, 0x69, 0x68, 0x6d, 0x76, 0x47,
-        0x73, 0x72, 0x6c, 0x6b, 0x35, 0x31, 0x78, 0x35, 0x61, 0x71, 0x48, 0x37,
-        0x56, 0x76, 0x71, 0x71, 0x6a, 0x2b, 0x54, 0x44, 0x79, 0x6a, 0x38, 0x5a,
-        0x33, 0x47, 0x6c, 0x33, 0x64, 0x4d, 0x70, 0x36, 0x4b, 0x45, 0x6e, 0x67,
-        0x74, 0x42, 0x78, 0x78, 0x68, 0x78, 0x4f, 0x2f, 0x70, 0x49, 0x39, 0x65,
-        0x75, 0x51, 0x47, 0x55, 0x69, 0x77, 0x57, 0x47, 0x63, 0x59, 0x37, 0x43,
-        0x64, 0x54, 0x41, 0x6f, 0x54, 0x53, 0x78, 0x32, 0x77, 0x68, 0x38, 0x62,
-        0x4f, 0x73, 0x6d, 0x44, 0x55, 0x6c, 0x6c, 0x77, 0x4f, 0x45, 0x37, 0x39,
-        0x37, 0x38, 0x5a, 0x68, 0x54, 0x52, 0x73, 0x53, 0x4e, 0x30, 0x62, 0x64,
-        0x35, 0x37, 0x65, 0x79, 0x6e, 0x44, 0x61, 0x61, 0x65, 0x32, 0x33, 0x54,
-        0x77, 0x49, 0x6e, 0x68, 0x65, 0x7a, 0x35, 0x69, 0x48, 0x4f, 0x78, 0x75,
-        0x57, 0x6c, 0x42, 0x79, 0x6a, 0x45, 0x76, 0x34, 0x52, 0x79, 0x44, 0x50,
-        0x53, 0x63, 0x30, 0x66, 0x41, 0x53, 0x6f, 0x4b, 0x77, 0x48, 0x37, 0x49,
-        0x41, 0x6a, 0x37, 0x56, 0x59, 0x4d, 0x7a, 0x75, 0x6a, 0x37, 0x32, 0x2f,
-        0x63, 0x39, 0x78, 0x4c, 0x4f, 0x41, 0x35, 0x34, 0x58, 0x75, 0x79, 0x72,
-        0x75, 0x61, 0x4d, 0x64, 0x71, 0x69, 0x30, 0x7a, 0x6c, 0x41, 0x34, 0x76,
-        0x2f, 0x61, 0x66, 0x37, 0x78, 0x4a, 0x39, 0x30, 0x58, 0x56, 0x4d, 0x57,
-        0x64, 0x57, 0x39, 0x36, 0x45, 0x47, 0x42, 0x47, 0x6b, 0x49, 0x6c, 0x6f,
-        0x33, 0x33, 0x66, 0x41, 0x3d, 0x3d, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00,
-        0x00, 0x01, 0x62, 0x79, 0x74, 0x65, 0x6d, 0x61, 0x6e, 0x2d, 0x61, 0x63,
-        0x74, 0x69, 0x6f, 0x6e, 0x30, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x02,
-        0x1a, 0x62, 0x79, 0x74, 0x65, 0x6d, 0x61, 0x6e, 0x2d, 0x72, 0x75, 0x6c,
-        0x65, 0x52, 0x55, 0x4c, 0x45, 0x20, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x20,
-        0x73, 0x6c, 0x65, 0x65, 0x70, 0x20, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64,
-        0x20, 0x69, 0x6e, 0x76, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73,
-        0x0a, 0x43, 0x4c, 0x41, 0x53, 0x53, 0x20, 0x63, 0x6f, 0x6d, 0x2e, 0x72,
-        0x65, 0x64, 0x68, 0x61, 0x74, 0x2e, 0x74, 0x68, 0x65, 0x72, 0x6d, 0x6f,
-        0x73, 0x74, 0x61, 0x74, 0x2e, 0x62, 0x79, 0x74, 0x65, 0x6d, 0x61, 0x6e,
-        0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x49, 0x6e, 0x66, 0x69, 0x6e, 0x69,
-        0x74, 0x65, 0x4c, 0x6f, 0x6f, 0x70, 0x0a, 0x4d, 0x45, 0x54, 0x48, 0x4f,
-        0x44, 0x20, 0x73, 0x6c, 0x65, 0x65, 0x70, 0x0a, 0x41, 0x54, 0x20, 0x45,
-        0x4e, 0x54, 0x52, 0x59, 0x0a, 0x49, 0x46, 0x20, 0x74, 0x72, 0x75, 0x65,
-        0x0a, 0x44, 0x4f, 0x0a, 0x69, 0x6e, 0x63, 0x72, 0x65, 0x6d, 0x65, 0x6e,
-        0x74, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x28, 0x22, 0x73, 0x6c,
-        0x65, 0x65, 0x70, 0x2d, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x22,
-        0x29, 0x0a, 0x45, 0x4e, 0x44, 0x52, 0x55, 0x4c, 0x45, 0x0a, 0x0a, 0x52,
-        0x55, 0x4c, 0x45, 0x20, 0x73, 0x65, 0x6e, 0x64, 0x20, 0x73, 0x6c, 0x65,
-        0x65, 0x70, 0x20, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x20, 0x69, 0x6e,
-        0x76, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x20, 0x74, 0x6f,
-        0x20, 0x74, 0x68, 0x65, 0x72, 0x6d, 0x6f, 0x73, 0x74, 0x61, 0x74, 0x0a,
-        0x43, 0x4c, 0x41, 0x53, 0x53, 0x20, 0x63, 0x6f, 0x6d, 0x2e, 0x72, 0x65,
-        0x64, 0x68, 0x61, 0x74, 0x2e, 0x74, 0x68, 0x65, 0x72, 0x6d, 0x6f, 0x73,
-        0x74, 0x61, 0x74, 0x2e, 0x62, 0x79, 0x74, 0x65, 0x6d, 0x61, 0x6e, 0x2e,
-        0x74, 0x65, 0x73, 0x74, 0x2e, 0x49, 0x6e, 0x66, 0x69, 0x6e, 0x69, 0x74,
-        0x65, 0x4c, 0x6f, 0x6f, 0x70, 0x0a, 0x4d, 0x45, 0x54, 0x48, 0x4f, 0x44, // slice B end
-        0x20, 0x73, 0x6c, 0x65, 0x65, 0x70, 0x0a, 0x48, 0x45, 0x4c, 0x50, 0x45,
-        0x52, 0x20, 0x6f, 0x72, 0x67, 0x2e, 0x6a, 0x62, 0x6f, 0x73, 0x73, 0x2e,
-        0x62, 0x79, 0x74, 0x65, 0x6d, 0x61, 0x6e, 0x2e, 0x74, 0x68, 0x65, 0x72,
-        0x6d, 0x6f, 0x73, 0x74, 0x61, 0x74, 0x2e, 0x68, 0x65, 0x6c, 0x70, 0x65,
-        0x72, 0x2e, 0x54, 0x68, 0x65, 0x72, 0x6d, 0x6f, 0x73, 0x74, 0x61, 0x74,
-        0x48, 0x65, 0x6c, 0x70, 0x65, 0x72, 0x0a, 0x41, 0x54, 0x20, 0x45, 0x4e,
-        0x54, 0x52, 0x59, 0x0a, 0x42, 0x49, 0x4e, 0x44, 0x20, 0x63, 0x6f, 0x75,
-        0x6e, 0x74, 0x65, 0x72, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x20, 0x69,
-        0x6e, 0x74, 0x20, 0x3d, 0x20, 0x72, 0x65, 0x61, 0x64, 0x43, 0x6f, 0x75,
-        0x6e, 0x74, 0x65, 0x72, 0x28, 0x22, 0x73, 0x6c, 0x65, 0x65, 0x70, 0x2d,
-        0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x22, 0x29, 0x0a, 0x49, 0x46,
-        0x20, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x56, 0x61, 0x6c, 0x75,
-        0x65, 0x20, 0x25, 0x20, 0x31, 0x30, 0x20, 0x3d, 0x3d, 0x20, 0x30, 0x0a,
-        0x44, 0x4f, 0x0a, 0x73, 0x65, 0x6e, 0x64, 0x28, 0x22, 0x63, 0x6f, 0x6d,
-        0x2e, 0x72, 0x65, 0x64, 0x68, 0x61, 0x74, 0x2e, 0x74, 0x68, 0x65, 0x72,
-        0x6d, 0x6f, 0x73, 0x74, 0x61, 0x74, 0x2e, 0x62, 0x79, 0x74, 0x65, 0x6d,
-        0x61, 0x6e, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x49, 0x6e, 0x66, 0x69,
-        0x6e, 0x69, 0x74, 0x65, 0x4c, 0x6f, 0x6f, 0x70, 0x22, 0x2c, 0x20, 0x20,
-        0x22, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x20, 0x73, 0x6c, 0x65, 0x65,
-        0x70, 0x28, 0x29, 0x20, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0x2c, 0x20,
-        0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x56, 0x61, 0x6c, 0x75, 0x65,
-        0x29, 0x3b, 0x0a, 0x45, 0x4e, 0x44, 0x52, 0x55, 0x4c, 0x45, 0x0a, 0x00,
-        0x00, 0x00, 0x0c, 0x00, 0x00, 0x01, 0x58, 0x63, 0x6c, 0x69, 0x65, 0x6e,
-        0x74, 0x2d, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x4d, 0x64, 0x4a, 0x30, 0x38,
-        0x6c, 0x51, 0x34, 0x2b, 0x34, 0x48, 0x39, 0x61, 0x73, 0x4f, 0x51, 0x61,
-        0x59, 0x78, 0x56, 0x36, 0x58, 0x44, 0x58, 0x62, 0x49, 0x43, 0x69, 0x33,
-        0x6e, 0x6a, 0x49, 0x37, 0x4a, 0x6d, 0x5a, 0x39, 0x57, 0x38, 0x57, 0x67,
-        0x4e, 0x37, 0x4c, 0x57, 0x38, 0x52, 0x49, 0x59, 0x5a, 0x30, 0x61, 0x43,
-        0x46, 0x70, 0x61, 0x48, 0x51, 0x49, 0x48, 0x73, 0x47, 0x33, 0x38, 0x6d,
-        0x4f, 0x4c, 0x76, 0x5a, 0x52, 0x7a, 0x4c, 0x4e, 0x77, 0x49, 0x34, 0x35,
-        0x4e, 0x47, 0x2f, 0x71, 0x6f, 0x78, 0x56, 0x75, 0x2b, 0x42, 0x41, 0x38,
-        0x65, 0x6b, 0x45, 0x42, 0x70, 0x4c, 0x6c, 0x48, 0x43, 0x6f, 0x74, 0x78,
-        0x4c, 0x6b, 0x2b, 0x31, 0x46, 0x37, 0x79, 0x5a, 0x6f, 0x6e, 0x6d, 0x6b,
-        0x45, 0x66, 0x58, 0x71, 0x36, 0x6f, 0x34, 0x69, 0x6d, 0x73, 0x56, 0x55,
-        0x2f, 0x38, 0x45, 0x55, 0x33, 0x36, 0x44, 0x68, 0x37, 0x59, 0x52, 0x64,
-        0x35, 0x71, 0x4f, 0x6e, 0x65, 0x72, 0x4a, 0x37, 0x4e, 0x49, 0x74, 0x72,
-        0x77, 0x2f, 0x6b, 0x42, 0x75, 0x56, 0x61, 0x41, 0x6c, 0x73, 0x56, 0x4c,
-        0x32, 0x42, 0x62, 0x6a, 0x37, 0x72, 0x79, 0x76, 0x56, 0x56, 0x45, 0x30,
-        0x31, 0x47, 0x67, 0x4d, 0x4a, 0x6c, 0x42, 0x4c, 0x51, 0x6a, 0x6e, 0x6a,
-        0x62, 0x32, 0x31, 0x65, 0x4f, 0x32, 0x79, 0x67, 0x32, 0x77, 0x34, 0x41,
-        0x4b, 0x44, 0x4f, 0x67, 0x67, 0x48, 0x4d, 0x77, 0x63, 0x43, 0x41, 0x61,
-        0x5a, 0x6c, 0x31, 0x77, 0x57, 0x31, 0x70, 0x4b, 0x51, 0x4a, 0x41, 0x76,
-        0x6c, 0x54, 0x30, 0x46, 0x59, 0x48, 0x72, 0x77, 0x53, 0x55, 0x4a, 0x51,
-        0x35, 0x31, 0x4a, 0x72, 0x5a, 0x45, 0x55, 0x4a, 0x6a, 0x46, 0x78, 0x5a,
-        0x33, 0x38, 0x78, 0x78, 0x59, 0x48, 0x41, 0x30, 0x48, 0x4d, 0x66, 0x66,
-        0x55, 0x69, 0x44, 0x63, 0x7a, 0x71, 0x6b, 0x46, 0x6a, 0x30, 0x38, 0x77,
-        0x35, 0x39, 0x47, 0x2f, 0x36, 0x35, 0x32, 0x4a, 0x59, 0x2f, 0x68, 0x57,
-        0x70, 0x6f, 0x4d, 0x39, 0x75, 0x7a, 0x77, 0x5a, 0x50, 0x2b, 0x74, 0x56,
-        0x34, 0x73, 0x43, 0x57, 0x58, 0x46, 0x46, 0x35, 0x49, 0x75, 0x42, 0x42,
-        0x38, 0x50, 0x46, 0x77, 0x61, 0x65, 0x71, 0x67, 0x58, 0x44, 0x77, 0x72,
-        0x62, 0x78, 0x67, 0x59, 0x4d, 0x6a, 0x43, 0x67, 0x6b, 0x39, 0x6e, 0x4e,
-        0x5a, 0x75, 0x76, 0x64, 0x73, 0x64, 0x61, 0x56, 0x31, 0x62, 0x47, 0x52,
-        0x51, 0x3d, 0x3d, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x05, 0x6c,
-        0x69, 0x73, 0x74, 0x65, 0x6e, 0x2d, 0x70, 0x6f, 0x72, 0x74, 0x31, 0x33,
-        0x33, 0x30, 0x30, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x46, 0x72,
-        0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x63, 0x6f, 0x6d, 0x2e, 0x72,
-        0x65, 0x64, 0x68, 0x61, 0x74, 0x2e, 0x74, 0x68, 0x65, 0x72, 0x6d, 0x6f,
-        0x73, 0x74, 0x61, 0x74, 0x2e, 0x76, 0x6d, 0x2e, 0x62, 0x79, 0x74, 0x65,
-        0x6d, 0x61, 0x6e, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x69, 0x6e,
-        0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2e, 0x42, 0x79, 0x74, 0x65, 0x6d,
-        0x61, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x65, 0x63,
-        0x65, 0x69, 0x76, 0x65, 0x72, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00,
-        0x24, 0x76, 0x6d, 0x2d, 0x69, 0x64, 0x66, 0x35, 0x62, 0x34, 0x63, 0x63,
-        0x34, 0x34, 0x2d, 0x30, 0x36, 0x66, 0x32, 0x2d, 0x34, 0x38, 0x34, 0x37,
-        0x2d, 0x39, 0x37, 0x64, 0x33, 0x2d, 0x62, 0x35, 0x62, 0x30, 0x62, 0x31,
-        0x39, 0x38, 0x66, 0x38, 0x34, 0x33 // slice C end
-    };
-    
-    private static final byte[] ENCODED_INCOMPLETE_REQUEST_WITH_MANY_PARAMS_A = new byte[] {
-        0x00, 0x00, 0x00, 0x11, 0x52, 0x45, 0x53, 0x50, 0x4f, 0x4e, 0x53, 0x45,
-        0x5f, 0x45, 0x58, 0x50, 0x45, 0x43, 0x54, 0x45, 0x44,
-        0x00, 0x00, 0x00, 0x08, // 8 parameters 
-    };
-    
-    private static final byte[] ENCODED_INCOMPLETE_REQUEST_WITH_MANY_PARAMS_B = new byte[] {
-        0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x15, 0x61, 0x63, 0x74, 0x69,
-        0x6f, 0x6e, 0x2d, 0x6e, 0x61, 0x6d, 0x65, 0x76, 0x6d, 0x2d, 0x62, 0x79,
-        0x74, 0x65, 0x6d, 0x61, 0x6e, 0x2d, 0x69, 0x6e, 0x73, 0x74, 0x72, 0x75,
-        0x6d, 0x65, 0x6e, 0x74, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x01, 0x58,
-        0x61, 0x75, 0x74, 0x68, 0x2d, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x2f, 0x55,
-        0x46, 0x44, 0x76, 0x39, 0x50, 0x54, 0x33, 0x6e, 0x4c, 0x4d, 0x57, 0x31,
-        0x6c, 0x45, 0x4a, 0x55, 0x79, 0x54, 0x78, 0x66, 0x49, 0x48, 0x66, 0x55,
-        0x38, 0x62, 0x52, 0x78, 0x63, 0x41, 0x77, 0x34, 0x49, 0x64, 0x56, 0x59,
-        0x4c, 0x37, 0x4a, 0x7a, 0x48, 0x52, 0x4f, 0x72, 0x75, 0x7a, 0x4e, 0x76,
-        0x49, 0x70, 0x75, 0x76, 0x71, 0x72, 0x4c, 0x55, 0x4e, 0x30, 0x31, 0x66,
-        0x30, 0x68, 0x47, 0x49, 0x47, 0x4d, 0x6d, 0x7a, 0x63, 0x35, 0x38, 0x69,
-        0x79, 0x6c, 0x72, 0x62, 0x70, 0x67, 0x59, 0x31, 0x77, 0x6b, 0x55, 0x41,
-        0x30, 0x34, 0x59, 0x4c, 0x35, 0x57, 0x4f, 0x46, 0x36, 0x36, 0x79, 0x73,
-        0x66, 0x77, 0x4c, 0x57, 0x77, 0x44, 0x41, 0x38, 0x4d, 0x32, 0x52, 0x37,
-        0x6b, 0x68, 0x72, 0x4b, 0x30, 0x78, 0x59, 0x36, 0x2b, 0x44, 0x4c, 0x53,
-        0x41, 0x31, 0x6e, 0x4f, 0x55, 0x54, 0x2b, 0x69, 0x68, 0x6d, 0x76, 0x47,
-        0x73, 0x72, 0x6c, 0x6b, 0x35, 0x31, 0x78, 0x35, 0x61, 0x71, 0x48, 0x37,
-        0x56, 0x76, 0x71, 0x71, 0x6a, 0x2b, 0x54, 0x44, 0x79, 0x6a, 0x38, 0x5a,
-        0x33, 0x47, 0x6c, 0x33, 0x64, 0x4d, 0x70, 0x36, 0x4b, 0x45, 0x6e, 0x67,
-        0x74, 0x42, 0x78, 0x78, 0x68, 0x78, 0x4f, 0x2f, 0x70, 0x49, 0x39, 0x65,
-        0x75, 0x51, 0x47, 0x55, 0x69, 0x77, 0x57, 0x47, 0x63, 0x59, 0x37, 0x43,
-        0x64, 0x54, 0x41, 0x6f, 0x54, 0x53, 0x78, 0x32, 0x77, 0x68, 0x38, 0x62,
-        0x4f, 0x73, 0x6d, 0x44, 0x55, 0x6c, 0x6c, 0x77, 0x4f, 0x45, 0x37, 0x39,
-        0x37, 0x38, 0x5a, 0x68, 0x54, 0x52, 0x73, 0x53, 0x4e, 0x30, 0x62, 0x64,
-        0x35, 0x37, 0x65, 0x79, 0x6e, 0x44, 0x61, 0x61, 0x65, 0x32, 0x33, 0x54,
-        0x77, 0x49, 0x6e, 0x68, 0x65, 0x7a, 0x35, 0x69, 0x48, 0x4f, 0x78, 0x75,
-        0x57, 0x6c, 0x42, 0x79, 0x6a, 0x45, 0x76, 0x34, 0x52, 0x79, 0x44, 0x50,
-        0x53, 0x63, 0x30, 0x66, 0x41, 0x53, 0x6f, 0x4b, 0x77, 0x48, 0x37, 0x49,
-        0x41, 0x6a, 0x37, 0x56, 0x59, 0x4d, 0x7a, 0x75, 0x6a, 0x37, 0x32, 0x2f,
-        0x63, 0x39, 0x78, 0x4c, 0x4f, 0x41, 0x35, 0x34, 0x58, 0x75, 0x79, 0x72,
-        0x75, 0x61, 0x4d, 0x64, 0x71, 0x69, 0x30, 0x7a, 0x6c, 0x41, 0x34, 0x76,
-        0x2f, 0x61, 0x66, 0x37, 0x78, 0x4a, 0x39, 0x30, 0x58, 0x56, 0x4d, 0x57,
-        0x64, 0x57, 0x39, 0x36, 0x45, 0x47, 0x42, 0x47, 0x6b, 0x49, 0x6c, 0x6f,
-        0x33, 0x33, 0x66, 0x41, 0x3d, 0x3d, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00,
-        0x00, 0x01, 0x62, 0x79, 0x74, 0x65, 0x6d, 0x61, 0x6e, 0x2d, 0x61, 0x63,
-        0x74, 0x69, 0x6f, 0x6e, 0x30, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x02,
-        0x1a, 0x62, 0x79, 0x74, 0x65, 0x6d, 0x61, 0x6e, 0x2d, 0x72, 0x75, 0x6c,
-        0x65, 0x52, 0x55, 0x4c, 0x45, 0x20, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x20,
-        0x73, 0x6c, 0x65, 0x65, 0x70, 0x20, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64,
-        0x20, 0x69, 0x6e, 0x76, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73,
-        0x0a, 0x43, 0x4c, 0x41, 0x53, 0x53, 0x20, 0x63, 0x6f, 0x6d, 0x2e, 0x72,
-        0x65, 0x64, 0x68, 0x61, 0x74, 0x2e, 0x74, 0x68, 0x65, 0x72, 0x6d, 0x6f,
-        0x73, 0x74, 0x61, 0x74, 0x2e, 0x62, 0x79, 0x74, 0x65, 0x6d, 0x61, 0x6e,
-        0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x49, 0x6e, 0x66, 0x69, 0x6e, 0x69,
-        0x74, 0x65, 0x4c, 0x6f, 0x6f, 0x70, 0x0a, 0x4d, 0x45, 0x54, 0x48, 0x4f,
-        0x44, 0x20, 0x73, 0x6c, 0x65, 0x65, 0x70, 0x0a, 0x41, 0x54, 0x20, 0x45,
-        0x4e, 0x54, 0x52, 0x59, 0x0a, 0x49, 0x46, 0x20, 0x74, 0x72, 0x75, 0x65,
-        0x0a, 0x44, 0x4f, 0x0a, 0x69, 0x6e, 0x63, 0x72, 0x65, 0x6d, 0x65, 0x6e,
-        0x74, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x28, 0x22, 0x73, 0x6c,
-        0x65, 0x65, 0x70, 0x2d, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x22,
-        0x29, 0x0a, 0x45, 0x4e, 0x44, 0x52, 0x55, 0x4c, 0x45, 0x0a, 0x0a, 0x52,
-        0x55, 0x4c, 0x45, 0x20, 0x73, 0x65, 0x6e, 0x64, 0x20, 0x73, 0x6c, 0x65,
-        0x65, 0x70, 0x20, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x20, 0x69, 0x6e,
-        0x76, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x20, 0x74, 0x6f,
-        0x20, 0x74, 0x68, 0x65, 0x72, 0x6d, 0x6f, 0x73, 0x74, 0x61, 0x74, 0x0a,
-        0x43, 0x4c, 0x41, 0x53, 0x53, 0x20, 0x63, 0x6f, 0x6d, 0x2e, 0x72, 0x65,
-        0x64, 0x68, 0x61, 0x74, 0x2e, 0x74, 0x68, 0x65, 0x72, 0x6d, 0x6f, 0x73,
-        0x74, 0x61, 0x74, 0x2e, 0x62, 0x79, 0x74, 0x65, 0x6d, 0x61, 0x6e, 0x2e,
-        0x74, 0x65, 0x73, 0x74, 0x2e, 0x49, 0x6e, 0x66, 0x69, 0x6e, 0x69, 0x74,
-        0x65, 0x4c, 0x6f, 0x6f, 0x70, 0x0a, 0x4d, 0x45, 0x54, 0x48, 0x4f, 0x44,
-    };
-    
-    private static final byte[] ENCODED_INCOMPLETE_REQUEST_WITH_MANY_PARAMS_C = new byte[] {
-        0x20, 0x73, 0x6c, 0x65, 0x65, 0x70, 0x0a, 0x48, 0x45, 0x4c, 0x50, 0x45,
-        0x52, 0x20, 0x6f, 0x72, 0x67, 0x2e, 0x6a, 0x62, 0x6f, 0x73, 0x73, 0x2e,
-        0x62, 0x79, 0x74, 0x65, 0x6d, 0x61, 0x6e, 0x2e, 0x74, 0x68, 0x65, 0x72,
-        0x6d, 0x6f, 0x73, 0x74, 0x61, 0x74, 0x2e, 0x68, 0x65, 0x6c, 0x70, 0x65,
-        0x72, 0x2e, 0x54, 0x68, 0x65, 0x72, 0x6d, 0x6f, 0x73, 0x74, 0x61, 0x74,
-        0x48, 0x65, 0x6c, 0x70, 0x65, 0x72, 0x0a, 0x41, 0x54, 0x20, 0x45, 0x4e,
-        0x54, 0x52, 0x59, 0x0a, 0x42, 0x49, 0x4e, 0x44, 0x20, 0x63, 0x6f, 0x75,
-        0x6e, 0x74, 0x65, 0x72, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x20, 0x69,
-        0x6e, 0x74, 0x20, 0x3d, 0x20, 0x72, 0x65, 0x61, 0x64, 0x43, 0x6f, 0x75,
-        0x6e, 0x74, 0x65, 0x72, 0x28, 0x22, 0x73, 0x6c, 0x65, 0x65, 0x70, 0x2d,
-        0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x22, 0x29, 0x0a, 0x49, 0x46,
-        0x20, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x56, 0x61, 0x6c, 0x75,
-        0x65, 0x20, 0x25, 0x20, 0x31, 0x30, 0x20, 0x3d, 0x3d, 0x20, 0x30, 0x0a,
-        0x44, 0x4f, 0x0a, 0x73, 0x65, 0x6e, 0x64, 0x28, 0x22, 0x63, 0x6f, 0x6d,
-        0x2e, 0x72, 0x65, 0x64, 0x68, 0x61, 0x74, 0x2e, 0x74, 0x68, 0x65, 0x72,
-        0x6d, 0x6f, 0x73, 0x74, 0x61, 0x74, 0x2e, 0x62, 0x79, 0x74, 0x65, 0x6d,
-        0x61, 0x6e, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x49, 0x6e, 0x66, 0x69,
-        0x6e, 0x69, 0x74, 0x65, 0x4c, 0x6f, 0x6f, 0x70, 0x22, 0x2c, 0x20, 0x20,
-        0x22, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x20, 0x73, 0x6c, 0x65, 0x65,
-        0x70, 0x28, 0x29, 0x20, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0x2c, 0x20,
-        0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x56, 0x61, 0x6c, 0x75, 0x65,
-        0x29, 0x3b, 0x0a, 0x45, 0x4e, 0x44, 0x52, 0x55, 0x4c, 0x45, 0x0a, 0x00,
-        0x00, 0x00, 0x0c, 0x00, 0x00, 0x01, 0x58, 0x63, 0x6c, 0x69, 0x65, 0x6e,
-        0x74, 0x2d, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x4d, 0x64, 0x4a, 0x30, 0x38,
-        0x6c, 0x51, 0x34, 0x2b, 0x34, 0x48, 0x39, 0x61, 0x73, 0x4f, 0x51, 0x61,
-        0x59, 0x78, 0x56, 0x36, 0x58, 0x44, 0x58, 0x62, 0x49, 0x43, 0x69, 0x33,
-        0x6e, 0x6a, 0x49, 0x37, 0x4a, 0x6d, 0x5a, 0x39, 0x57, 0x38, 0x57, 0x67,
-        0x4e, 0x37, 0x4c, 0x57, 0x38, 0x52, 0x49, 0x59, 0x5a, 0x30, 0x61, 0x43,
-        0x46, 0x70, 0x61, 0x48, 0x51, 0x49, 0x48, 0x73, 0x47, 0x33, 0x38, 0x6d,
-        0x4f, 0x4c, 0x76, 0x5a, 0x52, 0x7a, 0x4c, 0x4e, 0x77, 0x49, 0x34, 0x35,
-        0x4e, 0x47, 0x2f, 0x71, 0x6f, 0x78, 0x56, 0x75, 0x2b, 0x42, 0x41, 0x38,
-        0x65, 0x6b, 0x45, 0x42, 0x70, 0x4c, 0x6c, 0x48, 0x43, 0x6f, 0x74, 0x78,
-        0x4c, 0x6b, 0x2b, 0x31, 0x46, 0x37, 0x79, 0x5a, 0x6f, 0x6e, 0x6d, 0x6b,
-        0x45, 0x66, 0x58, 0x71, 0x36, 0x6f, 0x34, 0x69, 0x6d, 0x73, 0x56, 0x55,
-        0x2f, 0x38, 0x45, 0x55, 0x33, 0x36, 0x44, 0x68, 0x37, 0x59, 0x52, 0x64,
-        0x35, 0x71, 0x4f, 0x6e, 0x65, 0x72, 0x4a, 0x37, 0x4e, 0x49, 0x74, 0x72,
-        0x77, 0x2f, 0x6b, 0x42, 0x75, 0x56, 0x61, 0x41, 0x6c, 0x73, 0x56, 0x4c,
-        0x32, 0x42, 0x62, 0x6a, 0x37, 0x72, 0x79, 0x76, 0x56, 0x56, 0x45, 0x30,
-        0x31, 0x47, 0x67, 0x4d, 0x4a, 0x6c, 0x42, 0x4c, 0x51, 0x6a, 0x6e, 0x6a,
-        0x62, 0x32, 0x31, 0x65, 0x4f, 0x32, 0x79, 0x67, 0x32, 0x77, 0x34, 0x41,
-        0x4b, 0x44, 0x4f, 0x67, 0x67, 0x48, 0x4d, 0x77, 0x63, 0x43, 0x41, 0x61,
-        0x5a, 0x6c, 0x31, 0x77, 0x57, 0x31, 0x70, 0x4b, 0x51, 0x4a, 0x41, 0x76,
-        0x6c, 0x54, 0x30, 0x46, 0x59, 0x48, 0x72, 0x77, 0x53, 0x55, 0x4a, 0x51,
-        0x35, 0x31, 0x4a, 0x72, 0x5a, 0x45, 0x55, 0x4a, 0x6a, 0x46, 0x78, 0x5a,
-        0x33, 0x38, 0x78, 0x78, 0x59, 0x48, 0x41, 0x30, 0x48, 0x4d, 0x66, 0x66,
-        0x55, 0x69, 0x44, 0x63, 0x7a, 0x71, 0x6b, 0x46, 0x6a, 0x30, 0x38, 0x77,
-        0x35, 0x39, 0x47, 0x2f, 0x36, 0x35, 0x32, 0x4a, 0x59, 0x2f, 0x68, 0x57,
-        0x70, 0x6f, 0x4d, 0x39, 0x75, 0x7a, 0x77, 0x5a, 0x50, 0x2b, 0x74, 0x56,
-        0x34, 0x73, 0x43, 0x57, 0x58, 0x46, 0x46, 0x35, 0x49, 0x75, 0x42, 0x42,
-        0x38, 0x50, 0x46, 0x77, 0x61, 0x65, 0x71, 0x67, 0x58, 0x44, 0x77, 0x72,
-        0x62, 0x78, 0x67, 0x59, 0x4d, 0x6a, 0x43, 0x67, 0x6b, 0x39, 0x6e, 0x4e,
-        0x5a, 0x75, 0x76, 0x64, 0x73, 0x64, 0x61, 0x56, 0x31, 0x62, 0x47, 0x52,
-        0x51, 0x3d, 0x3d, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x05, 0x6c,
-        0x69, 0x73, 0x74, 0x65, 0x6e, 0x2d, 0x70, 0x6f, 0x72, 0x74, 0x31, 0x33,
-        0x33, 0x30, 0x30, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x46, 0x72,
-        0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x63, 0x6f, 0x6d, 0x2e, 0x72,
-        0x65, 0x64, 0x68, 0x61, 0x74, 0x2e, 0x74, 0x68, 0x65, 0x72, 0x6d, 0x6f,
-        0x73, 0x74, 0x61, 0x74, 0x2e, 0x76, 0x6d, 0x2e, 0x62, 0x79, 0x74, 0x65,
-        0x6d, 0x61, 0x6e, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x69, 0x6e,
-        0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2e, 0x42, 0x79, 0x74, 0x65, 0x6d,
-        0x61, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x65, 0x63,
-        0x65, 0x69, 0x76, 0x65, 0x72, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00,
-        0x24, 0x76, 0x6d, 0x2d, 0x69, 0x64, 0x66, 0x35, 0x62, 0x34, 0x63, 0x63,
-        0x34, 0x34, 0x2d, 0x30, 0x36, 0x66, 0x32, 0x2d, 0x34, 0x38, 0x34, 0x37,
-        0x2d, 0x39, 0x37, 0x64, 0x33, 0x2d, 0x62, 0x35, 0x62, 0x30, 0x62, 0x31,
-        0x39, 0x38, 0x66, 0x38, 0x34, 0x33
-    };
-    
-    /*
-     * This is serialized format for
-     * req = new Request(RequestType.RESPONSE_EXPECTED, blah)
-     */
-    private static final byte[] ENCODED_REQUEST_WITH_NO_PARAMS = new byte[] {
-        0x00, 0x00, 0x00, 0x11, 0x52, 0x45, 0x53, 0x50, 0x4f, 0x4e, 0x53, 0x45,
-        0x5f, 0x45, 0x58, 0x50, 0x45, 0x43, 0x54, 0x45, 0x44, 0x00, 0x00, 0x00,
-        0x00
-    };
-    
-    private static final byte[][] GARBAGE_AS_REQUEST = new byte[][] {
-            // general garbage
-            { 0x0d, 0x0b, 0x0e, 0x0e, 0x0f },
-            // first two bytes are broken
-            { 0x0f, 0x0d, 0x00, 0x11, 0x52, 0x45, 0x53, 0x50, 0x4f, 0x4e, 0x53,
-                    0x45, 0x5f, 0x45, 0x58, 0x50, 0x45, 0x43, 0x54, 0x45, 0x44,
-                    0x00, 0x00, 0x00, 0x00 },
-            // last byte indicates params, which are missing
-            { 0x00, 0x00, 0x00, 0x11, 0x52, 0x45, 0x53, 0x50, 0x4f, 0x4e, 0x53,
-                    0x45, 0x5f, 0x45, 0x58, 0x50, 0x45, 0x43, 0x54, 0x45, 0x44,
-                    0x00, 0x00, 0x00, 0x0f } };
-    
-    
-    private static final byte[] TYPE = RequestType.RESPONSE_EXPECTED.toString().getBytes();
-
-    private ChannelHandlerContext ctx;
-    private CommandChannelRequestDecoder decoder;
-
-    @Before
-    public void setUp() {
-        ctx = mock(ChannelHandlerContext.class);
-        when(ctx.channel()).thenReturn(mock(Channel.class));
-        decoder = new CommandChannelRequestDecoder();
-    }
-
-    @Test
-    public void testDecode() throws Exception {
-        ByteBuf buffer = Unpooled.buffer();
-        buffer.writeInt(TYPE.length);
-        buffer.writeBytes(TYPE);
-        buffer.writeInt(0);
-
-        List<Object> resultList = new ArrayList<>();
-        decoder.decode(ctx, buffer, resultList);
-        assertEquals(1, resultList.size());
-        Request request = (Request)resultList.get(0);
-        assertTrue(RequestType.RESPONSE_EXPECTED == (RequestType) request.getType());
-    }
-
-    @Test
-    public void testDecodeWithParameters() throws Exception {
-        String parmName = "parameter";
-        String parmValue = "hello";
-        ByteBuf buffer = Unpooled.buffer();
-        buffer.writeInt(TYPE.length);
-        buffer.writeBytes(TYPE);
-        buffer.writeInt(1);
-        buffer.writeInt(parmName.getBytes().length);
-        buffer.writeInt(parmValue.getBytes().length);
-        buffer.writeBytes(parmName.getBytes());
-        buffer.writeBytes(parmValue.getBytes());
-        
-        List<Object> resultList = new ArrayList<>();
-        decoder.decode(ctx, buffer, resultList);
-        assertEquals(1, resultList.size());
-        Request request = (Request) resultList.get(0);
-        Collection<String> parmNames = request.getParameterNames();
-
-        assertEquals(1, parmNames.size());
-        assertTrue(parmNames.contains(parmName));
-        String decodedValue = request.getParameter(parmName);
-        assertEquals(parmValue, decodedValue);
-    }
-    
-    @Test
-    public void testDecodeWithLongParams() throws Exception {
-        String parmName = "parameter";
-        String param2Name = "foo-bar";
-        String param2Value = "RESPONSE_EXPECTED";
-        ByteBuf buffer = Unpooled.buffer();
-        buffer.writeInt(TYPE.length);
-        buffer.writeBytes(TYPE);
-        buffer.writeInt(2);
-        buffer.writeInt(parmName.getBytes().length);
-        buffer.writeInt(LONG_PARAM_VALUE.getBytes().length);
-        buffer.writeBytes(parmName.getBytes());
-        buffer.writeBytes(LONG_PARAM_VALUE.getBytes());
-        buffer.writeInt(param2Name.getBytes().length);
-        buffer.writeInt(param2Value.getBytes().length);
-        buffer.writeBytes(param2Name.getBytes());
-        buffer.writeBytes(param2Value.getBytes());
-        
-        List<Object> resultList = new ArrayList<>();
-        decoder.decode(ctx, buffer, resultList);
-        assertEquals(1, resultList.size());
-        Request request = (Request) resultList.get(0);
-        Collection<String> parmNames = request.getParameterNames();
-
-        assertEquals(2, parmNames.size());
-        assertTrue(parmNames.contains(parmName));
-        String decodedValue = request.getParameter(parmName);
-        assertEquals(LONG_PARAM_VALUE, decodedValue);
-    }
-    
-    @Test
-    public void testDecodeWithParametersFromBytesArray() throws Exception {
-        ByteBuf buffer = Unpooled.copiedBuffer(ENCODED_REQUEST_WITH_PARAMS);
-        Request expected = new Request(RequestType.RESPONSE_EXPECTED, null);
-        expected.setParameter("param1", "value1");
-        expected.setParameter("param2", "value2");
-        List<Object> resultList = new ArrayList<>();
-        new CommandChannelRequestDecoder().decode(ctx, buffer, resultList);
-        assertEquals(1, resultList.size());
-        Message actual = (Message)resultList.get(0);
-        assertTrue(actual instanceof Request);
-        assertTrue(Messages.equal(expected, (Request)actual));
-        InetSocketAddress addr = new InetSocketAddress(1234);
-        buffer = Unpooled.copiedBuffer(ENCODED_REQUEST_WITH_NO_PARAMS);
-        expected = new Request(RequestType.RESPONSE_EXPECTED, addr);
-        resultList = new ArrayList<>();
-        new CommandChannelRequestDecoder().decode(ctx, buffer, resultList);
-        assertEquals(1, resultList.size());
-        actual = (Message)resultList.get(0);
-        assertTrue(actual instanceof Request);
-        assertTrue(Messages.equal(expected, (Request)actual));
-    }
-    
-    /**
-     * Tests the case where the decode() method is called multiple times due
-     * to some data not yet available so as to be able to decode the request in
-     * it's entirety.
-     * 
-     * @throws Exception
-     */
-    @Test
-    public void testDecodeWithParamsFromBytes() throws Exception {
-        CommandChannelRequestDecoder decoder = new CommandChannelRequestDecoder();
-
-        // First decode all-at-once
-        List<Object> allAtOnceResultList = new ArrayList<>();
-        ByteBuf firstBuffer = Unpooled.copiedBuffer(ENCODED_REQUEST_WITH_MANY_PARAMS);
-        decoder.decode(ctx, firstBuffer, allAtOnceResultList);
-        assertEquals(1, allAtOnceResultList.size());
-        Message firstActual = (Message)allAtOnceResultList.get(0);
-        assertTrue(firstActual instanceof Request);
-        Request firstRequest = (Request)firstActual;
-        
-        List<Object> resultList = new ArrayList<>();
-        ByteBuf buffer = Unpooled.copiedBuffer(ENCODED_INCOMPLETE_REQUEST_WITH_MANY_PARAMS_A);
-        int bytesAvailable = ENCODED_INCOMPLETE_REQUEST_WITH_MANY_PARAMS_A.length;
-        assertEquals(bytesAvailable, buffer.readableBytes());
-        decoder.decode(ctx, buffer, resultList); // request type got decoded
-        assertEquals(bytesAvailable, buffer.readableBytes());
-        assertEquals(0, resultList.size());
-        ByteBuf remaining = Unpooled.copiedBuffer(buffer);
-        assertEquals(bytesAvailable, remaining.array().length);
-        buffer = Unpooled.copiedBuffer(remaining.array(), ENCODED_INCOMPLETE_REQUEST_WITH_MANY_PARAMS_B);
-        bytesAvailable = remaining.array().length + ENCODED_INCOMPLETE_REQUEST_WITH_MANY_PARAMS_B.length;
-        assertEquals(bytesAvailable, buffer.readableBytes());
-        decoder.decode(ctx, buffer, resultList); // nothing additional should get decoded
-        assertEquals(bytesAvailable, buffer.readableBytes());
-        assertEquals(0, resultList.size());
-        buffer = Unpooled.copiedBuffer(remaining.array(),
-                             ENCODED_INCOMPLETE_REQUEST_WITH_MANY_PARAMS_B,
-                             ENCODED_INCOMPLETE_REQUEST_WITH_MANY_PARAMS_C);
-        bytesAvailable = remaining.array().length +
-                           ENCODED_INCOMPLETE_REQUEST_WITH_MANY_PARAMS_B.length +
-                           ENCODED_INCOMPLETE_REQUEST_WITH_MANY_PARAMS_C.length;
-        assertEquals(bytesAvailable, buffer.readableBytes());
-        decoder.decode(ctx, buffer, resultList); // this time decoding should complete
-        assertEquals(0, buffer.readableBytes());
-        assertEquals(1, resultList.size());
-        Message actual = (Message)resultList.get(0);
-        assertTrue(actual instanceof Request);
-        Request actualReq = (Request) actual;
-        assertEquals(RequestType.RESPONSE_EXPECTED, actualReq.getType());
-        assertEquals("Expected 8 parameters", 8, actualReq.getParameterNames().size());
-        assertEquals(Integer.toString(0), actualReq.getParameter("byteman-action"));
-        String rule = actualReq.getParameter("byteman-rule");
-        assertNotNull(rule);
-        assertEquals(538, rule.length());
-        assertTrue(Messages.equal(firstRequest, actualReq));
-    }
-    
-    @Test
-    public void decodingOfGarbageDoesNotAddToResultList()
-            throws Exception {
-        for (int i = 0; i < GARBAGE_AS_REQUEST.length; i++) {
-            ByteBuf buffer = Unpooled
-                    .copiedBuffer(GARBAGE_AS_REQUEST[0]);
-            CommandChannelRequestDecoder decoder = new CommandChannelRequestDecoder();
-            List<Object> outList = new ArrayList<>();
-            decoder.decode(ctx, buffer, outList);
-            assertEquals(0, outList.size());
-        }
-    }
-}
-
--- a/agent/command-server/src/test/java/com/redhat/thermostat/agent/command/server/internal/CommandChannelServerContextTest.java	Tue Jun 13 12:02:12 2017 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,150 +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
- * <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.command.server.internal;
-
-import static org.junit.Assert.assertNotNull;
-import static org.mockito.Matchers.eq;
-import static org.mockito.Matchers.isA;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-import javax.net.ssl.SSLContext;
-import javax.net.ssl.SSLEngine;
-import javax.net.ssl.SSLSession;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.InOrder;
-import org.mockito.Mockito;
-import org.powermock.api.mockito.PowerMockito;
-import org.powermock.core.classloader.annotations.PrepareForTest;
-import org.powermock.modules.junit4.PowerMockRunner;
-
-import com.redhat.thermostat.agent.command.server.internal.CommandChannelServerContext.ServerChannelInitializer;
-import com.redhat.thermostat.agent.command.server.internal.CommandChannelServerContext.ServerChannelPipelineInitializerCreator;
-import com.redhat.thermostat.agent.ipc.client.IPCMessageChannel;
-import com.redhat.thermostat.common.ssl.SSLContextFactory;
-import com.redhat.thermostat.shared.config.SSLConfiguration;
-
-import io.netty.bootstrap.AbstractBootstrap;
-import io.netty.channel.ChannelPipeline;
-import io.netty.channel.socket.SocketChannel;
-import io.netty.handler.ssl.SslHandler;
-
-@RunWith(PowerMockRunner.class)
-@PrepareForTest({ SSLContextFactory.class,
-        SSLEngine.class, SSLContext.class })
-public class CommandChannelServerContextTest {
-
-    private CommandChannelServerContext ctx;
-    private SSLConfiguration mockSSLConf;
-    private TestServerChannelPipelineInitializerCreator creator;
-
-    @Before
-    public void setUp() {
-        creator = new TestServerChannelPipelineInitializerCreator();
-        mockSSLConf = mock(SSLConfiguration.class);
-        when(mockSSLConf.enableForCmdChannel()).thenReturn(false);
-        IPCMessageChannel agentChannel = mock(IPCMessageChannel.class);
-        ctx = new CommandChannelServerContext(mockSSLConf, agentChannel, creator);
-    }
-
-    @Test
-    public void testBootstrap() throws Exception {
-        AbstractBootstrap<?, ?> bootstrap = ctx.getBootstrap();
-        assertNotNull(bootstrap);
-        SocketChannel channel = mock(SocketChannel.class);
-        ChannelPipeline mockPipeline = mock(ChannelPipeline.class);
-        when(channel.pipeline()).thenReturn(mockPipeline);
-        creator.initializer.initChannel(channel);
-        InOrder inOrder = Mockito.inOrder(mockPipeline);
-        inOrder.verify(mockPipeline).addLast(eq("decoder"), isA(CommandChannelRequestDecoder.class));
-        inOrder.verify(mockPipeline).addLast(eq("encoder"), isA(ResponseEncoder.class));
-        inOrder.verify(mockPipeline).addLast(eq("handler"), isA(ServerHandler.class));
-    }
-    
-    @Test
-    public void testBootstrapSSL() throws Exception {
-        when(mockSSLConf.enableForCmdChannel()).thenReturn(true);
-        PowerMockito.mockStatic(SSLContextFactory.class);
-        // SSL classes need to be mocked with PowerMockito
-        SSLContext context = PowerMockito.mock(SSLContext.class);
-        when(SSLContextFactory.getServerContext(isA(SSLConfiguration.class))).thenReturn(context);
-        SSLEngine engine = PowerMockito.mock(SSLEngine.class);
-        when(context.createSSLEngine()).thenReturn(engine);
-        when(engine.getSession()).thenReturn(mock(SSLSession.class));
-        
-        AbstractBootstrap<?, ?> bootstrap = ctx.getBootstrap();
-        assertNotNull(bootstrap);
-        SocketChannel channel = mock(SocketChannel.class);
-        ChannelPipeline mockPipeline = mock(ChannelPipeline.class);
-        when(channel.pipeline()).thenReturn(mockPipeline);
-        creator.initializer.initChannel(channel);
-        InOrder inOrder = Mockito.inOrder(mockPipeline);
-        inOrder.verify(mockPipeline).addLast(eq("ssl"), isA(SslHandler.class));
-        inOrder.verify(mockPipeline).addLast(eq("decoder"), isA(CommandChannelRequestDecoder.class));
-        inOrder.verify(mockPipeline).addLast(eq("encoder"), isA(ResponseEncoder.class));
-        inOrder.verify(mockPipeline).addLast(eq("handler"), isA(ServerHandler.class));
-        Mockito.verify(engine).setUseClientMode(false);
-    }
-    
-    static class TestServerChannelPipelineInitializerCreator extends ServerChannelPipelineInitializerCreator {
-        
-        TestServerPipelineInitializer initializer;
-        
-        @Override
-        ServerChannelInitializer createInitializer(SSLConfiguration sslConf, IPCMessageChannel agentChannel) {
-            initializer = new TestServerPipelineInitializer(sslConf, agentChannel);
-            return initializer;
-        }
-    }
-    
-    static class TestServerPipelineInitializer extends ServerChannelInitializer {
-        
-        
-        TestServerPipelineInitializer(SSLConfiguration config, IPCMessageChannel agentChannel) {
-            super(config, agentChannel);
-        }
-        
-        SocketChannel getInitializedChannel(SocketChannel ch) throws Exception {
-            super.initChannel(ch);
-            return ch;
-        }
-    }
-}
-
--- a/agent/command-server/src/test/java/com/redhat/thermostat/agent/command/server/internal/CommandChannelServerImplTest.java	Tue Jun 13 12:02:12 2017 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,111 +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
- * <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.command.server.internal;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.fail;
-import static org.mockito.Matchers.any;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import java.io.IOException;
-import java.net.InetSocketAddress;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.mockito.ArgumentCaptor;
-
-import io.netty.bootstrap.AbstractBootstrap;
-import io.netty.bootstrap.ServerBootstrap;
-import io.netty.channel.ChannelFuture;
-import io.netty.channel.EventLoopGroup;
-
-public class CommandChannelServerImplTest {
-
-    private CommandChannelServerContext ctx;
-    private ServerBootstrap bootstrap;
-    private EventLoopGroup group;
-
-    @SuppressWarnings({ "rawtypes", "unchecked" })
-    @Before
-    public void setUp() {
-        bootstrap = mock(ServerBootstrap.class);
-        when(bootstrap.bind(any(InetSocketAddress.class))).thenReturn(mock(ChannelFuture.class));
-        group = mock(EventLoopGroup.class);
-        when(bootstrap.group()).thenReturn(group);
-        ctx = mock(CommandChannelServerContext.class);
-        when(ctx.getBootstrap()).thenReturn((AbstractBootstrap) bootstrap);
-    }
-
-    @Test
-    public void testStartListening() throws IOException {
-        CommandChannelServerImpl server = new CommandChannelServerImpl(ctx);
-        server.startListening("127.0.0.1", 123);
-
-        ArgumentCaptor<InetSocketAddress> argument = ArgumentCaptor.forClass(InetSocketAddress.class);
-        verify(bootstrap).bind(argument.capture());
-        
-        InetSocketAddress addr = new InetSocketAddress("127.0.0.1", 123);
-        assertEquals(addr, argument.getValue());
-    }
-    
-    @SuppressWarnings("unchecked")
-    @Test
-    public void startListeningFailureThrowsException() {
-        CommandChannelServerImpl server = new CommandChannelServerImpl(ctx);
-
-        when(bootstrap.bind(any(InetSocketAddress.class))).thenThrow(IOException.class);
-        
-        try {
-            server.startListening("does-not-resolve.example.com", 123);
-            fail("Should have thrown exception");
-        } catch (IOException e) {
-            // pass
-        }
-    }
-
-    @Test
-    public void testStopListening() {
-        CommandChannelServerImpl server = new CommandChannelServerImpl(ctx);
-        server.stopListening();
-
-        verify(group).shutdownGracefully();
-    }
-    
-}
-
--- a/agent/command-server/src/test/java/com/redhat/thermostat/agent/command/server/internal/CommandChannelServerMainTest.java	Tue Jun 13 12:02:12 2017 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,212 +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
- * <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.command.server.internal;
-
-import static org.junit.Assert.fail;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyInt;
-import static org.mockito.Mockito.doNothing;
-import static org.mockito.Mockito.doThrow;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import java.io.IOException;
-import java.nio.ByteBuffer;
-
-import com.redhat.thermostat.shared.config.OS;
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.mockito.ArgumentCaptor;
-
-import com.redhat.thermostat.agent.command.server.internal.CommandChannelServerMain.ServerCreator;
-import com.redhat.thermostat.agent.command.server.internal.CommandChannelServerMain.ShutdownHookHandler;
-import com.redhat.thermostat.agent.command.server.internal.CommandChannelServerMain.Sleeper;
-import com.redhat.thermostat.agent.ipc.client.ClientIPCService;
-import com.redhat.thermostat.agent.ipc.client.IPCMessageChannel;
-import com.redhat.thermostat.shared.config.SSLConfiguration;
-
-public class CommandChannelServerMainTest {
-
-    private static final String IGNORED_ROOT = "/";
-    private CommandChannelServerImpl server;
-    private ClientIPCService ipcService;
-    private IPCMessageChannel agentChannel;
-    private SSLConfigurationParser parser;
-    private ShutdownHookHandler shutdownHandler;
-    private Sleeper sleeper;
-
-    @Before
-    public void setUpOnce() throws IOException {
-        System.setProperty(CommandChannelServerMain.CONFIG_FILE_PROP, "/path/to/ipc/config");
-        ipcService = mock(ClientIPCService.class);
-        agentChannel = mock(IPCMessageChannel.class);
-        when(ipcService.connectToServer(CommandChannelServerMain.IPC_SERVER_NAME)).thenReturn(agentChannel);
-        
-        SSLConfiguration config = mock(SSLConfiguration.class);
-        parser = mock(SSLConfigurationParser.class);
-        when(parser.parseSSLConfiguration(agentChannel)).thenReturn(config);
-        
-        server = mock(CommandChannelServerImpl.class);
-        ServerCreator creator = mock(ServerCreator.class);
-        when(creator.createServer(config, agentChannel)).thenReturn(server);
-        
-        shutdownHandler = mock(ShutdownHookHandler.class);
-        sleeper = mock(Sleeper.class);
-
-        if (OS.IS_WINDOWS) {
-            // Command Channel now uses a native DLL for Windows named pipes on Windows
-            // because of this, CommonPathsImpl tries to initialize.
-            System.setProperty("THERMOSTAT_HOME", IGNORED_ROOT);
-            System.setProperty("USER_THERMOSTAT_HOME", IGNORED_ROOT);
-        }
-
-        CommandChannelServerMain.setIPCService(ipcService);
-        CommandChannelServerMain.setSSLConfigurationParser(parser);
-        CommandChannelServerMain.setServerCreator(creator);
-        CommandChannelServerMain.setShutdownHookHandler(shutdownHandler);
-        CommandChannelServerMain.setSleeper(sleeper);
-    }
-    
-    @After
-    public void tearDownOnce() {
-        System.clearProperty("THERMOSTAT_HOME");
-        System.clearProperty("USER_THERMOSTAT_HOME");
-        System.clearProperty(CommandChannelServerMain.CONFIG_FILE_PROP);
-        CommandChannelServerMain.setIPCService(null);
-        CommandChannelServerMain.setSSLConfigurationParser(new SSLConfigurationParser());
-        CommandChannelServerMain.setServerCreator(new ServerCreator());
-        CommandChannelServerMain.setShutdownHookHandler(new ShutdownHookHandler());
-        CommandChannelServerMain.setSleeper(new Sleeper());
-    }
-
-    @Test
-    public void testNotEnoughArgs() throws IOException {
-        try {
-            CommandChannelServerMain.main(new String[] { "hello" });
-            fail("Expected IOException");
-        } catch (IOException e) {
-            verify(server, never()).startListening(any(String.class), anyInt());
-            verify(sleeper, never()).sleepWait();
-        }
-    }
-    
-    @Test
-    public void testBadPort() throws IOException {
-        try {
-            CommandChannelServerMain.main(new String[] { "hello", "world" });
-            fail("Expected IOException");
-        } catch (IOException e) {
-            verify(server, never()).startListening(any(String.class), anyInt());
-            verify(sleeper, never()).sleepWait();
-        }
-    }
-    
-    @Test
-    public void testSSLConfigParseFailed() throws IOException {
-        when(parser.parseSSLConfiguration(agentChannel)).thenThrow(new IOException("TEST"));
-        try {
-            CommandChannelServerMain.main(new String[] { "hello", "123" });
-            fail("Expected IOException");
-        } catch (IOException e) {
-            verify(server, never()).startListening(any(String.class), anyInt());
-            verify(sleeper, never()).sleepWait();
-        }
-    }
-    
-    @Test
-    public void testStartListeningFailed() throws IOException {
-        doThrow(new IOException("TEST")).when(server).startListening(any(String.class), anyInt());
-        try {
-            CommandChannelServerMain.main(new String[] { "hello", "123" });
-            fail("Expected IOException");
-        } catch (IOException e) {
-            verify(shutdownHandler, never()).addShutdownHook(any(Thread.class));
-            verify(sleeper, never()).sleepWait();
-            verify(server).stopListening();
-        }
-    }
-    
-    @Test
-    public void testStartedMessageException() throws IOException {
-        doThrow(new IOException("TEST")).when(agentChannel).writeMessage(any(ByteBuffer.class));
-        try {
-            CommandChannelServerMain.main(new String[] { "hello", "123" });
-            fail("Expected IOException");
-        } catch (IOException e) {
-            verify(shutdownHandler, never()).addShutdownHook(any(Thread.class));
-            verify(sleeper, never()).sleepWait();
-        }
-    }
-    
-    @Test
-    public void testReadyMessageException() throws IOException {
-        doNothing().doThrow(new IOException("TEST")).when(agentChannel).writeMessage(any(ByteBuffer.class));
-        try {
-            CommandChannelServerMain.main(new String[] { "hello", "123" });
-            fail("Expected IOException");
-        } catch (IOException e) {
-            verify(shutdownHandler, never()).addShutdownHook(any(Thread.class));
-            verify(sleeper, never()).sleepWait();
-        }
-    }
-    
-    @Test
-    public void testSuccess() throws IOException {
-        CommandChannelServerMain.main(new String[] { "hello", "123" });
-        
-        verify(server).startListening("hello", 123);
-        verify(sleeper).sleepWait();
-    }
-    
-    @Test
-    public void testShutdownHook() throws IOException, InterruptedException {
-        CommandChannelServerMain.main(new String[] { "hello", "123" });
-        
-        ArgumentCaptor<Thread> hookCaptor = ArgumentCaptor.forClass(Thread.class);
-        verify(shutdownHandler).addShutdownHook(hookCaptor.capture());
-        Thread hook = hookCaptor.getValue();
-        
-        hook.start();
-        hook.join();
-        
-        verify(server).stopListening();
-    }
-
-}
--- a/agent/command-server/src/test/java/com/redhat/thermostat/agent/command/server/internal/JsonResponseParserTest.java	Tue Jun 13 12:02:12 2017 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,188 +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
- * <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.command.server.internal;
-
-import static org.junit.Assert.assertEquals;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-import java.io.IOException;
-import java.nio.ByteBuffer;
-import java.nio.charset.Charset;
-
-import org.junit.Before;
-import org.junit.Test;
-
-import com.google.gson.Gson;
-import com.google.gson.GsonBuilder;
-import com.google.gson.JsonArray;
-import com.google.gson.JsonElement;
-import com.google.gson.JsonObject;
-import com.redhat.thermostat.agent.ipc.client.IPCMessageChannel;
-import com.redhat.thermostat.common.command.Response;
-import com.redhat.thermostat.common.command.Response.ResponseType;
-
-public class JsonResponseParserTest {
-    
-    private IPCMessageChannel agentChannel;
-    private JsonResponseParser parser;
-
-    @Before
-    public void setUp() {
-        agentChannel = mock(IPCMessageChannel.class);
-        parser = new JsonResponseParser();
-    }
-
-    @Test
-    public void testSuccess() throws IOException {
-        JsonObject jsonResponse = createResponse();
-        byte[] encoded = toJson(jsonResponse);
-        mockByteChannel(encoded);
-        
-        Response response = parser.parseResponse(agentChannel);
-        assertEquals(ResponseType.OK, response.getType());
-    }
-    
-    @Test(expected=IOException.class)
-    public void testEmpty() throws IOException {
-        mockByteChannel(new byte[0]);
-        parser.parseResponse(agentChannel);
-    }
-    
-    @Test(expected=IOException.class)
-    public void testNotJson() throws IOException {
-        mockByteChannel("Not JSON".getBytes(Charset.forName("UTF-8")));
-        parser.parseResponse(agentChannel);
-    }
-    
-    @Test(expected=IOException.class)
-    public void testBadResponseRoot() throws IOException {
-        byte[] encoded = toJson(new JsonArray());
-        mockByteChannel(encoded);
-        parser.parseResponse(agentChannel);
-    }
-    
-    @Test(expected=IOException.class)
-    public void testParamsMissing() throws IOException {
-        byte[] encoded = toJson(new JsonObject());
-        mockByteChannel(encoded);
-        parser.parseResponse(agentChannel);
-    }
-    
-    @Test(expected=IOException.class)
-    public void testBadParams() throws IOException {
-        JsonObject jsonResponse = createResponse();
-        jsonResponse.add(CommandChannelConstants.RESPONSE_JSON_TOP, new JsonArray());
-        
-        byte[] encoded = toJson(new JsonObject());
-        mockByteChannel(encoded);
-        parser.parseResponse(agentChannel);
-    }
-    
-    @Test(expected=IOException.class)
-    public void testNullParams() throws IOException {
-        JsonObject jsonResponse = createResponse();
-        jsonResponse.add(CommandChannelConstants.RESPONSE_JSON_TOP, null);
-        
-        byte[] encoded = toJson(new JsonObject());
-        mockByteChannel(encoded);
-        parser.parseResponse(agentChannel);
-    }
-    
-    @Test(expected=IOException.class)
-    public void testResponseTypeMissing() throws IOException {
-        JsonObject root = createResponse();
-        JsonObject jsonResponse = root.get(CommandChannelConstants.RESPONSE_JSON_TOP).getAsJsonObject();
-        jsonResponse.remove(CommandChannelConstants.RESPONSE_JSON_TYPE);
-        
-        byte[] encoded = toJson(root);
-        mockByteChannel(encoded);
-        parser.parseResponse(agentChannel);
-    }
-    
-    @Test(expected=IOException.class)
-    public void testResponseTypeNotString() throws IOException {
-        JsonObject root = createResponse();
-        JsonObject jsonResponse = root.get(CommandChannelConstants.RESPONSE_JSON_TOP).getAsJsonObject();
-        jsonResponse.add(CommandChannelConstants.RESPONSE_JSON_TYPE, new JsonArray());
-        
-        byte[] encoded = toJson(root);
-        mockByteChannel(encoded);
-        parser.parseResponse(agentChannel);
-    }
-    
-    @Test(expected=IOException.class)
-    public void testResponseTypeNull() throws IOException {
-        JsonObject root = createResponse();
-        JsonObject jsonResponse = root.get(CommandChannelConstants.RESPONSE_JSON_TOP).getAsJsonObject();
-        jsonResponse.addProperty(CommandChannelConstants.RESPONSE_JSON_TYPE, (String) null);
-        
-        byte[] encoded = toJson(root);
-        mockByteChannel(encoded);
-        parser.parseResponse(agentChannel);
-    }
-    
-    @Test(expected=IOException.class)
-    public void testResponseTypeNotEnum() throws IOException {
-        JsonObject root = createResponse();
-        JsonObject jsonResponse = root.get(CommandChannelConstants.RESPONSE_JSON_TOP).getAsJsonObject();
-        jsonResponse.addProperty(CommandChannelConstants.RESPONSE_JSON_TYPE, "Not a ResponseType");
-        
-        byte[] encoded = toJson(root);
-        mockByteChannel(encoded);
-        parser.parseResponse(agentChannel);
-    }
-
-    private JsonObject createResponse() {
-        JsonObject params = new JsonObject();
-        params.addProperty(CommandChannelConstants.RESPONSE_JSON_TYPE, ResponseType.OK.name());
-        JsonObject root = new JsonObject();
-        root.add(CommandChannelConstants.RESPONSE_JSON_TOP, params);
-        return root;
-    }
-    
-    private byte[] toJson(JsonElement element) {
-        Gson gson = new GsonBuilder().serializeNulls().create();
-        String jsonString = gson.toJson(element);
-        return jsonString.getBytes(Charset.forName("UTF-8"));
-    }
-
-    private void mockByteChannel(final byte[] encoded) throws IOException {
-        when(agentChannel.readMessage()).thenReturn(ByteBuffer.wrap(encoded));
-    }
-
-}
--- a/agent/command-server/src/test/java/com/redhat/thermostat/agent/command/server/internal/ResponseEncoderTest.java	Tue Jun 13 12:02:12 2017 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,80 +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
- * <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.command.server.internal;
-
-import static org.junit.Assert.assertEquals;
-
-import java.nio.charset.Charset;
-
-import org.junit.Test;
-
-import com.redhat.thermostat.agent.command.server.internal.ResponseEncoder;
-import com.redhat.thermostat.common.command.Response;
-import com.redhat.thermostat.common.command.Response.ResponseType;
-
-import io.netty.buffer.ByteBuf;
-import io.netty.buffer.ByteBufUtil;
-import io.netty.buffer.Unpooled;
-
-public class ResponseEncoderTest {
-
-    private static final boolean DEBUG = false;
-    
-    @Test
-    public void testEncode() throws Exception {
-        ResponseEncoder encoder = new ResponseEncoder();
-        String responseExp = "OK";
-        ByteBuf stringBuf = Unpooled.copiedBuffer(responseExp, Charset.defaultCharset());
-        ByteBuf buf = Unpooled.buffer(4);
-        buf.writeInt(responseExp.getBytes().length);
-        ByteBuf expected = Unpooled.wrappedBuffer(buf, stringBuf);
-        Response ok = new Response(ResponseType.OK);
-        ByteBuf actual = (ByteBuf)encoder.encode(ok);
-        if (DEBUG) {
-            printBuffers(actual, expected);
-        }
-        assertEquals(0, ByteBufUtil.compare(expected, actual));
-    }
-    
-    private void printBuffers(ByteBuf actual, ByteBuf expected) {
-        System.out.println("hexdump expected\n-------------------------------------");
-        System.out.println(ByteBufUtil.hexDump(expected));
-        System.out.println("\nhexdump actual\n-------------------------------------");
-        System.out.println(ByteBufUtil.hexDump(actual) + "\n\n");
-    }
-}
-
--- a/agent/command-server/src/test/java/com/redhat/thermostat/agent/command/server/internal/SSLConfigurationParserTest.java	Tue Jun 13 12:02:12 2017 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,330 +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
- * <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.command.server.internal;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNull;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-import java.io.File;
-import java.io.IOException;
-import java.nio.ByteBuffer;
-import java.nio.charset.Charset;
-
-import org.junit.Before;
-import org.junit.Test;
-
-import com.google.gson.Gson;
-import com.google.gson.GsonBuilder;
-import com.google.gson.JsonArray;
-import com.google.gson.JsonElement;
-import com.google.gson.JsonObject;
-import com.redhat.thermostat.agent.ipc.client.IPCMessageChannel;
-import com.redhat.thermostat.shared.config.SSLConfiguration;
-
-public class SSLConfigurationParserTest {
-    
-    private SSLConfigurationParser parser;
-    private IPCMessageChannel agentChannel;
-    
-    @Before
-    public void setUp() throws Exception {
-        parser = new SSLConfigurationParser();
-        agentChannel = mock(IPCMessageChannel.class);
-    }
-    
-    @Test
-    public void testSuccess() throws Exception {
-        JsonObject root = createConfig();
-        byte[] encoded = toJson(root);
-        mockByteChannel(encoded);
-        
-        SSLConfiguration config = parser.parseSSLConfiguration(agentChannel);
-        assertEquals(new File("/path/to/keystore").getAbsolutePath(), config.getKeystoreFile().getAbsolutePath());
-        assertEquals("My Keystore Password", config.getKeyStorePassword());
-        assertEquals(true, config.enableForCmdChannel());
-        assertEquals(true, config.enableForBackingStorage());
-        assertEquals(false, config.disableHostnameVerification());
-    }
-
-    @Test
-    public void testSuccessNoKeystore() throws IOException {
-        JsonObject root = createConfig();
-        JsonObject params = root.get(CommandChannelConstants.SSL_JSON_ROOT).getAsJsonObject();
-        params.addProperty(CommandChannelConstants.SSL_JSON_KEYSTORE_FILE, (String) null);
-        
-        byte[] encoded = toJson(root);
-        mockByteChannel(encoded);
-        
-        SSLConfiguration config = parser.parseSSLConfiguration(agentChannel);
-        assertNull(config.getKeystoreFile());
-        assertEquals("My Keystore Password", config.getKeyStorePassword());
-        assertEquals(true, config.enableForCmdChannel());
-        assertEquals(true, config.enableForBackingStorage());
-        assertEquals(false, config.disableHostnameVerification());
-    }
-    
-    @Test
-    public void testSuccessNoKeystorePass() throws IOException {
-        JsonObject root = createConfig();
-        JsonObject params = root.get(CommandChannelConstants.SSL_JSON_ROOT).getAsJsonObject();
-        params.addProperty(CommandChannelConstants.SSL_JSON_KEYSTORE_PASS, (String) null);
-
-        byte[] encoded = toJson(root);
-        mockByteChannel(encoded);
-        
-        SSLConfiguration config = parser.parseSSLConfiguration(agentChannel);
-        assertEquals(new File("/path/to/keystore").getAbsolutePath(), config.getKeystoreFile().getAbsolutePath());
-        assertNull(config.getKeyStorePassword());
-        assertEquals(true, config.enableForCmdChannel());
-        assertEquals(true, config.enableForBackingStorage());
-        assertEquals(false, config.disableHostnameVerification());
-    }
-
-    @Test(expected=IOException.class)
-    public void testConfigMissing() throws IOException {
-        mockByteChannel(new byte[0]);
-        parser.parseSSLConfiguration(agentChannel);
-    }
-    
-    @Test(expected=IOException.class)
-    public void testBadConfigNotJson() throws IOException {
-        mockByteChannel("Not JSON".getBytes(Charset.forName("UTF-8")));
-        parser.parseSSLConfiguration(agentChannel);
-    }
-    
-    @Test(expected=IOException.class)
-    public void testBadConfigRoot() throws IOException {
-        byte[] encoded = toJson(new JsonArray());
-        mockByteChannel(encoded);
-        parser.parseSSLConfiguration(agentChannel);
-    }
-    
-    @Test(expected=IOException.class)
-    public void testParamsMissing() throws IOException {
-        byte[] encoded = toJson(new JsonObject());
-        mockByteChannel(encoded);
-        parser.parseSSLConfiguration(agentChannel);
-    }
-    
-    @Test(expected=IOException.class)
-    public void testBadParams() throws IOException {
-        JsonObject root = createConfig();
-        root.add(CommandChannelConstants.SSL_JSON_ROOT, new JsonArray());
-        
-        byte[] encoded = toJson(root);
-        mockByteChannel(encoded);
-        parser.parseSSLConfiguration(agentChannel);
-    }
-    
-    @Test(expected=IOException.class)
-    public void testNullParams() throws IOException {
-        JsonObject root = createConfig();
-        root.add(CommandChannelConstants.SSL_JSON_ROOT, null);
-        
-        byte[] encoded = toJson(root);
-        mockByteChannel(encoded);
-        parser.parseSSLConfiguration(agentChannel);
-    }
-    
-    @Test(expected=IOException.class)
-    public void testKeystoreFileMissing() throws IOException {
-        JsonObject root = createConfig();
-        JsonObject params = root.get(CommandChannelConstants.SSL_JSON_ROOT).getAsJsonObject();
-        params.remove(CommandChannelConstants.SSL_JSON_KEYSTORE_FILE);
-        
-        byte[] encoded = toJson(root);
-        mockByteChannel(encoded);
-        parser.parseSSLConfiguration(agentChannel);
-    }
-    
-    @Test(expected=IOException.class)
-    public void testKeystoreFileBad() throws IOException {
-        JsonObject root = createConfig();
-        JsonObject params = root.get(CommandChannelConstants.SSL_JSON_ROOT).getAsJsonObject();
-        params.add(CommandChannelConstants.SSL_JSON_KEYSTORE_FILE, new JsonArray());
-        
-        byte[] encoded = toJson(root);
-        mockByteChannel(encoded);
-        parser.parseSSLConfiguration(agentChannel);
-    }
-    
-    @Test(expected=IOException.class)
-    public void testKeystorePassMissing() throws IOException {
-        JsonObject root = createConfig();
-        JsonObject params = root.get(CommandChannelConstants.SSL_JSON_ROOT).getAsJsonObject();
-        params.remove(CommandChannelConstants.SSL_JSON_KEYSTORE_PASS);
-        
-        byte[] encoded = toJson(root);
-        mockByteChannel(encoded);
-        parser.parseSSLConfiguration(agentChannel);
-    }
-    
-    @Test(expected=IOException.class)
-    public void testKeystorePassBad() throws IOException {
-        JsonObject root = createConfig();
-        JsonObject params = root.get(CommandChannelConstants.SSL_JSON_ROOT).getAsJsonObject();
-        params.add(CommandChannelConstants.SSL_JSON_KEYSTORE_PASS, new JsonArray());
-        
-        byte[] encoded = toJson(root);
-        mockByteChannel(encoded);
-        parser.parseSSLConfiguration(agentChannel);
-    }
-    
-    @Test(expected=IOException.class)
-    public void testCmdChannelMissing() throws IOException {
-        JsonObject root = createConfig();
-        JsonObject params = root.get(CommandChannelConstants.SSL_JSON_ROOT).getAsJsonObject();
-        params.remove(CommandChannelConstants.SSL_JSON_COMMAND_CHANNEL);
-        
-        byte[] encoded = toJson(root);
-        mockByteChannel(encoded);
-        parser.parseSSLConfiguration(agentChannel);
-    }
-    
-    @Test(expected=IOException.class)
-    public void testCmdChannelBad() throws IOException {
-        JsonObject root = createConfig();
-        JsonObject params = root.get(CommandChannelConstants.SSL_JSON_ROOT).getAsJsonObject();
-        params.add(CommandChannelConstants.SSL_JSON_COMMAND_CHANNEL, new JsonArray());
-        
-        byte[] encoded = toJson(root);
-        mockByteChannel(encoded);
-        parser.parseSSLConfiguration(agentChannel);
-    }
-    
-    @Test(expected=IOException.class)
-    public void testCmdChannelNull() throws IOException {
-        JsonObject root = createConfig();
-        JsonObject params = root.get(CommandChannelConstants.SSL_JSON_ROOT).getAsJsonObject();
-        params.addProperty(CommandChannelConstants.SSL_JSON_COMMAND_CHANNEL, (String) null);
-        
-        byte[] encoded = toJson(root);
-        mockByteChannel(encoded);
-        parser.parseSSLConfiguration(agentChannel);
-    }
-    
-    @Test(expected=IOException.class)
-    public void testBackingStorageMissing() throws IOException {
-        JsonObject root = createConfig();
-        JsonObject params = root.get(CommandChannelConstants.SSL_JSON_ROOT).getAsJsonObject();
-        params.remove(CommandChannelConstants.SSL_JSON_BACKING_STORAGE);
-        
-        byte[] encoded = toJson(root);
-        mockByteChannel(encoded);
-        parser.parseSSLConfiguration(agentChannel);
-    }
-    
-    @Test(expected=IOException.class)
-    public void testBackingStorageBad() throws IOException {
-        JsonObject root = createConfig();
-        JsonObject params = root.get(CommandChannelConstants.SSL_JSON_ROOT).getAsJsonObject();
-        params.add(CommandChannelConstants.SSL_JSON_BACKING_STORAGE, new JsonArray());
-        
-        byte[] encoded = toJson(root);
-        mockByteChannel(encoded);
-        parser.parseSSLConfiguration(agentChannel);
-    }
-    
-    @Test(expected=IOException.class)
-    public void testBackingStorageNull() throws IOException {
-        JsonObject root = createConfig();
-        JsonObject params = root.get(CommandChannelConstants.SSL_JSON_ROOT).getAsJsonObject();
-        params.addProperty(CommandChannelConstants.SSL_JSON_BACKING_STORAGE, (String) null);
-        
-        byte[] encoded = toJson(root);
-        mockByteChannel(encoded);
-        parser.parseSSLConfiguration(agentChannel);
-    }
-    
-    @Test(expected=IOException.class)
-    public void testHostnameVerifyMissing() throws IOException {
-        JsonObject root = createConfig();
-        JsonObject params = root.get(CommandChannelConstants.SSL_JSON_ROOT).getAsJsonObject();
-        params.remove(CommandChannelConstants.SSL_JSON_HOSTNAME_VERIFICATION);
-        
-        byte[] encoded = toJson(root);
-        mockByteChannel(encoded);
-        parser.parseSSLConfiguration(agentChannel);
-    }
-    
-    @Test(expected=IOException.class)
-    public void testHostnameVerifyBad() throws IOException {
-        JsonObject root = createConfig();
-        JsonObject params = root.get(CommandChannelConstants.SSL_JSON_ROOT).getAsJsonObject();
-        params.add(CommandChannelConstants.SSL_JSON_HOSTNAME_VERIFICATION, new JsonArray());
-        
-        byte[] encoded = toJson(root);
-        mockByteChannel(encoded);
-        parser.parseSSLConfiguration(agentChannel);
-    }
-    
-    @Test(expected=IOException.class)
-    public void testHostnameVerifyNull() throws IOException {
-        JsonObject root = createConfig();
-        JsonObject params = root.get(CommandChannelConstants.SSL_JSON_ROOT).getAsJsonObject();
-        params.addProperty(CommandChannelConstants.SSL_JSON_HOSTNAME_VERIFICATION, (String) null);
-        
-        byte[] encoded = toJson(root);
-        mockByteChannel(encoded);
-        parser.parseSSLConfiguration(agentChannel);
-    }
-    
-    private JsonObject createConfig() {
-        JsonObject params = new JsonObject();
-        params.addProperty(CommandChannelConstants.SSL_JSON_KEYSTORE_FILE, "/path/to/keystore");
-        params.addProperty(CommandChannelConstants.SSL_JSON_KEYSTORE_PASS, "My Keystore Password");
-        params.addProperty(CommandChannelConstants.SSL_JSON_COMMAND_CHANNEL, true);
-        params.addProperty(CommandChannelConstants.SSL_JSON_BACKING_STORAGE, true);
-        params.addProperty(CommandChannelConstants.SSL_JSON_HOSTNAME_VERIFICATION, false);
-        
-        JsonObject root = new JsonObject();
-        root.add(CommandChannelConstants.SSL_JSON_ROOT, params);
-        return root;
-    }
-
-    private byte[] toJson(JsonElement element) {
-        Gson gson = new GsonBuilder().serializeNulls().create();
-        String jsonString = gson.toJson(element);
-        return jsonString.getBytes(Charset.forName("UTF-8"));
-    }
-
-    private void mockByteChannel(final byte[] encoded) throws IOException {
-        when(agentChannel.readMessage()).thenReturn(ByteBuffer.wrap(encoded));
-    }
-}
--- a/agent/command-server/src/test/java/com/redhat/thermostat/agent/command/server/internal/ServerHandlerTest.java	Tue Jun 13 12:02:12 2017 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,154 +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
- * <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.command.server.internal;
-
-import static org.junit.Assert.assertEquals;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.isA;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import java.net.InetSocketAddress;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.mockito.ArgumentCaptor;
-
-import com.redhat.thermostat.agent.command.server.internal.ServerHandler.SSLHandshakeDoneListener;
-import com.redhat.thermostat.agent.ipc.client.IPCMessageChannel;
-import com.redhat.thermostat.common.command.Request;
-import com.redhat.thermostat.common.command.Request.RequestType;
-import com.redhat.thermostat.common.command.Response;
-import com.redhat.thermostat.common.command.Response.ResponseType;
-import com.redhat.thermostat.shared.config.SSLConfiguration;
-
-import io.netty.channel.Channel;
-import io.netty.channel.ChannelFuture;
-import io.netty.channel.ChannelHandlerContext;
-import io.netty.channel.ChannelPipeline;
-import io.netty.handler.ssl.SslHandler;
-import io.netty.util.concurrent.Future;
-
-public class ServerHandlerTest {
-
-    private ChannelPipeline pipeline;
-    private ChannelHandlerContext ctx;
-    private ServerHandler handler;
-    private IPCMessageChannel agentChannel;
-    private JsonResponseParser responseParser;
-    private JsonRequestEncoder requestEncoder;
-
-    @Before
-    public void setup() {
-        Channel channel = mock(Channel.class);
-        pipeline = mock(ChannelPipeline.class);
-        ChannelFuture channelFuture = mock(ChannelFuture.class);
-        when(pipeline.write(isA(Response.class))).thenReturn(channelFuture);
-
-        when(channel.pipeline()).thenReturn(pipeline);
-        ctx = mock(ChannelHandlerContext.class);
-        when(ctx.channel()).thenReturn(channel);
-        
-        SSLConfiguration mockSSLConf = mock(SSLConfiguration.class);
-        when(mockSSLConf.enableForCmdChannel()).thenReturn(true);
-        agentChannel = mock(IPCMessageChannel.class);
-        when(agentChannel.isOpen()).thenReturn(true);
-        
-        requestEncoder = mock(JsonRequestEncoder.class);
-        responseParser = mock(JsonResponseParser.class);
-        handler = new ServerHandler(mockSSLConf, agentChannel, requestEncoder, responseParser);
-    }
-
-    @Test
-    public void channelActiveAddsSSLListener() throws Exception {
-        ChannelPipeline pipeline = mock(ChannelPipeline.class);
-        when(ctx.pipeline()).thenReturn(pipeline);
-        SslHandler sslHandler = mock(SslHandler.class);
-        when(pipeline.get(SslHandler.class)).thenReturn(sslHandler);
-        @SuppressWarnings("unchecked")
-        Future<Channel> handshakeFuture = mock(Future.class);
-        when(sslHandler.handshakeFuture()).thenReturn(handshakeFuture);
-        
-        handler.channelActive(ctx);
-        verify(handshakeFuture).addListener(any(SSLHandshakeDoneListener.class));
-    }
-
-    @Test
-    public void invalidRequestReturnsAnErrorResponse() throws Exception {
-        // target and receiver are null
-        Request request = mock(Request.class);
-        handler.channelRead0(ctx, request);
-        verify(requestEncoder, never()).encodeRequestAndSend(agentChannel, request);
-        
-        ArgumentCaptor<Response> responseCaptor = ArgumentCaptor.forClass(Response.class);
-        verify(pipeline).writeAndFlush(responseCaptor.capture());
-        assertEquals(ResponseType.ERROR, responseCaptor.getValue().getType());
-    }
-    
-    @Test
-    public void testRequestReceived() throws Exception {
-        Request request = new Request(RequestType.RESPONSE_EXPECTED, new InetSocketAddress("127.0.0.1", 123));
-        request.setReceiver("com.example.MyReceiver");
-        request.setParameter("hello", "world");
-        
-        Response response = new Response(ResponseType.OK);
-        when(responseParser.parseResponse(agentChannel)).thenReturn(response);
-
-        handler.channelRead0(ctx, request);
-        
-        verify(requestEncoder).encodeRequestAndSend(agentChannel, request);
-        verify(pipeline).writeAndFlush(response);
-    }
-    
-    @Test
-    public void testRequestReceivedChannelClosed() throws Exception {
-        Request request = new Request(RequestType.RESPONSE_EXPECTED, new InetSocketAddress("127.0.0.1", 123));
-        request.setReceiver("com.example.MyReceiver");
-        request.setParameter("hello", "world");
-        
-        when(agentChannel.isOpen()).thenReturn(false);
-        handler.channelRead0(ctx, request);
-        verify(requestEncoder, never()).encodeRequestAndSend(agentChannel, request);
-        
-        ArgumentCaptor<Response> responseCaptor = ArgumentCaptor.forClass(Response.class);
-        verify(pipeline).writeAndFlush(responseCaptor.capture());
-        assertEquals(ResponseType.ERROR, responseCaptor.getValue().getType());
-    }
-    
-}
\ No newline at end of file
--- a/agent/command/pom.xml	Tue Jun 13 12:02:12 2017 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,175 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-
- 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
- <http://www.gnu.org/licenses/>.
-
- Linking this code with other modules is making a combined work
- based on this code.  Thus, the terms and conditions of the GNU
- General Public License cover the whole combination.
-
- As a special exception, the copyright holders of this code give
- you permission to link this code with independent modules to
- produce an executable, regardless of the license terms of these
- independent modules, and to copy and distribute the resulting
- executable under terms of your choice, provided that you also
- meet, for each linked independent module, the terms and conditions
- of the license of that module.  An independent module is a module
- which is not derived from or based on this code.  If you modify
- this code, you may extend this exception to your version of the
- library, but you are not obligated to do so.  If you do not wish
- to do so, delete this exception statement from your version.
-
--->
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
-  <modelVersion>4.0.0</modelVersion>
-
-  <parent>
-    <groupId>com.redhat.thermostat</groupId>
-    <artifactId>thermostat-agent</artifactId>
-    <version>1.99.12-SNAPSHOT</version>
-  </parent>
-
-  <artifactId>thermostat-agent-command</artifactId>
-  <packaging>bundle</packaging>
-
-  <name>Thermostat Command Channel Server</name>
-
-  <dependencies>
-    <dependency>
-      <groupId>junit</groupId>
-      <artifactId>junit</artifactId>
-      <scope>test</scope>
-    </dependency>
-    <dependency>
-      <groupId>org.mockito</groupId>
-      <artifactId>mockito-core</artifactId>
-      <scope>test</scope>
-    </dependency>
-    <dependency>
-      <groupId>org.powermock</groupId>
-      <artifactId>powermock-api-mockito</artifactId>
-      <scope>test</scope>
-    </dependency>
-    <dependency>
-      <groupId>org.powermock</groupId>
-      <artifactId>powermock-module-junit4</artifactId>
-      <scope>test</scope>
-    </dependency>
-    <dependency>
-      <groupId>org.osgi</groupId>
-      <artifactId>org.osgi.core</artifactId>
-    </dependency>
-
-    <dependency>
-      <groupId>commons-codec</groupId>
-      <artifactId>commons-codec</artifactId>
-      <version>${commons-codec.version}</version>
-    </dependency>
-    <dependency>
-      <groupId>com.google.code.gson</groupId>
-      <artifactId>gson</artifactId>
-    </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-agent-ipc-server</artifactId>
-      <version>${project.version}</version>
-    </dependency>
-    <dependency>
-      <groupId>com.redhat.thermostat</groupId>
-      <artifactId>thermostat-storage-core</artifactId>
-      <version>${project.version}</version>
-    </dependency>
-    <dependency>
-      <groupId>com.redhat.thermostat</groupId>
-      <artifactId>thermostat-common-test</artifactId>
-      <version>${project.version}</version>
-      <scope>test</scope>
-    </dependency>
-  </dependencies>
-
-  <build>
-    <plugins>
-      <plugin>
-        <groupId>org.apache.felix</groupId>
-        <artifactId>maven-bundle-plugin</artifactId>
-        <extensions>true</extensions>
-        <configuration>
-          <instructions>
-            <Bundle-Vendor>Red Hat, Inc.</Bundle-Vendor>
-            <Bundle-Activator>com.redhat.thermostat.agent.command.internal.Activator</Bundle-Activator>
-            <Bundle-SymbolicName>com.redhat.thermostat.agent.command</Bundle-SymbolicName>
-            <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>
-
-
-      <plugin>
-        <groupId>org.apache.maven.plugins</groupId>
-        <artifactId>maven-resources-plugin</artifactId>
-        <executions>
-          <execution>
-            <id>copy</id>
-            <phase>generate-resources</phase>
-            <goals>
-              <goal>copy-resources</goal>
-            </goals>
-            <configuration>
-              <overwrite>true</overwrite>
-              <outputDirectory>${project.build.directory}</outputDirectory>
-              <resources>
-                <resource>
-                  <directory>../../common/portability/target</directory>
-                  <includes>
-                    <include>${sharedlib.prefix}thermostat-common-portability${sharedlib.suffix}</include>
-                  </includes>
-                </resource>
-              </resources>
-            </configuration>
-          </execution>
-        </executions>
-      </plugin>
-
-      <plugin>
-        <groupId>org.apache.maven.plugins</groupId>
-        <artifactId>maven-surefire-plugin</artifactId>
-        <configuration>
-          <systemPropertyVariables>
-            <com.redhat.thermostat.shared.loader.testNativesHome>${project.build.directory}</com.redhat.thermostat.shared.loader.testNativesHome>
-          </systemPropertyVariables>
-        </configuration>
-      </plugin>
-    </plugins>
-  </build>
-
-</project>
-
--- a/agent/command/src/main/java/com/redhat/thermostat/agent/command/ConfigurationServer.java	Tue Jun 13 12:02:12 2017 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,69 +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
- * <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.command;
-
-import java.io.IOException;
-
-import com.redhat.thermostat.annotations.Service;
-import com.redhat.thermostat.common.command.Request;
-
-/**
- * An agent-side service that allows starting and stopping the command-channel
- * server.
- *
- * The command-channel server allows the agent to receive and process
- * {@link Request}s.
- */
-@Service
-public interface ConfigurationServer {
-
-    /**
-     * Starts the configuration server so it listens at the interface specified
-     * by the {@code host} and {@code port}.
-     * @throws IOException if it fails to listen
-     */
-    void startListening(String host, int port) throws IOException;
-
-    /**
-     * Shuts down the configuration server. No additional {@link Request}s will
-     * be accepted after this is called, but existing ones may be processed to
-     * completion.
-     */
-    void stopListening();
-
-}
-
--- a/agent/command/src/main/java/com/redhat/thermostat/agent/command/internal/Activator.java	Tue Jun 13 12:02:12 2017 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,102 +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
- * <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.command.internal;
-
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-import org.osgi.framework.BundleActivator;
-import org.osgi.framework.BundleContext;
-import org.osgi.framework.ServiceRegistration;
-
-import com.redhat.thermostat.agent.command.ConfigurationServer;
-import com.redhat.thermostat.agent.command.ReceiverRegistry;
-import com.redhat.thermostat.agent.ipc.server.AgentIPCService;
-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.utils.LoggingUtils;
-import com.redhat.thermostat.shared.config.CommonPaths;
-import com.redhat.thermostat.shared.config.SSLConfiguration;
-
-public class Activator implements BundleActivator {
-
-    private static final Logger logger = LoggingUtils.getLogger(Activator.class);
-
-    @SuppressWarnings("rawtypes")
-    private ServiceRegistration confServerRegistration;
-    private ReceiverRegistry receivers;
-    private MultipleServiceTracker sslConfigTracker;
-
-    @Override
-    public void start(final BundleContext context) throws Exception {
-        logger.log(Level.INFO, "activating thermostat-agent-confserver");
-        receivers = new ReceiverRegistry(context);
-        receivers.registerReceiver(new PingReceiver());
-        
-        Class<?>[] deps = { CommonPaths.class, SSLConfiguration.class, AgentIPCService.class };
-        sslConfigTracker = new MultipleServiceTracker(context, deps, new Action() {
-            
-            @Override
-            public void dependenciesAvailable(DependencyProvider services) {
-                CommonPaths paths = services.get(CommonPaths.class);
-                SSLConfiguration sslConf = services.get(SSLConfiguration.class);
-                AgentIPCService ipcService = services.get(AgentIPCService.class);
-                CommandChannelDelegate confServer = new CommandChannelDelegate(receivers, sslConf, 
-                        paths.getSystemBinRoot(), ipcService);
-                confServerRegistration = context.registerService(ConfigurationServer.class.getName(), confServer, null);
-            }
-
-            @Override
-            public void dependenciesUnavailable() {
-                confServerRegistration.unregister();
-                confServerRegistration = null;
-            }
-        });
-        sslConfigTracker.open();
-    }
-
-    @Override
-    public void stop(BundleContext context) throws Exception {
-        sslConfigTracker.close();
-        if (confServerRegistration != null) {
-            confServerRegistration.unregister();
-        }
-    }
-
-}
-
--- a/agent/command/src/main/java/com/redhat/thermostat/agent/command/internal/AgentRequestDecoder.java	Tue Jun 13 12:02:12 2017 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,147 +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
- * <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.command.internal;
-
-import java.io.IOException;
-import java.net.InetSocketAddress;
-import java.nio.charset.Charset;
-import java.util.Map;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-import com.google.gson.Gson;
-import com.google.gson.GsonBuilder;
-import com.google.gson.JsonElement;
-import com.google.gson.JsonObject;
-import com.google.gson.JsonParseException;
-import com.google.gson.JsonParser;
-import com.redhat.thermostat.common.command.Request;
-import com.redhat.thermostat.common.command.Request.RequestType;
-import com.redhat.thermostat.common.utils.LoggingUtils;
-
-class AgentRequestDecoder {
-    
-    private static final Logger logger = LoggingUtils.getLogger(AgentRequestDecoder.class);
-
-    Request decodeRequest(byte[] jsonRequest) throws IOException {
-        logger.log(Level.FINEST, "Agent: decoding request received from command channel");
-        
-        String jsonRequestString = new String(jsonRequest, Charset.forName("UTF-8"));
-        Request request;
-        try {
-            GsonBuilder builder = new GsonBuilder();
-            Gson gson = builder.create();
-            JsonParser parser = new JsonParser();
-            JsonElement parsed = parser.parse(jsonRequestString);
-            
-            // Get root of JsonObject tree
-            JsonObject root = toJsonObject(parsed);
-            JsonElement requestElement = root.get(CommandChannelConstants.REQUEST_JSON_TOP);
-            requireNonJsonNull(requestElement, "Request data missing");
-            JsonObject requestObj = toJsonObject(requestElement);
-            
-            // Create Request
-            RequestType requestType = parseRequestType(gson, requestObj);
-            InetSocketAddress target = parseTargetAddress(gson, requestObj);
-            request = new Request(requestType, target);
-            
-            // Add parameters to Request
-            JsonElement paramsElement = requestObj.get(CommandChannelConstants.REQUEST_JSON_PARAMS);
-            requireNonJsonNull(paramsElement, "Request parameters missing");
-            JsonObject paramsObj = toJsonObject(paramsElement);
-            for (Map.Entry<String, JsonElement> param : paramsObj.entrySet()) {
-                String paramName = param.getKey();
-                
-                JsonElement value = param.getValue();
-                // Parameter value should be a string (JsonPrimitive) or null (JsonNull)
-                if (!value.isJsonPrimitive() && !value.isJsonNull()) {
-                    throw new IOException("Malformed parameter value");
-                }
-                String paramValue = gson.fromJson(value, String.class);
-                
-                request.setParameter(paramName, paramValue);
-            }
-        } catch (JsonParseException e) {
-            throw new IOException("Failed to parse request", e);
-        }
-        
-        return request;
-    }
-
-    private RequestType parseRequestType(Gson gson, JsonObject requestObj) throws IOException {
-        JsonElement requestTypeObj = requestObj.get(CommandChannelConstants.REQUEST_JSON_TYPE);
-        requireNonJsonNull(requestTypeObj, "Request type missing");
-        String requestTypeString = gson.fromJson(requestTypeObj, String.class);
-        try {
-            return RequestType.valueOf(requestTypeString);
-        } catch (IllegalArgumentException e) {
-            throw new IOException("Invalid request type: " + requestTypeString);
-        }
-    }
-
-    private InetSocketAddress parseTargetAddress(Gson gson, JsonObject requestObj) throws IOException {
-        JsonElement hostnameObj = requestObj.get(CommandChannelConstants.REQUEST_JSON_HOST);
-        requireNonJsonNull(hostnameObj, "Target hostname missing");
-        String hostname = gson.fromJson(hostnameObj, String.class);
-        
-        JsonElement portObj = requestObj.get(CommandChannelConstants.REQUEST_JSON_PORT);
-        requireNonJsonNull(portObj, "Target port number missing");
-        int port = gson.fromJson(portObj, int.class);
-        
-        // Ensure address is a valid one
-        try {
-            return new InetSocketAddress(hostname, port);
-        } catch (IllegalArgumentException e) {
-            throw new IOException("Invalid target address");
-        }
-    }
-    
-    private JsonObject toJsonObject(JsonElement element) throws IOException {
-        if (!element.isJsonObject()) {
-            throw new IOException("Malformed data received");
-        }
-        return element.getAsJsonObject();
-    }
-
-    private void requireNonJsonNull(JsonElement element, String errorMessage) throws IOException {
-        if (element == null || element.isJsonNull()) {
-            throw new IOException(errorMessage);
-        }
-    }
-    
-}
-
--- a/agent/command/src/main/java/com/redhat/thermostat/agent/command/internal/AgentResponseEncoder.java	Tue Jun 13 12:02:12 2017 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,61 +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
- * <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.command.internal;
-
-import java.nio.charset.Charset;
-
-import com.google.gson.Gson;
-import com.google.gson.GsonBuilder;
-import com.google.gson.JsonObject;
-import com.redhat.thermostat.common.command.Response;
-
-class AgentResponseEncoder {
-
-    byte[] encodeResponse(Response response) {
-        GsonBuilder builder = new GsonBuilder();
-        Gson gson = builder.create();
-        
-        JsonObject responseTypeObj = new JsonObject();
-        responseTypeObj.addProperty(CommandChannelConstants.RESPONSE_JSON_TYPE, response.getType().name());
-        JsonObject responseRoot = new JsonObject();
-        responseRoot.add(CommandChannelConstants.RESPONSE_JSON_TOP, responseTypeObj);
-        
-        String jsonResponse = gson.toJson(responseRoot);
-        return jsonResponse.getBytes(Charset.forName("UTF-8"));
-    }
-    
-}
--- a/agent/command/src/main/java/com/redhat/thermostat/agent/command/internal/CommandChannelConstants.java	Tue Jun 13 12:02:12 2017 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,66 +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
- * <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.command.internal;
-
-import java.nio.charset.Charset;
-
-interface CommandChannelConstants {
-
-    // Server startup state tokens
-    byte[] SERVER_STARTED_TOKEN = "<SERVER STARTED>".getBytes(Charset.forName("UTF-8"));
-    byte[] SERVER_READY_TOKEN = "<SERVER READY>".getBytes(Charset.forName("UTF-8"));
-    
-    // SSLConfiguration JSON members
-    String SSL_JSON_ROOT = "sslConfiguration";
-    String SSL_JSON_KEYSTORE_FILE = "keystoreFile";
-    String SSL_JSON_KEYSTORE_PASS = "keystorePass";
-    String SSL_JSON_COMMAND_CHANNEL = "enabledCommandChannel";
-    String SSL_JSON_BACKING_STORAGE = "enabledBackingStorage";
-    String SSL_JSON_HOSTNAME_VERIFICATION = "disableHostnameVerification";
-    
-    // Request JSON members
-    String REQUEST_JSON_TOP = "request";
-    String REQUEST_JSON_TYPE = "type";
-    String REQUEST_JSON_HOST = "targetHost";
-    String REQUEST_JSON_PORT = "targetPort";
-    String REQUEST_JSON_PARAMS = "parameters";
-    
-    // Response JSON members
-    String RESPONSE_JSON_TOP = "response";
-    String RESPONSE_JSON_TYPE = "type";
-
-}
--- a/agent/command/src/main/java/com/redhat/thermostat/agent/command/internal/CommandChannelDelegate.java	Tue Jun 13 12:02:12 2017 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,348 +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
- * <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.command.internal;
-
-import java.io.File;
-import java.io.IOException;
-import java.nio.ByteBuffer;
-import java.nio.charset.Charset;
-import java.nio.file.FileSystems;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.attribute.UserPrincipal;
-import java.util.Arrays;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.atomic.AtomicInteger;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-import com.redhat.thermostat.common.portability.PortableProcessFactory;
-import com.redhat.thermostat.agent.command.ConfigurationServer;
-import com.redhat.thermostat.agent.command.ReceiverRegistry;
-import com.redhat.thermostat.agent.command.RequestReceiver;
-import com.redhat.thermostat.agent.ipc.server.AgentIPCService;
-import com.redhat.thermostat.agent.ipc.server.IPCMessage;
-import com.redhat.thermostat.agent.ipc.server.ThermostatIPCCallbacks;
-import com.redhat.thermostat.common.command.Message.MessageType;
-import com.redhat.thermostat.common.command.Request;
-import com.redhat.thermostat.common.command.Response;
-import com.redhat.thermostat.common.command.Response.ResponseType;
-import com.redhat.thermostat.common.utils.LoggingUtils;
-import com.redhat.thermostat.shared.config.SSLConfiguration;
-import com.redhat.thermostat.shared.config.OS;
-
-class CommandChannelDelegate implements ConfigurationServer, ThermostatIPCCallbacks {
-    
-    private static final Logger logger = LoggingUtils.getLogger(CommandChannelDelegate.class);
-    private static final String CMD_NAME = "thermostat-command-channel";
-    private static final String IPC_SERVER_NAME = "command-channel";
-
-    // States for 'state' field
-    private static final int STATE_NOT_STARTED = 0;
-    private static final int STATE_STARTED = 1;
-    private static final int STATE_READY = 2;
-    private static final int STATE_ERROR = -1;
-    
-    private final ReceiverRegistry receivers;
-    private final SSLConfiguration sslConf;
-    private final File binPath;
-    private final AgentIPCService ipcService;
-    private final CountDownLatch readyLatch;
-    private final SSLConfigurationEncoder sslEncoder;
-    private final AgentRequestDecoder requestDecoder;
-    private final AgentResponseEncoder responseEncoder;
-    private final ProcessCreator procCreator;
-    private final ProcessUserInfoBuilder userInfoBuilder;
-    private final FileSystemUtils fsUtils;
-    private Process process;
-    private AtomicInteger state;
-    
-    CommandChannelDelegate(ReceiverRegistry receivers, SSLConfiguration sslConf, File binPath,
-            AgentIPCService ipcService) {
-        this(receivers, sslConf, binPath, ipcService, new CountDownLatch(1), new SSLConfigurationEncoder(), 
-                new AgentRequestDecoder(), new AgentResponseEncoder(), new ProcessUserInfoBuilder(),
-                new FileSystemUtils(), new ProcessCreator());
-    }
-
-    /** For testing only */
-    CommandChannelDelegate(ReceiverRegistry receivers, SSLConfiguration sslConf, File binPath, 
-            AgentIPCService ipcService, CountDownLatch readyLatch, SSLConfigurationEncoder sslEncoder, 
-            AgentRequestDecoder requestDecoder, AgentResponseEncoder responseEncoder,
-            ProcessUserInfoBuilder userInfoBuilder, FileSystemUtils fsUtils, ProcessCreator procCreator) {
-        this.receivers = receivers;
-        this.sslConf = sslConf;
-        this.binPath = binPath;
-        this.ipcService = ipcService;
-        this.readyLatch = readyLatch;
-        this.sslEncoder = sslEncoder;
-        this.requestDecoder = requestDecoder;
-        this.responseEncoder = responseEncoder;
-        this.procCreator = procCreator;
-        this.userInfoBuilder = userInfoBuilder;
-        this.fsUtils = fsUtils;
-        this.state = new AtomicInteger();
-    }
-
-    @Override
-    public void startListening(String hostname, int port) throws IOException {
-        // Determine if this process is running as a privileged user
-        // NOTE: on Windows, security is handled by permission bits, not userid.
-        if (OS.IS_UNIX && userInfoBuilder.isPrivilegedUser()) {
-            // Get owner of command channel script, which will also be the user running it
-            Path cmdPath = fsUtils.getPath(binPath.getAbsolutePath(), CMD_NAME);
-            UserPrincipal unprivilegedPrincipal = fsUtils.getOwner(cmdPath);
-            // Create IPC server owned by user running command channel script
-            ipcService.createServer(IPC_SERVER_NAME, this, unprivilegedPrincipal);
-        } else {
-            // Create IPC server owned by current user
-            ipcService.createServer(IPC_SERVER_NAME, this);
-        }
-        
-        startServer(hostname, port);
-    }
-
-    @Override
-    public void stopListening() {
-        try {
-            killServer();
-            // Clean up IPC server
-            destroyIPCServerIfExists();
-        } catch (IOException e) {
-            logger.log(Level.WARNING, "Error occurred while stopping command channel server", e);
-        }
-    }
-
-    private void destroyIPCServerIfExists() throws IOException {
-        if (ipcService.serverExists(IPC_SERVER_NAME)) {
-            ipcService.destroyServer(IPC_SERVER_NAME);
-        }
-    }
-    
-    @Override
-    public void messageReceived(IPCMessage message) {
-        byte[] result = null;
-        ByteBuffer buf = message.get();
-        byte[] data = new byte[buf.limit()];
-        buf.get(data);
-        
-        switch (state.get()) {
-        case STATE_NOT_STARTED:
-            // First message from server just tells us it's started
-            boolean started = checkStart(data);
-            if (started) {
-                // Return SSL Configuration
-                try {
-                    result = sslEncoder.encodeAsJson(sslConf);
-                } catch (IOException e) {
-                    logger.log(Level.SEVERE, "Unable to encode SSL configuration", e);
-                    state.set(STATE_ERROR);
-                }
-            }
-            break;
-        case STATE_STARTED:
-            // Second message from server indicates that it has processed
-            // the SSL configuration and is ready to receive requests
-            checkReady(data);
-            readyLatch.countDown();
-            // No response
-            break;
-        case STATE_READY:
-            // Parse requests
-            Request request;
-            try {
-                request = parseRequest(data);
-                // Return response as bytes
-                result = requestReceived(request);
-            } catch (IOException e) {
-                logger.log(Level.WARNING, "Unable to process request from command channel", e);
-                // Send error response
-                Response error = new Response(ResponseType.ERROR);
-                result = encodeResponse(error);
-            }
-            break;
-        default: // STATE_ERROR
-            // Do nothing
-        }
-        
-        if (state.get() == STATE_ERROR && readyLatch.getCount() > 0) {
-            // Will never become ready, so throw exception
-            readyLatch.countDown();
-        }
-        
-        // Send reply using IPC service
-        if (result != null) {
-            ByteBuffer reply = ByteBuffer.wrap(result);
-            try {
-                message.reply(reply);
-            } catch (IOException e) {
-                logger.log(Level.WARNING, "Failed to send reply via command channel", e);
-            }
-        }
-    }
-    
-    private void startServer(String hostname, int port) throws IOException {
-        File ipcConfig = ipcService.getConfigurationFile();
-        String[] processArgs = OS.IS_UNIX
-                ? new String[]{ binPath.getAbsolutePath() + File.separator + CMD_NAME, hostname,
-                                String.valueOf(port), ipcConfig.getAbsolutePath() }
-                : new String[] { "cmd", "/c", binPath.getAbsolutePath() + File.separator + CMD_NAME + ".cmd", hostname,
-                                String.valueOf(port), ipcConfig.getAbsolutePath(), "" + PortableProcessFactory.getInstance().getCurrentProcessPid()};
-
-        ProcessBuilder builder = new ProcessBuilder(processArgs);
-        // This has the problem of some messages/Exceptions not
-        // showing up in the parent's stderr stream if used together
-        // with JUL-logging. One such example is CNFE in
-        // the child process. In that case your best bet is
-        // Redirect.to(File).
-        builder.inheritIO();
-        logger.info("Starting command channel server process");
-        process = procCreator.startProcess(builder);
-        
-        // Wait for started notification
-        try {
-            waitForStarted();
-        } catch (InterruptedException e) {
-            Thread.currentThread().interrupt();
-        }
-    }
-
-    private void waitForStarted() throws IOException, InterruptedException {
-        // Wait for started token
-        readyLatch.await();
-        // If not started at this point, there was a token mismatch
-        if (state.get() != STATE_READY) {
-            throw new IOException("Command channel server failed to start");
-        }
-        logger.info("Command channel server ready to accept requests");
-    }
-
-    private void killServer() {
-        if (process != null) {
-            logger.info("Stopping command channel server process");
-            process.destroy();
-        }
-    }
-    
-    private byte[] requestReceived(Request request) {
-        String receiverName = request.getReceiver();
-        MessageType requestType = request.getType();
-        logger.info("Request received: '" + requestType + "' for '" + receiverName + "'");
-        boolean authSucceeded = authenticateRequestIfNecessary(request);
-        Response response = null;
-        if (! authSucceeded) {
-            logger.info("Authentication for request failed");
-            response = new Response(ResponseType.AUTH_FAILED);
-        } else {
-            if (receiverName != null && requestType != null) {
-                RequestReceiver receiver = receivers.getReceiver(receiverName);
-                if (receiver != null) {
-                    response = receiver.receive(request);
-                }
-            }
-
-            if (response == null) {
-                logger.info("Receiver with name '" + receiverName + "' not found ");
-                response = new Response(ResponseType.ERROR);
-            }
-        }
-        
-        return encodeResponse(response);
-    }
-
-    private Request parseRequest(byte[] data) throws IOException {
-        return requestDecoder.decodeRequest(data);
-    }
-
-    private byte[] encodeResponse(Response response) {
-        return responseEncoder.encodeResponse(response);
-    }
-
-    private boolean authenticateRequestIfNecessary(Request request) {
-        /*
-         * FIXME Authentication no longer works after the introduction of the
-         * web gateway. For now, we bypass authentication until the new
-         * WebSockets command channel is integrated.
-         */
-        return true;
-    }
-
-    private boolean checkStart(byte[] data) {
-        boolean tokenMatch = Arrays.equals(CommandChannelConstants.SERVER_STARTED_TOKEN, data);
-        if (!tokenMatch) {
-            String message = new String(data, Charset.forName("UTF-8"));
-            logger.severe("Unexpected start message from command channel: " + message);
-            state.set(STATE_ERROR);
-        } else {
-            // Set state to indicate started
-            state.set(STATE_STARTED);
-        }
-        return tokenMatch;
-    }
-    
-    private void checkReady(byte[] data) {
-        boolean tokenMatch = Arrays.equals(CommandChannelConstants.SERVER_READY_TOKEN, data);
-        if (!tokenMatch) {
-            String message = new String(data, Charset.forName("UTF-8"));
-            logger.severe("Unexpected ready message from command channel: " + message);
-            state.set(STATE_ERROR);
-        } else {
-            // Set state to indicate ready
-            state.set(STATE_READY);
-        }
-    }
-
-    /** for testing only */
-    static class ProcessCreator {
-        Process startProcess(ProcessBuilder builder) throws IOException {
-            return builder.start();
-        }
-    }
-    
-    /** for testing only */
-    static class FileSystemUtils {
-        Path getPath(String first, String... more) {
-            return FileSystems.getDefault().getPath(first, more);
-        }
-        
-        UserPrincipal getOwner(Path path) throws IOException {
-            return Files.getOwner(path);
-        }
-    }
-    
-}
-
-
--- a/agent/command/src/main/java/com/redhat/thermostat/agent/command/internal/ProcessUserInfoBuilder.java	Tue Jun 13 12:02:12 2017 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,112 +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
- * <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.command.internal;
-
-import com.redhat.thermostat.shared.config.OS;
-
-import java.io.BufferedReader;
-import java.io.File;
-import java.io.FileReader;
-import java.io.IOException;
-
-/*
- * FIXME: This class was copied from system-backend.
- * Replace when this information is available from an API.
- */
-class ProcessUserInfoBuilder {
-
-    private static final String PROC_STATUS_SELF_PATH = "/proc/self/status";
-    private static final String PROC_STATUS_UID = "Uid:";
-    private final FileReaderCreator readerCreator;
-
-    ProcessUserInfoBuilder() {
-        this(new FileReaderCreator());
-    }
-
-    ProcessUserInfoBuilder(FileReaderCreator readerCreator) {
-        this.readerCreator = readerCreator;
-    }
-
-    private long getUid() throws IOException {
-        FileReader reader = readerCreator.create(PROC_STATUS_SELF_PATH);
-        long uid = getUidFromProcfs(new BufferedReader(reader));
-        return uid;
-    }
-
-    boolean isPrivilegedUser() throws IOException {
-        return OS.IS_LINUX ? (getUid() == 0) : false;
-    }
-
-    /*
-     * Look for the following line:
-     * Uid:  <RealUid>   <EffectiveUid>   <SavedUid>   <FSUid>
-     */
-    private long getUidFromProcfs(BufferedReader br) throws IOException {
-        long uid = -1;
-        String line;
-        while ((line = br.readLine()) != null) {
-            line = line.trim();
-            if (line.startsWith(PROC_STATUS_UID)) {
-                String[] parts = line.split("\\s+");
-                if (parts.length == 5) {
-                    try {
-                        // Use Real UID
-                        uid = Long.parseLong(parts[1]);
-                    } catch (NumberFormatException e) {
-                        throw new IOException("Unexpected output from ps command", e);
-                    }
-                }
-                else {
-                    throw new IOException("Expected 5 parts from split /proc/${pid}/status output, got " + parts.length);
-                }
-            }
-        }
-        if (uid < 0) {
-            throw new IOException("Unable to determine UID from /proc/${pid}/status");
-        }
-        return uid;
-    }
-
-    // For testing purposes
-    static class FileReaderCreator {
-        FileReader create(String path) throws IOException {
-            return new FileReader(new File(path));
-        }
-    }
-
-}
-
--- a/agent/command/src/main/java/com/redhat/thermostat/agent/command/internal/SSLConfigurationEncoder.java	Tue Jun 13 12:02:12 2017 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,84 +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
- * <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.command.internal;
-
-import java.io.File;
-import java.io.IOException;
-import java.nio.charset.Charset;
-
-import com.google.gson.Gson;
-import com.google.gson.GsonBuilder;
-import com.google.gson.JsonObject;
-import com.redhat.thermostat.shared.config.SSLConfiguration;
-
-class SSLConfigurationEncoder {
-    
-    // Total size of JSON encoded SSLConfiguration should be no more than this size in bytes
-    private static final int SSL_CONF_MAX_BYTES = 8192;
-    
-    byte[] encodeAsJson(SSLConfiguration sslConf) throws IOException {
-        GsonBuilder builder = new GsonBuilder();
-        builder.serializeNulls(); // Necessary since keystore file/password can be null
-        Gson gson = builder.create();
-        
-        JsonObject paramsObj = new JsonObject();
-        File keystoreFile = sslConf.getKeystoreFile();
-        String keystorePath = null;
-        if (keystoreFile != null) {
-            keystorePath = keystoreFile.getAbsolutePath();
-        }
-        paramsObj.addProperty(CommandChannelConstants.SSL_JSON_KEYSTORE_FILE, keystorePath);
-        paramsObj.addProperty(CommandChannelConstants.SSL_JSON_KEYSTORE_PASS, sslConf.getKeyStorePassword());
-        paramsObj.addProperty(CommandChannelConstants.SSL_JSON_COMMAND_CHANNEL, sslConf.enableForCmdChannel());
-        paramsObj.addProperty(CommandChannelConstants.SSL_JSON_BACKING_STORAGE, sslConf.enableForBackingStorage());
-        paramsObj.addProperty(CommandChannelConstants.SSL_JSON_HOSTNAME_VERIFICATION, sslConf.disableHostnameVerification());
-        
-        JsonObject sslConfigRoot = new JsonObject();
-        sslConfigRoot.add(CommandChannelConstants.SSL_JSON_ROOT, paramsObj);
-        
-        String jsonSslConf = gson.toJson(sslConfigRoot);
-        byte[] jsonSslConfBytes = jsonSslConf.getBytes(Charset.forName("UTF-8"));
-        if (jsonSslConfBytes.length > SSL_CONF_MAX_BYTES) {
-            throw new IOException("JSON-encoded SSL configuration larger than maximum of "
-                    + SSL_CONF_MAX_BYTES + " bytes");
-        }
-        return jsonSslConfBytes;
-    }
-    
-    
-
-}
--- a/agent/command/src/main/java/com/redhat/thermostat/agent/command/package-info.java	Tue Jun 13 12:02:12 2017 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,41 +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
- * <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.
- */
-
-
-/**
- * Access the command-channel / Request API on the agent-side
- */
-package com.redhat.thermostat.agent.command;
--- a/agent/command/src/test/java/com/redhat/thermostat/agent/command/internal/AgentRequestDecoderTest.java	Tue Jun 13 12:02:12 2017 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,307 +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
- * <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.command.internal;
-
-import static org.junit.Assert.*;
-
-import java.io.IOException;
-import java.net.InetSocketAddress;
-import java.nio.charset.Charset;
-import java.util.Collection;
-
-import org.junit.Before;
-import org.junit.Test;
-
-import com.google.gson.Gson;
-import com.google.gson.GsonBuilder;
-import com.google.gson.JsonArray;
-import com.google.gson.JsonElement;
-import com.google.gson.JsonObject;
-import com.redhat.thermostat.common.command.Request;
-import com.redhat.thermostat.common.command.Request.RequestType;
-
-public class AgentRequestDecoderTest {
-
-    private AgentRequestDecoder decoder;
-
-    @Before
-    public void setUp() throws Exception {
-        decoder = new AgentRequestDecoder();
-    }
-    
-    @Test(expected=IOException.class)
-    public void testEmptyString() throws IOException {
-        decoder.decodeRequest(new byte[0]);
-    }
-    
-    @Test(expected=IOException.class)
-    public void testNotJson() throws IOException {
-        decoder.decodeRequest("Not JSON".getBytes(Charset.forName("UTF-8")));
-    }
-    
-    @Test(expected=IOException.class)
-    public void testNotJsonObject() throws IOException {
-        byte[] json = toJson(new JsonArray());
-        decoder.decodeRequest(json);
-    }
-    
-    @Test(expected=IOException.class)
-    public void testRequestMissing() throws IOException {
-        byte[] json = toJson(new JsonObject());
-        decoder.decodeRequest(json);
-    }
-    
-    @Test(expected=IOException.class)
-    public void testBadRequestObject() throws IOException {
-        JsonObject root = createRequest();
-        root.add(CommandChannelConstants.REQUEST_JSON_TOP, new JsonArray());
-        
-        byte[] json = toJson(root);
-        decoder.decodeRequest(json);
-    }
-    
-    @Test(expected=IOException.class)
-    public void testNullRequestObject() throws IOException {
-        JsonObject root = createRequest();
-        root.add(CommandChannelConstants.REQUEST_JSON_TOP, null);
-        
-        byte[] json = toJson(root);
-        decoder.decodeRequest(json);
-    }
-    
-    @Test(expected=IOException.class)
-    public void testRequestTypeMissing() throws IOException {
-        JsonObject root = createRequest();
-        JsonObject jsonRequest = root.get(CommandChannelConstants.REQUEST_JSON_TOP).getAsJsonObject();
-        jsonRequest.remove(CommandChannelConstants.REQUEST_JSON_TYPE);
-        
-        byte[] json = toJson(root);
-        decoder.decodeRequest(json);
-    }
-    
-    @Test(expected=IOException.class)
-    public void testRequestTypeNotString() throws IOException {
-        JsonObject root = createRequest();
-        JsonObject jsonRequest = root.get(CommandChannelConstants.REQUEST_JSON_TOP).getAsJsonObject();
-        jsonRequest.add(CommandChannelConstants.REQUEST_JSON_TYPE, new JsonArray());
-        
-        byte[] json = toJson(root);
-        decoder.decodeRequest(json);
-    }
-    
-    @Test(expected=IOException.class)
-    public void testRequestTypeNull() throws IOException {
-        JsonObject root = createRequest();
-        JsonObject jsonRequest = root.get(CommandChannelConstants.REQUEST_JSON_TOP).getAsJsonObject();
-        jsonRequest.addProperty(CommandChannelConstants.REQUEST_JSON_TYPE, (String) null);
-        
-        byte[] json = toJson(root);
-        decoder.decodeRequest(json);
-    }
-    
-    @Test(expected=IOException.class)
-    public void testRequestTypeNotEnum() throws IOException {
-        JsonObject root = createRequest();
-        JsonObject jsonRequest = root.get(CommandChannelConstants.REQUEST_JSON_TOP).getAsJsonObject();
-        jsonRequest.addProperty(CommandChannelConstants.REQUEST_JSON_TYPE, "Not a RequestType");
-        
-        byte[] json = toJson(root);
-        decoder.decodeRequest(json);
-    }
-
-    @Test(expected=IOException.class)
-    public void testHostnameMissing() throws IOException {
-        JsonObject root = createRequest();
-        JsonObject jsonRequest = root.get(CommandChannelConstants.REQUEST_JSON_TOP).getAsJsonObject();
-        jsonRequest.remove(CommandChannelConstants.REQUEST_JSON_HOST);
-        
-        byte[] json = toJson(root);
-        decoder.decodeRequest(json);
-    }
-    
-    @Test(expected=IOException.class)
-    public void testHostnameNotString() throws IOException {
-        JsonObject root = createRequest();
-        JsonObject jsonRequest = root.get(CommandChannelConstants.REQUEST_JSON_TOP).getAsJsonObject();
-        jsonRequest.add(CommandChannelConstants.REQUEST_JSON_HOST, new JsonArray());
-        
-        byte[] json = toJson(root);
-        decoder.decodeRequest(json);
-    }
-    
-    @Test(expected=IOException.class)
-    public void testHostnameNull() throws IOException {
-        JsonObject root = createRequest();
-        JsonObject jsonRequest = root.get(CommandChannelConstants.REQUEST_JSON_TOP).getAsJsonObject();
-        jsonRequest.addProperty(CommandChannelConstants.REQUEST_JSON_HOST, (String) null);
-        
-        byte[] json = toJson(root);
-        decoder.decodeRequest(json);
-    }
-    
-    @Test(expected=IOException.class)
-    public void testPortMissing() throws IOException {
-        JsonObject root = createRequest();
-        JsonObject jsonRequest = root.get(CommandChannelConstants.REQUEST_JSON_TOP).getAsJsonObject();
-        jsonRequest.remove(CommandChannelConstants.REQUEST_JSON_PORT);
-        
-        byte[] json = toJson(root);
-        decoder.decodeRequest(json);
-    }
-    
-    @Test(expected=IOException.class)
-    public void testPortNotInt() throws IOException {
-        JsonObject root = createRequest();
-        JsonObject jsonRequest = root.get(CommandChannelConstants.REQUEST_JSON_TOP).getAsJsonObject();
-        jsonRequest.addProperty(CommandChannelConstants.REQUEST_JSON_PORT, "Not a Port");
-        
-        byte[] json = toJson(root);
-        decoder.decodeRequest(json);
-    }
-    
-    @Test(expected=IOException.class)
-    public void testPortNull() throws IOException {
-        JsonObject root = createRequest();
-        JsonObject jsonRequest = root.get(CommandChannelConstants.REQUEST_JSON_TOP).getAsJsonObject();
-        jsonRequest.addProperty(CommandChannelConstants.REQUEST_JSON_PORT, (String) null);
-        
-        byte[] json = toJson(root);
-        decoder.decodeRequest(json);
-    }
-    
-    @Test(expected=IOException.class)
-    public void testParamsMissing() throws IOException {
-        JsonObject root = createRequest();
-        JsonObject jsonRequest = root.get(CommandChannelConstants.REQUEST_JSON_TOP).getAsJsonObject();
-        jsonRequest.remove(CommandChannelConstants.REQUEST_JSON_PARAMS);
-        
-        byte[] json = toJson(root);
-        decoder.decodeRequest(json);
-    }
-    
-    @Test(expected=IOException.class)
-    public void testBadParams() throws IOException {
-        JsonObject root = createRequest();
-        JsonObject jsonRequest = root.get(CommandChannelConstants.REQUEST_JSON_TOP).getAsJsonObject();
-        jsonRequest.add(CommandChannelConstants.REQUEST_JSON_PARAMS, new JsonArray());
-        
-        byte[] json = toJson(root);
-        decoder.decodeRequest(json);
-    }
-    
-    @Test(expected=IOException.class)
-    public void testParamsNull() throws IOException {
-        JsonObject root = createRequest();
-        JsonObject jsonRequest = root.get(CommandChannelConstants.REQUEST_JSON_TOP).getAsJsonObject();
-        jsonRequest.add(CommandChannelConstants.REQUEST_JSON_PARAMS, null);
-        
-        byte[] json = toJson(root);
-        decoder.decodeRequest(json);
-    }
-    
-    @Test(expected=IOException.class)
-    public void testParamsValueNotString() throws IOException {
-        JsonObject root = createRequest();
-        JsonObject jsonRequest = root.get(CommandChannelConstants.REQUEST_JSON_TOP).getAsJsonObject();
-        JsonObject params = jsonRequest.get(CommandChannelConstants.REQUEST_JSON_PARAMS).getAsJsonObject();
-        params.add("name", new JsonArray());
-        
-        byte[] json = toJson(root);
-        decoder.decodeRequest(json);
-    }
-    
-    @Test
-    public void testSuccess() throws IOException {
-        JsonObject root = createRequest();
-        byte[] json = toJson(root);
-        Request request = decoder.decodeRequest(json);
-        
-        assertEquals(RequestType.NO_RESPONSE_EXPECTED, request.getType());
-        assertEquals(new InetSocketAddress("127.0.0.1", 12000), request.getTarget());
-        
-        Collection<String> parameterNames = request.getParameterNames();
-        assertEquals(2, parameterNames.size());
-        assertTrue(parameterNames.contains("name1"));
-        assertTrue(parameterNames.contains("name2"));
-        assertEquals("value1", request.getParameter("name1"));
-        assertEquals("value2", request.getParameter("name2"));
-    }
-    
-    @Test
-    public void testSuccessNullValue() throws IOException {
-        JsonObject root = createRequest();
-        JsonObject jsonRequest = root.get(CommandChannelConstants.REQUEST_JSON_TOP).getAsJsonObject();
-        JsonObject params = jsonRequest.get(CommandChannelConstants.REQUEST_JSON_PARAMS).getAsJsonObject();
-        params.addProperty("name1", (String) null);
-
-        byte[] json = toJson(root);
-        Request request = decoder.decodeRequest(json);
-        
-        assertEquals(RequestType.NO_RESPONSE_EXPECTED, request.getType());
-        assertEquals(new InetSocketAddress("127.0.0.1", 12000), request.getTarget());
-        
-        Collection<String> parameterNames = request.getParameterNames();
-        assertEquals(2, parameterNames.size());
-        assertTrue(parameterNames.contains("name1"));
-        assertTrue(parameterNames.contains("name2"));
-        assertNull(request.getParameter("name1"));
-        assertEquals("value2", request.getParameter("name2"));
-    }
-    
-    private JsonObject createRequest() {
-        JsonObject jsonRequest = new JsonObject();
-        jsonRequest.addProperty(CommandChannelConstants.REQUEST_JSON_TYPE, RequestType.NO_RESPONSE_EXPECTED.name());
-        jsonRequest.addProperty(CommandChannelConstants.REQUEST_JSON_HOST, "127.0.0.1");
-        jsonRequest.addProperty(CommandChannelConstants.REQUEST_JSON_PORT, "12000");
-        
-        JsonObject params = new JsonObject();
-        params.addProperty("name1", "value1");
-        params.addProperty("name2", "value2");
-        jsonRequest.add(CommandChannelConstants.REQUEST_JSON_PARAMS, params);
-        
-        JsonObject root = new JsonObject();
-        root.add(CommandChannelConstants.REQUEST_JSON_TOP, jsonRequest);
-        return root;
-    }
-    
-    private byte[] toJson(JsonElement element) {
-        Gson gson = new GsonBuilder().serializeNulls().create();
-        String jsonString = gson.toJson(element);
-        return jsonString.getBytes(Charset.forName("UTF-8"));
-    }
-
-}
--- a/agent/command/src/test/java/com/redhat/thermostat/agent/command/internal/AgentResponseEncoderTest.java	Tue Jun 13 12:02:12 2017 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,70 +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
- * <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.command.internal;
-
-import static org.junit.Assert.assertArrayEquals;
-
-import java.nio.charset.Charset;
-
-import org.junit.Test;
-
-import com.google.gson.Gson;
-import com.google.gson.GsonBuilder;
-import com.google.gson.JsonObject;
-import com.redhat.thermostat.common.command.Response;
-import com.redhat.thermostat.common.command.Response.ResponseType;
-
-public class AgentResponseEncoderTest {
-
-    @Test
-    public void testEncodedResponse() {
-        Gson gson = new GsonBuilder().create();
-        JsonObject jsonResponse = new JsonObject();
-        jsonResponse.addProperty(CommandChannelConstants.RESPONSE_JSON_TYPE, ResponseType.OK.name());
-        JsonObject jsonRoot = new JsonObject();
-        jsonRoot.add(CommandChannelConstants.RESPONSE_JSON_TOP, jsonResponse);
-        String expected = gson.toJson(jsonRoot);
-        byte[] expectedBytes = expected.getBytes(Charset.forName("UTF-8"));
-        
-        AgentResponseEncoder encoder = new AgentResponseEncoder();
-        Response response = new Response(ResponseType.OK);
-        byte[] result = encoder.encodeResponse(response);
-        
-        assertArrayEquals(expectedBytes, result);
-    }
-
-}
--- a/agent/command/src/test/java/com/redhat/thermostat/agent/command/internal/CommandChannelDelegateTest.java	Tue Jun 13 12:02:12 2017 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,406 +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
- * <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.command.internal;
-
-import static org.junit.Assert.assertArrayEquals;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.fail;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.eq;
-import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import java.io.File;
-import java.io.IOException;
-import java.lang.ProcessBuilder.Redirect;
-import java.nio.ByteBuffer;
-import java.nio.charset.Charset;
-import java.nio.file.Path;
-import java.nio.file.attribute.UserPrincipal;
-import java.util.Arrays;
-import java.util.List;
-import java.util.concurrent.CountDownLatch;
-
-import com.redhat.thermostat.common.portability.PortableProcessFactory;
-import org.junit.Before;
-import org.junit.Test;
-import org.mockito.ArgumentCaptor;
-import org.mockito.invocation.InvocationOnMock;
-import org.mockito.stubbing.Answer;
-
-import com.redhat.thermostat.agent.command.ReceiverRegistry;
-import com.redhat.thermostat.agent.command.RequestReceiver;
-import com.redhat.thermostat.agent.command.internal.CommandChannelDelegate.FileSystemUtils;
-import com.redhat.thermostat.agent.command.internal.CommandChannelDelegate.ProcessCreator;
-import com.redhat.thermostat.agent.ipc.server.AgentIPCService;
-import com.redhat.thermostat.agent.ipc.server.IPCMessage;
-import com.redhat.thermostat.common.command.Request;
-import com.redhat.thermostat.common.command.Request.RequestType;
-import com.redhat.thermostat.common.command.Response;
-import com.redhat.thermostat.common.command.Response.ResponseType;
-import com.redhat.thermostat.shared.config.OS;
-import com.redhat.thermostat.shared.config.SSLConfiguration;
-
-public class CommandChannelDelegateTest {
-    
-    private static final String IPC_SERVER_NAME = "command-channel";
-    private static final byte[] ENCODED_SSL_CONFIG = { 'S', 'S', 'L' };
-    private static final byte[] ENCODED_REQUEST = { 'R', 'E', 'Q' };
-    private static final byte[] ENCODED_RESPONSE_OK = { 'O', 'K' };
-    private static final byte[] ENCODED_RESPONSE_AUTH_FAILED = { 'A', 'U', 'T', 'H' };
-    private static final byte[] ENCODED_RESPONSE_ERROR = { 'E', 'R', 'R' };
-    
-    private ProcessCreator processCreator;
-    private ReceiverRegistry receivers;
-    private File binPath;
-    private CommandChannelDelegate delegate;
-    private Process process;
-    private AgentIPCService ipcService;
-    private File ipcConfig;
-    private AgentRequestDecoder requestDecoder;
-    private AgentResponseEncoder responseEncoder;
-    private SSLConfigurationEncoder sslConfEncoder;
-    private CountDownLatch latch;
-    private SSLConfiguration sslConf;
-    private IPCMessage startedMessage;
-    private FileSystemUtils fsUtils;
-    private ProcessUserInfoBuilder userInfoBuilder;
-
-    @Before
-    public void setUp() throws Exception {
-        receivers = mock(ReceiverRegistry.class);
-        sslConf = mock(SSLConfiguration.class);
-        binPath = new File("/path/to/thermostat/home/");
-        processCreator = mock(ProcessCreator.class);
-        process = mock(Process.class);
-        ipcService = mock(AgentIPCService.class);
-        ipcConfig = new File("/path/to/ipc/config");
-        
-        requestDecoder = mock(AgentRequestDecoder.class);
-        responseEncoder = mock(AgentResponseEncoder.class);
-        // Return different encoded response for different response types
-        when(responseEncoder.encodeResponse(any(Response.class))).thenAnswer(new Answer<Object>() {
-            @Override
-            public Object answer(InvocationOnMock invocation) throws Throwable {
-                Response resp = (Response) invocation.getArguments()[0];
-                ResponseType type = resp.getType();
-                switch (type) {
-                case OK:
-                    return ENCODED_RESPONSE_OK;
-                case AUTH_FAILED:
-                    return ENCODED_RESPONSE_AUTH_FAILED;
-                case ERROR:
-                    return ENCODED_RESPONSE_ERROR;
-                default:
-                    throw new IOException("Unexpected ResponseType: " + type.name());
-                }
-            }
-        });
-        sslConfEncoder = mock(SSLConfigurationEncoder.class);
-        when(sslConfEncoder.encodeAsJson(sslConf)).thenReturn(ENCODED_SSL_CONFIG);
-        
-        when(processCreator.startProcess(any(ProcessBuilder.class))).thenReturn(process);
-        when(ipcService.getConfigurationFile()).thenReturn(ipcConfig);
-        
-        latch = mock(CountDownLatch.class);
-        fsUtils = mock(FileSystemUtils.class);
-        userInfoBuilder = mock(ProcessUserInfoBuilder.class);
-        delegate = new CommandChannelDelegate(receivers, sslConf, binPath, ipcService, 
-                latch, sslConfEncoder, requestDecoder, responseEncoder, userInfoBuilder,
-                fsUtils, processCreator);
-        
-        startedMessage = mock(IPCMessage.class);
-        when(startedMessage.get()).thenReturn(ByteBuffer.wrap(CommandChannelConstants.SERVER_STARTED_TOKEN));
-        final IPCMessage readyMessage = mock(IPCMessage.class);
-        when(readyMessage.get()).thenReturn(ByteBuffer.wrap(CommandChannelConstants.SERVER_READY_TOKEN));
-        doAnswer(new Answer<Void>() {
-            @Override
-            public Void answer(InvocationOnMock invocation) throws Throwable {
-                // Invoke callbacks with started message
-                delegate.messageReceived(startedMessage);
-                verify(startedMessage).reply(eq(ByteBuffer.wrap(ENCODED_SSL_CONFIG)));
-                // Invoke callbacks with ready message
-                delegate.messageReceived(readyMessage);
-                verify(readyMessage, never()).reply(any(ByteBuffer.class));
-                return null;
-            }
-        }).when(latch).await();
-    }
-
-    @Test
-    public void testServerStarted() throws Exception {
-        delegate.startListening("127.0.0.1", 123);
-        
-        verify(ipcService).createServer(IPC_SERVER_NAME, delegate);
-        verify(processCreator).startProcess(any(ProcessBuilder.class));
-    }
-    
-    @Test
-    public void testServerStartedPrivUser() throws Exception {
-        when(userInfoBuilder.isPrivilegedUser()).thenReturn(true);
-        Path scriptPath = mock(Path.class);
-        when(fsUtils.getPath(binPath.getAbsolutePath(), "thermostat-command-channel")).thenReturn(scriptPath);
-        UserPrincipal principal = mock(UserPrincipal.class);
-        when(fsUtils.getOwner(scriptPath)).thenReturn(principal);
-        delegate.startListening("127.0.0.1", 123);
-        
-        if (OS.IS_WINDOWS) {
-            verify(ipcService).createServer(IPC_SERVER_NAME, delegate);
-        } else { // Unix and macOS
-            verify(ipcService).createServer(IPC_SERVER_NAME, delegate, principal);
-        }
-        verify(processCreator).startProcess(any(ProcessBuilder.class));
-    }
-    
-    @Test
-    public void testServerFailsToStart() throws Exception {
-        doAnswer(new Answer<Void>() {
-            @Override
-            public Void answer(InvocationOnMock invocation) throws Throwable {
-                // Invoke callbacks with wrong started message
-                IPCMessage message = mock(IPCMessage.class);
-                ByteBuffer badData = ByteBuffer.wrap("not the server started message".getBytes(Charset.forName("UTF-8")));
-                when(message.get()).thenReturn(badData);
-                delegate.messageReceived(message);
-                verify(message, never()).reply(any(ByteBuffer.class));
-                return null;
-            }
-        }).when(latch).await();
-        
-        try {
-            delegate.startListening("127.0.0.1", 123);
-            fail("Expected IOException");
-        } catch (IOException e) {
-            verify(ipcService).createServer(IPC_SERVER_NAME, delegate);
-            verify(processCreator).startProcess(any(ProcessBuilder.class));
-        }
-    }
-    
-    @Test
-    public void testServerFailsToStartParseFail() throws Exception {
-        when(sslConfEncoder.encodeAsJson(sslConf)).thenThrow(new IOException("TEST"));
-        doAnswer(new Answer<Void>() {
-            @Override
-            public Void answer(InvocationOnMock invocation) throws Throwable {
-                // Invoke callbacks with started message
-                delegate.messageReceived(startedMessage);
-                return null;
-            }
-        }).when(latch).await();
-        
-        try {
-            delegate.startListening("127.0.0.1", 123);
-            fail("Expected IOException");
-        } catch (IOException e) {
-            verify(ipcService).createServer(IPC_SERVER_NAME, delegate);
-            verify(processCreator).startProcess(any(ProcessBuilder.class));
-        }
-    }
-    
-    @Test
-    public void testServerFailsToBecomeReady() throws Exception {
-        doAnswer(new Answer<Void>() {
-            @Override
-            public Void answer(InvocationOnMock invocation) throws Throwable {
-                // Invoke callbacks with started message
-                delegate.messageReceived(startedMessage);
-                // Invoke callbacks with wrong ready message
-                IPCMessage message = mock(IPCMessage.class);
-                ByteBuffer badData = ByteBuffer.wrap("not the server started message".getBytes(Charset.forName("UTF-8")));
-                when(message.get()).thenReturn(badData);
-                delegate.messageReceived(message);
-                verify(message, never()).reply(any(ByteBuffer.class));
-                return null;
-            }
-        }).when(latch).await();
-        
-        try {
-            delegate.startListening("127.0.0.1", 123);
-            fail("Expected IOException");
-        } catch (IOException e) {
-            verify(ipcService).createServer(IPC_SERVER_NAME, delegate);
-            verify(processCreator).startProcess(any(ProcessBuilder.class));
-        }
-    }
-
-    @Test
-    public void testProcessCmdLine() throws IOException {
-        delegate.startListening("127.0.0.1", 123);
-        
-        String[] linuxArgs = new String[] {
-                "/path/to/thermostat/home/thermostat-command-channel",
-                "127.0.0.1",
-                "123",
-                "/path/to/ipc/config"
-        };
-
-        // in Windows we need to ensure the drive letter appears - by calling getAbsolutePath()
-        // avoid this call in non-windows to simplify test setup
-        String[] winArgs = OS.IS_WINDOWS ? new String[] {
-                "cmd",
-                "/c",
-                new File("/path/to/thermostat/home/thermostat-command-channel.cmd").getAbsolutePath(),
-                "127.0.0.1",
-                "123",
-                new File("/path/to/ipc/config").getAbsolutePath(),
-                Integer.toString(PortableProcessFactory.getInstance().getCurrentProcessPid())
-        } : null;
-
-        final String[] expectedArgs = OS.IS_UNIX ? linuxArgs : winArgs;
-        
-        ArgumentCaptor<ProcessBuilder> builderCaptor = ArgumentCaptor.forClass(ProcessBuilder.class);
-        verify(processCreator).startProcess(builderCaptor.capture());
-        ProcessBuilder builder = builderCaptor.getValue();
-        final List<String> actualArgs = builder.command();
-
-        assertEquals(Arrays.asList(expectedArgs), actualArgs);
-        assertEquals(Redirect.INHERIT, builder.redirectError());
-        assertEquals(Redirect.INHERIT, builder.redirectOutput());
-        assertEquals(Redirect.INHERIT, builder.redirectInput());
-    }
-    
-    @Test
-    public void testStopListening() throws IOException {
-        delegate.startListening("127.0.0.1", 123);
-        when(ipcService.serverExists(IPC_SERVER_NAME)).thenReturn(true);
-        delegate.stopListening();
-        
-        verify(process).destroy();
-        verify(ipcService).destroyServer(IPC_SERVER_NAME);
-    }
-    
-    @Test
-    public void testStopListeningNotExist() throws IOException {
-        delegate.startListening("127.0.0.1", 123);
-        delegate.stopListening();
-        
-        verify(process).destroy();
-        verify(ipcService, never()).destroyServer(IPC_SERVER_NAME);
-    }
-    
-    @Test
-    public void testRequestReceived() throws IOException {
-        RequestReceiver receiver = mock(RequestReceiver.class);
-        Request request = createRequest(receiver);
-        
-        byte[] result = receiveRequestAndReturnResponse(request);
-        verify(receivers).getReceiver("com.example.MyReceiver");
-        verify(receiver).receive(request);
-        
-        assertArrayEquals(ENCODED_RESPONSE_OK, result);
-    }
-
-    private byte[] receiveRequestAndReturnResponse(Request request) throws IOException {
-        delegate.startListening("127.0.0.1", 123);
-        
-        // Receive encoded request
-        when(requestDecoder.decodeRequest(ENCODED_REQUEST)).thenReturn(request);
-        IPCMessage message = mock(IPCMessage.class);
-        ByteBuffer data = ByteBuffer.wrap(ENCODED_REQUEST);
-        when(message.get()).thenReturn(data);
-        delegate.messageReceived(message);
-        
-        return getReply(message);
-    }
-
-    private byte[] getReply(IPCMessage message) throws IOException {
-        ArgumentCaptor<ByteBuffer> replyCaptor = ArgumentCaptor.forClass(ByteBuffer.class);
-        verify(message).reply(replyCaptor.capture());
-        ByteBuffer reply = replyCaptor.getValue();
-        return reply.array();
-    }
-    
-    @Test
-    public void testRequestReceivedParseFail() throws IOException {
-        delegate.startListening("127.0.0.1", 123);
-        
-        RequestReceiver receiver = mock(RequestReceiver.class);
-        Request request = createRequest(receiver);
-        
-        // Should catch exception and return error response
-        when(requestDecoder.decodeRequest(ENCODED_REQUEST)).thenThrow(new IOException("TEST"));
-        
-        IPCMessage message = mock(IPCMessage.class);
-        ByteBuffer data = ByteBuffer.wrap(ENCODED_REQUEST);
-        when(message.get()).thenReturn(data);
-        delegate.messageReceived(message);
-        
-        byte[] result = getReply(message);
-        verify(receivers, never()).getReceiver("com.example.MyReceiver");
-        verify(receiver, never()).receive(request);
-        
-        assertArrayEquals(ENCODED_RESPONSE_ERROR, result);
-    }
-    
-    @Test
-    public void testRequestReceivedNoReceiver() throws IOException {
-        Request request = mock(Request.class);
-        when(request.getType()).thenReturn(RequestType.RESPONSE_EXPECTED);
-        
-        byte[] result = receiveRequestAndReturnResponse(request);
-        assertArrayEquals(ENCODED_RESPONSE_ERROR, result);
-    }
-    
-    @Test
-    public void testRequestReceivedNoType() throws IOException {
-        Request request = mock(Request.class);
-        
-        when(request.getReceiver()).thenReturn("com.example.MyReceiver");
-        RequestReceiver receiver = mock(RequestReceiver.class);
-        when(receivers.getReceiver("com.example.MyReceiver")).thenReturn(receiver);
-        when(receiver.receive(request)).thenReturn(new Response(ResponseType.OK));
-        
-        byte[] result = receiveRequestAndReturnResponse(request);
-        verify(receiver, never()).receive(request);
-        assertArrayEquals(ENCODED_RESPONSE_ERROR, result);
-    }
-    
-    private Request createRequest(RequestReceiver receiver) {
-        Request request = mock(Request.class);
-        when(request.getType()).thenReturn(RequestType.RESPONSE_EXPECTED);
-        
-        when(request.getReceiver()).thenReturn("com.example.MyReceiver");
-        when(receivers.getReceiver("com.example.MyReceiver")).thenReturn(receiver);
-        when(receiver.receive(request)).thenReturn(new Response(ResponseType.OK));
-        return request;
-    }
-    
-}
-
--- a/agent/command/src/test/java/com/redhat/thermostat/agent/command/internal/SSLConfigurationEncoderTest.java	Tue Jun 13 12:02:12 2017 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,140 +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
- * <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.command.internal;
-
-import static org.junit.Assert.assertEquals;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-import java.io.File;
-import java.io.IOException;
-import java.nio.charset.Charset;
-
-import com.google.gson.JsonElement;
-import com.google.gson.JsonParser;
-import com.redhat.thermostat.shared.config.OS;
-import com.redhat.thermostat.testutils.TestUtils;
-import org.junit.Before;
-import org.junit.Test;
-
-import com.google.gson.Gson;
-import com.google.gson.GsonBuilder;
-import com.google.gson.JsonObject;
-import com.redhat.thermostat.shared.config.SSLConfiguration;
-
-public class SSLConfigurationEncoderTest {
-    
-    private static final String KEYSTORE_PASS = "My Keystore Password";
-    private static final String KEYSTORE_FILE = "/path/to/keystore";
-    private SSLConfiguration sslConf;
-    
-    @Before
-    public void setUp() {
-        sslConf = mock(SSLConfiguration.class);
-        File keystore = new File(KEYSTORE_FILE);
-        when(sslConf.getKeystoreFile()).thenReturn(keystore);
-        when(sslConf.getKeyStorePassword()).thenReturn(KEYSTORE_PASS);
-        when(sslConf.enableForBackingStorage()).thenReturn(true);
-        when(sslConf.enableForCmdChannel()).thenReturn(true);
-        when(sslConf.disableHostnameVerification()).thenReturn(false);
-    }
-
-    @Test
-    public void testSSLConfig() throws IOException {
-        String expected = getJsonString(KEYSTORE_FILE, KEYSTORE_PASS);
-        String result = getEncodedSSLConfiguration();
-        result = fixKeystorePath(result);
-        assertEquals(expected, result);
-    }
-    
-    @Test
-    public void testSSLConfigNoKeystoreFile() throws IOException {
-        when(sslConf.getKeystoreFile()).thenReturn(null);
-        
-        String expected = getJsonString(null, KEYSTORE_PASS);
-        String result = getEncodedSSLConfiguration();
-        // no need to fix the keystore path here, because the path is NULL
-        assertEquals(expected, result);
-    }
-    
-    @Test
-    public void testSSLConfigNoKeystorePass() throws IOException {
-        when(sslConf.getKeyStorePassword()).thenReturn(null);
-        
-        String expected = getJsonString(KEYSTORE_FILE, null);
-        String result = getEncodedSSLConfiguration();
-        result = fixKeystorePath(result);
-        assertEquals(expected, result);
-    }
-
-    private String getEncodedSSLConfiguration() throws IOException {
-        SSLConfigurationEncoder encoder = new SSLConfigurationEncoder();
-        byte[] encoded = encoder.encodeAsJson(sslConf);
-        return new String(encoded, Charset.forName("UTF-8"));
-    }
-    
-    private String getJsonString(String keystoreFile, String keystorePass) {
-        GsonBuilder builder = new GsonBuilder();
-        builder.serializeNulls();
-        Gson gson = builder.create();
-        
-        JsonObject params = new JsonObject();
-        params.addProperty(CommandChannelConstants.SSL_JSON_KEYSTORE_FILE, keystoreFile);
-        params.addProperty(CommandChannelConstants.SSL_JSON_KEYSTORE_PASS, keystorePass);
-        params.addProperty(CommandChannelConstants.SSL_JSON_COMMAND_CHANNEL, true);
-        params.addProperty(CommandChannelConstants.SSL_JSON_BACKING_STORAGE, true);
-        params.addProperty(CommandChannelConstants.SSL_JSON_HOSTNAME_VERIFICATION, false);
-        
-        JsonObject sslConfigJson = new JsonObject();
-        sslConfigJson.add(CommandChannelConstants.SSL_JSON_ROOT, params);
-        return gson.toJson(sslConfigJson);
-    }
-
-    private String fixKeystorePath( final String json ) {
-        if (OS.IS_UNIX) {
-            return json;
-        }
-        else {
-            // on Windows, patch the filename to appear in Unix format
-            JsonElement je = new JsonParser().parse(json);
-            JsonObject jo = je.getAsJsonObject();
-            final String fn = jo.get("sslConfiguration").getAsJsonObject().get("keystoreFile").getAsString();
-            jo.get("sslConfiguration").getAsJsonObject().addProperty("keystoreFile", TestUtils.convertWinPathToUnixPath(fn));
-            return je.toString();
-        }
-    }
-}
--- a/agent/core/src/main/java/com/redhat/thermostat/agent/Agent.java	Tue Jun 13 12:02:12 2017 +0200
+++ b/agent/core/src/main/java/com/redhat/thermostat/agent/Agent.java	Mon Jun 12 20:50:03 2017 +0200
@@ -49,7 +49,6 @@
 import com.redhat.thermostat.common.ActionListener;
 import com.redhat.thermostat.common.LaunchException;
 import com.redhat.thermostat.common.ThermostatExtensionRegistry;
-import com.redhat.thermostat.common.utils.HostPortPair;
 import com.redhat.thermostat.common.utils.LoggingUtils;
 import com.redhat.thermostat.storage.core.WriterID;
 import com.redhat.thermostat.storage.dao.AgentInfoDAO;
@@ -175,10 +174,6 @@
         AgentInformation agentInfo = new AgentInformation(writerId);
         agentInfo.setStartTime(config.getStartTime());
         agentInfo.setAlive(true);
-        // Report the configured publish address if any. Otherwise,
-        // defaults to (agent-local) configured listen address.
-        HostPortPair publishAddress = config.getConfigPublishAddress();
-        agentInfo.setConfigListenAddress(publishAddress.toExternalForm());
         return agentInfo;
     }
 
--- a/agent/core/src/main/java/com/redhat/thermostat/agent/config/AgentConfigsUtils.java	Tue Jun 13 12:02:12 2017 +0200
+++ b/agent/core/src/main/java/com/redhat/thermostat/agent/config/AgentConfigsUtils.java	Mon Jun 12 20:50:03 2017 +0200
@@ -92,19 +92,6 @@
             configuration.setPurge(true);
         }
 
-        String configListenAddress = properties.getProperty(AgentProperties.CONFIG_LISTEN_ADDRESS.name());
-        if (configListenAddress != null) {
-            configuration.setConfigListenAddress(configListenAddress);
-        } else {
-            // TODO: we could avoid this, which means the agent doesn't want to
-            // accept any connection
-            configuration.setConfigListenAddress("127.0.0.1:12000");
-        }
-        
-        String configPublishAddress = properties.getProperty(AgentProperties.CONFIG_PUBLISH_ADDRESS.name());
-        if (configPublishAddress != null) {
-            configuration.setConfigPublishAddress(configPublishAddress);
-        }
     }
 }
 
--- a/agent/core/src/main/java/com/redhat/thermostat/agent/config/AgentProperties.java	Tue Jun 13 12:02:12 2017 +0200
+++ b/agent/core/src/main/java/com/redhat/thermostat/agent/config/AgentProperties.java	Mon Jun 12 20:50:03 2017 +0200
@@ -40,7 +40,5 @@
 
     DB_URL,
     SAVE_ON_EXIT,
-    CONFIG_LISTEN_ADDRESS,
-    CONFIG_PUBLISH_ADDRESS,
 }
 
--- a/agent/core/src/main/java/com/redhat/thermostat/agent/config/AgentStartupConfiguration.java	Tue Jun 13 12:02:12 2017 +0200
+++ b/agent/core/src/main/java/com/redhat/thermostat/agent/config/AgentStartupConfiguration.java	Mon Jun 12 20:50:03 2017 +0200
@@ -36,10 +36,6 @@
 
 package com.redhat.thermostat.agent.config;
 
-import java.util.List;
-
-import com.redhat.thermostat.common.utils.HostPortPair;
-import com.redhat.thermostat.common.utils.HostPortsParser;
 import com.redhat.thermostat.storage.config.StartupConfiguration;
 
 public class AgentStartupConfiguration implements StartupConfiguration {
@@ -47,8 +43,6 @@
     private boolean purge;
     private String url;
     private long startTime;
-    private HostPortPair listenAddr;
-    private HostPortPair publishAddr;
     
     AgentStartupConfiguration() {
     }
@@ -78,36 +72,5 @@
     public boolean purge() {
         return purge;
     }
-
-    public void setConfigListenAddress(String address) {
-        this.listenAddr = parseAddress(address);
-    }
-
-    public HostPortPair getConfigListenAddress() {
-        return listenAddr;
-    }
-    
-    public void setConfigPublishAddress(String address) {
-        this.publishAddr = parseAddress(address);
-    }
-    
-    public HostPortPair getConfigPublishAddress() {
-        if (publishAddr != null) {
-            return publishAddr;
-        }
-        // Otherwise default to configured listen address
-        // as the publish address for backwards compat reasons.
-        return listenAddr;
-    }
-
-    private HostPortPair parseAddress(String address) throws AssertionError {
-        HostPortsParser parser = new HostPortsParser(address);
-        parser.parse();
-        List<HostPortPair> list = parser.getHostsPorts();
-        if (list.size() != 1) {
-            throw new AssertionError("Multiple listen addresses not supported! Got: " + address);
-        }
-        return parser.getHostsPorts().get(0);
-    }
 }
 
--- a/agent/core/src/test/java/com/redhat/thermostat/agent/AgentTest.java	Tue Jun 13 12:02:12 2017 +0200
+++ b/agent/core/src/test/java/com/redhat/thermostat/agent/AgentTest.java	Mon Jun 12 20:50:03 2017 +0200
@@ -58,7 +58,6 @@
 import com.redhat.thermostat.common.ActionEvent;
 import com.redhat.thermostat.common.ActionListener;
 import com.redhat.thermostat.common.ThermostatExtensionRegistry;
-import com.redhat.thermostat.common.utils.HostPortPair;
 import com.redhat.thermostat.storage.core.WriterID;
 import com.redhat.thermostat.storage.dao.AgentInfoDAO;
 import com.redhat.thermostat.storage.dao.BackendInfoDAO;
@@ -84,7 +83,6 @@
         config = mock(AgentStartupConfiguration.class);
         when(config.getStartTime()).thenReturn(123L);
         when(config.purge()).thenReturn(true);
-        when(config.getConfigPublishAddress()).thenReturn(new HostPortPair("foo", 23));
         
         agentInfoDao = mock(AgentInfoDAO.class);
         backendInfoDao = mock(BackendInfoDAO.class);
@@ -208,7 +206,6 @@
         AgentStartupConfiguration config = mock(AgentStartupConfiguration.class);
         when(config.getStartTime()).thenReturn(123L);
         when(config.purge()).thenReturn(false);
-        when(config.getConfigPublishAddress()).thenReturn(new HostPortPair("foo", 23));
         
         WriterID id = mock(WriterID.class);
         Agent agent = new Agent(backendRegistry, config, agentInfoDao, backendInfoDao, id, poolTracker);
--- a/agent/core/src/test/java/com/redhat/thermostat/agent/config/AgentConfigsUtilsTest.java	Tue Jun 13 12:02:12 2017 +0200
+++ b/agent/core/src/test/java/com/redhat/thermostat/agent/config/AgentConfigsUtilsTest.java	Mon Jun 12 20:50:03 2017 +0200
@@ -44,7 +44,6 @@
 import org.junit.Assert;
 import org.junit.Test;
 
-import com.redhat.thermostat.common.utils.HostPortPair;
 import com.redhat.thermostat.shared.config.InvalidConfigurationException;
 import com.redhat.thermostat.testutils.TestUtils;
 
@@ -80,22 +79,6 @@
     }
     
     @Test
-    public void testSystemAddressProp() {
-        Properties sysProps = createSystemProperties();
-        setConfigs(sysProps, new Properties());
-        AgentStartupConfiguration config = AgentConfigsUtils.createAgentConfigs();
-
-        HostPortPair hostPorts = config.getConfigListenAddress();
-        Assert.assertEquals("42.42.42.42", hostPorts.getHost());
-        Assert.assertEquals(42, hostPorts.getPort());
-        
-        // Not explicitly set, should default to config listen address
-        HostPortPair publishAddr = config.getConfigPublishAddress();
-        Assert.assertEquals("42.42.42.42", publishAddr.getHost());
-        Assert.assertEquals(42, publishAddr.getPort());
-    }
-    
-    @Test
     public void testUserDbUrl() throws InvalidConfigurationException, IOException {
         Properties sysProps = createSystemProperties();
         Properties userProps = createUserProperties();
@@ -115,69 +98,6 @@
         Assert.assertTrue(config.purge());
     }
     
-    @Test
-    public void testUserAddressProp() {
-        Properties sysProps = createSystemProperties();
-        Properties userProps = createUserProperties();
-        setConfigs(sysProps, userProps);
-        AgentStartupConfiguration config = AgentConfigsUtils.createAgentConfigs();        
-
-        HostPortPair hostPorts = config.getConfigListenAddress();
-        Assert.assertEquals("24.24.24.24", hostPorts.getHost());
-        Assert.assertEquals(24, hostPorts.getPort());
-        
-        // Not explicitly set, should default to config listen address
-        HostPortPair publishAddr = config.getConfigPublishAddress();
-        Assert.assertEquals("24.24.24.24", publishAddr.getHost());
-        Assert.assertEquals(24, publishAddr.getPort());
-    }
-    
-    @Test
-    public void canParseIpv6ConfigAddress() {
-        String ipV6AddressPair = "[::1]:12000";
-        Properties sysProps = createSystemProperties(ipV6AddressPair);
-        setConfigs(sysProps, new Properties());
-        AgentStartupConfiguration config = AgentConfigsUtils.createAgentConfigs();        
-
-        HostPortPair listenAddr = config.getConfigListenAddress();
-        Assert.assertEquals("::1", listenAddr.getHost());
-        Assert.assertEquals(12000, listenAddr.getPort());
-    }
-    
-    @Test
-    public void canOptionallySetSystemPublishAddress() {
-        Properties sysProps = createSystemProperties();
-        sysProps.put("CONFIG_PUBLISH_ADDRESS", "foo.example.com:9999");
-        setConfigs(sysProps, new Properties());
-        AgentStartupConfiguration config = AgentConfigsUtils.createAgentConfigs();        
-
-        HostPortPair listenAddr = config.getConfigListenAddress();
-        Assert.assertEquals("42.42.42.42", listenAddr.getHost());
-        Assert.assertEquals(42, listenAddr.getPort());
-        
-        HostPortPair publishAddr = config.getConfigPublishAddress();
-        Assert.assertEquals("foo.example.com", publishAddr.getHost());
-        Assert.assertEquals(9999, publishAddr.getPort());
-    }
-    
-    @Test
-    public void canOptionallySetUserPublishAddress() {
-        Properties sysProps = createSystemProperties();
-        sysProps.put("CONFIG_PUBLISH_ADDRESS", "foo.example.com:9999");
-        Properties userProps = createUserProperties();
-        userProps.put("CONFIG_PUBLISH_ADDRESS", "33.33.33.33:9333");
-        setConfigs(sysProps, userProps);
-        AgentStartupConfiguration config = AgentConfigsUtils.createAgentConfigs();        
-
-        HostPortPair listenAddr = config.getConfigListenAddress();
-        Assert.assertEquals("24.24.24.24", listenAddr.getHost());
-        Assert.assertEquals(24, listenAddr.getPort());
-        
-        HostPortPair publishAddr = config.getConfigPublishAddress();
-        Assert.assertEquals("33.33.33.33", publishAddr.getHost());
-        Assert.assertEquals(9333, publishAddr.getPort());
-    }
-    
     private Properties createSystemProperties(String configListenAddress) {
         return doCreateSystemProperties(configListenAddress);
     }
--- a/agent/core/src/test/java/com/redhat/thermostat/agent/config/AgentStartupConfigurationTest.java	Tue Jun 13 12:02:12 2017 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,96 +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
- * <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 static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
-
-import org.junit.Before;
-import org.junit.Test;
-
-import com.redhat.thermostat.common.utils.HostPortPair;
-
-public class AgentStartupConfigurationTest {
-
-    private static final String IP_V4_LISTEN_ADDRESS = "127.0.0.1:12000";
-    private static final String IP_V6_LISTEN_ADDRESS = "[::1]:12000";
-    
-    private AgentStartupConfiguration config;
-    
-    @Before
-    public void setup() {
-        config = new AgentStartupConfiguration();
-    }
-    
-    @Test
-    public void testIPv4ConfigListenAddress() {
-        config.setConfigListenAddress(IP_V4_LISTEN_ADDRESS);
-        HostPortPair hostPort = config.getConfigListenAddress();
-        assertEquals("127.0.0.1", hostPort.getHost());
-        assertEquals(12000, hostPort.getPort());
-    }
-    
-    @Test
-    public void testIPv6ConfigListenAddress() {
-        config.setConfigListenAddress(IP_V6_LISTEN_ADDRESS);
-        HostPortPair hostPort = config.getConfigListenAddress();
-        assertEquals("::1", hostPort.getHost());
-        assertEquals(12000, hostPort.getPort());
-    }
-    
-    @Test
-    public void noPublishAddressDefaultsToListenAddress() {
-        config.setConfigListenAddress(IP_V6_LISTEN_ADDRESS);
-        HostPortPair publishAddr = config.getConfigPublishAddress();
-        assertNotNull(publishAddr);
-        assertEquals("::1", publishAddr.getHost());
-        assertEquals(12000, publishAddr.getPort());
-    }
-    
-    @Test
-    public void canSetPublishAddressIndependentOfListenAddress() {
-        config.setConfigListenAddress(IP_V6_LISTEN_ADDRESS);
-        config.setConfigPublishAddress("foo.example.com:9212");
-        HostPortPair publishAddr = config.getConfigPublishAddress();
-        assertNotNull(publishAddr);
-        assertEquals("foo.example.com", publishAddr.getHost());
-        assertEquals(9212, publishAddr.getPort());
-        HostPortPair listenAddr = config.getConfigListenAddress();
-        assertFalse(listenAddr.toExternalForm().equals(publishAddr.toExternalForm()));
-    }
-}
--- a/agent/pom.xml	Tue Jun 13 12:02:12 2017 +0200
+++ b/agent/pom.xml	Mon Jun 12 20:50:03 2017 +0200
@@ -61,8 +61,6 @@
   <modules>
     <module>cli</module>
     <module>core</module>
-    <module>command</module>
-    <module>command-server</module>
     <module>ipc</module>
     <module>proxy</module>
   </modules>
--- a/common/command/pom.xml	Tue Jun 13 12:02:12 2017 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,100 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-
- 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
- <http://www.gnu.org/licenses/>.
-
- Linking this code with other modules is making a combined work
- based on this code.  Thus, the terms and conditions of the GNU
- General Public License cover the whole combination.
-
- As a special exception, the copyright holders of this code give
- you permission to link this code with independent modules to
- produce an executable, regardless of the license terms of these
- independent modules, and to copy and distribute the resulting
- executable under terms of your choice, provided that you also
- meet, for each linked independent module, the terms and conditions
- of the license of that module.  An independent module is a module
- which is not derived from or based on this code.  If you modify
- this code, you may extend this exception to your version of the
- library, but you are not obligated to do so.  If you do not wish
- to do so, delete this exception statement from your version.
-
--->
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
-  <modelVersion>4.0.0</modelVersion>
-
-  <parent>
-    <groupId>com.redhat.thermostat</groupId>
-    <artifactId>thermostat-common</artifactId>
-    <version>1.99.12-SNAPSHOT</version>
-  </parent>
-
-  <artifactId>thermostat-common-command</artifactId>
-  <packaging>bundle</packaging>
-
-  <name>Thermostat Command Channel Common Library</name>
-
-  <dependencies>
-    <dependency>
-      <groupId>junit</groupId>
-      <artifactId>junit</artifactId>
-      <scope>test</scope>
-    </dependency>
-    <dependency>
-      <groupId>org.mockito</groupId>
-      <artifactId>mockito-core</artifactId>
-      <scope>test</scope>
-    </dependency>
-    <dependency>
-      <groupId>io.netty</groupId>
-      <artifactId>netty-handler</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>com.redhat.thermostat</groupId>
-      <artifactId>thermostat-common-core</artifactId>
-      <version>${project.version}</version>
-    </dependency>
-  </dependencies>
-
-  <build>
-    <plugins>
-      <plugin>
-        <groupId>org.apache.felix</groupId>
-        <artifactId>maven-bundle-plugin</artifactId>
-        <extensions>true</extensions>
-        <configuration>
-          <instructions>
-            <Bundle-Vendor>Red Hat, Inc.</Bundle-Vendor>
-            <Bundle-Activator />
-            <Bundle-SymbolicName>com.redhat.thermostat.common.command</Bundle-SymbolicName>
-            <Export-Package>
-              com.redhat.thermostat.common.command,
-              com.redhat.thermostat.common.command.noapi
-            </Export-Package>
-            <Private-Package />
-            <!-- Do not autogenerate uses clauses in Manifests -->
-            <_nouses>true</_nouses>
-          </instructions>
-        </configuration>
-      </plugin>
-    </plugins>
-  </build>
-
-</project>
-
--- a/common/command/src/main/java/com/redhat/thermostat/common/command/InvalidMessageException.java	Tue Jun 13 12:02:12 2017 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,50 +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
- * <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;
-
-/**
- * Exception thrown when an improperly encoded {@link Message} has been received.
- *
- */
-@SuppressWarnings("serial")
-public class InvalidMessageException extends Exception {
-
-    public InvalidMessageException(String message) {
-        super(message);
-    }
-}
-
--- a/common/command/src/main/java/com/redhat/thermostat/common/command/Message.java	Tue Jun 13 12:02:12 2017 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,53 +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
- * <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;
-
-/**
- * Base interface for the various message types.
- *
- * @see Request
- * @see Response
- */
-public interface Message {
-
-    interface MessageType {
-    }
-
-    MessageType getType();
-
-}
-
--- a/common/command/src/main/java/com/redhat/thermostat/common/command/Messages.java	Tue Jun 13 12:02:12 2017 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,94 +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
- * <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 java.util.Collection;
-
-/**
- * Helper class for comparing messages, such as {@link Request} and
- * {@link Response}.
- * 
- */
-public class Messages {
-
-    /**
-     * Two requests, a and b, are considered equal if and only if they are,
-     * not-null, of the same type and all parameters (all keys and values)
-     * match. Listeners and targets are ignored.
-     * 
-     * @return true if a and b are both not-null and equal. false otherwise.
-     */
-    public static boolean equal(Request a, Request b) {
-        if (a == null || b == null) {
-            return false;
-        }
-        // type needs to be the same
-        if (a.getType() != b.getType()) {
-            return false;
-        }
-        // all parameters and values need to match
-        Collection<String> ourParamValues = a.getParameterNames();
-        Collection<String> otherParamValues = b.getParameterNames();
-        if (ourParamValues.size() != otherParamValues.size()) {
-            return false;
-        }
-        for (String name: ourParamValues) {
-            String otherParamValue = b.getParameter(name);
-            if (otherParamValue == null) {
-                // other doesn't have param which we have
-                return false;
-            } else {
-                // both requests contain same param name
-                String ourParamValue = a.getParameter(name);
-                if (!ourParamValue.equals(otherParamValue)) {
-                    return false;
-                }
-            }
-        }
-        return true;
-    }
-    
-    /**
-     * Two responses are equal if and only if they are of the same type.
-     * 
-     * @return true if a and b are both not-null and are equal. false otherwise.
-     */
-    public static boolean equal(Response a, Response b) {
-        return a.getType() == b.getType();
-    }
-}
-
--- a/common/command/src/main/java/com/redhat/thermostat/common/command/Request.java	Tue Jun 13 12:02:12 2017 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,161 +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
- * <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 java.net.InetSocketAddress;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
-import java.util.TreeMap;
-import java.util.concurrent.CopyOnWriteArrayList;
-
-/**
- * <p>
- * A Request object represents a request passed from a client
- * to an agent. Each request is separate, complete and unordered. The
- * agent may or may not take an action when it receives a request.
- * </p>
- * <p>
- * Requests are meant for controlling the agent, not for asking an
- * agent nor sending an agent any sort of data.
- * </p>
- */
-public class Request implements Message {
-    
-    private static final String RECEIVER = "receiver";
-    
-    public static final String CLIENT_TOKEN = "client-token";
-    public static final String AUTH_TOKEN = "auth-token";
-    public static final String ACTION = "action-name";
-    public static final String UNKNOWN_HOSTNAME = "";
-
-    private static final String FILTERED_PARAM_VALUE = "<filtered>";
-    private static final Set<String> FILTERED_PARAMS;
-    
-    static {
-        FILTERED_PARAMS = new HashSet<>();
-        FILTERED_PARAMS.add(AUTH_TOKEN);
-        FILTERED_PARAMS.add(CLIENT_TOKEN);
-    }
-    
-
-    public enum RequestType implements MessageType {
-        NO_RESPONSE_EXPECTED,
-        RESPONSE_EXPECTED,
-        MULTIPART_RESPONSE_EXPECTED;
-    }
-
-    private final RequestType type;
-    private final Map<String, String> parameters;
-    private final InetSocketAddress target;
-    private final Collection<RequestResponseListener> listeners;
-
-
-    public Request(RequestType type, InetSocketAddress target) {
-        this.type = type;
-        parameters = new TreeMap<>();
-        this.target = target;
-        listeners = new CopyOnWriteArrayList<>();
-    }
-
-    @Override
-    public MessageType getType() {
-        return type;
-    }
-
-    public void setParameter(String name, String value) {
-        parameters.put(name, value);
-    }
-
-    public String getParameter(String name) {
-        return parameters.get(name);
-    }
-
-    public Collection<String> getParameterNames() {
-        return parameters.keySet();
-    }
-
-    public void setReceiver(String clazz) {
-        setParameter(RECEIVER, clazz);
-    }
-
-    public String getReceiver() {
-        return getParameter(RECEIVER);
-    }
-
-    public InetSocketAddress getTarget() {
-        return target;
-    }
-
-    public void addListener(RequestResponseListener listener) {
-        listeners.add(listener);
-    }
-
-    public void removeListener(RequestResponseListener listener) {
-        listeners.remove(listener);
-    }
-
-    public Collection<RequestResponseListener> getListeners() {
-        return Collections.unmodifiableCollection(listeners);
-    }
-    
-    @Override
-    public String toString() {
-        Map<String, String> filteredParams = getFilteredParams(parameters);
-        return "{ Request: {target = " + target.toString() + "}, {type = " +
-                type.name() + "}, {parameters = " + filteredParams +
-                "} }";
-    }
-    
-    // package-private for testing
-    Map<String, String> getFilteredParams(Map<String, String> unfiltered) {
-        Map<String, String> filtered = new TreeMap<>();
-        for (String key: unfiltered.keySet()) {
-            if (FILTERED_PARAMS.contains(key)) {
-                // actual value may be security sensitive
-                filtered.put(key, FILTERED_PARAM_VALUE);
-            } else {
-                filtered.put(key, unfiltered.get(key));
-            }
-        }
-        return filtered;
-    }
-    
-}
-
--- a/common/command/src/main/java/com/redhat/thermostat/common/command/RequestResponseListener.java	Tue Jun 13 12:02:12 2017 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,47 +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
- * <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;
-
-/**
- * A listener that is notified of the response to given request.
- */
-public interface RequestResponseListener {
-
-    public void fireComplete(Request request, Response response);
-
-}
-
--- a/common/command/src/main/java/com/redhat/thermostat/common/command/Response.java	Tue Jun 13 12:02:12 2017 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,97 +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
- * <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;
-
-
-/**
- * A Response object represents a response message passed from an agent
- * to a client.  Responses must not be used to send data; they reach only a
- * specific client and are not recorded.
- * <p>
- * The implementation details of this class are subject to change at any time.
- * <p>
- * Response objects are serialized over the command channel in the following
- * format:
- * <pre>
- * ------------
- * | A | TYPE |
- * ------------
- * </pre>
- * A is an 32 bit integer representing the length - in bytes - of TYPE. TYPE
- * is a byte array representing the string of the response type (e.g.
- * "OK").
- */
-public class Response implements Message {
-
-    // TODO add parameter support to provide more information in some of these types.
-    public enum ResponseType implements MessageType {
-        /** Request has been acknowledged and completed agent-side */
-        OK,
-
-        /** Request has been acknowledged and refused agent-side. */
-        NOK,
-
-        /**
-         * Request has been acknowledged, but no action deemed necessary
-         * agent-side.
-         */
-        NOOP,
-
-        /**
-         * An error occurred. The status of the request is not known.
-         */
-        ERROR,
-
-	/**
-         * When authentication fails in SecureStorage.
-	 */
-	AUTH_FAILED;
-    }
-
-    private ResponseType type;
-
-    public Response (ResponseType type) {
-        this.type = type;
-    }
-
-    @Override
-    public ResponseType getType() {
-        return type;
-    }
-
-}
-
--- a/common/command/src/main/java/com/redhat/thermostat/common/command/noapi/ConfigurationCommandContext.java	Tue Jun 13 12:02:12 2017 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,50 +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
- * <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.noapi;
-
-import com.redhat.thermostat.shared.config.SSLConfiguration;
-
-import io.netty.bootstrap.AbstractBootstrap;
-
-public interface ConfigurationCommandContext {
-
-    public AbstractBootstrap<?, ?> getBootstrap();
-
-    public SSLConfiguration getSSLConfiguration();
-
-}
-
--- a/common/command/src/main/java/com/redhat/thermostat/common/command/noapi/DecodingHelper.java	Tue Jun 13 12:02:12 2017 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,158 +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
- * <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.noapi;
-
-import io.netty.buffer.ByteBuf;
-import io.netty.buffer.Unpooled;
-
-public class DecodingHelper {
-    
-    /**
-     * Attempts to decode a String from the given buffer, carefully not changing
-     * the reader index of the buffer. The returned decoding context will tell
-     * you if the decoding has fully completed and if so how many bytes have
-     * been consumed. It's the callers responsibility to free the buffer's
-     * resources. Set the {@link ByteBuf#readerIndex(int)} to the value as
-     * returned by the decoding context and then discard bytes using
-     * {@link ByteBuf#discardReadBytes()}.
-     * 
-     * @param buffer
-     *            The buffer from which to decode the String from.
-     * 
-     * @return A decoding context giving you details about the decoding status
-     *         and a way to retrieve the decoded value.
-     */
-    public static StringDecodingContext decodeString(ByteBuf buffer) {
-        StringDecodingContext ctx = new StringDecodingContext();
-        return decodeString(buffer, ctx);
-    }
-    
-    private static StringDecodingContext decodeString(ByteBuf buffer, StringDecodingContext ctx) {
-        if (buffer.readableBytes() < 4) {
-            ctx.setState(StringDecodingState.INCOMPLETE_LENGTH_VAL);
-            return ctx;
-        }
-        int length = buffer.getInt(buffer.readerIndex());
-        ctx.addToBytesRead(4);
-        ctx.setState(StringDecodingState.LENGTH_READ);
-        return decodeString(length, buffer, ctx);
-    }
-
-    /**
-     * Attempts to decode String parameters from the given buffer, carefully not
-     * changing the reader index of the buffer. The returned decoding context
-     * will tell you if the decoding has fully completed and if so how many
-     * bytes have been consumed. It's the callers responsibility to free the
-     * buffer's resources. Set the {@link ByteBuf#readerIndex(int)} to the value
-     * as returned by the decoding context and then discard bytes using
-     * {@link ByteBuf#discardReadBytes()}.
-     * 
-     * @param buffer The buffer from which to decode the parameters from.
-     * @return A decoding context giving you details about the decoding status
-     *         and a way to retrieve the decoded parameters.
-     */
-    public static ParameterDecodingContext decodeParameters(ByteBuf buffer) {
-        ParameterDecodingContext ctx = new ParameterDecodingContext();
-        return decodeParameters(buffer, ctx);
-    }
-    
-    private static ParameterDecodingContext decodeParameters(ByteBuf buffer, ParameterDecodingContext ctx) {
-        if (buffer.readableBytes() < 4) {
-            ctx.setState(ParameterDecodingState.INCOMPLETE_PARAMS_LENGTH);
-            return ctx;
-        }
-        int numParms = buffer.getInt(buffer.readerIndex());
-        ctx.addToBytesRead(4);
-        ctx.setState(ParameterDecodingState.PARAMS_LENGTH_READ);
-        for (int i = 0; i < numParms; i++) {
-            decodeParameter(buffer, ctx);
-        }
-        if ( (ctx.getState() == ParameterDecodingState.PARAMS_LENGTH_READ && numParms == 0)
-             || ctx.getState() == ParameterDecodingState.PARAM_KV_DATA_PLUS_ONE_READ) {
-            // Either zero parameters, or all params successfully read
-            ctx.setState(ParameterDecodingState.ALL_PARAMETERS_READ);
-        }
-        return ctx;
-    }
-
-    private static void decodeParameter(ByteBuf buffer, ParameterDecodingContext ctx) {
-        if (buffer.readableBytes() < ctx.getBytesRead() + 8) {
-            ctx.setState(ParameterDecodingState.INCOMPLETE_PARAM_KV_LENGTH);
-            return;
-        }
-        int currIdx = buffer.readerIndex() + ctx.getBytesRead();
-        int nameLength = buffer.getInt(currIdx);
-        int valueLength = buffer.getInt(currIdx + 4);
-        ctx.setState(ParameterDecodingState.PARAM_KV_LENGTH_READ);
-        ctx.addToBytesRead(8);
-        int nameStartIdx = buffer.readerIndex() + ctx.getBytesRead();
-        ByteBuf nameBuf = buffer.slice(nameStartIdx, buffer.readableBytes() - nameStartIdx);
-        StringDecodingContext nameCtx = decodeString(nameLength, nameBuf, new StringDecodingContext());
-        if (nameCtx.getState() != StringDecodingState.VALUE_READ) {
-            ctx.setState(ParameterDecodingState.INCOMPLETE_PARAM_KV_DATA);
-            return;
-        }
-        String name = nameCtx.getValue();
-        ctx.addToBytesRead(nameCtx.getBytesRead());
-        int valueStartIdx = buffer.readerIndex() + ctx.getBytesRead();
-        ByteBuf valueBuf = buffer.slice(valueStartIdx, buffer.readableBytes() - valueStartIdx);
-        StringDecodingContext valueCtx = decodeString(valueLength, valueBuf, new StringDecodingContext());
-        if (valueCtx.getState() != StringDecodingState.VALUE_READ) {
-            ctx.setState(ParameterDecodingState.INCOMPLETE_PARAM_KV_DATA);
-            return;
-        }
-        String value = valueCtx.getValue();
-        ctx.addToBytesRead(valueCtx.getBytesRead());
-        ctx.setState(ParameterDecodingState.PARAM_KV_DATA_PLUS_ONE_READ);
-        ctx.addParameter(name, value);
-    }
-
-    private static StringDecodingContext decodeString(int length, ByteBuf buffer, StringDecodingContext ctx) {
-        if (buffer.readableBytes() < ctx.getBytesRead() + length) {
-            ctx.setState(StringDecodingState.INCOMPLETE_STR_VAL);
-            return ctx;
-        }
-        int startIdx = buffer.readerIndex() + ctx.getBytesRead();
-        ByteBuf valueBuf = buffer.slice(startIdx, length);
-        byte[] stringBytes = Unpooled.copiedBuffer(valueBuf).array();
-        ctx.setState(StringDecodingState.VALUE_READ);
-        ctx.addToBytesRead(length);
-        ctx.setValue(new String(stringBytes));
-        return ctx;
-    }
-}
-
--- a/common/command/src/main/java/com/redhat/thermostat/common/command/noapi/EncodingHelper.java	Tue Jun 13 12:02:12 2017 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,68 +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
- * <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.noapi;
-
-import io.netty.buffer.ByteBuf;
-import io.netty.buffer.Unpooled;
-
-public class EncodingHelper {
-
-    public static void encode(String name, String value, ByteBuf dynamicBuffer) {
-        byte[] nameBytes = name.getBytes();
-        byte[] valueBytes = value.getBytes();
-        dynamicBuffer.writeInt(nameBytes.length);
-        dynamicBuffer.writeInt(valueBytes.length);
-        dynamicBuffer.writeBytes(nameBytes);
-        dynamicBuffer.writeBytes(valueBytes);
-    }
-    
-    public static ByteBuf encode(String value) {
-        byte[] valBytes = value.getBytes();
-        int length = 4 + valBytes.length;
-        ByteBuf buf = Unpooled.buffer(length, length);
-        buf.writeInt(valBytes.length);
-        buf.writeBytes(valBytes);
-        return buf;
-    }
-
-    public static String trimType(String full) {
-        int typePointer = full.lastIndexOf('.');
-        return full.substring(typePointer + 1);
-    }
-    
-}
-
--- a/common/command/src/main/java/com/redhat/thermostat/common/command/noapi/MessageEncoder.java	Tue Jun 13 12:02:12 2017 +0200
+++ /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
- * <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.noapi;
-
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-import com.redhat.thermostat.common.command.Message;
-import com.redhat.thermostat.common.utils.LoggingUtils;
-
-import io.netty.buffer.ByteBuf;
-import io.netty.channel.ChannelHandlerContext;
-import io.netty.handler.codec.MessageToByteEncoder;
-
-public abstract class MessageEncoder extends MessageToByteEncoder<Message> {
-
-    private static final Logger logger = LoggingUtils.getLogger(MessageEncoder.class);
-    
-    protected MessageEncoder() {
-        super();
-    }
-    
-    @Override
-    public void encode(ChannelHandlerContext ctx, Message msg, ByteBuf out) {
-        ByteBuf encodedMessage = encode(msg);
-        out.writeBytes(encodedMessage);
-    }
-
-    /**
-     * Transforms the specified message into another message and return the
-     * transformed message. Note that you can not return {@code null}, unlike
-     * you can in
-     * {@link MessageDecoder#decode(org.jboss.netty.buffer.ChannelBuffer)}; you
-     * must return something, at least {@link ChannelBuffers#EMPTY_BUFFER}.
-     */
-    protected abstract ByteBuf encode(Message originalMessage);
-    
-    @Override
-    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
-        logger.log(Level.WARNING, "Exception caught", cause);
-        ctx.close();
-    }
-}
-
--- a/common/command/src/main/java/com/redhat/thermostat/common/command/noapi/ParameterDecodingContext.java	Tue Jun 13 12:02:12 2017 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,99 +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
- * <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.noapi;
-
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Map;
-
-/**
- * Parameter decoding context.
- * 
- * @see DecodingHelper
- * @see ParameterDecodingState
- */
-public class ParameterDecodingContext {
-    
-    ParameterDecodingContext() {
-        // package-private constructor. Only this package creates instances.
-    }
-
-    private final Map<String, String> values = new HashMap<>();
-    private ParameterDecodingState state;
-    private int bytesRead;
-    
-    /**
-     * 
-     * @return A map of parameter {@code key=value} pairs.
-     * 
-     * @throws IllegalStateException if not all parameters have yet been decoded.
-     */
-    public Map<String, String> getValues() {
-        if (state != ParameterDecodingState.ALL_PARAMETERS_READ) {
-            throw new IllegalStateException("Not all parameters have yet been decoded");
-        }
-        return Collections.unmodifiableMap(values);
-    }
-    
-    /**
-     * 
-     * @return The current decoding state.
-     */
-    public ParameterDecodingState getState() {
-        return state;
-    }
-    
-    /**
-     * 
-     * @return The number of bytes consumed so far.
-     */
-    public int getBytesRead() {
-        return bytesRead;
-    }
-    
-    void setState(ParameterDecodingState newState) {
-        state = newState;
-    }
-    
-    void addParameter(String key, String value) {
-        values.put(key, value);
-    }
-    
-    void addToBytesRead(int numBytes) {
-        bytesRead += numBytes;
-    }
-}
--- a/common/command/src/main/java/com/redhat/thermostat/common/command/noapi/ParameterDecodingState.java	Tue Jun 13 12:02:12 2017 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,54 +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
- * <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.noapi;
-
-public enum ParameterDecodingState {
-    /** Insufficient data for decoding parameters */
-    INCOMPLETE_PARAMS_LENGTH,
-    /** Number of parameters has been read */
-    PARAMS_LENGTH_READ,
-    /** Insufficient data for decoding a parameter pair */
-    INCOMPLETE_PARAM_KV_LENGTH,
-    /** Length of param's key/value has been read */
-    PARAM_KV_LENGTH_READ,
-    /** Insufficient data for decoding values of a parameter pair */
-    INCOMPLETE_PARAM_KV_DATA,
-    /** One (of potentially many) KV data pair decoded */
-    PARAM_KV_DATA_PLUS_ONE_READ,
-    /** All parameters have been read from fragmented data */
-    ALL_PARAMETERS_READ
-}
--- a/common/command/src/main/java/com/redhat/thermostat/common/command/noapi/RequestEncoder.java	Tue Jun 13 12:02:12 2017 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,116 +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
- * <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.noapi;
-
-import java.util.Collection;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-import com.redhat.thermostat.common.command.Message;
-import com.redhat.thermostat.common.command.Request;
-import com.redhat.thermostat.common.utils.LoggingUtils;
-
-import io.netty.buffer.ByteBuf;
-import io.netty.buffer.ByteBufUtil;
-import io.netty.buffer.Unpooled;
-
-/**
- * <p>
- * {@link Request} objects are serialized over the command channel in the
- * following format:
- * <pre>
- * -------------------------
- * | A | TYPE | B | PARAMS |
- * -------------------------
- * 
- * A is an 32 bit integer representing the length - in bytes - of TYPE. TYPE
- * is a byte array representing the string of the request type (e.g.
- * "RESPONSE_EXPECTED") B is a 32 bit integer representing the number of
- * request parameters which follow.
- * 
- * PARAMS (if B > 0) is a variable length stream of the following format:
- * 
- * It is a simple encoding of name => value pairs.
- * 
- * -----------------------------------------------------------------------------------------------
- * | I_1 | K_1 | P_1 | V_1 | ... | I_(n-1) | K_(n-1) | P_(n-1) | V_(n-1) | I_n | K_n | P_n | V_n |
- * -----------------------------------------------------------------------------------------------
- * 
- * I_n  A 32 bit integer representing the length - in bytes - of the n'th
- *      parameter name.
- * K_n  A 32 bit integer representing the length - in bytes - of the n'th
- *      parameter value.
- * P_n  A byte array representing the string of the n'th parameter name.
- * V_n  A byte array representing the string of the n'th parameter value.
- * </pre>
- * </p>
- */
-public class RequestEncoder extends MessageEncoder {
-
-    private static final Logger logger = LoggingUtils.getLogger(RequestEncoder.class);
-
-    /*
-     * See the javadoc of RequestEncoder for a description of the encoding.
-     */
-    @Override
-    public ByteBuf encode(Message msg) {
-        // At this point we are only getting Messages. Since our only
-        // registered MessageEncoder is the one for Requests a cast
-        // to Request should be safe.
-        Request request = (Request) msg;
-        logger.log(Level.FINEST, "encoding Request object " + request.toString());
-
-        // Request Type
-        String requestType = EncodingHelper.trimType(request.getType()
-                .toString());
-        ByteBuf typeBuffer = EncodingHelper.encode(requestType);
-
-        // Parameters
-        ByteBuf parmsBuffer = Unpooled.buffer();
-        Collection<String> parmNames = request.getParameterNames();
-        parmsBuffer.writeInt(parmNames.size());
-        for (String parmName : parmNames) {
-            EncodingHelper.encode(parmName, request.getParameter(parmName),
-                    parmsBuffer);
-        }
-        // Compose the full message.
-        ByteBuf buf = Unpooled.wrappedBuffer(typeBuffer, parmsBuffer);
-        logger.log(Level.FINEST, "encoded reqest as: " + ByteBufUtil.hexDump(buf));
-        return buf;
-    }
-}
-
--- a/common/command/src/main/java/com/redhat/thermostat/common/command/noapi/StringDecodingContext.java	Tue Jun 13 12:02:12 2017 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,93 +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
- * <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.noapi;
-
-/**
- * Context for decoding Strings.
- * 
- * @see DecodingHelper
- * @see StringDecodingState
- */
-public class StringDecodingContext {
-    
-    StringDecodingContext() {
-        // package-private constructor. Only this package creates instances.
-    }
-    
-    private StringDecodingState state;
-    private String val;
-    private int bytesRead;
-    
-    /**
-     * 
-     * @return The decoded String value.
-     */
-    public String getValue() {
-        if (state != StringDecodingState.VALUE_READ) {
-            throw new IllegalStateException("Data not yet defragmented");
-        }
-        return val;
-    }
-    
-    /**
-     * 
-     * @return The current decoding state.
-     */
-    public StringDecodingState getState() {
-        return state;
-    }
-    
-    /**
-     * 
-     * @return The bytes read from a buffer.
-     */
-    public int getBytesRead() {
-        return bytesRead;
-    }
-    
-    void setState(StringDecodingState newState) {
-        state = newState;
-    }
-    
-    void setValue(String value) {
-        val = value;
-    }
-    
-    void addToBytesRead(int value) {
-        bytesRead += value;
-    }
-}
\ No newline at end of file
--- a/common/command/src/main/java/com/redhat/thermostat/common/command/noapi/StringDecodingState.java	Tue Jun 13 12:02:12 2017 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,51 +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
- * <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.noapi;
-
-public enum StringDecodingState {
-    /** Not enough data to decode any String */
-    INCOMPLETE_LENGTH_VAL,
-    /**
-     * Enough data to decode the length of the String, but not the String
-     * value just yet.
-     */
-    LENGTH_READ,
-    /** Incomplete data for string value decoding. */
-    INCOMPLETE_STR_VAL,
-    /** String length and value read **/
-    VALUE_READ
-}
\ No newline at end of file
--- a/common/command/src/test/java/com/redhat/thermostat/common/command/MessagesTest.java	Tue Jun 13 12:02:12 2017 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,96 +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
- * <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.assertFalse;
-import static org.junit.Assert.assertTrue;
-
-import org.junit.Test;
-
-import com.redhat.thermostat.common.command.Request.RequestType;
-import com.redhat.thermostat.common.command.Response.ResponseType;
-
-public class MessagesTest {
-    
-    @Test
-    public void testRequestsEqual() {
-        // self
-        Request req1 = new Request(RequestType.RESPONSE_EXPECTED, null);
-        assertTrue(Messages.equal(req1, req1));
-        req1.setParameter("test", "blah");
-        assertTrue(Messages.equal(req1, req1));
-        
-        req1 = new Request(RequestType.RESPONSE_EXPECTED, null);
-        // basics
-        assertFalse(Messages.equal((Request)null, (Request)null));
-        assertFalse(Messages.equal(req1, null));
-        assertFalse(Messages.equal(req1, new Request(RequestType.MULTIPART_RESPONSE_EXPECTED, null)));
-        
-        Request req2 = new Request(RequestType.RESPONSE_EXPECTED, null);
-        String receiverClassName = "com.example.receivers.MyReceiver";
-        req1.setReceiver(receiverClassName);
-        req2.setReceiver(receiverClassName);
-        // receivers are parameters
-        assertTrue(Messages.equal(req1, req2));
-        
-        // add parameters
-        req1.setParameter("fluff", "foo");
-        req2.setParameter("fluff", "foo");
-        assertTrue(Messages.equal(req1, req1));
-        
-        // one key is different
-        req2.setParameter("test", "false");
-        assertFalse(Messages.equal(req1, req2));
-        
-        req1.setParameter("test", "false");
-        assertTrue(Messages.equal(req1, req2));
-        req2.setParameter("test", "true");
-        assertFalse(Messages.equal(req1, req2));
-    }
-    
-    @Test
-    public void testResponsesEqual() {
-        Response r = new Response(ResponseType.NOK);
-        assertTrue(Messages.equal(r, r));
-        Response r2 = new Response(ResponseType.NOK);
-        assertTrue(Messages.equal(r, r2));
-        Response r3 = new Response(ResponseType.OK);
-        assertFalse(Messages.equal(r2, r3));
-        assertFalse(Messages.equal(r, r3));
-    }
-}
-
--- a/common/command/src/test/java/com/redhat/thermostat/common/command/RequestTest.java	Tue Jun 13 12:02:12 2017 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,186 +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
- * <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 static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-
-import java.net.InetSocketAddress;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
-
-import org.junit.Test;
-
-import com.redhat.thermostat.common.command.Request.RequestType;
-
-public class RequestTest {
-
-    private class RequestResponseListenerImpl implements RequestResponseListener {
-        @Override
-        public void fireComplete(Request request, Response response) {
-            // Won't actually be used.
-        }
-    }
-
-    private static final int PORT = 123;
-    private static final String HOST = "test.example.com";
-
-    @Test
-    public void testGetTypeAndTarget() {
-        Request request = new Request(RequestType.RESPONSE_EXPECTED, new InetSocketAddress(HOST, PORT));
-        RequestType type = (RequestType) request.getType();
-        assertEquals(RequestType.RESPONSE_EXPECTED, type);
-
-        InetSocketAddress target = (InetSocketAddress) request.getTarget();
-        assertEquals(PORT, target.getPort());
-        assertEquals(HOST, target.getHostString());
-    }
-
-    @Test
-    public void testAddListener() {
-        Request request = new Request(RequestType.RESPONSE_EXPECTED, new InetSocketAddress(HOST, PORT));
-        RequestResponseListener listener1 = new RequestResponseListenerImpl();
-        RequestResponseListener listener2 = new RequestResponseListenerImpl();
-        request.addListener(listener1);
-        request.addListener(listener2);
-        Collection<RequestResponseListener> listeners = request.getListeners();
-        assertEquals(2, listeners.size());
-        assertTrue(listeners.contains(listener1));
-        assertTrue(listeners.contains(listener2));
-    }
-
-    @Test
-    public void testRemoveListener() {
-        Request request = new Request(RequestType.RESPONSE_EXPECTED, new InetSocketAddress(HOST, PORT));
-        RequestResponseListener listener1 = new RequestResponseListenerImpl();
-        RequestResponseListener listener2 = new RequestResponseListenerImpl();
-        request.addListener(listener1);
-        request.addListener(listener2);
-        Collection<RequestResponseListener> listeners = request.getListeners();
-        assertEquals(2, listeners.size());
-        assertTrue(listeners.contains(listener1));
-        assertTrue(listeners.contains(listener2));
-        request.removeListener(listener1);
-        listeners = request.getListeners();
-        assertEquals(1, listeners.size());
-        assertTrue(listeners.contains(listener2));
-        assertFalse(listeners.contains(listener1));
-    }
-    
-    @Test
-    public void canGetHostname() {
-        // unresolved hostname
-        InetSocketAddress addr = new InetSocketAddress(HOST, PORT);
-        assertTrue(addr.isUnresolved());
-        Request request = new Request(RequestType.RESPONSE_EXPECTED, addr);
-        assertEquals(HOST, request.getTarget().getHostString());
-        
-    }
-    
-    @Test
-    public void testToString() {
-        InetSocketAddress addr = new InetSocketAddress(1234);
-        Request request = new Request(RequestType.RESPONSE_EXPECTED, addr);
-        assertEquals("{ Request: {target = "+ addr.toString() + "}, {type = RESPONSE_EXPECTED}, {parameters = {}} }", request.toString());
-    }
-    
-    /*
-     * It is important that request parameters won't get logged (at any log
-     * level). Since toString() is used in some log statements it should be
-     * sufficient to verify toString() filters parameters appropriately.
-     */
-    @Test
-    public void testToStringFiltersParams() {
-        InetSocketAddress addr = new InetSocketAddress(1234);
-        Request request = new Request(RequestType.RESPONSE_EXPECTED, addr);
-        request.setParameter(Request.AUTH_TOKEN, "foo-auth-token");
-        request.setParameter(Request.CLIENT_TOKEN, "bar-client-token");
-        String preamble = "{ Request: {target = "+ addr.toString() + 
-                "}, {type = RESPONSE_EXPECTED}, ";
-        String postfix = " }";
-        // maps aren't ordered. work around it by using a set assert.
-        String expectedParamOrdering1 = "{parameters = {" +
-                Request.AUTH_TOKEN +"=<filtered>, " +
-                Request.CLIENT_TOKEN +"=<filtered>}}";
-        String expectedParamOrdering2 = "{parameters = {" +
-                Request.CLIENT_TOKEN +"=<filtered>, " +
-                Request.AUTH_TOKEN +"=<filtered>}}";
-        Set<String> expectedStrings = new HashSet<>();
-        expectedStrings.add(preamble + expectedParamOrdering1 + postfix);
-        expectedStrings.add(preamble + expectedParamOrdering2 + postfix);
-        String actual = request.toString();
-        assertTrue("Security sensitive parameters should be filtered! String was: "
-                + actual, expectedStrings.contains(actual));
-    }
-    
-    @Test
-    public void testFilterParams() {
-        Map<String, String> origParams = new HashMap<>();
-        origParams.put(Request.AUTH_TOKEN, "foo-auth-token");
-        origParams.put(Request.CLIENT_TOKEN, "bar-client-token");
-        origParams.put("foo-param", "something");
-        
-        InetSocketAddress addr = new InetSocketAddress(1234);
-        Request request = new Request(RequestType.RESPONSE_EXPECTED, addr);
-        Map<String, String> filteredParams = request.getFilteredParams(origParams);
-        assertEquals(Request.AUTH_TOKEN + " should be filtered", "<filtered>", filteredParams.get(Request.AUTH_TOKEN));
-        assertEquals(Request.CLIENT_TOKEN + " should be filtered", "<filtered>", filteredParams.get(Request.CLIENT_TOKEN));
-        assertEquals("something", filteredParams.get("foo-param"));
-        assertEquals(3, filteredParams.size());
-        
-        origParams.clear();
-        origParams.put(Request.AUTH_TOKEN, "foo-auth-token");
-        origParams.put("foo-param", "something-new");
-        filteredParams = request.getFilteredParams(origParams);
-        assertEquals(Request.AUTH_TOKEN + " should be filtered", "<filtered>", filteredParams.get(Request.AUTH_TOKEN));
-        assertEquals("something-new", filteredParams.get("foo-param"));
-        assertEquals(2, filteredParams.size());
-        
-        origParams.clear();
-        origParams.put("bar-param", "should-be-unchanged");
-        origParams.put(Request.CLIENT_TOKEN, "client-token");
-        filteredParams = request.getFilteredParams(origParams);
-        assertEquals(Request.CLIENT_TOKEN + " should be filtered", "<filtered>", filteredParams.get(Request.CLIENT_TOKEN));
-        assertEquals("should-be-unchanged", filteredParams.get("bar-param"));
-        assertEquals(2, filteredParams.size());
-    }
-}
-
--- a/common/command/src/test/java/com/redhat/thermostat/common/command/ResponseTest.java	Tue Jun 13 12:02:12 2017 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,55 +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
- * <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 org.junit.Test;
-
-import com.redhat.thermostat.common.command.Response.ResponseType;
-
-public class ResponseTest {
-
-    @Test
-    public void testGetType() {
-        Response r = new Response(ResponseType.OK);
-        assertEquals(ResponseType.OK, r.getType());
-        r = new Response(ResponseType.ERROR);
-        assertEquals(ResponseType.ERROR, r.getType());
-    }
-}
-
--- a/common/command/src/test/java/com/redhat/thermostat/common/command/noapi/DecodingHelperTest.java	Tue Jun 13 12:02:12 2017 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,217 +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
- * <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.noapi;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-
-import org.junit.Test;
-
-import io.netty.buffer.ByteBuf;
-import io.netty.buffer.Unpooled;
-
-public class DecodingHelperTest {
-
-    /**
-     * Fragmented string case. Not enough data for string decoding. Specifically
-     * missing length of string.
-     */
-    @Test
-    public void decodeStringMissingLengthBytes() {
-        ByteBuf buf = Unpooled.buffer(0, 0);
-        assertEquals(0, buf.readerIndex());
-        StringDecodingContext ctx = DecodingHelper.decodeString(buf);
-        assertEquals("reader index should be untouched", 0, buf.readerIndex());
-        assertEquals(StringDecodingState.INCOMPLETE_LENGTH_VAL, ctx.getState());
-        assertEquals(0, ctx.getBytesRead());
-    }
-    
-    /**
-     * Fragmented string case. Not enough data for string decoding. Specifically
-     * missing string bytes.
-     */
-    @Test
-    public void decodeStringMissingStringBytes() {
-        ByteBuf buf = Unpooled.buffer(0, 4);
-        buf.writeInt(5);
-        assertEquals(0, buf.readerIndex());
-        StringDecodingContext ctx = DecodingHelper.decodeString(buf);
-        assertEquals("reader index should be untouched", 0, buf.readerIndex());
-        assertEquals(StringDecodingState.INCOMPLETE_STR_VAL, ctx.getState());
-        assertEquals(4, ctx.getBytesRead());
-    }
-    
-    @Test
-    public void canDecodeStringFull() {
-        String original = "this is a test";
-        ByteBuf buf = Unpooled.buffer(0, 4 + original.length());
-        buf.writeInt(original.length());
-        buf.writeBytes(original.getBytes());
-        assertEquals(0, buf.readerIndex());
-        StringDecodingContext ctx = DecodingHelper.decodeString(buf);
-        assertEquals("reader index should be untouched", 0, buf.readerIndex());
-        assertEquals(original.getBytes().length + 4, ctx.getBytesRead());
-        assertEquals(StringDecodingState.VALUE_READ, ctx.getState());
-        assertEquals(original, ctx.getValue());
-    }
-    
-    @Test
-    public void canDecodeParametersFull() {
-        String key1 = "key1";
-        String value1 = "value1";
-        String key2 = "this is a key";
-        String value2 = "value 2";
-        int totalBytes = 4 /* # params */ +
-                         8 /* len key + len value */ +
-                         key1.getBytes().length +
-                         value1.getBytes().length +
-                         8 /* len key + len value */ +
-                         key2.getBytes().length +
-                         value2.getBytes().length;
-        ByteBuf buf = Unpooled.buffer(0, totalBytes);
-        buf.writeInt(2);
-        buf.writeInt(key1.getBytes().length);
-        buf.writeInt(value1.getBytes().length);
-        buf.writeBytes(key1.getBytes());
-        buf.writeBytes(value1.getBytes());
-        buf.writeInt(key2.getBytes().length);
-        buf.writeInt(value2.getBytes().length);
-        buf.writeBytes(key2.getBytes());
-        buf.writeBytes(value2.getBytes());
-        assertEquals(0, buf.readerIndex());
-        ParameterDecodingContext ctx = DecodingHelper.decodeParameters(buf);
-        assertEquals(ParameterDecodingState.ALL_PARAMETERS_READ, ctx.getState());
-        assertEquals(totalBytes, ctx.getBytesRead());
-        assertEquals("reader index should be untouched", 0, buf.readerIndex());
-        assertEquals(value1, ctx.getValues().get(key1));
-        assertEquals(value2, ctx.getValues().get(key2));
-    }
-    
-    @Test
-    public void canDecodeParametersFullZeroParams() {
-        int totalBytes = 4 /* # params */;
-        ByteBuf buf = Unpooled.buffer(0, totalBytes);
-        buf.writeInt(0);
-        assertEquals(0, buf.readerIndex());
-        ParameterDecodingContext ctx = DecodingHelper.decodeParameters(buf);
-        assertEquals(ParameterDecodingState.ALL_PARAMETERS_READ, ctx.getState());
-        assertEquals(totalBytes, ctx.getBytesRead());
-        assertEquals("reader index should be untouched", 0, buf.readerIndex());
-        assertTrue(ctx.getValues().isEmpty());
-    }
-    
-    @Test
-    public void canDecodeFragementedParamNoNumParams() {
-        ByteBuf buf = Unpooled.buffer(0, 0);
-        assertEquals(0, buf.readerIndex());
-        ParameterDecodingContext ctx = DecodingHelper.decodeParameters(buf);
-        assertEquals(ParameterDecodingState.INCOMPLETE_PARAMS_LENGTH, ctx.getState());
-        assertEquals(0, ctx.getBytesRead());
-        assertEquals("reader index should be untouched", 0, buf.readerIndex());
-    }
-    
-    /**
-     * Fragmented test case where the length of the value is missing in the
-     * buffer.
-     */
-    @Test
-    public void canDecodeFragementedParamNoLengthValue() {
-        ByteBuf buf = Unpooled.buffer(0, 8);
-        buf.writeInt(1); // one parameter
-        buf.writeInt(3); // length of key
-        assertEquals(0, buf.readerIndex());
-        ParameterDecodingContext ctx = DecodingHelper.decodeParameters(buf);
-        assertEquals(ParameterDecodingState.INCOMPLETE_PARAM_KV_LENGTH, ctx.getState());
-        assertEquals(4, ctx.getBytesRead()); // num params have been read
-        assertEquals("reader index should be untouched", 0, buf.readerIndex());
-    }
-    
-    /**
-     * Fragmented test case where the length of both the key and value is
-     * missing in the buffer.
-     */
-    @Test
-    public void canDecodeFragementedParamNoLengthKey() {
-        ByteBuf buf = Unpooled.buffer(0, 4);
-        buf.writeInt(1); // one parameter
-        assertEquals(0, buf.readerIndex());
-        ParameterDecodingContext ctx = DecodingHelper.decodeParameters(buf);
-        assertEquals(ParameterDecodingState.INCOMPLETE_PARAM_KV_LENGTH, ctx.getState());
-        assertEquals(4, ctx.getBytesRead()); // num params have been read
-        assertEquals("reader index should be untouched", 0, buf.readerIndex());
-    }
-    
-    /**
-     * Fragmented test case where the key/value data is missing from
-     * the buffer.
-     */
-    @Test
-    public void canDecodeFragementedParamNoDataForKeyValue() {
-        ByteBuf buf = Unpooled.buffer(0, 12);
-        buf.writeInt(1); // one parameter
-        buf.writeInt(2); // length of key
-        buf.writeInt(7); // length of value
-        assertEquals(0, buf.readerIndex());
-        ParameterDecodingContext ctx = DecodingHelper.decodeParameters(buf);
-        assertEquals(ParameterDecodingState.INCOMPLETE_PARAM_KV_DATA, ctx.getState());
-        assertEquals(12, ctx.getBytesRead()); // num params + key/value length have been read
-        assertEquals("reader index should be untouched", 0, buf.readerIndex());
-    }
-    
-    /**
-     * Fragmented test case where the key/value data is missing from
-     * the buffer.
-     */
-    @Test
-    public void canDecodeFragementedParamNoLengthForSecondParam() {
-        String firstKey = "first";
-        String firstValue = "value";
-        int bytesInBuf = 12 + firstKey.getBytes().length + firstValue.getBytes().length;
-        ByteBuf buf = Unpooled.buffer(0, bytesInBuf);
-        buf.writeInt(2); // two params
-        buf.writeInt(firstKey.getBytes().length);
-        buf.writeInt(firstValue.getBytes().length);
-        buf.writeBytes(firstKey.getBytes());
-        buf.writeBytes(firstValue.getBytes());
-        assertEquals(0, buf.readerIndex());
-        ParameterDecodingContext ctx = DecodingHelper.decodeParameters(buf);
-        assertEquals(ParameterDecodingState.INCOMPLETE_PARAM_KV_LENGTH, ctx.getState());
-        assertEquals(bytesInBuf, ctx.getBytesRead()); // read up to the first param
-        assertEquals("reader index should be untouched", 0, buf.readerIndex());
-    }
-}
-
--- a/common/command/src/test/java/com/redhat/thermostat/common/command/noapi/EncodingHelperTest.java	Tue Jun 13 12:02:12 2017 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,74 +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
- * <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.noapi;
-
-import static org.junit.Assert.assertEquals;
-
-import java.nio.ByteBuffer;
-
-import org.junit.Test;
-
-import io.netty.buffer.ByteBuf;
-
-public class EncodingHelperTest {
-
-    @Test
-    public void testEncode() {
-        String input = "a test string";
-        byte[] inputBytes = input.getBytes();
-        ByteBuf buf = EncodingHelper.encode(input);
-        int encodedMessageLength = buf.readInt();
-        assertEquals(inputBytes.length, encodedMessageLength);
-        ByteBuffer bbuf = ByteBuffer.allocate(buf.readableBytes());
-        buf.readBytes(bbuf);
-        byte[] output = bbuf.array();
-        assertEquals(inputBytes.length, output.length);
-        for (int i = 0; i < inputBytes.length; i++) {
-            assertEquals(inputBytes[i], output[i]);
-        }
-    }
-
-    @Test
-    public void testTrimType() {
-        String expected = "remaining";
-        String original = "this.is.going.to.be.trimmed.with.only.the.last." + expected;
-        String stripped = EncodingHelper.trimType(original);
-        assertEquals(expected, stripped);
-    }
-
-}
-
--- a/common/command/src/test/java/com/redhat/thermostat/common/command/noapi/RequestEncoderTest.java	Tue Jun 13 12:02:12 2017 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,160 +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
- * <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.noapi;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-
-import java.net.InetSocketAddress;
-import java.nio.charset.Charset;
-import java.util.Arrays;
-
-import org.junit.Test;
-
-import com.redhat.thermostat.common.command.Request;
-import com.redhat.thermostat.common.command.Request.RequestType;
-
-import io.netty.buffer.ByteBuf;
-import io.netty.buffer.ByteBufUtil;
-import io.netty.buffer.Unpooled;
-
-public class RequestEncoderTest {
-
-    private static final boolean DEBUG = false;
-    /**
-     * Represents low-level bytes for:
-     * <pre>
-     * Request request = new Request(RequestType.RESPONSE_EXPECTED, new InetSocketAddress(20))
-     * request.setParameter("receiver", "com.redhat.foo.bar.Receiver");
-     * </pre>
-     */
-    private static final byte[] REQUEST_BYTES = new byte[] { 0, 0, 0, 17, 82,
-            69, 83, 80, 79, 78, 83, 69, 95, 69, 88, 80, 69, 67, 84, 69, 68, 0,
-            0, 0, 1, 0, 0, 0, 8, 0, 0, 0, 27, 114, 101, 99, 101, 105, 118, 101,
-            114, 99, 111, 109, 46, 114, 101, 100, 104, 97, 116, 46, 102, 111,
-            111, 46, 98, 97, 114, 46, 82, 101, 99, 101, 105, 118, 101, 114 };
-
-    @Test
-    public void testEncodingRequestToArray() {
-        Request request = new Request(RequestType.RESPONSE_EXPECTED, new InetSocketAddress(20));
-        request.setParameter("receiver", "com.redhat.foo.bar.Receiver");
-        RequestEncoder encoder = new RequestEncoder();
-        ByteBuf buf = encoder.encode(request);
-        byte[] array = Unpooled.copiedBuffer(buf).array();
-        assertTrue(Arrays.equals(REQUEST_BYTES, array));
-    }
-    
-    @Test
-    public void canEncodeSimpleRequestWithNoParams() throws Exception {
-        RequestEncoder encoder = new RequestEncoder();
-        String responseExp = "RESPONSE_EXPECTED";
-        ByteBuf stringBuf = Unpooled.copiedBuffer(responseExp, Charset.defaultCharset());
-        ByteBuf buf = Unpooled.buffer(4);
-        buf.writeInt(responseExp.getBytes().length);
-        ByteBuf buf2 = Unpooled.wrappedBuffer(buf, stringBuf);
-        buf = Unpooled.buffer(4);
-        buf.writeInt(0);
-        ByteBuf expected = Unpooled.wrappedBuffer(buf2, buf);
-        InetSocketAddress addr = new InetSocketAddress("testhost", 12);
-        Request item = new Request(RequestType.RESPONSE_EXPECTED, addr);
-        ByteBuf actual = encoder.encode(item);
-        if (DEBUG) {
-            printBuffers(actual, expected);
-        }
-        assertEquals(0, ByteBufUtil.compare(expected, actual));
-    }
-    
-    @Test
-    public void canEncodeRequestWithParams() throws Exception {
-        InetSocketAddress addr = new InetSocketAddress(1234);
-
-        // Prepare request we'd like to encode
-        Request item = new Request(RequestType.RESPONSE_EXPECTED, addr);
-        String param1Name = "param1";
-        String param1Value = "value1";
-        String param2Name = "param2";
-        String param2Value = "value2";
-        item.setParameter(param1Name, param1Value);
-        item.setParameter(param2Name, param2Value);
-        RequestEncoder encoder = new RequestEncoder();
-        
-        // build expected
-        String responseExp = "RESPONSE_EXPECTED";
-        ByteBuf stringBuf = Unpooled.copiedBuffer(responseExp, Charset.defaultCharset());
-        ByteBuf buf = Unpooled.buffer(4);
-        buf.writeInt(responseExp.getBytes().length);
-        ByteBuf buf2 = Unpooled.wrappedBuffer(buf, stringBuf);
-        buf = Unpooled.buffer(4);
-        buf.writeInt(2);
-        ByteBuf request = Unpooled.wrappedBuffer(buf2, buf);
-        ByteBuf nameLen = Unpooled.buffer(4);
-        nameLen.writeInt(param1Name.getBytes().length);
-        ByteBuf valueLen = Unpooled.buffer(4);
-        valueLen.writeInt(param1Value.getBytes().length);
-        ByteBuf lens = Unpooled.wrappedBuffer(nameLen, valueLen);
-        ByteBuf nameBuf = Unpooled.copiedBuffer(param1Name, Charset.defaultCharset());
-        ByteBuf valueBuf = Unpooled.copiedBuffer(param1Value, Charset.defaultCharset());
-        ByteBuf payload = Unpooled.wrappedBuffer(nameBuf, valueBuf);
-        ByteBuf param1Buf = Unpooled.wrappedBuffer(lens, payload);
-        nameLen = Unpooled.buffer(4);
-        nameLen.writeInt(param2Name.getBytes().length);
-        valueLen = Unpooled.buffer(4);
-        valueLen.writeInt(param2Value.getBytes().length);
-        lens = Unpooled.wrappedBuffer(nameLen, valueLen);
-        nameBuf = Unpooled.copiedBuffer(param2Name, Charset.defaultCharset());
-        valueBuf = Unpooled.copiedBuffer(param2Value, Charset.defaultCharset());
-        payload = Unpooled.wrappedBuffer(nameBuf, valueBuf);
-        ByteBuf param2Buf = Unpooled.wrappedBuffer(lens, payload);
-        ByteBuf params = Unpooled.wrappedBuffer(param1Buf, param2Buf);
-        ByteBuf expected = Unpooled.wrappedBuffer(request, params);
-        
-        // Encode item for actual
-        ByteBuf actual = encoder.encode(item);
-        if (DEBUG) {
-            printBuffers(actual, expected);
-        }
-        assertEquals(0, ByteBufUtil.compare(expected, actual));
-    }
-
-    private void printBuffers(ByteBuf actual, ByteBuf expected) {
-        System.out.println("hexdump expected\n-------------------------------------");
-        System.out.println(ByteBufUtil.hexDump(expected));
-        System.out.println("\nhexdump actual\n-------------------------------------");
-        System.out.println(ByteBufUtil.hexDump(actual) + "\n\n");
-    }
-}
-
--- a/common/pom.xml	Tue Jun 13 12:02:12 2017 +0200
+++ b/common/pom.xml	Mon Jun 12 20:50:03 2017 +0200
@@ -66,7 +66,6 @@
 
   <modules>
     <module>core</module>
-    <module>command</module>
     <module>portability</module>
     <module>test</module>
   </modules>
--- a/distribution/assembly/core-assembly.xml	Tue Jun 13 12:02:12 2017 +0200
+++ b/distribution/assembly/core-assembly.xml	Mon Jun 12 20:50:03 2017 +0200
@@ -53,22 +53,18 @@
         <include>com.redhat.thermostat:thermostat-launcher</include>
         <include>com.redhat.thermostat:thermostat-agent-core</include>
         <include>com.redhat.thermostat:thermostat-agent-cli</include>
-        <include>com.redhat.thermostat:thermostat-agent-command</include>
-        <include>com.redhat.thermostat:thermostat-agent-command-server</include>
         <include>com.redhat.thermostat:thermostat-agent-proxy-server</include>
         <include>com.redhat.thermostat:thermostat-agent-ipc-tcpsocket-server</include>
         <include>com.redhat.thermostat:thermostat-agent-ipc-tcpsocket-client</include>
         <include>com.redhat.thermostat:thermostat-agent-ipc-unixsocket-server</include>
         <include>com.redhat.thermostat:thermostat-agent-ipc-unixsocket-client</include>
         <include>com.redhat.thermostat:thermostat-common-core</include>
-        <include>com.redhat.thermostat:thermostat-common-command</include>
         <include>com.redhat.thermostat:thermostat-common-portability</include>
         <include>com.redhat.thermostat:thermostat-process-handler</include>
         <include>com.redhat.thermostat:thermostat-storage-core</include>
         <include>com.redhat.thermostat:thermostat-system-backend</include>
         <include>org.osgi:org.osgi.compendium</include>
         <include>org.apache:org.apache.felix.scr</include>
-        <include>io.netty:netty-handler</include>
         <include>commons-codec:commons-codec</include>
         <include>org.apache.httpcomponents:httpclient-osgi</include>
         <include>org.eclipse.jetty:jetty-client</include>
--- a/distribution/config/agent.properties	Tue Jun 13 12:02:12 2017 +0200
+++ b/distribution/config/agent.properties	Mon Jun 12 20:50:03 2017 +0200
@@ -2,22 +2,6 @@
 # or rather will purge the db
 SAVE_ON_EXIT=true
 
-# A netty-based side channel for accepting configuration/tuning
-# requests from the client will listen for connections on the address
-# configured here.
-# If this is removed or commented out, the default port is 127.0.0.1:12000
-# If this includes an IPv6 address, enclose it in [ and ] like:
-# [1fff:0:a88:85a3::ac1f]:12000
-CONFIG_LISTEN_ADDRESS=127.0.0.1:12000
-
-# Related to CONFIG_LISTEN_ADDRESS. CONFIG_PUBLISH_ADDRESS will be used
-# as an address advertised to clients to reach the agent. If not specified
-# defaults to CONFIG_LISTEN_ADDRESS. This config value is useful if the
-# listen address of the agent is some local-only resolvable address.
-# In that case, specify the globally reachable address of the agent via
-# CONFIG_PUBLISH_ADDRESS. See CONFIG_LISTEN_ADDRESS for supported formats.
-#CONFIG_PUBLISH_ADDRESS=agent.example.com:12000
-
 # Connection URL to storage. This can be overridden with the -d option
 # on the command line.
 DB_URL=http://127.0.0.1:8999/thermostat/storage
--- a/distribution/pom.xml	Tue Jun 13 12:02:12 2017 +0200
+++ b/distribution/pom.xml	Mon Jun 12 20:50:03 2017 +0200
@@ -423,16 +423,6 @@
     </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-agent-command-server</artifactId>
-      <version>${project.version}</version>
-    </dependency>
-    <dependency>
-      <groupId>com.redhat.thermostat</groupId>
       <artifactId>thermostat-agent-proxy-server</artifactId>
       <version>${project.version}</version>
     </dependency>
@@ -463,11 +453,6 @@
     </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-common-portability</artifactId>
       <version>${project.version}</version>
     </dependency>
--- a/plugins/killvm/agent/pom.xml	Tue Jun 13 12:02:12 2017 +0200
+++ b/plugins/killvm/agent/pom.xml	Mon Jun 12 20:50:03 2017 +0200
@@ -84,11 +84,6 @@
     </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>
@@ -103,9 +98,19 @@
       <version>${project.version}</version>
     </dependency>
     <dependency>
+      <groupId>com.redhat.thermostat</groupId>
+      <artifactId>thermostat-commands-agent</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
       <groupId>org.osgi</groupId>
       <artifactId>org.osgi.core</artifactId>
     </dependency>
+    <!-- declarative services -->
+    <dependency>
+      <groupId>org.apache.felix</groupId>
+      <artifactId>org.apache.felix.scr.annotations</artifactId>
+    </dependency>
   </dependencies>
 
   <build>
@@ -117,7 +122,6 @@
         <configuration>
           <instructions>
             <Bundle-Vendor>Red Hat, Inc.</Bundle-Vendor>
-            <Bundle-Activator>com.redhat.thermostat.killvm.agent.internal.Activator</Bundle-Activator>
             <Bundle-SymbolicName>com.redhat.thermostat.killvm.agent</Bundle-SymbolicName>
             <Private-Package>com.redhat.thermostat.killvm.agent.internal</Private-Package>
             <!-- Do not autogenerate uses clauses in Manifests -->
@@ -125,6 +129,18 @@
           </instructions>
         </configuration>
       </plugin>
+      <plugin>
+        <groupId>org.apache.felix</groupId>
+        <artifactId>maven-scr-plugin</artifactId>
+        <executions>
+          <execution>
+            <id>generate-scr-scrdescriptor</id>
+            <goals>
+              <goal>scr</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
     </plugins>
   </build>
 
--- a/plugins/killvm/agent/src/main/java/com/redhat/thermostat/killvm/agent/internal/Activator.java	Tue Jun 13 12:02:12 2017 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,79 +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
- * <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.killvm.agent.internal;
-
-import com.redhat.thermostat.service.process.ProcessHandler;
-import org.osgi.framework.BundleActivator;
-import org.osgi.framework.BundleContext;
-import org.osgi.framework.ServiceReference;
-import org.osgi.util.tracker.ServiceTracker;
-
-import com.redhat.thermostat.agent.command.ReceiverRegistry;
-
-public class Activator implements BundleActivator {
-
-    private ReceiverRegistry registry;
-    private ServiceTracker killActionTracker;
-
-    @Override
-    public void start(final BundleContext context) {
-        registry = new ReceiverRegistry(context);
-
-        killActionTracker = new ServiceTracker(context, ProcessHandler.class, null) {
-            @Override
-            public Object addingService(ServiceReference reference) {
-                ProcessHandler processHandler = (ProcessHandler) super.addingService(reference);
-                registry.registerReceiver(new KillVmReceiver(processHandler));
-                return processHandler;
-            }
-
-            @Override
-            public void removedService(ServiceReference reference, Object service) {
-                registry.unregisterReceivers();
-                super.removedService(reference, service);
-            }
-        };
-
-        killActionTracker.open();
-    }
-
-    @Override
-    public void stop(BundleContext context) {
-        killActionTracker.close();
-    }
-}
-
--- a/plugins/killvm/agent/src/main/java/com/redhat/thermostat/killvm/agent/internal/KillVmReceiver.java	Tue Jun 13 12:02:12 2017 +0200
+++ b/plugins/killvm/agent/src/main/java/com/redhat/thermostat/killvm/agent/internal/KillVmReceiver.java	Mon Jun 12 20:50:03 2017 +0200
@@ -39,41 +39,51 @@
 import java.util.logging.Level;
 import java.util.logging.Logger;
 
-import com.redhat.thermostat.agent.command.RequestReceiver;
-import com.redhat.thermostat.common.command.Request;
-import com.redhat.thermostat.common.command.Response;
-import com.redhat.thermostat.common.command.Response.ResponseType;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Property;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.Service;
+
+import com.redhat.thermostat.commands.agent.receiver.RequestReceiver;
+import com.redhat.thermostat.commands.model.AgentRequest;
+import com.redhat.thermostat.commands.model.WebSocketResponse;
+import com.redhat.thermostat.commands.model.WebSocketResponse.ResponseType;
 import com.redhat.thermostat.common.utils.LoggingUtils;
 import com.redhat.thermostat.service.process.ProcessHandler;
 import com.redhat.thermostat.service.process.UNIXSignal;
 
+@Component
+@Service(value = RequestReceiver.class)
+@Property(name = "servicename", value = "com.redhat.thermostat.killvm.agent.internal.KillVmReceiver")
 public class KillVmReceiver implements RequestReceiver {
 
-    private final ProcessHandler processService;
     private static final Logger log = LoggingUtils.getLogger(KillVmReceiver.class);
     
-    public KillVmReceiver(ProcessHandler theService) {
-        this.processService = theService;
-    }
+    @Reference
+    private ProcessHandler processService;
     
     @Override
-    public Response receive(Request request) {
+    public WebSocketResponse receive(AgentRequest request) {
         if (processService == null) {
             // no dice, should have service by now
             log.severe("Process service is null!");
-            return new Response(ResponseType.ERROR);
+            return new WebSocketResponse(request.getSequenceId(), ResponseType.ERROR);
         }
-        String strPid = request.getParameter("vm-pid");
+        String strPid = request.getParam("vm-pid");
         try {
             Integer pid = Integer.parseInt(strPid);
             processService.sendSignal(pid, UNIXSignal.TERM);
             log.fine("Killed VM with PID " + pid);
-            return new Response(ResponseType.OK);
+            return new WebSocketResponse(request.getSequenceId(), ResponseType.OK);
         } catch (NumberFormatException e) {
             log.log(Level.WARNING, "Invalid PID argument", e);
-            return new Response(ResponseType.ERROR);
+            return new WebSocketResponse(request.getSequenceId(), ResponseType.ERROR);
         }
     }
+    
+    protected void bindProcessService(ProcessHandler handler) {
+        this.processService = handler;
+    }
 
 }
 
--- a/plugins/killvm/agent/src/test/java/com/redhat/thermostat/killvm/agent/internal/ActivatorTest.java	Tue Jun 13 12:02:12 2017 +0200
+++ /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
- * <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.killvm.agent.internal;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.Mockito.mock;
-
-import org.junit.Test;
-
-import com.redhat.thermostat.agent.command.RequestReceiver;
-import com.redhat.thermostat.service.process.ProcessHandler;
-import com.redhat.thermostat.testutils.StubBundleContext;
-
-public class ActivatorTest {
-
-    /**
-     * Makes sure receiver is registered and unix service gets set.
-     */
-    @Test
-    public void verifyKillReciverIsNotRegisteredWithoutDependencies() {
-        StubBundleContext ctx = new StubBundleContext();
-        Activator activator = new Activator();
-        activator.start(ctx);
-
-        assertEquals(0, ctx.getAllServices().size());
-
-        activator.stop(ctx);
-    }
-
-    @Test
-    public void verifyKillReciverIsRegistered() {
-        StubBundleContext ctx = new StubBundleContext();
-
-        ctx.registerService(ProcessHandler.class, mock(ProcessHandler.class), null);
-
-        Activator activator = new Activator();
-        activator.start(ctx);
-
-        assertEquals(2, ctx.getAllServices().size());
-        assertTrue(ctx.isServiceRegistered(RequestReceiver.class.getName(), KillVmReceiver.class));
-
-        activator.stop(ctx);
-
-        assertEquals(1, ctx.getAllServices().size());
-    }
-}
-
--- a/plugins/killvm/agent/src/test/java/com/redhat/thermostat/killvm/agent/internal/KillVmReceiverTest.java	Tue Jun 13 12:02:12 2017 +0200
+++ b/plugins/killvm/agent/src/test/java/com/redhat/thermostat/killvm/agent/internal/KillVmReceiverTest.java	Mon Jun 12 20:50:03 2017 +0200
@@ -39,60 +39,71 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.fail;
 import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
 
-import java.lang.reflect.Constructor;
 import java.lang.reflect.Method;
+import java.util.SortedMap;
+import java.util.TreeMap;
 
-import com.redhat.thermostat.service.process.ProcessHandler;
 import org.junit.Test;
 
-import com.redhat.thermostat.common.command.Request;
-import com.redhat.thermostat.common.command.Response;
-import com.redhat.thermostat.common.command.Response.ResponseType;
+import com.redhat.thermostat.commands.model.AgentRequest;
+import com.redhat.thermostat.commands.model.WebSocketResponse;
+import com.redhat.thermostat.commands.model.WebSocketResponse.ResponseType;
+import com.redhat.thermostat.service.process.ProcessHandler;
 
 public class KillVmReceiverTest {
 
     @Test
     public void receiverReturnsOk() {
         ProcessHandler proc = mock(ProcessHandler.class);
-        KillVmReceiver receiver = new KillVmReceiver(proc);
-        Request req = mock(Request.class);
-        when(req.getParameter("vm-pid")).thenReturn("12345");
-        Response response = receiver.receive(req);
-        assertEquals(ResponseType.OK, response.getType());
+        KillVmReceiver receiver = new KillVmReceiver();
+        receiver.bindProcessService(proc);
+        SortedMap<String, String> params = new TreeMap<>();
+        params.put("vm-pid", "12345");
+        AgentRequest req = new AgentRequest(322, params);
+        WebSocketResponse response = receiver.receive(req);
+        assertEquals(ResponseType.OK, response.getResponseType());
+        assertEquals(322, response.getSequenceId());
     }
     
     @Test
     public void receiverReturnsErrorNoPid() {
         ProcessHandler proc = mock(ProcessHandler.class);
-        KillVmReceiver receiver = new KillVmReceiver(proc);
-        Request req = mock(Request.class);
-        Response response = receiver.receive(req);
-        assertEquals(ResponseType.ERROR, response.getType());
+        KillVmReceiver receiver = new KillVmReceiver();
+        receiver.bindProcessService(proc);
+        SortedMap<String, String> params = new TreeMap<>();
+        AgentRequest req = new AgentRequest(-1, params);
+        WebSocketResponse response = receiver.receive(req);
+        assertEquals(ResponseType.ERROR, response.getResponseType());
+        assertEquals(-1, response.getSequenceId());
     }
     
     @Test
     public void receiverReturnsErrorBadPid() {
         ProcessHandler proc = mock(ProcessHandler.class);
-        KillVmReceiver receiver = new KillVmReceiver(proc);
-        Request req = mock(Request.class);
-        when(req.getParameter("vm-pid")).thenReturn("hi");
-        Response response = receiver.receive(req);
-        assertEquals(ResponseType.ERROR, response.getType());
+        KillVmReceiver receiver = new KillVmReceiver();
+        receiver.bindProcessService(proc);
+        SortedMap<String, String> params = new TreeMap<>();
+        params.put("vm-pid", "hi");
+        AgentRequest req = new AgentRequest(211, params);
+        WebSocketResponse response = receiver.receive(req);
+        assertEquals(ResponseType.ERROR, response.getResponseType());
+        assertEquals(211, response.getSequenceId());
     }
 
     @Test
     public void receiverReturnsErrorNoProcessHandler() {
-        KillVmReceiver receiver = new KillVmReceiver(null);
-        Request req = mock(Request.class);
-        Response response = receiver.receive(req);
-        assertEquals(ResponseType.ERROR, response.getType());
+        KillVmReceiver receiver = new KillVmReceiver();
+        SortedMap<String, String> params = new TreeMap<>();
+        AgentRequest req = new AgentRequest(11, params);
+        WebSocketResponse response = receiver.receive(req);
+        assertEquals(ResponseType.ERROR, response.getResponseType());
+        assertEquals(11, response.getSequenceId());
     }
 
     /**
      * When a request is issued the fully qualified receiver class name is set
-     * via {@link Request#setReceiver(String)}. This test makes sure that this
+     * via the 'receiver' param name. This test makes sure that this
      * class is actually where it's supposed to be.
      * 
      * @throws Exception
@@ -109,11 +120,14 @@
             fail("com.redhat.thermostat.agent.killvm.internal.KillVmReceiver class not found, but used by some request!");
         }
         try {
-            Constructor<?> constructor = receiver.getConstructor(ProcessHandler.class);
             ProcessHandler service = mock(ProcessHandler.class);
-            Object instance = constructor.newInstance(service);
-            Method m = receiver.getMethod("receive", Request.class);
-            Request req = mock(Request.class);
+            Object instance = receiver.newInstance();
+            Method bind = receiver.getDeclaredMethod("bindProcessService", ProcessHandler.class);
+            bind.invoke(instance, service);
+            Method m = receiver.getMethod("receive", AgentRequest.class);
+            SortedMap<String, String> params = new TreeMap<>();
+            params.put("vm-pid", "12345");
+            AgentRequest req = new AgentRequest(322, params);
             m.invoke(instance, req);
         } catch (Exception e) {
             e.printStackTrace();
--- a/plugins/vm-gc/remote-collector-command/pom.xml	Tue Jun 13 12:02:12 2017 +0200
+++ b/plugins/vm-gc/remote-collector-command/pom.xml	Mon Jun 12 20:50:03 2017 +0200
@@ -90,8 +90,13 @@
     </dependency>
     <dependency>
       <groupId>com.redhat.thermostat</groupId>
-      <artifactId>thermostat-agent-command</artifactId>
-      <version>${project.version}</version>
+      <artifactId>thermostat-commands-agent</artifactId>
+      <version>${project.version}</version>      
+    </dependency>
+    <!-- declarative services -->
+    <dependency>
+      <groupId>org.apache.felix</groupId>
+      <artifactId>org.apache.felix.scr.annotations</artifactId>
     </dependency>
     <dependency>
       <groupId>com.redhat.thermostat</groupId>
@@ -112,7 +117,6 @@
           <instructions>
             <Bundle-Vendor>Red Hat, Inc.</Bundle-Vendor>
             <Bundle-SymbolicName>com.redhat.thermostat.gc.remote.command</Bundle-SymbolicName>
-            <Bundle-Activator>com.redhat.thermostat.gc.remote.command.internal.Activator</Bundle-Activator>
             <Export-Package>
               com.redhat.thermostat.gc.remote.command,
             </Export-Package>
@@ -124,6 +128,18 @@
           </instructions>
         </configuration>
       </plugin>
+      <plugin>
+        <groupId>org.apache.felix</groupId>
+        <artifactId>maven-scr-plugin</artifactId>
+        <executions>
+          <execution>
+            <id>generate-scr-scrdescriptor</id>
+            <goals>
+              <goal>scr</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
     </plugins>
   </build>
 
--- a/plugins/vm-gc/remote-collector-command/src/main/java/com/redhat/thermostat/gc/remote/command/GCRequestReceiver.java	Tue Jun 13 12:02:12 2017 +0200
+++ b/plugins/vm-gc/remote-collector-command/src/main/java/com/redhat/thermostat/gc/remote/command/GCRequestReceiver.java	Mon Jun 12 20:50:03 2017 +0200
@@ -39,41 +39,47 @@
 import java.util.logging.Level;
 import java.util.logging.Logger;
 
-import com.redhat.thermostat.agent.command.RequestReceiver;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Property;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.Service;
+
 import com.redhat.thermostat.agent.utils.management.MXBeanConnectionPool;
-import com.redhat.thermostat.common.command.Request;
-import com.redhat.thermostat.common.command.Response;
-import com.redhat.thermostat.common.command.Response.ResponseType;
+import com.redhat.thermostat.commands.agent.receiver.RequestReceiver;
+import com.redhat.thermostat.commands.model.AgentRequest;
+import com.redhat.thermostat.commands.model.WebSocketResponse;
+import com.redhat.thermostat.commands.model.WebSocketResponse.ResponseType;
 import com.redhat.thermostat.common.utils.LoggingUtils;
 import com.redhat.thermostat.gc.remote.command.internal.GC;
 import com.redhat.thermostat.gc.remote.command.internal.GCException;
 import com.redhat.thermostat.gc.remote.common.command.GCAction;
 
+@Component
+@Service(value = RequestReceiver.class)
+@Property(name = "servicename", value = "com.redhat.thermostat.gc.remote.command.GCRequestReceiver")
 public class GCRequestReceiver implements RequestReceiver {
 
     private static final Logger logger = LoggingUtils.getLogger(GCRequestReceiver.class);
+    
+    @Reference
     private MXBeanConnectionPool pool;
 
-    public GCRequestReceiver(MXBeanConnectionPool pool) {
-        this.pool = pool;
-    }
-
     @Override
-    public Response receive(Request request) {
-        Response response = new Response(ResponseType.OK);
+    public WebSocketResponse receive(AgentRequest request) {
+        WebSocketResponse response = new WebSocketResponse(request.getSequenceId(), ResponseType.OK);
         
-        String command = request.getParameter(GCAction.class.getName());
+        String command = request.getParam(GCAction.class.getName());
         switch (GCAction.valueOf(command)) {
         case REQUEST_GC:
-            String strPid = request.getParameter(GCAction.VM_PID);
+            String strPid = request.getParam(GCAction.VM_PID);
             try {
                 int vmId = Integer.parseInt(strPid);
                 new GC(pool, vmId).gc();
             } catch (GCException gce) {
-                response = new Response(ResponseType.ERROR);
+                response = new WebSocketResponse(request.getSequenceId(), ResponseType.ERROR);
                 logger.log(Level.WARNING, "GC request failed", gce);
             } catch (NumberFormatException e) {
-                response = new Response(ResponseType.ERROR);
+                response = new WebSocketResponse(request.getSequenceId(), ResponseType.ERROR);
                 logger.log(Level.WARNING, "Invalid PID: " + strPid, e);
             }
             break;
--- a/plugins/vm-gc/remote-collector-command/src/main/java/com/redhat/thermostat/gc/remote/command/internal/Activator.java	Tue Jun 13 12:02:12 2017 +0200
+++ /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
- * <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.gc.remote.command.internal;
-
-import org.osgi.framework.BundleActivator;
-import org.osgi.framework.BundleContext;
-import org.osgi.framework.ServiceReference;
-import org.osgi.util.tracker.ServiceTracker;
-
-import com.redhat.thermostat.agent.command.ReceiverRegistry;
-import com.redhat.thermostat.agent.utils.management.MXBeanConnectionPool;
-import com.redhat.thermostat.gc.remote.command.GCRequestReceiver;
-
-public class Activator implements BundleActivator {
-
-    private ServiceTracker tracker;
-
-    @Override
-    public void start(BundleContext context) throws Exception {
-        final ReceiverRegistry registry = new ReceiverRegistry(context);
-
-        tracker = new ServiceTracker(context, MXBeanConnectionPool.class, null) {
-            @Override
-            public MXBeanConnectionPool addingService(ServiceReference reference) {
-                MXBeanConnectionPool pool = (MXBeanConnectionPool) super.addingService(reference);
-                registry.registerReceiver(new GCRequestReceiver(pool));
-                return pool;
-            };
-
-            @Override
-            public void removedService(ServiceReference reference, Object service) {
-                registry.unregisterReceivers();
-                super.removedService(reference, service);
-            };
-        };
-        tracker.open();
-    }
-
-    @Override
-    public void stop(BundleContext context) throws Exception {
-        tracker.close();
-    }
-}
-
--- a/plugins/vm-gc/remote-collector-command/src/test/java/com/redhat/thermostat/gc/remote/command/internal/ActivatorTest.java	Tue Jun 13 12:02:12 2017 +0200
+++ /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
- * <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.gc.remote.command.internal;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.Mockito.mock;
-
-import org.junit.Before;
-import org.junit.Test;
-
-import com.redhat.thermostat.agent.command.RequestReceiver;
-import com.redhat.thermostat.agent.utils.management.MXBeanConnectionPool;
-import com.redhat.thermostat.gc.remote.command.GCRequestReceiver;
-import com.redhat.thermostat.testutils.StubBundleContext;
-
-public class ActivatorTest {
-
-    private StubBundleContext context;
-    private Activator activator;
-
-    @Before
-    public void setup() {
-        context = new StubBundleContext();
-        activator = new Activator();
-    }
-
-    @Test
-    public void verifyReceiverRegistered() throws Exception {
-        MXBeanConnectionPool mxBeanConnectionPool = mock(MXBeanConnectionPool.class);
-        context.registerService(MXBeanConnectionPool.class, mxBeanConnectionPool, null);
-
-        activator.start(context);
-
-        assertEquals(2, context.getAllServices().size());
-        assertTrue(context.isServiceRegistered(RequestReceiver.class.getName(), GCRequestReceiver.class));
-
-        activator.stop(context);
-    }
-
-    @Test
-    public void verifyActivatorDoesNotRegisterServiceOnMissingDeps() throws Exception {
-        activator.start(context);
-
-        assertEquals(0, context.getAllServices().size());
-
-        activator.stop(context);
-    }
-}
--- a/pom.xml	Tue Jun 13 12:02:12 2017 +0200
+++ b/pom.xml	Mon Jun 12 20:50:03 2017 +0200
@@ -303,7 +303,6 @@
     <felix.scr.annotations.version>1.9.12</felix.scr.annotations.version>
     <kxml2.version>2.3.0</kxml2.version>
 
-    <netty.version>4.0.42.Final</netty.version>
     <httpcomponents.core.version>4.3.2</httpcomponents.core.version>
     <httpcomponents.client.version>4.3.4</httpcomponents.client.version>
     <gson.version>2.2.2</gson.version>
@@ -621,16 +620,6 @@
         <artifactId>expectj</artifactId>
         <version>${expectj.version}</version>
       </dependency>
-      <dependency>
-        <groupId>io.netty</groupId>
-        <artifactId>netty-handler</artifactId>
-        <version>${netty.version}</version>
-      </dependency>
-      <dependency>
-        <groupId>io.netty</groupId>
-        <artifactId>netty-buffer</artifactId>
-        <version>${netty.version}</version>
-      </dependency>
 
       <!-- Note that jfreechart pulls in jcommon as a dep.
            The jcommon jar is used explicitly in the main