changeset 896:3cc2f2ea31b3

Create VM ClassStat agent and common bundles This commit extracts VM ClassStat data collection from SystemBackend and related classes into a vm-classstat-agent bundle. It also moves the VmClassStatDAO from common-core into a vm-classstat-common bundle that registers the DAO once Storage is available. This also removes the DAO from DAOFactory. This commit also simplifies what's left of JvmStatHostListener since it only handles collecting VmInfo data, and rewrites the corresponding test to better test the VmInfo relevant functionality. Reviewed-by: omajid Review-thread: http://icedtea.classpath.org/pipermail/thermostat/2013-January/004998.html
author Elliott Baron <ebaron@redhat.com>
date Mon, 07 Jan 2013 15:51:53 -0500
parents ceb28ef8f9e8
children 72638d2844fa
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/VmClassStatDAO.java common/core/src/main/java/com/redhat/thermostat/common/dao/VmClassStatDAOImpl.java common/core/src/test/java/com/redhat/thermostat/common/dao/MongoDAOFactoryTest.java common/core/src/test/java/com/redhat/thermostat/common/dao/VmClassStatDAOTest.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/JvmStatVmClassListener.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 system-backend/src/test/java/com/redhat/thermostat/backend/system/JvmStatVmClassListenerTest.java vm-classstat/agent/pom.xml vm-classstat/agent/src/main/java/com/redhat/thermostat/vm/classstat/agent/internal/Activator.java vm-classstat/agent/src/main/java/com/redhat/thermostat/vm/classstat/agent/internal/VmClassStatBackend.java vm-classstat/agent/src/main/java/com/redhat/thermostat/vm/classstat/agent/internal/VmClassStatDataExtractor.java vm-classstat/agent/src/main/java/com/redhat/thermostat/vm/classstat/agent/internal/VmClassStatHostListener.java vm-classstat/agent/src/main/java/com/redhat/thermostat/vm/classstat/agent/internal/VmClassStatVmListener.java vm-classstat/agent/src/test/java/com/redhat/thermostat/vm/classstat/agent/internal/ActivatorTest.java vm-classstat/agent/src/test/java/com/redhat/thermostat/vm/classstat/agent/internal/VmClassStatBackendTest.java vm-classstat/agent/src/test/java/com/redhat/thermostat/vm/classstat/agent/internal/VmClassStatDataExtractorTest.java vm-classstat/agent/src/test/java/com/redhat/thermostat/vm/classstat/agent/internal/VmClassStatHostListenerTest.java vm-classstat/agent/src/test/java/com/redhat/thermostat/vm/classstat/agent/internal/VmClassStatVmListenerTest.java vm-classstat/client-core/pom.xml vm-classstat/client-core/src/main/java/com/redhat/thermostat/vm/classstat/client/core/VmClassStatService.java vm-classstat/client-core/src/main/java/com/redhat/thermostat/vm/classstat/client/core/internal/Activator.java vm-classstat/client-core/src/main/java/com/redhat/thermostat/vm/classstat/client/core/internal/VmClassStatController.java vm-classstat/client-core/src/test/java/com/redhat/thermostat/vm/classstat/client/core/internal/ActivatorTest.java vm-classstat/client-core/src/test/java/com/redhat/thermostat/vm/classstat/client/core/internal/VmClassStatControllerTest.java vm-classstat/common/pom.xml vm-classstat/common/src/main/java/com/redhat/thermostat/vm/classstat/common/VmClassStatDAO.java vm-classstat/common/src/main/java/com/redhat/thermostat/vm/classstat/common/internal/Activator.java vm-classstat/common/src/main/java/com/redhat/thermostat/vm/classstat/common/internal/VmClassStatDAOImpl.java vm-classstat/common/src/test/java/com/redhat/thermostat/vm/classstat/common/internal/ActivatorTest.java vm-classstat/common/src/test/java/com/redhat/thermostat/vm/classstat/common/internal/VmClassStatDAOTest.java vm-classstat/pom.xml
diffstat 41 files changed, 1823 insertions(+), 588 deletions(-) [+]
line wrap: on
line diff
--- a/common/core/src/main/java/com/redhat/thermostat/common/dao/DAOFactory.java	Mon Jan 07 15:50:59 2013 -0500
+++ b/common/core/src/main/java/com/redhat/thermostat/common/dao/DAOFactory.java	Mon Jan 07 15:51:53 2013 -0500
@@ -56,8 +56,6 @@
 
     public VmInfoDAO getVmInfoDAO();
 
-    public VmClassStatDAO getVmClassStatsDAO();
-
     public void registerDAOsAndStorageAsOSGiServices();
     public void unregisterDAOsAndStorageAsOSGiServices();
 
--- a/common/core/src/main/java/com/redhat/thermostat/common/dao/DAOFactoryImpl.java	Mon Jan 07 15:50:59 2013 -0500
+++ b/common/core/src/main/java/com/redhat/thermostat/common/dao/DAOFactoryImpl.java	Mon Jan 07 15:51:53 2013 -0500
@@ -59,7 +59,6 @@
     private HostInfoDAO hostInfoDAO;
     private NetworkInterfaceInfoDAO networkInfoDAO;
     private VmInfoDAO vmInfoDAO;
-    private VmClassStatDAO vmClassStatDAO;
 
     public DAOFactoryImpl(StorageProvider prov) {
         this(FrameworkUtil.getBundle(DAOFactoryImpl.class).getBundleContext(), prov);
@@ -106,12 +105,6 @@
     }
 
     @Override
-    public VmClassStatDAO getVmClassStatsDAO() {
-        ensureStorageConnected();
-        return vmClassStatDAO;
-    }
-
-    @Override
     public Storage getStorage() {
         return storage;
     }
@@ -135,7 +128,6 @@
         registerAndRecordService(NetworkInterfaceInfoDAO.class, getNetworkInterfaceInfoDAO());
 
         registerAndRecordService(VmInfoDAO.class, getVmInfoDAO());
