changeset 895:ceb28ef8f9e8

Create VM GC agent and common bundles This commit extracts VM GC data collection from SystemBackend and related classes to a vm-gc-agent bundle. It also moves the VmGcStatDAO from common-core to a vm-gc-common bundle that registers the DAO once Storage is available. This also removes the DAO from DAOFactory. Reviewed-by: omajid Review-thread: http://icedtea.classpath.org/pipermail/thermostat/2013-January/004997.html
author Elliott Baron <ebaron@redhat.com>
date Mon, 07 Jan 2013 15:50:59 -0500
parents 0ba74f790a8a
children 3cc2f2ea31b3
files common/core/src/main/java/com/redhat/thermostat/common/dao/DAOFactory.java common/core/src/main/java/com/redhat/thermostat/common/dao/DAOFactoryImpl.java common/core/src/main/java/com/redhat/thermostat/common/dao/VmGcStatDAO.java common/core/src/main/java/com/redhat/thermostat/common/dao/VmGcStatDAOImpl.java common/core/src/test/java/com/redhat/thermostat/common/dao/MongoDAOFactoryTest.java common/core/src/test/java/com/redhat/thermostat/common/dao/VmGcStatDAOTest.java distribution/config/commands/agent.properties distribution/config/commands/gui.properties distribution/pom.xml eclipse/com.redhat.thermostat.client.feature/feature.xml system-backend/src/main/java/com/redhat/thermostat/backend/system/JvmStatDataExtractor.java system-backend/src/main/java/com/redhat/thermostat/backend/system/JvmStatHostListener.java system-backend/src/main/java/com/redhat/thermostat/backend/system/JvmStatVmListener.java system-backend/src/main/java/com/redhat/thermostat/backend/system/SystemBackend.java system-backend/src/test/java/com/redhat/thermostat/backend/system/JvmStatDataExtractorTest.java system-backend/src/test/java/com/redhat/thermostat/backend/system/JvmStatHostListenerTest.java vm-gc/agent/pom.xml vm-gc/agent/src/main/java/com/redhat/thermostat/vm/gc/agent/internal/Activator.java vm-gc/agent/src/main/java/com/redhat/thermostat/vm/gc/agent/internal/VmGcBackend.java vm-gc/agent/src/main/java/com/redhat/thermostat/vm/gc/agent/internal/VmGcDataExtractor.java vm-gc/agent/src/main/java/com/redhat/thermostat/vm/gc/agent/internal/VmGcHostListener.java vm-gc/agent/src/main/java/com/redhat/thermostat/vm/gc/agent/internal/VmGcVmListener.java vm-gc/agent/src/test/java/com/redhat/thermostat/vm/gc/agent/internal/ActivatorTest.java vm-gc/agent/src/test/java/com/redhat/thermostat/vm/gc/agent/internal/VmGcBackendTest.java vm-gc/agent/src/test/java/com/redhat/thermostat/vm/gc/agent/internal/VmGcDataExtractorTest.java vm-gc/agent/src/test/java/com/redhat/thermostat/vm/gc/agent/internal/VmGcHostListenerTest.java vm-gc/agent/src/test/java/com/redhat/thermostat/vm/gc/agent/internal/VmGcVmListenerTest.java vm-gc/client-core/pom.xml vm-gc/client-core/src/main/java/com/redhat/thermostat/vm/gc/client/core/VmGcService.java vm-gc/client-core/src/main/java/com/redhat/thermostat/vm/gc/client/core/internal/Activator.java vm-gc/client-core/src/main/java/com/redhat/thermostat/vm/gc/client/core/internal/VmGcController.java vm-gc/client-core/src/test/java/com/redhat/thermostat/vm/gc/client/core/internal/ActivatorTest.java vm-gc/client-core/src/test/java/com/redhat/thermostat/vm/gc/client/core/internal/VmGcControllerTest.java vm-gc/common/pom.xml vm-gc/common/src/main/java/com/redhat/thermostat/vm/gc/common/VmGcStatDAO.java vm-gc/common/src/main/java/com/redhat/thermostat/vm/gc/common/internal/Activator.java vm-gc/common/src/main/java/com/redhat/thermostat/vm/gc/common/internal/VmGcStatDAOImpl.java vm-gc/common/src/test/java/com/redhat/thermostat/vm/gc/common/internal/ActivatorTest.java vm-gc/common/src/test/java/com/redhat/thermostat/vm/gc/common/internal/VmGcStatDAOTest.java vm-gc/pom.xml
diffstat 40 files changed, 1765 insertions(+), 457 deletions(-) [+]
line wrap: on
line diff
--- a/common/core/src/main/java/com/redhat/thermostat/common/dao/DAOFactory.java	Mon Jan 07 15:50:03 2013 -0500
+++ b/common/core/src/main/java/com/redhat/thermostat/common/dao/DAOFactory.java	Mon Jan 07 15:50:59 2013 -0500
@@ -58,8 +58,6 @@
 
     public VmClassStatDAO getVmClassStatsDAO();
 
-    public VmGcStatDAO getVmGcStatDAO();
-
     public void registerDAOsAndStorageAsOSGiServices();
     public void unregisterDAOsAndStorageAsOSGiServices();
 
--- a/common/core/src/main/java/com/redhat/thermostat/common/dao/DAOFactoryImpl.java	Mon Jan 07 15:50:03 2013 -0500
+++ b/common/core/src/main/java/com/redhat/thermostat/common/dao/DAOFactoryImpl.java	Mon Jan 07 15:50:59 2013 -0500
@@ -60,7 +60,6 @@
     private NetworkInterfaceInfoDAO networkInfoDAO;
     private VmInfoDAO vmInfoDAO;
     private VmClassStatDAO vmClassStatDAO;
-    private VmGcStatDAO vmGcStatDAO;
 
     public DAOFactoryImpl(StorageProvider prov) {
         this(FrameworkUtil.getBundle(DAOFactoryImpl.class).getBundleContext(), prov);
@@ -113,12 +112,6 @@
     }
 
     @Override
-    public VmGcStatDAO getVmGcStatDAO() {
-        ensureStorageConnected();
-        return vmGcStatDAO;
-    }
-
-    @Override
     public Storage getStorage() {
         return storage;
     }
@@ -143,7 +136,6 @@
 
         registerAndRecordService(VmInfoDAO.class, getVmInfoDAO());
         registerAndRecordService(VmClassStatDAO.class, getVmClassStatsDAO());
