changeset 2617:f06bab050620

Add vm-shenandoah plugin Provides a shenandoahvisualizer-based visualization of memory regions and GC status for Shenandoah. See http://icedtea.classpath.org/people/rkennke/shenandoahvisualizer Reviewed-by: jkang Review-thread: http://icedtea.classpath.org/pipermail/thermostat/2017-March/022332.html
author Andrew Azores <aazores@redhat.com>
date Mon, 06 Mar 2017 09:38:30 -0500
parents 3572e808e4cf
children 0fe78675d94b
files distribution/assembly/plugin-assembly-agent.xml distribution/assembly/plugin-assembly.xml distribution/packaging/fedora/0002_shared_osgi_spec_fixes.patch distribution/pom.xml pom.xml storage/mongo/src/main/java/com/redhat/thermostat/storage/mongodb/internal/LongArrayCodec.java storage/mongo/src/main/java/com/redhat/thermostat/storage/mongodb/internal/MongoConnection.java storage/mongo/src/main/java/com/redhat/thermostat/storage/mongodb/internal/MongoPojoConverter.java storage/mongo/src/test/java/com/redhat/thermostat/storage/mongodb/internal/LongArrayCodecTest.java vm-shenandoah/agent/pom.xml vm-shenandoah/agent/src/main/java/com/redhat/thermostat/vm/shenandoah/agent/internal/Activator.java vm-shenandoah/agent/src/main/java/com/redhat/thermostat/vm/shenandoah/agent/internal/VmShenandoahBackend.java vm-shenandoah/agent/src/main/java/com/redhat/thermostat/vm/shenandoah/agent/internal/VmShenandoahDataExtractor.java vm-shenandoah/agent/src/main/java/com/redhat/thermostat/vm/shenandoah/agent/internal/VmShenandoahListener.java vm-shenandoah/agent/src/test/java/com/redhat/thermostat/vm/shenandoah/agent/internal/ActivatorTest.java vm-shenandoah/agent/src/test/java/com/redhat/thermostat/vm/shenandoah/agent/internal/VmShenandoahBackendTest.java vm-shenandoah/agent/src/test/java/com/redhat/thermostat/vm/shenandoah/agent/internal/VmShenandoahDataExtractorTest.java vm-shenandoah/agent/src/test/java/com/redhat/thermostat/vm/shenandoah/agent/internal/VmShenandoahListenerTest.java vm-shenandoah/client-core/pom.xml vm-shenandoah/client-core/src/main/java/com/redhat/thermostat/vm/shenandoah/client/core/VmShenandoahService.java vm-shenandoah/client-core/src/main/java/com/redhat/thermostat/vm/shenandoah/client/core/VmShenandoahView.java vm-shenandoah/client-core/src/main/java/com/redhat/thermostat/vm/shenandoah/client/core/VmShenandoahViewProvider.java vm-shenandoah/client-core/src/main/java/com/redhat/thermostat/vm/shenandoah/client/core/internal/Activator.java vm-shenandoah/client-core/src/main/java/com/redhat/thermostat/vm/shenandoah/client/core/internal/VmShenandoahController.java vm-shenandoah/client-core/src/main/java/com/redhat/thermostat/vm/shenandoah/client/core/internal/VmShenandoahServiceImpl.java vm-shenandoah/client-core/src/main/java/com/redhat/thermostat/vm/shenandoah/client/core/locale/LocaleResources.java vm-shenandoah/client-core/src/main/resources/com/redhat/thermostat/vm/shenandoah/client/core/locale/strings.properties vm-shenandoah/client-core/src/test/java/com/redhat/thermostat/vm/shenandoah/client/core/internal/ActivatorTest.java vm-shenandoah/client-core/src/test/java/com/redhat/thermostat/vm/shenandoah/client/core/internal/VmShenandoahControllerTest.java vm-shenandoah/client-core/src/test/java/com/redhat/thermostat/vm/shenandoah/client/core/internal/VmShenandoahServiceImplTest.java vm-shenandoah/client-core/src/test/java/com/redhat/thermostat/vm/shenandoah/client/core/locale/LocaleResourcesTest.java vm-shenandoah/client-swing/pom.xml vm-shenandoah/client-swing/src/main/java/com/redhat/thermostat/vm/shenandoah/client/swing/internal/Activator.java vm-shenandoah/client-swing/src/main/java/com/redhat/thermostat/vm/shenandoah/client/swing/internal/Colors.java vm-shenandoah/client-swing/src/main/java/com/redhat/thermostat/vm/shenandoah/client/swing/internal/RegionFlag.java vm-shenandoah/client-swing/src/main/java/com/redhat/thermostat/vm/shenandoah/client/swing/internal/RegionStat.java vm-shenandoah/client-swing/src/main/java/com/redhat/thermostat/vm/shenandoah/client/swing/internal/ShenandoahVisualizer.java vm-shenandoah/client-swing/src/main/java/com/redhat/thermostat/vm/shenandoah/client/swing/internal/Snapshot.java vm-shenandoah/client-swing/src/main/java/com/redhat/thermostat/vm/shenandoah/client/swing/internal/SnapshotView.java vm-shenandoah/client-swing/src/main/java/com/redhat/thermostat/vm/shenandoah/client/swing/internal/SwingVmShenandoahView.java vm-shenandoah/client-swing/src/main/java/com/redhat/thermostat/vm/shenandoah/client/swing/internal/SwingVmShenandoahViewProvider.java vm-shenandoah/client-swing/src/main/java/com/redhat/thermostat/vm/shenandoah/client/swing/internal/locale/LocaleResources.java vm-shenandoah/client-swing/src/main/resources/com/redhat/thermostat/vm/shenandoah/client/swing/internal/locale/strings.properties vm-shenandoah/client-swing/src/test/java/com/redhat/thermostat/vm/shenandoah/client/swing/internal/ActivatorTest.java vm-shenandoah/client-swing/src/test/java/com/redhat/thermostat/vm/shenandoah/client/swing/internal/RegionStatTest.java vm-shenandoah/client-swing/src/test/java/com/redhat/thermostat/vm/shenandoah/client/swing/internal/SwingVmShenandoahViewProviderTest.java vm-shenandoah/client-swing/src/test/java/com/redhat/thermostat/vm/shenandoah/client/swing/internal/locale/LocaleResourcesTest.java vm-shenandoah/common/pom.xml vm-shenandoah/common/src/main/java/com/redhat/thermostat/vm/shenandoah/common/VmShenandoahDao.java vm-shenandoah/common/src/main/java/com/redhat/thermostat/vm/shenandoah/common/VmShenandoahStat.java vm-shenandoah/common/src/main/java/com/redhat/thermostat/vm/shenandoah/common/internal/Activator.java vm-shenandoah/common/src/main/java/com/redhat/thermostat/vm/shenandoah/common/internal/VmShenandoahDaoImpl.java vm-shenandoah/common/src/main/java/com/redhat/thermostat/vm/shenandoah/common/internal/VmShenandoahDaoImplStatementDescriptorRegistration.java vm-shenandoah/common/src/main/java/com/redhat/thermostat/vm/shenandoah/common/internal/VmShenandoahStatCategoryRegistration.java vm-shenandoah/common/src/main/resources/META-INF/services/com.redhat.thermostat.storage.core.auth.CategoryRegistration vm-shenandoah/common/src/main/resources/META-INF/services/com.redhat.thermostat.storage.core.auth.StatementDescriptorRegistration vm-shenandoah/common/src/test/java/com/redhat/thermostat/vm/shenandoah/common/internal/ActivatorTest.java vm-shenandoah/common/src/test/java/com/redhat/thermostat/vm/shenandoah/common/internal/VmShenandoahDaoImplStatementDescriptorRegistrationTest.java vm-shenandoah/common/src/test/java/com/redhat/thermostat/vm/shenandoah/common/internal/VmShenandoahDaoImplTest.java vm-shenandoah/common/src/test/java/com/redhat/thermostat/vm/shenandoah/common/internal/VmShenandoahStatCategoryRegistrationTest.java vm-shenandoah/distribution/pom.xml vm-shenandoah/distribution/thermostat-plugin.xml vm-shenandoah/pom.xml web/war/pom.xml
diffstat 64 files changed, 5149 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- a/distribution/assembly/plugin-assembly-agent.xml	Thu Mar 09 13:36:40 2017 -0500
+++ b/distribution/assembly/plugin-assembly-agent.xml	Mon Mar 06 09:38:30 2017 -0500
@@ -65,6 +65,7 @@
         <include>com.redhat.thermostat:thermostat-vm-profiler-distribution</include>
         <include>com.redhat.thermostat:thermostat-killvm-distribution</include>
         <include>com.redhat.thermostat:thermostat-vm-numa-distribution</include>
+        <include>com.redhat.thermostat:thermostat-vm-shenandoah-distribution</include>
       </includes>
     </dependencySet>  
   </dependencySets>
--- a/distribution/assembly/plugin-assembly.xml	Thu Mar 09 13:36:40 2017 -0500
+++ b/distribution/assembly/plugin-assembly.xml	Mon Mar 06 09:38:30 2017 -0500
@@ -80,6 +80,7 @@
         <include>com.redhat.thermostat:thermostat-platform-swing-distribution</include>
         <include>com.redhat.thermostat:thermostat-dependency-tool-distribution</include>
         <include>com.redhat.thermostat:thermostat-gui-distribution</include>
+        <include>com.redhat.thermostat:thermostat-vm-shenandoah-distribution</include>
       </includes>
     </dependencySet>  
   </dependencySets>
--- a/distribution/packaging/fedora/0002_shared_osgi_spec_fixes.patch	Thu Mar 09 13:36:40 2017 -0500
+++ b/distribution/packaging/fedora/0002_shared_osgi_spec_fixes.patch	Mon Mar 06 09:38:30 2017 -0500
@@ -2258,6 +2258,86 @@
      </dependency>
      <dependency>
        <groupId>com.redhat.thermostat</groupId>
+diff --git a/vm-shenandoah/agent/pom.xml b/vm-shenandoah/agent/pom.xml
+--- a/vm-shenandoah/agent/pom.xml
++++ b/vm-shenandoah/agent/pom.xml
+@@ -82,14 +82,8 @@
+       <scope>test</scope>
+     </dependency>
+     <dependency>
+-      <groupId>org.osgi</groupId>
+-      <artifactId>org.osgi.core</artifactId>
+-      <scope>provided</scope>
+-    </dependency>
+-    <dependency>
+-      <groupId>org.osgi</groupId>
+-      <artifactId>org.osgi.compendium</artifactId>
+-      <scope>provided</scope>
++      <groupId>org.apache.felix</groupId>
++      <artifactId>org.apache.felix.framework</artifactId>
+     </dependency>
+     <dependency>
+       <groupId>com.redhat.thermostat</groupId>
+diff --git a/vm-shenandoah/client-core/pom.xml b/vm-shenandoah/client-core/pom.xml
+--- a/vm-shenandoah/client-core/pom.xml
++++ b/vm-shenandoah/client-core/pom.xml
+@@ -89,14 +89,8 @@
+       <scope>test</scope>
+     </dependency>
+     <dependency>
+-      <groupId>org.osgi</groupId>
+-      <artifactId>org.osgi.core</artifactId>
+-      <scope>provided</scope>
+-    </dependency>
+-    <dependency>
+-      <groupId>org.osgi</groupId>
+-      <artifactId>org.osgi.compendium</artifactId>
+-      <scope>provided</scope>
++      <groupId>org.apache.felix</groupId>
++      <artifactId>org.apache.felix.framework</artifactId>
+     </dependency>
+     <dependency>
+       <groupId>com.redhat.thermostat</groupId>
+diff --git a/vm-shenandoah/client-swing/pom.xml b/vm-shenandoah/client-swing/pom.xml
+--- a/vm-shenandoah/client-swing/pom.xml
++++ b/vm-shenandoah/client-swing/pom.xml
+@@ -99,14 +99,8 @@
+       <scope>test</scope>
+     </dependency>
+     <dependency>
+-      <groupId>org.osgi</groupId>
+-      <artifactId>org.osgi.core</artifactId>
+-      <scope>provided</scope>
+-    </dependency>
+-    <dependency>
+-      <groupId>org.osgi</groupId>
+-      <artifactId>org.osgi.compendium</artifactId>
+-      <scope>provided</scope>
++      <groupId>org.apache.felix</groupId>
++      <artifactId>org.apache.felix.framework</artifactId>
+     </dependency>
+     <dependency>
+       <groupId>com.redhat.thermostat</groupId>
+diff --git a/vm-shenandoah/common/pom.xml b/vm-shenandoah/common/pom.xml
+--- a/vm-shenandoah/common/pom.xml
++++ b/vm-shenandoah/common/pom.xml
+@@ -102,14 +102,8 @@
+       <scope>test</scope>
+     </dependency>
+     <dependency>
+-      <groupId>org.osgi</groupId>
+-      <artifactId>org.osgi.core</artifactId>
+-      <scope>provided</scope>
+-    </dependency>
+-    <dependency>
+-      <groupId>org.osgi</groupId>
+-      <artifactId>org.osgi.compendium</artifactId>
+-      <scope>provided</scope>
++      <groupId>org.apache.felix</groupId>
++      <artifactId>org.apache.felix.framework</artifactId>
+     </dependency>
+     <dependency>
+       <groupId>com.redhat.thermostat</groupId>
 diff --git a/web/client/pom.xml b/web/client/pom.xml
 --- a/web/client/pom.xml
 +++ b/web/client/pom.xml
--- a/distribution/pom.xml	Thu Mar 09 13:36:40 2017 -0500
+++ b/distribution/pom.xml	Mon Mar 06 09:38:30 2017 -0500
@@ -751,6 +751,12 @@
     </dependency>
     <dependency>
       <groupId>com.redhat.thermostat</groupId>
+      <artifactId>thermostat-vm-shenandoah-distribution</artifactId>
+      <version>${project.version}</version>
+      <type>zip</type>
+    </dependency>
+    <dependency>
+      <groupId>com.redhat.thermostat</groupId>
       <artifactId>thermostat-web-endpoint-distribution</artifactId>
       <version>${project.version}</version>
       <type>zip</type>
--- a/pom.xml	Thu Mar 09 13:36:40 2017 -0500
+++ b/pom.xml	Mon Mar 06 09:38:30 2017 -0500
@@ -390,6 +390,7 @@
     <module>vm-io</module>
     <module>vm-jmx</module>
     <module>vm-profiler</module>
+    <module>vm-shenandoah</module>
     <module>notes</module>
     <module>numa</module>
     <module>vm-numa</module>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/storage/mongo/src/main/java/com/redhat/thermostat/storage/mongodb/internal/LongArrayCodec.java	Mon Mar 06 09:38:30 2017 -0500