-        registerAndRecordService(VmClassStatDAO.class, getVmClassStatsDAO());
     }
 
     /*
@@ -147,7 +139,6 @@
         hostInfoDAO = new HostInfoDAOImpl(storage, agentDAO);
         networkInfoDAO = new NetworkInterfaceInfoDAOImpl(storage);
         vmInfoDAO = new VmInfoDAOImpl(storage);
-        vmClassStatDAO = new VmClassStatDAOImpl(storage);
     }
 
     private <K> void registerAndRecordService(Class<K> serviceType, K serviceImplementation) {
--- a/common/core/src/main/java/com/redhat/thermostat/common/dao/VmClassStatDAO.java	Mon Jan 07 15:50:59 2013 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,56 +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.VmClassStat;
-
-public interface VmClassStatDAO {
-
-    static final Key<Long> loadedClassesKey = new Key<>("loadedClasses", false);
-
-    static final Category vmClassStatsCategory = new Category(
-            "vm-class-stats", Key.AGENT_ID, Key.VM_ID, Key.TIMESTAMP, loadedClassesKey);
-
-    public List<VmClassStat> getLatestClassStats(VmRef ref, long since);
-
-    public void putVmClassStat(VmClassStat stat);
-
-}
\ No newline at end of file
--- a/common/core/src/main/java/com/redhat/thermostat/common/dao/VmClassStatDAOImpl.java	Mon Jan 07 15:50:59 2013 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,64 +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.VmClassStat;
-
-class VmClassStatDAOImpl implements VmClassStatDAO {
-
-    private final Storage storage;
-    private final VmLatestPojoListGetter<VmClassStat> getter;
-
-    VmClassStatDAOImpl(Storage storage) {
-        this.storage = storage;
-        storage.registerCategory(vmClassStatsCategory);
-        this.getter = new VmLatestPojoListGetter<>(storage, vmClassStatsCategory, VmClassStat.class);
-    }
-
-    @Override
-    public List<VmClassStat> getLatestClassStats(VmRef ref, long lastUpdateTime) {
-        return getter.getLatest(ref, lastUpdateTime);
-    }
-
-    @Override
-    public void putVmClassStat(VmClassStat stat) {
-        storage.putPojo(vmClassStatsCategory, false, stat);
-    }
-}
--- a/common/core/src/test/java/com/redhat/thermostat/common/dao/MongoDAOFactoryTest.java	Mon Jan 07 15:50:59 2013 -0500
+++ b/common/core/src/test/java/com/redhat/thermostat/common/dao/MongoDAOFactoryTest.java	Mon Jan 07 15:51:53 2013 -0500
@@ -90,12 +90,6 @@
     }
 
     @Test
-    public void testGetVmClassStatsDAO() {
-        VmClassStatDAO dao = daoFactory.getVmClassStatsDAO();
-        assertNotNull(dao);
-    }
-
-    @Test
     public void testGetVmInfoDAO() {
         VmInfoDAO dao = daoFactory.getVmInfoDAO();
         assertNotNull(dao);
@@ -119,8 +113,8 @@
 
         daoFactory.registerDAOsAndStorageAsOSGiServices();
 
-        // currently 7 DAOs and Storage are registered
-        assertEquals(7, bundleContext.getAllServices().size());
+        // currently 6 DAOs and Storage are registered
+        assertEquals(6, bundleContext.getAllServices().size());
     }
 
     @Test
--- a/common/core/src/test/java/com/redhat/thermostat/common/dao/VmClassStatDAOTest.java	Mon Jan 07 15:50:59 2013 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,134 +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 org.mockito.invocation.InvocationOnMock;
-import org.mockito.stubbing.Answer;
-
-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.VmClassStat;
-import com.redhat.thermostat.test.MockQuery;
-
-public class VmClassStatDAOTest {
-
-    private static final Long TIMESTAMP = 1234L;
-    private static final Integer VM_ID = 123;
-    private static final Long LOADED_CLASSES = 12345L;
-
-    @Test
-    public void testCategory() {
-        assertEquals("vm-class-stats", VmClassStatDAO.vmClassStatsCategory.getName());
-        Collection<Key<?>> keys = VmClassStatDAO.vmClassStatsCategory.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<Long>("loadedClasses", false)));
-        assertEquals(4, keys.size());
-    }
-
-    @Test
-    public void testGetLatestClassStatsBasic() {
-
-        VmClassStat vmClassStat = getClassStat();
-
-        @SuppressWarnings("unchecked")
-        Cursor<VmClassStat> cursor = mock(Cursor.class);
-        when(cursor.hasNext()).thenReturn(true).thenReturn(false);
-        when(cursor.next()).thenReturn(vmClassStat);
-
-        Storage storage = mock(Storage.class);
-        when(storage.createQuery()).then(new Answer<Query>() {
-            @Override
-            public Query answer(InvocationOnMock invocation) throws Throwable {
-                return new MockQuery();
-            }
-        });
-        when(storage.findAllPojos(any(Query.class), same(VmClassStat.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);
-
-        VmClassStatDAO dao = new VmClassStatDAOImpl(storage);
-        List<VmClassStat> vmClassStats = dao.getLatestClassStats(vmRef, Long.MIN_VALUE);
-
-        ArgumentCaptor<MockQuery> arg = ArgumentCaptor.forClass(MockQuery.class);
-        verify(storage).findAllPojos(arg.capture(), same(VmClassStat.class));
-        assertTrue(arg.getValue().hasWhereClause(Key.TIMESTAMP, Criteria.GREATER_THAN, Long.MIN_VALUE));
-
-        assertEquals(1, vmClassStats.size());
-        VmClassStat stat = vmClassStats.get(0);
-        assertEquals(TIMESTAMP, (Long) stat.getTimeStamp());
-        assertEquals(LOADED_CLASSES, (Long) stat.getLoadedClasses());
-        assertEquals(VM_ID, (Integer) stat.getVmId());
-    }
-
-    private VmClassStat getClassStat() {
-        return new VmClassStat(VM_ID, TIMESTAMP, LOADED_CLASSES);
-    }
-
-    @Test
-    public void testPutVmClassStat() {
-
-        Storage storage = mock(Storage.class);
-        VmClassStat stat = new VmClassStat(VM_ID, TIMESTAMP, LOADED_CLASSES);
-        VmClassStatDAO dao = new VmClassStatDAOImpl(storage);
-        dao.putVmClassStat(stat);
-
-        verify(storage).putPojo(VmClassStatDAO.vmClassStatsCategory, false, stat);
-    }
-}
--- a/distribution/config/commands/agent.properties	Mon Jan 07 15:50:59 2013 -0500
+++ b/distribution/config/commands/agent.properties	Mon Jan 07 15:51:53 2013 -0500
@@ -19,6 +19,8 @@
           thermostat-vm-memory-agent-@project.version@.jar, \
           thermostat-vm-gc-common-@project.version@.jar, \
           thermostat-vm-gc-agent-@project.version@.jar, \
+          thermostat-vm-classstat-common-@project.version@.jar, \
+          thermostat-vm-classstat-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:59 2013 -0500
+++ b/distribution/config/commands/gui.properties	Mon Jan 07 15:51:53 2013 -0500
@@ -36,6 +36,7 @@
           thermostat-vm-heap-analysis-common-@project.version@.jar, \
           thermostat-vm-heap-analysis-command-@project.version@.jar, \
           thermostat-killvm-client-swing-@project.version@.jar, \
+          thermostat-vm-classstat-common-@project.version@.jar, \
           thermostat-vm-classstat-client-core-@project.version@.jar, \
           thermostat-vm-classstat-client-swing-@project.version@.jar, \
           thermostat-osgi-living-vm-filter-core-@project.version@.jar, \
--- a/distribution/pom.xml	Mon Jan 07 15:50:59 2013 -0500
+++ b/distribution/pom.xml	Mon Jan 07 15:51:53 2013 -0500
@@ -437,6 +437,11 @@
     </dependency>
     <dependency>
       <groupId>com.redhat.thermostat</groupId>
+      <artifactId>thermostat-vm-classstat-agent</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>com.redhat.thermostat</groupId>
       <artifactId>thermostat-vm-memory-client-swing</artifactId>
       <version>${project.version}</version>
     </dependency>
--- a/eclipse/com.redhat.thermostat.client.feature/feature.xml	Mon Jan 07 15:50:59 2013 -0500
+++ b/eclipse/com.redhat.thermostat.client.feature/feature.xml	Mon Jan 07 15:51:53 2013 -0500
@@ -217,4 +217,11 @@
          version="0.0.0"
          unpack="false"/>
 
+   <plugin
+         id="com.redhat.thermostat.vm.classstat.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:59 2013 -0500
+++ b/system-backend/src/main/java/com/redhat/thermostat/backend/system/JvmStatDataExtractor.java	Mon Jan 07 15:51:53 2013 -0500
@@ -98,8 +98,4 @@
         return MonitoredVmUtil.jvmArgs(vm);
     }
 
-    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:59 2013 -0500
+++ b/system-backend/src/main/java/com/redhat/thermostat/backend/system/JvmStatHostListener.java	Mon Jan 07 15:51:53 2013 -0500
@@ -38,11 +38,8 @@
 
 import java.net.URISyntaxException;
 import java.util.HashMap;
-import java.util.List;
 import java.util.Map;
 import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.CopyOnWriteArrayList;
 import java.util.concurrent.CopyOnWriteArraySet;
 import java.util.logging.Level;
 import java.util.logging.Logger;
@@ -53,12 +50,10 @@
 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.agent.JvmStatusListener;
 import com.redhat.thermostat.agent.JvmStatusNotifier;
-import com.redhat.thermostat.common.dao.VmClassStatDAO;
 import com.redhat.thermostat.common.dao.VmInfoDAO;
 import com.redhat.thermostat.common.utils.LoggingUtils;
 import com.redhat.thermostat.storage.model.VmInfo;
@@ -68,35 +63,16 @@
 
     private static final Logger logger = LoggingUtils.getLogger(JvmStatHostListener.class);
 
-    private boolean attachNew;
-
     private final VmInfoDAO vmInfoDAO;
-    private final VmClassStatDAO vmClassStatDAO;
 
     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, VmClassStatDAO vmClassStatDAO, boolean attachNew) {
+    JvmStatHostListener(VmInfoDAO vmInfoDAO) {
         this.vmInfoDAO = vmInfoDAO;
-        this.vmClassStatDAO = vmClassStatDAO;
-        this.attachNew = attachNew;        
     }
 
-    void removeAllListeners() {
-        for (MonitoredVm vm : monitoredVms.values()) {
-            for (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");
@@ -135,42 +111,16 @@
         MonitoredVm vm = host.getMonitoredVm(host.getHostIdentifier().resolve(
                 new VmIdentifier(vmId.toString())));
         if (vm != null) {
-            VmInfo info = null;
+            JvmStatDataExtractor extractor = new JvmStatDataExtractor(vm);
             try {
                 long startTime = System.currentTimeMillis();
                 long stopTime = Long.MIN_VALUE;
-                JvmStatDataExtractor extractor = new JvmStatDataExtractor(vm);
-                Map<String, String> properties = new HashMap<String, String>();
-                ProcDataSource dataSource = new ProcDataSource();
-                Map<String, String> environment = new ProcessEnvironmentBuilder(dataSource).build(vmId);
-                // TODO actually figure out the loaded libraries.
-                String[] loadedNativeLibraries = new String[0];
-                info = new VmInfo(vmId, startTime, stopTime,
-                        extractor.getJavaVersion(), extractor.getJavaHome(),
-                        extractor.getMainClass(), extractor.getCommandLine(),
-                        extractor.getVmName(), extractor.getVmInfo(), extractor.getVmVersion(), extractor.getVmArguments(),
-                        properties, environment, loadedNativeLibraries);
-                vmInfoDAO.putVmInfo(info);
+                recordVmInfo(vmId, startTime, stopTime, extractor);
                 logger.finer("Sent VM_STARTED messsage");
             } catch (MonitorException me) {
                 logger.log(Level.WARNING, "error getting vm info for " + vmId, me);
             }
 
-            if (attachNew) {
-                List<VmListener> listeners = registeredListeners.get(vm);
-                if (listeners == null) {
-                    listeners = new CopyOnWriteArrayList<>();
-                }
-                
-                VmListener listener = new JvmStatVmClassListener(vmClassStatDAO, vmId);
-                vm.addVmListener(listener);
-                listeners.add(listener);
-                
-                registeredListeners.put(vm, listeners);
-                
-            } else {
-                logger.log(Level.FINE, "skipping new vm " + vmId);
-            }
             for (JvmStatusListener statusListener : statusListeners) {
                 statusListener.jvmStarted(vmId);
             }
@@ -179,6 +129,21 @@
         }
     }
 
+    void recordVmInfo(Integer vmId, long startTime, long stopTime,
+            JvmStatDataExtractor extractor) throws MonitorException {
+        Map<String, String> properties = new HashMap<String, String>();
+        ProcDataSource dataSource = new ProcDataSource();
+        Map<String, String> environment = new ProcessEnvironmentBuilder(dataSource).build(vmId);
+        // TODO actually figure out the loaded libraries.
+        String[] loadedNativeLibraries = new String[0];
+        VmInfo info = new VmInfo(vmId, startTime, stopTime,
+                extractor.getJavaVersion(), extractor.getJavaHome(),
+                extractor.getMainClass(), extractor.getCommandLine(),
+                extractor.getVmName(), extractor.getVmInfo(), extractor.getVmVersion(), extractor.getVmArguments(),
+                properties, environment, loadedNativeLibraries);
+        vmInfoDAO.putVmInfo(info);
+    }
+
     private void sendStoppedVM(Integer vmId, MonitoredHost host) throws URISyntaxException, MonitorException {
         
         VmIdentifier resolvedVmID = host.getHostIdentifier().resolve(new VmIdentifier(vmId.toString()));
@@ -190,14 +155,6 @@
             vmInfoDAO.putVmStoppedTime(vmId, stopTime);
 
             MonitoredVm vm = monitoredVms.remove(vmId);
-            List<VmListener> listeners = registeredListeners.remove(vm);
-            for (VmListener listener : listeners) {
-                try {
-                    if (listener != null) vm.removeVmListener(listener);
-                } catch (MonitorException e) {
-                    logger.log(Level.WARNING, "can't remove vm listener", e);
-                }
-            }
             vm.detach();
         }
     }
@@ -211,4 +168,12 @@
     public void removeJvmStatusListener(JvmStatusListener listener) {
         statusListeners.remove(listener);
     }
+    
+    /*
+     * For testing purposes only.
+     */
+    Map<Integer, MonitoredVm> getMonitoredVms() {
+        return monitoredVms;
+    }
+    
 }
