changeset 2677:36f6b133eb8e

Add configurable URL to jvm-memory microservice Reviewed-by: jmatsuok Review-thread: http://icedtea.classpath.org/pipermail/thermostat/2017-May/023280.html
author Elliott Baron <ebaron@redhat.com>
date Thu, 25 May 2017 13:40:02 -0400
parents b0da6fc3df05
children 259b1bc3fb64
files plugins/vm-memory/common/src/main/java/com/redhat/thermostat/vm/memory/common/internal/Activator.java plugins/vm-memory/common/src/main/java/com/redhat/thermostat/vm/memory/common/internal/VmMemoryStatConfiguration.java plugins/vm-memory/common/src/main/java/com/redhat/thermostat/vm/memory/common/internal/VmMemoryStatDAOImpl.java plugins/vm-memory/common/src/main/java/com/redhat/thermostat/vm/memory/common/internal/VmTlabStatDAOImpl.java plugins/vm-memory/common/src/test/java/com/redhat/thermostat/vm/memory/common/internal/ActivatorTest.java plugins/vm-memory/common/src/test/java/com/redhat/thermostat/vm/memory/common/internal/VmMemoryStatConfigurationTest.java plugins/vm-memory/common/src/test/java/com/redhat/thermostat/vm/memory/common/internal/VmMemoryStatDAOTest.java plugins/vm-memory/common/src/test/java/com/redhat/thermostat/vm/memory/common/internal/VmTlabStatDAOTest.java plugins/vm-memory/distribution/assemblies/plugin-assembly.xml plugins/vm-memory/distribution/configFiles/gateway.properties
diffstat 10 files changed, 289 insertions(+), 62 deletions(-) [+]
line wrap: on
line diff
--- a/plugins/vm-memory/common/src/main/java/com/redhat/thermostat/vm/memory/common/internal/Activator.java	Thu May 25 14:36:44 2017 -0400
+++ b/plugins/vm-memory/common/src/main/java/com/redhat/thermostat/vm/memory/common/internal/Activator.java	Thu May 25 13:40:02 2017 -0400
@@ -36,24 +36,80 @@
 
 package com.redhat.thermostat.vm.memory.common.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.ServiceReference;
