changeset 251:ed634d83e742

Add delta information to jvm-gc service Reviewed-by: jerboaa Review-thread: http://icedtea.classpath.org/pipermail/thermostat/2017-July/024048.html Review-thread: http://icedtea.classpath.org/pipermail/thermostat/2017-August/024417.html
author Jie Kang <jkang@redhat.com>
date Thu, 07 Sep 2017 09:07:07 -0400
parents 2d924d7904a7
children 1428b14f642f
files common/mongodb/src/main/java/com/redhat/thermostat/gateway/common/mongodb/filters/MongoSortFilters.java services/jvm-gc/src/main/java/com/redhat/thermostat/gateway/service/jvm/gc/JvmGcHttpHandler.java services/jvm-gc/src/main/java/com/redhat/thermostat/gateway/service/jvm/gc/SwaggerSpecResourceHandler.java services/jvm-gc/src/main/java/com/redhat/thermostat/gateway/service/jvm/gc/http/JvmGcHttpHandler.java services/jvm-gc/src/main/java/com/redhat/thermostat/gateway/service/jvm/gc/http/SwaggerSpecResourceHandler.java services/jvm-gc/src/main/java/com/redhat/thermostat/gateway/service/jvm/gc/mongo/Fields.java services/jvm-gc/src/main/java/com/redhat/thermostat/gateway/service/jvm/gc/mongo/JvmGcMongoStorageHandler.java services/jvm-gc/src/main/resources/jvm-gc-swagger.yaml tests/integration-tests/src/test/java/com/redhat/thermostat/gateway/service/jvm/gc/JvmGcServiceIntegrationTest.java
diffstat 9 files changed, 775 insertions(+), 208 deletions(-) [+]
line wrap: on
line diff
--- a/common/mongodb/src/main/java/com/redhat/thermostat/gateway/common/mongodb/filters/MongoSortFilters.java	Wed Sep 06 13:11:55 2017 -0400
+++ b/common/mongodb/src/main/java/com/redhat/thermostat/gateway/common/mongodb/filters/MongoSortFilters.java	Thu Sep 07 09:07:07 2017 -0400
@@ -42,14 +42,17 @@
 
 public class MongoSortFilters {
 
+    public static final Character DESCENDING_SORT = '-';
+    public static final Character ASCENDING_SORT = '+';
+
     public static Bson createSortObject(String sort) {
         BasicDBObject sortObject = new BasicDBObject();
         if (sort != null) {
             String[] items = sort.split(",");
             for (String item : items) {
-                if (item.charAt(0) == '+') {
+                if (item.charAt(0) == ASCENDING_SORT) {
                     sortObject.append(item.substring(1), 1);
-                } else if (item.charAt(0) == '-') {
+                } else if (item.charAt(0) == DESCENDING_SORT) {
                     sortObject.append(item.substring(1), -1);
                 }
             }
--- a/services/jvm-gc/src/main/java/com/redhat/thermostat/gateway/service/jvm/gc/JvmGcHttpHandler.java	Wed Sep 06 13:11:55 2017 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,138 +0,0 @@
-/*
- * Copyright 2012-2017 Red Hat, Inc.
- *
- * This file is part of Thermostat.
- *
- * Thermostat is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published
- * by the Free Software Foundation; either version 2, or (at your
- * option) any later version.
- *
- * Thermostat is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Thermostat; see the file COPYING.  If not see
- * <http://www.gnu.org/licenses/>.
- *
- * Linking this code with other modules is making a combined work
- * based on this code.  Thus, the terms and conditions of the GNU
- * General Public License cover the whole combination.
- *
- * As a special exception, the copyright holders of this code give
- * you permission to link this code with independent modules to
- * produce an executable, regardless of the license terms of these
- * independent modules, and to copy and distribute the resulting
- * executable under terms of your choice, provided that you also
- * meet, for each linked independent module, the terms and conditions
- * of the license of that module.  An independent module is a module
- * which is not derived from or based on this code.  If you modify
- * this code, you may extend this exception to your version of the
- * library, but you are not obligated to do so.  If you do not wish
- * to do so, delete this exception statement from your version.
- */
-
-package com.redhat.thermostat.gateway.service.jvm.gc;
-
-import javax.servlet.ServletContext;
-import javax.servlet.http.HttpServletRequest;
-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.redhat.thermostat.gateway.common.mongodb.servlet.RequestParameters;
-import com.redhat.thermostat.gateway.common.mongodb.servlet.MongoHttpHandlerHelper;
-
-@Path("/")
-public class JvmGcHttpHandler {
-
-    private static final String collectionName = "jvm-gc";
-    private final MongoHttpHandlerHelper serviceHelper = new MongoHttpHandlerHelper( collectionName );
-
-    @GET
-    @Path("/jvms/{" + RequestParameters.JVM_ID +"}")
-    @Consumes({ "application/json" })
-    @Produces({ "application/json", "text/html; charset=utf-8" })
-    public Response getJvmGc(@PathParam(RequestParameters.JVM_ID) String jvmId,
-                             @QueryParam(RequestParameters.LIMIT) @DefaultValue("1") Integer limit,
-                             @QueryParam(RequestParameters.OFFSET) @DefaultValue("0") Integer offset,
-                             @QueryParam(RequestParameters.SORT) String sort,
-                             @QueryParam(RequestParameters.QUERY) String queries,
-                             @QueryParam(RequestParameters.INCLUDE) String includes,
-                             @QueryParam(RequestParameters.EXCLUDE) String excludes,
-                             @QueryParam(RequestParameters.METADATA) @DefaultValue("false") String metadata,
-                             @Context HttpServletRequest httpServletRequest,
-                             @Context ServletContext context) {
-        return serviceHelper.handleGetWithJvmID(httpServletRequest, context, null, jvmId, limit, offset, sort, queries, includes, excludes, metadata);
-    }
-
-
-    @GET
-    @Path("/systems/{" + RequestParameters.SYSTEM_ID +"}/jvms/{" + RequestParameters.JVM_ID +"}")
-    @Consumes({ "application/json" })
-    @Produces({ "application/json", "text/html; charset=utf-8" })
-    public Response getJvmGc(@PathParam(RequestParameters.SYSTEM_ID) String systemId,
-                             @PathParam(RequestParameters.JVM_ID) String jvmId,
-                             @QueryParam(RequestParameters.LIMIT) @DefaultValue("1") Integer limit,
-                             @QueryParam(RequestParameters.OFFSET) @DefaultValue("0") Integer offset,
-                             @QueryParam(RequestParameters.SORT) String sort,
-                             @QueryParam(RequestParameters.QUERY) String queries,
-                             @QueryParam(RequestParameters.INCLUDE) String includes,
-                             @QueryParam(RequestParameters.EXCLUDE) String excludes,
-                             @QueryParam(RequestParameters.METADATA) @DefaultValue("false") String metadata,
-                             @Context HttpServletRequest httpServletRequest,
-                             @Context ServletContext context) {
-        return serviceHelper.handleGetWithJvmID(httpServletRequest, context, systemId, jvmId, limit, offset, sort, queries, includes, excludes, metadata);
-    }
-
-    @PUT
-    @Path("/systems/{" + RequestParameters.SYSTEM_ID +"}/jvms/{" + RequestParameters.JVM_ID +"}")
-    @Consumes({ "application/json" })
-    @Produces({ "application/json", "text/html; charset=utf-8" })
-    public Response putJvmGc(String body,
-                             @PathParam(RequestParameters.SYSTEM_ID) String systemId,
-                             @PathParam(RequestParameters.JVM_ID) String jvmId,
-                             @QueryParam(RequestParameters.QUERY) String queries,
-                             @QueryParam(RequestParameters.METADATA) @DefaultValue("false") String metadata,
-                             @Context ServletContext context,
-                             @Context HttpServletRequest httpServletRequest) {
-        return serviceHelper.handlePutWithJvmId(httpServletRequest, context, systemId, jvmId, queries, metadata, body);
-    }
-
-    @POST
-    @Path("/systems/{" + RequestParameters.SYSTEM_ID +"}/jvms/{" + RequestParameters.JVM_ID +"}")
-    @Consumes({ "application/json" })
-    @Produces({ "application/json", "text/html; charset=utf-8" })
-    public Response postJvmGc(String body,
-                              @PathParam(RequestParameters.SYSTEM_ID) String systemId,
-                              @PathParam(RequestParameters.JVM_ID) String jvmId,
-                              @QueryParam(RequestParameters.METADATA) @DefaultValue("false") String metadata,
-                              @Context ServletContext context,
-                              @Context HttpServletRequest httpServletRequest) {
-        return serviceHelper.handlePostWithJvmID(httpServletRequest, context, systemId, jvmId, metadata, body);
-    }
-
-    @DELETE
-    @Path("/systems/{" + RequestParameters.SYSTEM_ID +"}/jvms/{" + RequestParameters.JVM_ID +"}")
-    @Consumes({ "application/json" })
-    @Produces({ "application/json", "text/html; charset=utf-8" })
-    public Response deleteJvmGc(@PathParam(RequestParameters.SYSTEM_ID) String systemId,
-                                @PathParam(RequestParameters.JVM_ID) String jvmId,
-                                @QueryParam(RequestParameters.QUERY) String queries,
-                                @QueryParam(RequestParameters.METADATA) @DefaultValue("false") String metadata,
-                                @Context ServletContext context,
-                                @Context HttpServletRequest httpServletRequest) {
-        return serviceHelper.handleDeleteWithJvmID(httpServletRequest, context, systemId, jvmId, queries, metadata);
-    }
-}
--- a/services/jvm-gc/src/main/java/com/redhat/thermostat/gateway/service/jvm/gc/SwaggerSpecResourceHandler.java	Wed Sep 06 13:11:55 2017 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,59 +0,0 @@
-/*
- * Copyright 2012-2017 Red Hat, Inc.
- *
- * This file is part of Thermostat.
- *
- * Thermostat is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published
- * by the Free Software Foundation; either version 2, or (at your
- * option) any later version.
- *
- * Thermostat is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Thermostat; see the file COPYING.  If not see
- * <http://www.gnu.org/licenses/>.
- *
- * Linking this code with other modules is making a combined work
- * based on this code.  Thus, the terms and conditions of the GNU
- * General Public License cover the whole combination.
- *
- * As a special exception, the copyright holders of this code give
- * you permission to link this code with independent modules to
- * produce an executable, regardless of the license terms of these
- * independent modules, and to copy and distribute the resulting
- * executable under terms of your choice, provided that you also
- * meet, for each linked independent module, the terms and conditions
- * of the license of that module.  An independent module is a module
- * which is not derived from or based on this code.  If you modify
- * this code, you may extend this exception to your version of the
- * library, but you are not obligated to do so.  If you do not wish
- * to do so, delete this exception statement from your version.
- */
-
-package com.redhat.thermostat.gateway.service.jvm.gc;
-
-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/jvm-gc/src/main/java/com/redhat/thermostat/gateway/service/jvm/gc/http/JvmGcHttpHandler.java	Thu Sep 07 09:07:07 2017 -0400
@@ -0,0 +1,174 @@
+/*
+ * 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.jvm.gc.http;
+
+import javax.servlet.ServletContext;
+import javax.servlet.http.HttpServletRequest;
+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.redhat.thermostat.gateway.common.core.auth.RealmAuthorizer;
+import com.redhat.thermostat.gateway.common.mongodb.ThermostatMongoStorage;
+import com.redhat.thermostat.gateway.common.mongodb.servlet.RequestParameters;
+import com.redhat.thermostat.gateway.common.mongodb.servlet.MongoHttpHandlerHelper;
+import com.redhat.thermostat.gateway.common.mongodb.servlet.ServletContextConstants;
+import com.redhat.thermostat.gateway.service.jvm.gc.mongo.JvmGcMongoStorageHandler;
+
+@Path("/")
+public class JvmGcHttpHandler {
+
+
+    private static final String collectionName = "jvm-gc";
+    private final JvmGcMongoStorageHandler mongoStorageHandler = new JvmGcMongoStorageHandler();
+    private final MongoHttpHandlerHelper serviceHelper = new MongoHttpHandlerHelper( collectionName );
+
+    @GET
+    @Path("/jvms/{" + RequestParameters.JVM_ID +"}")
+    @Consumes({ "application/json" })
+    @Produces({ "application/json", "text/html; charset=utf-8" })
+    public Response getJvmGc(@PathParam(RequestParameters.JVM_ID) String jvmId,
+                             @QueryParam(RequestParameters.LIMIT) @DefaultValue("1") Integer limit,
+                             @QueryParam(RequestParameters.OFFSET) @DefaultValue("0") Integer offset,
+                             @QueryParam(RequestParameters.SORT) String sort,
+                             @QueryParam(RequestParameters.QUERY) String queries,
+                             @QueryParam(RequestParameters.INCLUDE) String includes,
+                             @QueryParam(RequestParameters.EXCLUDE) String excludes,
+                             @QueryParam(RequestParameters.METADATA) @DefaultValue("false") String metadata,
+                             @Context HttpServletRequest httpServletRequest,
+                             @Context ServletContext context) {
+        return serviceHelper.handleGetWithJvmID(httpServletRequest, context, null, jvmId, limit, offset, sort, queries, includes, excludes, metadata);
+    }
+
+
+    @GET
+    @Path("/systems/{" + RequestParameters.SYSTEM_ID +"}/jvms/{" + RequestParameters.JVM_ID +"}")
+    @Consumes({ "application/json" })
+    @Produces({ "application/json", "text/html; charset=utf-8" })
+    public Response getJvmGc(@PathParam(RequestParameters.SYSTEM_ID) String systemId,
+                             @PathParam(RequestParameters.JVM_ID) String jvmId,
+                             @QueryParam(RequestParameters.LIMIT) @DefaultValue("1") Integer limit,
+                             @QueryParam(RequestParameters.OFFSET) @DefaultValue("0") Integer offset,
+                             @QueryParam(RequestParameters.SORT) String sort,
+                             @QueryParam(RequestParameters.QUERY) String queries,
+                             @QueryParam(RequestParameters.INCLUDE) String includes,
+                             @QueryParam(RequestParameters.EXCLUDE) String excludes,
+                             @QueryParam(RequestParameters.METADATA) @DefaultValue("false") String metadata,
+                             @Context HttpServletRequest httpServletRequest,
+                             @Context ServletContext context) {
+        return serviceHelper.handleGetWithJvmID(httpServletRequest, context, systemId, jvmId, limit, offset, sort, queries, includes, excludes, metadata);
+    }
+
+    @PUT
+    @Path("/systems/{" + RequestParameters.SYSTEM_ID +"}/jvms/{" + RequestParameters.JVM_ID +"}")
+    @Consumes({ "application/json" })
+    @Produces({ "application/json", "text/html; charset=utf-8" })
+    public Response putJvmGc(String body,
+                             @PathParam(RequestParameters.SYSTEM_ID) String systemId,
+                             @PathParam(RequestParameters.JVM_ID) String jvmId,
+                             @QueryParam(RequestParameters.QUERY) String queries,
+                             @QueryParam(RequestParameters.METADATA) @DefaultValue("false") String metadata,
+                             @Context ServletContext context,
+                             @Context HttpServletRequest httpServletRequest) {
+        return serviceHelper.handlePutWithJvmId(httpServletRequest, context, systemId, jvmId, queries, metadata, body);
+    }
+
+    @POST
+    @Path("/systems/{" + RequestParameters.SYSTEM_ID +"}/jvms/{" + RequestParameters.JVM_ID +"}")
+    @Consumes({ "application/json" })
+    @Produces({ "application/json", "text/html; charset=utf-8" })
+    public Response postJvmGc(String body,
+                              @PathParam(RequestParameters.SYSTEM_ID) String systemId,
+                              @PathParam(RequestParameters.JVM_ID) String jvmId,
+                              @QueryParam(RequestParameters.METADATA) @DefaultValue("false") String metadata,
+                              @Context ServletContext context,
+                              @Context HttpServletRequest httpServletRequest) {
+        return serviceHelper.handlePostWithJvmID(httpServletRequest, context, systemId, jvmId, metadata, body);
+    }
+
+    @DELETE
+    @Path("/systems/{" + RequestParameters.SYSTEM_ID +"}/jvms/{" + RequestParameters.JVM_ID +"}")
+    @Consumes({ "application/json" })
+    @Produces({ "application/json", "text/html; charset=utf-8" })
+    public Response deleteJvmGc(@PathParam(RequestParameters.SYSTEM_ID) String systemId,
+                                @PathParam(RequestParameters.JVM_ID) String jvmId,
+                                @QueryParam(RequestParameters.QUERY) String queries,
+                                @QueryParam(RequestParameters.METADATA) @DefaultValue("false") String metadata,
+                                @Context ServletContext context,
+                                @Context HttpServletRequest httpServletRequest) {
+        return serviceHelper.handleDeleteWithJvmID(httpServletRequest, context, systemId, jvmId, queries, metadata);
+    }
+
+    @GET
+    @Path("/delta/{jvmId}")
+    @Consumes({ "application/json" })
+    @Produces({ "application/json", "text/html; charset=utf-8" })
+    public Response getJvmGcDelta(@PathParam("jvmId") String jvmId,
+                                  @QueryParam("l") @DefaultValue("1") Integer limit,
+                                  @QueryParam("o") @DefaultValue("0") Integer offset,
+                                  @QueryParam("a") Long afterTimeStamp,
+                                  @QueryParam("b") Long beforeTimeStamp,
+                                  @QueryParam("m") @DefaultValue("false") Boolean metadata,
+                                  @Context HttpServletRequest httpServletRequest,
+                                  @Context ServletContext context) {
+        try {
+            RealmAuthorizer realmAuthorizer = (RealmAuthorizer) httpServletRequest.getAttribute(RealmAuthorizer.class.getName());
+
+            if (realmAuthorizer.readable()) {
+                ThermostatMongoStorage storage = (ThermostatMongoStorage) context.getAttribute(ServletContextConstants.MONGODB_CLIENT_ATTRIBUTE);
+
+                String response = mongoStorageHandler.getJvmGcDelta(storage.getDatabase().getCollection(collectionName),
+                        jvmId, limit, offset, afterTimeStamp, beforeTimeStamp, metadata, httpServletRequest,
+                        realmAuthorizer.getReadableRealms());
+                return Response.status(Response.Status.OK).entity(response).build();
+            } else {
+                return Response.status(Response.Status.FORBIDDEN).build();
+            }
+        } catch (Exception e) {
+            return Response.status(Response.Status.BAD_REQUEST).build();
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/services/jvm-gc/src/main/java/com/redhat/thermostat/gateway/service/jvm/gc/http/SwaggerSpecResourceHandler.java	Thu Sep 07 09:07:07 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.gateway.service.jvm.gc.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/jvm-gc/src/main/java/com/redhat/thermostat/gateway/service/jvm/gc/mongo/Fields.java	Thu Sep 07 09:07:07 2017 -0400
@@ -0,0 +1,44 @@
+/*
+ * 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.jvm.gc.mongo;
+
+class Fields {
+    static final String COLLECTOR_NAME = "collectorName";
+
+    static final String WALL_TIME = "wallTimeInMicros";
+    static final String WALL_TIME_DELTA = "wallTimeDelta";
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/services/jvm-gc/src/main/java/com/redhat/thermostat/gateway/service/jvm/gc/mongo/JvmGcMongoStorageHandler.java	Thu Sep 07 09:07:07 2017 -0400
@@ -0,0 +1,171 @@
+/*
+ * 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.jvm.gc.mongo;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.bson.Document;
+
+import com.mongodb.client.MongoCollection;
+import com.redhat.thermostat.gateway.common.mongodb.ThermostatFields;
+import com.redhat.thermostat.gateway.common.mongodb.executor.MongoDataResultContainer;
+import com.redhat.thermostat.gateway.common.mongodb.executor.MongoExecutor;
+import com.redhat.thermostat.gateway.common.mongodb.filters.MongoSortFilters;
+import com.redhat.thermostat.gateway.common.mongodb.response.ArgumentRunnable;
+import com.redhat.thermostat.gateway.common.mongodb.response.MongoMetaDataGenerator;
+import com.redhat.thermostat.gateway.common.mongodb.response.MongoMetaDataResponseBuilder;
+import com.redhat.thermostat.gateway.common.mongodb.response.MongoResponseBuilder;
+import com.redhat.thermostat.gateway.common.mongodb.servlet.RequestParameters;
+
+public class JvmGcMongoStorageHandler {
+
+    private static final String LONG_KEY = "$numberLong";
+    private final MongoExecutor mongoExecutor = new MongoExecutor();
+
+    public String getJvmGcDelta(MongoCollection<Document> collection,
+                                String jvmId, int limit, int offset,
+                                Long afterTimeStamp, Long beforeTimeStamp, boolean metadata,
+                                HttpServletRequest httpServletRequest, Set<String> realms) throws IOException {
+        final String descendingSort = MongoSortFilters.DESCENDING_SORT + ThermostatFields.TIMESTAMP;
+
+        List<String> queries = new ArrayList<>();
+        queries.add(ThermostatFields.JVM_ID + "==" + jvmId);
+
+        if (afterTimeStamp != null) {
+            queries.add(ThermostatFields.TIMESTAMP + ">" + afterTimeStamp);
+        }
+        if (beforeTimeStamp != null) {
+            queries.add(ThermostatFields.TIMESTAMP + "<" + beforeTimeStamp);
+        }
+
+        MongoDataResultContainer execResult = mongoExecutor.execGetRequest(
+                collection, limit, offset, descendingSort, queries, null, null, realms);
+
+        MongoResponseBuilder.Builder response = new MongoResponseBuilder.Builder();
+
+        final Map<String, Document> previous = new HashMap<>();
+        ArgumentRunnable<Document> runnable = new ArgumentRunnable<Document>() {
+            @Override
+            public void run(Document current) {
+                if (current.containsKey(Fields.WALL_TIME) && current.containsKey(Fields.COLLECTOR_NAME)) {
+                    String collectorName = current.getString(Fields.COLLECTOR_NAME);
+                    if (previous.containsKey(collectorName)) {
+                        Document pre = previous.get(collectorName);
+                        long prevWall = pre.get(Fields.WALL_TIME, Long.class);
+                        long thisWall = current.get(Fields.WALL_TIME, Long.class);
+                        long wallTimeDelta = prevWall - thisWall;
+                        setWallTimeDelta(pre, wallTimeDelta);
+                        previous.put(collectorName, current);
+                    } else {
+                        previous.put(collectorName, current);
+                    }
+                }
+            }
+        };
+        response.addQueryDocuments(execResult.getQueryDataResult(), runnable);
+
+        // At this point, previous contains the last Documents for each collector
+        // in the list, which is sorted by timeStamp descending. Find the next
+        // Document in storage with timeStamp less than this last Document to calculate
+        // delta for the last Document. Set delta to 0 if there is no Document
+
+        for (Map.Entry<String, Document> item : previous.entrySet()) {
+            List<String> nextQuery = new ArrayList<>();
+
+            nextQuery.add(ThermostatFields.TIMESTAMP + "<" +
+                    item.getValue().get(ThermostatFields.TIMESTAMP, Long.class));
+            nextQuery.add(Fields.COLLECTOR_NAME + "==" + item.getKey());
+            nextQuery.add(ThermostatFields.JVM_ID + "==" + jvmId);
+
+            MongoDataResultContainer execLastResult = mongoExecutor.execGetRequest(
+                    collection, 1, 0, descendingSort, nextQuery, null, null, realms);
+
+            Document first = execLastResult.getQueryDataResult().first();
+
+            long wallTimeDelta = 0;
+            if (first != null) {
+                long prevWall = item.getValue().get(Fields.WALL_TIME, Long.class);
+                long lastWall = first.get(Fields.WALL_TIME, Long.class);
+                wallTimeDelta = prevWall - lastWall;
+            }
+            setWallTimeDelta(item.getValue(), wallTimeDelta);
+        }
+
+        if (metadata) {
+            setMetadata(limit, offset, descendingSort, null, null, null, httpServletRequest, execResult, response);
+        }
+
+        return response.build();
+    }
+
+    private void setMetadata(int limit, int offset, String sort, String queries, String includes, String excludes, HttpServletRequest httpServletRequest,
+                             MongoDataResultContainer execResult, MongoResponseBuilder.Builder response) {
+        MongoMetaDataResponseBuilder.MetaBuilder metaDataResponse = new MongoMetaDataResponseBuilder.MetaBuilder();
+
+        LinkedHashMap<String, String> paramArgs = new LinkedHashMap<>();
+        paramArgs.put(RequestParameters.SORT, sort);
+        paramArgs.put(RequestParameters.QUERY, queries);
+        paramArgs.put(RequestParameters.INCLUDE, includes);
+        paramArgs.put(RequestParameters.EXCLUDE, excludes);
+        paramArgs.put(RequestParameters.METADATA, "true");
+        paramArgs.put(RequestParameters.LIMIT, String.valueOf(limit));
+        paramArgs.put(RequestParameters.OFFSET, String.valueOf(offset));
+        String baseUrl = httpServletRequest.getRequestURL().toString();
+
+        MongoMetaDataGenerator metaDataGenerator = new MongoMetaDataGenerator(limit, offset, sort, queries, includes, excludes, paramArgs, execResult, baseUrl);
+
+        metaDataGenerator.setDocAndPayloadCount(metaDataResponse);
+        metaDataGenerator.setPrev(metaDataResponse);
+        metaDataGenerator.setNext(metaDataResponse);
+
+        response.addMetaData(metaDataResponse.build());
+    }
+
+    private void setWallTimeDelta(Document toSet, long delta) {
+        Document d = new Document();
+        d.put(LONG_KEY, delta);
+        toSet.put(Fields.WALL_TIME_DELTA, d);
+    }
+}
--- a/services/jvm-gc/src/main/resources/jvm-gc-swagger.yaml	Wed Sep 06 13:11:55 2017 -0400
+++ b/services/jvm-gc/src/main/resources/jvm-gc-swagger.yaml	Thu Sep 07 09:07:07 2017 -0400
@@ -82,6 +82,21 @@
           description: OK
           schema:
             $ref: '#/definitions/metadata'
+  /delta/{jvmId}:
+    parameters:
+      - $ref: '#/parameters/jvm-id'
+    get:
+      description: Get jvm gc information for jvm {jvmId} with deltas for wallTime. Always sorted by timeStamp ascending
+      parameters:
+        - $ref: '#/parameters/limit'
+        - $ref: '#/parameters/offset'
+        - $ref: '#/parameters/before'
+        - $ref: '#/parameters/after'
+      responses:
+        '200':
+          description: OK
+          schema:
+            $ref: '#/definitions/jvm-gc-delta-response'
 definitions:
   jvm-gc-stats-response:
     type: object
@@ -107,8 +122,34 @@
         type: string
       runCount:
         $ref: '#/definitions/metric'
-      wallTime:
+      wallTimeInMicros:
         $ref: '#/definitions/metric'
+  jvm-gc-delta-response:
+    type: object
+    properties:
+      response:
+        $ref: '#/definitions/jvm-gc-delta-stats'
+  jvm-gc-delta-stats:
+    type: array
+    items:
+      $ref: '#/definitions/jvm-gc-delta-stat'
+  jvm-gc-delta-stat:
+      type: object
+      properties:
+        agentId:
+          type: string
+        jvmId:
+          type: string
+        timeStamp:
+          $ref: '#/definitions/timestamp'
+        collectorName:
+          type: string
+        runCount:
+          $ref: '#/definitions/metric'
+        wallTimeInMicros:
+          $ref: '#/definitions/metric'
+        wallTimeDelta:
+          $ref: '#/definitions/metric'
   metric:
     type: object
     properties:
@@ -165,6 +206,12 @@
     required: true
     schema:
       $ref: '#/definitions/jvm-gc-stats'
+  jvm-id:
+    name: jvmId
+    in: path
+    required: true
+    type: string
+    description: The ID of the jvm
   put-body:
     name: putBody
     in: body
@@ -214,6 +261,18 @@
       precedence over 'include' parameter
     type: string
     required: false
+  before:
+    name: b
+    in: query
+    description: Before timeStamp. A long that specifies to return documents before the timeStamp.
+    type: integer
+    format: int64
+  after:
+    name: a
+    in: query
+    description: After timeStamp. A long that specifies to return documents after the timeStamp.
+    type: integer
+    format: int64
   metadata:
     name: metadata
     type: boolean
--- a/tests/integration-tests/src/test/java/com/redhat/thermostat/gateway/service/jvm/gc/JvmGcServiceIntegrationTest.java	Wed Sep 06 13:11:55 2017 -0400
+++ b/tests/integration-tests/src/test/java/com/redhat/thermostat/gateway/service/jvm/gc/JvmGcServiceIntegrationTest.java	Thu Sep 07 09:07:07 2017 -0400
@@ -42,6 +42,7 @@
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
+import java.io.UnsupportedEncodingException;
 import java.lang.reflect.Type;
 import java.util.ArrayList;
 import java.util.List;
@@ -62,6 +63,7 @@
 import org.eclipse.jetty.client.util.StringContentProvider;
 import org.eclipse.jetty.http.HttpHeader;
 import org.eclipse.jetty.http.HttpMethod;
+import org.junit.Ignore;
 import org.junit.Test;
 
 import com.google.gson.Gson;
@@ -87,12 +89,12 @@
 
     private final String jsonData =
             "{\n" +
-            "   \"timeStamp\" : " + TIMESTAMP_TOKEN + ",\n" +
-            "   \"jvmId\" : " + JVMID_TOKEN + ",\n" +
-            "   \"collectorName\" : \"some-collection\",\n" +
-            "   \"runCount\" : \"22\",\n" +
-            "   \"wallTimeInMicros\" : \"333333\"\n" +
-            "}\n";
+                    "   \"timeStamp\" : " + TIMESTAMP_TOKEN + ",\n" +
+                    "   \"jvmId\" : " + JVMID_TOKEN + ",\n" +
+                    "   \"collectorName\" : \"some-collection\",\n" +
+                    "   \"runCount\" : \"22\",\n" +
+                    "   \"wallTimeInMicros\" : \"333333\"\n" +
+                    "}\n";
 
     private static final String QUERY_PREFIX = "query";
     private static final String LIMIT_PREFIX = "limit";
@@ -110,6 +112,8 @@
 
     private final String simpleUrl = baseUrl + "/" + serviceName + "/" + versionNumber;
     private final String serviceUrl = simpleUrl + "/systems/" + AGENT_ID + "/jvms/" + JVM_ID;
+    private final String deltaUrl = simpleUrl + "/delta";
+    private final String deltaJvmUrl = deltaUrl + "/" + JVM_ID;
     private final String SYSTEM_JVM_FRAGMENT = ",\"systemId\":\"" + AGENT_ID + "\",\"jvmId\":\"" + JVM_ID + "\"";
 
     public JvmGcServiceIntegrationTest() {
@@ -122,11 +126,15 @@
         ContentResponse response = client.newRequest(url).method(HttpMethod.GET).send();
 
         if (!expectedResponse.equals("")) {
-            assertEquals(expectedStatus, response.getStatus());
-            assertEquals(expectedResponse, response.getContentAsString());
+            verifyResponse(response, expectedResponse, expectedStatus);
         }
     }
 
+    private void verifyResponse(ContentResponse response, String expectedResponse, int expectedStatus) {
+        assertEquals(expectedStatus, response.getStatus());
+        assertEquals(expectedResponse, response.getContentAsString());
+    }
+
     private void makeHttpMethodRequest(HttpMethod httpMethod, String urlQuery, String dataToSend, String dataType,
                                        String expectedResponse, int expectedStatus)
             throws InterruptedException, TimeoutException, ExecutionException {
@@ -623,6 +631,252 @@
     }
 
     @Test
+    public void testGetDeltaWallTime() throws InterruptedException, ExecutionException, TimeoutException, UnsupportedEncodingException {
+        String data = "[{\"collectorName\" : \"CMS\", \"timeStamp\" : { \"$numberLong\" : \"600\" }," +
+                "\"wallTimeInMicros\": { \"$numberLong\" : \"212864\" } }," +
+                "{\"collectorName\" : \"CMS\", \"timeStamp\": { \"$numberLong\" : \"500\"}," +
+                "\"wallTimeInMicros\": { \"$numberLong\" : \"170887\"}}]";
+
+        makeHttpMethodRequest(HttpMethod.POST, "", data, "application/json", "", 200);
+
+        String expectedResponse = "{\"response\":[{\"collectorName\":\"CMS\",\"timeStamp\":600," +
+                "\"wallTimeInMicros\":212864" + SYSTEM_JVM_FRAGMENT + "," +
+                "\"wallTimeDelta\":{\"$numberLong\":41977}}]}";
+        ContentResponse response = client.newRequest(deltaJvmUrl).method(HttpMethod.GET).send();
+
+        verifyResponse(response, expectedResponse, 200);
+    }
+
+    @Test
+    public void testGetSingleDeltaWallTime() throws InterruptedException, ExecutionException, TimeoutException, UnsupportedEncodingException {
+        String data = "[{\"collectorName\" : \"CMS\", \"timeStamp\" : { \"$numberLong\" : \"600\" }," +
+                "\"wallTimeInMicros\": { \"$numberLong\" : \"212864\" } }]";
+
+        makeHttpMethodRequest(HttpMethod.POST, "", data, "application/json", "", 200);
+
+        String expectedResponse = "{\"response\":[{\"collectorName\":\"CMS\",\"timeStamp\":600," +
+                "\"wallTimeInMicros\":212864" + SYSTEM_JVM_FRAGMENT +  "," +
+                "\"wallTimeDelta\":{\"$numberLong\":0}}]}";
+        ContentResponse response = client.newRequest(deltaJvmUrl).method(HttpMethod.GET).param("l", "2").send();
+
+        verifyResponse(response, expectedResponse, 200);
+    }
+
+    @Test
+    public void testGetMultipleDeltaWallTime() throws InterruptedException, ExecutionException, TimeoutException, UnsupportedEncodingException {
+        String data = "[{\"collectorName\" : \"CMS\", \"timeStamp\" : { \"$numberLong\" : \"400\" }," +
+                "\"wallTimeInMicros\": { \"$numberLong\" : \"14000\" } }," +
+                "{\"collectorName\" : \"CMS\", \"timeStamp\" : { \"$numberLong\" : \"600\" }," +
+                "\"wallTimeInMicros\": { \"$numberLong\" : \"212864\" } }," +
+                "{\"collectorName\" : \"CMS\", \"timeStamp\": { \"$numberLong\" : \"500\"}," +
+                "\"wallTimeInMicros\": { \"$numberLong\" : \"170887\"}}]";
+
+        makeHttpMethodRequest(HttpMethod.POST, "", data, "application/json", "", 200);
+
+        String expectedResponse = "{\"response\":[{\"collectorName\":\"CMS\",\"timeStamp\":600," +
+                "\"wallTimeInMicros\":212864" + SYSTEM_JVM_FRAGMENT + "," +
+                "\"wallTimeDelta\":{\"$numberLong\":41977}},{\"collectorName\":\"CMS\"," +
+                "\"timeStamp\":500,\"wallTimeInMicros\":170887" + SYSTEM_JVM_FRAGMENT + "," +
+                "\"wallTimeDelta\":{\"$numberLong\":156887}},{\"collectorName\":\"CMS\"," +
+                "\"timeStamp\":400,\"wallTimeInMicros\":14000" + SYSTEM_JVM_FRAGMENT + "," +
+                "\"wallTimeDelta\":{\"$numberLong\":0}}]}";
+        ContentResponse response = client.newRequest(deltaJvmUrl).method(HttpMethod.GET).param("l", "10").send();
+
+        verifyResponse(response, expectedResponse, 200);
+    }
+
+    @Test
+    public void testGetDeltaWallTimeWithOffset() throws InterruptedException, ExecutionException, TimeoutException, UnsupportedEncodingException {
+        String data = "[{\"collectorName\" : \"CMS\", \"timeStamp\" : { \"$numberLong\" : \"400\" }," +
+                "\"wallTimeInMicros\": { \"$numberLong\" : \"14000\" } }," +
+                "{\"collectorName\" : \"CMS\", \"timeStamp\" : { \"$numberLong\" : \"600\" }," +
+                "\"wallTimeInMicros\": { \"$numberLong\" : \"212864\" } }," +
+                "{\"collectorName\" : \"CMS\", \"timeStamp\": { \"$numberLong\" : \"500\"}," +
+                "\"wallTimeInMicros\": { \"$numberLong\" : \"170887\"}}]";
+
+        makeHttpMethodRequest(HttpMethod.POST, "", data, "application/json", "", 200);
+        String offset = "1";
+        String expectedResponse = "{\"response\":[{\"collectorName\":\"CMS\",\"timeStamp\":500," +
+                "\"wallTimeInMicros\":170887" + SYSTEM_JVM_FRAGMENT + "," +
+                "\"wallTimeDelta\":{\"$numberLong\":156887}}]}";
+        ContentResponse response = client.newRequest(deltaJvmUrl).method(HttpMethod.GET).param("l", "1").param("o", offset).send();
+
+        verifyResponse(response, expectedResponse, 200);
+
+        offset = "2";
+        expectedResponse = "{\"response\":[{\"collectorName\":\"CMS\",\"timeStamp\":400," +
+                "\"wallTimeInMicros\":14000" + SYSTEM_JVM_FRAGMENT + "," +
+                "\"wallTimeDelta\":{\"$numberLong\":0}}]}";
+        response = client.newRequest(deltaJvmUrl).method(HttpMethod.GET).param("l", "1").param("o", offset).send();
+
+        verifyResponse(response, expectedResponse, 200);
+    }
+
+    @Test
+    public void testGetDeltaWallTimeWithAfter() throws InterruptedException, ExecutionException, TimeoutException, UnsupportedEncodingException {
+        String data = "[{\"collectorName\" : \"CMS\", \"timeStamp\" : { \"$numberLong\" : \"400\" }," +
+                "\"wallTimeInMicros\": { \"$numberLong\" : \"14000\" } }," +
+                "{\"collectorName\" : \"CMS\", \"timeStamp\" : { \"$numberLong\" : \"600\" }," +
+                "\"wallTimeInMicros\": { \"$numberLong\" : \"212864\" } }," +
+                "{\"collectorName\" : \"CMS\", \"timeStamp\": { \"$numberLong\" : \"500\"}," +
+                "\"wallTimeInMicros\": { \"$numberLong\" : \"170887\"}}]";
+
+        makeHttpMethodRequest(HttpMethod.POST, "", data, "application/json", "", 200);
+
+        String expectedResponse = "{\"response\":[{\"collectorName\":\"CMS\",\"timeStamp\":600," +
+                "\"wallTimeInMicros\":212864" + SYSTEM_JVM_FRAGMENT + "," +
+                "\"wallTimeDelta\":{\"$numberLong\":41977}},{\"collectorName\":\"CMS\"," +
+                "\"timeStamp\":500,\"wallTimeInMicros\":170887" + SYSTEM_JVM_FRAGMENT + "," +
+                "\"wallTimeDelta\":{\"$numberLong\":156887}}]}";
+        ContentResponse response = client.newRequest(deltaJvmUrl).method(HttpMethod.GET).param("l", "10").param("a", "400").send();
+
+        verifyResponse(response, expectedResponse, 200);
+    }
+
+    @Test
+    public void testGetDeltaWallTimeWithOffsetAndAfter() throws InterruptedException, ExecutionException, TimeoutException, UnsupportedEncodingException {
+        String data = "[{\"collectorName\" : \"CMS\", \"timeStamp\" : { \"$numberLong\" : \"400\" }," +
+                "\"wallTimeInMicros\": { \"$numberLong\" : \"14000\" } }," +
+                "{\"collectorName\" : \"CMS\", \"timeStamp\" : { \"$numberLong\" : \"600\" }," +
+                "\"wallTimeInMicros\": { \"$numberLong\" : \"212864\" } }," +
+                "{\"collectorName\" : \"CMS\", \"timeStamp\": { \"$numberLong\" : \"500\"}," +
+                "\"wallTimeInMicros\": { \"$numberLong\" : \"170887\"}}]";
+
+        makeHttpMethodRequest(HttpMethod.POST, "", data, "application/json", "", 200);
+
+        String expectedResponse = "{\"response\":[{\"collectorName\":\"CMS\",\"timeStamp\":500," +
+                "\"wallTimeInMicros\":170887" + SYSTEM_JVM_FRAGMENT + "," +
+                "\"wallTimeDelta\":{\"$numberLong\":156887}}]}";
+        ContentResponse response = client.newRequest(deltaJvmUrl).method(HttpMethod.GET).param("l", "10")
+                .param("a", "400").param("o", "1").send();
+
+        verifyResponse(response, expectedResponse, 200);
+    }
+
+    @Test
+    public void testGetDeltaWallTimeWithBefore() throws InterruptedException, ExecutionException, TimeoutException, UnsupportedEncodingException {
+        String data = "[{\"collectorName\" : \"CMS\", \"timeStamp\" : { \"$numberLong\" : \"400\" }," +
+                "\"wallTimeInMicros\": { \"$numberLong\" : \"14000\" } }," +
+                "{\"collectorName\" : \"CMS\", \"timeStamp\" : { \"$numberLong\" : \"600\" }," +
+                "\"wallTimeInMicros\": { \"$numberLong\" : \"212864\" } }," +
+                "{\"collectorName\" : \"CMS\", \"timeStamp\": { \"$numberLong\" : \"500\"}," +
+                "\"wallTimeInMicros\": { \"$numberLong\" : \"170887\"}}]";
+
+        makeHttpMethodRequest(HttpMethod.POST, "", data, "application/json", "", 200);
+
+        String expectedResponse = "{\"response\":[{\"collectorName\":\"CMS\",\"timeStamp\":400," +
+                "\"wallTimeInMicros\":14000" + SYSTEM_JVM_FRAGMENT + "," +
+                "\"wallTimeDelta\":{\"$numberLong\":0}}]}";
+        ContentResponse response = client.newRequest(deltaJvmUrl).method(HttpMethod.GET).param("l", "10")
+                .param("b", "500").send();
+
+        verifyResponse(response, expectedResponse, 200);
+    }
+
+    @Test
+    public void testGetDeltaWallTimeWithBeforeAndAfter() throws InterruptedException, ExecutionException, TimeoutException, UnsupportedEncodingException {
+        String data = "[{\"collectorName\" : \"CMS\", \"timeStamp\" : { \"$numberLong\" : \"400\" }," +
+                "\"wallTimeInMicros\": { \"$numberLong\" : \"14000\" } }," +
+                "{\"collectorName\" : \"CMS\", \"timeStamp\" : { \"$numberLong\" : \"600\" }," +
+                "\"wallTimeInMicros\": { \"$numberLong\" : \"212864\" } }," +
+                "{\"collectorName\" : \"CMS\", \"timeStamp\": { \"$numberLong\" : \"500\"}," +
+                "\"wallTimeInMicros\": { \"$numberLong\" : \"170887\"}}]";
+
+        makeHttpMethodRequest(HttpMethod.POST, "", data, "application/json", "", 200);
+
+        String expectedResponse = "{\"response\":[{\"collectorName\":\"CMS\",\"timeStamp\":500,\"wallTimeInMicros\":170887" + SYSTEM_JVM_FRAGMENT + ",\"wallTimeDelta\":{\"$numberLong\":156887}}]}";
+        ContentResponse response = client.newRequest(deltaJvmUrl).method(HttpMethod.GET).param("l", "10")
+                .param("b", "600").param("a", "400").send();
+
+        verifyResponse(response, expectedResponse, 200);
+    }
+
+    @Test
+    public void testGetDeltaWallTimeWithMultipleCollectors() throws InterruptedException, ExecutionException, TimeoutException, UnsupportedEncodingException {
+        String data = "[{\"collectorName\" : \"CMS\", \"timeStamp\" : { \"$numberLong\" : \"400\" }," +
+                "\"wallTimeInMicros\": { \"$numberLong\" : \"14000\" } }," +
+                "{\"collectorName\" : \"CMS\", \"timeStamp\" : { \"$numberLong\" : \"600\" }," +
+                "\"wallTimeInMicros\": { \"$numberLong\" : \"212864\" } }," +
+                "{\"collectorName\" : \"CMS\", \"timeStamp\": { \"$numberLong\" : \"500\"}," +
+                "\"wallTimeInMicros\": { \"$numberLong\" : \"170887\"} }," +
+                "{\"collectorName\" : \"ABC\", \"timeStamp\" : { \"$numberLong\" : \"440\" }," +
+                "\"wallTimeInMicros\": { \"$numberLong\" : \"500\" } }," +
+                "{\"collectorName\" : \"ABC\", \"timeStamp\" : { \"$numberLong\" : \"660\" }," +
+                "\"wallTimeInMicros\": { \"$numberLong\" : \"2000\" } }," +
+                "{\"collectorName\" : \"ABC\", \"timeStamp\": { \"$numberLong\" : \"550\"}," +
+                "\"wallTimeInMicros\": { \"$numberLong\" : \"1000\"}}]";
+
+        makeHttpMethodRequest(HttpMethod.POST, "", data, "application/json", "", 200);
+
+        String expectedResponse = "{\"response\":[{\"collectorName\":\"ABC\",\"timeStamp\":660," +
+                "\"wallTimeInMicros\":2000" + SYSTEM_JVM_FRAGMENT + "," +
+                "\"wallTimeDelta\":{\"$numberLong\":1000}},{\"collectorName\":\"CMS\",\"timeStamp\":600," +
+                "\"wallTimeInMicros\":212864" + SYSTEM_JVM_FRAGMENT + "," +
+                "\"wallTimeDelta\":{\"$numberLong\":41977}},{\"collectorName\":\"ABC\",\"timeStamp\":550," +
+                "\"wallTimeInMicros\":1000" + SYSTEM_JVM_FRAGMENT + "," +
+                "\"wallTimeDelta\":{\"$numberLong\":500}}]}";
+        ContentResponse response = client.newRequest(deltaJvmUrl).method(HttpMethod.GET).param("l", "10")
+                .param("a", "500").send();
+
+        verifyResponse(response, expectedResponse, 200);
+    }
+
+    @Test
+    @Ignore
+    public void testGetDeltaWallTimeWithMultipleJvms() throws InterruptedException, ExecutionException, TimeoutException, UnsupportedEncodingException {
+        String data = "[{\"jvmId\" : \"jvm-X\",\"collectorName\" : \"CMS\", \"timeStamp\" : { \"$numberLong\" : \"400\" }," +
+                "\"wallTimeInMicros\": { \"$numberLong\" : \"14000\" } }," +
+                "{\"jvmId\" : \"jvm-X\",\"collectorName\" : \"CMS\", \"timeStamp\" : { \"$numberLong\" : \"600\" }," +
+                "\"wallTimeInMicros\": { \"$numberLong\" : \"212864\" } }," +
+                "{\"jvmId\" : \"jvm-X\",\"collectorName\" : \"CMS\", \"timeStamp\": { \"$numberLong\" : \"500\"}," +
+                "\"wallTimeInMicros\": { \"$numberLong\" : \"170887\"} }," +
+                "{\"jvmId\" : \"jvm-Y\",\"collectorName\" : \"CMS\", \"timeStamp\" : { \"$numberLong\" : \"440\" }," +
+                "\"wallTimeInMicros\": { \"$numberLong\" : \"500\" } }," +
+                "{\"jvmId\" : \"jvm-Y\",\"collectorName\" : \"CMS\", \"timeStamp\" : { \"$numberLong\" : \"660\" }," +
+                "\"wallTimeInMicros\": { \"$numberLong\" : \"2000\" } }," +
+                "{\"jvmId\" : \"jvm-Y\",\"collectorName\" : \"CMS\", \"timeStamp\": { \"$numberLong\" : \"550\"}," +
+                "\"wallTimeInMicros\": { \"$numberLong\" : \"1000\"}}]";
+
+        makeHttpMethodRequest(HttpMethod.POST, "", data, "application/json", "", 200);
+
+        /*
+        {"response":[{"jvmId":"jvm-X","collectorName":"CMS","timeStamp":600,
+        "wallTimeInMicros":212864,"wallTimeDelta":{"$numberLong":41977}},
+        {"jvmId":"jvm-X","collectorName":"CMS","timeStamp":500,
+        "wallTimeInMicros":170887,"wallTimeDelta":{"$numberLong":156887}},
+        {"jvmId":"jvm-X","collectorName":"CMS","timeStamp":400,
+        "wallTimeInMicros":14000,"wallTimeDelta":{"$numberLong":0}}]}
+         */
+        String expectedResponse = "{\"response\":[{\"jvmId\":\"jvm-X\",\"collectorName\":\"CMS\",\"timeStamp\":600," +
+                "\"wallTimeInMicros\":212864,\"wallTimeDelta\":{\"$numberLong\":41977}}," +
+                "{\"jvmId\":\"jvm-X\",\"collectorName\":\"CMS\",\"timeStamp\":500," +
+                "\"wallTimeInMicros\":170887,\"wallTimeDelta\":{\"$numberLong\":156887}}," +
+                "{\"jvmId\":\"jvm-X\",\"collectorName\":\"CMS\",\"timeStamp\":400," +
+                "\"wallTimeInMicros\":14000,\"wallTimeDelta\":{\"$numberLong\":0}}]}";
+        ContentResponse response = client.newRequest(deltaUrl + "/jvm-X").method(HttpMethod.GET).param("l", "10").send();
+        verifyResponse(response, expectedResponse, 200);
+
+        /*
+        {"response":[{"jvmId":"jvm-Y","collectorName":"CMS","timeStamp":660,
+        "wallTimeInMicros":2000,"wallTimeDelta":{"$numberLong":1000}},
+        {"jvmId":"jvm-Y","collectorName":"CMS","timeStamp":550,
+        "wallTimeInMicros":1000,"wallTimeDelta":{"$numberLong":500}},
+        {"jvmId":"jvm-Y","collectorName":"CMS","timeStamp":440,
+        "wallTimeInMicros":500,"wallTimeDelta":{"$numberLong":0}}]}
+         */
+        expectedResponse = "{\"response\":[{\"jvmId\":\"jvm-Y\",\"collectorName\":\"CMS\",\"timeStamp\":660," +
+                "\"wallTimeInMicros\":2000,\"wallTimeDelta\":{\"$numberLong\":1000}}," +
+                "{\"jvmId\":\"jvm-Y\",\"collectorName\":\"CMS\",\"timeStamp\":550," +
+                "\"wallTimeInMicros\":1000,\"wallTimeDelta\":{\"$numberLong\":500}}," +
+                "{\"jvmId\":\"jvm-Y\",\"collectorName\":\"CMS\",\"timeStamp\":440," +
+                "\"wallTimeInMicros\":500,\"wallTimeDelta\":{\"$numberLong\":0}}]}";
+        response = client.newRequest(deltaUrl + "/jvm-Y").method(HttpMethod.GET).param("l", "10").send();
+
+
+        verifyResponse(response, expectedResponse, 200);
+    }
+
+    @Test
     public void testGetCannotSeeRealms() throws InterruptedException, ExecutionException, TimeoutException {
         String data = "[{\"item\":1},{\"item\":2}]";
         makeHttpMethodRequest(HttpMethod.POST, NO_QUERY, data, "application/json", NO_EXPECTED_RESPONSE, 200);