changeset 167:8822a33e3823

Add System Info API to web gateway See bug: http://icedtea.classpath.org/bugzilla/show_bug.cgi?id=3377 Reviewed-by: sgehwolf Review-thread: http://icedtea.classpath.org/pipermail/thermostat/2017-June/023421.html Review-thread: http://icedtea.classpath.org/pipermail/thermostat/2017-May/023313.html
author Simon Tooke <stooke@redhat.com>
date Wed, 31 May 2017 18:56:32 -0400
parents 10df2fd85ca1
children 3193c04a9932
files distribution/pom.xml distribution/src/etc/services.properties services/pom.xml services/systems/pom.xml services/systems/src/main/java/com/redhat/thermostat/service/systems/http/Parameters.java services/systems/src/main/java/com/redhat/thermostat/service/systems/http/SwaggerSpecResourceHandler.java services/systems/src/main/java/com/redhat/thermostat/service/systems/http/SystemsHttpHandler.java services/systems/src/main/java/com/redhat/thermostat/service/systems/mongo/Fields.java services/systems/src/main/java/com/redhat/thermostat/service/systems/mongo/MongoStorageHandler.java services/systems/src/main/resources/systems-swagger.yaml services/systems/src/main/webapp/WEB-INF/web.xml tests/integration-tests/src/test/java/com/redhat/thermostat/gateway/service/systems/SystemInfoIntegrationTest.java
diffstat 12 files changed, 1148 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- a/distribution/pom.xml	Wed May 31 18:48:25 2017 -0400
+++ b/distribution/pom.xml	Wed May 31 18:56:32 2017 -0400
@@ -213,6 +213,13 @@
                         </artifactItem>
                         <artifactItem>
                             <groupId>com.redhat.thermostat</groupId>
+                            <artifactId>thermostat-web-gateway-service-systems</artifactId>
+                            <version>${project.version}</version>
+                            <type>war</type>
+                            <overWrite>false</overWrite>
+                        </artifactItem>
+                        <artifactItem>
+                            <groupId>com.redhat.thermostat</groupId>
                             <artifactId>thermostat-web-gateway-service-white-pages</artifactId>
                             <version>${project.version}</version>
                             <type>war</type>
@@ -253,6 +260,13 @@
         </dependency>
         <dependency>
             <groupId>com.redhat.thermostat</groupId>
+            <artifactId>thermostat-web-gateway-service-systems</artifactId>
+            <version>${project.version}</version>
+            <type>war</type>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>com.redhat.thermostat</groupId>
             <artifactId>thermostat-web-gateway-service-commands</artifactId>
             <version>${project.version}</version>
             <type>war</type>
--- a/distribution/src/etc/services.properties	Wed May 31 18:48:25 2017 -0400
+++ b/distribution/src/etc/services.properties	Wed May 31 18:56:32 2017 -0400
@@ -2,4 +2,5 @@
 /jvm-gc = thermostat-web-gateway-service-jvm-gc-@project.version@.war
 /jvm-memory = thermostat-web-gateway-service-jvm-memory-@project.version@.war
 /jvms = thermostat-web-gateway-service-jvms-@project.version@.war
+/systems = thermostat-web-gateway-service-systems-@project.version@.war
 /white-pages = thermostat-web-gateway-service-white-pages-@project.version@.war
--- a/services/pom.xml	Wed May 31 18:48:25 2017 -0400
+++ b/services/pom.xml	Wed May 31 18:56:32 2017 -0400
@@ -55,6 +55,7 @@
         <module>jvm-gc</module>
         <module>jvms</module>
         <module>jvm-memory</module>