--- a/system-backend/src/main/java/com/redhat/thermostat/backend/system/JvmStatVmClassListener.java	Mon Jan 07 15:50:59 2013 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,90 +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.VmClassStatDAO;
-import com.redhat.thermostat.common.utils.LoggingUtils;
-import com.redhat.thermostat.storage.model.VmClassStat;
-
-class JvmStatVmClassListener implements VmListener {
-
-    private static final Logger logger = LoggingUtils.getLogger(JvmStatVmClassListener.class);
-
-    private VmClassStatDAO dao;
-    private int vmId;
-
-    JvmStatVmClassListener(VmClassStatDAO dao, int vmId) {
-        this.dao = dao;
-        this.vmId = vmId;
-    }
-
-    @Override
-    public void disconnected(VmEvent vmEvent) {
-        /* nothing to do here */
-    }
-
-    @Override
-    public void monitorStatusChanged(MonitorStatusChangeEvent vmEvent) {
-        /* nothing to do here */
-    }
-
-    @Override
-    public void monitorsUpdated(VmEvent vmEvent) {
-        MonitoredVm vm = vmEvent.getMonitoredVm();
-        try {
-            JvmStatDataExtractor extractor = new JvmStatDataExtractor(vm);
-            long loadedClasses = extractor.getLoadedClasses();
-            long timestamp = System.currentTimeMillis();
-            VmClassStat stat = new VmClassStat(vmId, timestamp, loadedClasses);
-            dao.putVmClassStat(stat);
-        } catch (MonitorException e) {
-            logger.log(Level.WARNING, "error gathering class info for vm " + vmId, e);
-        }
-
-
-    }
-
-}
--- a/system-backend/src/main/java/com/redhat/thermostat/backend/system/SystemBackend.java	Mon Jan 07 15:50:59 2013 -0500
+++ b/system-backend/src/main/java/com/redhat/thermostat/backend/system/SystemBackend.java	Mon Jan 07 15:51:53 2013 -0500
@@ -93,7 +93,7 @@
     protected void setDAOFactoryAction() {
         hostInfos = df.getHostInfoDAO();
         networkInterfaces = df.getNetworkInterfaceInfoDAO();
-        hostListener = new JvmStatHostListener(df.getVmInfoDAO(), df.getVmClassStatsDAO(), getObserveNewJvm());
+        hostListener = new JvmStatHostListener(df.getVmInfoDAO());
     }
 
     @Override