+import org.osgi.framework.ServiceRegistration;
+import org.osgi.util.tracker.ServiceTracker;
+
+import com.redhat.thermostat.common.config.experimental.ConfigurationInfoSource;
+import com.redhat.thermostat.common.utils.LoggingUtils;
 import com.redhat.thermostat.vm.memory.common.VmMemoryStatDAO;
 import com.redhat.thermostat.vm.memory.common.VmTlabStatDAO;
 
 public class Activator implements BundleActivator {
+    
+    private static final Logger logger = LoggingUtils.getLogger(Activator.class);
+    
+    private final DAOCreator creator;
+    private ServiceTracker tracker;
+    private ServiceRegistration memoryReg;
+    private ServiceRegistration tlabReg;
+    
+    public Activator() {
+        this(new DAOCreator());
+    }
+    
+    Activator(DAOCreator creator) {
+        this.creator = creator;
+    }
 
     @Override
     public void start(BundleContext context) throws Exception {
-        VmTlabStatDAO vmTlabStatDao = new VmTlabStatDAOImpl();
-        context.registerService(VmTlabStatDAO.class.getName(), vmTlabStatDao, null);
-        VmMemoryStatDAO vmMemoryStatDao = new VmMemoryStatDAOImpl();
-        context.registerService(VmMemoryStatDAO.class.getName(), vmMemoryStatDao, null);
+        tracker = new ServiceTracker(context, ConfigurationInfoSource.class.getName(), null) {
+            @Override
+            public Object addingService(ServiceReference reference) {
+                ConfigurationInfoSource source = (ConfigurationInfoSource) super.addingService(reference);
+                VmMemoryStatConfiguration config = new VmMemoryStatConfiguration(source);
+                try {
+                    VmMemoryStatDAO vmMemoryStatDao = creator.createMemoryStatDAO(config);
+                    memoryReg = context.registerService(VmMemoryStatDAO.class.getName(), vmMemoryStatDao, null);
+                    VmTlabStatDAO vmTlabStatDao = creator.createTlabStatDAO(config);
+                    tlabReg = context.registerService(VmTlabStatDAO.class.getName(), vmTlabStatDao, null);
+                } catch (Exception e) {
+                    logger.log(Level.SEVERE, "Failed to create DAOs", e);
+                }
+                return source;
+            }
+            @Override
+            public void removedService(ServiceReference reference, Object service) {
+                if (memoryReg != null) {
+                    memoryReg.unregister();
+                }
+                if (tlabReg != null) {
+                    tlabReg.unregister();
+                }
+                super.removedService(reference, service);
+            }
+        };
+        tracker.open();
     }
 
     @Override
     public void stop(BundleContext context) throws Exception {
-        // Nothing to do here.
+        tracker.close();
+    }
+    
+    static class DAOCreator {
+        VmMemoryStatDAO createMemoryStatDAO(VmMemoryStatConfiguration config) throws Exception {
+            return new VmMemoryStatDAOImpl(config);
+        }
+        VmTlabStatDAO createTlabStatDAO(VmMemoryStatConfiguration config) throws Exception {
+            return new VmTlabStatDAOImpl(config);
+        }
     }
 
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/vm-memory/common/src/main/java/com/redhat/thermostat/vm/memory/common/internal/VmMemoryStatConfiguration.java	Thu May 25 13:40:02 2017 -0400
@@ -0,0 +1,74 @@
+/*
+ * 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.vm.memory.common.internal;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Map;
+
+import com.redhat.thermostat.common.config.experimental.ConfigurationInfoSource;
+
+class VmMemoryStatConfiguration {
+    
+    private static final String PLUGIN_ID = "vm-memory";
+    private static final String CONFIG_FILE = "gateway.properties";
+    private static final String URL_PROP = "gatewayURL";
+    
+    private final ConfigurationInfoSource source;
+    
+    VmMemoryStatConfiguration(ConfigurationInfoSource source) {
+        this.source = source;
+    }
+    
+    String getGatewayURL() throws IOException {
+        Map<String, String> props = source.getConfiguration(PLUGIN_ID, CONFIG_FILE);
+        String url = props.get(URL_PROP);
+        if (url == null) {
+            throw new IOException("No gateway URL found for " + PLUGIN_ID + " in " + getConfigFilePath());
+        }
+        return url;
+    }
+    
+    private String getConfigFilePath() {
+        StringBuilder builder = new StringBuilder();
+        builder.append("$THERMOSTAT_HOME").append(File.separator).append("etc").append(File.separator)
+                .append("plugins.d").append(File.separator).append(PLUGIN_ID).append(File.separator)
+                .append(CONFIG_FILE);
+        return builder.toString();
+    }
+
+}
--- a/plugins/vm-memory/common/src/main/java/com/redhat/thermostat/vm/memory/common/internal/VmMemoryStatDAOImpl.java	Thu May 25 14:36:44 2017 -0400
+++ b/plugins/vm-memory/common/src/main/java/com/redhat/thermostat/vm/memory/common/internal/VmMemoryStatDAOImpl.java	Thu May 25 13:40:02 2017 -0400
@@ -57,20 +57,20 @@
 class VmMemoryStatDAOImpl implements VmMemoryStatDAO {
 
     private static final Logger logger = LoggingUtils.getLogger(VmMemoryStatDAOImpl.class);
+    private static final String CONTENT_TYPE = "application/json";
 
+    private final String gatewayURL;
     private final HttpClient client;
     private final HttpHelper httpHelper;
     private final JsonHelper jsonHelper;
 
-    private static final String GATEWAY_URL = "http://localhost:30000"; // TODO configurable
-    private static final String GATEWAY_PATH = "/jvm-memory/0.0.2/";
-    private static final String CONTENT_TYPE = "application/json";
-
-    VmMemoryStatDAOImpl() throws Exception {
-        this(new HttpClient(), new HttpHelper(), new JsonHelper(new VmMemoryStatTypeAdapter()));
+    VmMemoryStatDAOImpl(VmMemoryStatConfiguration config) throws Exception {
+        this(config, new HttpClient(), new HttpHelper(), new JsonHelper(new VmMemoryStatTypeAdapter()));
     }
 
-    VmMemoryStatDAOImpl(HttpClient client, HttpHelper httpHelper, JsonHelper jsonHelper) throws Exception {
+    VmMemoryStatDAOImpl(VmMemoryStatConfiguration config, HttpClient client, HttpHelper httpHelper, 
+            JsonHelper jsonHelper) throws Exception {
+        this.gatewayURL = config.getGatewayURL();
         this.client = client;
         this.httpHelper = httpHelper;
         this.jsonHelper = jsonHelper;
@@ -84,8 +84,7 @@
             String json = jsonHelper.toJson(Arrays.asList(stat));
             StringContentProvider provider = httpHelper.createContentProvider(json);
 
-            String url = GATEWAY_URL + GATEWAY_PATH;
-            Request httpRequest = client.newRequest(url);
+            Request httpRequest = client.newRequest(gatewayURL);
             httpRequest.method(HttpMethod.POST);
             httpRequest.content(provider, CONTENT_TYPE);
             sendRequest(httpRequest);
--- a/plugins/vm-memory/common/src/main/java/com/redhat/thermostat/vm/memory/common/internal/VmTlabStatDAOImpl.java	Thu May 25 14:36:44 2017 -0400
+++ b/plugins/vm-memory/common/src/main/java/com/redhat/thermostat/vm/memory/common/internal/VmTlabStatDAOImpl.java	Thu May 25 13:40:02 2017 -0400
@@ -57,21 +57,21 @@
 
 class VmTlabStatDAOImpl implements VmTlabStatDAO {
 
-    private static final String GATEWAY_URL = "http://localhost:30000"; // TODO configurable
-    private static final String GATEWAY_PATH = "/jvm-memory/0.0.2/";
+    private static final Logger logger = LoggingUtils.getLogger(VmTlabStatDAOImpl.class);
     private static final String CONTENT_TYPE = "application/json";
 
-    private static final Logger logger = LoggingUtils.getLogger(VmTlabStatDAOImpl.class);
-
+    private final String gatewayURL;
     private final HttpClient client;
     private final HttpHelper httpHelper;
     private final JsonHelper jsonHelper;
 
-    VmTlabStatDAOImpl() throws Exception {
-        this(new HttpClient(), new HttpHelper(), new JsonHelper(new VmTlabStatTypeAdapter()));
+    VmTlabStatDAOImpl(VmMemoryStatConfiguration config) throws Exception {
+        this(config, new HttpClient(), new HttpHelper(), new JsonHelper(new VmTlabStatTypeAdapter()));
     }
 
-    VmTlabStatDAOImpl(HttpClient client, HttpHelper httpHelper, JsonHelper jsonHelper) throws Exception {
+    VmTlabStatDAOImpl(VmMemoryStatConfiguration config, HttpClient client, HttpHelper httpHelper, 
+            JsonHelper jsonHelper) throws Exception {
+        this.gatewayURL = config.getGatewayURL();
         this.client = client;
         this.httpHelper = httpHelper;
         this.jsonHelper = jsonHelper;
@@ -86,8 +86,7 @@
             String json = jsonHelper.toJson(Arrays.asList(stat));
             StringContentProvider provider = httpHelper.createContentProvider(json);
 
-            String url = GATEWAY_URL + GATEWAY_PATH;
-            Request httpRequest = client.newRequest(url);
+            Request httpRequest = client.newRequest(gatewayURL);
             httpRequest.method(HttpMethod.POST);
             httpRequest.content(provider, CONTENT_TYPE);
             sendRequest(httpRequest);
--- a/plugins/vm-memory/common/src/test/java/com/redhat/thermostat/vm/memory/common/internal/ActivatorTest.java	Thu May 25 14:36:44 2017 -0400
+++ b/plugins/vm-memory/common/src/test/java/com/redhat/thermostat/vm/memory/common/internal/ActivatorTest.java	Thu May 25 13:40:02 2017 -0400
@@ -37,28 +37,40 @@
 package com.redhat.thermostat.vm.memory.common.internal;
 
 import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
 import org.junit.Test;
 
+import com.redhat.thermostat.common.config.experimental.ConfigurationInfoSource;
 import com.redhat.thermostat.testutils.StubBundleContext;
-import com.redhat.thermostat.vm.memory.common.VmMemoryStatDAO;
+import com.redhat.thermostat.vm.memory.common.internal.Activator.DAOCreator;
 
 public class ActivatorTest {
 
     @Test
     public void verifyActivatorRegistersServices() throws Exception {
+        DAOCreator creator = mock(DAOCreator.class);
+        VmMemoryStatDAOImpl memoryDao = mock(VmMemoryStatDAOImpl.class);
+        VmTlabStatDAOImpl tlabDao = mock(VmTlabStatDAOImpl.class);
+        when(creator.createMemoryStatDAO(any(VmMemoryStatConfiguration.class))).thenReturn(memoryDao);
+        when(creator.createTlabStatDAO(any(VmMemoryStatConfiguration.class))).thenReturn(tlabDao);
+        
+        ConfigurationInfoSource source = mock(ConfigurationInfoSource.class);
         StubBundleContext context = new StubBundleContext();
+        context.registerService(ConfigurationInfoSource.class.getName(), source, null);
 
-        Activator activator = new Activator();
+        Activator activator = new Activator(creator);
 
         activator.start(context);
 
-        assertTrue(context.isServiceRegistered(VmMemoryStatDAO.class.getName(), VmMemoryStatDAOImpl.class));
+        assertEquals(3, context.getAllServices().size());
 
         activator.stop(context);
 
         assertEquals(0, context.getServiceListeners().size());
-        assertEquals(2, context.getAllServices().size());
+        assertEquals(1, context.getAllServices().size());
     }
 
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/vm-memory/common/src/test/java/com/redhat/thermostat/vm/memory/common/internal/VmMemoryStatConfigurationTest.java	Thu May 25 13:40:02 2017 -0400
@@ -0,0 +1,77 @@
+/*
+ * 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.vm.memory.common.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.util.HashMap;
+import java.util.Map;
+
+import org.junit.Test;
+
+import com.redhat.thermostat.common.config.experimental.ConfigurationInfoSource;
+
+public class VmMemoryStatConfigurationTest {
+    
+    private static final String PLUGIN_ID = "vm-memory";
+    private static final String CONFIG_FILE = "gateway.properties";
+    private static final String URL_PROP = "gatewayURL";
+
+    @Test
+    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);
+        VmMemoryStatConfiguration config = new VmMemoryStatConfiguration(source);
+        
+        assertEquals("urlToGateway", config.getGatewayURL());
+    }
+    
+    @Test(expected=IOException.class)
+    public void testGetGatewayURLMissing() throws Exception {
+        ConfigurationInfoSource source = mock(ConfigurationInfoSource.class);
+        Map<String, String> props = new HashMap<>();
+        when(source.getConfiguration(PLUGIN_ID, CONFIG_FILE)).thenReturn(props);
+        VmMemoryStatConfiguration config = new VmMemoryStatConfiguration(source);
+        config.getGatewayURL();
+    }
+
+}
--- a/plugins/vm-memory/common/src/test/java/com/redhat/thermostat/vm/memory/common/internal/VmMemoryStatDAOTest.java	Thu May 25 14:36:44 2017 -0400
+++ b/plugins/vm-memory/common/src/test/java/com/redhat/thermostat/vm/memory/common/internal/VmMemoryStatDAOTest.java	Thu May 25 13:40:02 2017 -0400
@@ -38,8 +38,11 @@
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
+import static org.mockito.Matchers.anyListOf;
 import static org.mockito.Matchers.anyString;
+import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
 
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -57,30 +60,25 @@
 
 import com.redhat.thermostat.storage.core.Key;
 import com.redhat.thermostat.vm.memory.common.VmMemoryStatDAO;
+import com.redhat.thermostat.vm.memory.common.internal.VmMemoryStatDAOImpl.HttpHelper;
+import com.redhat.thermostat.vm.memory.common.internal.VmMemoryStatDAOImpl.JsonHelper;
 import com.redhat.thermostat.vm.memory.common.model.VmMemoryStat;
 import com.redhat.thermostat.vm.memory.common.model.VmMemoryStat.Generation;
 import com.redhat.thermostat.vm.memory.common.model.VmMemoryStat.Space;
 
-import static com.redhat.thermostat.vm.memory.common.internal.VmMemoryStatDAOImpl.HttpHelper;
-import static com.redhat.thermostat.vm.memory.common.internal.VmMemoryStatDAOImpl.JsonHelper;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-import static org.mockito.Mockito.anyListOf;
-
 public class VmMemoryStatDAOTest {
 
+    private static final String JSON = "{\"this\":\"is\",\"test\":\"JSON\"}";
+    private static final String CONTENT_TYPE = "application/json";
+    private static final String GATEWAY_URL = "http://example.com/jvm-memory/0.0.2/";
+    
     private HttpClient httpClient;
     private HttpHelper httpHelper;
     private JsonHelper jsonHelper;
     private StringContentProvider contentProvider;
     private Request request;
     private ContentResponse response;
-    private static final String JSON = "{\"this\":\"is\",\"test\":\"JSON\"}";
-    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://localhost:30000"; // TODO configurable
-    private static final String GATEWAY_PATH = "/jvm-memory/0.0.2/";
+    private VmMemoryStatConfiguration config;
 
     @Before
     public void setUp() throws Exception {
@@ -96,12 +94,13 @@
         when(httpHelper.createContentProvider(anyString())).thenReturn(contentProvider);
         jsonHelper = mock(JsonHelper.class);
         when(jsonHelper.toJson(anyListOf(VmMemoryStat.class))).thenReturn(JSON);
+        
+        config = mock(VmMemoryStatConfiguration.class);
+        when(config.getGatewayURL()).thenReturn(GATEWAY_URL);
     }
 
-    @SuppressWarnings("unchecked")
     @Test
     public void testPutVmMemoryStat() throws Exception {
-
         List<Generation> generations = new ArrayList<Generation>();
 
         int i = 0;
@@ -133,11 +132,10 @@
         VmMemoryStat stat = new VmMemoryStat("foo-agent", 1, "vmId", generations.toArray(new Generation[generations.size()]),
                 2, 3, 4, 5);
         
-        VmMemoryStatDAO dao = new VmMemoryStatDAOImpl(httpClient, httpHelper, jsonHelper);
+        VmMemoryStatDAO dao = new VmMemoryStatDAOImpl(config, httpClient, httpHelper, jsonHelper);
         dao.putVmMemoryStat(stat);
 
-        String url = GATEWAY_URL + GATEWAY_PATH;
-        verify(httpClient).newRequest(url);
+        verify(httpClient).newRequest(GATEWAY_URL);
         verify(request).method(HttpMethod.POST);
         verify(jsonHelper).toJson(Arrays.asList(stat));
         verify(httpHelper).createContentProvider(JSON);
--- a/plugins/vm-memory/common/src/test/java/com/redhat/thermostat/vm/memory/common/internal/VmTlabStatDAOTest.java	Thu May 25 14:36:44 2017 -0400
+++ b/plugins/vm-memory/common/src/test/java/com/redhat/thermostat/vm/memory/common/internal/VmTlabStatDAOTest.java	Thu May 25 13:40:02 2017 -0400
@@ -60,18 +60,19 @@
 
 public class VmTlabStatDAOTest {
 
+    private static final String JSON = "{\"this\":\"is\",\"test\":\"JSON\"}";
+    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 HttpClient httpClient;
     private HttpHelper httpHelper;
     private JsonHelper jsonHelper;
     private StringContentProvider contentProvider;
     private Request request;
     private ContentResponse response;
-    private static final String JSON = "{\"this\":\"is\",\"test\":\"JSON\"}";
-    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://localhost:30000"; // TODO configurable
-    private static final String GATEWAY_PATH = "/jvm-memory/0.0.2/";
+    private VmMemoryStatConfiguration config;
 
     @Before
     public void setUp() throws Exception {
@@ -87,6 +88,9 @@
         when(httpHelper.createContentProvider(anyString())).thenReturn(contentProvider);
         jsonHelper = mock(JsonHelper.class);
         when(jsonHelper.toJson(anyListOf(VmTlabStat.class))).thenReturn(JSON);
+        
+        config = mock(VmMemoryStatConfiguration.class);
+        when(config.getGatewayURL()).thenReturn(GATEWAY_URL);
     }
 
     @Test
@@ -108,11 +112,10 @@
         stat.setTotalFastWaste(678l);
         stat.setMaxFastWaste(333l);
 
-        VmTlabStatDAO dao = new VmTlabStatDAOImpl(httpClient, httpHelper, jsonHelper);
+        VmTlabStatDAO dao = new VmTlabStatDAOImpl(config, httpClient, httpHelper, jsonHelper);
         dao.putStat(stat);
 
-        String url = GATEWAY_URL + GATEWAY_PATH;
-        verify(httpClient).newRequest(url);
+        verify(httpClient).newRequest(GATEWAY_URL);
         verify(request).method(HttpMethod.POST);
         verify(jsonHelper).toJson(Arrays.asList(stat));
         verify(httpHelper).createContentProvider(JSON);
--- a/plugins/vm-memory/distribution/assemblies/plugin-assembly.xml	Thu May 25 14:36:44 2017 -0400
+++ b/plugins/vm-memory/distribution/assemblies/plugin-assembly.xml	Thu May 25 13:40:02 2017 -0400
@@ -43,8 +43,7 @@
   <formats>
     <format>zip</format>
   </formats>
-  <baseDirectory>${thermostat.plugin}</baseDirectory>
-  <includeBaseDirectory>true</includeBaseDirectory>
+  <includeBaseDirectory>false</includeBaseDirectory>
   
   <dependencySets>
     <dependencySet>
@@ -54,15 +53,23 @@
       </includes>
       <useProjectArtifact>false</useProjectArtifact>
       <useStrictFiltering>true</useStrictFiltering>
+      <outputDirectory>plugins/${thermostat.plugin}</outputDirectory>
     </dependencySet>
   </dependencySets>
   
-  <files>
-    <file>
-      <source>thermostat-plugin.xml</source>
-      <outputDirectory>/</outputDirectory>
+  <fileSets>
+    <fileSet>
+      <includes>
+        <include>thermostat-plugin.xml</include>
+      </includes>
+      <outputDirectory>plugins/${thermostat.plugin}</outputDirectory>
       <filtered>true</filtered>
-    </file>
-  </files>
+    </fileSet>
+    <fileSet>
+      <directory>configFiles</directory>
+      <outputDirectory>etc/plugins.d/${thermostat.plugin}</outputDirectory>
+      <filtered>true</filtered>
+    </fileSet>
+  </fileSets>
 </assembly>
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/vm-memory/distribution/configFiles/gateway.properties	Thu May 25 13:40:02 2017 -0400
@@ -0,0 +1,2 @@
+# URL to the jvm-memory microservice provided by the Thermostat web gateway
+gatewayURL=http://localhost:30000/jvm-memory/0.0.2/