changeset 2726:2b8ca1e3186d

Improve handling of microservice URLs Reviewed-by: jerboaa Review-thread: http://icedtea.classpath.org/pipermail/thermostat/2017-July/024234.html
author Elliott Baron <ebaron@redhat.com>
date Mon, 24 Jul 2017 12:29:44 -0400
parents 4f2764035d3b
children 499059dee241
files agent/core/src/main/java/com/redhat/thermostat/agent/http/HttpRequestService.java agent/core/src/test/java/com/redhat/thermostat/agent/http/HttpRequestServiceTest.java common/plugin/src/main/java/com/redhat/thermostat/common/plugin/PluginConfiguration.java common/plugin/src/main/java/com/redhat/thermostat/common/plugin/PluginDAOBase.java common/plugin/src/test/java/com/redhat/thermostat/common/plugin/PluginConfigurationTest.java plugins/commands/agent/src/main/java/com/redhat/thermostat/commands/agent/internal/CommandsBackend.java plugins/commands/agent/src/test/java/com/redhat/thermostat/commands/agent/internal/CommandsBackendTest.java plugins/host-cpu/agent/src/main/java/com/redhat/thermostat/host/cpu/agent/internal/CpuStatDAOImpl.java plugins/host-cpu/agent/src/test/java/com/redhat/thermostat/host/cpu/agent/internal/CpuStatDAOTest.java plugins/host-memory/agent/src/main/java/com/redhat/thermostat/host/memory/agent/internal/MemoryStatDAOImpl.java plugins/host-memory/agent/src/test/java/com/redhat/thermostat/host/memory/agent/internal/MemoryStatDAOTest.java plugins/host-network/agent/src/main/java/com/redhat/thermostat/host/network/internal/NetworkInfoListDAOImpl.java plugins/host-network/agent/src/test/java/com/redhat/thermostat/host/network/internal/NetworkInfoListDAOTest.java plugins/host-overview/agent/src/main/java/com/redhat/thermostat/host/overview/internal/models/HostInfoDAOImpl.java plugins/host-overview/agent/src/test/java/com/redhat/thermostat/host/overview/internal/models/HostInfoDAOImplTest.java plugins/jvm-overview/agent/src/main/java/com/redhat/thermostat/jvm/overview/agent/internal/model/VmInfoDAOImpl.java plugins/jvm-overview/agent/src/test/java/com/redhat/thermostat/jvm/overview/agent/internal/model/VmInfoDAOImplTest.java plugins/vm-gc/agent/src/main/java/com/redhat/thermostat/vm/gc/agent/internal/models/VmGcStatDAOImpl.java plugins/vm-gc/agent/src/test/java/com/redhat/thermostat/vm/gc/agent/internal/models/VmGcStatDAOImplTest.java plugins/vm-memory/agent/src/main/java/com/redhat/thermostat/vm/memory/agent/internal/models/VmMemoryStatDAOImpl.java plugins/vm-memory/agent/src/main/java/com/redhat/thermostat/vm/memory/agent/internal/models/VmTlabStatDAOImpl.java plugins/vm-memory/agent/src/test/java/com/redhat/thermostat/vm/memory/agent/internal/models/VmMemoryStatDAOImplTest.java plugins/vm-memory/agent/src/test/java/com/redhat/thermostat/vm/memory/agent/internal/models/VmTlabStatDAOTest.java
diffstat 23 files changed, 208 insertions(+), 175 deletions(-) [+]
line wrap: on
line diff
--- a/agent/core/src/main/java/com/redhat/thermostat/agent/http/HttpRequestService.java	Wed Jul 19 14:42:37 2017 +0200
+++ b/agent/core/src/main/java/com/redhat/thermostat/agent/http/HttpRequestService.java	Mon Jul 24 12:29:44 2017 -0400
@@ -38,6 +38,7 @@
 
 
 import java.io.IOException;
+import java.net.URI;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.TimeoutException;
@@ -120,14 +121,16 @@
     /**
      * Send a HTTP request
      * @param jsonPayload The payload to send, or null if no payload
-     * @param url The complete url to send to
+     * @param uri The complete URI to send to
      * @param requestType The HTTP request type: GET, PUT, POST or DELETE
      * @return The returned body for GET requests. {@code null} otherwise.
      */