@@ -147,10 +147,6 @@
         removeJvmStatusListener(this);
 
         try {
-            
-            // remove all listener from the host listener
-            hostListener.removeAllListeners();
-            
             host.removeHostListener(hostListener);
         } catch (MonitorException me) {
             logger.log(Level.INFO, "something went wrong in jvmstat's listening to this host");
--- a/system-backend/src/test/java/com/redhat/thermostat/backend/system/JvmStatDataExtractorTest.java	Mon Jan 07 15:50:59 2013 -0500
+++ b/system-backend/src/test/java/com/redhat/thermostat/backend/system/JvmStatDataExtractorTest.java	Mon Jan 07 15:51:53 2013 -0500
@@ -44,7 +44,6 @@
 
 import org.junit.Test;
 
-import sun.jvmstat.monitor.LongMonitor;
 import sun.jvmstat.monitor.MonitorException;
 import sun.jvmstat.monitor.MonitoredVm;
 import sun.jvmstat.monitor.StringMonitor;
@@ -60,15 +59,6 @@
         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 testCommandLine() throws MonitorException {
         final String MONITOR_NAME = "sun.rt.javaCommand";
@@ -173,17 +163,4 @@
         assertEquals(MONITOR_VALUE, returned);
     }
 
-    @Test
-    public void testLoadedClasses() throws MonitorException {
-        final String MONITOR_NAME = "java.cls.loadedClasses";
-        final Long LOADED_CLASSES = 99l;
-        MonitoredVm vm = buildLongMonitoredVm(MONITOR_NAME, LOADED_CLASSES);
-
-        JvmStatDataExtractor extractor = new JvmStatDataExtractor(vm);
-        Long returned = extractor.getLoadedClasses();
-
-        verify(vm).findByName(eq(MONITOR_NAME));
-        assertEquals(LOADED_CLASSES, returned);
-    }
-
 }
--- a/system-backend/src/test/java/com/redhat/thermostat/backend/system/JvmStatHostListenerTest.java	Mon Jan 07 15:50:59 2013 -0500
+++ b/system-backend/src/test/java/com/redhat/thermostat/backend/system/JvmStatHostListenerTest.java	Mon Jan 07 15:51:53 2013 -0500
@@ -36,58 +36,151 @@
 
 package com.redhat.thermostat.backend.system;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
 import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyString;
+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 java.net.URISyntaxException;
+import java.util.Collections;
 import java.util.HashSet;
 import java.util.Set;
 
+import org.junit.Before;
 import org.junit.Test;
-import org.mockito.Matchers;
+import org.mockito.ArgumentCaptor;
 
 import sun.jvmstat.monitor.HostIdentifier;
+import sun.jvmstat.monitor.MonitorException;
 import sun.jvmstat.monitor.MonitoredHost;
 import sun.jvmstat.monitor.MonitoredVm;
 import sun.jvmstat.monitor.StringMonitor;
 import sun.jvmstat.monitor.VmIdentifier;
 import sun.jvmstat.monitor.event.VmStatusChangeEvent;
 
-import com.redhat.thermostat.common.dao.VmClassStatDAO;
 import com.redhat.thermostat.common.dao.VmInfoDAO;
+import com.redhat.thermostat.storage.model.VmInfo;
 
 public class JvmStatHostListenerTest {
+    
+    private static String INFO_CMDLINE = "/path/to/executable command line args";
+    private static String INFO_JAVAHOME = "/path/to/java";
+    private static String INFO_JAVAVER = "1.9001";
+    private static String INFO_MAINCLASS = "MyMainClass";
+    private static String INFO_VMARGS = "-Xarg1 -Xarg2";
+    private static String INFO_VMINFO = "Info";
+    private static String INFO_VMNAME = "MyJVM";
+    private static String INFO_VMVER = "90.01";
 
-    @Test
-    public void testVmStatusChangedAddsVmClassListener() throws Exception {
-        VmStatusChangeEvent vmEvent = mock(VmStatusChangeEvent.class);
-        Set<Integer> startedVms = new HashSet<Integer>();
-        startedVms.add(123);
-        when(vmEvent.getStarted()).thenReturn(startedVms);
+    private JvmStatHostListener hostListener;
+    private MonitoredHost host;
+    private MonitoredVm monitoredVm1;
+    private MonitoredVm monitoredVm2;
+    private JvmStatDataExtractor extractor;
+    private VmInfoDAO vmInfoDAO;
 
-        MonitoredVm vm = mock(MonitoredVm.class);
+    @Before
+    public void setup() throws MonitorException, URISyntaxException {
+        vmInfoDAO = mock(VmInfoDAO.class);
+        hostListener = new JvmStatHostListener(vmInfoDAO);
+        
+        host = mock(MonitoredHost.class);
+        HostIdentifier hostId = mock(HostIdentifier.class);
+        monitoredVm1 = mock(MonitoredVm.class);
+        monitoredVm2 = mock(MonitoredVm.class);
         StringMonitor monitor = mock(StringMonitor.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);
+        when(monitoredVm1.findByName(any(String.class))).thenReturn(monitor);
+        when(monitoredVm2.findByName(any(String.class))).thenReturn(monitor);
         when(monitor.stringValue()).thenReturn("test");
         when(monitor.getValue()).thenReturn("test");
-        when(vm.findByName(anyString())).thenReturn(monitor);
-        MonitoredHost host = mock(MonitoredHost.class);
-        HostIdentifier hostId = mock(HostIdentifier.class);
-        when(host.getHostIdentifier()).thenReturn(hostId);
-        when(host.getMonitoredVm(any(VmIdentifier.class))).thenReturn(vm);
-        when(vmEvent.getMonitoredHost()).thenReturn(host);
-
-        VmClassStatDAO vmClassDAO = mock(VmClassStatDAO.class);
-        VmInfoDAO vmInfoDAO = mock(VmInfoDAO.class);
-
-        JvmStatHostListener l = new JvmStatHostListener(vmInfoDAO, vmClassDAO ,true);
-        SystemBackend backend = mock(SystemBackend.class);
-        when(backend.getObserveNewJvm()).thenReturn(true);
-
-        l.vmStatusChanged(vmEvent);
-
-        verify(vm).addVmListener(Matchers.isA(JvmStatVmClassListener.class));
+        
+        extractor = mock(JvmStatDataExtractor.class);
+        
+        when(extractor.getCommandLine()).thenReturn(INFO_CMDLINE);
+        when(extractor.getJavaHome()).thenReturn(INFO_JAVAHOME);
+        when(extractor.getJavaVersion()).thenReturn(INFO_JAVAVER);
+        when(extractor.getMainClass()).thenReturn(INFO_MAINCLASS);
+        when(extractor.getVmArguments()).thenReturn(INFO_VMARGS);
+        when(extractor.getVmInfo()).thenReturn(INFO_VMINFO);
+        when(extractor.getVmName()).thenReturn(INFO_VMNAME);
+        when(extractor.getVmVersion()).thenReturn(INFO_VMVER);
+    }
+    
+    @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));
+    }
+    
+    @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));
     }
 