-        registerAndRecordService(VmGcStatDAO.class, getVmGcStatDAO());
     }
 
     /*
@@ -156,7 +148,6 @@
         networkInfoDAO = new NetworkInterfaceInfoDAOImpl(storage);
         vmInfoDAO = new VmInfoDAOImpl(storage);
         vmClassStatDAO = new VmClassStatDAOImpl(storage);
-        vmGcStatDAO = new VmGcStatDAOImpl(storage);
     }
 
     private <K> void registerAndRecordService(Class<K> serviceType, K serviceImplementation) {
--- a/common/core/src/main/java/com/redhat/thermostat/common/dao/VmGcStatDAO.java	Mon Jan 07 15:50:03 2013 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,59 +0,0 @@
-/*
- * Copyright 2012 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.common.dao;
-
-import java.util.List;
-
-import com.redhat.thermostat.storage.core.Category;
-import com.redhat.thermostat.storage.core.Key;
-import com.redhat.thermostat.storage.model.VmGcStat;
-
-public interface VmGcStatDAO {
-
-    static final Key<String> collectorKey = new Key<>("collectorName", false);
-    static final Key<Long> runCountKey = new Key<>("runCount", false);
-    /** time in microseconds */
-    static final Key<Long> wallTimeKey = new Key<>("wallTime", false);
-
-    static final Category vmGcStatCategory = new Category("vm-gc-stats",
-            Key.AGENT_ID, Key.VM_ID, Key.TIMESTAMP, collectorKey,
-            runCountKey, wallTimeKey);
-
-    public List<VmGcStat> getLatestVmGcStats(VmRef ref, long since);
-
-    public void putVmGcStat(VmGcStat stat);
-}
--- a/common/core/src/main/java/com/redhat/thermostat/common/dao/VmGcStatDAOImpl.java	Mon Jan 07 15:50:03 2013 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,65 +0,0 @@
-/*
- * Copyright 2012 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.common.dao;
-
-import java.util.List;
-
-import com.redhat.thermostat.storage.core.Storage;
-import com.redhat.thermostat.storage.model.VmGcStat;
-
-class VmGcStatDAOImpl implements VmGcStatDAO {
-
-    private final Storage storage;
-    private final VmLatestPojoListGetter<VmGcStat> getter;
-
-    VmGcStatDAOImpl(Storage storage) {
-        this.storage = storage;
-        storage.registerCategory(vmGcStatCategory);
-        getter = new VmLatestPojoListGetter<>(storage, vmGcStatCategory, VmGcStat.class);
-    }
-
-    @Override
-    public List<VmGcStat> getLatestVmGcStats(VmRef ref, long since) {
-        return getter.getLatest(ref, since);
-    }
-
-    @Override
-    public void putVmGcStat(VmGcStat stat) {
-        storage.putPojo(vmGcStatCategory, false, stat);
-    }
-
-}
--- a/common/core/src/test/java/com/redhat/thermostat/common/dao/MongoDAOFactoryTest.java	Mon Jan 07 15:50:03 2013 -0500
+++ b/common/core/src/test/java/com/redhat/thermostat/common/dao/MongoDAOFactoryTest.java	Mon Jan 07 15:50:59 2013 -0500
@@ -96,12 +96,6 @@
     }
 
     @Test
-    public void testGetVmGcStatDAO() {
-        VmGcStatDAO dao = daoFactory.getVmGcStatDAO();
-        assertNotNull(dao);
-    }
-
-    @Test
     public void testGetVmInfoDAO() {
         VmInfoDAO dao = daoFactory.getVmInfoDAO();
         assertNotNull(dao);
@@ -125,8 +119,8 @@
 
         daoFactory.registerDAOsAndStorageAsOSGiServices();
 
-        // currently 8 DAOs and Storage are registered
-        assertEquals(8, bundleContext.getAllServices().size());
+        // currently 7 DAOs and Storage are registered
+        assertEquals(7, bundleContext.getAllServices().size());
     }
 
     @Test
--- a/common/core/src/test/java/com/redhat/thermostat/common/dao/VmGcStatDAOTest.java	Mon Jan 07 15:50:03 2013 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,129 +0,0 @@
-/*
- * Copyright 2012 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.common.dao;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.same;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import java.util.Collection;
-import java.util.List;
-
-import org.junit.Test;
-import org.mockito.ArgumentCaptor;
-
-import com.redhat.thermostat.storage.core.Cursor;
-import com.redhat.thermostat.storage.core.Key;
-import com.redhat.thermostat.storage.core.Query;
-import com.redhat.thermostat.storage.core.Storage;
-import com.redhat.thermostat.storage.core.Query.Criteria;
-import com.redhat.thermostat.storage.model.VmGcStat;
-import com.redhat.thermostat.test.MockQuery;
-
-public class VmGcStatDAOTest {
-
-    private static final Integer VM_ID = 123;
-    private static final Long TIMESTAMP = 456L;
-    private static final String COLLECTOR = "collector1";
-    private static final Long RUN_COUNT = 10L;
-    private static final Long WALL_TIME = 9L;
-
-    @Test
-    public void testCategory() {
-        assertEquals("vm-gc-stats", VmGcStatDAO.vmGcStatCategory.getName());
-        Collection<Key<?>> keys = VmGcStatDAO.vmGcStatCategory.getKeys();
-        assertTrue(keys.contains(new Key<>("agentId", true)));
-        assertTrue(keys.contains(new Key<Integer>("vmId", true)));
-        assertTrue(keys.contains(new Key<Long>("timeStamp", false)));
-        assertTrue(keys.contains(new Key<String>("collectorName", false)));
-        assertTrue(keys.contains(new Key<Long>("runCount", false)));
-        assertTrue(keys.contains(new Key<Long>("wallTime", false)));
-        assertEquals(6, keys.size());
-    }
-
-    @Test
-    public void testGetLatestVmGcStatsBasic() {
-
-        VmGcStat vmGcStat = new VmGcStat(VM_ID, TIMESTAMP, COLLECTOR, RUN_COUNT, WALL_TIME);
-
-        @SuppressWarnings("unchecked")
-        Cursor<VmGcStat> cursor = mock(Cursor.class);
-        when(cursor.hasNext()).thenReturn(true).thenReturn(false);
-        when(cursor.next()).thenReturn(vmGcStat);
-
-        Storage storage = mock(Storage.class);
-        when(storage.createQuery()).thenReturn(new MockQuery());
-        when(storage.findAllPojos(any(Query.class), same(VmGcStat.class))).thenReturn(cursor);
-
-        HostRef hostRef = mock(HostRef.class);
-        when(hostRef.getAgentId()).thenReturn("system");
-
-        VmRef vmRef = mock(VmRef.class);
-        when(vmRef.getAgent()).thenReturn(hostRef);
-        when(vmRef.getId()).thenReturn(321);
-
-
-        VmGcStatDAO dao = new VmGcStatDAOImpl(storage);
-        List<VmGcStat> vmGcStats = dao.getLatestVmGcStats(vmRef, Long.MIN_VALUE);
-
-        ArgumentCaptor<MockQuery> arg = ArgumentCaptor.forClass(MockQuery.class);
-        verify(storage).findAllPojos(arg.capture(), same(VmGcStat.class));
-        assertTrue(arg.getValue().hasWhereClause(Key.TIMESTAMP, Criteria.GREATER_THAN, Long.MIN_VALUE));
-
-        assertEquals(1, vmGcStats.size());
-        VmGcStat stat = vmGcStats.get(0);
-        assertEquals(TIMESTAMP, (Long) stat.getTimeStamp());
-        assertEquals(VM_ID, (Integer) stat.getVmId());
-        assertEquals(COLLECTOR, stat.getCollectorName());
-        assertEquals(RUN_COUNT, (Long) stat.getRunCount());
-        assertEquals(WALL_TIME, (Long) stat.getWallTime());
-    }
-
-    @Test
-    public void testPutVmGcStat() {
-        Storage storage = mock(Storage.class);
-        VmGcStat stat = new VmGcStat(VM_ID, TIMESTAMP, COLLECTOR, RUN_COUNT, WALL_TIME);
-        VmGcStatDAO dao = new VmGcStatDAOImpl(storage);
-        dao.putVmGcStat(stat);
-
-        verify(storage).putPojo(VmGcStatDAO.vmGcStatCategory, false, stat);
-    }
-}
--- a/distribution/config/commands/agent.properties	Mon Jan 07 15:50:03 2013 -0500
+++ b/distribution/config/commands/agent.properties	Mon Jan 07 15:50:59 2013 -0500
@@ -17,6 +17,8 @@
           thermostat-vm-cpu-agent-@project.version@.jar, \
           thermostat-vm-memory-common-@project.version@.jar, \
           thermostat-vm-memory-agent-@project.version@.jar, \
+          thermostat-vm-gc-common-@project.version@.jar, \
+          thermostat-vm-gc-agent-@project.version@.jar, \
           thermostat-vm-heap-analysis-common-@project.version@.jar, \
           thermostat-vm-heap-analysis-agent-@project.version@.jar, \
           thermostat-killvm-agent-@project.version@.jar, \
--- a/distribution/config/commands/gui.properties	Mon Jan 07 15:50:03 2013 -0500
+++ b/distribution/config/commands/gui.properties	Mon Jan 07 15:50:59 2013 -0500
@@ -25,6 +25,7 @@
           thermostat-vm-cpu-common-@project.version@.jar, \
           thermostat-vm-cpu-client-core-@project.version@.jar, \
           thermostat-vm-cpu-client-swing-@project.version@.jar, \
+          thermostat-vm-gc-common-@project.version@.jar, \
           thermostat-vm-gc-client-core-@project.version@.jar, \
           thermostat-vm-gc-client-swing-@project.version@.jar, \
           thermostat-vm-memory-common-@project.version@.jar, \
--- a/distribution/pom.xml	Mon Jan 07 15:50:03 2013 -0500
+++ b/distribution/pom.xml	Mon Jan 07 15:50:59 2013 -0500
@@ -427,6 +427,11 @@
     </dependency>
     <dependency>
       <groupId>com.redhat.thermostat</groupId>
+      <artifactId>thermostat-vm-gc-agent</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>com.redhat.thermostat</groupId>
       <artifactId>thermostat-vm-classstat-client-swing</artifactId>
       <version>${project.version}</version>
     </dependency>
--- a/eclipse/com.redhat.thermostat.client.feature/feature.xml	Mon Jan 07 15:50:03 2013 -0500
+++ b/eclipse/com.redhat.thermostat.client.feature/feature.xml	Mon Jan 07 15:50:59 2013 -0500
@@ -210,4 +210,11 @@
          version="0.0.0"
          unpack="false"/>
 
+   <plugin
+         id="com.redhat.thermostat.vm.gc.common"
+         download-size="0"
+         install-size="0"
+         version="0.0.0"
+         unpack="false"/>
+
 </feature>
--- a/system-backend/src/main/java/com/redhat/thermostat/backend/system/JvmStatDataExtractor.java	Mon Jan 07 15:50:03 2013 -0500
+++ b/system-backend/src/main/java/com/redhat/thermostat/backend/system/JvmStatDataExtractor.java	Mon Jan 07 15:50:59 2013 -0500
@@ -98,22 +98,6 @@
         return MonitoredVmUtil.jvmArgs(vm);
     }
 
-    public long getTotalCollectors() throws MonitorException {
-        return (Long) vm.findByName("sun.gc.policy.collectors").getValue();
-    }
-
-    public String getCollectorName(long collector) throws MonitorException {
-        return (String) vm.findByName("sun.gc.collector." + collector + ".name").getValue();
-    }
-
-    public long getCollectorTime(long collector) throws MonitorException {
-        return (Long) vm.findByName("sun.gc.collector." + collector + ".time").getValue();
-    }
-
-    public long getCollectorInvocations(long collector) throws MonitorException {
-        return (Long) vm.findByName("sun.gc.collector." + collector + ".invocations").getValue();
-    }
-
     public long getLoadedClasses() throws MonitorException {
         return (Long) vm.findByName("java.cls.loadedClasses").getValue();
     }
--- a/system-backend/src/main/java/com/redhat/thermostat/backend/system/JvmStatHostListener.java	Mon Jan 07 15:50:03 2013 -0500
+++ b/system-backend/src/main/java/com/redhat/thermostat/backend/system/JvmStatHostListener.java	Mon Jan 07 15:50:59 2013 -0500
@@ -59,7 +59,6 @@
 import com.redhat.thermostat.agent.JvmStatusListener;
 import com.redhat.thermostat.agent.JvmStatusNotifier;
 import com.redhat.thermostat.common.dao.VmClassStatDAO;
-import com.redhat.thermostat.common.dao.VmGcStatDAO;
 import com.redhat.thermostat.common.dao.VmInfoDAO;
 import com.redhat.thermostat.common.utils.LoggingUtils;
 import com.redhat.thermostat.storage.model.VmInfo;
@@ -73,16 +72,14 @@
 
     private final VmInfoDAO vmInfoDAO;
     private final VmClassStatDAO vmClassStatDAO;
-    private final VmGcStatDAO vmGcStatDAO;
 
     private Map<Integer, MonitoredVm> monitoredVms  = new HashMap<>();
     private Map<MonitoredVm, List<VmListener>> registeredListeners  = new ConcurrentHashMap<>();
     
     private Set<JvmStatusListener> statusListeners = new CopyOnWriteArraySet<JvmStatusListener>();
 
-    JvmStatHostListener(VmInfoDAO vmInfoDAO, VmGcStatDAO vmGcStatDAO, VmClassStatDAO vmClassStatDAO, boolean attachNew) {
+    JvmStatHostListener(VmInfoDAO vmInfoDAO, VmClassStatDAO vmClassStatDAO, boolean attachNew) {
         this.vmInfoDAO = vmInfoDAO;
-        this.vmGcStatDAO = vmGcStatDAO;
         this.vmClassStatDAO = vmClassStatDAO;
         this.attachNew = attachNew;        
     }
@@ -165,11 +162,7 @@
                     listeners = new CopyOnWriteArrayList<>();
                 }
                 
-                VmListener listener =  new JvmStatVmListener(vmGcStatDAO, vmId);
-                vm.addVmListener(listener);
-                listeners.add(listener);
-                
-                listener = new JvmStatVmClassListener(vmClassStatDAO, vmId);
+                VmListener listener = new JvmStatVmClassListener(vmClassStatDAO, vmId);
                 vm.addVmListener(listener);
                 listeners.add(listener);
                 
--- a/system-backend/src/main/java/com/redhat/thermostat/backend/system/JvmStatVmListener.java	Mon Jan 07 15:50:03 2013 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,101 +0,0 @@
-/*
- * Copyright 2012 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.backend.system;
-
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-import sun.jvmstat.monitor.MonitorException;
-import sun.jvmstat.monitor.MonitoredVm;
-import sun.jvmstat.monitor.event.MonitorStatusChangeEvent;
-import sun.jvmstat.monitor.event.VmEvent;
-import sun.jvmstat.monitor.event.VmListener;
-
-import com.redhat.thermostat.common.dao.VmGcStatDAO;
-import com.redhat.thermostat.common.utils.LoggingUtils;
-import com.redhat.thermostat.storage.model.VmGcStat;
-
-public class JvmStatVmListener implements VmListener {
-
-    private static final Logger logger = LoggingUtils.getLogger(JvmStatVmListener.class);
-
-    private final int vmId;
-    private final VmGcStatDAO gcDAO;
-
-    public JvmStatVmListener(VmGcStatDAO vmGcStatDao, int vmId) {
-        gcDAO = vmGcStatDao;
-        this.vmId = vmId;
-    }
-
-    @Override
-    public void disconnected(VmEvent event) {
-        /* nothing to do here */
-    }
-
-    @Override
-    public void monitorStatusChanged(MonitorStatusChangeEvent event) {
-        /* nothing to do here */
-    }
-
-    @Override
-    public void monitorsUpdated(VmEvent event) {
-        MonitoredVm vm = event.getMonitoredVm();
-        if (vm == null) {
-            throw new NullPointerException();
-        }
-        recordGcStat(vm);
-    }
-
-    private void recordGcStat(MonitoredVm vm) {
-        try {
-            JvmStatDataExtractor extractor = new JvmStatDataExtractor(vm);
-            long collectors = extractor.getTotalCollectors();
-            for (int i = 0; i < collectors; i++) {
-                long timestamp = System.currentTimeMillis();
-                VmGcStat stat = new VmGcStat(vmId, timestamp,
-                        extractor.getCollectorName(i),
-                        extractor.getCollectorInvocations(i),
-                        extractor.getCollectorTime(i));
-                gcDAO.putVmGcStat(stat);
-            }
-        } catch (MonitorException e) {
-            logger.log(Level.WARNING, "error gathering gc info for vm " + vmId, e);
-        }
-
-    }
-
-}
--- a/system-backend/src/main/java/com/redhat/thermostat/backend/system/SystemBackend.java	Mon Jan 07 15:50:03 2013 -0500
+++ b/system-backend/src/main/java/com/redhat/thermostat/backend/system/SystemBackend.java	Mon Jan 07 15:50:59 2013 -0500
@@ -93,7 +93,7 @@
     protected void setDAOFactoryAction() {
         hostInfos = df.getHostInfoDAO();
         networkInterfaces = df.getNetworkInterfaceInfoDAO();
-        hostListener = new JvmStatHostListener(df.getVmInfoDAO(), df.getVmGcStatDAO(), df.getVmClassStatsDAO(), getObserveNewJvm());
+        hostListener = new JvmStatHostListener(df.getVmInfoDAO(), df.getVmClassStatsDAO(), getObserveNewJvm());
     }
 
     @Override