+        <module>systems</module>
         <module>white-pages</module>
     </modules>
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/services/systems/pom.xml	Wed May 31 18:56:32 2017 -0400
@@ -0,0 +1,97 @@
+<!--
+
+ 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/maven-v4_0_0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <artifactId>thermostat-web-gateway-services</artifactId>
+        <groupId>com.redhat.thermostat</groupId>
+        <version>1.99.12-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>thermostat-web-gateway-service-systems</artifactId>
+
+    <packaging>war</packaging>
+
+    <name>Thermostat Web Gateway System Info Service</name>
+
+    <properties>
+        <com.redhat.thermostat.gateway.SERVICE_NAME>systems</com.redhat.thermostat.gateway.SERVICE_NAME>
+    </properties>
+
+    <build>
+        <plugins>
+            <plugin>
+                <artifactId>maven-war-plugin</artifactId>
+                <configuration>
+                    <webResources>
+                        <resource>
+                            <directory>src/main/webapp</directory>
+                            <filtering>true</filtering>
+                        </resource>
+                    </webResources>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+    <dependencies>
+        <!-- Thermostat Web Gateway Dependencies -->
+        <dependency>
+            <groupId>com.redhat.thermostat</groupId>
+            <artifactId>thermostat-web-gateway-common-mongodb</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+
+        <!-- Servlet API deps -->
+        <dependency>
+            <groupId>javax.servlet</groupId>
+            <artifactId>javax.servlet-api</artifactId>
+            <version>${javax.servlet.version}</version>
+            <scope>provided</scope>
+        </dependency>
+
+        <!-- JAX-RS Dependencies -->
+        <dependency>
+            <groupId>javax.ws.rs</groupId>
+            <artifactId>javax.ws.rs-api</artifactId>
+            <version>${javax-rs-api.version}</version>
+            <scope>provided</scope>
+        </dependency>
+    </dependencies>
+</project>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/services/systems/src/main/java/com/redhat/thermostat/service/systems/http/Parameters.java	Wed May 31 18:56:32 2017 -0400
@@ -0,0 +1,51 @@
+/*
+ * 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.service.systems.http;
+
+class Parameters {
+    static final String SYSTEM_ID = "systemId";
+
+    static final String SORT = "sort";
+    static final String QUERY = "query";
+    static final String OFFSET = "offset";
+    static final String LIMIT = "limit";
+    static final String INCLUDE = "include";
+    static final String EXCLUDE = "exclude";
+
+    static final String TIMESTAMP = "timeStamp";
+    static final String ALIVE_ONLY = "aliveOnly";
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/services/systems/src/main/java/com/redhat/thermostat/service/systems/http/SwaggerSpecResourceHandler.java	Wed May 31 18:56:32 2017 -0400
@@ -0,0 +1,59 @@
+/*
+ * 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.service.systems.http;
+
+import java.io.IOException;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import com.redhat.thermostat.gateway.common.core.servlet.BasicResourceHandler;
+
+@Path("doc/{fileName: .+\\.yaml}")
+@Produces(MediaType.TEXT_PLAIN)
+public class SwaggerSpecResourceHandler extends BasicResourceHandler {
+
+    @GET
+    public Response getFileAsPlainText(@PathParam("fileName") String fileName) throws IOException {
+        return getFileAsResponse(SwaggerSpecResourceHandler.class.getClassLoader(), fileName);
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/services/systems/src/main/java/com/redhat/thermostat/service/systems/http/SystemsHttpHandler.java	Wed May 31 18:56:32 2017 -0400
@@ -0,0 +1,153 @@
+/*
+ * 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.service.systems.http;
+
+import javax.servlet.ServletContext;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.DefaultValue;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.Response;
+
+
+import com.mongodb.DBObject;
+import com.redhat.thermostat.gateway.common.mongodb.ThermostatMongoStorage;
+import com.redhat.thermostat.gateway.common.mongodb.servlet.ServletContextConstants;
+import com.redhat.thermostat.service.systems.mongo.MongoStorageHandler;
+
+@Path("/")
+public class SystemsHttpHandler {
+    private final MongoStorageHandler mongoStorageHandler = new MongoStorageHandler();
+    private final String collectionName = "system-info";
+
+    @GET
+    @Consumes({ "application/json" })
+    @Produces({ "application/json", "text/html; charset=utf-8" })
+    public Response getSystemInfoAll(@QueryParam(Parameters.LIMIT) @DefaultValue("1") Integer limit,
+                                  @QueryParam(Parameters.OFFSET) @DefaultValue("0") Integer offset,
+                                  @QueryParam(Parameters.SORT) String sort,
+                                  @QueryParam(Parameters.QUERY) String queries,
+                                  @QueryParam(Parameters.INCLUDE) String includes,
+                                  @QueryParam(Parameters.EXCLUDE) String excludes,
+                                  @Context ServletContext context
+    ) {
+        try {
+            ThermostatMongoStorage storage = (ThermostatMongoStorage) context.getAttribute(ServletContextConstants.MONGODB_CLIENT_ATTRIBUTE);
+            String message = mongoStorageHandler.getSystemInfos(storage.getDatabase().getCollection(collectionName), limit, offset, sort, queries, includes, excludes);
+            return Response.status(Response.Status.OK).entity(message).build();
+        } catch (Exception e) {
+            return Response.status(Response.Status.BAD_REQUEST).build();
+        }
+    }
+
+    @GET
+    @Path("/systems/{" + Parameters.SYSTEM_ID +"}")
+    @Consumes({ "application/json" })
+    @Produces({ "application/json", "text/html; charset=utf-8" })
+    public Response getSystemInfo(@PathParam(Parameters.SYSTEM_ID) String systemId,
+                                  @QueryParam(Parameters.LIMIT) @DefaultValue("1") Integer limit,
+                                  @QueryParam(Parameters.OFFSET) @DefaultValue("0") Integer offset,
+                                  @QueryParam(Parameters.SORT) String sort,
+                                  @QueryParam(Parameters.INCLUDE) String includes,
+                                  @QueryParam(Parameters.EXCLUDE) String excludes,
+                                  @Context ServletContext context
+    ) {
+        try {
+            ThermostatMongoStorage storage = (ThermostatMongoStorage) context.getAttribute(ServletContextConstants.MONGODB_CLIENT_ATTRIBUTE);
+            String message = mongoStorageHandler.getSystemInfo(storage.getDatabase().getCollection(collectionName), systemId, limit, offset, sort, includes, excludes);
+            return Response.status(Response.Status.OK).entity(message).build();
+        } catch (Exception e) {
+            return Response.status(Response.Status.BAD_REQUEST).build();
+        }
+    }
+
+    @PUT
+    @Path("/systems/{" + Parameters.SYSTEM_ID +"}")
+    @Consumes({ "application/json" })
+    @Produces({ "application/json", "text/html; charset=utf-8" })
+    public Response putSystemInfo(String body,
+                                  @PathParam(Parameters.SYSTEM_ID) String systemId,
+                                  @QueryParam(Parameters.QUERY) String queries,
+                                  @Context ServletContext context) {
+        try {
+            ThermostatMongoStorage storage = (ThermostatMongoStorage) context.getAttribute(ServletContextConstants.MONGODB_CLIENT_ATTRIBUTE);
+            mongoStorageHandler.updateSystemInfo(storage.getDatabase().getCollection(collectionName), body, systemId, queries);
+            return Response.status(Response.Status.OK).build();
+        } catch (Exception e) {
+            return Response.status(Response.Status.BAD_REQUEST).build();
+        }
+    }
+
+    @POST
+    @Path("/systems/{" + Parameters.SYSTEM_ID +"}")
+    @Consumes({ "application/json" })
+    @Produces({ "application/json", "text/html; charset=utf-8" })
+    public Response postSystemInfo(String body,
+                                   @PathParam(Parameters.SYSTEM_ID) String systemId,
+                                   @Context ServletContext context) {
+        try {
+            ThermostatMongoStorage storage = (ThermostatMongoStorage) context.getAttribute(ServletContextConstants.MONGODB_CLIENT_ATTRIBUTE);
+            mongoStorageHandler.addSystemInfos(storage.getDatabase().getCollection(collectionName, DBObject.class), body, systemId);
+            return Response.status(Response.Status.OK).build();
+        } catch (Exception e) {
+            return Response.status(Response.Status.BAD_REQUEST).build();
+        }
+    }
+
+    @DELETE
+    @Path("/systems/{" + Parameters.SYSTEM_ID +"}")
+    @Consumes({ "application/json" })
+    @Produces({ "application/json", "text/html; charset=utf-8" })
+    public Response deleteSystemInfo(@PathParam(Parameters.SYSTEM_ID) String systemId,
+                                     @QueryParam(Parameters.QUERY) String queries,
+                                     @Context ServletContext context) {
+        try {
+            ThermostatMongoStorage storage = (ThermostatMongoStorage) context.getAttribute(ServletContextConstants.MONGODB_CLIENT_ATTRIBUTE);
+            mongoStorageHandler.deleteSystemInfo(storage.getDatabase().getCollection(collectionName), systemId);
+            return Response.status(Response.Status.OK).build();
+        } catch (Exception e) {
+            return Response.status(Response.Status.BAD_REQUEST).build();
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/services/systems/src/main/java/com/redhat/thermostat/service/systems/mongo/Fields.java	Wed May 31 18:56:32 2017 -0400
@@ -0,0 +1,48 @@
+/*
+ * 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.service.systems.mongo;
+
+class Fields {
+    static final String SYSTEM_ID = "systemId";
+
+    static final String SET = "set";
+    static final String LAST_UPDATED = "lastUpdated";
+    static final String STOP_TIME = "stopTime";
+
+    static final String RESPONSE = "response";
+    static final String SYSTEMS = "systems";
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/services/systems/src/main/java/com/redhat/thermostat/service/systems/mongo/MongoStorageHandler.java	Wed May 31 18:56:32 2017 -0400
@@ -0,0 +1,173 @@
+/*
+ * 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.service.systems.mongo;
+
+import com.mongodb.BasicDBObject;
+import com.mongodb.Block;
+import com.mongodb.CursorType;
+import com.mongodb.DBObject;
+import com.mongodb.client.FindIterable;
+import com.mongodb.client.MongoCollection;
+import com.mongodb.util.JSON;
+import com.redhat.thermostat.gateway.common.mongodb.filters.MongoRequestFilters;
+import com.redhat.thermostat.gateway.common.mongodb.filters.MongoSortFilters;
+import com.redhat.thermostat.gateway.common.mongodb.response.MongoResponseBuilder;
+import org.bson.Document;
+import org.bson.conversions.Bson;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import static com.mongodb.client.model.Filters.and;
+import static com.mongodb.client.model.Filters.eq;
+import static com.mongodb.client.model.Filters.lt;
+import static com.mongodb.client.model.Filters.or;
+import static com.mongodb.client.model.Projections.exclude;
+import static com.mongodb.client.model.Projections.excludeId;
+import static com.mongodb.client.model.Projections.fields;
+import static com.mongodb.client.model.Projections.include;
+
+public class MongoStorageHandler {
+
+    private final MongoResponseBuilder mongoResponseBuilder = new MongoResponseBuilder();
+
+    public String getSystemInfos(MongoCollection<Document> collection, Integer limit, Integer offset, String sort, String queries, String includes, String excludes) {
+        FindIterable<Document> documents;
+        if (queries != null) {
+            List<String> queriesList = Arrays.asList(queries.split(","));
+            final Bson query = MongoRequestFilters.buildQueriesFilter(queriesList);
+            documents = collection.find(query);
+        } else {
+            documents = collection.find();
+        }
+
+        documents = buildProjection(documents, includes, excludes);
+
+        final Bson sortObject = MongoSortFilters.createSortObject(sort);
+        documents = documents.sort(sortObject).limit(limit).skip(offset).batchSize(limit).cursorType(CursorType.NonTailable);
+
+        return mongoResponseBuilder.buildGetResponseString(documents);
+    }
+
+    public String getSystemInfo(MongoCollection<Document> collection, String systemId, Integer limit, Integer offset, String sort, String includes, String excludes) {
+        Bson query = eq(Fields.SYSTEM_ID, systemId);
+        FindIterable<Document> documents = collection.find(query);
+
+        documents = buildProjection(documents, includes, excludes);
+
+        final Bson sortObject = MongoSortFilters.createSortObject(sort);
+        documents = documents.sort(sortObject).limit(limit).skip(offset).batchSize(limit + offset).cursorType(CursorType.NonTailable);
+
+        return mongoResponseBuilder.buildGetResponseString(documents);
+    }
+
+    private FindIterable<Document> buildProjection(FindIterable<Document> documents, String includes, String excludes) {
+        if (excludes != null) {
+            List<String> excludesList = Arrays.asList(excludes.split(","));
+            documents = documents.projection(fields(exclude(excludesList), excludeId()));
+        } else if (includes != null) {
+            List<String> includesList = Arrays.asList(includes.split(","));
+            documents = documents.projection(fields(include(includesList), excludeId()));
+        } else {
+            documents = documents.projection(excludeId());
+        }
+
+        return documents;
+    }
+
+    public void addSystemInfos(MongoCollection<DBObject> collection, String body, String systemId) {
+        if (body.length() > 0) {
+            List<DBObject> inputList = (List<DBObject>) JSON.parse(body);
+            for (DBObject o : inputList) {
+                o.put(Fields.SYSTEM_ID, systemId);
+            }
+            collection.insertMany(inputList);
+        }
+    }
+
+    public void deleteSystemInfo(MongoCollection<Document> collection, String systemId) {
+        Bson query = eq(Fields.SYSTEM_ID, systemId);
+        deleteDocuments(collection, query);
+    }
+
+    private void deleteDocuments(MongoCollection<Document> collection, Bson query) {
+        collection.deleteMany(query);
+    }
+
+    public void updateSystemInfo(MongoCollection<Document> collection, String body, String systemId, String queries) {
+        Bson baseQuery = eq(Fields.SYSTEM_ID, systemId);
+
+        BasicDBObject inputObject = (BasicDBObject) JSON.parse(body);
+        BasicDBObject setObject = (BasicDBObject) inputObject.get(Fields.SET);
+        if (setObject.containsField(Fields.SYSTEM_ID)) {
+            throw new UnsupportedOperationException("Updating " +  Fields.SYSTEM_ID + " fields is not allowed");
+        }
+
+        final List<String> queriesList;
+        if (queries != null) {
+            queriesList = Arrays.asList(queries.split(","));
+        } else {
+            queriesList = Collections.emptyList();
+        }
+
+        final Bson fields = new Document("$set", setObject);
+
+        collection.updateMany(and(baseQuery, MongoRequestFilters.buildQueriesFilter(queriesList)), fields);
+    }
+
+    public void updateTimestamps(MongoCollection<Document> collection, String body, String systemId, Long timeStamp) {
+        final Bson filter;
+        if (body != null && body.length() > 0) {
+            List<String> systems = (List<String>) JSON.parse(body);
+            List<Bson> systemFilters = new ArrayList<>();
+            for (String id : systems) {
+                systemFilters.add(eq(Fields.SYSTEM_ID, id));
+            }
+            filter = or(eq(Fields.SYSTEM_ID, systemId), or(systemFilters));
+        } else {
+            filter = eq(Fields.SYSTEM_ID, systemId);
+        }
+
+        String setDocument = "{ \"$set\" : { \"" + Fields.LAST_UPDATED + "\":" + timeStamp + " } }";
+        final Bson update = Document.parse(setDocument);
+        collection.updateMany(filter, update);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/services/systems/src/main/resources/systems-swagger.yaml	Wed May 31 18:56:32 2017 -0400
@@ -0,0 +1,192 @@
+swagger: '2.0'
+info:
+  version: 0.0.1
+  title: Thermostat Web Gateway System Information API
+  license:
+    name: GPL v2 with Classpath Exception
+    url: 'http://www.gnu.org/licenses'
+consumes:
+  - application/json
+produces:
+  - application/json
+  - text/html; charset=utf-8
+basePath: /systems/0.0.1
+paths:
+  /:
+    get:
+      description: Get information for all systems.
+      parameters:
+        - $ref: '#/parameters/limit'
+        - $ref: '#/parameters/offset'
+        - $ref: '#/parameters/sort'
+        - $ref: '#/parameters/include'
+        - $ref: '#/parameters/exclude'
+        - $ref: '#/parameters/query'
+        - $ref: '#/parameters/alive'
+      responses:
+        '200':
+          description: OK
+          schema:
+            $ref: '#/definitions/systems-get-response'
+  /systems/{systemId}:
+    parameters:
+      - $ref: '#/parameters/system-id'
+    get:
+      description: Get information for system {systemId}.
+      parameters:
+        - $ref: '#/parameters/limit'
+        - $ref: '#/parameters/offset'
+        - $ref: '#/parameters/sort'
+        - $ref: '#/parameters/include'
+        - $ref: '#/parameters/exclude'
+        - $ref: '#/parameters/query'
+        - $ref: '#/parameters/alive'
+      responses:
+        '200':
+          description: OK
+          schema:
+            $ref: '#/definitions/systems-get-response'
+    put:
+      description: Update information for system {systemId}.
+      parameters:
+        - $ref: '#/parameters/systems-put-body'
+        - $ref: '#/parameters/query'
+      responses:
+        '200':
+          description: OK
+    post:
+      description: Add information for system {systemId}
+      parameters:
+        - $ref: '#/parameters/system-info-array'
+      responses:
+        '200':
+          description: OK
+    delete:
+      description: Delete information for system ID {systemId}.
+      parameters:
+        - $ref: '#/parameters/query'
+      responses:
+        '200':
+          description: OK
+definitions:
+  systems-get-response:
+    type: object
+    properties:
+      response:
+          $ref: '#/definitions/system-info-array'
+  system-info-array:
+    type: array
+    items:
+      $ref: '#/definitions/system-info'
+  system-info:
+    type: object
+    properties:
+      systemId:
+        type: string
+      agentId:
+        type: string
+      hostname:
+        type: string
+      osName:
+        type: string
+      osKernel:
+        type: string
+      osArch:
+        type: string
+      cpuCount:
+        type: integer
+      cpuModel:
+        type: string
+      totalMemory:
+        type: integer
+        format: int64
+      timeCreated:
+        type: integer
+        format: int64
+      lastUpdated:
+        type: integer
+        format: int64
+  systems-put-body:
+    type: object
+    properties:
+      "set":
+        type: object
+parameters:
+  system-id:
+    name: systemId
+    in: path
+    required: true
+    type: string
+  system-info-array:
+    name: system-info-array
+    in: body
+    description: The system information
+    required: true
+    schema:
+      $ref: '#/definitions/system-info-array'
+  systems-put-body:
+    name: body
+    in: body
+    description: >-
+      The JSON object containing a 'set' object. This contains single item JSON
+      objects that specify the field to replace and the JSON value to replace with.
+      Must not include 'systemId' field. Example { "set" : {
+      "field" : "value", "field2":{"object":"item"} }
+    required: true
+    schema:
+      $ref: '#/definitions/systems-put-body'
+  alive:
+    name: alive
+    in: query
+    description: Whether to return only systems that are currently running
+    type: boolean
+    required: false
+    default: true
+  limit:
+    name: limit
+    in: query
+    description: Limit of items to return. Example '1'
+    type: integer
+    required: false
+    default: 1
+  offset:
+    name: offset
+    in: query
+    description: Offset of items to return. Example '0'
+    type: integer
+    required: true
+    default: 0
+  sort:
+    name: sort
+    in: query
+    description: Sort string. Comma separated list of fields prefixed with '+' for ascending or '-' for descending. Example '?sort=+a,-b' Fields use dot notation for embedded documents. Example 'outer.inner' refers to field inner contained in field outer.
+    type: string
+    required: false
+  query:
+    name: query
+    in: query
+    description: Query string. Comma separated list of key, comparator, value pairs. Comparator supports '==', '<=', '>=', '<', '>', '!='. Example '?query=a==b,c!=d'. Keys are fields in documents and use dot notation for embedded documents. Example 'outer.inner' refers to field inner contained in field outer.
+    type: string
+    required: false
+  include:
+    name: include
+    in: query
+    description: >-
+      Inclusion string. Comma separated list of fields to include in the
+      response. Example '?include=a,b' Fields use dot notation for embedded
+      documents. Example 'outer.inner' refers to field inner contained in field
+      outer. Cannot be used in combination with 'exclude' parameter Overriden by
+      'exclude' parameter
+    type: string
+    required: false
+  exclude:
+    name: exclude
+    in: query
+    description: >-
+      Exclusion string. Comma separated list of fields to exclude in the
+      response. Example '?exclude=a,b' Fields use dot notation for embedded
+      documents. Example 'outer.inner' refers to field inner contained in field
+      outer. Cannot be used in combination with 'include' parameter; takes
+      precedence over 'include' parameter
+    type: string
+    required: false
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/services/systems/src/main/webapp/WEB-INF/web.xml	Wed May 31 18:56:32 2017 -0400
@@ -0,0 +1,68 @@
+<!--
+
+ 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.
+
+-->
+<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
+         http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
+         version="3.1">
+    <servlet>
+        <servlet-name>SystemInfoServlet</servlet-name>
+        <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
+        <init-param>
+            <param-name>
+                jersey.config.server.provider.packages
+            </param-name>
+            <param-value>
+                com.redhat.thermostat.service.systems,
+            </param-value>
+        </init-param>
+    </servlet>
+    <servlet-mapping>
+        <servlet-name>SystemInfoServlet</servlet-name>
+        <url-pattern>/0.0.1/*</url-pattern>
+    </servlet-mapping>
+    <!-- Service configuration -->
+    <context-param>
+        <param-name>com.redhat.thermostat.gateway.SERVICE_NAME</param-name>
+        <param-value>@com.redhat.thermostat.gateway.SERVICE_NAME@</param-value>
+    </context-param>
+    <!-- Listener for setting up the storage connection -->
+    <listener>
+        <listener-class>com.redhat.thermostat.gateway.common.mongodb.servlet.StorageConnectionSettingListener</listener-class>
+    </listener>
+</web-app>
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/integration-tests/src/test/java/com/redhat/thermostat/gateway/service/systems/SystemInfoIntegrationTest.java	Wed May 31 18:56:32 2017 -0400
@@ -0,0 +1,291 @@
+/*
+ * 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.gateway.service.systems;
+
+import com.google.gson.JsonArray;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParser;
+import com.redhat.thermostat.gateway.tests.integration.IntegrationTest;
+import org.eclipse.jetty.client.api.ContentResponse;
+import org.eclipse.jetty.client.api.Request;
+import org.eclipse.jetty.client.util.StringContentProvider;
+import org.eclipse.jetty.http.HttpMethod;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.UUID;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeoutException;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+public class SystemInfoIntegrationTest extends IntegrationTest {
+
+    private static final String collectionName = "system-info";
+    private static final String serviceURL = baseUrl + "/systems/0.0.1";
+    private static final int HTTP_200_OK = 200;
+    private static final String CPU_STRING1 = "Intel";
+    private static final String CPU_STRING2 = "AMD";
+    private static final String AGENT_ID = getRandomSystemId();
+    private static final String HOSTNAME = getRandomSystemId();
+    private static final String systemInfoJSON =
+            "{\n" +
+            "  \"agentId\": \"" + AGENT_ID + "\",\n" +
+            "  \"hostname\": \"" + HOSTNAME + "\",\n" +
+            "  \"osName\": \"Windows 10\",\n" +
+            "  \"osKernel\": \"10.0\",\n" +
+            "  \"cpuModel\": \"" + CPU_STRING1 + "\",\n" +
+            "  \"cpuCount\": 4,\n" +
+            "  \"totalMemory\": {\n" +
+            "    \"$numberLong\": \"12566220800\"\n" +
+            "  }\n" +
+            "}";
+
+    private static class TinyHostInfo {
+        TinyHostInfo(String sytehId, String agentId, String hostName, String cpuModel) {
+            this.systemId = sytehId;
+            this.agentId = agentId;
+            this.hostName = hostName;
+            this.cpuModel = cpuModel;
+        }
+        String hostName;
+        String agentId;
+        String systemId;
+        String cpuModel;
+    }
+
+    public SystemInfoIntegrationTest() {
+        super(serviceURL);
+    }
+
+    @Before
+    public void beforeIntegrationTest() {
+        mongodTestUtil.dropCollection(collectionName);
+    }
+
+    @Test
+    public void testGetAll() throws InterruptedException, TimeoutException, ExecutionException {
+        final String systemid1 = getRandomSystemId();
+        postSystemInfo(systemid1);
+
+        final String systemid2 = getRandomSystemId();
+        postSystemInfo(systemid2);
+
+        ContentResponse response = client.newRequest(serviceURL).method(HttpMethod.GET).send();
+        assertEquals(HTTP_200_OK, response.getStatus());
+        final List<TinyHostInfo> list = parseHostInfo(response, null);
+        assertEquals(1, list.size());
+
+        ContentResponse response2 = client.newRequest(serviceURL + "?limit=99").method(HttpMethod.GET).send();
+        assertEquals(HTTP_200_OK, response2.getStatus());
+        final List<TinyHostInfo> list2 = parseHostInfo(response2, null);
+        assertEquals(2, list2.size());
+
+        ContentResponse response3 = client.newRequest(serviceURL + "?limit=0").method(HttpMethod.GET).send();
+        assertEquals(HTTP_200_OK, response3.getStatus());
+        final List<TinyHostInfo> list3 = parseHostInfo(response3, null);
+        assertEquals(2, list3.size());
+    }
+
+    @Test
+    public void testGetAllEmpty() throws InterruptedException, TimeoutException, ExecutionException {
+        ContentResponse response = client.newRequest(serviceURL).method(HttpMethod.GET).send();
+        assertEquals(HTTP_200_OK, response.getStatus());
+        final List<TinyHostInfo> list = parseHostInfo(response, null);
+        assertTrue(list.isEmpty());
+    }
+
+    @Test
+    public void testGetUnknown() throws InterruptedException, TimeoutException, ExecutionException {
+        final String systemid = getRandomSystemId();
+        getUnknownSystemInfo(systemid);
+    }
+
+    @Test
+    public void testCreateOne() throws InterruptedException, TimeoutException, ExecutionException {
+
+        final String systemid = getRandomSystemId();
+        postSystemInfo(systemid);
+        getKnownSystemInfo(systemid);
+    }
+
+    @Test
+    public void testPut() throws InterruptedException, TimeoutException, ExecutionException {
+
+        final String systemid = getRandomSystemId();
+
+        // create it
+        postSystemInfo(systemid);
+
+        // retrieve it
+        final ContentResponse response1 = getKnownSystemInfo(systemid);
+        final List<TinyHostInfo> list1 = parseHostInfo(response1, systemid);
+        assertEquals(1, list1.size());
+        assertEquals(CPU_STRING1, list1.get(0).cpuModel);
+
+        // modify it
+        putSystemInfo(systemid, CPU_STRING2);
+
+        // ensure it was changed
+        final ContentResponse response2 = getKnownSystemInfo(systemid);
+        final List<TinyHostInfo> list2 = parseHostInfo(response2, systemid);
+        assertEquals(1, list2.size());
+        assertEquals(CPU_STRING2, list2.get(0).cpuModel);
+    }
+
+    @Test
+    public void testDeleteUnknown() throws InterruptedException, TimeoutException, ExecutionException {
+        final String systemid = getRandomSystemId();
+
+        // delete it
+        deleteSystemInfo(systemid);
+    }
+
+    @Test
+    public void testDeleteOne() throws InterruptedException, ExecutionException, TimeoutException {
+        final String systemid = getRandomSystemId();
+
+        // create the new record
+        postSystemInfo(systemid);
+
+        // check that it's there
+        getKnownSystemInfo(systemid);
+
+        // delete it
+        deleteSystemInfo(systemid);
+
+        // check that it's not there
+        getUnknownSystemInfo(systemid);
+    }
+
+    private ContentResponse postSystemInfo(final String systemid) throws InterruptedException, ExecutionException, TimeoutException {
+        final Request request = client.newRequest(serviceURL + "/systems/" + systemid);
+        request.header("Content-Type", "application/json");
+        request.content(new StringContentProvider("[" + createSystemInfoJSON(systemid) + "]"));
+        ContentResponse response = request.method(HttpMethod.POST).send();
+        assertEquals(HTTP_200_OK, response.getStatus());
+        final String expected = "";
+        assertEquals(expected, response.getContentAsString());
+        return response;
+    }
+
+    private List<TinyHostInfo> parseHostInfo(ContentResponse contentResponse, final String expectedSystemId) {
+
+        JsonParser parser = new JsonParser();
+        JsonObject json = (JsonObject) parser.parse(contentResponse.getContentAsString());
+        JsonElement response = json.get("response");
+
+        JsonArray allData = response.getAsJsonArray();
+        List<TinyHostInfo> result = new ArrayList<>();
+
+        for (JsonElement entry : allData) {
+
+            json = (JsonObject) parser.parse(entry.toString());
+
+            assertTrue(json.has("systemId"));
+            assertTrue(json.has("agentId"));
+            assertTrue(json.has("hostname"));
+            assertTrue(json.has("cpuModel"));
+
+            final String systemId = json.get("systemId").getAsString();
+            final String agentId = json.get("agentId").getAsString();
+            final String hostName = json.get("hostname").getAsString();
+            final String cpuModel = json.get("cpuModel").getAsString();
+
+            assertEquals(AGENT_ID, agentId);
+            assertEquals(HOSTNAME, hostName);
+            if (expectedSystemId != null) {
+                assertEquals(expectedSystemId, systemId);
+            }
+
+            TinyHostInfo hi = new TinyHostInfo(systemId, agentId, hostName, cpuModel);
+
+            result.add(hi);
+        }
+        return result;
+    }
+
+    private ContentResponse putSystemInfo(final String systemid, final String cpuid) throws InterruptedException, ExecutionException, TimeoutException {
+        final Request request = client.newRequest(serviceURL + "/systems/" + systemid);
+        request.header("Content-Type", "application/json");
+        final String contentStr = createSystemInfoJSON(systemid).replace(CPU_STRING1, cpuid);
+        request.content(new StringContentProvider("{ \"set\" : " +contentStr + "}"));
+        ContentResponse response = request.method(HttpMethod.PUT).send();
+        assertEquals(HTTP_200_OK, response.getStatus());
+        final String expected = "";
+        assertEquals(expected, response.getContentAsString());
+        return response;
+    }
+
+    private ContentResponse getUnknownSystemInfo(final String systemid) throws InterruptedException, ExecutionException, TimeoutException {
+        ContentResponse response = getSystemInfo(systemid);
+        assertTrue(parseHostInfo(response, systemid).isEmpty());
+        return response;
+    }
+
+    private ContentResponse getKnownSystemInfo(final String systemid) throws InterruptedException, ExecutionException, TimeoutException {
+        ContentResponse response = getSystemInfo(systemid);
+        assertEquals(1, parseHostInfo(response, systemid).size());
+        return response;
+    }
+
+    private ContentResponse getSystemInfo(final String systemid) throws InterruptedException, ExecutionException, TimeoutException {
+        ContentResponse response = client.newRequest(serviceURL + "/systems/" + systemid).method(HttpMethod.GET).send();
+        assertEquals(HTTP_200_OK, response.getStatus());
+        return response;
+    }
+
+    private ContentResponse deleteSystemInfo(final String systemid) throws InterruptedException, ExecutionException, TimeoutException {
+        ContentResponse response = client.newRequest(serviceURL + "/systems/" + systemid).method(HttpMethod.DELETE).send();
+        assertEquals(HTTP_200_OK, response.getStatus());
+        final String expected = "";
+        assertEquals(expected, response.getContentAsString());
+        return response;
+    }
+
+    private static String getRandomSystemId() {
+        return UUID.randomUUID().toString();
+    }
+
+    private String createSystemInfoJSON(final String systemid) {
+        return systemInfoJSON;
+    }
+}