+    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);
+    }
+
+    @Test
+    public void testRecordVmInfo() throws MonitorException {
+        final int INFO_ID = 1;
+        final long INFO_STARTTIME = Long.MIN_VALUE;
+        final long INFO_STOPTIME = Long.MAX_VALUE;
+        hostListener.recordVmInfo(INFO_ID, INFO_STARTTIME, INFO_STOPTIME, extractor);
+        ArgumentCaptor<VmInfo> captor = ArgumentCaptor.forClass(VmInfo.class);
+        verify(vmInfoDAO).putVmInfo(captor.capture());
+        VmInfo info = captor.getValue();
+        
+        assertEquals(INFO_ID, info.getVmId());
+        assertEquals(INFO_STARTTIME, info.getStartTimeStamp());
+        assertEquals(INFO_STOPTIME, info.getStopTimeStamp());
+        assertEquals(INFO_CMDLINE, info.getJavaCommandLine());
+        assertEquals(INFO_JAVAHOME, info.getJavaHome());
+        assertEquals(INFO_JAVAVER, info.getJavaVersion());
+        assertEquals(INFO_MAINCLASS, info.getMainClass());
+        assertEquals(INFO_VMARGS, info.getVmArguments());
+        assertEquals(INFO_VMINFO, info.getVmInfo());
+        assertEquals(INFO_VMNAME, info.getVmName());
+        assertEquals(INFO_VMVER, info.getVmVersion());
+    }
 }