@@ -0,0 +1,84 @@
+/*
+ * 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.storage.mongodb.internal;
+
+import org.bson.BsonReader;
+import org.bson.BsonType;
+import org.bson.BsonWriter;
+import org.bson.codecs.Codec;
+import org.bson.codecs.DecoderContext;
+import org.bson.codecs.EncoderContext;
+import org.bson.codecs.LongCodec;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class LongArrayCodec implements Codec<long[]> {
+
+    private final LongCodec primCodec = new LongCodec();
+
+    @Override
+    public void encode(BsonWriter writer, long[] value,
+                       EncoderContext encoderContext) {
+        writer.writeStartArray();
+        for (long i: value) {
+            primCodec.encode(writer, i, encoderContext);
+        }
+        writer.writeEndArray();
+    }
+
+    @Override
+    public Class<long[]> getEncoderClass() {
+        return long[].class;
+    }
+
+    @Override
+    public long[] decode(BsonReader reader, DecoderContext decoderContext) {
+        reader.readStartArray();
+        List<Long> values = new ArrayList<>();
+        while (reader.readBsonType() != BsonType.END_OF_DOCUMENT) {
+            long val = primCodec.decode(reader, decoderContext);
+            values.add(val);
+        }
+        reader.readEndArray();
+        long[] result = new long[values.size()];
+        for (int i = 0; i < values.size(); i++) {
+            result[i] = values.get(i);
+        }
+        return result;
+    }
+}
--- a/storage/mongo/src/main/java/com/redhat/thermostat/storage/mongodb/internal/MongoConnection.java	Thu Mar 09 13:36:40 2017 -0500
+++ b/storage/mongo/src/main/java/com/redhat/thermostat/storage/mongodb/internal/MongoConnection.java	Mon Mar 06 09:38:30 2017 -0500
@@ -151,6 +151,7 @@
         CodecRegistry arrayCodecRegistry = CodecRegistries.fromCodecs(
                                                 new DoubleArrayCodec(),
                                                 new IntegerArrayCodec(),
+                                                new LongArrayCodec(),
                                                 new StringArrayCodec());
         CodecRegistry ourCodecRegistry = CodecRegistries.fromRegistries(
                                                 defaultCodecRegistry,
--- a/storage/mongo/src/main/java/com/redhat/thermostat/storage/mongodb/internal/MongoPojoConverter.java	Thu Mar 09 13:36:40 2017 -0500
+++ b/storage/mongo/src/main/java/com/redhat/thermostat/storage/mongodb/internal/MongoPojoConverter.java	Mon Mar 06 09:38:30 2017 -0500
@@ -42,6 +42,7 @@
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.List;
 import java.util.Set;
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/storage/mongo/src/test/java/com/redhat/thermostat/storage/mongodb/internal/LongArrayCodecTest.java	Mon Mar 06 09:38:30 2017 -0500
@@ -0,0 +1,92 @@
+/*
+ * 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.storage.mongodb.internal;
+
+import org.bson.BsonReader;
+import org.bson.BsonWriter;
+import org.bson.codecs.DecoderContext;
+import org.bson.codecs.EncoderContext;
+import org.bson.json.JsonReader;
+import org.bson.json.JsonWriter;
+import org.junit.Test;
+
+import java.io.ByteArrayOutputStream;
+import java.io.PrintWriter;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+import static org.junit.matchers.JUnitMatchers.containsString;
+
+public class LongArrayCodecTest {
+
+    @Test
+    public void canEncodeLongArray() {
+        long[] values = new long[]{
+                333, Long.MAX_VALUE, -902
+        };
+        LongArrayCodec codec = new LongArrayCodec();
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        BsonWriter writer = new JsonWriter(new PrintWriter(baos));
+        EncoderContext ctxt = EncoderContext.builder().build();
+
+        writer.writeStartDocument();
+        writer.writeName("foo");
+        codec.encode(writer, values, ctxt);
+        writer.writeEndDocument();
+        writer.flush();
+        String json = new String(baos.toByteArray());
+        assertThat(json, containsString("\"foo\""));
+        assertThat(json, containsString("{ \"foo\" : [{ \"$numberLong\" : \"" + values[0] + "\" }, " +
+                "{ \"$numberLong\" : \"" + values[1] + "\" }, { \"$numberLong\" : \"" + values[2] + "\" }] }"));
+    }
+
+    @Test
+    public void canDecodeLongArray() {
+        String json = "{ \"foo\" : [{ \"$numberLong\" : \"333\" }, { \"$numberLong\" : \"9223372036854775807\" }, { \"$numberLong\" : \"-902\" }] }";
+        BsonReader reader = new JsonReader(json);
+        reader.readStartDocument();
+        String name = reader.readName();
+        assertEquals("foo", name);
+        LongArrayCodec codec = new LongArrayCodec();
+        long[] decoded = codec.decode(reader, DecoderContext.builder().build());
+        reader.readEndDocument();
+        assertEquals(333, decoded[0]);
+        assertEquals(Long.MAX_VALUE, decoded[1]);
+        assertEquals(-902, decoded[2]);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-shenandoah/agent/pom.xml	Mon Mar 06 09:38:30 2017 -0500
@@ -0,0 +1,121 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+ Copyright 2012-2017 Red Hat, Inc.
+
+ This file is part of Thermostat.
+
+ Thermostat is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published
+ by the Free Software Foundation; either version 2, or (at your
+ option) any later version.
+
+ Thermostat is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Thermostat; see the file COPYING.  If not see
+ <http://www.gnu.org/licenses/>.
+
+ Linking this code with other modules is making a combined work
+ based on this code.  Thus, the terms and conditions of the GNU
+ General Public License cover the whole combination.
+
+ As a special exception, the copyright holders of this code give
+ you permission to link this code with independent modules to
+ produce an executable, regardless of the license terms of these
+ independent modules, and to copy and distribute the resulting
+ executable under terms of your choice, provided that you also
+ meet, for each linked independent module, the terms and conditions
+ of the license of that module.  An independent module is a module
+ which is not derived from or based on this code.  If you modify
+ this code, you may extend this exception to your version of the
+ library, but you are not obligated to do so.  If you do not wish
+ to do so, delete this exception statement from your version.
+
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <artifactId>thermostat-vm-shenandoah</artifactId>
+    <groupId>com.redhat.thermostat</groupId>
+    <version>1.99.12-SNAPSHOT</version>
+  </parent>
+  <artifactId>thermostat-vm-shenandoah-agent</artifactId>
+  <packaging>bundle</packaging>
+  <name>Thermostat VM Shenandoah Agent plugin</name>
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.felix</groupId>
+        <artifactId>maven-bundle-plugin</artifactId>
+        <extensions>true</extensions>
+        <configuration>
+          <instructions>
+            <Bundle-Vendor>Red Hat, Inc.</Bundle-Vendor>
+            <Bundle-SymbolicName>com.redhat.thermostat.vm.shenandoah.agent</Bundle-SymbolicName>
+            <Bundle-Activator>com.redhat.thermostat.vm.shenandoah.agent.internal.Activator</Bundle-Activator>
+            <Export-Package>
+              com.redhat.thermostat.vm.shenandoah.agent,
+            </Export-Package>
+            <Private-Package>
+              com.redhat.thermostat.vm.shenandoah.agent.internal,
+            </Private-Package>
+            <!-- Do not autogenerate uses clauses in Manifests -->
+            <_nouses>true</_nouses>
+          </instructions>
+        </configuration>
+      </plugin>
+    </plugins>
+  </build>
+  <dependencies>
+    <dependency>
+      <groupId>junit</groupId>
+      <artifactId>junit</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.mockito</groupId>
+      <artifactId>mockito-core</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.osgi</groupId>
+      <artifactId>org.osgi.core</artifactId>
+      <scope>provided</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.osgi</groupId>
+      <artifactId>org.osgi.compendium</artifactId>
+      <scope>provided</scope>
+    </dependency>
+    <dependency>
+      <groupId>com.redhat.thermostat</groupId>
+      <artifactId>thermostat-agent-core</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>com.redhat.thermostat</groupId>
+      <artifactId>thermostat-common-core</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>com.redhat.thermostat</groupId>
+      <artifactId>thermostat-storage-core</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>com.redhat.thermostat</groupId>
+      <artifactId>thermostat-common-test</artifactId>
+      <version>${project.version}</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>com.redhat.thermostat</groupId>
+      <artifactId>thermostat-vm-shenandoah-common</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+  </dependencies>
+</project>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-shenandoah/agent/src/main/java/com/redhat/thermostat/vm/shenandoah/agent/internal/Activator.java	Mon Mar 06 09:38:30 2017 -0500
@@ -0,0 +1,91 @@
+/*
+ * Copyright 2012-2017 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.vm.shenandoah.agent.internal;
+
+import com.redhat.thermostat.agent.VmStatusListenerRegistrar;
+import com.redhat.thermostat.backend.Backend;
+import com.redhat.thermostat.backend.BackendService;
+import com.redhat.thermostat.common.MultipleServiceTracker;
+import com.redhat.thermostat.common.Version;
+import com.redhat.thermostat.storage.core.WriterID;
+import com.redhat.thermostat.vm.shenandoah.common.VmShenandoahDao;
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceRegistration;
+
+public class Activator implements BundleActivator {
+
+    private MultipleServiceTracker tracker;
+    private VmShenandoahBackend backend;
+    private ServiceRegistration registration;
+
+    @Override
+    public void start(final BundleContext bundleContext) throws Exception {
+        Class<?>[] deps = new Class<?>[] {
+                BackendService.class,
+                VmShenandoahDao.class,
+                WriterID.class
+        };
+
+        final VmStatusListenerRegistrar registrar = new VmStatusListenerRegistrar(bundleContext);
+
+        tracker = new MultipleServiceTracker(bundleContext, deps, new MultipleServiceTracker.Action() {
+            @Override
+            public void dependenciesAvailable(MultipleServiceTracker.DependencyProvider services) {
+                VmShenandoahDao dao = services.get(VmShenandoahDao.class);
+                Version version = new Version(bundleContext.getBundle());
+                WriterID writerID = services.get(WriterID.class);
+                backend = new VmShenandoahBackend(dao, version, registrar, writerID);
+                registration = bundleContext.registerService(Backend.class.getName(), backend, null);
+            }
+
+            @Override
+            public void dependenciesUnavailable() {
+                if (backend.isActive()) {
+                    backend.deactivate();
+                }
+                registration.unregister();
+            }
+        });
+        tracker.open();
+    }
+
+    @Override
+    public void stop(BundleContext bundleContext) throws Exception {
+        tracker.close();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-shenandoah/agent/src/main/java/com/redhat/thermostat/vm/shenandoah/agent/internal/VmShenandoahBackend.java	Mon Mar 06 09:38:30 2017 -0500
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2012-2017 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.vm.shenandoah.agent.internal;
+
+import com.redhat.thermostat.agent.VmStatusListenerRegistrar;
+import com.redhat.thermostat.backend.VmListenerBackend;
+import com.redhat.thermostat.backend.VmUpdateListener;
+import com.redhat.thermostat.common.Version;
+import com.redhat.thermostat.storage.core.WriterID;
+import com.redhat.thermostat.vm.shenandoah.common.VmShenandoahDao;
+
+public class VmShenandoahBackend extends VmListenerBackend {
+
+    private final VmShenandoahDao dao;
+
+    public VmShenandoahBackend(VmShenandoahDao dao, Version version,
+                               VmStatusListenerRegistrar registrar, WriterID writerID) {
+        super("VM Shenandoah Backend",
+                "Gathers Shenandoah-GC-specific statistics",
+                "Red Hat, Inc.", version.getVersionNumber(), true, registrar, writerID);
+        this.dao = dao;
+    }
+
+    @Override
+    protected VmUpdateListener createVmListener(String writerId, String vmId, int pid) {
+        return new VmShenandoahListener(writerId, vmId, dao);
+    }
+
+    @Override
+    public int getOrderValue() {
+        return ORDER_MEMORY_GROUP + 21;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-shenandoah/agent/src/main/java/com/redhat/thermostat/vm/shenandoah/agent/internal/VmShenandoahDataExtractor.java	Mon Mar 06 09:38:30 2017 -0500
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2012-2017 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.vm.shenandoah.agent.internal;
+
+import com.redhat.thermostat.backend.VmUpdate;
+import com.redhat.thermostat.backend.VmUpdateException;
+
+public class VmShenandoahDataExtractor {
+
+    private final VmUpdate vmUpdate;
+
+    public VmShenandoahDataExtractor(VmUpdate vmUpdate) {
+        this.vmUpdate = vmUpdate;
+    }
+
+    public long getTimestamp() throws VmUpdateException {
+        return vmUpdate.getPerformanceCounterLong("sun.gc.shenandoah.regions.timestamp");
+    }
+
+    public long getMaxRegions() throws VmUpdateException {
+        return vmUpdate.getPerformanceCounterLong("sun.gc.shenandoah.regions.max_regions");
+    }
+
+    public long getRegionSize() throws VmUpdateException {
+        return vmUpdate.getPerformanceCounterLong("sun.gc.shenandoah.regions.region_size");
+    }
+
+    public long getStatus() throws VmUpdateException {
+        return vmUpdate.getPerformanceCounterLong("sun.gc.shenandoah.regions.status");
+    }
+
+    public long[] getRegionData() throws VmUpdateException {
+        long[] data = new long[(int) getMaxRegions()];
+        for (int i = 0; i < data.length; i++) {
+            data[i] = vmUpdate.getPerformanceCounterLong("sun.gc.shenandoah.regions.region." + i + ".data");
+        }
+        return data;
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-shenandoah/agent/src/main/java/com/redhat/thermostat/vm/shenandoah/agent/internal/VmShenandoahListener.java	Mon Mar 06 09:38:30 2017 -0500
@@ -0,0 +1,90 @@
+/*
+ * Copyright 2012-2017 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.vm.shenandoah.agent.internal;
+
+import com.redhat.thermostat.backend.VmUpdate;
+import com.redhat.thermostat.backend.VmUpdateException;
+import com.redhat.thermostat.backend.VmUpdateListener;
+import com.redhat.thermostat.common.Clock;
+import com.redhat.thermostat.common.SystemClock;
+import com.redhat.thermostat.common.utils.LoggingUtils;
+import com.redhat.thermostat.vm.shenandoah.common.VmShenandoahDao;
+import com.redhat.thermostat.vm.shenandoah.common.VmShenandoahStat;
+
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+public class VmShenandoahListener implements VmUpdateListener {
+
+    private static final Logger logger = LoggingUtils.getLogger(VmShenandoahListener.class);
+
+    private final String writerID;
+    private final String vmId;
+    private final VmShenandoahDao dao;
+    private final Clock clock;
+    private boolean isActive = true;
+
+    public VmShenandoahListener(String writerID, String vmId, VmShenandoahDao dao) {
+        this(writerID, vmId, dao, new SystemClock());
+    }
+
+    // Package-private for testing
+    VmShenandoahListener(String writerID, String vmId, VmShenandoahDao dao, Clock clock) {
+        this.writerID = writerID;
+        this.vmId = vmId;
+        this.dao = dao;
+        this.clock = clock;
+    }
+
+    @Override
+    public void countersUpdated(VmUpdate update) {
+        if (!isActive) {
+            return;
+        }
+        try {
+            VmShenandoahDataExtractor extractor = new VmShenandoahDataExtractor(update);
+            VmShenandoahStat stat = new VmShenandoahStat(writerID, vmId, clock.getRealTimeMillis(),
+                    extractor.getTimestamp(), extractor.getMaxRegions(), extractor.getRegionSize(), extractor.getStatus(), extractor.getRegionData());
+            dao.putShenandoahStat(stat);
+        } catch (VmUpdateException e) {
+            logger.log(Level.WARNING, "Error gathering Shenandoah statistics for VM " + vmId, e);
+        } catch (NullPointerException e) {
+            // VM is not running Shenandoah
+            isActive = false;
+        }
+    }
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-shenandoah/agent/src/test/java/com/redhat/thermostat/vm/shenandoah/agent/internal/ActivatorTest.java	Mon Mar 06 09:38:30 2017 -0500
@@ -0,0 +1,107 @@
+/*
+ * Copyright 2012-2017 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.vm.shenandoah.agent.internal;
+
+import com.redhat.thermostat.backend.Backend;
+import com.redhat.thermostat.backend.BackendService;
+import com.redhat.thermostat.storage.core.WriterID;
+import com.redhat.thermostat.testutils.StubBundleContext;
+import com.redhat.thermostat.vm.shenandoah.common.VmShenandoahDao;
+import org.junit.Before;
+import org.junit.Test;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.Version;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotSame;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class ActivatorTest {
+
+    private StubBundleContext context;
+    private Activator activator;
+
+    @Before
+    public void setup() {
+        context = new StubBundleContext();
+        activator = new Activator();
+    }
+
+    @Test
+    public void testActivatorDoesNothingWithMissingDeps() throws Exception {
+        activator.start(context);
+
+        assertTrue(context.getAllServices().isEmpty());
+        assertNotSame(context.getServiceListeners().size(), 1);
+
+        activator.stop(context);
+
+        assertTrue(context.getAllServices().isEmpty());
+        assertTrue(context.getServiceListeners().isEmpty());
+    }
+
+    @Test
+    public void testRegistersBackend() throws Exception {
+        context = new StubBundleContext() {
+            @Override
+            public Bundle getBundle() {
+                Bundle result = mock(Bundle.class);
+                when(result.getVersion()).thenReturn(Version.emptyVersion);
+                return result;
+            }
+        };
+
+        BackendService backendService = mock(BackendService.class);
+        VmShenandoahDao vmShenandoahDao = mock(VmShenandoahDao.class);
+        WriterID writerID = mock(WriterID.class);
+
+        context.registerService(BackendService.class, backendService, null);
+        context.registerService(VmShenandoahDao.class, vmShenandoahDao, null);
+        context.registerService(WriterID.class, writerID, null);
+
+        activator.start(context);
+
+        assertTrue(context.isServiceRegistered(Backend.class.getName(), VmShenandoahBackend.class));
+
+        activator.stop(context);
+
+        assertFalse(context.isServiceRegistered(Backend.class.getName(), VmShenandoahBackend.class));
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-shenandoah/agent/src/test/java/com/redhat/thermostat/vm/shenandoah/agent/internal/VmShenandoahBackendTest.java	Mon Mar 06 09:38:30 2017 -0500
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2012-2017 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.vm.shenandoah.agent.internal;
+
+import com.redhat.thermostat.agent.VmStatusListenerRegistrar;
+import com.redhat.thermostat.common.Ordered;
+import com.redhat.thermostat.common.Version;
+import com.redhat.thermostat.storage.core.WriterID;
+import com.redhat.thermostat.vm.shenandoah.common.VmShenandoahDao;
+import org.junit.Before;
+import org.junit.Test;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class VmShenandoahBackendTest {
+
+    private VmShenandoahBackend backend;
+
+    @Before
+    public void setup() {
+        VmShenandoahDao dao = mock(VmShenandoahDao.class);
+
+        Version version = mock(Version.class);
+        when(version.getVersionNumber()).thenReturn("0.0.0");
+
+        VmStatusListenerRegistrar registrar = mock(VmStatusListenerRegistrar.class);
+
+        WriterID id = mock(WriterID.class);
+        backend = new VmShenandoahBackend(dao, version, registrar, id);
+    }
+
+    @Test
+    public void testOrderValue() {
+        int order = backend.getOrderValue();
+        assertTrue(order >= Ordered.ORDER_MEMORY_GROUP);
+        assertTrue(order < Ordered.ORDER_NETWORK_GROUP);
+    }
+
+    @Test
+    public void testCreateVmListener() {
+        assertNotNull(backend.createVmListener("foo-writerID", "foo-vmId", 100));
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-shenandoah/agent/src/test/java/com/redhat/thermostat/vm/shenandoah/agent/internal/VmShenandoahDataExtractorTest.java	Mon Mar 06 09:38:30 2017 -0500
@@ -0,0 +1,95 @@
+/*
+ * Copyright 2012-2017 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.vm.shenandoah.agent.internal;
+
+import com.redhat.thermostat.backend.VmUpdate;
+import com.redhat.thermostat.backend.VmUpdateException;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.InOrder;
+
+import static org.mockito.Mockito.inOrder;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+public class VmShenandoahDataExtractorTest {
+
+    private VmShenandoahDataExtractor extractor;
+    private VmUpdate update;
+
+    @Before
+    public void setup() {
+        update = mock(VmUpdate.class);
+        extractor = new VmShenandoahDataExtractor(update);
+    }
+
+    @Test
+    public void testGetTimestamp() throws VmUpdateException {
+        extractor.getTimestamp();
+        verify(update).getPerformanceCounterLong("sun.gc.shenandoah.regions.timestamp");
+    }
+
+    @Test
+    public void testGetMaxRegions() throws VmUpdateException {
+        extractor.getMaxRegions();
+        verify(update).getPerformanceCounterLong("sun.gc.shenandoah.regions.max_regions");
+    }
+
+    @Test
+    public void testGetRegionSize() throws VmUpdateException {
+        extractor.getRegionSize();
+        verify(update).getPerformanceCounterLong("sun.gc.shenandoah.regions.region_size");
+    }
+
+    @Test
+    public void testGetStatus() throws VmUpdateException {
+        extractor.getStatus();
+        verify(update).getPerformanceCounterLong("sun.gc.shenandoah.regions.status");
+    }
+
+    @Test
+    public void testGetRegionData() throws VmUpdateException {
+        when(update.getPerformanceCounterLong("sun.gc.shenandoah.regions.max_regions")).thenReturn(2L);
+        extractor.getRegionData();
+        InOrder inOrder = inOrder(update);
+        inOrder.verify(update).getPerformanceCounterLong("sun.gc.shenandoah.regions.max_regions");
+        inOrder.verify(update).getPerformanceCounterLong("sun.gc.shenandoah.regions.region.0.data");
+        inOrder.verify(update).getPerformanceCounterLong("sun.gc.shenandoah.regions.region.1.data");
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-shenandoah/agent/src/test/java/com/redhat/thermostat/vm/shenandoah/agent/internal/VmShenandoahListenerTest.java	Mon Mar 06 09:38:30 2017 -0500
@@ -0,0 +1,123 @@
+/*
+ * Copyright 2012-2017 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.vm.shenandoah.agent.internal;
+
+import com.redhat.thermostat.backend.VmUpdate;
+import com.redhat.thermostat.backend.VmUpdateException;
+import com.redhat.thermostat.common.Clock;
+import com.redhat.thermostat.vm.shenandoah.common.VmShenandoahDao;
+import com.redhat.thermostat.vm.shenandoah.common.VmShenandoahStat;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertThat;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.when;
+
+public class VmShenandoahListenerTest {
+
+    private String writerID;
+    private String vmId;
+    private VmShenandoahDao dao;
+    private Clock clock;
+    private VmUpdate vmUpdate;
+
+    private VmShenandoahListener listener;
+
+    @Before
+    public void setup() throws VmUpdateException {
+        writerID = "foo-writerID";
+        vmId = "foo-vmId";
+        dao = mock(VmShenandoahDao.class);
+        clock = mock(Clock.class);
+        when(clock.getRealTimeMillis()).thenReturn(100L);
+        vmUpdate = mock(VmUpdate.class);
+        when(vmUpdate.getPerformanceCounterLong("sun.gc.shenandoah.regions.timestamp")).thenReturn(50L);
+        when(vmUpdate.getPerformanceCounterLong("sun.gc.shenandoah.regions.max_regions")).thenReturn(2L);
+        when(vmUpdate.getPerformanceCounterLong("sun.gc.shenandoah.regions.region_size")).thenReturn(1024L);
+        when(vmUpdate.getPerformanceCounterLong("sun.gc.shenandoah.regions.status")).thenReturn(4L);
+        when(vmUpdate.getPerformanceCounterLong("sun.gc.shenandoah.regions.region.0.data")).thenReturn(10L);
+        when(vmUpdate.getPerformanceCounterLong("sun.gc.shenandoah.regions.region.1.data")).thenReturn(10L);
+
+        listener = new VmShenandoahListener(writerID, vmId, dao, clock);
+    }
+
+    @Test
+    public void testCountersUpdated() throws VmUpdateException {
+        listener.countersUpdated(vmUpdate);
+
+        verify(clock).getRealTimeMillis();
+        verify(vmUpdate).getPerformanceCounterLong("sun.gc.shenandoah.regions.timestamp");
+        verify(vmUpdate, times(2)).getPerformanceCounterLong("sun.gc.shenandoah.regions.max_regions");
+        verify(vmUpdate).getPerformanceCounterLong("sun.gc.shenandoah.regions.region_size");
+        verify(vmUpdate).getPerformanceCounterLong("sun.gc.shenandoah.regions.status");
+        verify(vmUpdate).getPerformanceCounterLong("sun.gc.shenandoah.regions.region.0.data");
+        verify(vmUpdate).getPerformanceCounterLong("sun.gc.shenandoah.regions.region.1.data");
+        verifyNoMoreInteractions(vmUpdate);
+
+        ArgumentCaptor<VmShenandoahStat> daoCaptor = ArgumentCaptor.forClass(VmShenandoahStat.class);
+        verify(dao).putShenandoahStat(daoCaptor.capture());
+        VmShenandoahStat stat = daoCaptor.getValue();
+        assertThat(stat.getTimeStamp(), is(100L));
+        assertThat(stat.getMaxRegions(), is(2L));
+        assertThat(stat.getRegionSize(), is(1024L));
+        assertThat(stat.getRegionStatus(), is(4L));
+        assertThat(stat.getRegionData(), is(equalTo(new long[]{10L, 10L})));
+        verifyNoMoreInteractions(dao);
+    }
+
+    @Test
+    public void testDisablesIfNotRunningShenandoah() throws VmUpdateException {
+        when(vmUpdate.getPerformanceCounterLong(anyString())).thenThrow(NullPointerException.class);
+        when(vmUpdate.getPerformanceCounterString(anyString())).thenThrow(NullPointerException.class);
+        listener.countersUpdated(vmUpdate);
+        verify(vmUpdate).getPerformanceCounterLong("sun.gc.shenandoah.regions.timestamp");
+        listener.countersUpdated(vmUpdate);
+        verify(vmUpdate).getPerformanceCounterLong("sun.gc.shenandoah.regions.timestamp"); // still just once
+
+        verifyZeroInteractions(dao);
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-shenandoah/client-core/pom.xml	Mon Mar 06 09:38:30 2017 -0500
@@ -0,0 +1,122 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+ Copyright 2012-2017 Red Hat, Inc.
+
+ This file is part of Thermostat.
+
+ Thermostat is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published
+ by the Free Software Foundation; either version 2, or (at your
+ option) any later version.
+
+ Thermostat is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Thermostat; see the file COPYING.  If not see
+ <http://www.gnu.org/licenses/>.
+
+ Linking this code with other modules is making a combined work
+ based on this code.  Thus, the terms and conditions of the GNU
+ General Public License cover the whole combination.
+
+ As a special exception, the copyright holders of this code give
+ you permission to link this code with independent modules to
+ produce an executable, regardless of the license terms of these
+ independent modules, and to copy and distribute the resulting
+ executable under terms of your choice, provided that you also
+ meet, for each linked independent module, the terms and conditions
+ of the license of that module.  An independent module is a module
+ which is not derived from or based on this code.  If you modify
+ this code, you may extend this exception to your version of the
+ library, but you are not obligated to do so.  If you do not wish
+ to do so, delete this exception statement from your version.
+
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <artifactId>thermostat-vm-shenandoah</artifactId>
+    <groupId>com.redhat.thermostat</groupId>
+    <version>1.99.12-SNAPSHOT</version>
+  </parent>
+  <artifactId>thermostat-vm-shenandoah-client-core</artifactId>
+  <packaging>bundle</packaging>
+  <name>Thermostat VM Shenandoah GC Core Client plugin</name>
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.felix</groupId>
+        <artifactId>maven-bundle-plugin</artifactId>
+        <extensions>true</extensions>
+        <configuration>
+          <instructions>
+            <Bundle-Vendor>Red Hat, Inc.</Bundle-Vendor>
+            <Bundle-SymbolicName>com.redhat.thermostat.vm.shenandoah.client.core</Bundle-SymbolicName>
+            <Bundle-Activator>com.redhat.thermostat.vm.shenandoah.client.core.internal.Activator</Bundle-Activator>
+            <Export-Package>
+              com.redhat.thermostat.vm.shenandoah.client.core,
+              com.redhat.thermostat.vm.shenandoah.client.core.locale,
+            </Export-Package>
+            <Private-Package>
+              com.redhat.thermostat.vm.shenandoah.client.core.internal
+            </Private-Package>
+            <!-- Do not autogenerate uses clauses in Manifests -->
+            <_nouses>true</_nouses>
+          </instructions>
+        </configuration>
+      </plugin>
+    </plugins>
+  </build>
+  <dependencies>
+    <dependency>
+      <groupId>junit</groupId>
+      <artifactId>junit</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>com.redhat.thermostat</groupId>
+      <artifactId>thermostat-common-test</artifactId>
+      <version>${project.version}</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.mockito</groupId>
+      <artifactId>mockito-core</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.osgi</groupId>
+      <artifactId>org.osgi.core</artifactId>
+      <scope>provided</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.osgi</groupId>
+      <artifactId>org.osgi.compendium</artifactId>
+      <scope>provided</scope>
+    </dependency>
+    <dependency>
+      <groupId>com.redhat.thermostat</groupId>
+      <artifactId>thermostat-common-core</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>com.redhat.thermostat</groupId>
+      <artifactId>thermostat-client-core</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>com.redhat.thermostat</groupId>
+      <artifactId>thermostat-storage-core</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>com.redhat.thermostat</groupId>
+      <artifactId>thermostat-vm-shenandoah-common</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+  </dependencies>
+</project>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-shenandoah/client-core/src/main/java/com/redhat/thermostat/vm/shenandoah/client/core/VmShenandoahService.java	Mon Mar 06 09:38:30 2017 -0500
@@ -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.vm.shenandoah.client.core;
+
+import com.redhat.thermostat.client.core.InformationService;
+import com.redhat.thermostat.storage.core.VmRef;
+
+public interface VmShenandoahService extends InformationService<VmRef> {
+    String SERVICE_ID = "com.redhat.thermostat.vm.shenandoah";
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-shenandoah/client-core/src/main/java/com/redhat/thermostat/vm/shenandoah/client/core/VmShenandoahView.java	Mon Mar 06 09:38:30 2017 -0500
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2012-2017 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.vm.shenandoah.client.core;
+
+import com.redhat.thermostat.client.core.views.BasicView;
+import com.redhat.thermostat.client.core.views.UIComponent;
+import com.redhat.thermostat.vm.shenandoah.common.VmShenandoahStat;
+
+public abstract class VmShenandoahView extends BasicView implements UIComponent {
+
+    public abstract void setEnabled(boolean enabled);
+
+    public abstract void setData(VmShenandoahStat vmShenandoahStat);
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-shenandoah/client-core/src/main/java/com/redhat/thermostat/vm/shenandoah/client/core/VmShenandoahViewProvider.java	Mon Mar 06 09:38:30 2017 -0500
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2012-2017 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.vm.shenandoah.client.core;
+
+import com.redhat.thermostat.annotations.ExtensionPoint;
+import com.redhat.thermostat.client.core.views.ViewProvider;
+
+@ExtensionPoint
+public interface VmShenandoahViewProvider extends ViewProvider {
+    @Override
+    VmShenandoahView createView();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-shenandoah/client-core/src/main/java/com/redhat/thermostat/vm/shenandoah/client/core/internal/Activator.java	Mon Mar 06 09:38:30 2017 -0500
@@ -0,0 +1,104 @@
+/*
+ * Copyright 2012-2017 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.vm.shenandoah.client.core.internal;
+
+import com.redhat.thermostat.client.core.InformationService;
+import com.redhat.thermostat.common.ApplicationService;
+import com.redhat.thermostat.common.Constants;
+import com.redhat.thermostat.common.MultipleServiceTracker;
+import com.redhat.thermostat.storage.core.VmRef;
+import com.redhat.thermostat.storage.dao.AgentInfoDAO;
+import com.redhat.thermostat.storage.dao.VmInfoDAO;
+import com.redhat.thermostat.vm.shenandoah.client.core.VmShenandoahService;
+import com.redhat.thermostat.vm.shenandoah.client.core.VmShenandoahViewProvider;
+import com.redhat.thermostat.vm.shenandoah.common.VmShenandoahDao;
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceRegistration;
+
+import java.util.Dictionary;
+import java.util.Hashtable;
+
+public class Activator implements BundleActivator {
+
+    private MultipleServiceTracker serviceTracker;
+    private ServiceRegistration registration;
+
+    @Override
+    public void start(final BundleContext context) throws Exception {
+        final Class<?>[] deps = new Class<?>[] {
+                ApplicationService.class,
+                AgentInfoDAO.class,
+                VmInfoDAO.class,
+                VmShenandoahDao.class,
+                VmShenandoahViewProvider.class
+        };
+
+        serviceTracker = new MultipleServiceTracker(context, deps, new MultipleServiceTracker.Action() {
+            @Override
+            public void dependenciesAvailable(MultipleServiceTracker.DependencyProvider services) {
+                ApplicationService appSvc = services.get(ApplicationService.class);
+                AgentInfoDAO agentInfoDAO = services.get(AgentInfoDAO.class);
+                VmInfoDAO vmInfoDAO = services.get(VmInfoDAO.class);
+                VmShenandoahDao vmShenandoahDao = services.get(VmShenandoahDao.class);
+                VmShenandoahViewProvider viewProvider = services.get(VmShenandoahViewProvider.class);
+
+                VmShenandoahService shenandoahService =
+                        new VmShenandoahServiceImpl(appSvc, agentInfoDAO, vmInfoDAO, vmShenandoahDao, viewProvider);
+                Dictionary<String, String> properties = new Hashtable<>();
+                properties.put(Constants.GENERIC_SERVICE_CLASSNAME, VmRef.class.getName());
+                properties.put(InformationService.KEY_SERVICE_ID, VmShenandoahService.SERVICE_ID);
+                registration = context.registerService(InformationService.class.getName(), shenandoahService, properties);
+            }
+
+            @Override
+            public void dependenciesUnavailable() {
+                if (registration != null) {
+                    registration.unregister();
+                }
+            }
+        });
+        serviceTracker.open();
+    }
+
+    @Override
+    public void stop(BundleContext context) throws Exception {
+        if (serviceTracker != null) {
+            serviceTracker.close();
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-shenandoah/client-core/src/main/java/com/redhat/thermostat/vm/shenandoah/client/core/internal/VmShenandoahController.java	Mon Mar 06 09:38:30 2017 -0500
@@ -0,0 +1,132 @@
+/*
+ * Copyright 2012-2017 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.vm.shenandoah.client.core.internal;
+
+import com.redhat.thermostat.client.core.controllers.InformationServiceController;
+import com.redhat.thermostat.client.core.views.BasicView;
+import com.redhat.thermostat.client.core.views.UIComponent;
+import com.redhat.thermostat.common.ActionEvent;
+import com.redhat.thermostat.common.ActionListener;
+import com.redhat.thermostat.common.ApplicationService;
+import com.redhat.thermostat.common.NotImplementedException;
+import com.redhat.thermostat.common.Timer;
+import com.redhat.thermostat.common.Timers;
+import com.redhat.thermostat.shared.locale.LocalizedString;
+import com.redhat.thermostat.shared.locale.Translate;
+import com.redhat.thermostat.storage.core.VmRef;
+import com.redhat.thermostat.storage.model.TimeStampedPojoComparator;
+import com.redhat.thermostat.vm.shenandoah.client.core.VmShenandoahView;
+import com.redhat.thermostat.vm.shenandoah.client.core.VmShenandoahViewProvider;
+import com.redhat.thermostat.vm.shenandoah.client.core.locale.LocaleResources;
+import com.redhat.thermostat.vm.shenandoah.common.VmShenandoahDao;
+import com.redhat.thermostat.vm.shenandoah.common.VmShenandoahStat;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+public class VmShenandoahController implements InformationServiceController<VmRef> {
+
+    private static final Translate<LocaleResources> t = LocaleResources.createLocalizer();
+
+    private final VmRef ref;
+    private final VmShenandoahDao dao;
+    private final VmShenandoahView view;
+    private final Timer timer;
+
+    private long lastSeenTimestamp;
+
+    public VmShenandoahController(ApplicationService appSvc, VmRef ref, VmShenandoahDao dao, VmShenandoahViewProvider viewProvider) {
+        this.ref = ref;
+        this.dao = dao;
+        this.view = viewProvider.createView();
+
+        timer = Timers.createDataRefreshTimer(appSvc.getTimerFactory(), new Runnable() {
+            @Override
+            public void run() {
+                doUpdateData();
+            }
+        });
+        timer.setDelay(1L);
+
+        view.addActionListener(new ActionListener<BasicView.Action>() {
+            @Override
+            public void actionPerformed(ActionEvent<BasicView.Action> actionEvent) {
+                switch (actionEvent.getActionId()) {
+                    case HIDDEN:
+                        stop();
+                        break;
+                    case VISIBLE:
+                        start();
+                        break;
+                    default:
+                        throw new NotImplementedException("unknown action: " + actionEvent.getActionId());
+                }
+            }
+        });
+    }
+
+    private void start() {
+        timer.start();
+    }
+
+    private void stop() {
+        timer.stop();
+    }
+
+    private void doUpdateData() {
+        List<VmShenandoahStat> stats = dao.getLatestShenandoahStats(ref, lastSeenTimestamp);
+        boolean hasStats = !stats.isEmpty();
+        view.setEnabled(hasStats);
+        if (hasStats) {
+            Collections.sort(stats, new TimeStampedPojoComparator<VmShenandoahStat>());
+            VmShenandoahStat stat = stats.get(stats.size() - 1);
+            lastSeenTimestamp = stat.getTimeStamp();
+            view.setData(stat);
+        }
+    }
+
+    @Override
+    public UIComponent getView() {
+        return view;
+    }
+
+    @Override
+    public LocalizedString getLocalizedName() {
+        return t.localize(LocaleResources.TAB_TITLE);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-shenandoah/client-core/src/main/java/com/redhat/thermostat/vm/shenandoah/client/core/internal/VmShenandoahServiceImpl.java	Mon Mar 06 09:38:30 2017 -0500
@@ -0,0 +1,103 @@
+/*
+ * Copyright 2012-2017 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.vm.shenandoah.client.core.internal;
+
+import com.redhat.thermostat.client.core.controllers.InformationServiceController;
+import com.redhat.thermostat.common.ApplicationService;
+import com.redhat.thermostat.common.Filter;
+import com.redhat.thermostat.storage.core.AgentId;
+import com.redhat.thermostat.storage.core.VmRef;
+import com.redhat.thermostat.storage.dao.AgentInfoDAO;
+import com.redhat.thermostat.storage.dao.VmInfoDAO;
+import com.redhat.thermostat.storage.model.AgentInformation;
+import com.redhat.thermostat.storage.model.VmInfo;
+import com.redhat.thermostat.vm.shenandoah.client.core.VmShenandoahService;
+import com.redhat.thermostat.vm.shenandoah.client.core.VmShenandoahViewProvider;
+import com.redhat.thermostat.vm.shenandoah.common.VmShenandoahDao;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+public class VmShenandoahServiceImpl implements VmShenandoahService {
+
+    private final Filter<VmRef> filter;
+    private final ApplicationService appSvc;
+    private final VmShenandoahDao vmShenandoahDao;
+    private final VmShenandoahViewProvider viewProvider;
+    private final Map<VmRef, VmShenandoahController> controllers = new ConcurrentHashMap<>();
+
+    public VmShenandoahServiceImpl(ApplicationService appSvc, final AgentInfoDAO agentInfoDAO, final VmInfoDAO vmInfoDAO,
+                                   final VmShenandoahDao vmShenandoahDao, VmShenandoahViewProvider viewProvider) {
+        this.appSvc = appSvc;
+        this.vmShenandoahDao = vmShenandoahDao;
+        this.viewProvider = viewProvider;
+        this.filter = new Filter<VmRef>() {
+            @Override
+            public boolean matches(VmRef toMatch) {
+                return isAlive(toMatch) && hasStats(toMatch);
+            }
+
+            private boolean isAlive(VmRef ref) {
+                VmInfo vmInfo = vmInfoDAO.getVmInfo(ref);
+                AgentInformation agentInformation = agentInfoDAO.getAgentInformation(new AgentId(vmInfo.getAgentId()));
+                return VmInfo.AliveStatus.RUNNING.equals(vmInfo.isAlive(agentInformation));
+            }
+
+            private boolean hasStats(VmRef ref) {
+                return vmShenandoahDao.getCount(ref) > 0;
+            }
+        };
+    }
+
+    @Override
+    public InformationServiceController<VmRef> getInformationServiceController(VmRef ref) {
+        if (controllers.get(ref) == null) {
+            controllers.put(ref, new VmShenandoahController(appSvc, ref, vmShenandoahDao, viewProvider));
+        }
+        return controllers.get(ref);
+    }
+
+    @Override
+    public Filter<VmRef> getFilter() {
+        return filter;
+    }
+
+    @Override
+    public int getOrderValue() {
+        return ORDER_MEMORY_GROUP + 21;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-shenandoah/client-core/src/main/java/com/redhat/thermostat/vm/shenandoah/client/core/locale/LocaleResources.java	Mon Mar 06 09:38:30 2017 -0500
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2012-2017 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.vm.shenandoah.client.core.locale;
+
+import com.redhat.thermostat.shared.locale.Translate;
+
+public enum LocaleResources  {
+
+    TAB_TITLE,
+    TAB_DETAIL,
+    SHENANDOAH_UNAVAILABLE,
+
+    ;
+
+    static final String RESOURCE_BUNDLE =
+            "com.redhat.thermostat.vm.shenandoah.client.core.locale.strings";
+
+    public static Translate<LocaleResources> createLocalizer() {
+        return new Translate<>(RESOURCE_BUNDLE, LocaleResources.class);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-shenandoah/client-core/src/main/resources/com/redhat/thermostat/vm/shenandoah/client/core/locale/strings.properties	Mon Mar 06 09:38:30 2017 -0500
@@ -0,0 +1,3 @@
+TAB_TITLE=Shenandoah
+TAB_DETAIL=Memory Regions
+SHENANDOAH_UNAVAILABLE=Shenandoah is unavailable on this VM
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-shenandoah/client-core/src/test/java/com/redhat/thermostat/vm/shenandoah/client/core/internal/ActivatorTest.java	Mon Mar 06 09:38:30 2017 -0500
@@ -0,0 +1,101 @@
+/*
+ * Copyright 2012-2017 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.vm.shenandoah.client.core.internal;
+
+import com.redhat.thermostat.client.core.InformationService;
+import com.redhat.thermostat.common.ApplicationService;
+import com.redhat.thermostat.storage.dao.AgentInfoDAO;
+import com.redhat.thermostat.storage.dao.VmInfoDAO;
+import com.redhat.thermostat.testutils.StubBundleContext;
+import com.redhat.thermostat.vm.shenandoah.client.core.VmShenandoahViewProvider;
+import com.redhat.thermostat.vm.shenandoah.common.VmShenandoahDao;
+import org.junit.Before;
+import org.junit.Test;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotSame;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
+
+public class ActivatorTest {
+
+    private StubBundleContext context;
+    private Activator activator;
+
+    @Before
+    public void setup() {
+        context = new StubBundleContext();
+        activator = new Activator();
+    }
+
+    @Test
+    public void testActivatorDoesNothingWithMissingDeps() throws Exception {
+        activator.start(context);
+
+        assertTrue(context.getAllServices().isEmpty());
+        assertNotSame(context.getServiceListeners().size(), 1);
+
+        activator.stop(context);
+
+        assertTrue(context.getAllServices().isEmpty());
+        assertTrue(context.getServiceListeners().isEmpty());
+    }
+
+    @Test
+    public void verifyServiceRegistered() throws Exception {
+        ApplicationService appSvc = mock(ApplicationService.class);
+        AgentInfoDAO agentInfoDAO = mock(AgentInfoDAO.class);
+        VmInfoDAO vmInfoDAO = mock(VmInfoDAO.class);
+        VmShenandoahDao vmShenandoahDao = mock(VmShenandoahDao.class);
+        VmShenandoahViewProvider viewProvider = mock(VmShenandoahViewProvider.class);
+
+        context.registerService(ApplicationService.class, appSvc, null);
+        context.registerService(AgentInfoDAO.class, agentInfoDAO, null);
+        context.registerService(VmInfoDAO.class, vmInfoDAO, null);
+        context.registerService(VmShenandoahDao.class, vmShenandoahDao, null);
+        context.registerService(VmShenandoahViewProvider.class, viewProvider, null);
+
+        activator.start(context);
+
+        assertTrue(context.isServiceRegistered(InformationService.class.getName(), VmShenandoahServiceImpl.class));
+
+        activator.stop(context);
+
+        assertFalse(context.isServiceRegistered(InformationService.class.getName(), VmShenandoahServiceImpl.class));
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-shenandoah/client-core/src/test/java/com/redhat/thermostat/vm/shenandoah/client/core/internal/VmShenandoahControllerTest.java	Mon Mar 06 09:38:30 2017 -0500
@@ -0,0 +1,178 @@
+/*
+ * Copyright 2012-2017 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.vm.shenandoah.client.core.internal;
+
+import com.redhat.thermostat.client.core.views.BasicView;
+import com.redhat.thermostat.common.ActionEvent;
+import com.redhat.thermostat.common.ActionListener;
+import com.redhat.thermostat.common.ApplicationService;
+import com.redhat.thermostat.common.Timer;
+import com.redhat.thermostat.common.TimerFactory;
+import com.redhat.thermostat.shared.locale.Translate;
+import com.redhat.thermostat.storage.core.VmRef;
+import com.redhat.thermostat.vm.shenandoah.client.core.VmShenandoahView;
+import com.redhat.thermostat.vm.shenandoah.client.core.VmShenandoahViewProvider;
+import com.redhat.thermostat.vm.shenandoah.client.core.locale.LocaleResources;
+import com.redhat.thermostat.vm.shenandoah.common.VmShenandoahDao;
+import com.redhat.thermostat.vm.shenandoah.common.VmShenandoahStat;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.InOrder;
+
+import java.util.Arrays;
+import java.util.Collections;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertThat;
+import static org.mockito.Matchers.anyLong;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Matchers.isA;
+import static org.mockito.Mockito.inOrder;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+public class VmShenandoahControllerTest {
+
+    private static final Translate<LocaleResources> t = LocaleResources.createLocalizer();
+
+    private ApplicationService appSvc;
+    private VmRef ref;
+    private VmShenandoahDao dao;
+    private VmShenandoahViewProvider viewProvider;
+    private VmShenandoahView view;
+    private TimerFactory timerFactory;
+    private Timer timer;
+
+    private VmShenandoahController controller;
+
+    @Before
+    public void setup() {
+        appSvc = mock(ApplicationService.class);
+        ref = mock(VmRef.class);
+        dao = mock(VmShenandoahDao.class);
+        viewProvider = mock(VmShenandoahViewProvider.class);
+        view = mock(VmShenandoahView.class);
+        timerFactory = mock(TimerFactory.class);
+        timer = mock(Timer.class);
+
+        when(viewProvider.createView()).thenReturn(view);
+        when(timerFactory.createTimer()).thenReturn(timer);
+        when(appSvc.getTimerFactory()).thenReturn(timerFactory);
+
+        controller = new VmShenandoahController(appSvc, ref, dao, viewProvider);
+    }
+
+    @Test
+    public void testTabTitle() {
+        assertThat(controller.getLocalizedName().getContents(), is(t.localize(LocaleResources.TAB_TITLE).getContents()));
+    }
+
+    @Test
+    public void verifyCreatesView() {
+        verify(viewProvider).createView();
+    }
+
+    @Test
+    public void testReturnsCorrectView() {
+        assertThat((VmShenandoahView) controller.getView(), is(view));
+    }
+
+    @Test
+    public void verifyUsesTimerFactory() {
+        verify(timerFactory).createTimer();
+    }
+
+    @Test
+    public void verifyAddsViewActionListener() {
+        verify(view).addActionListener(isA(ActionListener.class));
+    }
+
+    @Test
+    public void testStopsOnViewHidden() {
+        ArgumentCaptor<ActionListener> captor = ArgumentCaptor.forClass(ActionListener.class);
+        verify(view).addActionListener(captor.capture());
+        ActionListener listener = captor.getValue();
+        listener.actionPerformed(new ActionEvent(controller, BasicView.Action.HIDDEN));
+        verify(timer).stop();
+    }
+
+    @Test
+    public void testStartsOnViewVisible() {
+        ArgumentCaptor<ActionListener> captor = ArgumentCaptor.forClass(ActionListener.class);
+        verify(view).addActionListener(captor.capture());
+        ActionListener listener = captor.getValue();
+        listener.actionPerformed(new ActionEvent(controller, BasicView.Action.VISIBLE));
+        verify(timer).start();
+    }
+
+    @Test
+    public void testUpdateActionWithNoDaoData() {
+        when(dao.getCount(ref)).thenReturn(0L);
+        when(dao.getLatestShenandoahStats(eq(ref), anyLong())).thenReturn(Collections.<VmShenandoahStat>emptyList());
+        ArgumentCaptor<Runnable> actionCaptor = ArgumentCaptor.forClass(Runnable.class);
+        verify(timer).setAction(actionCaptor.capture());
+        Runnable action = actionCaptor.getValue();
+        action.run();
+
+        InOrder inOrder = inOrder(dao, view);
+        inOrder.verify(dao).getLatestShenandoahStats(eq(ref), anyLong());
+        inOrder.verify(view).setEnabled(false);
+        inOrder.verifyNoMoreInteractions();
+    }
+
+    @Test
+    public void testUpdateActionWithDaoData() {
+        VmShenandoahStat stat1 = new VmShenandoahStat("writerID", "vmId", 150L, 50L, 2L, 1024L, 0L, new long[]{1L, 2L});
+        VmShenandoahStat stat2 = new VmShenandoahStat("writerID", "vmId", 100L, 50L, 2L, 1024L, 1L, new long[]{3L, 4L});
+
+        when(dao.getCount(ref)).thenReturn(2L);
+        when(dao.getLatestShenandoahStats(eq(ref), anyLong())).thenReturn(Arrays.asList(stat1, stat2));
+        ArgumentCaptor<Runnable> actionCaptor = ArgumentCaptor.forClass(Runnable.class);
+        verify(timer).setAction(actionCaptor.capture());
+        Runnable action = actionCaptor.getValue();
+        action.run();
+
+        InOrder inOrder = inOrder(dao, view);
+        inOrder.verify(dao).getLatestShenandoahStats(eq(ref), anyLong());
+        inOrder.verify(view).setEnabled(true);
+        inOrder.verify(view).setData(stat1);
+        inOrder.verifyNoMoreInteractions();
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-shenandoah/client-core/src/test/java/com/redhat/thermostat/vm/shenandoah/client/core/internal/VmShenandoahServiceImplTest.java	Mon Mar 06 09:38:30 2017 -0500
@@ -0,0 +1,158 @@
+/*
+ * Copyright 2012-2017 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.vm.shenandoah.client.core.internal;
+
+import com.redhat.thermostat.common.ApplicationService;
+import com.redhat.thermostat.common.Filter;
+import com.redhat.thermostat.common.Ordered;
+import com.redhat.thermostat.storage.core.AgentId;
+import com.redhat.thermostat.storage.core.VmRef;
+import com.redhat.thermostat.storage.dao.AgentInfoDAO;
+import com.redhat.thermostat.storage.dao.VmInfoDAO;
+import com.redhat.thermostat.storage.model.AgentInformation;
+import com.redhat.thermostat.storage.model.VmInfo;
+import com.redhat.thermostat.vm.shenandoah.client.core.VmShenandoahViewProvider;
+import com.redhat.thermostat.vm.shenandoah.common.VmShenandoahDao;
+import org.junit.Before;
+import org.junit.Test;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Matchers.isA;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class VmShenandoahServiceImplTest {
+
+    private ApplicationService appSvc;
+    private AgentInfoDAO agentInfoDAO;
+    private VmInfoDAO vmInfoDAO;
+    private VmShenandoahDao vmShenandoahDao;
+    private VmShenandoahViewProvider viewProvider;
+    private VmRef vmRef;
+
+    private VmShenandoahServiceImpl service;
+
+    @Before
+    public void setup() {
+        appSvc = mock(ApplicationService.class);
+        agentInfoDAO = mock(AgentInfoDAO.class);
+        vmInfoDAO = mock(VmInfoDAO.class);
+        vmShenandoahDao = mock(VmShenandoahDao.class);
+        viewProvider = mock(VmShenandoahViewProvider.class);
+        vmRef = mock(VmRef.class);
+
+        service = new VmShenandoahServiceImpl(appSvc, agentInfoDAO, vmInfoDAO, vmShenandoahDao, viewProvider);
+    }
+
+    @Test
+    public void testFilterDeadVm() {
+        Filter<VmRef> filter = service.getFilter();
+
+        AgentInformation agentInformation = new AgentInformation();
+        agentInformation.setAgentId("foo-agentId");
+        agentInformation.setAlive(true);
+        when(agentInfoDAO.getAgentInformation(isA(AgentId.class))).thenReturn(agentInformation);
+        VmInfo vmInfo = new VmInfo();
+        vmInfo.setAgentId(agentInformation.getAgentId());
+        vmInfo.setStartTimeStamp(0L);
+        vmInfo.setStopTimeStamp(100L);
+        when(vmInfoDAO.getVmInfo(eq(vmRef))).thenReturn(vmInfo);
+
+        assertFalse(filter.matches(vmRef));
+    }
+
+    @Test
+    public void testFilterUnknownVm() {
+        Filter<VmRef> filter = service.getFilter();
+
+        AgentInformation agentInformation = new AgentInformation();
+        agentInformation.setAgentId("foo-agentId");
+        agentInformation.setAlive(false);
+        when(agentInfoDAO.getAgentInformation(isA(AgentId.class))).thenReturn(agentInformation);
+        VmInfo vmInfo = new VmInfo();
+        vmInfo.setAgentId(agentInformation.getAgentId());
+        vmInfo.setStartTimeStamp(0L);
+        vmInfo.setStopTimeStamp(100L);
+        when(vmInfoDAO.getVmInfo(eq(vmRef))).thenReturn(vmInfo);
+
+        assertFalse(filter.matches(vmRef));
+    }
+
+    @Test
+    public void testFilterAliveVmWithShenandoahStats() {
+        Filter<VmRef> filter = service.getFilter();
+
+        AgentInformation agentInformation = new AgentInformation();
+        agentInformation.setAgentId("foo-agentId");
+        agentInformation.setAlive(true);
+        when(agentInfoDAO.getAgentInformation(isA(AgentId.class))).thenReturn(agentInformation);
+        VmInfo vmInfo = new VmInfo();
+        vmInfo.setAgentId(agentInformation.getAgentId());
+        when(vmInfoDAO.getVmInfo(eq(vmRef))).thenReturn(vmInfo);
+
+        when(vmShenandoahDao.getCount(eq(vmRef))).thenReturn(2L);
+
+        assertTrue(filter.matches(vmRef));
+    }
+
+    @Test
+    public void testFilterAliveVmWithoutShenandoahStats() {
+        Filter<VmRef> filter = service.getFilter();
+
+        AgentInformation agentInformation = new AgentInformation();
+        agentInformation.setAgentId("foo-agentId");
+        agentInformation.setAlive(true);
+        when(agentInfoDAO.getAgentInformation(isA(AgentId.class))).thenReturn(agentInformation);
+        VmInfo vmInfo = new VmInfo();
+        vmInfo.setAgentId(agentInformation.getAgentId());
+        when(vmInfoDAO.getVmInfo(eq(vmRef))).thenReturn(vmInfo);
+
+        when(vmShenandoahDao.getCount(eq(vmRef))).thenReturn(0L);
+
+        assertFalse(filter.matches(vmRef));
+    }
+
+    @Test
+    public void testOrderValue() {
+        assertThat(service.getOrderValue(), is(Ordered.ORDER_MEMORY_GROUP + 21));
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-shenandoah/client-core/src/test/java/com/redhat/thermostat/vm/shenandoah/client/core/locale/LocaleResourcesTest.java	Mon Mar 06 09:38:30 2017 -0500
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2012-2017 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.vm.shenandoah.client.core.locale;
+
+import com.redhat.thermostat.testutils.AbstractLocaleResourcesTest;
+
+public class LocaleResourcesTest  extends AbstractLocaleResourcesTest<LocaleResources> {
+
+    @Override
+    protected Class<LocaleResources> getEnumClass() {
+        return LocaleResources.class;
+    }
+
+    @Override
+    protected String getResourceBundle() {
+        return LocaleResources.RESOURCE_BUNDLE;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-shenandoah/client-swing/pom.xml	Mon Mar 06 09:38:30 2017 -0500
@@ -0,0 +1,137 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+ Copyright 2012-2017 Red Hat, Inc.
+
+ This file is part of Thermostat.
+
+ Thermostat is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published
+ by the Free Software Foundation; either version 2, or (at your
+ option) any later version.
+
+ Thermostat is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Thermostat; see the file COPYING.  If not see
+ <http://www.gnu.org/licenses/>.
+
+ Linking this code with other modules is making a combined work
+ based on this code.  Thus, the terms and conditions of the GNU
+ General Public License cover the whole combination.
+
+ As a special exception, the copyright holders of this code give
+ you permission to link this code with independent modules to
+ produce an executable, regardless of the license terms of these
+ independent modules, and to copy and distribute the resulting
+ executable under terms of your choice, provided that you also
+ meet, for each linked independent module, the terms and conditions
+ of the license of that module.  An independent module is a module
+ which is not derived from or based on this code.  If you modify
+ this code, you may extend this exception to your version of the
+ library, but you are not obligated to do so.  If you do not wish
+ to do so, delete this exception statement from your version.
+
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <artifactId>thermostat-vm-shenandoah</artifactId>
+    <groupId>com.redhat.thermostat</groupId>
+    <version>1.99.12-SNAPSHOT</version>
+  </parent>
+  <artifactId>thermostat-vm-shenandoah-client-swing</artifactId>
+  <packaging>bundle</packaging>
+  <name>Thermostat VM Shenandoah GC Swing Client plugin</name>
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.felix</groupId>
+        <artifactId>maven-bundle-plugin</artifactId>
+        <extensions>true</extensions>
+        <configuration>
+          <instructions>
+            <Bundle-Vendor>Red Hat, Inc.</Bundle-Vendor>
+            <Bundle-SymbolicName>com.redhat.thermostat.vm.shenandoah.client.swing</Bundle-SymbolicName>
+            <Bundle-Activator>com.redhat.thermostat.vm.shenandoah.client.swing.internal.Activator</Bundle-Activator>
+            <Export-Package>
+              com.redhat.thermostat.vm.shenandoah.client.swing
+            </Export-Package>
+            <Private-Package>
+              com.redhat.thermostat.vm.shenandoah.client.swing.internal,
+              com.redhat.thermostat.vm.shenandoah.client.swing.internal.locale
+            </Private-Package>
+            <!-- Do not autogenerate uses clauses in Manifests -->
+            <_nouses>true</_nouses>
+          </instructions>
+        </configuration>
+      </plugin>
+    </plugins>
+  </build>
+  <dependencies>
+    <dependency>
+      <groupId>junit</groupId>
+      <artifactId>junit</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>com.redhat.thermostat</groupId>
+      <artifactId>thermostat-common-test</artifactId>
+      <version>${project.version}</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.mockito</groupId>
+      <artifactId>mockito-core</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.easytesting</groupId>
+      <artifactId>fest-swing</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>net.java.openjdk.cacio</groupId>
+      <artifactId>cacio-tta</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.osgi</groupId>
+      <artifactId>org.osgi.core</artifactId>
+      <scope>provided</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.osgi</groupId>
+      <artifactId>org.osgi.compendium</artifactId>
+      <scope>provided</scope>
+    </dependency>
+    <dependency>
+      <groupId>com.redhat.thermostat</groupId>
+      <artifactId>thermostat-common-core</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>com.redhat.thermostat</groupId>
+      <artifactId>thermostat-client-core</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>com.redhat.thermostat</groupId>
+      <artifactId>thermostat-storage-core</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>com.redhat.thermostat</groupId>
+      <artifactId>thermostat-client-swing</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>com.redhat.thermostat</groupId>
+      <artifactId>thermostat-vm-shenandoah-client-core</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+  </dependencies>
+</project>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-shenandoah/client-swing/src/main/java/com/redhat/thermostat/vm/shenandoah/client/swing/internal/Activator.java	Mon Mar 06 09:38:30 2017 -0500
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2012-2017 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.vm.shenandoah.client.swing.internal;
+
+import com.redhat.thermostat.vm.shenandoah.client.core.VmShenandoahViewProvider;
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceRegistration;
+
+public class Activator implements BundleActivator {
+
+    private ServiceRegistration viewProviderRegistration;
+
+    @Override
+    public void start(final BundleContext context) throws Exception {
+        viewProviderRegistration = context.registerService(VmShenandoahViewProvider.class.getName(), new SwingVmShenandoahViewProvider(), null);
+    }
+
+    @Override
+    public void stop(BundleContext context) throws Exception {
+        if (viewProviderRegistration != null) {
+            viewProviderRegistration.unregister();
+        }
+    }
+
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-shenandoah/client-swing/src/main/java/com/redhat/thermostat/vm/shenandoah/client/swing/internal/Colors.java	Mon Mar 06 09:38:30 2017 -0500
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2012-2017 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.vm.shenandoah.client.swing.internal;
+
+import java.awt.Color;
+
+public enum Colors {
+
+    USED_ALLOC(new Color(0, 250, 250)),
+    USED(new Color(150, 150, 150)),
+    LIVE(new Color(0, 200, 0)),
+    LIVE_BORDER(new Color(0, 100, 0)),
+    CSET(Color.YELLOW),
+    HUMONGOUS(Color.RED),
+    ;
+
+    private final Color color;
+
+    Colors(Color color) {
+        this.color = color;
+    }
+
+    public Color getColor() {
+        return color;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-shenandoah/client-swing/src/main/java/com/redhat/thermostat/vm/shenandoah/client/swing/internal/RegionFlag.java	Mon Mar 06 09:38:30 2017 -0500
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2012-2017 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.vm.shenandoah.client.swing.internal;
+
+public enum RegionFlag {
+    UNUSED,
+    HUMONGOUS,
+    IN_COLLECTION_SET,
+    RECENTLY_ALLOCATED,
+    PINNED,
+    ;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-shenandoah/client-swing/src/main/java/com/redhat/thermostat/vm/shenandoah/client/swing/internal/RegionStat.java	Mon Mar 06 09:38:30 2017 -0500
@@ -0,0 +1,170 @@
+/*
+ * Copyright 2012-2017 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.vm.shenandoah.client.swing.internal;
+
+import java.awt.Color;
+import java.awt.Graphics;
+import java.util.EnumSet;
+
+import static com.redhat.thermostat.vm.shenandoah.client.swing.internal.Colors.LIVE;
+import static com.redhat.thermostat.vm.shenandoah.client.swing.internal.Colors.LIVE_BORDER;
+import static com.redhat.thermostat.vm.shenandoah.client.swing.internal.Colors.USED;
+import static com.redhat.thermostat.vm.shenandoah.client.swing.internal.Colors.USED_ALLOC;
+
+public class RegionStat {
+
+    private static final int USED_MASK = 0x1fffffff;
+    private static final int USED_SHIFT = 0;
+    private static final int LIVE_MASK = 0x1fffffff;
+    private static final int LIVE_SHIFT = 29;
+    private static final int FLAGS_MASK = 0x3f;
+    private static final int FLAGS_SHIFT = 58;
+
+    private final EnumSet<RegionFlag> flags;
+    private final double liveLvl;
+    private final double usedLvl;
+
+    public RegionStat(double usedLvl, double liveLvl, EnumSet<RegionFlag> flags) {
+        this.usedLvl = usedLvl;
+        this.liveLvl = liveLvl;
+        this.flags = flags;
+    }
+
+    public RegionStat(long maxSize, long data) {
+        long used = (data >>> USED_SHIFT) & USED_MASK;
+        usedLvl = Math.min(1D, 1D * used / maxSize);
+
+        long live = (data >>> LIVE_SHIFT) & LIVE_MASK;
+        liveLvl = Math.min(1D, 1D * live / maxSize);
+
+        long stat = (data >>> FLAGS_SHIFT) & FLAGS_MASK;
+
+        flags = EnumSet.noneOf(RegionFlag.class);
+
+        if ((stat & 1)  > 0) flags.add(RegionFlag.UNUSED);
+        if ((stat & 2)  > 0) flags.add(RegionFlag.IN_COLLECTION_SET);
+        if ((stat & 4)  > 0) flags.add(RegionFlag.HUMONGOUS);
+        if ((stat & 8)  > 0) flags.add(RegionFlag.RECENTLY_ALLOCATED);
+        if ((stat & 16) > 0) flags.add(RegionFlag.PINNED);
+    }
+
+    public void render(Graphics g, int x, int y, int width, int height) {
+        g.setColor(Color.WHITE);
+        g.fillRect(x, y, width, height);
+
+        int usedWidth = (int) (width * usedLvl);
+        g.setColor(
+                flags.contains(RegionFlag.RECENTLY_ALLOCATED) ?
+                        USED_ALLOC.getColor() : USED.getColor()
+        );
+        g.fillRect(x, y, usedWidth, height);
+
+        if (!flags.contains(RegionFlag.RECENTLY_ALLOCATED)) {
+            int liveWidth = (int) (width * liveLvl);
+            g.setColor(LIVE.getColor());
+            g.fillRect(x, y, liveWidth, height);
+
+            g.setColor(LIVE_BORDER.getColor());
+            g.drawLine(x + liveWidth, y, x + liveWidth, y + height);
+        }
+
+        if (flags.contains(RegionFlag.IN_COLLECTION_SET)) {
+            g.setColor(Colors.CSET.getColor());
+            g.fillRect(x, y, width, height / 3);
+            g.setColor(Color.BLACK);
+            g.drawRect(x, y, width, height / 3);
+        }
+
+        if (flags.contains(RegionFlag.HUMONGOUS)) {
+            g.setColor(Colors.HUMONGOUS.getColor());
+            g.fillRect(x, y, width, height / 3);
+            g.setColor(Color.BLACK);
+            g.drawRect(x, y, width, height / 3);
+        }
+
+        if (flags.contains(RegionFlag.UNUSED)) {
+            g.setColor(Color.BLACK);
+            g.drawLine(x, y, x + width, y + height);
+            g.drawLine(x, y + height, x + width, y);
+        }
+
+        if (flags.contains(RegionFlag.PINNED)) {
+            g.setColor(Color.RED);
+            g.fillOval(x + width/2, y + height/2, width/4, height/4);
+        }
+
+        g.setColor(Color.BLACK);
+        g.drawRect(x, y, width, height);
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+
+        RegionStat that = (RegionStat) o;
+
+        if (Double.compare(that.liveLvl, liveLvl) != 0) return false;
+        if (Double.compare(that.usedLvl, usedLvl) != 0) return false;
+        return flags.equals(that.flags);
+    }
+
+    @Override
+    public int hashCode() {
+        int result;
+        long temp;
+        result = flags.hashCode();
+        temp = Double.doubleToLongBits(liveLvl);
+        result = 31 * result + (int) (temp ^ (temp >>> 32));
+        temp = Double.doubleToLongBits(usedLvl);
+        result = 31 * result + (int) (temp ^ (temp >>> 32));
+        return result;
+    }
+
+    public double live() {
+        return liveLvl;
+    }
+
+    public double used() {
+        return usedLvl;
+    }
+
+    public EnumSet<RegionFlag> flags() {
+        return flags;
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-shenandoah/client-swing/src/main/java/com/redhat/thermostat/vm/shenandoah/client/swing/internal/ShenandoahVisualizer.java	Mon Mar 06 09:38:30 2017 -0500
@@ -0,0 +1,380 @@
+/*
+ * Copyright 2012-2017 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.vm.shenandoah.client.swing.internal;
+
+import com.redhat.thermostat.common.Clock;
+import com.redhat.thermostat.shared.locale.Translate;
+import com.redhat.thermostat.vm.shenandoah.client.swing.internal.locale.LocaleResources;
+import com.redhat.thermostat.vm.shenandoah.common.VmShenandoahStat;
+
+import javax.swing.JPanel;
+import java.awt.Color;
+import java.awt.Container;
+import java.awt.Font;
+import java.awt.Graphics;
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.Insets;
+import java.awt.event.ComponentAdapter;
+import java.awt.event.ComponentEvent;
+import java.awt.font.TextAttribute;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Date;
+import java.util.Deque;
+import java.util.EnumSet;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+import static com.redhat.thermostat.vm.shenandoah.client.swing.internal.RegionFlag.HUMONGOUS;
+import static com.redhat.thermostat.vm.shenandoah.client.swing.internal.RegionFlag.IN_COLLECTION_SET;
+import static com.redhat.thermostat.vm.shenandoah.client.swing.internal.RegionFlag.PINNED;
+import static com.redhat.thermostat.vm.shenandoah.client.swing.internal.RegionFlag.RECENTLY_ALLOCATED;
+import static com.redhat.thermostat.vm.shenandoah.client.swing.internal.RegionFlag.UNUSED;
+
+/**
+ * See http://icedtea.classpath.org/people/rkennke/shenandoahvisualizer
+ */
+class ShenandoahVisualizer extends JPanel {
+
+    private static final Translate<LocaleResources> t = LocaleResources.createLocalizer();
+
+    private final Render render;
+    private VmShenandoahStat stat;
+
+    ShenandoahVisualizer() {
+        setLayout(new GridBagLayout());
+
+        DataProvider dataProvider = new DataProvider();
+
+        this.render = new Render(dataProvider, this);
+
+        JPanel regionsPanel = new JPanel() {
+            public void paint(Graphics g) {
+                render.renderRegions(g);
+            }
+        };
+
+        JPanel legendPanel = new JPanel() {
+            @Override
+            public void paint(Graphics g) {
+                render.renderLegend(g);
+            }
+        };
+
+        JPanel statusPanel = new JPanel() {
+            @Override
+            public void paint(Graphics g) {
+                render.renderStats(g);
+            }
+        };
+
+        JPanel graphPanel = new JPanel() {
+            @Override
+            public void paint(Graphics g) {
+                render.renderGraph(g);
+            }
+        };
+
+        regionsPanel.addComponentListener(new ComponentAdapter() {
+            public void componentResized(ComponentEvent ev) {
+                render.notifyRegionResized(ev.getComponent().getWidth(), ev.getComponent().getHeight());
+            }
+        });
+
+        graphPanel.addComponentListener(new ComponentAdapter() {
+            public void componentResized(ComponentEvent ev) {
+                render.notifyGraphResized(ev.getComponent().getWidth(), ev.getComponent().getHeight());
+            }
+        });
+
+        Insets pad = new Insets(8, 2, 8, 2);
+
+        {
+            GridBagConstraints c = new GridBagConstraints();
+            c.fill = GridBagConstraints.BOTH;
+            c.gridx = 0;
+            c.gridy = 0;
+            c.weightx = 3;
+            c.weighty = 1;
+            c.insets = pad;
+            this.add(graphPanel, c);
+        }
+
+        {
+            GridBagConstraints c = new GridBagConstraints();
+            c.fill = GridBagConstraints.BOTH;
+            c.gridx = 0;
+            c.gridy = 1;
+            c.weightx = 3;
+            c.weighty = 5;
+            c.insets = pad;
+            this.add(regionsPanel, c);
+        }
+
+        {
+            GridBagConstraints c = new GridBagConstraints();
+            c.fill = GridBagConstraints.BOTH;
+            c.gridx = 1;
+            c.gridy = 0;
+            c.weightx = 2;
+            c.weighty = 1;
+            c.insets = pad;
+            this.add(statusPanel, c);
+        }
+
+        {
+            GridBagConstraints c = new GridBagConstraints();
+            c.fill = GridBagConstraints.BOTH;
+            c.gridx = 1;
+            c.gridy = 1;
+            c.weightx = 2;
+            c.weighty = 1;
+            c.insets = pad;
+            this.add(legendPanel, c);
+        }
+    }
+
+    public void setStat(VmShenandoahStat stat) {
+        this.stat = stat;
+        render.run();
+    }
+
+    private static class Render implements Runnable {
+        static final Map<TextAttribute, Object> TEXT_ATTRIBUTES = new HashMap<>();
+
+        static {
+            TEXT_ATTRIBUTES.put(TextAttribute.FAMILY, Font.SANS_SERIF);
+            TEXT_ATTRIBUTES.put(TextAttribute.WEIGHT, TextAttribute.WEIGHT_LIGHT);
+            TEXT_ATTRIBUTES.put(TextAttribute.SIZE, 11);
+        }
+
+        final DataProvider data;
+        final Container container;
+
+        int regionWidth, regionHeight;
+        int graphWidth, graphHeight;
+
+        final Deque<SnapshotView> lastSnapshots;
+        volatile Snapshot snapshot;
+
+        public Render(DataProvider data, Container container) {
+            this.data = data;
+            this.container = container;
+            this.lastSnapshots = new LinkedList<>();
+            this.snapshot = data.snapshot();
+        }
+
+        @Override
+        public synchronized void run() {
+            Snapshot cur = data.snapshot();
+            if (!cur.equals(snapshot)) {
+                snapshot = cur;
+                lastSnapshots.add(new SnapshotView(cur));
+                if (lastSnapshots.size() > graphWidth) {
+                    lastSnapshots.removeFirst();
+                }
+                container.repaint();
+            }
+        }
+
+        public synchronized void renderGraph(Graphics g) {
+            if (lastSnapshots.size() < 2) return;
+
+            g.setColor(Color.BLACK);
+            g.fillRect(0, 0, graphWidth, graphHeight);
+
+            double stepY = 1D * graphHeight / snapshot.total();
+            long firstTime = lastSnapshots.getFirst().time();
+            long lastTime = lastSnapshots.getLast().time();
+            double stepX = 1D * Math.min(lastSnapshots.size(), graphWidth) / (lastTime - firstTime);
+            for (SnapshotView s : lastSnapshots) {
+                int x = (int) Math.round((s.time() - firstTime) * stepX);
+
+                if (s.isMarking()) {
+                    g.setColor(new Color(100, 100, 0));
+                    g.drawRect(x, 0, 1, graphHeight);
+                }
+
+                if (s.isEvacuating()) {
+                    g.setColor(new Color(100, 0, 0));
+                    g.drawRect(x, 0, 1, graphHeight);
+                }
+
+                g.setColor(Colors.USED.getColor());
+                g.drawRect(x, (int) Math.round(graphHeight - s.used() * stepY), 1, 1);
+                g.setColor(Colors.USED_ALLOC.getColor());
+                g.drawRect(x, (int) Math.round(graphHeight - s.recentlyAllocated() * stepY), 1, 1);
+                g.setColor(Colors.HUMONGOUS.getColor());
+                g.drawRect(x, (int) Math.round(graphHeight - s.humongous() * stepY), 1, 1);
+                g.setColor(Colors.LIVE.getColor());
+                g.drawRect(x, (int) Math.round(graphHeight - s.live() * stepY), 1, 1);
+                g.setColor(Colors.CSET.getColor());
+                g.drawRect(x, (int) Math.round(graphHeight - s.collectionSet() * stepY), 1, 1);
+            }
+        }
+
+        public synchronized void renderLegend(Graphics g) {
+            g.setFont(Font.getFont(TEXT_ATTRIBUTES));
+            final int sqSize = line();
+
+            Map<String, RegionStat> items = new LinkedHashMap<>();
+
+            items.put(t.localize(LocaleResources.UNUSED_REGION_LABEL).getContents(),
+                    new RegionStat(0.0, 0.0, EnumSet.of(UNUSED)));
+
+            items.put(t.localize(LocaleResources.EMPTY_REGION_LABEL).getContents(),
+                    new RegionStat(0.0, 0.0, EnumSet.noneOf(RegionFlag.class)));
+
+            items.put(t.localize(LocaleResources.HALF_USED_REGION_LABEL).getContents(),
+                    new RegionStat(0.5, 0.0, EnumSet.noneOf(RegionFlag.class)));
+
+            items.put(t.localize(LocaleResources.FULLY_USED_REGION_LABEL).getContents(),
+                    new RegionStat(1.0, 0.0, EnumSet.noneOf(RegionFlag.class)));
+
+            items.put(t.localize(LocaleResources.FULLY_USED_RECENTLY_ALLOCATED_REGION_LABEL).getContents(),
+                    new RegionStat(1.0, 0.0, EnumSet.of(RECENTLY_ALLOCATED)));
+
+            items.put(t.localize(LocaleResources.FULLY_LIVE_REGION_LABEL).getContents(),
+                    new RegionStat(1.0, 1.0, EnumSet.noneOf(RegionFlag.class)));
+
+            items.put(t.localize(LocaleResources.FULLY_LIVE_HUMONGOUS_REGION_LABEL).getContents(),
+                    new RegionStat(1.0, 1.0, EnumSet.of(HUMONGOUS)));
+
+            items.put(t.localize(LocaleResources.THIRD_LIVE_REGION_LABEL).getContents(),
+                    new RegionStat(1.0, 0.3, EnumSet.noneOf(RegionFlag.class)));
+
+            items.put(t.localize(LocaleResources.THIRD_LIVE_COLLECITON_SET_REGION_LABEL).getContents(),
+                    new RegionStat(1.0, 0.3, EnumSet.of(IN_COLLECTION_SET)));
+
+            items.put(t.localize(LocaleResources.THIRD_LIVE_PINNED_REGION_LABEL).getContents(),
+                    new RegionStat(1.0, 0.3, EnumSet.of(PINNED)));
+
+            int i = 1;
+            for (String key : items.keySet()) {
+                int y = (int) (i * sqSize * 1.5);
+                items.get(key).render(g, 0, y, sqSize, sqSize);
+                g.drawString(key, (int) (sqSize * 1.5), y + sqSize);
+                i++;
+            }
+        }
+
+        public synchronized void renderRegions(Graphics g) {
+            try {
+                int area = regionWidth * regionHeight;
+                int sqSize = Math.max(1, (int) Math.sqrt(1D * area / snapshot.regionCount()));
+                int cols = regionWidth / sqSize;
+
+                for (int i = 0; i < snapshot.regionCount(); i++) {
+                    int rectx = (i % cols) * sqSize;
+                    int recty = (i / cols) * sqSize;
+
+                    RegionStat s = snapshot.get(i);
+                    s.render(g, rectx, recty, sqSize, sqSize);
+                }
+            } catch (ArithmeticException ignored) {
+            }
+        }
+
+        public synchronized void renderStats(Graphics g) {
+            g.setFont(Font.getFont(TEXT_ATTRIBUTES));
+
+            String status;
+            if (snapshot.isMarking() && snapshot.isEvacuating()) {
+                status = t.localize(LocaleResources.MARKING_AND_EVACUATING_STATUS).getContents();
+            } else if (snapshot.isMarking()) {
+                status = t.localize(LocaleResources.MARKING_STATUS).getContents();
+            } else if (snapshot.isEvacuating()) {
+                status = t.localize(LocaleResources.EVACUATING_STATUS).getContents();
+            } else {
+                status = t.localize(LocaleResources.IDLE_STATUS).getContents();
+            }
+
+            g.setColor(Color.BLACK);
+            g.drawString(t.localize(LocaleResources.TIME_STAT_LABEL, Clock.DEFAULT_DATE_FORMAT.format(new Date(snapshot.time()))).getContents(),
+                    0, 1 * line());
+            g.drawString(t.localize(LocaleResources.STATUS_STAT_LABEL, status).getContents(), 0, 2 * line());
+            g.drawString(t.localize(LocaleResources.TOTAL_STAT_LABEL, Long.toString(snapshot.total())).getContents(), 0, 3 * line());
+            g.drawString(t.localize(LocaleResources.USED_STAT_LABEL, Long.toString(snapshot.used())).getContents(), 0, 4 * line());
+            g.drawString(t.localize(LocaleResources.LIVE_STAT_LABEL, Long.toString(snapshot.live())).getContents(), 0, 5 * line());
+        }
+
+        public synchronized void notifyRegionResized(int width, int height) {
+            this.regionWidth = width;
+            this.regionHeight = height;
+        }
+
+        public synchronized void notifyGraphResized(int width, int height) {
+            this.graphWidth = width;
+            this.graphHeight = height;
+        }
+
+        private int line() {
+            int result = (int) (container.getSize().getHeight() / 30);
+            if (result % 2 == 1) {
+                result--;
+            }
+            return result;
+        }
+    }
+
+    private class DataProvider {
+        public Snapshot snapshot() {
+            if (stat == null) {
+                return new Snapshot(0L, 0L, Collections.<RegionStat>emptyList(), false, false);
+            }
+            long maxSize = stat.getRegionSize();
+            long[] data = stat.getRegionData();
+            long timestamp = stat.getTimeStamp();
+            long status = stat.getRegionStatus();
+
+            List<RegionStat> stats = new ArrayList<>();
+            for (long l : data) {
+                stats.add(new RegionStat(maxSize, l));
+            }
+            boolean isMarking = (status & 0x1) > 0;
+            boolean isEvacuating = (status & 0x2) > 0;
+
+            return new Snapshot(timestamp, maxSize, stats, isMarking, isEvacuating);
+        }
+    }
+}
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-shenandoah/client-swing/src/main/java/com/redhat/thermostat/vm/shenandoah/client/swing/internal/Snapshot.java	Mon Mar 06 09:38:30 2017 -0500
@@ -0,0 +1,149 @@
+/*
+ * Copyright 2012-2017 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.vm.shenandoah.client.swing.internal;
+
+import java.util.List;
+import java.util.Objects;
+
+public class Snapshot {
+
+    private final long time;
+    private final long regionSize;
+    private final List<RegionStat> stats;
+    private final boolean isMarking;
+    private final boolean isEvacuating;
+
+    public Snapshot(long time, long regionSize, List<RegionStat> stats, boolean isMarking, boolean isEvacuating) {
+        this.time = time;
+        this.regionSize = regionSize;
+        this.stats = stats;
+        this.isMarking = isMarking;
+        this.isEvacuating = isEvacuating;
+    }
+
+    public boolean isMarking() {
+        return isMarking;
+    }
+
+    public boolean isEvacuating() {
+        return isEvacuating;
+    }
+
+    public RegionStat get(int i) {
+        return stats.get(i);
+    }
+
+    public long time() {
+        return time;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+
+        Snapshot snapshot = (Snapshot) o;
+
+        if (time != snapshot.time) return false;
+        if (isMarking != snapshot.isMarking) return false;
+        if (isEvacuating != snapshot.isEvacuating) return false;
+        return Objects.equals(stats, snapshot.stats);
+    }
+
+    @Override
+    public int hashCode() {
+        int result = (int) (time ^ (time >>> 32));
+        result = 31 * result + (stats != null ? stats.hashCode() : 0);
+        result = 31 * result + (isMarking ? 1 : 0);
+        result = 31 * result + (isEvacuating ? 1 : 0);
+        return result;
+    }
+
+    public int regionCount() {
+        return stats.size();
+    }
+
+    public long total() {
+        return regionSize * regionCount();
+    }
+
+    public long used() {
+        long used = 0L;
+        for (RegionStat rs : stats) {
+            used += regionSize * rs.used();
+        }
+        return used;
+    }
+
+    public long recentlyAllocated() {
+        long used = 0L;
+        for (RegionStat rs : stats) {
+            if (rs.flags().contains(RegionFlag.RECENTLY_ALLOCATED)) {
+                used += regionSize * rs.used();
+            }
+        }
+        return used;
+    }
+
+    public long collectionSet() {
+        long used = 0L;
+        for (RegionStat rs : stats) {
+            if (rs.flags().contains(RegionFlag.IN_COLLECTION_SET)) {
+                used += regionSize * rs.used();
+            }
+        }
+        return used;
+    }
+
+    public long humongous() {
+        long used = 0L;
+        for (RegionStat rs : stats) {
+            if (rs.flags().contains(RegionFlag.HUMONGOUS)) {
+                used += regionSize * rs.used();
+            }
+        }
+        return used;
+    }
+
+    public long live() {
+        long live = 0L;
+        for (RegionStat rs : stats) {
+            live += regionSize * rs.live();
+        }
+        return live;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-shenandoah/client-swing/src/main/java/com/redhat/thermostat/vm/shenandoah/client/swing/internal/SnapshotView.java	Mon Mar 06 09:38:30 2017 -0500
@@ -0,0 +1,98 @@
+/*
+ * Copyright 2012-2017 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.vm.shenandoah.client.swing.internal;
+
+public class SnapshotView {
+
+    private final long time;
+    private final boolean isMarking;
+    private final boolean isEvacuating;
+    private final long total;
+    private final long used;
+    private final long live;
+    private final long recentlyAllocated;
+    private final long humongous;
+    private final long collectionSet;
+
+    public SnapshotView(Snapshot s) {
+        this.time = s.time();
+        this.isEvacuating = s.isEvacuating();
+        this.isMarking = s.isMarking();
+        total = s.total();
+        used = s.used();
+        live = s.live();
+        recentlyAllocated = s.recentlyAllocated();
+        humongous = s.humongous();
+        collectionSet = s.collectionSet();
+    }
+
+    public boolean isMarking() {
+        return isMarking;
+    }
+
+    public boolean isEvacuating() {
+        return isEvacuating;
+    }
+
+    public long time() {
+        return time;
+    }
+
+    public long total() {
+        return total;
+    }
+
+    public long used() {
+        return used;
+    }
+
+    public long recentlyAllocated() {
+        return recentlyAllocated;
+    }
+
+    public long collectionSet() {
+        return collectionSet;
+    }
+
+    public long humongous() {
+        return humongous;
+    }
+
+    public long live() {
+        return live;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-shenandoah/client-swing/src/main/java/com/redhat/thermostat/vm/shenandoah/client/swing/internal/SwingVmShenandoahView.java	Mon Mar 06 09:38:30 2017 -0500
@@ -0,0 +1,96 @@
+/*
+ * Copyright 2012-2017 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.vm.shenandoah.client.swing.internal;
+
+import com.redhat.thermostat.client.swing.SwingComponent;
+import com.redhat.thermostat.client.swing.components.HeaderPanel;
+import com.redhat.thermostat.client.swing.experimental.ComponentVisibilityNotifier;
+import com.redhat.thermostat.shared.locale.LocalizedString;
+import com.redhat.thermostat.shared.locale.Translate;
+import com.redhat.thermostat.vm.shenandoah.client.core.VmShenandoahView;
+import com.redhat.thermostat.vm.shenandoah.client.core.locale.LocaleResources;
+import com.redhat.thermostat.vm.shenandoah.common.VmShenandoahStat;
+
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.SwingUtilities;
+import java.awt.BorderLayout;
+import java.awt.Component;
+
+public class SwingVmShenandoahView extends VmShenandoahView implements SwingComponent {
+
+    private static final Translate<LocaleResources> t = LocaleResources.createLocalizer();
+
+    private final HeaderPanel headerPanel = new HeaderPanel(t.localize(LocaleResources.TAB_DETAIL));
+    private final ShenandoahVisualizer visualizer = new ShenandoahVisualizer();
+
+    public SwingVmShenandoahView() {
+        super();
+
+        new ComponentVisibilityNotifier().initialize(headerPanel, notifier);
+    }
+
+    @Override
+    public void setEnabled(boolean enabled) {
+        if (enabled) {
+            headerPanel.setContent(visualizer);
+        } else {
+            JPanel statusPanel = new JPanel(new BorderLayout());
+
+            String wrappedText = "<html>" + t.localize(LocaleResources.SHENANDOAH_UNAVAILABLE).getContents() + "</html>";
+            JLabel descriptionLabel = new JLabel(wrappedText);
+            statusPanel.add(descriptionLabel, BorderLayout.PAGE_START);
+
+            headerPanel.setContent(statusPanel);
+        }
+    }
+
+    @Override
+    public void setData(final VmShenandoahStat vmShenandoahStat) {
+        SwingUtilities.invokeLater(new Runnable() {
+            @Override
+            public void run() {
+                visualizer.setStat(vmShenandoahStat);
+            }
+        });
+    }
+
+    @Override
+    public Component getUiComponent() {
+        return headerPanel;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-shenandoah/client-swing/src/main/java/com/redhat/thermostat/vm/shenandoah/client/swing/internal/SwingVmShenandoahViewProvider.java	Mon Mar 06 09:38:30 2017 -0500
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2012-2017 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.vm.shenandoah.client.swing.internal;
+
+import com.redhat.thermostat.vm.shenandoah.client.core.VmShenandoahView;
+import com.redhat.thermostat.vm.shenandoah.client.core.VmShenandoahViewProvider;
+
+public class SwingVmShenandoahViewProvider implements VmShenandoahViewProvider {
+    @Override
+    public VmShenandoahView createView() {
+        return new SwingVmShenandoahView();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-shenandoah/client-swing/src/main/java/com/redhat/thermostat/vm/shenandoah/client/swing/internal/locale/LocaleResources.java	Mon Mar 06 09:38:30 2017 -0500
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2012-2017 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.vm.shenandoah.client.swing.internal.locale;
+
+import com.redhat.thermostat.shared.locale.Translate;
+
+public enum LocaleResources  {
+
+    UNUSED_REGION_LABEL,
+    EMPTY_REGION_LABEL,
+    HALF_USED_REGION_LABEL,
+    FULLY_USED_REGION_LABEL,
+    FULLY_USED_RECENTLY_ALLOCATED_REGION_LABEL,
+    FULLY_LIVE_REGION_LABEL,
+    FULLY_LIVE_HUMONGOUS_REGION_LABEL,
+    THIRD_LIVE_REGION_LABEL,
+    THIRD_LIVE_COLLECITON_SET_REGION_LABEL,
+    THIRD_LIVE_PINNED_REGION_LABEL,
+
+    MARKING_STATUS,
+    EVACUATING_STATUS,
+    MARKING_AND_EVACUATING_STATUS,
+    IDLE_STATUS,
+
+    TIME_STAT_LABEL,
+    STATUS_STAT_LABEL,
+    TOTAL_STAT_LABEL,
+    USED_STAT_LABEL,
+    LIVE_STAT_LABEL,
+    ;
+
+    static final String RESOURCE_BUNDLE =
+            "com.redhat.thermostat.vm.shenandoah.client.swing.internal.locale.strings";
+
+    public static Translate<LocaleResources> createLocalizer() {
+        return new Translate<>(RESOURCE_BUNDLE, LocaleResources.class);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-shenandoah/client-swing/src/main/resources/com/redhat/thermostat/vm/shenandoah/client/swing/internal/locale/strings.properties	Mon Mar 06 09:38:30 2017 -0500
@@ -0,0 +1,21 @@
+UNUSED_REGION_LABEL=Unused
+EMPTY_REGION_LABEL=Empty
+HALF_USED_REGION_LABEL=1/2 Used
+FULLY_USED_REGION_LABEL=Fully Used
+FULLY_USED_RECENTLY_ALLOCATED_REGION_LABEL=Fully Used, Recently Allocated
+FULLY_LIVE_REGION_LABEL=Fully Live
+FULLY_LIVE_HUMONGOUS_REGION_LABEL=Fully Live + Humongous
+THIRD_LIVE_REGION_LABEL=1/3 Live
+THIRD_LIVE_COLLECITON_SET_REGION_LABEL=1/3 Live + In Collection Set
+THIRD_LIVE_PINNED_REGION_LABEL=1/3 Live + Pinned
+
+MARKING_STATUS=marking
+EVACUATING_STATUS=evacuating
+MARKING_AND_EVACUATING_STATUS=marking, evacuating
+IDLE_STATUS=idle
+
+TIME_STAT_LABEL=Time: {0}
+STATUS_STAT_LABEL=Status: {0}
+TOTAL_STAT_LABEL=Total: {0} KB
+USED_STAT_LABEL=Used: {0} KB
+LIVE_STAT_LABEL=Live: {0} KB
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-shenandoah/client-swing/src/test/java/com/redhat/thermostat/vm/shenandoah/client/swing/internal/ActivatorTest.java	Mon Mar 06 09:38:30 2017 -0500
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2012-2017 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.vm.shenandoah.client.swing.internal;
+
+import com.redhat.thermostat.testutils.StubBundleContext;
+import com.redhat.thermostat.vm.shenandoah.client.core.VmShenandoahViewProvider;
+import org.junit.Before;
+import org.junit.Test;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+public class ActivatorTest {
+
+    private StubBundleContext context;
+    private Activator activator;
+
+    @Before
+    public void setup() {
+        context = new StubBundleContext();
+        activator = new Activator();
+    }
+
+    @Test
+    public void verifyViewProviderRegistered() throws Exception {
+        assertFalse(context.isServiceRegistered(VmShenandoahViewProvider.class.getName(), SwingVmShenandoahViewProvider.class));
+        activator.start(context);
+        assertTrue(context.isServiceRegistered(VmShenandoahViewProvider.class.getName(), SwingVmShenandoahViewProvider.class));
+        activator.stop(context);
+        assertFalse(context.isServiceRegistered(VmShenandoahViewProvider.class.getName(), SwingVmShenandoahViewProvider.class));
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-shenandoah/client-swing/src/test/java/com/redhat/thermostat/vm/shenandoah/client/swing/internal/RegionStatTest.java	Mon Mar 06 09:38:30 2017 -0500
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2012-2017 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.vm.shenandoah.client.swing.internal;
+
+import org.junit.Test;
+
+import java.util.EnumSet;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+
+public class RegionStatTest {
+
+    private RegionStat stat;
+
+    @Test
+    public void testConstructor() {
+        stat = new RegionStat(0xfffffff, 0xffff0000);
+        assertThat(stat.used(), is(1.0));
+        assertThat(stat.live(), is(1.0));
+        EnumSet<RegionFlag> flags = stat.flags();
+        assertTrue(flags.contains(RegionFlag.UNUSED));
+        assertTrue(flags.contains(RegionFlag.IN_COLLECTION_SET));
+        assertTrue(flags.contains(RegionFlag.HUMONGOUS));
+        assertTrue(flags.contains(RegionFlag.RECENTLY_ALLOCATED));
+        assertTrue(flags.contains(RegionFlag.PINNED));
+    }
+
+    @Test
+    public void testConstructor2() {
+        stat = new RegionStat(0x0f, 0x00f);
+        assertThat(stat.used(), is(1.0));
+        assertThat(stat.live(), is(0.0));
+        EnumSet<RegionFlag> flags = stat.flags();
+        assertFalse(flags.contains(RegionFlag.UNUSED));
+        assertFalse(flags.contains(RegionFlag.IN_COLLECTION_SET));
+        assertFalse(flags.contains(RegionFlag.HUMONGOUS));
+        assertFalse(flags.contains(RegionFlag.RECENTLY_ALLOCATED));
+        assertFalse(flags.contains(RegionFlag.PINNED));
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-shenandoah/client-swing/src/test/java/com/redhat/thermostat/vm/shenandoah/client/swing/internal/SwingVmShenandoahViewProviderTest.java	Mon Mar 06 09:38:30 2017 -0500
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2012-2017 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.vm.shenandoah.client.swing.internal;
+
+import com.redhat.thermostat.annotations.internal.CacioTest;
+import com.redhat.thermostat.vm.shenandoah.client.core.VmShenandoahView;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+
+import static junit.framework.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+@Category(CacioTest.class)
+public class SwingVmShenandoahViewProviderTest {
+
+    private SwingVmShenandoahViewProvider viewProvider;
+
+    @Before
+    public void setup() {
+        viewProvider = new SwingVmShenandoahViewProvider();
+    }
+
+    @Test
+    public void testCreateView() {
+        VmShenandoahView view = viewProvider.createView();
+        assertNotNull(view);
+        assertTrue(view instanceof SwingVmShenandoahView);
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-shenandoah/client-swing/src/test/java/com/redhat/thermostat/vm/shenandoah/client/swing/internal/locale/LocaleResourcesTest.java	Mon Mar 06 09:38:30 2017 -0500
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2012-2017 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.vm.shenandoah.client.swing.internal.locale;
+
+import com.redhat.thermostat.testutils.AbstractLocaleResourcesTest;
+
+public class LocaleResourcesTest extends AbstractLocaleResourcesTest<LocaleResources> {
+
+    @Override
+    protected Class<LocaleResources> getEnumClass() {
+        return LocaleResources.class;
+    }
+
+    @Override
+    protected String getResourceBundle() {
+        return LocaleResources.RESOURCE_BUNDLE;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-shenandoah/common/pom.xml	Mon Mar 06 09:38:30 2017 -0500
@@ -0,0 +1,131 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+ Copyright 2012-2017 Red Hat, Inc.
+
+ This file is part of Thermostat.
+
+ Thermostat is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published
+ by the Free Software Foundation; either version 2, or (at your
+ option) any later version.
+
+ Thermostat is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Thermostat; see the file COPYING.  If not see
+ <http://www.gnu.org/licenses/>.
+
+ Linking this code with other modules is making a combined work
+ based on this code.  Thus, the terms and conditions of the GNU
+ General Public License cover the whole combination.
+
+ As a special exception, the copyright holders of this code give
+ you permission to link this code with independent modules to
+ produce an executable, regardless of the license terms of these
+ independent modules, and to copy and distribute the resulting
+ executable under terms of your choice, provided that you also
+ meet, for each linked independent module, the terms and conditions
+ of the license of that module.  An independent module is a module
+ which is not derived from or based on this code.  If you modify
+ this code, you may extend this exception to your version of the
+ library, but you are not obligated to do so.  If you do not wish
+ to do so, delete this exception statement from your version.
+
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <artifactId>thermostat-vm-shenandoah</artifactId>
+    <groupId>com.redhat.thermostat</groupId>
+    <version>1.99.12-SNAPSHOT</version>
+  </parent>
+  <artifactId>thermostat-vm-shenandoah-common</artifactId>
+  <packaging>bundle</packaging>
+  <name>Thermostat VM Shenandoah Common plugin</name>
+  <build>
+    <resources>
+      <resource>
+        <directory>src/main/resources</directory>
+        <filtering>true</filtering>
+        <excludes>
+          <exclude>**/*.png</exclude>
+          <exclude>**/*.xml</exclude>
+          <exclude>**/*.xsd</exclude>
+        </excludes>
+      </resource>
+      <resource>
+        <directory>src/main/resources</directory>
+        <filtering>false</filtering>
+        <includes>
+          <include>**/*.png</include>
+          <include>**/*.xml</include>
+          <include>**/*.xsd</include>
+        </includes>
+      </resource>
+    </resources>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.felix</groupId>
+        <artifactId>maven-bundle-plugin</artifactId>
+        <extensions>true</extensions>
+        <configuration>
+          <instructions>
+            <Bundle-Vendor>Red Hat, Inc.</Bundle-Vendor>
+            <Bundle-SymbolicName>com.redhat.thermostat.vm.shenandoah.common</Bundle-SymbolicName>
+            <Bundle-Activator>com.redhat.thermostat.vm.shenandoah.common.internal.Activator</Bundle-Activator>
+            <Export-Package>
+              com.redhat.thermostat.vm.shenandoah.common,
+            </Export-Package>
+            <Private-Package>
+              com.redhat.thermostat.vm.shenandoah.common.internal,
+            </Private-Package>
+            <!-- Do not autogenerate uses clauses in Manifests -->
+            <_nouses>true</_nouses>
+          </instructions>
+        </configuration>
+      </plugin>
+    </plugins>
+  </build>
+  <dependencies>
+    <dependency>
+      <groupId>junit</groupId>
+      <artifactId>junit</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.mockito</groupId>
+      <artifactId>mockito-core</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.osgi</groupId>
+      <artifactId>org.osgi.core</artifactId>
+      <scope>provided</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.osgi</groupId>
+      <artifactId>org.osgi.compendium</artifactId>
+      <scope>provided</scope>
+    </dependency>
+    <dependency>
+      <groupId>com.redhat.thermostat</groupId>
+      <artifactId>thermostat-common-core</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>com.redhat.thermostat</groupId>
+      <artifactId>thermostat-storage-core</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>com.redhat.thermostat</groupId>
+      <artifactId>thermostat-common-test</artifactId>
+      <version>${project.version}</version>
+      <scope>test</scope>
+    </dependency>
+  </dependencies>
+</project>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-shenandoah/common/src/main/java/com/redhat/thermostat/vm/shenandoah/common/VmShenandoahDao.java	Mon Mar 06 09:38:30 2017 -0500
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2012-2017 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.vm.shenandoah.common;
+
+import com.redhat.thermostat.annotations.Service;
+import com.redhat.thermostat.storage.core.Category;
+import com.redhat.thermostat.storage.core.Key;
+import com.redhat.thermostat.storage.core.VmRef;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+@Service
+public interface VmShenandoahDao {
+
+    Key<Long> REGION_TIMESTAMP_KEY = new Key<>("regionTimeStamp");
+    Key<Long> MAX_REGIONS_KEY = new Key<>("maxRegions");
+    Key<Long> REGION_SIZE_KEY = new Key<>("regionSize");
+    Key<Long> REGION_STATUS_KEY = new Key<>("regionStatus");
+    Key<Long[]> REGION_DATA_KEY = new Key<>("regionData");
+
+    Category<VmShenandoahStat> VM_SHENANDOAH_STAT_CATEGORY = new Category<>("vm-shenandoah-stats", VmShenandoahStat.class,
+            Arrays.<Key<?>>asList(Key.AGENT_ID, Key.VM_ID, Key.TIMESTAMP, REGION_TIMESTAMP_KEY, MAX_REGIONS_KEY, REGION_SIZE_KEY, REGION_STATUS_KEY, REGION_DATA_KEY),
+            Collections.<Key<?>>singletonList(Key.TIMESTAMP));
+
+    void putShenandoahStat(VmShenandoahStat vmShenandoahStat);
+
+    List<VmShenandoahStat> getLatestShenandoahStats(VmRef ref, long since);
+
+    long getCount(VmRef ref);
+
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-shenandoah/common/src/main/java/com/redhat/thermostat/vm/shenandoah/common/VmShenandoahStat.java	Mon Mar 06 09:38:30 2017 -0500
@@ -0,0 +1,172 @@
+/*
+ * Copyright 2012-2017 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.vm.shenandoah.common;
+
+import com.redhat.thermostat.storage.core.Entity;
+import com.redhat.thermostat.storage.core.Persist;
+import com.redhat.thermostat.storage.model.BasePojo;
+import com.redhat.thermostat.storage.model.TimeStampedPojo;
+
+import java.util.Arrays;
+
+@Entity
+public class VmShenandoahStat extends BasePojo implements TimeStampedPojo {
+
+    private String vmId;
+    private long timeStamp;
+    private long regionTimeStamp;
+    private long maxRegions;
+    private long regionSize;
+    private long regionStatus;
+    private long[] regionData;
+
+    public VmShenandoahStat() {
+        super(null);
+    }
+
+    public VmShenandoahStat(String writerId, String vmId, long timeStamp,
+                            long regionTimeStamp, long maxRegions, long regionSize, long regionStatus, long[] regionData) {
+        super(writerId);
+        this.vmId = vmId;
+        this.timeStamp = timeStamp;
+        this.regionTimeStamp = regionTimeStamp;
+        this.maxRegions = maxRegions;
+        this.regionSize = regionSize;
+        this.regionStatus = regionStatus;
+        this.regionData = regionData;
+    }
+
+    @Persist
+    public String getVmId() {
+        return vmId;
+    }
+
+    @Persist
+    public void setVmId(String vmId) {
+        this.vmId = vmId;
+    }
+
+    @Persist
+    public long getTimeStamp() {
+        return timeStamp;
+    }
+
+    @Persist
+    public void setTimeStamp(long timeStamp) {
+        this.timeStamp = timeStamp;
+    }
+
+    @Persist
+    public long getRegionTimeStamp() {
+        return regionTimeStamp;
+    }
+
+    @Persist
+    public void setRegionTimeStamp(long regionTimeStamp) {
+        this.regionTimeStamp = regionTimeStamp;
+    }
+
+    @Persist
+    public long getMaxRegions() {
+        return maxRegions;
+    }
+
+    @Persist
+    public void setMaxRegions(long maxRegions) {
+        this.maxRegions = maxRegions;
+    }
+
+    @Persist
+    public long getRegionSize() {
+        return regionSize;
+    }
+
+    @Persist
+    public void setRegionSize(long regionSize) {
+        this.regionSize = regionSize;
+    }
+
+    @Persist
+    public long getRegionStatus() {
+        return regionStatus;
+    }
+
+    @Persist
+    public void setRegionStatus(long regionStatus) {
+        this.regionStatus = regionStatus;
+    }
+
+    @Persist
+    public long[] getRegionData() {
+        return regionData;
+    }
+
+    @Persist
+    public void setRegionData(long[] regionData) {
+        this.regionData = regionData;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        if (!super.equals(o)) return false;
+
+        VmShenandoahStat that = (VmShenandoahStat) o;
+
+        if (getTimeStamp() != that.getTimeStamp()) return false;
+        if (getRegionTimeStamp() != that.getRegionTimeStamp()) return false;
+        if (getMaxRegions() != that.getMaxRegions()) return false;
+        if (getRegionSize() != that.getRegionSize()) return false;
+        if (getRegionStatus() != that.getRegionStatus()) return false;
+        if (!getVmId().equals(that.getVmId())) return false;
+        return Arrays.equals(getRegionData(), that.getRegionData());
+    }
+
+    @Override
+    public int hashCode() {
+        int result = super.hashCode();
+        result = 31 * result + getVmId().hashCode();
+        result = 31 * result + (int) (getTimeStamp() ^ (getTimeStamp() >>> 32));
+        result = 31 * result + (int) (getRegionTimeStamp() ^ (getRegionTimeStamp() >>> 32));
+        result = 31 * result + (int) (getMaxRegions() ^ (getMaxRegions() >>> 32));
+        result = 31 * result + (int) (getRegionSize() ^ (getRegionSize() >>> 32));
+        result = 31 * result + (int) (getRegionStatus() ^ (getRegionStatus() >>> 32));
+        result = 31 * result + Arrays.hashCode(getRegionData());
+        return result;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-shenandoah/common/src/main/java/com/redhat/thermostat/vm/shenandoah/common/internal/Activator.java	Mon Mar 06 09:38:30 2017 -0500
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2012-2017 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.vm.shenandoah.common.internal;
+
+import com.redhat.thermostat.common.MultipleServiceTracker;
+import com.redhat.thermostat.storage.core.Storage;
+import com.redhat.thermostat.vm.shenandoah.common.VmShenandoahDao;
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceRegistration;
+
+public class Activator implements BundleActivator {
+
+    private MultipleServiceTracker tracker;
+    private ServiceRegistration registration;
+
+    @Override
+    public void start(final BundleContext bundleContext) throws Exception {
+        tracker = new MultipleServiceTracker(bundleContext, new Class<?>[]{Storage.class}, new MultipleServiceTracker.Action() {
+            @Override
+            public void dependenciesAvailable(MultipleServiceTracker.DependencyProvider services) {
+                Storage storage = services.get(Storage.class);
+                registration = bundleContext.registerService(VmShenandoahDao.class.getName(), new VmShenandoahDaoImpl(storage), null);
+            }
+
+            @Override
+            public void dependenciesUnavailable() {
+                registration.unregister();
+            }
+        });
+        tracker.open();
+    }
+
+    @Override
+    public void stop(BundleContext bundleContext) throws Exception {
+        if (tracker != null) {
+            tracker.close();
+        }
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-shenandoah/common/src/main/java/com/redhat/thermostat/vm/shenandoah/common/internal/VmShenandoahDaoImpl.java	Mon Mar 06 09:38:30 2017 -0500
@@ -0,0 +1,125 @@
+/*
+ * Copyright 2012-2017 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.vm.shenandoah.common.internal;
+
+import com.redhat.thermostat.common.utils.LoggingUtils;
+import com.redhat.thermostat.storage.core.Category;
+import com.redhat.thermostat.storage.core.CategoryAdapter;
+import com.redhat.thermostat.storage.core.Key;
+import com.redhat.thermostat.storage.core.PreparedStatement;
+import com.redhat.thermostat.storage.core.Storage;
+import com.redhat.thermostat.storage.core.VmLatestPojoListGetter;
+import com.redhat.thermostat.storage.core.VmRef;
+import com.redhat.thermostat.storage.dao.AbstractDao;
+import com.redhat.thermostat.storage.dao.AbstractDaoQuery;
+import com.redhat.thermostat.storage.dao.AbstractDaoStatement;
+import com.redhat.thermostat.storage.model.AggregateCount;
+import com.redhat.thermostat.vm.shenandoah.common.VmShenandoahDao;
+import com.redhat.thermostat.vm.shenandoah.common.VmShenandoahStat;
+
+import java.util.List;
+import java.util.logging.Logger;
+
+public class VmShenandoahDaoImpl extends AbstractDao implements VmShenandoahDao {
+
+    private static final Logger logger = LoggingUtils.getLogger(VmShenandoahDaoImpl.class);
+
+    static final String DESC_ADD_VM_SHENANDOAH_STAT = "ADD " + VM_SHENANDOAH_STAT_CATEGORY.getName() +
+           " SET '" + Key.AGENT_ID.getName()               + "' = ?s , " +
+                "'" + Key.VM_ID.getName()                  + "' = ?s , " +
+                "'" + Key.TIMESTAMP.getName()              + "' = ?l , " +
+                "'" + REGION_TIMESTAMP_KEY.getName()       + "' = ?l , " +
+                "'" + MAX_REGIONS_KEY.getName()            + "' = ?l , " +
+                "'" + REGION_SIZE_KEY.getName()            + "' = ?l , " +
+                "'" + REGION_STATUS_KEY.getName()          + "' = ?l , " +
+                "'" + REGION_DATA_KEY.getName()            + "' = ?l[";
+
+    static final String QUERY_COUNT_SHENANDOAH_STATS_BY_VM_ID = ""
+            + "QUERY-COUNT " + VM_SHENANDOAH_STAT_CATEGORY.getName() + " "
+            + "WHERE '" + Key.VM_ID.getName() + "' = ?s";
+
+    private final Storage storage;
+    final Category<AggregateCount> aggregateCountCategory;
+    final VmLatestPojoListGetter<VmShenandoahStat> getter;
+
+    public VmShenandoahDaoImpl(Storage storage) {
+        this.storage = storage;
+        storage.registerCategory(VM_SHENANDOAH_STAT_CATEGORY);
+        this.aggregateCountCategory = new CategoryAdapter<VmShenandoahStat, AggregateCount>(VM_SHENANDOAH_STAT_CATEGORY).getAdapted(AggregateCount.class);
+        storage.registerCategory(aggregateCountCategory);
+        this.getter = new VmLatestPojoListGetter<>(storage, VM_SHENANDOAH_STAT_CATEGORY);
+    }
+
+    @Override
+    public void putShenandoahStat(final VmShenandoahStat vmShenandoahStat) {
+        executeStatement(new AbstractDaoStatement<VmShenandoahStat>(storage, VM_SHENANDOAH_STAT_CATEGORY, DESC_ADD_VM_SHENANDOAH_STAT) {
+            @Override
+            public PreparedStatement<VmShenandoahStat> customize(PreparedStatement<VmShenandoahStat> preparedStatement) {
+                preparedStatement.setString(0, vmShenandoahStat.getAgentId());
+                preparedStatement.setString(1, vmShenandoahStat.getVmId());
+                preparedStatement.setLong(2, vmShenandoahStat.getTimeStamp());
+                preparedStatement.setLong(3, vmShenandoahStat.getRegionTimeStamp());
+                preparedStatement.setLong(4, vmShenandoahStat.getMaxRegions());
+                preparedStatement.setLong(5, vmShenandoahStat.getRegionSize());
+                preparedStatement.setLong(6, vmShenandoahStat.getRegionStatus());
+                preparedStatement.setLongList(7, vmShenandoahStat.getRegionData());
+                return preparedStatement;
+            }
+        });
+    }
+
+    @Override
+    public List<VmShenandoahStat> getLatestShenandoahStats(VmRef ref, long since) {
+        return getter.getLatest(ref, since);
+    }
+
+    @Override
+    public long getCount(final VmRef ref) {
+        return executeQuery(new AbstractDaoQuery<AggregateCount>(storage, aggregateCountCategory, QUERY_COUNT_SHENANDOAH_STATS_BY_VM_ID) {
+            @Override
+            public PreparedStatement<AggregateCount> customize(PreparedStatement<AggregateCount> preparedStatement) {
+                preparedStatement.setString(0, ref.getVmId());
+                return preparedStatement;
+            }
+        }).head().getCount();
+    }
+
+    @Override
+    protected Logger getLogger() {
+        return logger;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-shenandoah/common/src/main/java/com/redhat/thermostat/vm/shenandoah/common/internal/VmShenandoahDaoImplStatementDescriptorRegistration.java	Mon Mar 06 09:38:30 2017 -0500
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2012-2017 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.vm.shenandoah.common.internal;
+
+import com.redhat.thermostat.storage.core.VmLatestPojoListGetter;
+import com.redhat.thermostat.storage.core.auth.StatementDescriptorRegistration;
+import com.redhat.thermostat.vm.shenandoah.common.VmShenandoahDao;
+
+import java.util.HashSet;
+import java.util.Set;
+
+public class VmShenandoahDaoImplStatementDescriptorRegistration implements StatementDescriptorRegistration {
+
+    static final String descriptor = String.format(
+            VmLatestPojoListGetter.VM_LATEST_QUERY_FORMAT,
+            VmShenandoahDao.VM_SHENANDOAH_STAT_CATEGORY.getName());
+
+    @Override
+    public Set<String> getStatementDescriptors() {
+        Set<String> descs = new HashSet<>(2);
+        descs.add(descriptor);
+        descs.add(VmShenandoahDaoImpl.DESC_ADD_VM_SHENANDOAH_STAT);
+        descs.add(VmShenandoahDaoImpl.QUERY_COUNT_SHENANDOAH_STATS_BY_VM_ID);
+        return descs;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-shenandoah/common/src/main/java/com/redhat/thermostat/vm/shenandoah/common/internal/VmShenandoahStatCategoryRegistration.java	Mon Mar 06 09:38:30 2017 -0500
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2012-2017 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.vm.shenandoah.common.internal;
+
+import com.redhat.thermostat.storage.core.auth.CategoryRegistration;
+import com.redhat.thermostat.vm.shenandoah.common.VmShenandoahDao;
+
+import java.util.HashSet;
+import java.util.Set;
+
+public class VmShenandoahStatCategoryRegistration implements CategoryRegistration {
+    @Override
+    public Set<String> getCategoryNames() {
+        Set<String> categories = new HashSet<>(1);
+        categories.add(VmShenandoahDao.VM_SHENANDOAH_STAT_CATEGORY.getName());
+        return categories;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-shenandoah/common/src/main/resources/META-INF/services/com.redhat.thermostat.storage.core.auth.CategoryRegistration	Mon Mar 06 09:38:30 2017 -0500
@@ -0,0 +1,1 @@
+com.redhat.thermostat.vm.shenandoah.common.internal.VmShenandoahStatCategoryRegistration
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-shenandoah/common/src/main/resources/META-INF/services/com.redhat.thermostat.storage.core.auth.StatementDescriptorRegistration	Mon Mar 06 09:38:30 2017 -0500
@@ -0,0 +1,1 @@
+com.redhat.thermostat.vm.shenandoah.common.internal.VmShenandoahDaoImplStatementDescriptorRegistration
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-shenandoah/common/src/test/java/com/redhat/thermostat/vm/shenandoah/common/internal/ActivatorTest.java	Mon Mar 06 09:38:30 2017 -0500
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2012-2017 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.vm.shenandoah.common.internal;
+
+import com.redhat.thermostat.storage.core.Storage;
+import com.redhat.thermostat.testutils.StubBundleContext;
+import com.redhat.thermostat.vm.shenandoah.common.VmShenandoahDao;
+import org.junit.Before;
+import org.junit.Test;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
+
+public class ActivatorTest {
+
+    private Storage storage;
+    private StubBundleContext context;
+    private Activator activator;
+
+    @Before
+    public void setup() {
+        storage = mock(Storage.class);
+        context = new StubBundleContext();
+        activator = new Activator();
+    }
+
+    @Test
+    public void testDoesNotRegisterDaoIfStorageUnavailable() throws Exception {
+        assertFalse(context.isServiceRegistered(VmShenandoahDao.class.getName(), VmShenandoahDaoImpl.class));
+        activator.start(context);
+        assertFalse(context.isServiceRegistered(VmShenandoahDao.class.getName(), VmShenandoahDaoImpl.class));
+    }
+
+    @Test
+    public void testRegistersDaoWhenStorageAvailable() throws Exception {
+        context.registerService(Storage.class.getName(), storage, null);
+        assertFalse(context.isServiceRegistered(VmShenandoahDao.class.getName(), VmShenandoahDaoImpl.class));
+        activator.start(context);
+        assertTrue(context.isServiceRegistered(VmShenandoahDao.class.getName(), VmShenandoahDaoImpl.class));
+    }
+
+    @Test
+    public void testUnregistersDaoWhenStopped() throws Exception {
+        context.registerService(Storage.class.getName(), storage, null);
+        assertFalse(context.isServiceRegistered(VmShenandoahDao.class.getName(), VmShenandoahDaoImpl.class));
+        activator.start(context);
+        assertTrue(context.isServiceRegistered(VmShenandoahDao.class.getName(), VmShenandoahDaoImpl.class));
+        activator.stop(context);
+        assertFalse(context.isServiceRegistered(VmShenandoahDao.class.getName(), VmShenandoahDaoImpl.class));
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-shenandoah/common/src/test/java/com/redhat/thermostat/vm/shenandoah/common/internal/VmShenandoahDaoImplStatementDescriptorRegistrationTest.java	Mon Mar 06 09:38:30 2017 -0500
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2012-2017 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.vm.shenandoah.common.internal;
+
+import com.redhat.thermostat.storage.core.auth.StatementDescriptorRegistration;
+import com.redhat.thermostat.testutils.ServiceLoaderTest;
+import org.junit.Test;
+
+import java.util.Set;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+
+public class VmShenandoahDaoImplStatementDescriptorRegistrationTest extends ServiceLoaderTest<StatementDescriptorRegistration> {
+
+    public VmShenandoahDaoImplStatementDescriptorRegistrationTest() {
+        super(StatementDescriptorRegistration.class, STORAGE_SERVICES, VmShenandoahDaoImplStatementDescriptorRegistration.class);
+    }
+
+    @Test
+    public void registersAllDescriptors() {
+        VmShenandoahDaoImplStatementDescriptorRegistration reg = new VmShenandoahDaoImplStatementDescriptorRegistration();
+        Set<String> descriptors = reg.getStatementDescriptors();
+        assertEquals(3, descriptors.size());
+        assertFalse("null descriptor not allowed", descriptors.contains(null));
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-shenandoah/common/src/test/java/com/redhat/thermostat/vm/shenandoah/common/internal/VmShenandoahDaoImplTest.java	Mon Mar 06 09:38:30 2017 -0500
@@ -0,0 +1,155 @@
+/*
+ * Copyright 2012-2017 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.vm.shenandoah.common.internal;
+
+import com.redhat.thermostat.storage.core.BasicBatchCursor;
+import com.redhat.thermostat.storage.core.Cursor;
+import com.redhat.thermostat.storage.core.DescriptorParsingException;
+import com.redhat.thermostat.storage.core.HostRef;
+import com.redhat.thermostat.storage.core.Key;
+import com.redhat.thermostat.storage.core.PreparedStatement;
+import com.redhat.thermostat.storage.core.StatementDescriptor;
+import com.redhat.thermostat.storage.core.StatementExecutionException;
+import com.redhat.thermostat.storage.core.Storage;
+import com.redhat.thermostat.storage.core.VmRef;
+import com.redhat.thermostat.storage.model.AggregateCount;
+import com.redhat.thermostat.storage.model.Pojo;
+import com.redhat.thermostat.storage.model.VmInfo;
+import com.redhat.thermostat.vm.shenandoah.common.VmShenandoahDao;
+import com.redhat.thermostat.vm.shenandoah.common.VmShenandoahStat;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+
+import java.util.Collection;
+import java.util.NoSuchElementException;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+public class VmShenandoahDaoImplTest {
+
+    private Storage storage;
+    private PreparedStatement stmt;
+    private VmShenandoahDaoImpl dao;
+
+    @Before
+    public void seup() throws DescriptorParsingException {
+        storage = mock(Storage.class);
+        stmt = mock(PreparedStatement.class);
+        when(storage.prepareStatement(any(StatementDescriptor.class))).thenReturn(stmt);
+        dao = new VmShenandoahDaoImpl(storage);
+    }
+
+    @Test
+    public void testRegistersStatCategory() {
+        verify(storage).registerCategory(VmShenandoahDao.VM_SHENANDOAH_STAT_CATEGORY);
+    }
+
+    @Test
+    public void testRegistersAggregateCategory() {
+        verify(storage).registerCategory(dao.aggregateCountCategory);
+    }
+
+    @Test
+    public void testCategory() {
+        assertEquals("vm-shenandoah-stats", VmShenandoahDao.VM_SHENANDOAH_STAT_CATEGORY.getName());
+        Collection<Key<?>> keys = VmShenandoahDao.VM_SHENANDOAH_STAT_CATEGORY.getKeys();
+        assertTrue(keys.contains(new Key<>("agentId")));
+        assertTrue(keys.contains(new Key<Integer>("vmId")));
+        assertTrue(keys.contains(new Key<Long>("timeStamp")));
+        assertTrue(keys.contains(new Key<Long>("regionTimeStamp")));
+        assertTrue(keys.contains(new Key<Long>("maxRegions")));
+        assertTrue(keys.contains(new Key<Long>("regionSize")));
+        assertTrue(keys.contains(new Key<Long>("regionStatus")));
+        assertTrue(keys.contains(new Key<Long>("regionData")));
+        assertEquals(8, keys.size());
+    }
+
+    @Test
+    @SuppressWarnings("unchecked")
+    public void testPutShenandoahStat() throws DescriptorParsingException {
+        VmShenandoahStat stat = new VmShenandoahStat("foo-writerId", "foo-vmId", 100L, 50L, 2L, 1024L, 1L, new long[]{1L, 2L});
+        stat.setAgentId("foo-agentId");
+        dao.putShenandoahStat(stat);
+
+        ArgumentCaptor<StatementDescriptor> descriptorCaptor = ArgumentCaptor.forClass(StatementDescriptor.class);
+        verify(storage).prepareStatement(descriptorCaptor.capture());
+        StatementDescriptor<VmShenandoahStat> descriptor = descriptorCaptor.getValue();
+        assertThat(descriptor.getCategory(), is(VmShenandoahDao.VM_SHENANDOAH_STAT_CATEGORY));
+        assertThat(descriptor.getDescriptor(), is(VmShenandoahDaoImpl.DESC_ADD_VM_SHENANDOAH_STAT));
+
+        verify(stmt).setString(0, stat.getAgentId());
+        verify(stmt).setString(1, stat.getVmId());
+        verify(stmt).setLong(2, stat.getTimeStamp());
+        verify(stmt).setLong(3, stat.getRegionTimeStamp());
+        verify(stmt).setLong(4, stat.getMaxRegions());
+        verify(stmt).setLong(5, stat.getRegionSize());
+        verify(stmt).setLong(6, stat.getRegionStatus());
+        verify(stmt).setLongList(7, stat.getRegionData());
+    }
+
+    @Test
+    @SuppressWarnings("unchecked")
+    public void testGetCount() throws DescriptorParsingException, StatementExecutionException {
+        AggregateCount aggregateCount = new AggregateCount();
+        aggregateCount.setCount(2L);
+
+        Cursor<AggregateCount> cursor = mock(Cursor.class);
+        when(cursor.hasNext()).thenReturn(true).thenReturn(false);
+        when(cursor.next()).thenReturn(aggregateCount).thenThrow(NoSuchElementException.class);
+
+        when(stmt.executeQuery()).thenReturn(cursor);
+
+        HostRef hostRef = new HostRef("host-id", "host-name");
+        VmInfo vmInfo = new VmInfo();
+        dao.getCount(new VmRef(hostRef, vmInfo));
+
+        ArgumentCaptor<StatementDescriptor> descriptorCaptor = ArgumentCaptor.forClass(StatementDescriptor.class);
+        verify(storage).prepareStatement(descriptorCaptor.capture());
+        StatementDescriptor<AggregateCount> descriptor = descriptorCaptor.getValue();
+        assertThat(descriptor.getCategory(), is(dao.aggregateCountCategory));
+        assertThat(descriptor.getDescriptor(), is(VmShenandoahDaoImpl.QUERY_COUNT_SHENANDOAH_STATS_BY_VM_ID));
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-shenandoah/common/src/test/java/com/redhat/thermostat/vm/shenandoah/common/internal/VmShenandoahStatCategoryRegistrationTest.java	Mon Mar 06 09:38:30 2017 -0500
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2012-2017 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.vm.shenandoah.common.internal;
+
+import com.redhat.thermostat.storage.core.auth.CategoryRegistration;
+import com.redhat.thermostat.testutils.ServiceLoaderTest;
+import com.redhat.thermostat.vm.shenandoah.common.VmShenandoahDao;
+import org.junit.Test;
+
+import java.util.Set;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+public class VmShenandoahStatCategoryRegistrationTest extends ServiceLoaderTest<CategoryRegistration> {
+
+    public VmShenandoahStatCategoryRegistrationTest() {
+        super(CategoryRegistration.class, STORAGE_SERVICES, VmShenandoahStatCategoryRegistration.class);
+    }
+
+    @Test
+    public void testRegistersAllCategories() {
+        VmShenandoahStatCategoryRegistration reg = new VmShenandoahStatCategoryRegistration();
+        Set<String> categories = reg.getCategoryNames();
+        assertEquals(1, categories.size());
+        assertFalse("null descriptor not allowed", categories.contains(null));
+        assertTrue(categories.contains(VmShenandoahDao.VM_SHENANDOAH_STAT_CATEGORY.getName()));
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-shenandoah/distribution/pom.xml	Mon Mar 06 09:38:30 2017 -0500
@@ -0,0 +1,112 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+ Copyright 2012-2017 Red Hat, Inc.
+
+ This file is part of Thermostat.
+
+ Thermostat is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published
+ by the Free Software Foundation; either version 2, or (at your
+ option) any later version.
+
+ Thermostat is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Thermostat; see the file COPYING.  If not see
+ <http://www.gnu.org/licenses/>.
+
+ Linking this code with other modules is making a combined work
+ based on this code.  Thus, the terms and conditions of the GNU
+ General Public License cover the whole combination.
+
+ As a special exception, the copyright holders of this code give
+ you permission to link this code with independent modules to
+ produce an executable, regardless of the license terms of these
+ independent modules, and to copy and distribute the resulting
+ executable under terms of your choice, provided that you also
+ meet, for each linked independent module, the terms and conditions
+ of the license of that module.  An independent module is a module
+ which is not derived from or based on this code.  If you modify
+ this code, you may extend this exception to your version of the
+ library, but you are not obligated to do so.  If you do not wish
+ to do so, delete this exception statement from your version.
+
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <groupId>com.redhat.thermostat</groupId>
+    <artifactId>thermostat-vm-shenandoah</artifactId>
+    <version>1.99.12-SNAPSHOT</version>
+  </parent>
+
+  <artifactId>thermostat-vm-shenandoah-distribution</artifactId>
+  <packaging>pom</packaging>
+
+  <name>Thermostat Shenandoah GC plug-in - Distribution</name>
+
+  <properties>
+    <thermostat.plugin>vm-shenandoah</thermostat.plugin>
+  </properties>
+
+  <build>
+    <plugins>
+      <plugin>
+        <artifactId>maven-assembly-plugin</artifactId>
+        <dependencies>
+          <dependency>
+            <groupId>com.redhat.thermostat</groupId>
+            <artifactId>thermostat-assembly</artifactId>
+            <version>${project.version}</version>
+          </dependency>
+        </dependencies>
+        <configuration>
+          <descriptorRefs>
+            <descriptorRef>plugin-assembly</descriptorRef>
+          </descriptorRefs>
+          <appendAssemblyId>false</appendAssemblyId>
+        </configuration>
+        <executions>
+          <execution>
+            <id>assemble-plugin</id>
+            <phase>package</phase>
+            <goals>
+              <goal>single</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+    </plugins>
+  </build>
+  
+  <!-- Explicitly list all plug-in artifacts, transitive dependencies
+       are not included in assembly. -->
+  <dependencies>
+    <dependency>
+      <groupId>com.redhat.thermostat</groupId>
+      <artifactId>thermostat-vm-shenandoah-agent</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>com.redhat.thermostat</groupId>
+      <artifactId>thermostat-vm-shenandoah-common</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>com.redhat.thermostat</groupId>
+      <artifactId>thermostat-vm-shenandoah-client-core</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>com.redhat.thermostat</groupId>
+      <artifactId>thermostat-vm-shenandoah-client-swing</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+  </dependencies>
+
+</project>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-shenandoah/distribution/thermostat-plugin.xml	Mon Mar 06 09:38:30 2017 -0500
@@ -0,0 +1,58 @@
+<?xml version="1.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.
+
+-->
+<plugin xmlns="http://icedtea.classpath.org/thermostat/plugins/v1.0"
+        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+        xsi:schemaLocation="http://icedtea.classpath.org/thermostat/plugins/v1.0 thermostat-plugin.xsd">
+  <extensions>
+    <extension>
+      <name>gui</name>
+      <bundles>
+        <bundle><symbolic-name>com.redhat.thermostat.vm.shenandoah.client.core</symbolic-name><version>${project.version}</version></bundle>
+        <bundle><symbolic-name>com.redhat.thermostat.vm.shenandoah.client.swing</symbolic-name><version>${project.version}</version></bundle>
+      </bundles>
+    </extension>
+    <extension>
+      <name>agent</name>
+      <bundles>
+        <bundle><symbolic-name>com.redhat.thermostat.vm.shenandoah.common</symbolic-name><version>${project.version}</version></bundle>
+        <bundle><symbolic-name>com.redhat.thermostat.vm.shenandoah.agent</symbolic-name><version>${project.version}</version></bundle>
+      </bundles>
+    </extension>
+  </extensions>
+</plugin>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-shenandoah/pom.xml	Mon Mar 06 09:38:30 2017 -0500
@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+ Copyright 2012-2017 Red Hat, Inc.
+
+ This file is part of Thermostat.
+
+ Thermostat is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published
+ by the Free Software Foundation; either version 2, or (at your
+ option) any later version.
+
+ Thermostat is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Thermostat; see the file COPYING.  If not see
+ <http://www.gnu.org/licenses/>.
+
+ Linking this code with other modules is making a combined work
+ based on this code.  Thus, the terms and conditions of the GNU
+ General Public License cover the whole combination.
+
+ As a special exception, the copyright holders of this code give
+ you permission to link this code with independent modules to
+ produce an executable, regardless of the license terms of these
+ independent modules, and to copy and distribute the resulting
+ executable under terms of your choice, provided that you also
+ meet, for each linked independent module, the terms and conditions
+ of the license of that module.  An independent module is a module
+ which is not derived from or based on this code.  If you modify
+ this code, you may extend this exception to your version of the
+ library, but you are not obligated to do so.  If you do not wish
+ to do so, delete this exception statement from your version.
+
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <groupId>com.redhat.thermostat</groupId>
+    <artifactId>thermostat</artifactId>
+    <version>1.99.12-SNAPSHOT</version>
+  </parent>
+
+  <artifactId>thermostat-vm-shenandoah</artifactId>
+  <packaging>pom</packaging>
+
+  <name>Thermostat Shenandoah GC plug-in</name>
+
+  <modules>
+    <module>agent</module>
+    <module>common</module>
+    <module>client-core</module>
+    <module>client-swing</module>
+    <module>distribution</module>
+  </modules>
+</project>
\ No newline at end of file
--- a/web/war/pom.xml	Thu Mar 09 13:36:40 2017 -0500
+++ b/web/war/pom.xml	Mon Mar 06 09:38:30 2017 -0500
@@ -204,6 +204,11 @@
     </dependency>
     <dependency>
       <groupId>com.redhat.thermostat</groupId>
+      <artifactId>thermostat-vm-shenandoah-common</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>com.redhat.thermostat</groupId>
       <artifactId>thermostat-vm-heap-analysis-common</artifactId>
       <version>${project.version}</version>
     </dependency>