-    public String sendHttpRequest(String jsonPayload, String url, String requestType) throws RequestFailedException {
+    public String sendHttpRequest(String jsonPayload, URI uri, String requestType) throws RequestFailedException {
         // TODO: refactor agent pass around HttpMethod enum instead of string - it's faster and takes less space.
         HttpMethod requestMethod = HttpMethod.valueOf(requestType);
-        Request request = client.newRequest(url);
+        // Normalize URI to ensure any duplicate slashes are removed
+        uri = uri.normalize();
+        Request request = client.newRequest(uri);
         if (jsonPayload != null) {
             request.content(new StringContentProvider(jsonPayload), "application/json");
         }
--- a/agent/core/src/test/java/com/redhat/thermostat/agent/http/HttpRequestServiceTest.java	Wed Jul 19 14:42:37 2017 +0200
+++ b/agent/core/src/test/java/com/redhat/thermostat/agent/http/HttpRequestServiceTest.java	Mon Jul 24 12:29:44 2017 -0400
@@ -48,6 +48,7 @@
 import static org.mockito.Mockito.when;
 
 import java.io.IOException;
+import java.net.URI;
 import java.nio.ByteBuffer;
 import java.nio.charset.StandardCharsets;
 import java.util.concurrent.ExecutionException;
@@ -69,8 +70,8 @@
 
 public class HttpRequestServiceTest {
     private static final String POST_METHOD = HttpRequestService.POST;
-    private static final String URL = "http://127.0.0.1:30000/test";
-    private static final String GET_URL = URL + "?q=foo&l=3";
+    private static final URI GATEWAY_URI = URI.create("http://127.0.0.1:30000/test/");
+    private static final URI GET_URI = GATEWAY_URI.resolve("?q=foo&l=3");
     private static final String payload = "{}";
     private static final String keycloakUrl = "http://127.0.0.1:31000/keycloak";
 
@@ -82,7 +83,7 @@
     public void setup() throws InterruptedException, ExecutionException, TimeoutException {
         client = mock(HttpClientFacade.class);
         httpRequest = mock(Request.class);
-        when(client.newRequest(eq(URL))).thenReturn(httpRequest);
+        when(client.newRequest(eq(GATEWAY_URI))).thenReturn(httpRequest);
         ContentResponse response = mock(ContentResponse.class);
         when(response.getStatus()).thenReturn(HttpStatus.OK_200);
         when(httpRequest.send()).thenReturn(response);
@@ -96,7 +97,7 @@
 
         HttpRequestService service = createAndActivateRequestService(configuration);
 
-        service.sendHttpRequest(payload, URL, POST_METHOD);
+        service.sendHttpRequest(payload, GATEWAY_URI, POST_METHOD);
 
         verify(configuration).isKeycloakEnabled();
         verifyHttpPostRequest(httpRequest);
@@ -112,7 +113,7 @@
         Request keycloakRequest = mock(Request.class);
         setupKeycloakRequest(keycloakRequest);
 
-        service.sendHttpRequest(payload, URL, POST_METHOD);
+        service.sendHttpRequest(payload, GATEWAY_URI, POST_METHOD);
 
         verify(configuration).isKeycloakEnabled();
         verifyHttpPostRequest(httpRequest);
@@ -131,7 +132,7 @@
         Request keycloakRequest = mock(Request.class);
         setupKeycloakRequest(keycloakRequest);
 
-        service.sendHttpRequest(payload, URL, POST_METHOD);
+        service.sendHttpRequest(payload, GATEWAY_URI, POST_METHOD);
 
         verify(configuration).isKeycloakEnabled();
         verifyHttpPostRequest(httpRequest);
@@ -140,7 +141,7 @@
 
         verifyKeycloakAcquire(keycloakRequest);
 
-        service.sendHttpRequest(payload, URL, POST_METHOD);
+        service.sendHttpRequest(payload, GATEWAY_URI, POST_METHOD);
 
 
         ArgumentCaptor<StringContentProvider> payloadCaptor = ArgumentCaptor.forClass(StringContentProvider.class);
@@ -164,10 +165,10 @@
 
         HttpRequestService service = createAndActivateRequestService(configuration);
 
-        String response = service.sendHttpRequest(null, URL, POST_METHOD);
+        String response = service.sendHttpRequest(null, GATEWAY_URI, POST_METHOD);
         assertNull(response);
 
-        verify(client).newRequest(URL);
+        verify(client).newRequest(GATEWAY_URI);
         verify(configuration).isKeycloakEnabled();
 
         verify(httpRequest, times(0)).content(any(StringContentProvider.class), anyString());
@@ -185,31 +186,55 @@
     @Test
     public void testGetRequestWithResponse() throws Exception {
         String getContent = "foo bar";
+        HttpClientCreator creator = mock(HttpClientCreator.class);
+        HttpClientFacade getClient = setupHttpClient(creator, getContent);
+        
+        AgentStartupConfiguration configuration = createNoKeycloakConfig();
+        HttpRequestService service = new HttpRequestService(creator, configuration);
+        service.activate();
+        String content = service.sendHttpRequest(null, GET_URI, HttpRequestService.GET);
+        verify(getClient).newRequest(GET_URI);
+        assertEquals(getContent, content);
+    }
+    
+    @Test
+    public void testGetRequestNormalizesURI() throws Exception {
+        String getContent = "foo bar";
+        HttpClientCreator creator = mock(HttpClientCreator.class);
+        HttpClientFacade getClient = setupHttpClient(creator, getContent);
+        
+        AgentStartupConfiguration configuration = createNoKeycloakConfig();
+        HttpRequestService service = new HttpRequestService(creator, configuration);
+        service.activate();
+        
+        // Add extra slashes to URI
+        URI uri = URI.create("http://127.0.0.1:30000//test//?q=bar&l=5");
+        URI normalized = URI.create("http://127.0.0.1:30000/test/?q=bar&l=5");
+        String content = service.sendHttpRequest(null, uri, HttpRequestService.GET);
+        verify(getClient).newRequest(normalized);
+        assertEquals(getContent, content);
+    }
+
+    private HttpClientFacade setupHttpClient(HttpClientCreator creator, String getContent) throws Exception {
         Request request = mock(Request.class);
         ContentResponse contentResponse = mock(ContentResponse.class);
         when(contentResponse.getStatus()).thenReturn(HttpStatus.OK_200);
         when(contentResponse.getContentAsString()).thenReturn(getContent);
         when(request.send()).thenReturn(contentResponse);
         HttpClientFacade getClient = mock(HttpClientFacade.class);
-        when(getClient.newRequest(eq(GET_URL))).thenReturn(request);
-        HttpClientCreator creator = mock(HttpClientCreator.class);
+        when(getClient.newRequest(any(URI.class))).thenReturn(request);
         when(creator.create(any(SSLConfiguration.class))).thenReturn(getClient);
-        
-        AgentStartupConfiguration configuration = createNoKeycloakConfig();
-        HttpRequestService service = new HttpRequestService(creator, configuration);
-        service.activate();
-        String content = service.sendHttpRequest(null, GET_URL, HttpRequestService.GET);
-        assertEquals(getContent, content);
+        return getClient;
     }
     
     @Test(expected = RequestFailedException.class)
     public void failureThrowsRequestFailedException() throws Exception {
         Request request = mock(Request.class);
-        when(client.newRequest(any(String.class))).thenReturn(request);
+        when(client.newRequest(any(URI.class))).thenReturn(request);
         AgentStartupConfiguration configuration = createNoKeycloakConfig();
         doThrow(IOException.class).when(request).send();
         HttpRequestService service = createAndActivateRequestService(configuration);
-        service.sendHttpRequest("foo", "bar", HttpRequestService.DELETE /*any valid method*/);
+        service.sendHttpRequest("foo", GATEWAY_URI, HttpRequestService.DELETE /*any valid method*/);
     }
 
     private AgentStartupConfiguration createNoKeycloakConfig() {
@@ -219,7 +244,7 @@
     }
 
     private void verifyHttpPostRequest(Request httpRequest) throws InterruptedException, ExecutionException, TimeoutException {
-        verify(client).newRequest(URL);
+        verify(client).newRequest(GATEWAY_URI);
         verify(httpRequest).content(any(StringContentProvider.class), eq("application/json"));
         verify(httpRequest).method(eq(HttpMethod.valueOf(POST_METHOD)));
         verify(httpRequest).send();
--- a/common/plugin/src/main/java/com/redhat/thermostat/common/plugin/PluginConfiguration.java	Wed Jul 19 14:42:37 2017 +0200
+++ b/common/plugin/src/main/java/com/redhat/thermostat/common/plugin/PluginConfiguration.java	Mon Jul 24 12:29:44 2017 -0400
@@ -40,6 +40,8 @@
 
 import java.io.File;
 import java.io.IOException;
+import java.net.URI;
+import java.net.URISyntaxException;
 import java.util.Map;
 
 public class PluginConfiguration {
@@ -55,13 +57,32 @@
         this.pluginId = pluginId;
     }
 
-    public String getGatewayURL() throws IOException {
+    /**
+     * Returns the microservice URL defined by the "gatewayURL" property in the gateway.properties file
+     * for this plugin, expressed as a {@link URI}. If the provided URL does not end in a '/' character, 
+     * one is appended by this method.
+     * <p>
+     * Since the microservice path is already included, appending to this URI should be done using 
+     * the {@link URI#resolve(String)} method with a relative path.
+     * @return a URI to the web gateway microservice used by this plugin
+     * @throws IOException if the gatewayURL property is missing or invalid
+     */
+    public URI getGatewayURL() throws IOException {
         Map<String, String> props = source.getConfiguration(pluginId, CONFIG_FILE);
         String url = props.get(URL_PROP);
         if (url == null) {
             throw new IOException("No gateway URL found for " + pluginId + " in " + getConfigFilePath());
         }
-        return url;
+        try {
+            // Ensure the URI ends with a '/' so relative paths resolve under the microservice path segment
+            if (!url.endsWith("/")) {
+                url = url.concat("/");
+            }
+            URI gatewayURI = new URI(url);
+            return gatewayURI;
+        } catch (URISyntaxException e) {
+            throw new IOException("Invalid URL found for " + pluginId + ": " + url, e);
+        }
     }
 
     private String getConfigFilePath() {
--- a/common/plugin/src/main/java/com/redhat/thermostat/common/plugin/PluginDAOBase.java	Wed Jul 19 14:42:37 2017 +0200
+++ b/common/plugin/src/main/java/com/redhat/thermostat/common/plugin/PluginDAOBase.java	Mon Jul 24 12:29:44 2017 -0400
@@ -37,6 +37,7 @@
 package com.redhat.thermostat.common.plugin;
 
 import java.io.IOException;
+import java.net.URI;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 
@@ -48,7 +49,7 @@
     protected abstract String toJsonString(Tobj obj) throws IOException;
     protected abstract HttpRequestService getHttpRequestService();
     protected abstract PluginConfiguration getConfig();
-    protected abstract String getURL(final String basepath);
+    protected abstract URI getPostURI(final URI basepath);
     protected abstract Logger getLogger();
 
     public void put(final Tobj obj) {
@@ -56,9 +57,9 @@
             HttpRequestService httpRequestService = getHttpRequestService();
             String json = toJsonString(obj);
 
-            final String gatewayURL = getConfig().getGatewayURL();
-            final String url = getURL(gatewayURL);
-            httpRequestService.sendHttpRequest(json, url, HttpRequestService.POST);
+            final URI gatewayURI = getConfig().getGatewayURL();
+            final URI postURI = getPostURI(gatewayURI);
+            httpRequestService.sendHttpRequest(json, postURI, HttpRequestService.POST);
         } catch (IOException | RequestFailedException e) {
             getLogger().log(Level.WARNING, "Failed to send " + obj.getClass().getName() + " to web gateway", e);
         }
--- a/common/plugin/src/test/java/com/redhat/thermostat/common/plugin/PluginConfigurationTest.java	Wed Jul 19 14:42:37 2017 +0200
+++ b/common/plugin/src/test/java/com/redhat/thermostat/common/plugin/PluginConfigurationTest.java	Mon Jul 24 12:29:44 2017 -0400
@@ -41,6 +41,7 @@
 import static org.mockito.Mockito.when;
 
 import java.io.IOException;
+import java.net.URI;
 import java.util.HashMap;
 import java.util.Map;
 
@@ -58,11 +59,22 @@
     public void testGetGatewayURL() throws Exception {
         ConfigurationInfoSource source = mock(ConfigurationInfoSource.class);
         Map<String, String> props = new HashMap<>();
+        props.put(URL_PROP, "urlToGateway/");
+        when(source.getConfiguration(PLUGIN_ID, CONFIG_FILE)).thenReturn(props);
+        PluginConfiguration config = new PluginConfiguration(source, PLUGIN_ID);
+
+        assertEquals(URI.create("urlToGateway/"), config.getGatewayURL());
+    }
+    
+    @Test
+    public void testGetGatewayURLNoSlash() throws Exception {
+        ConfigurationInfoSource source = mock(ConfigurationInfoSource.class);
+        Map<String, String> props = new HashMap<>();
         props.put(URL_PROP, "urlToGateway");
         when(source.getConfiguration(PLUGIN_ID, CONFIG_FILE)).thenReturn(props);
         PluginConfiguration config = new PluginConfiguration(source, PLUGIN_ID);
 
-        assertEquals("urlToGateway", config.getGatewayURL());
+        assertEquals(URI.create("urlToGateway/"), config.getGatewayURL());
     }
 
     @Test(expected=IOException.class)
--- a/plugins/commands/agent/src/main/java/com/redhat/thermostat/commands/agent/internal/CommandsBackend.java	Wed Jul 19 14:42:37 2017 +0200
+++ b/plugins/commands/agent/src/main/java/com/redhat/thermostat/commands/agent/internal/CommandsBackend.java	Mon Jul 24 12:29:44 2017 -0400
@@ -38,7 +38,6 @@
 
 import java.io.IOException;
 import java.net.URI;
-import java.net.URISyntaxException;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 import java.util.logging.Level;
@@ -78,7 +77,7 @@
     private static final String DESCRIPTION = "Establishes web-socket connections to the microservice endpoint so as to be able to receive command requests";
     private static final String VENDOR = "Red Hat Inc.";
     private static final String PLUGIN_ID = "commands";
-    private static final String ENDPOINT_FORMAT = "%s/systems/%s/agents/%s";
+    private static final String ENDPOINT_FORMAT = "systems/%s/agents/%s";
     private static final String UNKNOWN_CREDS = "UNKNOWN:UNKNOWN";
 
     private final WsClientCreator wsClientCreator;
@@ -184,14 +183,15 @@
     private boolean connectWsClient() {
         boolean expired = false;
         try {
-            String microserviceURL = config.getGatewayURL();
+            URI microserviceURI = config.getGatewayURL();
             // String agent = agentId.getWriterID();
             // FIXME: Use reasonable agent/system name to register, not
             // hard-coded one
             // Unfortunately, the microservice is currently set up only for
             // a hard-coded one.
             String agent = "testAgent";
-            URI agentUri = new URI(String.format(ENDPOINT_FORMAT, microserviceURL, "ignoreMe", agent));
+            String cmdUriPath = String.format(ENDPOINT_FORMAT, "ignoreMe", agent);
+            URI agentUri = microserviceURI.resolve(cmdUriPath);
             AgentSocketOnMessageCallback onMsgCallback = new AgentSocketOnMessageCallback(receiverReg);
             CmdChannelAgentSocket agentSocket = new CmdChannelAgentSocket(
                     onMsgCallback, socketConnectLatch, agent);
@@ -201,7 +201,7 @@
             wsClient.connect(agentSocket, agentUri, agentRequest);
             logger.fine("WebSocket connect initiated.");
             expired = !socketConnectLatch.await(10, TimeUnit.SECONDS);
-        } catch (IOException | URISyntaxException | InterruptedException e) {
+        } catch (IOException | InterruptedException e) {
             logger.warning("Failed to connect to endpoint. Reason: " + e.getMessage());
             logger.log(Level.FINE, "Failed to connect to endpoint", e);
             return false;
--- a/plugins/commands/agent/src/test/java/com/redhat/thermostat/commands/agent/internal/CommandsBackendTest.java	Wed Jul 19 14:42:37 2017 +0200
+++ b/plugins/commands/agent/src/test/java/com/redhat/thermostat/commands/agent/internal/CommandsBackendTest.java	Mon Jul 24 12:29:44 2017 -0400
@@ -70,7 +70,7 @@
 
 public class CommandsBackendTest {
 
-    private static final String GW_URL = "ws://example.com/commands/v1";
+    private static final URI GW_URL = URI.create("ws://example.com/commands/v1/");
     private CommandsBackend backend;
     private StorageCredentials creds;
     private BundleContext bundleContext;
@@ -146,8 +146,8 @@
         verify(client).connect(any(CmdChannelAgentSocket.class), uriCaptor.capture(), reqCaptor.capture());
         assertTrue("Expected successful activation", success);
         URI uri = uriCaptor.getValue();
-        String expectedURI = GW_URL + "/systems/ignoreMe/agents/testAgent";
-        assertEquals(expectedURI, uri.toString());
+        URI expectedURI = GW_URL.resolve("systems/ignoreMe/agents/testAgent");
+        assertEquals(expectedURI, uri);
         ClientUpgradeRequest req = reqCaptor.getValue();
         String expectedHeader = base64EncodedHeader(username + ":" + password);
         String actualHeader = req.getHeader(HttpHeader.AUTHORIZATION.asString());
--- a/plugins/host-cpu/agent/src/main/java/com/redhat/thermostat/host/cpu/agent/internal/CpuStatDAOImpl.java	Wed Jul 19 14:42:37 2017 +0200
+++ b/plugins/host-cpu/agent/src/main/java/com/redhat/thermostat/host/cpu/agent/internal/CpuStatDAOImpl.java	Mon Jul 24 12:29:44 2017 -0400
@@ -37,24 +37,24 @@
 package com.redhat.thermostat.host.cpu.agent.internal;
 
 import java.io.IOException;
+import java.net.URI;
 import java.util.Arrays;
 import java.util.List;
 import java.util.logging.Logger;
 
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.Service;
+
 import com.redhat.thermostat.agent.http.HttpRequestService;
 import com.redhat.thermostat.common.config.experimental.ConfigurationInfoSource;
 import com.redhat.thermostat.common.plugin.PluginConfiguration;
+import com.redhat.thermostat.common.plugin.PluginDAOBase;
 import com.redhat.thermostat.common.plugin.SystemID;
 import com.redhat.thermostat.common.utils.LoggingUtils;
-import com.redhat.thermostat.common.plugin.PluginDAOBase;
 import com.redhat.thermostat.host.cpu.model.CpuStat;
 import com.redhat.thermostat.host.cpu.model.CpuStatTypeAdapter;
-import org.apache.felix.scr.annotations.Activate;
-import org.apache.felix.scr.annotations.Component;
-import org.apache.felix.scr.annotations.Reference;
-import org.apache.felix.scr.annotations.Service;
-import org.eclipse.jetty.client.HttpClient;
-import org.eclipse.jetty.client.util.StringContentProvider;
 
 @Component
 @Service(value = CpuStatDAO.class)
@@ -115,8 +115,8 @@
     }
 
     @Override
-    protected String getURL(String basepath) {
-        return basepath + "/systems/" + systemID.getSystemID();
+    protected URI getPostURI(URI basepath) {
+        return basepath.resolve("systems/" + systemID.getSystemID());
     }
 
     // DS bind method
--- a/plugins/host-cpu/agent/src/test/java/com/redhat/thermostat/host/cpu/agent/internal/CpuStatDAOTest.java	Wed Jul 19 14:42:37 2017 +0200
+++ b/plugins/host-cpu/agent/src/test/java/com/redhat/thermostat/host/cpu/agent/internal/CpuStatDAOTest.java	Mon Jul 24 12:29:44 2017 -0400
@@ -37,15 +37,12 @@
 package com.redhat.thermostat.host.cpu.agent.internal;
 
 import static org.mockito.Matchers.anyListOf;
-import static org.mockito.Matchers.anyString;
-import static org.mockito.Matchers.eq;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
-import java.util.HashMap;
-import java.util.Map;
+import java.net.URI;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -56,17 +53,16 @@
 import com.redhat.thermostat.common.config.experimental.ConfigurationInfoSource;
 import com.redhat.thermostat.common.plugin.PluginConfiguration;
 import com.redhat.thermostat.common.plugin.SystemID;
+import com.redhat.thermostat.host.cpu.agent.internal.CpuStatDAOImpl.ConfigurationCreator;
 import com.redhat.thermostat.host.cpu.model.CpuStat;
 
 public class CpuStatDAOTest {
 
-    private static final String URL = "http://localhost:26000/api/system-cpu/0.0.1";
+    private static final URI GATEWAY_URI = URI.create("http://localhost:26000/api/system-cpu/0.0.1/");
     private static final String SOME_JSON = "{\"some\" : \"json\"}";
     private static final double times[] = { 33., 44, };
     private static final String HOST_NAME = "somehostname";
 
-    private static final String URL_PROP = "gatewayURL";
-
     private CpuStat info;
     private CpuStatDAOImpl.JsonHelper jsonHelper;
     private ConfigurationInfoSource cfiSource;
@@ -84,12 +80,10 @@
         when(jsonHelper.toJson(anyListOf(CpuStat.class))).thenReturn(SOME_JSON);
 
         cfiSource = mock(ConfigurationInfoSource.class);
-        Map<String,String> map = new HashMap<>();
-        map.put(URL_PROP, URL);
-        when(cfiSource.getConfiguration(anyString(),anyString())).thenReturn(map);
-
-        configCreator = mock(CpuStatDAOImpl.ConfigurationCreator.class);
-        when(configCreator.create(eq(cfiSource))).thenReturn(new PluginConfiguration(cfiSource, CpuStatDAOImpl.PLUGIN_ID));
+        configCreator = mock(ConfigurationCreator.class);
+        PluginConfiguration pluginConfig = mock(PluginConfiguration.class);
+        when(pluginConfig.getGatewayURL()).thenReturn(GATEWAY_URI);
+        when(configCreator.create(cfiSource)).thenReturn(pluginConfig);
 
         httpRequestService = mock(HttpRequestService.class);
         idservice = mock(SystemID.class);
@@ -106,7 +100,7 @@
         dao.activate();
         dao.put(info);
 
-        verify(httpRequestService, times(1)).sendHttpRequest(SOME_JSON, URL + "/systems/" + HOST_NAME, HttpRequestService.POST);
+        verify(httpRequestService, times(1)).sendHttpRequest(SOME_JSON, GATEWAY_URI.resolve("systems/" + HOST_NAME), HttpRequestService.POST);
     }
 
 }
--- a/plugins/host-memory/agent/src/main/java/com/redhat/thermostat/host/memory/agent/internal/MemoryStatDAOImpl.java	Wed Jul 19 14:42:37 2017 +0200
+++ b/plugins/host-memory/agent/src/main/java/com/redhat/thermostat/host/memory/agent/internal/MemoryStatDAOImpl.java	Mon Jul 24 12:29:44 2017 -0400
@@ -37,10 +37,16 @@
 package com.redhat.thermostat.host.memory.agent.internal;
 
 import java.io.IOException;
+import java.net.URI;
 import java.util.Arrays;
 import java.util.List;
 import java.util.logging.Logger;
 
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.Service;
+
 import com.redhat.thermostat.agent.http.HttpRequestService;
 import com.redhat.thermostat.common.config.experimental.ConfigurationInfoSource;
 import com.redhat.thermostat.common.plugin.PluginConfiguration;
@@ -50,11 +56,6 @@
 import com.redhat.thermostat.host.memory.model.MemoryStat;
 import com.redhat.thermostat.host.memory.model.MemoryStatTypeAdapter;
 
-import org.apache.felix.scr.annotations.Activate;
-import org.apache.felix.scr.annotations.Component;
-import org.apache.felix.scr.annotations.Reference;
-import org.apache.felix.scr.annotations.Service;
-
 @Component
 @Service(value = MemoryStatDAO.class)
 public class MemoryStatDAOImpl extends PluginDAOBase<MemoryStat, MemoryStatDAOImpl> implements MemoryStatDAO {
@@ -114,8 +115,8 @@
     }
 
     @Override
-    protected String getURL(String basepath) {
-        return basepath + "/systems/" + systemID.getSystemID();
+    protected URI getPostURI(URI basepath) {
+        return basepath.resolve("systems/" + systemID.getSystemID());
     }
 
     // DS bind methods
--- a/plugins/host-memory/agent/src/test/java/com/redhat/thermostat/host/memory/agent/internal/MemoryStatDAOTest.java	Wed Jul 19 14:42:37 2017 +0200
+++ b/plugins/host-memory/agent/src/test/java/com/redhat/thermostat/host/memory/agent/internal/MemoryStatDAOTest.java	Mon Jul 24 12:29:44 2017 -0400
@@ -37,15 +37,12 @@
 package com.redhat.thermostat.host.memory.agent.internal;
 
 import static org.mockito.Matchers.anyListOf;
-import static org.mockito.Matchers.anyString;
-import static org.mockito.Matchers.eq;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
-import java.util.HashMap;
-import java.util.Map;
+import java.net.URI;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -56,16 +53,15 @@
 import com.redhat.thermostat.common.config.experimental.ConfigurationInfoSource;
 import com.redhat.thermostat.common.plugin.PluginConfiguration;
 import com.redhat.thermostat.common.plugin.SystemID;
+import com.redhat.thermostat.host.memory.agent.internal.MemoryStatDAOImpl.ConfigurationCreator;
 import com.redhat.thermostat.host.memory.model.MemoryStat;
 
 public class MemoryStatDAOTest {
 
-    private static final String URL = "http://localhost:26000/api/system-memory/0.0.1";
+    private static final URI GATEWAY_URI = URI.create("http://localhost:26000/api/system-memory/0.0.1/");
     private static final String SOME_JSON = "{\"some\" : \"json\"}";
     private static final String HOST_NAME = "somehostname";
 
-    private static final String URL_PROP = "gatewayURL";
-
     private MemoryStat info;
     private MemoryStatDAOImpl.JsonHelper jsonHelper;
     private ConfigurationInfoSource cfiSource;
@@ -82,15 +78,12 @@
         when(jsonHelper.toJson(anyListOf(MemoryStat.class))).thenReturn(SOME_JSON);
 
         cfiSource = mock(ConfigurationInfoSource.class);
-        Map<String, String> map = new HashMap<>();
-        map.put(URL_PROP, URL);
-        when(cfiSource.getConfiguration(anyString(), anyString())).thenReturn(map);
+        configCreator = mock(ConfigurationCreator.class);
+        PluginConfiguration pluginConfig = mock(PluginConfiguration.class);
+        when(pluginConfig.getGatewayURL()).thenReturn(GATEWAY_URI);
+        when(configCreator.create(cfiSource)).thenReturn(pluginConfig);
 
         httpRequestService = mock(HttpRequestService.class);
-
-        configCreator = mock(MemoryStatDAOImpl.ConfigurationCreator.class);
-        when(configCreator.create(eq(cfiSource))).thenReturn(new PluginConfiguration(cfiSource, MemoryStatDAOImpl.PLUGIN_ID));
-
         idservice = mock(SystemID.class);
         when(idservice.getSystemID()).thenReturn(HOST_NAME);
     }
@@ -104,7 +97,7 @@
         dao.activate();
         dao.put(info);
 
-        verify(httpRequestService, times(1)).sendHttpRequest(SOME_JSON, URL + "/systems/" + HOST_NAME, HttpRequestService.POST);
+        verify(httpRequestService, times(1)).sendHttpRequest(SOME_JSON, GATEWAY_URI.resolve("systems/" + HOST_NAME), HttpRequestService.POST);
     }
 }
 
--- a/plugins/host-network/agent/src/main/java/com/redhat/thermostat/host/network/internal/NetworkInfoListDAOImpl.java	Wed Jul 19 14:42:37 2017 +0200
+++ b/plugins/host-network/agent/src/main/java/com/redhat/thermostat/host/network/internal/NetworkInfoListDAOImpl.java	Mon Jul 24 12:29:44 2017 -0400
@@ -37,23 +37,23 @@
 package com.redhat.thermostat.host.network.internal;
 
 import java.io.IOException;
+import java.net.URI;
 import java.util.logging.Logger;
 
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.Service;
+
 import com.redhat.thermostat.agent.http.HttpRequestService;
 import com.redhat.thermostat.common.config.experimental.ConfigurationInfoSource;
 import com.redhat.thermostat.common.plugin.PluginConfiguration;
 import com.redhat.thermostat.common.plugin.PluginDAOBase;
 import com.redhat.thermostat.common.plugin.SystemID;
+import com.redhat.thermostat.common.utils.LoggingUtils;
 import com.redhat.thermostat.host.network.model.NetworkInfoList;
 import com.redhat.thermostat.host.network.model.NetworkInfoListTypeAdapter;
 
-import org.apache.felix.scr.annotations.Activate;
-import org.apache.felix.scr.annotations.Component;
-import org.apache.felix.scr.annotations.Reference;
-import org.apache.felix.scr.annotations.Service;
-
-import com.redhat.thermostat.common.utils.LoggingUtils;
-
 @Component
 @Service(value = NetworkInfoListDAO.class)
 public class NetworkInfoListDAOImpl extends PluginDAOBase<NetworkInfoList, NetworkInfoListDAOImpl> implements NetworkInfoListDAO {
@@ -106,13 +106,13 @@
     }
 
     @Override
-    protected String getURL(String basepath) {
-        return basepath + "/systems/" + systemID.getSystemID();
+    protected URI getPostURI(URI basepath) {
+        return basepath.resolve("systems/" + systemID.getSystemID());
     }
 
     @Override
     protected Logger getLogger() {
-        return null;
+        return logger;
     }
 
     // DS bind method
--- a/plugins/host-network/agent/src/test/java/com/redhat/thermostat/host/network/internal/NetworkInfoListDAOTest.java	Wed Jul 19 14:42:37 2017 +0200
+++ b/plugins/host-network/agent/src/test/java/com/redhat/thermostat/host/network/internal/NetworkInfoListDAOTest.java	Mon Jul 24 12:29:44 2017 -0400
@@ -37,41 +37,35 @@
 package com.redhat.thermostat.host.network.internal;
 
 import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyString;
-import static org.mockito.Matchers.eq;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import java.net.URI;
 import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.Map;
+
+import org.eclipse.jetty.http.HttpMethod;
+import org.junit.Before;
+import org.junit.Test;
 
 import com.redhat.thermostat.agent.http.HttpRequestService;
 import com.redhat.thermostat.common.config.experimental.ConfigurationInfoSource;
 import com.redhat.thermostat.common.plugin.PluginConfiguration;
 import com.redhat.thermostat.common.plugin.SystemID;
+import com.redhat.thermostat.host.network.internal.NetworkInfoListDAOImpl.ConfigurationCreator;
+import com.redhat.thermostat.host.network.internal.NetworkInfoListDAOImpl.JsonHelper;
 import com.redhat.thermostat.host.network.model.NetworkInfoList;
 import com.redhat.thermostat.host.network.model.NetworkInterfaceInfo;
-import com.redhat.thermostat.host.network.internal.NetworkInfoListDAOImpl.JsonHelper;
-
-import org.eclipse.jetty.client.api.ContentResponse;
-import org.eclipse.jetty.http.HttpMethod;
-import org.eclipse.jetty.http.HttpStatus;
-
-import org.junit.Before;
-import org.junit.Test;
 
 public class NetworkInfoListDAOTest {
 
-    private static final String URL = "http://localhost:26000/api/v100/network-info/systems/";
+    private static final URI GATEWAY_URI = URI.create("http://localhost:26000/api/v100/network-info/");
     private static final String INTERFACE_NAME = "some interface. maybe eth0";
     private static final long TIMESTAMP = 333;
     private static final String IPV4_ADDR = "256.256.256.256";
     private static final String IPV6_ADDR = "100:100:100::::1";
     private static final String SOME_JSON = "{\"some\" : \"json\"}";
-    private static final String URL_PROP = "gatewayURL";
     private static final String HOST_NAME = "somehostname";
     private static final String AGENT_ID = "xxx some agent";
 
@@ -93,18 +87,12 @@
         when(jsonHelper.toJson(any(NetworkInfoList.class))).thenReturn(SOME_JSON);
 
         cfiSource = mock(ConfigurationInfoSource.class);
-        Map<String,String> map = new HashMap<>();
-        map.put(URL_PROP, URL);
-        when(cfiSource.getConfiguration(anyString(),anyString())).thenReturn(map);
-
-        configCreator = mock(NetworkInfoListDAOImpl.ConfigurationCreator.class);
-        when(configCreator.create(eq(cfiSource))).thenReturn(new PluginConfiguration(cfiSource, NetworkInfoListDAOImpl.PLUGIN_ID));
-
+        configCreator = mock(ConfigurationCreator.class);
+        PluginConfiguration pluginConfig = mock(PluginConfiguration.class);
+        when(pluginConfig.getGatewayURL()).thenReturn(GATEWAY_URI);
+        when(configCreator.create(cfiSource)).thenReturn(pluginConfig);
+        
         httpRequestService = mock(HttpRequestService.class);
-        ContentResponse contentResponse = mock(ContentResponse.class);
-       // when(httpRequestService.sendHttpRequest(anyString(), anyString(), anyString())).thenReturn(contentResponse);
-        when(contentResponse.getStatus()).thenReturn(HttpStatus.OK_200);
-
         idservice = mock(SystemID.class);
         when(idservice.getSystemID()).thenReturn(HOST_NAME);
     }
@@ -121,7 +109,7 @@
         NetworkInfoList obj = new NetworkInfoList(AGENT_ID, TIMESTAMP, new ArrayList<NetworkInterfaceInfo>());
         dao.put(obj);
 
-        verify(httpRequestService, times(1)).sendHttpRequest(SOME_JSON, URL + "/systems/" + HOST_NAME, HttpMethod.POST.asString());
+        verify(httpRequestService, times(1)).sendHttpRequest(SOME_JSON, GATEWAY_URI.resolve("systems/" + HOST_NAME), HttpMethod.POST.asString());
     }
 }
 
--- a/plugins/host-overview/agent/src/main/java/com/redhat/thermostat/host/overview/internal/models/HostInfoDAOImpl.java	Wed Jul 19 14:42:37 2017 +0200
+++ b/plugins/host-overview/agent/src/main/java/com/redhat/thermostat/host/overview/internal/models/HostInfoDAOImpl.java	Mon Jul 24 12:29:44 2017 -0400
@@ -37,24 +37,24 @@
 package com.redhat.thermostat.host.overview.internal.models;
 
 import java.io.IOException;
+import java.net.URI;
 import java.util.Arrays;
 import java.util.List;
 import java.util.logging.Logger;
 
-import com.redhat.thermostat.agent.http.HttpRequestService;
-import com.redhat.thermostat.common.plugin.SystemID;
 import org.apache.felix.scr.annotations.Activate;
 import org.apache.felix.scr.annotations.Component;
 import org.apache.felix.scr.annotations.Reference;
 import org.apache.felix.scr.annotations.Service;
 
+import com.redhat.thermostat.agent.http.HttpRequestService;
 import com.redhat.thermostat.common.config.experimental.ConfigurationInfoSource;
 import com.redhat.thermostat.common.plugin.PluginConfiguration;
 import com.redhat.thermostat.common.plugin.PluginDAOBase;
+import com.redhat.thermostat.common.plugin.SystemID;
 import com.redhat.thermostat.common.utils.LoggingUtils;
-
+import com.redhat.thermostat.host.overview.model.HostInfo;
 import com.redhat.thermostat.host.overview.model.HostInfoTypeAdapter;
-import com.redhat.thermostat.host.overview.model.HostInfo;
 
 @Component
 @Service(value = HostInfoDAO.class)
@@ -93,8 +93,8 @@
         this.config = configCreator.create(configurationInfoSource);
     }
 
-    public String getURL(final String base) {
-        return base + "/systems/" + systemID.getSystemID();
+    public URI getPostURI(final URI base) {
+        return base.resolve("systems/" + systemID.getSystemID());
     }
 
     public String getPluginId() {
--- a/plugins/host-overview/agent/src/test/java/com/redhat/thermostat/host/overview/internal/models/HostInfoDAOImplTest.java	Wed Jul 19 14:42:37 2017 +0200
+++ b/plugins/host-overview/agent/src/test/java/com/redhat/thermostat/host/overview/internal/models/HostInfoDAOImplTest.java	Mon Jul 24 12:29:44 2017 -0400
@@ -37,15 +37,12 @@
 package com.redhat.thermostat.host.overview.internal.models;
 
 import static org.mockito.Matchers.anyListOf;
-import static org.mockito.Matchers.anyString;
-import static org.mockito.Matchers.eq;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
-import java.util.HashMap;
-import java.util.Map;
+import java.net.URI;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -59,9 +56,10 @@
 
 public class HostInfoDAOImplTest {
 
-    private static final String URL = "http://localhost:26000/api/systems/v0.0.3";
+    private static final URI GATEWAY_URI = URI.create("http://localhost:26000/api/systems/v0.0.3/");
+    private static final URI PUT_URI = GATEWAY_URI.resolve("systems/aHostName");
+    private static final String HOST_NAME = "aHostName";
     private static final String SOME_JSON = "{\"some\" : \"json\"}";
-    private static final String HOST_NAME = "a host name";
     private static final long TIMESTAMP = 77L;
     private static final String OS_NAME = "some os";
     private static final String OS_KERNEL = "some kernel";
@@ -69,8 +67,6 @@
     private static final int CPU_NUM = -1;
     private static final long MEMORY_TOTAL = 0xCAFEBABEl;
 
-    private static final String URL_PROP = "gatewayURL";
-
     private HostInfo info;
     private HostInfoDAOImpl.JsonHelper jsonHelper;
     private ConfigurationInfoSource cfiSource;
@@ -87,11 +83,10 @@
         when(jsonHelper.toJson(anyListOf(HostInfo.class))).thenReturn(SOME_JSON);
 
         cfiSource = mock(ConfigurationInfoSource.class);
-        Map<String,String> map = new HashMap<>();
-        map.put(URL_PROP, URL);
-        when(cfiSource.getConfiguration(anyString(),anyString())).thenReturn(map);
         configCreator = mock(ConfigurationCreator.class);
-        when(configCreator.create(eq(cfiSource))).thenReturn(new PluginConfiguration(cfiSource, HostInfoDAOImpl.PLUGIN_ID));
+        PluginConfiguration pluginConfig = mock(PluginConfiguration.class);
+        when(pluginConfig.getGatewayURL()).thenReturn(GATEWAY_URI);
+        when(configCreator.create(cfiSource)).thenReturn(pluginConfig);
 
         idservice = mock(SystemID.class);
         when(idservice.getSystemID()).thenReturn(HOST_NAME);
@@ -107,7 +102,7 @@
         dao.activate();
         
         dao.put(info);
-        verify(httpRequestService, times(1)).sendHttpRequest(SOME_JSON, URL + "/systems/" + HOST_NAME, HttpRequestService.POST);
+        verify(httpRequestService, times(1)).sendHttpRequest(SOME_JSON, PUT_URI, HttpRequestService.POST);
     }
 
 }
--- a/plugins/jvm-overview/agent/src/main/java/com/redhat/thermostat/jvm/overview/agent/internal/model/VmInfoDAOImpl.java	Wed Jul 19 14:42:37 2017 +0200
+++ b/plugins/jvm-overview/agent/src/main/java/com/redhat/thermostat/jvm/overview/agent/internal/model/VmInfoDAOImpl.java	Mon Jul 24 12:29:44 2017 -0400
@@ -37,6 +37,7 @@
 package com.redhat.thermostat.jvm.overview.agent.internal.model;
 
 import java.io.IOException;
+import java.net.URI;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
@@ -74,7 +75,7 @@
     private final JsonHelper jsonHelper;
     private final ConfigurationCreator configCreator;
 
-    private String gatewayURL;
+    private URI gatewayURL;
 
     @Reference
     private ConfigurationInfoSource configInfoSource;
@@ -116,8 +117,8 @@
         try {
             // Encode as JSON and send as POST request
             String json = jsonHelper.toJson(Arrays.asList(info));
-            String url = getAddURL();
-            httpRequestService.sendHttpRequest(json, url, HttpRequestService.POST);
+            URI uri = getAddURI();
+            httpRequestService.sendHttpRequest(json, uri, HttpRequestService.POST);
         } catch (IOException | RequestFailedException e) {
            logger.log(Level.WARNING, "Failed to send JVM information to web gateway", e);
         }
@@ -129,35 +130,28 @@
             // Encode as JSON and send as PUT request
             VmInfoUpdate update = new VmInfoUpdate(timestamp);
             String json = jsonHelper.toJson(update);
-            String url = getUpdateURL(vmId);
-            httpRequestService.sendHttpRequest(json, url, HttpRequestService.PUT);
+            URI uri = getUpdateURI(vmId);
+            httpRequestService.sendHttpRequest(json, uri, HttpRequestService.PUT);
         } catch (IOException | RequestFailedException e) {
            logger.log(Level.WARNING, "Failed to send JVM information update to web gateway", e);
         }
     }
     
-    private String getAddURL() {
-        StringBuilder builder = buildURL();
+    private URI getAddURI() {
+        StringBuilder builder = new StringBuilder();
         builder.append(SYSTEM_PATH);
         builder.append(systemID.getSystemID());
-        return builder.toString();
+        return gatewayURL.resolve(builder.toString());
     }
 
-    private StringBuilder buildURL() {
+    private URI getUpdateURI(String vmId) {
         StringBuilder builder = new StringBuilder();
-        builder.append(gatewayURL);
-        return builder;
-    }
-    
-    private String getUpdateURL(String vmId) {
-        StringBuilder builder = new StringBuilder();
-        builder.append(buildURL());
         builder.append(UPDATE_PREFIX);
         builder.append(SYSTEM_PATH);
         builder.append(systemID.getSystemID());
         builder.append(VM_PATH);
         builder.append(vmId);
-        return builder.toString();
+        return gatewayURL.resolve(builder.toString());
     }
     
     protected void bindHttpRequestService(HttpRequestService httpRequestService) {
--- a/plugins/jvm-overview/agent/src/test/java/com/redhat/thermostat/jvm/overview/agent/internal/model/VmInfoDAOImplTest.java	Wed Jul 19 14:42:37 2017 +0200
+++ b/plugins/jvm-overview/agent/src/test/java/com/redhat/thermostat/jvm/overview/agent/internal/model/VmInfoDAOImplTest.java	Mon Jul 24 12:29:44 2017 -0400
@@ -44,12 +44,12 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import java.net.URI;
 import java.util.Arrays;
 import java.util.HashMap;
 import java.util.Map;
 
 import org.junit.Before;
-import org.junit.Ignore;
 import org.junit.Test;
 import org.mockito.ArgumentCaptor;
 
@@ -64,9 +64,9 @@
 
 public class VmInfoDAOImplTest {
 
-    private static final String GATEWAY_URL = "http://localhost:30000/jvms/0.0.1/";
-    private static final String POST_URL = GATEWAY_URL + "systems/foo";
-    private static final String UPDATE_URL = GATEWAY_URL + "update/systems/foo/jvms/vmId";
+    private static final URI GATEWAY_URI = URI.create("http://localhost:30000/jvms/0.0.1/");
+    private static final URI POST_URI = GATEWAY_URI.resolve("systems/foo");
+    private static final URI UPDATE_URI = GATEWAY_URI.resolve("update/systems/foo/jvms/vmId");
     private static final String SOME_JSON = "{\"some\" : \"json\"}";
     private static final String SOME_OTHER_JSON = "{\"some\" : {\"other\" : \"json\"}}";
     
@@ -103,7 +103,7 @@
 
         source = mock(ConfigurationInfoSource.class);
         config = mock(PluginConfiguration.class);
-        when(config.getGatewayURL()).thenReturn(GATEWAY_URL);
+        when(config.getGatewayURL()).thenReturn(GATEWAY_URI);
         creator = mock(ConfigurationCreator.class);
         when(creator.create(source)).thenReturn(config);
         systemID = mock(SystemID.class);
@@ -124,7 +124,7 @@
         dao.putVmInfo(info);
         
         verify(jsonHelper).toJson(eq(Arrays.asList(info)));
-        verify(httpRequestService).sendHttpRequest(SOME_JSON, POST_URL, HttpRequestService.POST);
+        verify(httpRequestService).sendHttpRequest(SOME_JSON, POST_URI, HttpRequestService.POST);
     }
 
     @Test
@@ -140,7 +140,7 @@
         VmInfoUpdate update = updateCaptor.getValue();
         assertEquals(3L, update.getStoppedTime());
                 
-        verify(httpRequestService).sendHttpRequest(SOME_OTHER_JSON, UPDATE_URL, HttpRequestService.PUT);
+        verify(httpRequestService).sendHttpRequest(SOME_OTHER_JSON, UPDATE_URI, HttpRequestService.PUT);
     }
 
 }
--- a/plugins/vm-gc/agent/src/main/java/com/redhat/thermostat/vm/gc/agent/internal/models/VmGcStatDAOImpl.java	Wed Jul 19 14:42:37 2017 +0200
+++ b/plugins/vm-gc/agent/src/main/java/com/redhat/thermostat/vm/gc/agent/internal/models/VmGcStatDAOImpl.java	Mon Jul 24 12:29:44 2017 -0400
@@ -37,6 +37,7 @@
 package com.redhat.thermostat.vm.gc.agent.internal.models;
 
 import java.io.IOException;
+import java.net.URI;
 import java.util.Arrays;
 import java.util.List;
 import java.util.logging.Level;
@@ -66,7 +67,7 @@
     
     @Reference
     private ConfigurationInfoSource configInfoSource;
-    private String gatewayURL;
+    private URI gatewayURL;
 
     @Reference
     private HttpRequestService httpRequestService;
--- a/plugins/vm-gc/agent/src/test/java/com/redhat/thermostat/vm/gc/agent/internal/models/VmGcStatDAOImplTest.java	Wed Jul 19 14:42:37 2017 +0200
+++ b/plugins/vm-gc/agent/src/test/java/com/redhat/thermostat/vm/gc/agent/internal/models/VmGcStatDAOImplTest.java	Mon Jul 24 12:29:44 2017 -0400
@@ -42,6 +42,7 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import java.net.URI;
 import java.util.Arrays;
 
 import org.junit.Before;
@@ -58,7 +59,7 @@
 
     private static final String AGENT_ID = "some-agent";
     private static final String JSON = "{\"this\":\"is\",\"also\":\"JSON\"}";
-    private static final String GATEWAY_URL = "http://example.com/jvm-gc";
+    private static final URI GATEWAY_URI = URI.create("http://example.com/jvm-gc/");
 
     private VmGcStat stat;
     private JsonHelper jsonHelper;
@@ -81,7 +82,7 @@
 
         ConfigurationInfoSource source = mock(ConfigurationInfoSource.class);
         PluginConfiguration config = mock(PluginConfiguration.class);
-        when(config.getGatewayURL()).thenReturn(GATEWAY_URL);
+        when(config.getGatewayURL()).thenReturn(GATEWAY_URI);
         ConfigurationCreator creator = mock(ConfigurationCreator.class);
         when(creator.create(source)).thenReturn(config);
 
@@ -97,7 +98,7 @@
 
         verify(jsonHelper).toJson(eq(Arrays.asList(stat)));
 
-        verify(httpRequestService).sendHttpRequest(JSON, GATEWAY_URL, HttpRequestService.POST);
+        verify(httpRequestService).sendHttpRequest(JSON, GATEWAY_URI, HttpRequestService.POST);
     }
 
 }
--- a/plugins/vm-memory/agent/src/main/java/com/redhat/thermostat/vm/memory/agent/internal/models/VmMemoryStatDAOImpl.java	Wed Jul 19 14:42:37 2017 +0200
+++ b/plugins/vm-memory/agent/src/main/java/com/redhat/thermostat/vm/memory/agent/internal/models/VmMemoryStatDAOImpl.java	Mon Jul 24 12:29:44 2017 -0400
@@ -37,6 +37,7 @@
 package com.redhat.thermostat.vm.memory.agent.internal.models;
 
 import java.io.IOException;
+import java.net.URI;
 import java.util.Arrays;
 import java.util.List;
 import java.util.logging.Level;
@@ -64,7 +65,7 @@
     private final JsonHelper jsonHelper;
     private final ConfigurationCreator configCreator;
 
-    private String gatewayURL;
+    private URI gatewayURL;
 
     @Reference
     private ConfigurationInfoSource configInfoSource;
--- a/plugins/vm-memory/agent/src/main/java/com/redhat/thermostat/vm/memory/agent/internal/models/VmTlabStatDAOImpl.java	Wed Jul 19 14:42:37 2017 +0200
+++ b/plugins/vm-memory/agent/src/main/java/com/redhat/thermostat/vm/memory/agent/internal/models/VmTlabStatDAOImpl.java	Mon Jul 24 12:29:44 2017 -0400
@@ -37,6 +37,7 @@
 package com.redhat.thermostat.vm.memory.agent.internal.models;
 
 import java.io.IOException;
+import java.net.URI;
 import java.util.List;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.TimeoutException;
@@ -70,7 +71,7 @@
     private final JsonHelper jsonHelper;
     private final ConfigurationCreator configurationCreator;
 
-    private String gatewayURL;
+    private URI gatewayURL;
 
     @Reference
     private ConfigurationInfoSource configInfoSource;
--- a/plugins/vm-memory/agent/src/test/java/com/redhat/thermostat/vm/memory/agent/internal/models/VmMemoryStatDAOImplTest.java	Wed Jul 19 14:42:37 2017 +0200
+++ b/plugins/vm-memory/agent/src/test/java/com/redhat/thermostat/vm/memory/agent/internal/models/VmMemoryStatDAOImplTest.java	Mon Jul 24 12:29:44 2017 -0400
@@ -41,6 +41,7 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import java.net.URI;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
@@ -59,7 +60,7 @@
 public class VmMemoryStatDAOImplTest {
 
     private static final String JSON = "{\"this\":\"is\",\"test\":\"JSON\"}";
-    private static final String GATEWAY_URL = "http://example.com/jvm-memory/0.0.2/";
+    private static final URI GATEWAY_URI = URI.create("http://example.com/jvm-memory/0.0.2/");
     
     private JsonHelper jsonHelper;
     private PluginConfiguration config;
@@ -73,7 +74,7 @@
         when(jsonHelper.toJson(anyListOf(VmMemoryStat.class))).thenReturn(JSON);
         
         config = mock(PluginConfiguration.class);
-        when(config.getGatewayURL()).thenReturn(GATEWAY_URL);
+        when(config.getGatewayURL()).thenReturn(GATEWAY_URI);
 
         source = mock(ConfigurationInfoSource.class);
 
@@ -122,7 +123,7 @@
         dao.putVmMemoryStat(stat);
 
         verify(jsonHelper).toJson(Arrays.asList(stat));
-        verify(httpRequestService).sendHttpRequest(JSON, GATEWAY_URL, HttpRequestService.POST);
+        verify(httpRequestService).sendHttpRequest(JSON, GATEWAY_URI, HttpRequestService.POST);
     }
     
 }
--- a/plugins/vm-memory/agent/src/test/java/com/redhat/thermostat/vm/memory/agent/internal/models/VmTlabStatDAOTest.java	Wed Jul 19 14:42:37 2017 +0200
+++ b/plugins/vm-memory/agent/src/test/java/com/redhat/thermostat/vm/memory/agent/internal/models/VmTlabStatDAOTest.java	Mon Jul 24 12:29:44 2017 -0400
@@ -45,6 +45,7 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import java.net.URI;
 import java.util.Arrays;
 
 import com.redhat.thermostat.common.config.experimental.ConfigurationInfoSource;
@@ -66,7 +67,7 @@
     private static final String VM_ID = "0xcafe";
     private static final String AGENT_ID = "agent";
     private static final String CONTENT_TYPE = "application/json";
-    private static final String GATEWAY_URL = "http://example.com/jvm-memory/0.0.2/";
+    private static final URI GATEWAY_URI = URI.create("http://example.com/jvm-memory/0.0.2/");
     
     private HttpClient httpClient;
     private HttpHelper httpHelper;
@@ -92,13 +93,13 @@
         when(jsonHelper.toJson(anyListOf(VmTlabStat.class))).thenReturn(JSON);
         
         config = mock(PluginConfiguration.class);
-        when(config.getGatewayURL()).thenReturn(GATEWAY_URL);
+        when(config.getGatewayURL()).thenReturn(GATEWAY_URI);
     }
 
     @Test
     public void testActivation() throws Exception {
         PluginConfiguration pluginConfig = mock(PluginConfiguration.class);
-        when(pluginConfig.getGatewayURL()).thenReturn("someGatewayURL");
+        when(pluginConfig.getGatewayURL()).thenReturn(GATEWAY_URI);
         VmTlabStatDAOImpl.ConfigurationCreator configCreator = mock(VmTlabStatDAOImpl.ConfigurationCreator.class);
         ConfigurationInfoSource configInfoSource = mock(ConfigurationInfoSource.class);
         when(configCreator.create(configInfoSource)).thenReturn(pluginConfig);
@@ -138,7 +139,7 @@
         VmTlabStatDAO dao = new VmTlabStatDAOImpl(httpClient, httpHelper, jsonHelper, configurationCreator, configInfoSource);
         dao.putStat(stat);
 
-        verify(httpClient).newRequest(GATEWAY_URL);
+        verify(httpClient).newRequest(GATEWAY_URI);
         verify(request).method(HttpMethod.POST);
         verify(jsonHelper).toJson(Arrays.asList(stat));
         verify(httpHelper).createContentProvider(JSON);