\ No newline at end of file
--- a/system-backend/src/test/java/com/redhat/thermostat/backend/system/JvmStatVmClassListenerTest.java	Mon Jan 07 15:50:59 2013 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,100 +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 static org.junit.Assert.assertEquals;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import org.junit.Test;
-import org.mockito.ArgumentCaptor;
-
-import sun.jvmstat.monitor.Monitor;
-import sun.jvmstat.monitor.MonitoredVm;
-import sun.jvmstat.monitor.event.VmEvent;
-
-import com.redhat.thermostat.common.dao.VmClassStatDAO;
-import com.redhat.thermostat.storage.model.VmClassStat;
-
-public class JvmStatVmClassListenerTest {
-
-    private static final Integer VM_ID = 123;
-    private static final Long LOADED_CLASSES = 1234L;
-
-    @Test
-    public void testMonitorUpdatedClassStat() throws Exception {
-
-        VmClassStatDAO dao = mock(VmClassStatDAO.class);
-
-        JvmStatVmClassListener l = new JvmStatVmClassListener(dao, VM_ID);
-        VmEvent vmEvent = mock(VmEvent.class);
-        MonitoredVm monitoredVm = mock(MonitoredVm.class);
-        Monitor m = mock(Monitor.class);
-        when(m.getValue()).thenReturn(LOADED_CLASSES);
-        when(monitoredVm.findByName("java.cls.loadedClasses")).thenReturn(m);
-        when(vmEvent.getMonitoredVm()).thenReturn(monitoredVm);
-
-        l.monitorsUpdated(vmEvent);
-
-        ArgumentCaptor<VmClassStat> arg = ArgumentCaptor.forClass(VmClassStat.class);
-        verify(dao).putVmClassStat(arg.capture());
-        VmClassStat stat = arg.getValue();
-        assertEquals(LOADED_CLASSES, (Long) stat.getLoadedClasses());
-        assertEquals(VM_ID, (Integer) stat.getVmId());
-    }
-
-    @Test
-    public void testMonitorUpdatedClassStatTwice() throws Exception {
-
-        VmClassStatDAO dao = mock(VmClassStatDAO.class);
-
-        JvmStatVmClassListener l = new JvmStatVmClassListener(dao, VM_ID);
-        VmEvent vmEvent = mock(VmEvent.class);
-        MonitoredVm monitoredVm = mock(MonitoredVm.class);
-        Monitor m = mock(Monitor.class);
-        when(m.getValue()).thenReturn(LOADED_CLASSES);
-        when(monitoredVm.findByName("java.cls.loadedClasses")).thenReturn(m);
-        when(vmEvent.getMonitoredVm()).thenReturn(monitoredVm);
-
-        l.monitorsUpdated(vmEvent);
-        l.monitorsUpdated(vmEvent);
-
-        // This checks a bug where the Category threw an IllegalStateException because the DAO
-        // created a new one on each call, thus violating the unique guarantee of Category.
-    }
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-classstat/agent/pom.xml	Mon Jan 07 15:51:53 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-classstat</artifactId>
+    <groupId>com.redhat.thermostat</groupId>
+    <version>0.5.0-SNAPSHOT</version>
+  </parent>
+  <artifactId>thermostat-vm-classstat-agent</artifactId>
+  <packaging>bundle</packaging>
+  <name>Thermostat VM ClassStat 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.classstat.agent</Bundle-SymbolicName>
+            <Bundle-Activator>com.redhat.thermostat.vm.classstat.agent.internal.Activator</Bundle-Activator>
+            <Export-Package>
+              com.redhat.thermostat.vm.classstat.agent
+            </Export-Package>
+            <Private-Package>
+              com.redhat.thermostat.vm.classstat.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-classstat-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-classstat/agent/src/main/java/com/redhat/thermostat/vm/classstat/agent/internal/Activator.java	Mon Jan 07 15:51:53 2013 -0500
@@ -0,0 +1,96 @@
+/*
+ * 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.classstat.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.classstat.common.VmClassStatDAO;
+
+public class Activator implements BundleActivator {
+    
+    private MultipleServiceTracker tracker;
+    private VmClassStatBackend backend;
+    private ServiceRegistration reg;
+    
+    @Override
+    public void start(final BundleContext context) throws Exception {
+        Class<?>[] deps = new Class<?>[] {
+                BackendService.class,
+                VmClassStatDAO.class
+        };
+        tracker = new MultipleServiceTracker(context, deps, new Action() {
+
+            @Override
+            public void dependenciesAvailable(Map<String, Object> services) {
+                VmClassStatDAO vmClassStatDao = (VmClassStatDAO) services.get(VmClassStatDAO.class.getName());
+                Version version = new Version(context.getBundle());
+                backend = new VmClassStatBackend(vmClassStatDao, 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.
+     */
+    VmClassStatBackend getBackend() {
+        return backend;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-classstat/agent/src/main/java/com/redhat/thermostat/vm/classstat/agent/internal/VmClassStatBackend.java	Mon Jan 07 15:51:53 2013 -0500
@@ -0,0 +1,142 @@
+/*
+ * 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.classstat.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.classstat.common.VmClassStatDAO;
+
+public class VmClassStatBackend extends Backend {
+
+    private static final Logger LOGGER = LoggingUtils.getLogger(VmClassStatBackend.class);
+
+    private VmClassStatDAO vmClassStats;
+    private HostIdentifier hostId;
+    private MonitoredHost host;
+    private VmClassStatHostListener hostListener;
+    private boolean started;
+
+    public VmClassStatBackend(VmClassStatDAO vmClassStatDAO, Version version) {
+        super(new BackendID("VM Classes Backend", VmClassStatBackend.class.getName()));
+        this.vmClassStats = vmClassStatDAO;
+        
+        setConfigurationValue(BackendsProperties.VENDOR.name(), "Red Hat, Inc.");
+        setConfigurationValue(BackendsProperties.DESCRIPTION.name(), "Gathers class loading statistics about a JVM");
+        setConfigurationValue(BackendsProperties.VERSION.name(), version.getVersionNumber());
+        
+        try {
+            hostId = new HostIdentifier((String) null);
+            host = MonitoredHost.getMonitoredHost(hostId);
+            hostListener = new VmClassStatHostListener(vmClassStats, 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, "Failed to add host listener", me);
+            }
+
+        }
+        return started;
+    }
+
+    @Override
+    public boolean deactivate() {
+        if (started && host != null) {
+            try {
+                host.removeHostListener(hostListener);
+                started = false;
+            } catch (MonitorException me) {
+                LOGGER.log(Level.INFO, "Failed to remove host listener");
+            }
+        }
+        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 + 30;
+    }
+
+    /*
+     * For testing purposes only.
+     */
+    void setHost(MonitoredHost host) {
+        this.host = host;
+    }
+    
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-classstat/agent/src/main/java/com/redhat/thermostat/vm/classstat/agent/internal/VmClassStatDataExtractor.java	Mon Jan 07 15:51:53 2013 -0500
@@ -0,0 +1,72 @@
+/*
+ * 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.classstat.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 VmClassStatDataExtractor {
+
+    /*
+     * 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 VmClassStatDataExtractor(MonitoredVm vm) {
+        this.vm = vm;
+    }
+
+    public long getLoadedClasses() throws MonitorException {
+        return (Long) vm.findByName("java.cls.loadedClasses").getValue();
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-classstat/agent/src/main/java/com/redhat/thermostat/vm/classstat/agent/internal/VmClassStatHostListener.java	Mon Jan 07 15:51:53 2013 -0500
@@ -0,0 +1,170 @@
+/*
+ * 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.classstat.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.classstat.common.VmClassStatDAO;
+
+public class VmClassStatHostListener implements HostListener {
+
+    private static final Logger logger = LoggingUtils.getLogger(VmClassStatHostListener.class);
+
+    private boolean attachNew;
+
+    private final VmClassStatDAO vmClassStatDAO;
+
+    private Map<Integer, MonitoredVm> monitoredVms  = new HashMap<>();
+    private Map<MonitoredVm, VmClassStatVmListener> registeredListeners  = new ConcurrentHashMap<>();
+
+    VmClassStatHostListener(VmClassStatDAO vmClassStatDAO, boolean attachNew) {
+        this.vmClassStatDAO = vmClassStatDAO;
+        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) {
+                VmClassStatVmListener listener = new VmClassStatVmListener(vmClassStatDAO, 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, VmClassStatVmListener> getRegisteredListeners() {
+        return registeredListeners;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-classstat/agent/src/main/java/com/redhat/thermostat/vm/classstat/agent/internal/VmClassStatVmListener.java	Mon Jan 07 15:51:53 2013 -0500
@@ -0,0 +1,90 @@
+/*
+ * 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.classstat.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.VmClassStat;
+import com.redhat.thermostat.vm.classstat.common.VmClassStatDAO;
+
+class VmClassStatVmListener implements VmListener {
+
+    private static final Logger logger = LoggingUtils.getLogger(VmClassStatVmListener.class);
+
+    private VmClassStatDAO dao;
+    private int vmId;
+
+    VmClassStatVmListener(VmClassStatDAO dao, int vmId) {
+        this.dao = dao;
+        this.vmId = vmId;
+    }
+
+    @Override
+    public void disconnected(VmEvent vmEvent) {
+        /* nothing to do here */
+    }
+
+    @Override
+    public void monitorStatusChanged(MonitorStatusChangeEvent vmEvent) {
+        /* nothing to do here */
+    }
+
+    @Override
+    public void monitorsUpdated(VmEvent vmEvent) {
+        MonitoredVm vm = vmEvent.getMonitoredVm();
+        try {
+            VmClassStatDataExtractor extractor = new VmClassStatDataExtractor(vm);
+            long loadedClasses = extractor.getLoadedClasses();
+            long timestamp = System.currentTimeMillis();
+            VmClassStat stat = new VmClassStat(vmId, timestamp, loadedClasses);
+            dao.putVmClassStat(stat);
+        } catch (MonitorException e) {
+            logger.log(Level.WARNING, "error gathering class info for vm " + vmId, e);
+        }
+
+
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-classstat/agent/src/test/java/com/redhat/thermostat/vm/classstat/agent/internal/ActivatorTest.java	Mon Jan 07 15:51:53 2013 -0500
@@ -0,0 +1,103 @@
+/*
+ * 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.classstat.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.classstat.common.VmClassStatDAO;
+
+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);
+        VmClassStatDAO vmClassStatDAO = mock(VmClassStatDAO.class);
+
+        context.registerService(BackendService.class, service, null);
+        context.registerService(VmClassStatDAO.class, vmClassStatDAO, null);
+
+        Activator activator = new Activator();
+
+        activator.start(context);
+
+        assertTrue(context.isServiceRegistered(Backend.class.getName(), VmClassStatBackend.class));
+        VmClassStatBackend 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-classstat/agent/src/test/java/com/redhat/thermostat/vm/classstat/agent/internal/VmClassStatBackendTest.java	Mon Jan 07 15:51:53 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.classstat.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.classstat.common.VmClassStatDAO;
+
+public class VmClassStatBackendTest {
+    
+    private VmClassStatBackend backend;
+    private MonitoredHost host;
+
+    @Before
+    public void setup() throws MonitorException, URISyntaxException {
+        VmClassStatDAO vmClassStatDao = mock(VmClassStatDAO.class);
+        
+        Version version = mock(Version.class);
+        when(version.getVersionNumber()).thenReturn("0.0.0");
+        
+        backend = new VmClassStatBackend(vmClassStatDao, 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-classstat/agent/src/test/java/com/redhat/thermostat/vm/classstat/agent/internal/VmClassStatDataExtractorTest.java	Mon Jan 07 15:51:53 2013 -0500
@@ -0,0 +1,75 @@
+/*
+ * 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.classstat.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;
+
+public class VmClassStatDataExtractorTest {
+
+    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 testLoadedClasses() throws MonitorException {
+        final String MONITOR_NAME = "java.cls.loadedClasses";
+        final Long LOADED_CLASSES = 99l;
+        MonitoredVm vm = buildLongMonitoredVm(MONITOR_NAME, LOADED_CLASSES);
+
+        VmClassStatDataExtractor extractor = new VmClassStatDataExtractor(vm);
+        Long returned = extractor.getLoadedClasses();
+
+        verify(vm).findByName(eq(MONITOR_NAME));
+        assertEquals(LOADED_CLASSES, returned);
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-classstat/agent/src/test/java/com/redhat/thermostat/vm/classstat/agent/internal/VmClassStatHostListenerTest.java	Mon Jan 07 15:51:53 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.classstat.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.classstat.common.VmClassStatDAO;
+
+public class VmClassStatHostListenerTest {
+    
+    private VmClassStatHostListener hostListener;
+    private MonitoredHost host;
+    private MonitoredVm monitoredVm1;
+    private MonitoredVm monitoredVm2;
+
+    @Before
+    public void setup() throws MonitorException, URISyntaxException {
+        VmClassStatDAO vmGcStatDAO = mock(VmClassStatDAO.class);
+        hostListener = new VmClassStatHostListener(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-classstat/agent/src/test/java/com/redhat/thermostat/vm/classstat/agent/internal/VmClassStatVmListenerTest.java	Mon Jan 07 15:51:53 2013 -0500
@@ -0,0 +1,101 @@
+/*
+ * 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.classstat.agent.internal;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+
+import sun.jvmstat.monitor.Monitor;
+import sun.jvmstat.monitor.MonitoredVm;
+import sun.jvmstat.monitor.event.VmEvent;
+
+import com.redhat.thermostat.storage.model.VmClassStat;
+import com.redhat.thermostat.vm.classstat.agent.internal.VmClassStatVmListener;
+import com.redhat.thermostat.vm.classstat.common.VmClassStatDAO;
+
+public class VmClassStatVmListenerTest {
+
+    private static final Integer VM_ID = 123;
+    private static final Long LOADED_CLASSES = 1234L;
+
+    @Test
+    public void testMonitorUpdatedClassStat() throws Exception {
+
+        VmClassStatDAO dao = mock(VmClassStatDAO.class);
+
+        VmClassStatVmListener l = new VmClassStatVmListener(dao, VM_ID);
+        VmEvent vmEvent = mock(VmEvent.class);
+        MonitoredVm monitoredVm = mock(MonitoredVm.class);
+        Monitor m = mock(Monitor.class);
+        when(m.getValue()).thenReturn(LOADED_CLASSES);
+        when(monitoredVm.findByName("java.cls.loadedClasses")).thenReturn(m);
+        when(vmEvent.getMonitoredVm()).thenReturn(monitoredVm);
+
+        l.monitorsUpdated(vmEvent);
+
+        ArgumentCaptor<VmClassStat> arg = ArgumentCaptor.forClass(VmClassStat.class);
+        verify(dao).putVmClassStat(arg.capture());
+        VmClassStat stat = arg.getValue();
+        assertEquals(LOADED_CLASSES, (Long) stat.getLoadedClasses());
+        assertEquals(VM_ID, (Integer) stat.getVmId());
+    }
+
+    @Test
+    public void testMonitorUpdatedClassStatTwice() throws Exception {
+
+        VmClassStatDAO dao = mock(VmClassStatDAO.class);
+
+        VmClassStatVmListener l = new VmClassStatVmListener(dao, VM_ID);
+        VmEvent vmEvent = mock(VmEvent.class);
+        MonitoredVm monitoredVm = mock(MonitoredVm.class);
+        Monitor m = mock(Monitor.class);
+        when(m.getValue()).thenReturn(LOADED_CLASSES);
+        when(monitoredVm.findByName("java.cls.loadedClasses")).thenReturn(m);
+        when(vmEvent.getMonitoredVm()).thenReturn(monitoredVm);
+
+        l.monitorsUpdated(vmEvent);
+        l.monitorsUpdated(vmEvent);
+
+        // This checks a bug where the Category threw an IllegalStateException because the DAO
+        // created a new one on each call, thus violating the unique guarantee of Category.
+    }
+}
--- a/vm-classstat/client-core/pom.xml	Mon Jan 07 15:50:59 2013 -0500
+++ b/vm-classstat/client-core/pom.xml	Mon Jan 07 15:51:53 2013 -0500
@@ -62,6 +62,11 @@
     </dependency>
     <dependency>
       <groupId>com.redhat.thermostat</groupId>
+      <artifactId>thermostat-vm-classstat-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-classstat/client-core/src/main/java/com/redhat/thermostat/vm/classstat/client/core/VmClassStatService.java	Mon Jan 07 15:50:59 2013 -0500
+++ b/vm-classstat/client-core/src/main/java/com/redhat/thermostat/vm/classstat/client/core/VmClassStatService.java	Mon Jan 07 15:51:53 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.VmClassStatDAO;
 import com.redhat.thermostat.common.dao.VmRef;
 import com.redhat.thermostat.common.utils.OSGIUtils;
 import com.redhat.thermostat.vm.classstat.client.core.internal.VmClassStatController;
+import com.redhat.thermostat.vm.classstat.common.VmClassStatDAO;
 
 public class VmClassStatService implements InformationService<VmRef> {
 
--- a/vm-classstat/client-core/src/main/java/com/redhat/thermostat/vm/classstat/client/core/internal/Activator.java	Mon Jan 07 15:50:59 2013 -0500
+++ b/vm-classstat/client-core/src/main/java/com/redhat/thermostat/vm/classstat/client/core/internal/Activator.java	Mon Jan 07 15:51:53 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.VmClassStatDAO;
 import com.redhat.thermostat.common.dao.VmRef;
 import com.redhat.thermostat.vm.classstat.client.core.VmClassStatService;
+import com.redhat.thermostat.vm.classstat.common.VmClassStatDAO;
 
 public class Activator implements BundleActivator {
 
--- a/vm-classstat/client-core/src/main/java/com/redhat/thermostat/vm/classstat/client/core/internal/VmClassStatController.java	Mon Jan 07 15:50:59 2013 -0500
+++ b/vm-classstat/client-core/src/main/java/com/redhat/thermostat/vm/classstat/client/core/internal/VmClassStatController.java	Mon Jan 07 15:51:53 2013 -0500
@@ -49,7 +49,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.VmClassStatDAO;
 import com.redhat.thermostat.common.dao.VmRef;
 import com.redhat.thermostat.common.locale.Translate;
 import com.redhat.thermostat.storage.model.DiscreteTimeData;
@@ -57,6 +56,7 @@
 import com.redhat.thermostat.vm.classstat.client.core.VmClassStatView;
 import com.redhat.thermostat.vm.classstat.client.core.VmClassStatViewProvider;
 import com.redhat.thermostat.vm.classstat.client.locale.LocaleResources;
+import com.redhat.thermostat.vm.classstat.common.VmClassStatDAO;
 
 public class VmClassStatController implements InformationServiceController<VmRef> {
 
--- a/vm-classstat/client-core/src/test/java/com/redhat/thermostat/vm/classstat/client/core/internal/ActivatorTest.java	Mon Jan 07 15:50:59 2013 -0500
+++ b/vm-classstat/client-core/src/test/java/com/redhat/thermostat/vm/classstat/client/core/internal/ActivatorTest.java	Mon Jan 07 15:51:53 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.VmClassStatDAO;
 import com.redhat.thermostat.test.StubBundleContext;
 import com.redhat.thermostat.vm.classstat.client.core.VmClassStatService;
+import com.redhat.thermostat.vm.classstat.common.VmClassStatDAO;
 
 public class ActivatorTest {
 
--- a/vm-classstat/client-core/src/test/java/com/redhat/thermostat/vm/classstat/client/core/internal/VmClassStatControllerTest.java	Mon Jan 07 15:50:59 2013 -0500
+++ b/vm-classstat/client-core/src/test/java/com/redhat/thermostat/vm/classstat/client/core/internal/VmClassStatControllerTest.java	Mon Jan 07 15:51:53 2013 -0500
@@ -54,11 +54,11 @@
 import com.redhat.thermostat.common.ApplicationService;
 import com.redhat.thermostat.common.Timer;
 import com.redhat.thermostat.common.TimerFactory;
-import com.redhat.thermostat.common.dao.VmClassStatDAO;
 import com.redhat.thermostat.common.dao.VmRef;
 import com.redhat.thermostat.storage.model.VmClassStat;
 import com.redhat.thermostat.vm.classstat.client.core.VmClassStatView;
 import com.redhat.thermostat.vm.classstat.client.core.VmClassStatViewProvider;
+import com.redhat.thermostat.vm.classstat.common.VmClassStatDAO;
 
 public class VmClassStatControllerTest {
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-classstat/common/pom.xml	Mon Jan 07 15:51:53 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-classstat</artifactId>
+    <groupId>com.redhat.thermostat</groupId>
+    <version>0.5.0-SNAPSHOT</version>
+  </parent>
+  <artifactId>thermostat-vm-classstat-common</artifactId>
+  <packaging>bundle</packaging>
+  <name>Thermostat VM ClassStat 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.classstat.common</Bundle-SymbolicName>
+            <Bundle-Activator>com.redhat.thermostat.vm.classstat.common.internal.Activator</Bundle-Activator>
+            <Export-Package>
+              com.redhat.thermostat.vm.classstat.common
+            </Export-Package>
+            <Private-Package>
+              com.redhat.thermostat.vm.classstat.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-classstat/common/src/main/java/com/redhat/thermostat/vm/classstat/common/VmClassStatDAO.java	Mon Jan 07 15:51:53 2013 -0500
@@ -0,0 +1,57 @@
+/*
+ * 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.classstat.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.VmClassStat;
+
+public interface VmClassStatDAO {
+
+    static final Key<Long> loadedClassesKey = new Key<>("loadedClasses", false);
+
+    static final Category vmClassStatsCategory = new Category(
+            "vm-class-stats", Key.AGENT_ID, Key.VM_ID, Key.TIMESTAMP, loadedClassesKey);
+
+    public List<VmClassStat> getLatestClassStats(VmRef ref, long since);
+
+    public void putVmClassStat(VmClassStat stat);
+
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-classstat/common/src/main/java/com/redhat/thermostat/vm/classstat/common/internal/Activator.java	Mon Jan 07 15:51:53 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.classstat.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.classstat.common.VmClassStatDAO;
+
+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);
+                VmClassStatDAO vmClassStatDao = new VmClassStatDAOImpl(storage);
+                reg = context.registerService(VmClassStatDAO.class.getName(), vmClassStatDao, 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-classstat/common/src/main/java/com/redhat/thermostat/vm/classstat/common/internal/VmClassStatDAOImpl.java	Mon Jan 07 15:51:53 2013 -0500
@@ -0,0 +1,67 @@
+/*
+ * 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.classstat.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.VmClassStat;
+import com.redhat.thermostat.vm.classstat.common.VmClassStatDAO;
+
+class VmClassStatDAOImpl implements VmClassStatDAO {
+
+    private final Storage storage;
+    private final VmLatestPojoListGetter<VmClassStat> getter;
+
+    VmClassStatDAOImpl(Storage storage) {
+        this.storage = storage;
+        storage.registerCategory(vmClassStatsCategory);
+        this.getter = new VmLatestPojoListGetter<>(storage, vmClassStatsCategory, VmClassStat.class);
+    }
+
+    @Override
+    public List<VmClassStat> getLatestClassStats(VmRef ref, long lastUpdateTime) {
+        return getter.getLatest(ref, lastUpdateTime);
+    }
+
+    @Override
+    public void putVmClassStat(VmClassStat stat) {
+        storage.putPojo(vmClassStatsCategory, false, stat);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-classstat/common/src/test/java/com/redhat/thermostat/vm/classstat/common/internal/ActivatorTest.java	Mon Jan 07 15:51:53 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.classstat.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.classstat.common.VmClassStatDAO;
+
+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(VmClassStatDAO.class.getName(), VmClassStatDAOImpl.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-classstat/common/src/test/java/com/redhat/thermostat/vm/classstat/common/internal/VmClassStatDAOTest.java	Mon Jan 07 15:51:53 2013 -0500
@@ -0,0 +1,138 @@
+/*
+ * 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.classstat.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 org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+
+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.VmClassStat;
+import com.redhat.thermostat.test.MockQuery;
+import com.redhat.thermostat.vm.classstat.common.VmClassStatDAO;
+import com.redhat.thermostat.vm.classstat.common.internal.VmClassStatDAOImpl;
+
+public class VmClassStatDAOTest {
+
+    private static final Long TIMESTAMP = 1234L;
+    private static final Integer VM_ID = 123;
+    private static final Long LOADED_CLASSES = 12345L;
+
+    @Test
+    public void testCategory() {
+        assertEquals("vm-class-stats", VmClassStatDAO.vmClassStatsCategory.getName());
+        Collection<Key<?>> keys = VmClassStatDAO.vmClassStatsCategory.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<Long>("loadedClasses", false)));
+        assertEquals(4, keys.size());
+    }
+
+    @Test
+    public void testGetLatestClassStatsBasic() {
+
+        VmClassStat vmClassStat = getClassStat();
+
+        @SuppressWarnings("unchecked")
+        Cursor<VmClassStat> cursor = mock(Cursor.class);
+        when(cursor.hasNext()).thenReturn(true).thenReturn(false);
+        when(cursor.next()).thenReturn(vmClassStat);
+
+        Storage storage = mock(Storage.class);
+        when(storage.createQuery()).then(new Answer<Query>() {
+            @Override
+            public Query answer(InvocationOnMock invocation) throws Throwable {
+                return new MockQuery();
+            }
+        });
+        when(storage.findAllPojos(any(Query.class), same(VmClassStat.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);
+
+        VmClassStatDAO dao = new VmClassStatDAOImpl(storage);
+        List<VmClassStat> vmClassStats = dao.getLatestClassStats(vmRef, Long.MIN_VALUE);
+
+        ArgumentCaptor<MockQuery> arg = ArgumentCaptor.forClass(MockQuery.class);
+        verify(storage).findAllPojos(arg.capture(), same(VmClassStat.class));
+        assertTrue(arg.getValue().hasWhereClause(Key.TIMESTAMP, Criteria.GREATER_THAN, Long.MIN_VALUE));
+
+        assertEquals(1, vmClassStats.size());
+        VmClassStat stat = vmClassStats.get(0);
+        assertEquals(TIMESTAMP, (Long) stat.getTimeStamp());
+        assertEquals(LOADED_CLASSES, (Long) stat.getLoadedClasses());
+        assertEquals(VM_ID, (Integer) stat.getVmId());
+    }
+
+    private VmClassStat getClassStat() {
+        return new VmClassStat(VM_ID, TIMESTAMP, LOADED_CLASSES);
+    }
+
+    @Test
+    public void testPutVmClassStat() {
+
+        Storage storage = mock(Storage.class);
+        VmClassStat stat = new VmClassStat(VM_ID, TIMESTAMP, LOADED_CLASSES);
+        VmClassStatDAO dao = new VmClassStatDAOImpl(storage);
+        dao.putVmClassStat(stat);
+
+        verify(storage).putPojo(VmClassStatDAO.vmClassStatsCategory, false, stat);
+    }
+}
--- a/vm-classstat/pom.xml	Mon Jan 07 15:50:59 2013 -0500
+++ b/vm-classstat/pom.xml	Mon Jan 07 15:51:53 2013 -0500
@@ -51,8 +51,10 @@
   <name>Thermostat VM ClassStat plugin</name>
 
   <modules>
+    <module>agent</module>
     <module>client-core</module>
     <module>client-swing</module>
+    <module>common</module>
   </modules>
 
 </project>