--- a/system-backend/src/test/java/com/redhat/thermostat/backend/system/JvmStatDataExtractorTest.java	Mon Jan 07 15:50:03 2013 -0500
+++ b/system-backend/src/test/java/com/redhat/thermostat/backend/system/JvmStatDataExtractorTest.java	Mon Jan 07 15:50:59 2013 -0500
@@ -174,58 +174,6 @@
     }
 
     @Test
-    public void testTotalCollectors() throws MonitorException {
-        final String MONITOR_NAME = "sun.gc.policy.collectors";
-        final Long MONITOR_VALUE = 9l;
-        MonitoredVm vm = buildLongMonitoredVm(MONITOR_NAME, MONITOR_VALUE);
-
-        JvmStatDataExtractor extractor = new JvmStatDataExtractor(vm);
-        Long returned = extractor.getTotalCollectors();
-
-        verify(vm).findByName(eq(MONITOR_NAME));
-        assertEquals(MONITOR_VALUE, returned);
-    }
-
-    @Test
-    public void testCollectorName() throws MonitorException {
-        final String MONITOR_NAME = "sun.gc.collector.0.name";
-        final String COLLECTOR_NAME = "SomeMemoryCollector";
-        MonitoredVm vm = buildStringMonitoredVm(MONITOR_NAME, COLLECTOR_NAME);
-
-        JvmStatDataExtractor extractor = new JvmStatDataExtractor(vm);
-        String returned = extractor.getCollectorName(0);
-
-        verify(vm).findByName(eq(MONITOR_NAME));
-        assertEquals(COLLECTOR_NAME, returned);
-    }
-
-    @Test
-    public void testCollectorTime() throws MonitorException {
-        final String MONITOR_NAME = "sun.gc.collector.0.time";
-        final Long COLLECTOR_TIME = 99l;
-        MonitoredVm vm = buildLongMonitoredVm(MONITOR_NAME, COLLECTOR_TIME);
-
-        JvmStatDataExtractor extractor = new JvmStatDataExtractor(vm);
-        Long returned = extractor.getCollectorTime(0);
-
-        verify(vm).findByName(eq(MONITOR_NAME));
-        assertEquals(COLLECTOR_TIME, returned);
-    }
-
-    @Test
-    public void testCollectorInvocations() throws MonitorException {
-        final String MONITOR_NAME = "sun.gc.collector.0.invocations";
-        final Long COLLECTOR_INVOCATIONS = 99l;
-        MonitoredVm vm = buildLongMonitoredVm(MONITOR_NAME, COLLECTOR_INVOCATIONS);
-
-        JvmStatDataExtractor extractor = new JvmStatDataExtractor(vm);
-        Long returned = extractor.getCollectorInvocations(0);
-
-        verify(vm).findByName(eq(MONITOR_NAME));
-        assertEquals(COLLECTOR_INVOCATIONS, returned);
-    }
-
-    @Test
     public void testLoadedClasses() throws MonitorException {
         final String MONITOR_NAME = "java.cls.loadedClasses";
         final Long LOADED_CLASSES = 99l;
--- a/system-backend/src/test/java/com/redhat/thermostat/backend/system/JvmStatHostListenerTest.java	Mon Jan 07 15:50:03 2013 -0500
+++ b/system-backend/src/test/java/com/redhat/thermostat/backend/system/JvmStatHostListenerTest.java	Mon Jan 07 15:50:59 2013 -0500
@@ -81,7 +81,7 @@
         VmClassStatDAO vmClassDAO = mock(VmClassStatDAO.class);
         VmInfoDAO vmInfoDAO = mock(VmInfoDAO.class);
 
-        JvmStatHostListener l = new JvmStatHostListener(vmInfoDAO, null, vmClassDAO ,true);
+        JvmStatHostListener l = new JvmStatHostListener(vmInfoDAO, vmClassDAO ,true);
         SystemBackend backend = mock(SystemBackend.class);
         when(backend.getObserveNewJvm()).thenReturn(true);
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-gc/agent/pom.xml	Mon Jan 07 15:50:59 2013 -0500
@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<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-gc</artifactId>
+    <groupId>com.redhat.thermostat</groupId>
+    <version>0.5.0-SNAPSHOT</version>
+  </parent>
+  <artifactId>thermostat-vm-gc-agent</artifactId>
+  <packaging>bundle</packaging>
+  <name>Thermostat VM GC 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.gc.agent</Bundle-SymbolicName>
+            <Bundle-Activator>com.redhat.thermostat.vm.gc.agent.internal.Activator</Bundle-Activator>
+            <Export-Package>
+              com.redhat.thermostat.vm.gc.agent
+            </Export-Package>
+            <Private-Package>
+              com.redhat.thermostat.vm.gc.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-common-core</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>com.redhat.thermostat</groupId>
+      <artifactId>thermostat-vm-gc-common</artifactId>
+      <version>${project.version}</version>
+    </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-storage-core</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+  </dependencies>
+</project>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-gc/agent/src/main/java/com/redhat/thermostat/vm/gc/agent/internal/Activator.java	Mon Jan 07 15:50:59 2013 -0500
@@ -0,0 +1,97 @@
+/*
+ * Copyright 2013 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.gc.agent.internal;
+
+import java.util.Map;
+
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceRegistration;
+
+import com.redhat.thermostat.backend.Backend;
+import com.redhat.thermostat.backend.BackendService;
+import com.redhat.thermostat.common.MultipleServiceTracker;
+import com.redhat.thermostat.common.MultipleServiceTracker.Action;
+import com.redhat.thermostat.common.Version;
+import com.redhat.thermostat.vm.gc.common.VmGcStatDAO;
+
+public class Activator implements BundleActivator {
+    
+    private MultipleServiceTracker tracker;
+    private VmGcBackend backend;
+    private ServiceRegistration reg;
+    
+    @Override
+    public void start(final BundleContext context) throws Exception {
+        Class<?>[] deps = new Class<?>[] {
+                BackendService.class,
+                VmGcStatDAO.class
+        };
+        tracker = new MultipleServiceTracker(context, deps, new Action() {
+
+            @Override
+            public void dependenciesAvailable(Map<String, Object> services) {
+                VmGcStatDAO vmGcStatDao = (VmGcStatDAO) services.get(VmGcStatDAO.class.getName());
+                Version version = new Version(context.getBundle());
+                backend = new VmGcBackend(vmGcStatDao, version);
+                reg = context.registerService(Backend.class.getName(), backend, null);
+            }
+
+            @Override
+            public void dependenciesUnavailable() {
+                if (backend.isActive()) {
+                    backend.deactivate();
+                }
+                reg.unregister();
+            }
+            
+        });
+        tracker.open();
+    }
+
+    @Override
+    public void stop(BundleContext context) throws Exception {
+        tracker.close();
+    }
+    
+    /*
+     * For testing purposes only.
+     */
+    VmGcBackend getBackend() {
+        return backend;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-gc/agent/src/main/java/com/redhat/thermostat/vm/gc/agent/internal/VmGcBackend.java	Mon Jan 07 15:50:59 2013 -0500
@@ -0,0 +1,141 @@
+/*
+ * Copyright 2013 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.gc.agent.internal;
+
+import java.net.URISyntaxException;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import sun.jvmstat.monitor.HostIdentifier;
+import sun.jvmstat.monitor.MonitorException;
+import sun.jvmstat.monitor.MonitoredHost;
+
+import com.redhat.thermostat.backend.Backend;
+import com.redhat.thermostat.backend.BackendID;
+import com.redhat.thermostat.backend.BackendsProperties;
+import com.redhat.thermostat.common.Version;
+import com.redhat.thermostat.common.utils.LoggingUtils;
+import com.redhat.thermostat.vm.gc.common.VmGcStatDAO;
+
+public class VmGcBackend extends Backend {
+
+    private static final Logger LOGGER = LoggingUtils.getLogger(VmGcBackend.class);
+
+    private VmGcStatDAO vmGcStats;
+    private HostIdentifier hostId;
+    private MonitoredHost host;
+    private VmGcHostListener hostListener;
+    private boolean started;
+
+    public VmGcBackend(VmGcStatDAO vmGcStatDAO, Version version) {
+        super(new BackendID("VM GC Backend", VmGcBackend.class.getName()));
+        this.vmGcStats = vmGcStatDAO;
+        
+        setConfigurationValue(BackendsProperties.VENDOR.name(), "Red Hat, Inc.");
+        setConfigurationValue(BackendsProperties.DESCRIPTION.name(), "Gathers garbage collection statistics about a JVM");
+        setConfigurationValue(BackendsProperties.VERSION.name(), version.getVersionNumber());
+        
+        try {
+            hostId = new HostIdentifier((String) null);
+            host = MonitoredHost.getMonitoredHost(hostId);
+            hostListener = new VmGcHostListener(vmGcStats, attachToNewProcessByDefault());
+        } catch (MonitorException me) {
+            LOGGER.log(Level.WARNING, "Problems with connecting jvmstat to local machine", me);
+        } catch (URISyntaxException use) {
+            LOGGER.log(Level.WARNING, "Failed to create host identifier", use);
+        }
+    }
+
+    @Override
+    public boolean activate() {
+        if (!started && host != null) {
+            try {
+                host.addHostListener(hostListener);
+                started = true;
+            } catch (MonitorException me) {
+                LOGGER.log(Level.WARNING, "problems with connecting jvmstat to local machine", me);
+            }
+        }
+        return started;
+    }
+
+    @Override
+    public boolean deactivate() {
+        if (started && host != null) {
+            try {
+                host.removeHostListener(hostListener);
+                started = false;
+            } catch (MonitorException me) {
+                LOGGER.log(Level.INFO, "something went wrong in jvmstat's listening to this host");
+            }
+        }
+        return !started;
+    }
+    
+    @Override
+    public boolean isActive() {
+        return started;
+    }
+    
+    @Override
+    protected void setDAOFactoryAction() {
+        // No need for DAOFactory
+    }
+
+    @Override
+    public String getConfigurationValue(String key) {
+        return null;
+    }
+
+    @Override
+    public boolean attachToNewProcessByDefault() {
+        return true;
+    }
+
+    @Override
+    public int getOrderValue() {
+        return ORDER_MEMORY_GROUP + 20;
+    }
+
+    /*
+     * For testing purposes only.
+     */
+    void setHost(MonitoredHost host) {
+        this.host = host;
+    }
+    
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-gc/agent/src/main/java/com/redhat/thermostat/vm/gc/agent/internal/VmGcDataExtractor.java	Mon Jan 07 15:50:59 2013 -0500
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2013 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.gc.agent.internal;
+
+import sun.jvmstat.monitor.MonitorException;
+import sun.jvmstat.monitor.MonitoredVm;
+
+/**
+ * A helper class to provide type-safe access to commonly used jvmstat monitors
+ * <p>
+ * Implementation details: For local vms, jvmstat uses a ByteBuffer
+ * corresponding to mmap()ed hsperfdata file. The hsperfdata file is updated
+ * asynchronously by the vm that created the file. The polling that jvmstat api
+ * provides is merely an abstraction over this (possibly always up-to-date)
+ * ByteBuffer. So the data this class extracts is as current as possible, and
+ * does not correspond to when the jvmstat update events fired.
+ */
+public class VmGcDataExtractor {
+
+    /*
+     * Note, there may be a performance issue to consider here. We have a lot of
+     * string constants. When we start adding some of the more heavyweight
+     * features, and running into CPU issues this may need to be reconsidered in
+     * order to avoid the String pool overhead. See also:
+     * http://docs.oracle.com/javase/6/docs/api/java/lang/String.html#intern()
+     */
+
+    private final MonitoredVm vm;
+
+    public VmGcDataExtractor(MonitoredVm vm) {
+        this.vm = vm;
+    }
+
+    public long getTotalCollectors() throws MonitorException {
+        return (Long) vm.findByName("sun.gc.policy.collectors").getValue();
+    }
+
+    public String getCollectorName(long collector) throws MonitorException {
+        return (String) vm.findByName("sun.gc.collector." + collector + ".name").getValue();
+    }
+
+    public long getCollectorTime(long collector) throws MonitorException {
+        return (Long) vm.findByName("sun.gc.collector." + collector + ".time").getValue();
+    }
+
+    public long getCollectorInvocations(long collector) throws MonitorException {
+        return (Long) vm.findByName("sun.gc.collector." + collector + ".invocations").getValue();
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-gc/agent/src/main/java/com/redhat/thermostat/vm/gc/agent/internal/VmGcHostListener.java	Mon Jan 07 15:50:59 2013 -0500
@@ -0,0 +1,171 @@
+/*
+ * Copyright 2013 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.gc.agent.internal;
+
+import java.net.URISyntaxException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import sun.jvmstat.monitor.MonitorException;
+import sun.jvmstat.monitor.MonitoredHost;
+import sun.jvmstat.monitor.MonitoredVm;
+import sun.jvmstat.monitor.VmIdentifier;
+import sun.jvmstat.monitor.event.HostEvent;
+import sun.jvmstat.monitor.event.HostListener;
+import sun.jvmstat.monitor.event.VmListener;
+import sun.jvmstat.monitor.event.VmStatusChangeEvent;
+
+import com.redhat.thermostat.common.utils.LoggingUtils;
+import com.redhat.thermostat.vm.gc.common.VmGcStatDAO;
+
+public class VmGcHostListener implements HostListener {
+
+    private static final Logger logger = LoggingUtils.getLogger(VmGcHostListener.class);
+
+    private boolean attachNew;
+
+    private final VmGcStatDAO vmGcStatDAO;
+
+    private Map<Integer, MonitoredVm> monitoredVms  = new HashMap<>();
+    private Map<MonitoredVm, VmGcVmListener> registeredListeners  = new ConcurrentHashMap<>();
+    
+    VmGcHostListener(VmGcStatDAO vmGcStatDAO, boolean attachNew) {
+        this.vmGcStatDAO = vmGcStatDAO;
+        this.attachNew = attachNew;        
+    }
+
+    void removeAllListeners() {
+        for (MonitoredVm vm : monitoredVms.values()) {
+            VmListener listener = registeredListeners.get(vm);
+            try {
+                if (listener != null) {
+                    vm.removeVmListener(listener);
+                }
+            } catch (MonitorException e) {
+                logger.log(Level.WARNING, "can't remove vm listener", e);
+            }
+        }
+    }
+    
+    @Override
+    public void disconnected(HostEvent event) {
+        logger.warning("Disconnected from host");
+    }
+
+    @SuppressWarnings("unchecked") // Unchecked casts to (Set<Integer>).
+    @Override
+    public void vmStatusChanged(VmStatusChangeEvent event) {
+        MonitoredHost host = event.getMonitoredHost();
+
+        for (Integer newVm : (Set<Integer>) event.getStarted()) {
+            try {
+                logger.fine("New vm: " + newVm);
+                sendNewVM(newVm, host);
+            } catch (MonitorException e) {
+                logger.log(Level.WARNING, "error getting info for new vm" + newVm, e);
+            } catch (URISyntaxException e) {
+                logger.log(Level.WARNING, "error getting info for new vm" + newVm, e);
+            }
+        }
+
+        for (Integer stoppedVm : (Set<Integer>) event.getTerminated()) {
+            try {
+                logger.fine("stopped vm: " + stoppedVm);
+                sendStoppedVM(stoppedVm, host);
+            } catch (URISyntaxException e) {
+                logger.log(Level.WARNING, "error getting info for stopped vm" + stoppedVm, e);
+            } catch (MonitorException e) {
+                logger.log(Level.WARNING, "error getting info for stopped vm" + stoppedVm, e);
+            }
+        }
+    }
+
+    private void sendNewVM(Integer vmId, MonitoredHost host)
+            throws MonitorException, URISyntaxException {
+        MonitoredVm vm = host.getMonitoredVm(host.getHostIdentifier().resolve(
+                new VmIdentifier(vmId.toString())));
+        if (vm != null) {
+            if (attachNew) {
+                VmGcVmListener listener =  new VmGcVmListener(vmGcStatDAO, vmId);
+                vm.addVmListener(listener);
+                
+                registeredListeners.put(vm, listener);
+                logger.finer("Attached VmListener for VM: " + vmId);
+            } else {
+                logger.log(Level.FINE, "skipping new vm " + vmId);
+            }
+
+            monitoredVms.put(vmId, vm);
+        }
+    }
+
+    private void sendStoppedVM(Integer vmId, MonitoredHost host) throws URISyntaxException, MonitorException {
+        
+        VmIdentifier resolvedVmID = host.getHostIdentifier().resolve(new VmIdentifier(vmId.toString()));
+        if (resolvedVmID != null) {
+            MonitoredVm vm = monitoredVms.remove(vmId);
+            VmListener listener = registeredListeners.remove(vm);
+            try {
+                if (listener != null) {
+                    vm.removeVmListener(listener);
+                }
+            } catch (MonitorException e) {
+                logger.log(Level.WARNING, "can't remove vm listener", e);
+            }
+            vm.detach();
+        }
+    }
+    
+    /*
+     * For testing purposes only.
+     */
+    Map<Integer, MonitoredVm> getMonitoredVms() {
+        return monitoredVms;
+    }
+    
+    /*
+     * For testing purposes only.
+     */
+    Map<MonitoredVm, VmGcVmListener> getRegisteredListeners() {
+        return registeredListeners;
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-gc/agent/src/main/java/com/redhat/thermostat/vm/gc/agent/internal/VmGcVmListener.java	Mon Jan 07 15:50:59 2013 -0500
@@ -0,0 +1,102 @@
+/*
+ * Copyright 2013 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.gc.agent.internal;
+
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import sun.jvmstat.monitor.MonitorException;
+import sun.jvmstat.monitor.MonitoredVm;
+import sun.jvmstat.monitor.event.MonitorStatusChangeEvent;
+import sun.jvmstat.monitor.event.VmEvent;
+import sun.jvmstat.monitor.event.VmListener;
+
+import com.redhat.thermostat.common.utils.LoggingUtils;
+import com.redhat.thermostat.storage.model.VmGcStat;
+import com.redhat.thermostat.vm.gc.common.VmGcStatDAO;
+
+public class VmGcVmListener implements VmListener {
+
+    private static final Logger logger = LoggingUtils.getLogger(VmGcVmListener.class);
+
+    private final int vmId;
+    private final VmGcStatDAO gcDAO;
+
+    public VmGcVmListener(VmGcStatDAO vmGcStatDao, int vmId) {
+        gcDAO = vmGcStatDao;
+        this.vmId = vmId;
+    }
+
+    @Override
+    public void disconnected(VmEvent event) {
+        /* nothing to do here */
+    }
+
+    @Override
+    public void monitorStatusChanged(MonitorStatusChangeEvent event) {
+        /* nothing to do here */
+    }
+
+    @Override
+    public void monitorsUpdated(VmEvent event) {
+        MonitoredVm vm = event.getMonitoredVm();
+        if (vm == null) {
+            throw new NullPointerException();
+        }
+        
+        VmGcDataExtractor extractor = new VmGcDataExtractor(vm);
+        recordGcStat(vm, extractor);
+    }
+
+    void recordGcStat(MonitoredVm vm, VmGcDataExtractor extractor) {
+        try {
+            long collectors = extractor.getTotalCollectors();
+            for (int i = 0; i < collectors; i++) {
+                long timestamp = System.currentTimeMillis();
+                VmGcStat stat = new VmGcStat(vmId, timestamp,
+                        extractor.getCollectorName(i),
+                        extractor.getCollectorInvocations(i),
+                        extractor.getCollectorTime(i));
+                gcDAO.putVmGcStat(stat);
+            }
+        } catch (MonitorException e) {
+            logger.log(Level.WARNING, "error gathering gc info for vm " + vmId, e);
+        }
+
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-gc/agent/src/test/java/com/redhat/thermostat/vm/gc/agent/internal/ActivatorTest.java	Mon Jan 07 15:50:59 2013 -0500
@@ -0,0 +1,104 @@
+/*
+ * Copyright 2013 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.gc.agent.internal;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import org.junit.Test;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.Version;
+
+import com.redhat.thermostat.backend.Backend;
+import com.redhat.thermostat.backend.BackendService;
+import com.redhat.thermostat.test.StubBundleContext;
+import com.redhat.thermostat.vm.gc.common.VmGcStatDAO;
+
+public class ActivatorTest {
+    
+    @Test
+    public void verifyActivatorDoesNotRegisterServiceOnMissingDeps() throws Exception {
+        StubBundleContext context = new StubBundleContext();
+
+        Activator activator = new Activator();
+
+        activator.start(context);
+
+        assertEquals(0, context.getAllServices().size());
+        assertEquals(2, context.getServiceListeners().size());
+
+        activator.stop(context);
+    }
+
+    @Test
+    public void verifyActivatorRegistersServices() throws Exception {
+        StubBundleContext context = new StubBundleContext() {
+            @Override
+            public Bundle getBundle() {
+                Bundle result = mock(Bundle.class);
+                when(result.getVersion()).thenReturn(Version.emptyVersion);
+                return result;
+            }
+        };
+        
+        BackendService service = mock(BackendService.class);
+        VmGcStatDAO vmGcStatDAO = mock(VmGcStatDAO.class);
+
+        context.registerService(BackendService.class, service, null);
+        context.registerService(VmGcStatDAO.class, vmGcStatDAO, null);
+
+        Activator activator = new Activator();
+
+        activator.start(context);
+
+        assertTrue(context.isServiceRegistered(Backend.class.getName(), VmGcBackend.class));
+        VmGcBackend backend = activator.getBackend();
+        assertNotNull(backend);
+
+        activator.stop(context);
+        
+        assertFalse(backend.isActive());
+
+        assertEquals(0, context.getServiceListeners().size());
+        assertEquals(2, context.getAllServices().size());
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-gc/agent/src/test/java/com/redhat/thermostat/vm/gc/agent/internal/VmGcBackendTest.java	Mon Jan 07 15:50:59 2013 -0500
@@ -0,0 +1,91 @@
+/*
+ * Copyright 2013 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.gc.agent.internal;
+
+import static org.junit.Assert.assertFalse;
+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;
+
+import java.net.URISyntaxException;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import sun.jvmstat.monitor.MonitorException;
+import sun.jvmstat.monitor.MonitoredHost;
+import sun.jvmstat.monitor.event.HostListener;
+
+import com.redhat.thermostat.common.Version;
+import com.redhat.thermostat.vm.gc.common.VmGcStatDAO;
+
+public class VmGcBackendTest {
+    
+    private VmGcBackend backend;
+    private MonitoredHost host;
+
+    @Before
+    public void setup() throws MonitorException, URISyntaxException {
+        VmGcStatDAO vmGcStatDao = mock(VmGcStatDAO.class);
+        
+        Version version = mock(Version.class);
+        when(version.getVersionNumber()).thenReturn("0.0.0");
+        
+        backend = new VmGcBackend(vmGcStatDao, version);
+        
+        host = mock(MonitoredHost.class);
+        backend.setHost(host);
+    }
+
+    @Test
+    public void testStart() throws MonitorException {
+        backend.activate();
+        verify(host).addHostListener(any(HostListener.class));
+        assertTrue(backend.isActive());
+    }
+
+    @Test
+    public void testStop() throws MonitorException {
+        backend.activate();
+        backend.deactivate();
+        verify(host).removeHostListener(any(HostListener.class));
+        assertFalse(backend.isActive());
+    }
+    
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-gc/agent/src/test/java/com/redhat/thermostat/vm/gc/agent/internal/VmGcDataExtractorTest.java	Mon Jan 07 15:50:59 2013 -0500
@@ -0,0 +1,124 @@
+/*
+ * Copyright 2013 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.gc.agent.internal;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import org.junit.Test;
+
+import sun.jvmstat.monitor.LongMonitor;
+import sun.jvmstat.monitor.MonitorException;
+import sun.jvmstat.monitor.MonitoredVm;
+import sun.jvmstat.monitor.StringMonitor;
+
+public class VmGcDataExtractorTest {
+
+    private MonitoredVm buildStringMonitoredVm(String monitorName, String monitorReturn) throws MonitorException {
+        final StringMonitor monitor = mock(StringMonitor.class);
+        when(monitor.stringValue()).thenReturn(monitorReturn);
+        when(monitor.getValue()).thenReturn(monitorReturn);
+        MonitoredVm vm = mock(MonitoredVm.class);
+        when(vm.findByName(monitorName)).thenReturn(monitor);
+        return vm;
+    }
+
+    private MonitoredVm buildLongMonitoredVm(String monitorName, Long monitorReturn) throws MonitorException {
+        final LongMonitor monitor = mock(LongMonitor.class);
+        when(monitor.longValue()).thenReturn(monitorReturn);
+        when(monitor.getValue()).thenReturn(monitorReturn);
+        MonitoredVm vm = mock(MonitoredVm.class);
+        when(vm.findByName(monitorName)).thenReturn(monitor);
+        return vm;
+    }
+
+    @Test
+    public void testTotalCollectors() throws MonitorException {
+        final String MONITOR_NAME = "sun.gc.policy.collectors";
+        final Long MONITOR_VALUE = 9l;
+        MonitoredVm vm = buildLongMonitoredVm(MONITOR_NAME, MONITOR_VALUE);
+
+        VmGcDataExtractor extractor = new VmGcDataExtractor(vm);
+        Long returned = extractor.getTotalCollectors();
+
+        verify(vm).findByName(eq(MONITOR_NAME));
+        assertEquals(MONITOR_VALUE, returned);
+    }
+
+    @Test
+    public void testCollectorName() throws MonitorException {
+        final String MONITOR_NAME = "sun.gc.collector.0.name";
+        final String COLLECTOR_NAME = "SomeMemoryCollector";
+        MonitoredVm vm = buildStringMonitoredVm(MONITOR_NAME, COLLECTOR_NAME);
+
+        VmGcDataExtractor extractor = new VmGcDataExtractor(vm);
+        String returned = extractor.getCollectorName(0);
+
+        verify(vm).findByName(eq(MONITOR_NAME));
+        assertEquals(COLLECTOR_NAME, returned);
+    }
+
+    @Test
+    public void testCollectorTime() throws MonitorException {
+        final String MONITOR_NAME = "sun.gc.collector.0.time";
+        final Long COLLECTOR_TIME = 99l;
+        MonitoredVm vm = buildLongMonitoredVm(MONITOR_NAME, COLLECTOR_TIME);
+
+        VmGcDataExtractor extractor = new VmGcDataExtractor(vm);
+        Long returned = extractor.getCollectorTime(0);
+
+        verify(vm).findByName(eq(MONITOR_NAME));
+        assertEquals(COLLECTOR_TIME, returned);
+    }
+
+    @Test
+    public void testCollectorInvocations() throws MonitorException {
+        final String MONITOR_NAME = "sun.gc.collector.0.invocations";
+        final Long COLLECTOR_INVOCATIONS = 99l;
+        MonitoredVm vm = buildLongMonitoredVm(MONITOR_NAME, COLLECTOR_INVOCATIONS);
+
+        VmGcDataExtractor extractor = new VmGcDataExtractor(vm);
+        Long returned = extractor.getCollectorInvocations(0);
+
+        verify(vm).findByName(eq(MONITOR_NAME));
+        assertEquals(COLLECTOR_INVOCATIONS, returned);
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-gc/agent/src/test/java/com/redhat/thermostat/vm/gc/agent/internal/VmGcHostListenerTest.java	Mon Jan 07 15:50:59 2013 -0500
@@ -0,0 +1,136 @@
+/*
+ * Copyright 2013 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.gc.agent.internal;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.net.URISyntaxException;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import sun.jvmstat.monitor.HostIdentifier;
+import sun.jvmstat.monitor.MonitorException;
+import sun.jvmstat.monitor.MonitoredHost;
+import sun.jvmstat.monitor.MonitoredVm;
+import sun.jvmstat.monitor.VmIdentifier;
+import sun.jvmstat.monitor.event.VmStatusChangeEvent;
+
+import com.redhat.thermostat.vm.gc.common.VmGcStatDAO;
+
+public class VmGcHostListenerTest {
+    
+    private VmGcHostListener hostListener;
+    private MonitoredHost host;
+    private MonitoredVm monitoredVm1;
+    private MonitoredVm monitoredVm2;
+
+    @Before
+    public void setup() throws MonitorException, URISyntaxException {
+        VmGcStatDAO vmGcStatDAO = mock(VmGcStatDAO.class);
+        hostListener = new VmGcHostListener(vmGcStatDAO, true);
+        
+        host = mock(MonitoredHost.class);
+        HostIdentifier hostId = mock(HostIdentifier.class);
+        monitoredVm1 = mock(MonitoredVm.class);
+        monitoredVm2 = mock(MonitoredVm.class);
+        VmIdentifier vmId1 = new VmIdentifier("1");
+        VmIdentifier vmId2 = new VmIdentifier("2");
+        when(host.getHostIdentifier()).thenReturn(hostId);
+        when(host.getMonitoredVm(eq(vmId1))).thenReturn(monitoredVm1);
+        when(host.getMonitoredVm(eq(vmId2))).thenReturn(monitoredVm2);
+        when(hostId.resolve(eq(vmId1))).thenReturn(vmId1);
+        when(hostId.resolve(eq(vmId2))).thenReturn(vmId2);
+    }
+    
+    @Test
+    public void testNewVM() throws InterruptedException, MonitorException {
+        startVMs();
+        
+        assertTrue(hostListener.getMonitoredVms().containsKey(1));
+        assertTrue(hostListener.getMonitoredVms().containsKey(2));
+        assertEquals(monitoredVm1, hostListener.getMonitoredVms().get(1));
+        assertEquals(monitoredVm2, hostListener.getMonitoredVms().get(2));
+        
+        assertTrue(hostListener.getRegisteredListeners().containsKey(monitoredVm1));
+        assertTrue(hostListener.getRegisteredListeners().containsKey(monitoredVm2));
+    }
+    
+    @Test
+    public void testStoppedVM() throws InterruptedException, MonitorException {
+        final Set<Integer> stopped = new HashSet<>();
+        stopped.add(1);
+        
+        startVMs();
+        
+        // Trigger a change event
+        VmStatusChangeEvent event = mock(VmStatusChangeEvent.class);
+        when(event.getMonitoredHost()).thenReturn(host);
+        when(event.getStarted()).thenReturn(Collections.emptySet());
+        when(event.getTerminated()).thenReturn(stopped);
+        hostListener.vmStatusChanged(event);
+        
+        // Ensure only 1 removed
+        assertFalse(hostListener.getMonitoredVms().containsKey(1));
+        assertTrue(hostListener.getMonitoredVms().containsKey(2));
+        assertEquals(monitoredVm2, hostListener.getMonitoredVms().get(2));
+        
+        assertFalse(hostListener.getRegisteredListeners().containsKey(monitoredVm1));
+        assertTrue(hostListener.getRegisteredListeners().containsKey(monitoredVm2));
+    }
+
+    private void startVMs() throws InterruptedException, MonitorException {
+        final Set<Integer> started = new HashSet<>();
+        started.add(1);
+        started.add(2);
+
+        // Trigger a change event
+        VmStatusChangeEvent event = mock(VmStatusChangeEvent.class);
+        when(event.getMonitoredHost()).thenReturn(host);
+        when(event.getStarted()).thenReturn(started);
+        when(event.getTerminated()).thenReturn(Collections.emptySet());
+        hostListener.vmStatusChanged(event);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-gc/agent/src/test/java/com/redhat/thermostat/vm/gc/agent/internal/VmGcVmListenerTest.java	Mon Jan 07 15:50:59 2013 -0500
@@ -0,0 +1,112 @@
+/*
+ * Copyright 2013 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.gc.agent.internal;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import java.util.List;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+
+import sun.jvmstat.monitor.MonitorException;
+import sun.jvmstat.monitor.MonitoredVm;
+
+import com.redhat.thermostat.storage.model.VmGcStat;
+import com.redhat.thermostat.vm.gc.common.VmGcStatDAO;
+
+public class VmGcVmListenerTest {
+    private static final String[] GC_NAMES = new String[] { "GC1", "GC2" };
+    private static final Long[] GC_INVOCS = new Long[] { 500L, 1000L };
+    private static final Long[] GC_TIMES = new Long[] { 5000L, 10000L };
+    
+    private VmGcVmListener vmListener;
+    private MonitoredVm monitoredVm;
+    private VmGcDataExtractor extractor;
+    private VmGcStatDAO vmGcStatDAO;
+    
+    @Before
+    public void setup() throws MonitorException {
+        final int numGCs = 2;
+        vmGcStatDAO = mock(VmGcStatDAO.class);
+        vmListener = new VmGcVmListener(vmGcStatDAO, 0);
+        
+        monitoredVm = mock(MonitoredVm.class);
+        extractor = mock(VmGcDataExtractor.class);
+        
+        for (int i = 0; i < numGCs; i++) {
+            mockCollectorName(i);
+            mockCollectorInvocations(i);
+            mockCollectorTime(i);
+        }
+        
+        when(extractor.getTotalCollectors()).thenReturn((long) GC_NAMES.length);
+    }
+
+    private void mockCollectorName(int gc) throws MonitorException {
+        when(extractor.getCollectorName(gc)).thenReturn(GC_NAMES[gc]);
+    }
+    
+    private void mockCollectorInvocations(int gc) throws MonitorException {
+        when(extractor.getCollectorInvocations(gc)).thenReturn(GC_INVOCS[gc]);
+    }
+
+    private void mockCollectorTime(int gc) throws MonitorException {
+        when(extractor.getCollectorTime(gc)).thenReturn(GC_TIMES[gc]);
+    }
+    
+    @Test
+    public void testRecordMemoryStat() {
+        final int numCollectors = GC_NAMES.length;
+        vmListener.recordGcStat(monitoredVm, extractor);
+        ArgumentCaptor<VmGcStat> captor = ArgumentCaptor.forClass(VmGcStat.class);
+        verify(vmGcStatDAO, times(numCollectors)).putVmGcStat(captor.capture());
+        List<VmGcStat> gcStats = captor.getAllValues();
+        
+        for (int i = 0; i < numCollectors; i++) {
+            VmGcStat stat = gcStats.get(i);
+            assertEquals(GC_NAMES[i], stat.getCollectorName());
+            assertEquals(GC_INVOCS[i], (Long) stat.getRunCount());
+            assertEquals(GC_TIMES[i], (Long) stat.getWallTime());
+        }
+    }
+}
--- a/vm-gc/client-core/pom.xml	Mon Jan 07 15:50:03 2013 -0500
+++ b/vm-gc/client-core/pom.xml	Mon Jan 07 15:50:59 2013 -0500
@@ -67,6 +67,11 @@
     </dependency>
     <dependency>
       <groupId>com.redhat.thermostat</groupId>
+      <artifactId>thermostat-vm-gc-common</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>com.redhat.thermostat</groupId>
       <artifactId>thermostat-client-core</artifactId>
       <version>${project.version}</version>
     </dependency>
--- a/vm-gc/client-core/src/main/java/com/redhat/thermostat/vm/gc/client/core/VmGcService.java	Mon Jan 07 15:50:03 2013 -0500
+++ b/vm-gc/client-core/src/main/java/com/redhat/thermostat/vm/gc/client/core/VmGcService.java	Mon Jan 07 15:50:59 2013 -0500
@@ -41,10 +41,10 @@
 import com.redhat.thermostat.client.core.NameMatchingRefFilter;
 import com.redhat.thermostat.client.core.controllers.InformationServiceController;
 import com.redhat.thermostat.common.ApplicationService;
-import com.redhat.thermostat.common.dao.VmGcStatDAO;
 import com.redhat.thermostat.common.dao.VmRef;
 import com.redhat.thermostat.common.utils.OSGIUtils;
 import com.redhat.thermostat.vm.gc.client.core.internal.VmGcController;
+import com.redhat.thermostat.vm.gc.common.VmGcStatDAO;
 import com.redhat.thermostat.vm.memory.common.VmMemoryStatDAO;
 
 public class VmGcService implements InformationService<VmRef> {
--- a/vm-gc/client-core/src/main/java/com/redhat/thermostat/vm/gc/client/core/internal/Activator.java	Mon Jan 07 15:50:03 2013 -0500
+++ b/vm-gc/client-core/src/main/java/com/redhat/thermostat/vm/gc/client/core/internal/Activator.java	Mon Jan 07 15:50:59 2013 -0500
@@ -50,9 +50,9 @@
 import com.redhat.thermostat.common.Constants;
 import com.redhat.thermostat.common.MultipleServiceTracker;
 import com.redhat.thermostat.common.MultipleServiceTracker.Action;
-import com.redhat.thermostat.common.dao.VmGcStatDAO;
 import com.redhat.thermostat.common.dao.VmRef;
 import com.redhat.thermostat.vm.gc.client.core.VmGcService;
+import com.redhat.thermostat.vm.gc.common.VmGcStatDAO;
 import com.redhat.thermostat.vm.memory.common.VmMemoryStatDAO;
 
 public class Activator implements BundleActivator {
--- a/vm-gc/client-core/src/main/java/com/redhat/thermostat/vm/gc/client/core/internal/VmGcController.java	Mon Jan 07 15:50:03 2013 -0500
+++ b/vm-gc/client-core/src/main/java/com/redhat/thermostat/vm/gc/client/core/internal/VmGcController.java	Mon Jan 07 15:50:59 2013 -0500
@@ -55,7 +55,6 @@
 import com.redhat.thermostat.common.NotImplementedException;
 import com.redhat.thermostat.common.Timer;
 import com.redhat.thermostat.common.Timer.SchedulingType;
-import com.redhat.thermostat.common.dao.VmGcStatDAO;
 import com.redhat.thermostat.common.dao.VmRef;
 import com.redhat.thermostat.common.locale.Translate;
 import com.redhat.thermostat.storage.model.IntervalTimeData;
@@ -66,6 +65,7 @@
 import com.redhat.thermostat.vm.gc.client.core.VmGcView;
 import com.redhat.thermostat.vm.gc.client.core.VmGcViewProvider;
 import com.redhat.thermostat.vm.gc.client.locale.LocaleResources;
+import com.redhat.thermostat.vm.gc.common.VmGcStatDAO;
 import com.redhat.thermostat.vm.memory.common.VmMemoryStatDAO;
 
 public class VmGcController implements InformationServiceController<VmRef> {
--- a/vm-gc/client-core/src/test/java/com/redhat/thermostat/vm/gc/client/core/internal/ActivatorTest.java	Mon Jan 07 15:50:03 2013 -0500
+++ b/vm-gc/client-core/src/test/java/com/redhat/thermostat/vm/gc/client/core/internal/ActivatorTest.java	Mon Jan 07 15:50:59 2013 -0500
@@ -45,9 +45,9 @@
 
 import com.redhat.thermostat.client.core.InformationService;
 import com.redhat.thermostat.common.ApplicationService;
-import com.redhat.thermostat.common.dao.VmGcStatDAO;
 import com.redhat.thermostat.test.StubBundleContext;
 import com.redhat.thermostat.vm.gc.client.core.VmGcService;
+import com.redhat.thermostat.vm.gc.common.VmGcStatDAO;
 import com.redhat.thermostat.vm.memory.common.VmMemoryStatDAO;
 
 public class ActivatorTest {
--- a/vm-gc/client-core/src/test/java/com/redhat/thermostat/vm/gc/client/core/internal/VmGcControllerTest.java	Mon Jan 07 15:50:03 2013 -0500
+++ b/vm-gc/client-core/src/test/java/com/redhat/thermostat/vm/gc/client/core/internal/VmGcControllerTest.java	Mon Jan 07 15:50:59 2013 -0500
@@ -58,13 +58,13 @@
 import com.redhat.thermostat.common.Timer;
 import com.redhat.thermostat.common.Timer.SchedulingType;
 import com.redhat.thermostat.common.TimerFactory;
-import com.redhat.thermostat.common.dao.VmGcStatDAO;
 import com.redhat.thermostat.common.dao.VmRef;
 import com.redhat.thermostat.storage.model.VmGcStat;
 import com.redhat.thermostat.storage.model.VmMemoryStat;
 import com.redhat.thermostat.storage.model.VmMemoryStat.Generation;
 import com.redhat.thermostat.vm.gc.client.core.VmGcView;
 import com.redhat.thermostat.vm.gc.client.core.VmGcViewProvider;
+import com.redhat.thermostat.vm.gc.common.VmGcStatDAO;
 import com.redhat.thermostat.vm.memory.common.VmMemoryStatDAO;
 
 public class VmGcControllerTest {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-gc/common/pom.xml	Mon Jan 07 15:50:59 2013 -0500
@@ -0,0 +1,68 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<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-gc</artifactId>
+    <groupId>com.redhat.thermostat</groupId>
+    <version>0.5.0-SNAPSHOT</version>
+  </parent>
+  <artifactId>thermostat-vm-gc-common</artifactId>
+  <packaging>bundle</packaging>
+  <name>Thermostat VM GC Common 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.gc.common</Bundle-SymbolicName>
+            <Bundle-Activator>com.redhat.thermostat.vm.gc.common.internal.Activator</Bundle-Activator>
+            <Export-Package>
+              com.redhat.thermostat.vm.gc.common
+            </Export-Package>
+            <Private-Package>
+              com.redhat.thermostat.vm.gc.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>
+  </dependencies>
+</project>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-gc/common/src/main/java/com/redhat/thermostat/vm/gc/common/VmGcStatDAO.java	Mon Jan 07 15:50:59 2013 -0500
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2012 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.gc.common;
+
+import java.util.List;
+
+import com.redhat.thermostat.common.dao.VmRef;
+import com.redhat.thermostat.storage.core.Category;
+import com.redhat.thermostat.storage.core.Key;
+import com.redhat.thermostat.storage.model.VmGcStat;
+
+public interface VmGcStatDAO {
+
+    static final Key<String> collectorKey = new Key<>("collectorName", false);
+    static final Key<Long> runCountKey = new Key<>("runCount", false);
+    /** time in microseconds */
+    static final Key<Long> wallTimeKey = new Key<>("wallTime", false);
+
+    static final Category vmGcStatCategory = new Category("vm-gc-stats",
+            Key.AGENT_ID, Key.VM_ID, Key.TIMESTAMP, collectorKey,
+            runCountKey, wallTimeKey);
+
+    public List<VmGcStat> getLatestVmGcStats(VmRef ref, long since);
+
+    public void putVmGcStat(VmGcStat stat);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-gc/common/src/main/java/com/redhat/thermostat/vm/gc/common/internal/Activator.java	Mon Jan 07 15:50:59 2013 -0500
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2013 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.gc.common.internal;
+
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+import org.osgi.framework.ServiceRegistration;
+import org.osgi.util.tracker.ServiceTracker;
+
+import com.redhat.thermostat.storage.core.Storage;
+import com.redhat.thermostat.vm.gc.common.VmGcStatDAO;
+
+public class Activator implements BundleActivator {
+    
+    private ServiceTracker tracker;
+    private ServiceRegistration reg;
+
+    @Override
+    public void start(BundleContext context) throws Exception {
+        tracker = new ServiceTracker(context, Storage.class.getName(), null) {
+            @Override
+            public Object addingService(ServiceReference reference) {
+                Storage storage = (Storage) context.getService(reference);
+                VmGcStatDAO vmGcStatDao = new VmGcStatDAOImpl(storage);
+                reg = context.registerService(VmGcStatDAO.class.getName(), vmGcStatDao, null);
+                return super.addingService(reference);
+            }
+            
+            @Override
+            public void removedService(ServiceReference reference,
+                    Object service) {
+                reg.unregister();
+                super.removedService(reference, service);
+            }
+        };
+        tracker.open();
+    }
+
+    @Override
+    public void stop(BundleContext context) throws Exception {
+        tracker.close();
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-gc/common/src/main/java/com/redhat/thermostat/vm/gc/common/internal/VmGcStatDAOImpl.java	Mon Jan 07 15:50:59 2013 -0500
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2012 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.gc.common.internal;
+
+import java.util.List;
+
+import com.redhat.thermostat.common.dao.VmLatestPojoListGetter;
+import com.redhat.thermostat.common.dao.VmRef;
+import com.redhat.thermostat.storage.core.Storage;
+import com.redhat.thermostat.storage.model.VmGcStat;
+import com.redhat.thermostat.vm.gc.common.VmGcStatDAO;
+
+public class VmGcStatDAOImpl implements VmGcStatDAO {
+
+    private final Storage storage;
+    private final VmLatestPojoListGetter<VmGcStat> getter;
+
+    VmGcStatDAOImpl(Storage storage) {
+        this.storage = storage;
+        storage.registerCategory(vmGcStatCategory);
+        getter = new VmLatestPojoListGetter<>(storage, vmGcStatCategory, VmGcStat.class);
+    }
+
+    @Override
+    public List<VmGcStat> getLatestVmGcStats(VmRef ref, long since) {
+        return getter.getLatest(ref, since);
+    }
+
+    @Override
+    public void putVmGcStat(VmGcStat stat) {
+        storage.putPojo(vmGcStatCategory, false, stat);
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-gc/common/src/test/java/com/redhat/thermostat/vm/gc/common/internal/ActivatorTest.java	Mon Jan 07 15:50:59 2013 -0500
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2013 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.gc.common.internal;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
+
+import org.junit.Test;
+
+import com.redhat.thermostat.storage.core.Storage;
+import com.redhat.thermostat.test.StubBundleContext;
+import com.redhat.thermostat.vm.gc.common.VmGcStatDAO;
+
+public class ActivatorTest {
+    
+    @Test
+    public void verifyActivatorDoesNotRegisterServiceOnMissingDeps() throws Exception {
+        StubBundleContext context = new StubBundleContext();
+
+        Activator activator = new Activator();
+
+        activator.start(context);
+
+        assertEquals(0, context.getAllServices().size());
+        assertEquals(1, context.getServiceListeners().size());
+
+        activator.stop(context);
+    }
+
+    @Test
+    public void verifyActivatorRegistersServices() throws Exception {
+        StubBundleContext context = new StubBundleContext();
+        Storage storage = mock(Storage.class);
+
+        context.registerService(Storage.class, storage, null);
+
+        Activator activator = new Activator();
+
+        activator.start(context);
+
+        assertTrue(context.isServiceRegistered(VmGcStatDAO.class.getName(), VmGcStatDAOImpl.class));
+
+        activator.stop(context);
+
+        assertEquals(0, context.getServiceListeners().size());
+        assertEquals(1, context.getAllServices().size());
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-gc/common/src/test/java/com/redhat/thermostat/vm/gc/common/internal/VmGcStatDAOTest.java	Mon Jan 07 15:50:59 2013 -0500
@@ -0,0 +1,133 @@
+/*
+ * Copyright 2012 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.gc.common.internal;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.same;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import java.util.Collection;
+import java.util.List;
+
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+
+import com.redhat.thermostat.common.dao.HostRef;
+import com.redhat.thermostat.common.dao.VmRef;
+import com.redhat.thermostat.storage.core.Cursor;
+import com.redhat.thermostat.storage.core.Key;
+import com.redhat.thermostat.storage.core.Query;
+import com.redhat.thermostat.storage.core.Storage;
+import com.redhat.thermostat.storage.core.Query.Criteria;
+import com.redhat.thermostat.storage.model.VmGcStat;
+import com.redhat.thermostat.test.MockQuery;
+import com.redhat.thermostat.vm.gc.common.VmGcStatDAO;
+import com.redhat.thermostat.vm.gc.common.internal.VmGcStatDAOImpl;
+
+public class VmGcStatDAOTest {
+
+    private static final Integer VM_ID = 123;
+    private static final Long TIMESTAMP = 456L;
+    private static final String COLLECTOR = "collector1";
+    private static final Long RUN_COUNT = 10L;
+    private static final Long WALL_TIME = 9L;
+
+    @Test
+    public void testCategory() {
+        assertEquals("vm-gc-stats", VmGcStatDAO.vmGcStatCategory.getName());
+        Collection<Key<?>> keys = VmGcStatDAO.vmGcStatCategory.getKeys();
+        assertTrue(keys.contains(new Key<>("agentId", true)));
+        assertTrue(keys.contains(new Key<Integer>("vmId", true)));
+        assertTrue(keys.contains(new Key<Long>("timeStamp", false)));
+        assertTrue(keys.contains(new Key<String>("collectorName", false)));
+        assertTrue(keys.contains(new Key<Long>("runCount", false)));
+        assertTrue(keys.contains(new Key<Long>("wallTime", false)));
+        assertEquals(6, keys.size());
+    }
+
+    @Test
+    public void testGetLatestVmGcStatsBasic() {
+
+        VmGcStat vmGcStat = new VmGcStat(VM_ID, TIMESTAMP, COLLECTOR, RUN_COUNT, WALL_TIME);
+
+        @SuppressWarnings("unchecked")
+        Cursor<VmGcStat> cursor = mock(Cursor.class);
+        when(cursor.hasNext()).thenReturn(true).thenReturn(false);
+        when(cursor.next()).thenReturn(vmGcStat);
+
+        Storage storage = mock(Storage.class);
+        when(storage.createQuery()).thenReturn(new MockQuery());
+        when(storage.findAllPojos(any(Query.class), same(VmGcStat.class))).thenReturn(cursor);
+
+        HostRef hostRef = mock(HostRef.class);
+        when(hostRef.getAgentId()).thenReturn("system");
+
+        VmRef vmRef = mock(VmRef.class);
+        when(vmRef.getAgent()).thenReturn(hostRef);
+        when(vmRef.getId()).thenReturn(321);
+
+
+        VmGcStatDAO dao = new VmGcStatDAOImpl(storage);
+        List<VmGcStat> vmGcStats = dao.getLatestVmGcStats(vmRef, Long.MIN_VALUE);
+
+        ArgumentCaptor<MockQuery> arg = ArgumentCaptor.forClass(MockQuery.class);
+        verify(storage).findAllPojos(arg.capture(), same(VmGcStat.class));
+        assertTrue(arg.getValue().hasWhereClause(Key.TIMESTAMP, Criteria.GREATER_THAN, Long.MIN_VALUE));
+
+        assertEquals(1, vmGcStats.size());
+        VmGcStat stat = vmGcStats.get(0);
+        assertEquals(TIMESTAMP, (Long) stat.getTimeStamp());
+        assertEquals(VM_ID, (Integer) stat.getVmId());
+        assertEquals(COLLECTOR, stat.getCollectorName());
+        assertEquals(RUN_COUNT, (Long) stat.getRunCount());
+        assertEquals(WALL_TIME, (Long) stat.getWallTime());
+    }
+
+    @Test
+    public void testPutVmGcStat() {
+        Storage storage = mock(Storage.class);
+        VmGcStat stat = new VmGcStat(VM_ID, TIMESTAMP, COLLECTOR, RUN_COUNT, WALL_TIME);
+        VmGcStatDAO dao = new VmGcStatDAOImpl(storage);
+        dao.putVmGcStat(stat);
+
+        verify(storage).putPojo(VmGcStatDAO.vmGcStatCategory, false, stat);
+    }
+}
--- a/vm-gc/pom.xml	Mon Jan 07 15:50:03 2013 -0500
+++ b/vm-gc/pom.xml	Mon Jan 07 15:50:59 2013 -0500
@@ -51,8 +51,10 @@
   <name>Thermostat VM GC plugin</name>
 
   <modules>
+    <module>agent</module>
     <module>client-core</module>
     <module>client-swing</module>
+    <module>common</module>
     <module>remote-collector-command</module>
     <module>remote-collector-client-common</module>
     <module>remote-collector-client-swing</module>