changeset 894:0ba74f790a8a

Create VM Memory agent and common bundles This commit extracts VM Memory data collection from SystemBackend and related classes into a vm-memory-agent bundle. It also moves the VmMemoryStatDAO from common-core into a vm-memory-common bundle that registers the DAO once Storage is available. This also removes the DAO from DAOFactory. This also temporarily adds the vm-memory-common bundle as a dependency to client-cli until my later dynamic vm-stat patch. Reviewed-by: omajid Review-thread: http://icedtea.classpath.org/pipermail/thermostat/2013-January/004995.html
author Elliott Baron <ebaron@redhat.com>
date Mon, 07 Jan 2013 15:50:03 -0500
parents 9e6bcfc40ea1
children ceb28ef8f9e8
files client/cli/pom.xml client/cli/src/main/java/com/redhat/thermostat/client/cli/internal/VMStatCommand.java client/cli/src/main/java/com/redhat/thermostat/client/cli/internal/VMStatPrinter.java client/cli/src/test/java/com/redhat/thermostat/client/cli/internal/VmStatCommandTest.java 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/VmMemoryStatDAO.java common/core/src/main/java/com/redhat/thermostat/common/dao/VmMemoryStatDAOImpl.java common/core/src/test/java/com/redhat/thermostat/common/dao/MongoDAOFactoryTest.java common/core/src/test/java/com/redhat/thermostat/common/dao/VmMemoryStatDAOTest.java distribution/config/commands/agent.properties distribution/config/commands/gui.properties distribution/config/commands/vm-stat.properties distribution/pom.xml eclipse/com.redhat.thermostat.client.feature/feature.xml eclipse/com.redhat.thermostat.client.feature/pom.xml system-backend/src/main/java/com/redhat/thermostat/backend/system/JvmStatDataExtractor.java system-backend/src/main/java/com/redhat/thermostat/backend/system/JvmStatHostListener.java system-backend/src/main/java/com/redhat/thermostat/backend/system/JvmStatVmListener.java system-backend/src/main/java/com/redhat/thermostat/backend/system/SystemBackend.java system-backend/src/test/java/com/redhat/thermostat/backend/system/JvmStatDataExtractorTest.java system-backend/src/test/java/com/redhat/thermostat/backend/system/JvmStatHostListenerTest.java vm-gc/client-core/pom.xml vm-gc/client-core/src/main/java/com/redhat/thermostat/vm/gc/client/core/VmGcService.java vm-gc/client-core/src/main/java/com/redhat/thermostat/vm/gc/client/core/internal/Activator.java vm-gc/client-core/src/main/java/com/redhat/thermostat/vm/gc/client/core/internal/VmGcController.java vm-gc/client-core/src/test/java/com/redhat/thermostat/vm/gc/client/core/internal/ActivatorTest.java vm-gc/client-core/src/test/java/com/redhat/thermostat/vm/gc/client/core/internal/VmGcControllerTest.java vm-heap-analysis/client-core/pom.xml vm-heap-analysis/client-core/src/main/java/com/redhat/thermostat/vm/heap/analysis/client/core/HeapDumperService.java vm-heap-analysis/client-core/src/main/java/com/redhat/thermostat/vm/heap/analysis/client/core/internal/Activator.java vm-heap-analysis/client-core/src/main/java/com/redhat/thermostat/vm/heap/analysis/client/core/internal/HeapDumpController.java vm-heap-analysis/client-core/src/test/java/com/redhat/thermostat/vm/heap/analysis/client/core/internal/ActivatorTest.java vm-heap-analysis/client-core/src/test/java/com/redhat/thermostat/vm/heap/analysis/client/core/internal/HeapDumpControllerTest.java vm-memory/agent/pom.xml vm-memory/agent/src/main/java/com/redhat/thermostat/vm/memory/agent/internal/Activator.java vm-memory/agent/src/main/java/com/redhat/thermostat/vm/memory/agent/internal/VmMemoryBackend.java vm-memory/agent/src/main/java/com/redhat/thermostat/vm/memory/agent/internal/VmMemoryDataExtractor.java vm-memory/agent/src/main/java/com/redhat/thermostat/vm/memory/agent/internal/VmMemoryHostListener.java vm-memory/agent/src/main/java/com/redhat/thermostat/vm/memory/agent/internal/VmMemoryVmListener.java vm-memory/agent/src/test/java/com/redhat/thermostat/vm/memory/agent/internal/ActivatorTest.java vm-memory/agent/src/test/java/com/redhat/thermostat/vm/memory/agent/internal/VmMemoryBackendTest.java vm-memory/agent/src/test/java/com/redhat/thermostat/vm/memory/agent/internal/VmMemoryDataExtractorTest.java vm-memory/agent/src/test/java/com/redhat/thermostat/vm/memory/agent/internal/VmMemoryHostListenerTest.java vm-memory/agent/src/test/java/com/redhat/thermostat/vm/memory/agent/internal/VmMemoryVmListenerTest.java vm-memory/client-core/pom.xml vm-memory/client-core/src/main/java/com/redhat/thermostat/vm/memory/client/core/MemoryStatsService.java vm-memory/client-core/src/main/java/com/redhat/thermostat/vm/memory/client/core/internal/Activator.java vm-memory/client-core/src/main/java/com/redhat/thermostat/vm/memory/client/core/internal/MemoryStatsController.java vm-memory/client-core/src/test/java/com/redhat/thermostat/vm/memory/client/core/internal/ActivatorTest.java vm-memory/client-core/src/test/java/com/redhat/thermostat/vm/memory/client/core/internal/MemoryStatsControllerTest.java vm-memory/common/pom.xml vm-memory/common/src/main/java/com/redhat/thermostat/vm/memory/common/VmMemoryStatDAO.java vm-memory/common/src/main/java/com/redhat/thermostat/vm/memory/common/internal/Activator.java vm-memory/common/src/main/java/com/redhat/thermostat/vm/memory/common/internal/VmMemoryStatDAOImpl.java vm-memory/common/src/test/java/com/redhat/thermostat/vm/memory/common/internal/ActivatorTest.java vm-memory/common/src/test/java/com/redhat/thermostat/vm/memory/common/internal/VmMemoryStatDAOTest.java vm-memory/pom.xml
diffstat 58 files changed, 2084 insertions(+), 615 deletions(-) [+]
line wrap: on
line diff
--- a/client/cli/pom.xml	Mon Jan 07 15:48:38 2013 -0500
+++ b/client/cli/pom.xml	Mon Jan 07 15:50:03 2013 -0500
@@ -106,6 +106,12 @@
       <artifactId>thermostat-vm-cpu-common</artifactId>
       <version>${project.version}</version>
     </dependency>
+    <!-- Only temporary dependency -->
+    <dependency>
+      <groupId>com.redhat.thermostat</groupId>
+      <artifactId>thermostat-vm-memory-common</artifactId>
+      <version>${project.version}</version>
+    </dependency>
   </dependencies>
 
   <build>
@@ -126,7 +132,10 @@
             <!-- Do not autogenerate uses clauses in Manifests -->
             <_nouses>true</_nouses>
             <!-- Remove this later -->
-            <Import-Package>*,com.redhat.thermostat.vm.cpu.common;resolution:=optional</Import-Package>
+            <Import-Package>
+              *,com.redhat.thermostat.vm.cpu.common;resolution:=optional,
+              com.redhat.thermostat.vm.memory.common;resolution:=optional
+            </Import-Package>
           </instructions>
         </configuration>
       </plugin>
--- a/client/cli/src/main/java/com/redhat/thermostat/client/cli/internal/VMStatCommand.java	Mon Jan 07 15:48:38 2013 -0500
+++ b/client/cli/src/main/java/com/redhat/thermostat/client/cli/internal/VMStatCommand.java	Mon Jan 07 15:50:03 2013 -0500
@@ -48,12 +48,12 @@
 import com.redhat.thermostat.common.cli.CommandException;
 import com.redhat.thermostat.common.cli.HostVMArguments;
 import com.redhat.thermostat.common.cli.SimpleCommand;
-import com.redhat.thermostat.common.dao.VmMemoryStatDAO;
 import com.redhat.thermostat.common.dao.VmRef;
 import com.redhat.thermostat.common.locale.Translate;
 import com.redhat.thermostat.common.utils.LoggingUtils;
 import com.redhat.thermostat.common.utils.OSGIUtils;
 import com.redhat.thermostat.vm.cpu.common.VmCpuStatDAO;
+import com.redhat.thermostat.vm.memory.common.VmMemoryStatDAO;
 
 public class VMStatCommand extends SimpleCommand {
 
--- a/client/cli/src/main/java/com/redhat/thermostat/client/cli/internal/VMStatPrinter.java	Mon Jan 07 15:48:38 2013 -0500
+++ b/client/cli/src/main/java/com/redhat/thermostat/client/cli/internal/VMStatPrinter.java	Mon Jan 07 15:50:03 2013 -0500
@@ -48,7 +48,6 @@
 
 import com.redhat.thermostat.common.Size;
 import com.redhat.thermostat.common.cli.TableRenderer;
-import com.redhat.thermostat.common.dao.VmMemoryStatDAO;
 import com.redhat.thermostat.common.dao.VmRef;
 import com.redhat.thermostat.common.locale.Translate;
 import com.redhat.thermostat.storage.model.TimeStampedPojo;
@@ -57,6 +56,7 @@
 import com.redhat.thermostat.storage.model.VmCpuStat;
 import com.redhat.thermostat.storage.model.VmMemoryStat;
 import com.redhat.thermostat.vm.cpu.common.VmCpuStatDAO;
+import com.redhat.thermostat.vm.memory.common.VmMemoryStatDAO;
 
 class VMStatPrinter {
 
--- a/client/cli/src/test/java/com/redhat/thermostat/client/cli/internal/VmStatCommandTest.java	Mon Jan 07 15:48:38 2013 -0500
+++ b/client/cli/src/test/java/com/redhat/thermostat/client/cli/internal/VmStatCommandTest.java	Mon Jan 07 15:50:03 2013 -0500
@@ -64,7 +64,6 @@
 import com.redhat.thermostat.common.cli.CommandException;
 import com.redhat.thermostat.common.cli.SimpleArguments;
 import com.redhat.thermostat.common.dao.HostRef;
-import com.redhat.thermostat.common.dao.VmMemoryStatDAO;
 import com.redhat.thermostat.common.dao.VmRef;
 import com.redhat.thermostat.common.utils.OSGIUtils;
 import com.redhat.thermostat.storage.model.VmCpuStat;
@@ -74,6 +73,7 @@
 import com.redhat.thermostat.test.TestCommandContextFactory;
 import com.redhat.thermostat.test.TestTimerFactory;
 import com.redhat.thermostat.vm.cpu.common.VmCpuStatDAO;
+import com.redhat.thermostat.vm.memory.common.VmMemoryStatDAO;
 
 public class VmStatCommandTest {
 
--- a/common/core/src/main/java/com/redhat/thermostat/common/dao/DAOFactory.java	Mon Jan 07 15:48:38 2013 -0500
+++ b/common/core/src/main/java/com/redhat/thermostat/common/dao/DAOFactory.java	Mon Jan 07 15:50:03 2013 -0500
@@ -56,8 +56,6 @@
 
     public VmInfoDAO getVmInfoDAO();
 
-    public VmMemoryStatDAO getVmMemoryStatDAO();
-
     public VmClassStatDAO getVmClassStatsDAO();
 
     public VmGcStatDAO getVmGcStatDAO();
--- a/common/core/src/main/java/com/redhat/thermostat/common/dao/DAOFactoryImpl.java	Mon Jan 07 15:48:38 2013 -0500
+++ b/common/core/src/main/java/com/redhat/thermostat/common/dao/DAOFactoryImpl.java	Mon Jan 07 15:50:03 2013 -0500
@@ -60,7 +60,6 @@
     private NetworkInterfaceInfoDAO networkInfoDAO;
     private VmInfoDAO vmInfoDAO;
     private VmClassStatDAO vmClassStatDAO;
-    private VmMemoryStatDAO vmMemStatDAO;
     private VmGcStatDAO vmGcStatDAO;
 
     public DAOFactoryImpl(StorageProvider prov) {
@@ -108,12 +107,6 @@
     }
 
     @Override
-    public VmMemoryStatDAO getVmMemoryStatDAO() {
-        ensureStorageConnected();
-        return vmMemStatDAO;
-    }
-
-    @Override
     public VmClassStatDAO getVmClassStatsDAO() {
         ensureStorageConnected();
         return vmClassStatDAO;
@@ -151,7 +144,6 @@
         registerAndRecordService(VmInfoDAO.class, getVmInfoDAO());
         registerAndRecordService(VmClassStatDAO.class, getVmClassStatsDAO());
         registerAndRecordService(VmGcStatDAO.class, getVmGcStatDAO());
-        registerAndRecordService(VmMemoryStatDAO.class, getVmMemoryStatDAO());
     }
 
     /*
@@ -164,7 +156,6 @@
         networkInfoDAO = new NetworkInterfaceInfoDAOImpl(storage);
         vmInfoDAO = new VmInfoDAOImpl(storage);
         vmClassStatDAO = new VmClassStatDAOImpl(storage);
-        vmMemStatDAO = new VmMemoryStatDAOImpl(storage);
         vmGcStatDAO = new VmGcStatDAOImpl(storage);
     }
 
--- a/common/core/src/main/java/com/redhat/thermostat/common/dao/VmMemoryStatDAO.java	Mon Jan 07 15:48:38 2013 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,59 +0,0 @@
-/*
- * Copyright 2012 Red Hat, Inc.
- *
- * This file is part of Thermostat.
- *
- * Thermostat is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published
- * by the Free Software Foundation; either version 2, or (at your
- * option) any later version.
- *
- * Thermostat is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Thermostat; see the file COPYING.  If not see
- * <http://www.gnu.org/licenses/>.
- *
- * Linking this code with other modules is making a combined work
- * based on this code.  Thus, the terms and conditions of the GNU
- * General Public License cover the whole combination.
- *
- * As a special exception, the copyright holders of this code give
- * you permission to link this code with independent modules to
- * produce an executable, regardless of the license terms of these
- * independent modules, and to copy and distribute the resulting
- * executable under terms of your choice, provided that you also
- * meet, for each linked independent module, the terms and conditions
- * of the license of that module.  An independent module is a module
- * which is not derived from or based on this code.  If you modify
- * this code, you may extend this exception to your version of the
- * library, but you are not obligated to do so.  If you do not wish
- * to do so, delete this exception statement from your version.
- */
-
-package com.redhat.thermostat.common.dao;
-
-import java.util.List;
-
-import com.redhat.thermostat.storage.core.Category;
-import com.redhat.thermostat.storage.core.Key;
-import com.redhat.thermostat.storage.model.VmMemoryStat;
-import com.redhat.thermostat.storage.model.VmMemoryStat.Generation;
-
-public interface VmMemoryStatDAO {
-
-    static final Key<Generation[]> generationsKey = new Key<>("generations", false);
-
-    static final Category vmMemoryStatsCategory = new Category("vm-memory-stats",
-            Key.AGENT_ID, Key.VM_ID, Key.TIMESTAMP, generationsKey);
-
-    public VmMemoryStat getLatestMemoryStat(VmRef ref);
-
-    public List<VmMemoryStat> getLatestVmMemoryStats(VmRef vm, long since);
-
-    public void putVmMemoryStat(VmMemoryStat stat);
-
-}
--- a/common/core/src/main/java/com/redhat/thermostat/common/dao/VmMemoryStatDAOImpl.java	Mon Jan 07 15:48:38 2013 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,84 +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.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.VmMemoryStat;
-
-class VmMemoryStatDAOImpl implements VmMemoryStatDAO {
-
-    private final Storage storage;
-    private final VmLatestPojoListGetter<VmMemoryStat> getter;
-
-    VmMemoryStatDAOImpl(Storage storage) {
-        this.storage = storage;
-        storage.registerCategory(vmMemoryStatsCategory);
-        getter = new VmLatestPojoListGetter<>(storage, vmMemoryStatsCategory, VmMemoryStat.class);
-    }
-
-    @Override
-    public VmMemoryStat getLatestMemoryStat(VmRef ref) {
-        Query query = storage.createQuery()
-                .from(vmMemoryStatsCategory)
-                .where(Key.AGENT_ID, Criteria.EQUALS, ref.getAgent().getAgentId())
-                .where(Key.VM_ID, Criteria.EQUALS, ref.getId())
-                .sort(Key.TIMESTAMP, Query.SortDirection.DESCENDING)
-                .limit(1);
-        Cursor<VmMemoryStat> cursor = storage.findAllPojos(query, VmMemoryStat.class);
-        if (cursor.hasNext()) {
-            return cursor.next();
-        }
-        return null;
-    }
-
-    @Override
-    public void putVmMemoryStat(VmMemoryStat stat) {
-        storage.putPojo(vmMemoryStatsCategory, false, stat);
-    }
-
-    @Override
-    public List<VmMemoryStat> getLatestVmMemoryStats(VmRef ref, long since) {
-        return getter.getLatest(ref, since);
-    }
-
-}
--- a/common/core/src/test/java/com/redhat/thermostat/common/dao/MongoDAOFactoryTest.java	Mon Jan 07 15:48:38 2013 -0500
+++ b/common/core/src/test/java/com/redhat/thermostat/common/dao/MongoDAOFactoryTest.java	Mon Jan 07 15:50:03 2013 -0500
@@ -108,12 +108,6 @@
     }
 
     @Test
-    public void testGetVmMemoryStatDAO() {
-        VmMemoryStatDAO dao = daoFactory.getVmMemoryStatDAO();
-        assertNotNull(dao);
-    }
-
-    @Test
     public void testGetHostInfoDAO() {
         HostInfoDAO dao = daoFactory.getHostInfoDAO();
         assertNotNull(dao);
@@ -131,8 +125,8 @@
 
         daoFactory.registerDAOsAndStorageAsOSGiServices();
 
-        // currently 9 DAOs and Storage are registered
-        assertEquals(9, bundleContext.getAllServices().size());
+        // currently 8 DAOs and Storage are registered
+        assertEquals(8, bundleContext.getAllServices().size());
     }
 
     @Test
--- a/common/core/src/test/java/com/redhat/thermostat/common/dao/VmMemoryStatDAOTest.java	Mon Jan 07 15:48:38 2013 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,198 +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.ArrayList;
-import java.util.Collection;
-import java.util.List;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-
-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.VmMemoryStat;
-import com.redhat.thermostat.storage.model.VmMemoryStat.Generation;
-import com.redhat.thermostat.storage.model.VmMemoryStat.Space;
-import com.redhat.thermostat.test.MockQuery;
-
-public class VmMemoryStatDAOTest {
-
-    private static final int VM_ID = 0xcafe;
-    private static final String AGENT_ID = "agent";
-
-    private Storage storage;
-    private VmRef vmRef;
-
-    private MockQuery query;
-    private Cursor<VmMemoryStat> cursor;
-
-    @SuppressWarnings("unchecked")
-    @Before
-    public void setUp() {
-        
-
-        HostRef hostRef = mock(HostRef.class);
-        when(hostRef.getAgentId()).thenReturn(AGENT_ID);
-
-        vmRef = mock(VmRef.class);
-        when(vmRef.getAgent()).thenReturn(hostRef);
-        when(vmRef.getId()).thenReturn(VM_ID);
-
-        storage = mock(Storage.class);
-        query = new MockQuery();
-        when(storage.createQuery()).thenReturn(query);
-
-        cursor = mock(Cursor.class);
-        when(storage.findAllPojos(any(Query.class), same(VmMemoryStat.class))).thenReturn(cursor);
-
-        when(cursor.hasNext()).thenReturn(false);
-
-    }
-
-    @After
-    public void tearDown() {
-        query = null;
-        vmRef = null;
-        cursor = null;
-        storage = null;
-    }
-
-    @Test
-    public void testCategories() {
-        Collection<Key<?>> keys;
-
-        assertEquals("vm-memory-stats", VmMemoryStatDAO.vmMemoryStatsCategory.getName());
-        keys = VmMemoryStatDAO.vmMemoryStatsCategory.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<Generation[]>("generations", false)));
-        assertEquals(4, keys.size());
-    }
-
-    @Test
-    public void testGetLatest() {
-        VmMemoryStatDAO impl = new VmMemoryStatDAOImpl(storage);
-        impl.getLatestMemoryStat(vmRef);
-
-        verifyQuery();
-    }
-
-    @Test
-    public void testGetLatestSince() {
-        VmMemoryStatDAO impl = new VmMemoryStatDAOImpl(storage);
-        impl.getLatestVmMemoryStats(vmRef, 123);
-
-        verifyQuery();
-
-        assertTrue(query.hasWhereClause(Key.TIMESTAMP, Criteria.GREATER_THAN, 123l));
-    }
-
-    private void verifyQuery() {
-
-        assertTrue(query.hasWhereClause(Key.AGENT_ID, Criteria.EQUALS, AGENT_ID));
-        assertTrue(query.hasWhereClause(Key.VM_ID, Criteria.EQUALS, VM_ID));
-        assertTrue(query.hasSort(Key.TIMESTAMP, Query.SortDirection.DESCENDING));
-    }
-
-    @Test
-    public void testGetLatestReturnsNullWhenStorageEmpty() {
-        when(cursor.hasNext()).thenReturn(false);
-        when(cursor.next()).thenReturn(null);
-
-        Storage storage = mock(Storage.class);
-        when(storage.createQuery()).thenReturn(new MockQuery());
-        when(storage.findAllPojos(any(Query.class), same(VmMemoryStat.class))).thenReturn(cursor);
-
-        VmMemoryStatDAO impl = new VmMemoryStatDAOImpl(storage);
-        VmMemoryStat latest = impl.getLatestMemoryStat(vmRef);
-        assertTrue(latest == null);
-    }
-
-    @Test
-    public void testPutVmMemoryStat() {
-
-        List<Generation> generations = new ArrayList<Generation>();
-
-        int i = 0;
-        for (String genName: new String[] { "new", "old", "perm" }) {
-            Generation gen = new Generation();
-            gen.setName(genName);
-            gen.setCollector(gen.getName());
-            generations.add(gen);
-            List<Space> spaces = new ArrayList<Space>();
-            String[] spaceNames = null;
-            if (genName.equals("new")) {
-                spaceNames = new String[] { "eden", "s0", "s1" };
-            } else if (genName.equals("old")) {
-                spaceNames = new String[] { "old" };
-            } else {
-                spaceNames = new String[] { "perm" };
-            }
-            for (String spaceName: spaceNames) {
-                Space space = new Space();
-                space.setName(spaceName);
-                space.setIndex(0);
-                space.setUsed(i++);
-                space.setCapacity(i++);
-                space.setMaxCapacity(i++);
-                spaces.add(space);
-            }
-            gen.setSpaces(spaces.toArray(new Space[spaces.size()]));
-        }
-        VmMemoryStat stat = new VmMemoryStat(1, 2, generations.toArray(new Generation[generations.size()]));
-
-        Storage storage = mock(Storage.class);
-        VmMemoryStatDAO dao = new VmMemoryStatDAOImpl(storage);
-        dao.putVmMemoryStat(stat);
-
-        verify(storage).putPojo(VmMemoryStatDAO.vmMemoryStatsCategory, false, stat);
-    }
-}
--- a/distribution/config/commands/agent.properties	Mon Jan 07 15:48:38 2013 -0500
+++ b/distribution/config/commands/agent.properties	Mon Jan 07 15:50:03 2013 -0500
@@ -15,6 +15,8 @@
           thermostat-host-memory-agent-@project.version@.jar, \
           thermostat-vm-cpu-common-@project.version@.jar, \
           thermostat-vm-cpu-agent-@project.version@.jar, \
+          thermostat-vm-memory-common-@project.version@.jar, \
+          thermostat-vm-memory-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:48:38 2013 -0500
+++ b/distribution/config/commands/gui.properties	Mon Jan 07 15:50:03 2013 -0500
@@ -27,6 +27,7 @@
           thermostat-vm-cpu-client-swing-@project.version@.jar, \
           thermostat-vm-gc-client-core-@project.version@.jar, \
           thermostat-vm-gc-client-swing-@project.version@.jar, \
+          thermostat-vm-memory-common-@project.version@.jar, \
           thermostat-vm-memory-client-core-@project.version@.jar, \
           thermostat-vm-memory-client-swing-@project.version@.jar, \
           thermostat-vm-heap-analysis-client-core-@project.version@.jar, \
--- a/distribution/config/commands/vm-stat.properties	Mon Jan 07 15:48:38 2013 -0500
+++ b/distribution/config/commands/vm-stat.properties	Mon Jan 07 15:50:03 2013 -0500
@@ -1,5 +1,6 @@
 bundles = thermostat-client-cli-${project.version}.jar, \
           thermostat-vm-cpu-common-${project.version}.jar, \
+          thermostat-vm-memory-common-${project.version}.jar, \
           thermostat-storage-mongodb-${project.version}.jar, \
           thermostat-web-common-${project.version}.jar, \
           thermostat-web-client-${project.version}.jar, \
--- a/distribution/pom.xml	Mon Jan 07 15:48:38 2013 -0500
+++ b/distribution/pom.xml	Mon Jan 07 15:50:03 2013 -0500
@@ -437,6 +437,11 @@
     </dependency>
     <dependency>
       <groupId>com.redhat.thermostat</groupId>
+      <artifactId>thermostat-vm-memory-agent</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>com.redhat.thermostat</groupId>
       <artifactId>thermostat-vm-heap-analysis-client-swing</artifactId>
       <version>${project.version}</version>
     </dependency>
--- a/eclipse/com.redhat.thermostat.client.feature/feature.xml	Mon Jan 07 15:48:38 2013 -0500
+++ b/eclipse/com.redhat.thermostat.client.feature/feature.xml	Mon Jan 07 15:50:03 2013 -0500
@@ -203,4 +203,11 @@
          version="0.0.0"
          unpack="false"/>
 
+   <plugin
+         id="com.redhat.thermostat.vm.memory.common"
+         download-size="0"
+         install-size="0"
+         version="0.0.0"
+         unpack="false"/>
+
 </feature>
--- a/eclipse/com.redhat.thermostat.client.feature/pom.xml	Mon Jan 07 15:48:38 2013 -0500
+++ b/eclipse/com.redhat.thermostat.client.feature/pom.xml	Mon Jan 07 15:50:03 2013 -0500
@@ -41,6 +41,11 @@
     </dependency>
     <dependency>
       <groupId>com.redhat.thermostat</groupId>
+      <artifactId>thermostat-vm-memory-client-core</artifactId>
+      <version>0.5.0-SNAPSHOT</version>
+    </dependency>
+    <dependency>
+      <groupId>com.redhat.thermostat</groupId>
       <artifactId>thermostat-vm-gc-client-core</artifactId>
       <version>0.5.0-SNAPSHOT</version>
     </dependency>
--- a/system-backend/src/main/java/com/redhat/thermostat/backend/system/JvmStatDataExtractor.java	Mon Jan 07 15:48:38 2013 -0500
+++ b/system-backend/src/main/java/com/redhat/thermostat/backend/system/JvmStatDataExtractor.java	Mon Jan 07 15:50:03 2013 -0500
@@ -36,9 +36,6 @@
 
 package com.redhat.thermostat.backend.system;
 
-import com.redhat.thermostat.storage.model.VmMemoryStat.Generation;
-
-import sun.jvmstat.monitor.Monitor;
 import sun.jvmstat.monitor.MonitorException;
 import sun.jvmstat.monitor.MonitoredVm;
 import sun.jvmstat.monitor.MonitoredVmUtil;
@@ -117,52 +114,6 @@
         return (Long) vm.findByName("sun.gc.collector." + collector + ".invocations").getValue();
     }
 
-    public long getTotalGcGenerations() throws MonitorException {
-        return (Long) vm.findByName("sun.gc.policy.generations").getValue();
-    }
-
-    public String getGenerationName(long generation) throws MonitorException {
-        return (String) vm.findByName("sun.gc.generation." + generation + ".name").getValue();
-    }
-
-    public long getGenerationCapacity(long generation) throws MonitorException {
-        return (Long) vm.findByName("sun.gc.generation." + generation + ".capacity").getValue();
-    }
-
-    public long getGenerationMaxCapacity(long generation) throws MonitorException {
-        return (Long) vm.findByName("sun.gc.generation." + generation + ".maxCapacity").getValue();
-    }
-
-    public String getGenerationCollector(long generation) throws MonitorException {
-        // this is just re-implementing getCollectorName()
-        // TODO check generation number and collector number are always associated
-        Monitor m = vm.findByName("sun.gc.collector." + generation + ".name");
-        if (m == null) {
-            return Generation.COLLECTOR_NONE;
-        }
-        return (String) m.getValue();
-    }
-
-    public long getTotalSpaces(long generation) throws MonitorException {
-        return (Long) vm.findByName("sun.gc.generation." + generation + ".spaces").getValue();
-    }
-
-    public String getSpaceName(long generation, long space) throws MonitorException {
-        return (String) vm.findByName("sun.gc.generation." + generation + ".space." + space + ".name").getValue();
-    }
-
-    public long getSpaceCapacity(long generation, long space) throws MonitorException {
-        return (Long) vm.findByName("sun.gc.generation." + generation + ".space." + space + ".capacity").getValue();
-    }
-
-    public long getSpaceMaxCapacity(long generation, long space) throws MonitorException {
-        return (Long) vm.findByName("sun.gc.generation." + generation + ".space." + space + ".maxCapacity").getValue();
-    }
-
-    public long getSpaceUsed(long generation, long space) throws MonitorException {
-        return (Long) vm.findByName("sun.gc.generation." + generation + ".space." + space + ".used").getValue();
-    }
-
     public long getLoadedClasses() throws MonitorException {
         return (Long) vm.findByName("java.cls.loadedClasses").getValue();
     }
--- a/system-backend/src/main/java/com/redhat/thermostat/backend/system/JvmStatHostListener.java	Mon Jan 07 15:48:38 2013 -0500
+++ b/system-backend/src/main/java/com/redhat/thermostat/backend/system/JvmStatHostListener.java	Mon Jan 07 15:50:03 2013 -0500
@@ -61,7 +61,6 @@
 import com.redhat.thermostat.common.dao.VmClassStatDAO;
 import com.redhat.thermostat.common.dao.VmGcStatDAO;
 import com.redhat.thermostat.common.dao.VmInfoDAO;
-import com.redhat.thermostat.common.dao.VmMemoryStatDAO;
 import com.redhat.thermostat.common.utils.LoggingUtils;
 import com.redhat.thermostat.storage.model.VmInfo;
 import com.redhat.thermostat.utils.ProcDataSource;
@@ -73,7 +72,6 @@
     private boolean attachNew;
 
     private final VmInfoDAO vmInfoDAO;
-    private final VmMemoryStatDAO vmMemoryStatDAO;
     private final VmClassStatDAO vmClassStatDAO;
     private final VmGcStatDAO vmGcStatDAO;
 
@@ -82,10 +80,8 @@
     
     private Set<JvmStatusListener> statusListeners = new CopyOnWriteArraySet<JvmStatusListener>();
 
-    JvmStatHostListener(VmInfoDAO vmInfoDAO, VmMemoryStatDAO vmMemoryStatDAO, VmGcStatDAO vmGcStatDAO,
-            VmClassStatDAO vmClassStatDAO, boolean attachNew) {
+    JvmStatHostListener(VmInfoDAO vmInfoDAO, VmGcStatDAO vmGcStatDAO, VmClassStatDAO vmClassStatDAO, boolean attachNew) {
         this.vmInfoDAO = vmInfoDAO;
-        this.vmMemoryStatDAO = vmMemoryStatDAO;
         this.vmGcStatDAO = vmGcStatDAO;
         this.vmClassStatDAO = vmClassStatDAO;
         this.attachNew = attachNew;        
@@ -169,7 +165,7 @@
                     listeners = new CopyOnWriteArrayList<>();
                 }
                 
-                VmListener listener =  new JvmStatVmListener(vmMemoryStatDAO, vmGcStatDAO, vmId);
+                VmListener listener =  new JvmStatVmListener(vmGcStatDAO, vmId);
                 vm.addVmListener(listener);
                 listeners.add(listener);
                 
--- a/system-backend/src/main/java/com/redhat/thermostat/backend/system/JvmStatVmListener.java	Mon Jan 07 15:48:38 2013 -0500
+++ b/system-backend/src/main/java/com/redhat/thermostat/backend/system/JvmStatVmListener.java	Mon Jan 07 15:50:03 2013 -0500
@@ -46,12 +46,8 @@
 import sun.jvmstat.monitor.event.VmListener;
 
 import com.redhat.thermostat.common.dao.VmGcStatDAO;
-import com.redhat.thermostat.common.dao.VmMemoryStatDAO;
 import com.redhat.thermostat.common.utils.LoggingUtils;
 import com.redhat.thermostat.storage.model.VmGcStat;
-import com.redhat.thermostat.storage.model.VmMemoryStat;
-import com.redhat.thermostat.storage.model.VmMemoryStat.Generation;
-import com.redhat.thermostat.storage.model.VmMemoryStat.Space;
 
 public class JvmStatVmListener implements VmListener {
 
@@ -59,11 +55,9 @@
 
     private final int vmId;
     private final VmGcStatDAO gcDAO;
-    private final VmMemoryStatDAO memDAO;
 
-    public JvmStatVmListener(VmMemoryStatDAO vmMemoryStatDao, VmGcStatDAO vmGcStatDao, int vmId) {
+    public JvmStatVmListener(VmGcStatDAO vmGcStatDao, int vmId) {
         gcDAO = vmGcStatDao;
-        memDAO = vmMemoryStatDao;
         this.vmId = vmId;
     }
 
@@ -83,7 +77,6 @@
         if (vm == null) {
             throw new NullPointerException();
         }
-        recordMemoryStat(vm);
         recordGcStat(vm);
     }
 
@@ -105,38 +98,4 @@
 
     }
 
-    private void recordMemoryStat(MonitoredVm vm) {
-        try {
-            long timestamp = System.currentTimeMillis();
-            JvmStatDataExtractor extractor = new JvmStatDataExtractor(vm);
-            int maxGenerations = (int) extractor.getTotalGcGenerations();
-            Generation[] generations = new Generation[maxGenerations];
-            for (int generation = 0; generation < maxGenerations; generation++) {
-                Generation g = new Generation();
-                g.setName(extractor.getGenerationName(generation));
-                g.setCapacity(extractor.getGenerationCapacity(generation));
-                g.setMaxCapacity(extractor.getGenerationMaxCapacity(generation));
-                g.setCollector(extractor.getGenerationCollector(generation));
-                generations[generation] = g;
-                int maxSpaces = (int) extractor.getTotalSpaces(generation);
-                Space[] spaces = new Space[maxSpaces];
-                for (int space = 0; space < maxSpaces; space++) {
-                    Space s = new Space();
-                    s.setIndex((int) space);
-                    s.setName(extractor.getSpaceName(generation, space));
-                    s.setCapacity(extractor.getSpaceCapacity(generation, space));
-                    s.setMaxCapacity(extractor.getSpaceMaxCapacity(generation, space));
-                    s.setUsed(extractor.getSpaceUsed(generation, space));
-                    spaces[space] = s;
-                }
-                g.setSpaces(spaces);
-            }
-            VmMemoryStat stat = new VmMemoryStat(timestamp, vmId, generations);
-            memDAO.putVmMemoryStat(stat);
-        } catch (MonitorException e) {
-            logger.log(Level.WARNING, "error gathering memory info for vm " + vmId, e);
-        }
-    }
-
-
 }
--- a/system-backend/src/main/java/com/redhat/thermostat/backend/system/SystemBackend.java	Mon Jan 07 15:48:38 2013 -0500
+++ b/system-backend/src/main/java/com/redhat/thermostat/backend/system/SystemBackend.java	Mon Jan 07 15:50:03 2013 -0500
@@ -93,7 +93,7 @@
     protected void setDAOFactoryAction() {
         hostInfos = df.getHostInfoDAO();
         networkInterfaces = df.getNetworkInterfaceInfoDAO();
-        hostListener = new JvmStatHostListener(df.getVmInfoDAO(), df.getVmMemoryStatDAO(), df.getVmGcStatDAO(), df.getVmClassStatsDAO(), getObserveNewJvm());
+        hostListener = new JvmStatHostListener(df.getVmInfoDAO(), df.getVmGcStatDAO(), df.getVmClassStatsDAO(), getObserveNewJvm());
     }
 
     @Override
--- a/system-backend/src/test/java/com/redhat/thermostat/backend/system/JvmStatDataExtractorTest.java	Mon Jan 07 15:48:38 2013 -0500
+++ b/system-backend/src/test/java/com/redhat/thermostat/backend/system/JvmStatDataExtractorTest.java	Mon Jan 07 15:50:03 2013 -0500
@@ -226,140 +226,6 @@
     }
 
     @Test
-    public void testTotalGcGenerations() throws MonitorException {
-        final String MONITOR_NAME = "sun.gc.policy.generations";
-        final Long GC_GENERATIONS = 99l;
-        MonitoredVm vm = buildLongMonitoredVm(MONITOR_NAME, GC_GENERATIONS);
-
-        JvmStatDataExtractor extractor = new JvmStatDataExtractor(vm);
-        Long returned = extractor.getTotalGcGenerations();
-
-        verify(vm).findByName(eq(MONITOR_NAME));
-        assertEquals(GC_GENERATIONS, returned);
-    }
-
-    @Test
-    public void testGenerationName() throws MonitorException {
-        final String MONITOR_NAME = "sun.gc.generation.0.name";
-        final String GENERATION_NAME = "Youth";
-        MonitoredVm vm = buildStringMonitoredVm(MONITOR_NAME, GENERATION_NAME);
-
-        JvmStatDataExtractor extractor = new JvmStatDataExtractor(vm);
-        String returned = extractor.getGenerationName(0);
-
-        verify(vm).findByName(eq(MONITOR_NAME));
-        assertEquals(GENERATION_NAME, returned);
-    }
-
-    @Test
-    public void testGenerationCapacity() throws MonitorException {
-        final String MONITOR_NAME = "sun.gc.generation.0.capacity";
-        final Long GENERATION_CAPACITY = 99l;
-        MonitoredVm vm = buildLongMonitoredVm(MONITOR_NAME, GENERATION_CAPACITY);
-
-        JvmStatDataExtractor extractor = new JvmStatDataExtractor(vm);
-        Long returned = extractor.getGenerationCapacity(0);
-
-        verify(vm).findByName(eq(MONITOR_NAME));
-        assertEquals(GENERATION_CAPACITY, returned);
-    }
-
-    @Test
-    public void testGenerationMaxCapacity() throws MonitorException {
-        final String MONITOR_NAME = "sun.gc.generation.0.maxCapacity";
-        final Long GENERATION_MAX_CAPACITY = 99l;
-        MonitoredVm vm = buildLongMonitoredVm(MONITOR_NAME, GENERATION_MAX_CAPACITY);
-
-
-        JvmStatDataExtractor extractor = new JvmStatDataExtractor(vm);
-        Long returned = extractor.getGenerationMaxCapacity(0);
-
-        verify(vm).findByName(eq(MONITOR_NAME));
-        assertEquals(GENERATION_MAX_CAPACITY, returned);
-    }
-
-    @Test
-    public void testGenerationCollector() throws MonitorException {
-        final String MONITOR_NAME = "sun.gc.collector.0.name";
-        final String GENERATION_COLLECTOR = "generation collector";
-        MonitoredVm vm = buildStringMonitoredVm(MONITOR_NAME, GENERATION_COLLECTOR);
-
-        JvmStatDataExtractor extractor = new JvmStatDataExtractor(vm);
-        String returned = extractor.getGenerationCollector(0);
-
-        verify(vm).findByName(eq(MONITOR_NAME));
-        assertEquals(GENERATION_COLLECTOR, returned);
-    }
-
-    @Test
-    public void testTotalSpaces() throws MonitorException {
-        final Long TOTAL_SPACES = 99l;
-        final LongMonitor monitor = mock(LongMonitor.class);
-        when(monitor.getValue()).thenReturn(TOTAL_SPACES);
-        MonitoredVm vm = mock(MonitoredVm.class);
-        when(vm.findByName("sun.gc.generation.0.spaces")).thenReturn(monitor);
-
-        JvmStatDataExtractor extractor = new JvmStatDataExtractor(vm);
-        Long returned = extractor.getTotalSpaces(0);
-
-        verify(vm).findByName(eq("sun.gc.generation.0.spaces"));
-        assertEquals(TOTAL_SPACES, returned);
-    }
-
-
-    @Test
-    public void testSpaceName() throws MonitorException {
-        final String MONITOR_NAME = "sun.gc.generation.0.space.0.name";
-        final String SPACE_NAME = "Hilbert";
-        MonitoredVm vm = buildStringMonitoredVm(MONITOR_NAME, SPACE_NAME);
-
-        JvmStatDataExtractor extractor = new JvmStatDataExtractor(vm);
-        String returned = extractor.getSpaceName(0,0);
-
-        verify(vm).findByName(eq(MONITOR_NAME));
-        assertEquals(SPACE_NAME, returned);
-    }
-
-    @Test
-    public void testSpaceCapacity() throws MonitorException {
-        final String MONITOR_NAME = "sun.gc.generation.0.space.0.capacity";
-        final Long SPACE_CAPACITY = 99l;
-        MonitoredVm vm = buildLongMonitoredVm(MONITOR_NAME, SPACE_CAPACITY);
-
-        JvmStatDataExtractor extractor = new JvmStatDataExtractor(vm);
-        Long returned = extractor.getSpaceCapacity(0,0);
-
-        verify(vm).findByName(eq(MONITOR_NAME));
-        assertEquals(SPACE_CAPACITY, returned);
-    }
-
-    @Test
-    public void testSpaceMaxCapacity() throws MonitorException {
-        final String MONITOR_NAME = "sun.gc.generation.0.space.0.maxCapacity";
-        final Long SPACE_MAX_CAPACITY = 99l;
-        MonitoredVm vm = buildLongMonitoredVm(MONITOR_NAME, SPACE_MAX_CAPACITY);
-
-        JvmStatDataExtractor extractor = new JvmStatDataExtractor(vm);
-        Long returned = extractor.getSpaceMaxCapacity(0,0);
-
-        verify(vm).findByName(eq(MONITOR_NAME));
-        assertEquals(SPACE_MAX_CAPACITY, returned);
-    }
-
-    @Test
-    public void testSpaceUsed() throws MonitorException {
-        final String MONITOR_NAME = "sun.gc.generation.0.space.0.used";
-        final Long SPACE_USED = 99l;
-        MonitoredVm vm = buildLongMonitoredVm(MONITOR_NAME, SPACE_USED);
-
-        JvmStatDataExtractor extractor = new JvmStatDataExtractor(vm);
-        Long returned = extractor.getSpaceUsed(0,0);
-
-        verify(vm).findByName(eq(MONITOR_NAME));
-        assertEquals(SPACE_USED, returned);
-    }
-
-    @Test
     public void testLoadedClasses() throws MonitorException {
         final String MONITOR_NAME = "java.cls.loadedClasses";
         final Long LOADED_CLASSES = 99l;
--- a/system-backend/src/test/java/com/redhat/thermostat/backend/system/JvmStatHostListenerTest.java	Mon Jan 07 15:48:38 2013 -0500
+++ b/system-backend/src/test/java/com/redhat/thermostat/backend/system/JvmStatHostListenerTest.java	Mon Jan 07 15:50:03 2013 -0500
@@ -81,7 +81,7 @@
         VmClassStatDAO vmClassDAO = mock(VmClassStatDAO.class);
         VmInfoDAO vmInfoDAO = mock(VmInfoDAO.class);
 
-        JvmStatHostListener l = new JvmStatHostListener(vmInfoDAO, null, null, vmClassDAO ,true);
+        JvmStatHostListener l = new JvmStatHostListener(vmInfoDAO, null, vmClassDAO ,true);
         SystemBackend backend = mock(SystemBackend.class);
         when(backend.getObserveNewJvm()).thenReturn(true);
 
--- a/vm-gc/client-core/pom.xml	Mon Jan 07 15:48:38 2013 -0500
+++ b/vm-gc/client-core/pom.xml	Mon Jan 07 15:50:03 2013 -0500
@@ -62,6 +62,11 @@
     </dependency>
     <dependency>
       <groupId>com.redhat.thermostat</groupId>
+      <artifactId>thermostat-vm-memory-common</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>com.redhat.thermostat</groupId>
       <artifactId>thermostat-client-core</artifactId>
       <version>${project.version}</version>
     </dependency>
--- a/vm-gc/client-core/src/main/java/com/redhat/thermostat/vm/gc/client/core/VmGcService.java	Mon Jan 07 15:48:38 2013 -0500
+++ b/vm-gc/client-core/src/main/java/com/redhat/thermostat/vm/gc/client/core/VmGcService.java	Mon Jan 07 15:50:03 2013 -0500
@@ -42,10 +42,10 @@
 import com.redhat.thermostat.client.core.controllers.InformationServiceController;
 import com.redhat.thermostat.common.ApplicationService;
 import com.redhat.thermostat.common.dao.VmGcStatDAO;
-import com.redhat.thermostat.common.dao.VmMemoryStatDAO;
 import com.redhat.thermostat.common.dao.VmRef;
 import com.redhat.thermostat.common.utils.OSGIUtils;
 import com.redhat.thermostat.vm.gc.client.core.internal.VmGcController;
+import com.redhat.thermostat.vm.memory.common.VmMemoryStatDAO;
 
 public class VmGcService implements InformationService<VmRef> {
     
--- a/vm-gc/client-core/src/main/java/com/redhat/thermostat/vm/gc/client/core/internal/Activator.java	Mon Jan 07 15:48:38 2013 -0500
+++ b/vm-gc/client-core/src/main/java/com/redhat/thermostat/vm/gc/client/core/internal/Activator.java	Mon Jan 07 15:50:03 2013 -0500
@@ -51,9 +51,9 @@
 import com.redhat.thermostat.common.MultipleServiceTracker;
 import com.redhat.thermostat.common.MultipleServiceTracker.Action;
 import com.redhat.thermostat.common.dao.VmGcStatDAO;
-import com.redhat.thermostat.common.dao.VmMemoryStatDAO;
 import com.redhat.thermostat.common.dao.VmRef;
 import com.redhat.thermostat.vm.gc.client.core.VmGcService;
+import com.redhat.thermostat.vm.memory.common.VmMemoryStatDAO;
 
 public class Activator implements BundleActivator {
     
--- a/vm-gc/client-core/src/main/java/com/redhat/thermostat/vm/gc/client/core/internal/VmGcController.java	Mon Jan 07 15:48:38 2013 -0500
+++ b/vm-gc/client-core/src/main/java/com/redhat/thermostat/vm/gc/client/core/internal/VmGcController.java	Mon Jan 07 15:50:03 2013 -0500
@@ -56,7 +56,6 @@
 import com.redhat.thermostat.common.Timer;
 import com.redhat.thermostat.common.Timer.SchedulingType;
 import com.redhat.thermostat.common.dao.VmGcStatDAO;
-import com.redhat.thermostat.common.dao.VmMemoryStatDAO;
 import com.redhat.thermostat.common.dao.VmRef;
 import com.redhat.thermostat.common.locale.Translate;
 import com.redhat.thermostat.storage.model.IntervalTimeData;
@@ -67,6 +66,7 @@
 import com.redhat.thermostat.vm.gc.client.core.VmGcView;
 import com.redhat.thermostat.vm.gc.client.core.VmGcViewProvider;
 import com.redhat.thermostat.vm.gc.client.locale.LocaleResources;
+import com.redhat.thermostat.vm.memory.common.VmMemoryStatDAO;
 
 public class VmGcController implements InformationServiceController<VmRef> {
 
--- a/vm-gc/client-core/src/test/java/com/redhat/thermostat/vm/gc/client/core/internal/ActivatorTest.java	Mon Jan 07 15:48:38 2013 -0500
+++ b/vm-gc/client-core/src/test/java/com/redhat/thermostat/vm/gc/client/core/internal/ActivatorTest.java	Mon Jan 07 15:50:03 2013 -0500
@@ -46,9 +46,9 @@
 import com.redhat.thermostat.client.core.InformationService;
 import com.redhat.thermostat.common.ApplicationService;
 import com.redhat.thermostat.common.dao.VmGcStatDAO;
-import com.redhat.thermostat.common.dao.VmMemoryStatDAO;
 import com.redhat.thermostat.test.StubBundleContext;
 import com.redhat.thermostat.vm.gc.client.core.VmGcService;
+import com.redhat.thermostat.vm.memory.common.VmMemoryStatDAO;
 
 public class ActivatorTest {
     
--- a/vm-gc/client-core/src/test/java/com/redhat/thermostat/vm/gc/client/core/internal/VmGcControllerTest.java	Mon Jan 07 15:48:38 2013 -0500
+++ b/vm-gc/client-core/src/test/java/com/redhat/thermostat/vm/gc/client/core/internal/VmGcControllerTest.java	Mon Jan 07 15:50:03 2013 -0500
@@ -59,14 +59,13 @@
 import com.redhat.thermostat.common.Timer.SchedulingType;
 import com.redhat.thermostat.common.TimerFactory;
 import com.redhat.thermostat.common.dao.VmGcStatDAO;
-import com.redhat.thermostat.common.dao.VmMemoryStatDAO;
 import com.redhat.thermostat.common.dao.VmRef;
 import com.redhat.thermostat.storage.model.VmGcStat;
 import com.redhat.thermostat.storage.model.VmMemoryStat;
 import com.redhat.thermostat.storage.model.VmMemoryStat.Generation;
 import com.redhat.thermostat.vm.gc.client.core.VmGcView;
 import com.redhat.thermostat.vm.gc.client.core.VmGcViewProvider;
-import com.redhat.thermostat.vm.gc.client.core.internal.VmGcController;
+import com.redhat.thermostat.vm.memory.common.VmMemoryStatDAO;
 
 public class VmGcControllerTest {
 
--- a/vm-heap-analysis/client-core/pom.xml	Mon Jan 07 15:48:38 2013 -0500
+++ b/vm-heap-analysis/client-core/pom.xml	Mon Jan 07 15:50:03 2013 -0500
@@ -125,7 +125,11 @@
       <version>${project.version}</version>
       <scope>test</scope>
     </dependency>
-
+    <dependency>
+      <groupId>com.redhat.thermostat</groupId>
+      <artifactId>thermostat-vm-memory-common</artifactId>
+      <version>${project.version}</version>
+    </dependency>
     <dependency>
       <groupId>com.redhat.thermostat</groupId>
       <artifactId>thermostat-vm-heap-analysis-common</artifactId>
--- a/vm-heap-analysis/client-core/src/main/java/com/redhat/thermostat/vm/heap/analysis/client/core/HeapDumperService.java	Mon Jan 07 15:48:38 2013 -0500
+++ b/vm-heap-analysis/client-core/src/main/java/com/redhat/thermostat/vm/heap/analysis/client/core/HeapDumperService.java	Mon Jan 07 15:50:03 2013 -0500
@@ -41,11 +41,11 @@
 import com.redhat.thermostat.client.core.controllers.InformationServiceController;
 import com.redhat.thermostat.client.core.NameMatchingRefFilter;
 import com.redhat.thermostat.common.ApplicationService;
-import com.redhat.thermostat.common.dao.VmMemoryStatDAO;
 import com.redhat.thermostat.common.dao.VmRef;
 import com.redhat.thermostat.common.utils.OSGIUtils;
 import com.redhat.thermostat.vm.heap.analysis.client.core.internal.HeapDumpController;
 import com.redhat.thermostat.vm.heap.analysis.common.HeapDAO;
+import com.redhat.thermostat.vm.memory.common.VmMemoryStatDAO;
 
 public class HeapDumperService implements InformationService<VmRef> {
     
--- a/vm-heap-analysis/client-core/src/main/java/com/redhat/thermostat/vm/heap/analysis/client/core/internal/Activator.java	Mon Jan 07 15:48:38 2013 -0500
+++ b/vm-heap-analysis/client-core/src/main/java/com/redhat/thermostat/vm/heap/analysis/client/core/internal/Activator.java	Mon Jan 07 15:50:03 2013 -0500
@@ -50,10 +50,10 @@
 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.VmMemoryStatDAO;
 import com.redhat.thermostat.common.dao.VmRef;
 import com.redhat.thermostat.vm.heap.analysis.client.core.HeapDumperService;
 import com.redhat.thermostat.vm.heap.analysis.common.HeapDAO;
+import com.redhat.thermostat.vm.memory.common.VmMemoryStatDAO;
 
 public class Activator implements BundleActivator {
 
--- a/vm-heap-analysis/client-core/src/main/java/com/redhat/thermostat/vm/heap/analysis/client/core/internal/HeapDumpController.java	Mon Jan 07 15:48:38 2013 -0500
+++ b/vm-heap-analysis/client-core/src/main/java/com/redhat/thermostat/vm/heap/analysis/client/core/internal/HeapDumpController.java	Mon Jan 07 15:50:03 2013 -0500
@@ -54,7 +54,6 @@
 import com.redhat.thermostat.common.Timer;
 import com.redhat.thermostat.common.Timer.SchedulingType;
 import com.redhat.thermostat.common.cli.CommandException;
-import com.redhat.thermostat.common.dao.VmMemoryStatDAO;
 import com.redhat.thermostat.common.dao.VmRef;
 import com.redhat.thermostat.common.locale.Translate;
 import com.redhat.thermostat.storage.model.HeapInfo;
@@ -72,6 +71,7 @@
 import com.redhat.thermostat.vm.heap.analysis.client.locale.LocaleResources;
 import com.redhat.thermostat.vm.heap.analysis.common.HeapDAO;
 import com.redhat.thermostat.vm.heap.analysis.common.HeapDump;
+import com.redhat.thermostat.vm.memory.common.VmMemoryStatDAO;
 
 public class HeapDumpController implements InformationServiceController<VmRef> {
 
--- a/vm-heap-analysis/client-core/src/test/java/com/redhat/thermostat/vm/heap/analysis/client/core/internal/ActivatorTest.java	Mon Jan 07 15:48:38 2013 -0500
+++ b/vm-heap-analysis/client-core/src/test/java/com/redhat/thermostat/vm/heap/analysis/client/core/internal/ActivatorTest.java	Mon Jan 07 15:50:03 2013 -0500
@@ -45,10 +45,10 @@
 
 import com.redhat.thermostat.client.core.InformationService;
 import com.redhat.thermostat.common.ApplicationService;
-import com.redhat.thermostat.common.dao.VmMemoryStatDAO;
 import com.redhat.thermostat.test.StubBundleContext;
 import com.redhat.thermostat.vm.heap.analysis.client.core.HeapDumperService;
 import com.redhat.thermostat.vm.heap.analysis.common.HeapDAO;
+import com.redhat.thermostat.vm.memory.common.VmMemoryStatDAO;
 
 public class ActivatorTest {
     
--- a/vm-heap-analysis/client-core/src/test/java/com/redhat/thermostat/vm/heap/analysis/client/core/internal/HeapDumpControllerTest.java	Mon Jan 07 15:48:38 2013 -0500
+++ b/vm-heap-analysis/client-core/src/test/java/com/redhat/thermostat/vm/heap/analysis/client/core/internal/HeapDumpControllerTest.java	Mon Jan 07 15:50:03 2013 -0500
@@ -71,7 +71,6 @@
 import com.redhat.thermostat.common.TimerFactory;
 import com.redhat.thermostat.common.cli.CommandException;
 import com.redhat.thermostat.common.dao.HostRef;
-import com.redhat.thermostat.common.dao.VmMemoryStatDAO;
 import com.redhat.thermostat.common.dao.VmRef;
 import com.redhat.thermostat.storage.model.HeapInfo;
 import com.redhat.thermostat.storage.model.VmMemoryStat;
@@ -90,6 +89,7 @@
 import com.redhat.thermostat.vm.heap.analysis.client.core.ObjectRootsViewProvider;
 import com.redhat.thermostat.vm.heap.analysis.common.HeapDAO;
 import com.redhat.thermostat.vm.heap.analysis.common.HeapDump;
+import com.redhat.thermostat.vm.memory.common.VmMemoryStatDAO;
 
 public class HeapDumpControllerTest {
     private static int TIMEOUT_MS = 1000;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-memory/agent/pom.xml	Mon Jan 07 15:50:03 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-memory</artifactId>
+    <groupId>com.redhat.thermostat</groupId>
+    <version>0.5.0-SNAPSHOT</version>
+  </parent>
+  <artifactId>thermostat-vm-memory-agent</artifactId>
+  <packaging>bundle</packaging>
+  <name>Thermostat VM Memory 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.memory.agent</Bundle-SymbolicName>
+            <Bundle-Activator>com.redhat.thermostat.vm.memory.agent.internal.Activator</Bundle-Activator>
+            <Export-Package>
+              com.redhat.thermostat.vm.memory.agent
+            </Export-Package>
+            <Private-Package>
+              com.redhat.thermostat.vm.memory.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-memory-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-memory/agent/src/main/java/com/redhat/thermostat/vm/memory/agent/internal/Activator.java	Mon Jan 07 15:50:03 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.memory.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.memory.common.VmMemoryStatDAO;
+
+public class Activator implements BundleActivator {
+    
+    private MultipleServiceTracker tracker;
+    private VmMemoryBackend backend;
+    private ServiceRegistration reg;
+    
+    @Override
+    public void start(final BundleContext context) throws Exception {
+        Class<?>[] deps = new Class<?>[] {
+                BackendService.class,
+                VmMemoryStatDAO.class
+        };
+        tracker = new MultipleServiceTracker(context, deps, new Action() {
+            
+            @Override
+            public void dependenciesAvailable(Map<String, Object> services) {
+                VmMemoryStatDAO vmMemoryStatDao = (VmMemoryStatDAO) services.get(VmMemoryStatDAO.class.getName());
+                Version version = new Version(context.getBundle());
+                backend = new VmMemoryBackend(vmMemoryStatDao, 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.
+     */
+    VmMemoryBackend getBackend() {
+        return backend;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-memory/agent/src/main/java/com/redhat/thermostat/vm/memory/agent/internal/VmMemoryBackend.java	Mon Jan 07 15:50:03 2013 -0500
@@ -0,0 +1,141 @@
+/*
+ * Copyright 2013 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.vm.memory.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.memory.common.VmMemoryStatDAO;
+
+public class VmMemoryBackend extends Backend {
+
+    private static final Logger LOGGER = LoggingUtils.getLogger(VmMemoryBackend.class);
+
+    private VmMemoryStatDAO vmMemoryStats;
+    private HostIdentifier hostId;
+    private MonitoredHost host;
+    private VmMemoryHostListener hostListener;
+    private boolean started;
+
+    public VmMemoryBackend(VmMemoryStatDAO vmMemoryStatDAO, Version version) {
+        super(new BackendID("VM Memory Backend", VmMemoryBackend.class.getName()));
+        this.vmMemoryStats = vmMemoryStatDAO;
+        
+        setConfigurationValue(BackendsProperties.VENDOR.name(), "Red Hat, Inc.");
+        setConfigurationValue(BackendsProperties.DESCRIPTION.name(), "Gathers memory statistics about a JVM");
+        setConfigurationValue(BackendsProperties.VERSION.name(), version.getVersionNumber());
+        
+        try {
+            hostId = new HostIdentifier((String) null);
+            host = MonitoredHost.getMonitoredHost(hostId);
+            hostListener = new VmMemoryHostListener(vmMemoryStats, 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 + 40;
+    }
+
+    /*
+     * For testing purposes only.
+     */
+    void setHost(MonitoredHost host) {
+        this.host = host;
+    }
+    
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-memory/agent/src/main/java/com/redhat/thermostat/vm/memory/agent/internal/VmMemoryDataExtractor.java	Mon Jan 07 15:50:03 2013 -0500
@@ -0,0 +1,117 @@
+/*
+ * 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.memory.agent.internal;
+
+import sun.jvmstat.monitor.Monitor;
+import sun.jvmstat.monitor.MonitorException;
+import sun.jvmstat.monitor.MonitoredVm;
+
+import com.redhat.thermostat.storage.model.VmMemoryStat.Generation;
+
+/**
+ * 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 VmMemoryDataExtractor {
+
+    /*
+     * 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 VmMemoryDataExtractor(MonitoredVm vm) {
+        this.vm = vm;
+    }
+    
+    public long getTotalGcGenerations() throws MonitorException {
+        return (Long) vm.findByName("sun.gc.policy.generations").getValue();
+    }
+
+    public String getGenerationName(long generation) throws MonitorException {
+        return (String) vm.findByName("sun.gc.generation." + generation + ".name").getValue();
+    }
+
+    public long getGenerationCapacity(long generation) throws MonitorException {
+        return (Long) vm.findByName("sun.gc.generation." + generation + ".capacity").getValue();
+    }
+
+    public long getGenerationMaxCapacity(long generation) throws MonitorException {
+        return (Long) vm.findByName("sun.gc.generation." + generation + ".maxCapacity").getValue();
+    }
+
+    public String getGenerationCollector(long generation) throws MonitorException {
+        // this is just re-implementing getCollectorName()
+        // TODO check generation number and collector number are always associated
+        Monitor m = vm.findByName("sun.gc.collector." + generation + ".name");
+        if (m == null) {
+            return Generation.COLLECTOR_NONE;
+        }
+        return (String) m.getValue();
+    }
+
+    public long getTotalSpaces(long generation) throws MonitorException {
+        return (Long) vm.findByName("sun.gc.generation." + generation + ".spaces").getValue();
+    }
+
+    public String getSpaceName(long generation, long space) throws MonitorException {
+        return (String) vm.findByName("sun.gc.generation." + generation + ".space." + space + ".name").getValue();
+    }
+
+    public long getSpaceCapacity(long generation, long space) throws MonitorException {
+        return (Long) vm.findByName("sun.gc.generation." + generation + ".space." + space + ".capacity").getValue();
+    }
+
+    public long getSpaceMaxCapacity(long generation, long space) throws MonitorException {
+        return (Long) vm.findByName("sun.gc.generation." + generation + ".space." + space + ".maxCapacity").getValue();
+    }
+
+    public long getSpaceUsed(long generation, long space) throws MonitorException {
+        return (Long) vm.findByName("sun.gc.generation." + generation + ".space." + space + ".used").getValue();
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-memory/agent/src/main/java/com/redhat/thermostat/vm/memory/agent/internal/VmMemoryHostListener.java	Mon Jan 07 15:50:03 2013 -0500
@@ -0,0 +1,172 @@
+/*
+ * 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.memory.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.memory.common.VmMemoryStatDAO;
+
+public class VmMemoryHostListener implements HostListener {
+
+    private static final Logger logger = LoggingUtils.getLogger(VmMemoryHostListener.class);
+
+    private boolean attachNew;
+
+    private final VmMemoryStatDAO vmMemoryStatDAO;
+
+    private Map<Integer, MonitoredVm> monitoredVms  = new HashMap<>();
+    private Map<MonitoredVm, VmMemoryVmListener> registeredListeners  = new ConcurrentHashMap<>();
+    
+    VmMemoryHostListener(VmMemoryStatDAO vmMemoryStatDAO, boolean attachNew) {
+        this.vmMemoryStatDAO = vmMemoryStatDAO;
+        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) {
+                VmMemoryVmListener listener = new VmMemoryVmListener(vmMemoryStatDAO, 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);
+            VmMemoryVmListener 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, VmMemoryVmListener> getRegisteredListeners() {
+        return registeredListeners;
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-memory/agent/src/main/java/com/redhat/thermostat/vm/memory/agent/internal/VmMemoryVmListener.java	Mon Jan 07 15:50:03 2013 -0500
@@ -0,0 +1,120 @@
+/*
+ * 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.memory.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.VmMemoryStat;
+import com.redhat.thermostat.storage.model.VmMemoryStat.Generation;
+import com.redhat.thermostat.storage.model.VmMemoryStat.Space;
+import com.redhat.thermostat.vm.memory.common.VmMemoryStatDAO;
+
+public class VmMemoryVmListener implements VmListener {
+
+    private static final Logger logger = LoggingUtils.getLogger(VmMemoryVmListener.class);
+
+    private final int vmId;
+    private final VmMemoryStatDAO memDAO;
+
+    public VmMemoryVmListener(VmMemoryStatDAO vmMemoryStatDao, int vmId) {
+        memDAO = vmMemoryStatDao;
+        this.vmId = vmId;
+    }
+
+    @Override
+    public void disconnected(VmEvent event) {
+        /* nothing to do here */
+    }
+
+    @Override
+    public void monitorStatusChanged(MonitorStatusChangeEvent event) {
+        /* nothing to do here */
+    }
+
+    @Override
+    public void monitorsUpdated(VmEvent event) {
+        MonitoredVm vm = event.getMonitoredVm();
+        if (vm == null) {
+            throw new NullPointerException();
+        }
+        
+        VmMemoryDataExtractor extractor = new VmMemoryDataExtractor(vm);
+        recordMemoryStat(vm, extractor);
+    }
+
+    void recordMemoryStat(MonitoredVm vm, VmMemoryDataExtractor extractor) {
+        try {
+            long timestamp = System.currentTimeMillis();
+            int maxGenerations = (int) extractor.getTotalGcGenerations();
+            Generation[] generations = new Generation[maxGenerations];
+            for (int generation = 0; generation < maxGenerations; generation++) {
+                Generation g = new Generation();
+                g.setName(extractor.getGenerationName(generation));
+                g.setCapacity(extractor.getGenerationCapacity(generation));
+                g.setMaxCapacity(extractor.getGenerationMaxCapacity(generation));
+                g.setCollector(extractor.getGenerationCollector(generation));
+                generations[generation] = g;
+                int maxSpaces = (int) extractor.getTotalSpaces(generation);
+                Space[] spaces = new Space[maxSpaces];
+                for (int space = 0; space < maxSpaces; space++) {
+                    Space s = new Space();
+                    s.setIndex((int) space);
+                    s.setName(extractor.getSpaceName(generation, space));
+                    s.setCapacity(extractor.getSpaceCapacity(generation, space));
+                    s.setMaxCapacity(extractor.getSpaceMaxCapacity(generation, space));
+                    s.setUsed(extractor.getSpaceUsed(generation, space));
+                    spaces[space] = s;
+                }
+                g.setSpaces(spaces);
+            }
+            VmMemoryStat stat = new VmMemoryStat(timestamp, vmId, generations);
+            memDAO.putVmMemoryStat(stat);
+        } catch (MonitorException e) {
+            logger.log(Level.WARNING, "error gathering memory info for vm " + vmId, e);
+        }
+    }
+
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-memory/agent/src/test/java/com/redhat/thermostat/vm/memory/agent/internal/ActivatorTest.java	Mon Jan 07 15:50:03 2013 -0500
@@ -0,0 +1,104 @@
+/*
+ * Copyright 2013 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */ 
+
+package com.redhat.thermostat.vm.memory.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.memory.common.VmMemoryStatDAO;
+
+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);
+        VmMemoryStatDAO vmMemoryStatDAO = mock(VmMemoryStatDAO.class);
+
+        context.registerService(BackendService.class, service, null);
+        context.registerService(VmMemoryStatDAO.class, vmMemoryStatDAO, null);
+
+        Activator activator = new Activator();
+
+        activator.start(context);
+
+        assertTrue(context.isServiceRegistered(Backend.class.getName(), VmMemoryBackend.class));
+        VmMemoryBackend 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-memory/agent/src/test/java/com/redhat/thermostat/vm/memory/agent/internal/VmMemoryBackendTest.java	Mon Jan 07 15:50:03 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.memory.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.memory.common.VmMemoryStatDAO;
+
+public class VmMemoryBackendTest {
+    
+    private VmMemoryBackend backend;
+    private MonitoredHost host;
+
+    @Before
+    public void setup() throws MonitorException, URISyntaxException {
+        VmMemoryStatDAO vmMemoryStatDao = mock(VmMemoryStatDAO.class);
+        
+        Version version = mock(Version.class);
+        when(version.getVersionNumber()).thenReturn("0.0.0");
+        backend = new VmMemoryBackend(vmMemoryStatDao, 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-memory/agent/src/test/java/com/redhat/thermostat/vm/memory/agent/internal/VmMemoryDataExtractorTest.java	Mon Jan 07 15:50:03 2013 -0500
@@ -0,0 +1,206 @@
+/*
+ * 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.memory.agent.internal;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import org.junit.Test;
+
+import sun.jvmstat.monitor.LongMonitor;
+import sun.jvmstat.monitor.MonitorException;
+import sun.jvmstat.monitor.MonitoredVm;
+import sun.jvmstat.monitor.StringMonitor;
+
+public class VmMemoryDataExtractorTest {
+
+    private MonitoredVm buildStringMonitoredVm(String monitorName, String monitorReturn) throws MonitorException {
+        final StringMonitor monitor = mock(StringMonitor.class);
+        when(monitor.stringValue()).thenReturn(monitorReturn);
+        when(monitor.getValue()).thenReturn(monitorReturn);
+        MonitoredVm vm = mock(MonitoredVm.class);
+        when(vm.findByName(monitorName)).thenReturn(monitor);
+        return vm;
+    }
+
+    private MonitoredVm buildLongMonitoredVm(String monitorName, Long monitorReturn) throws MonitorException {
+        final LongMonitor monitor = mock(LongMonitor.class);
+        when(monitor.longValue()).thenReturn(monitorReturn);
+        when(monitor.getValue()).thenReturn(monitorReturn);
+        MonitoredVm vm = mock(MonitoredVm.class);
+        when(vm.findByName(monitorName)).thenReturn(monitor);
+        return vm;
+    }
+
+    @Test
+    public void testTotalGcGenerations() throws MonitorException {
+        final String MONITOR_NAME = "sun.gc.policy.generations";
+        final Long GC_GENERATIONS = 99l;
+        MonitoredVm vm = buildLongMonitoredVm(MONITOR_NAME, GC_GENERATIONS);
+
+        VmMemoryDataExtractor extractor = new VmMemoryDataExtractor(vm);
+        Long returned = extractor.getTotalGcGenerations();
+
+        verify(vm).findByName(eq(MONITOR_NAME));
+        assertEquals(GC_GENERATIONS, returned);
+    }
+
+    @Test
+    public void testGenerationName() throws MonitorException {
+        final String MONITOR_NAME = "sun.gc.generation.0.name";
+        final String GENERATION_NAME = "Youth";
+        MonitoredVm vm = buildStringMonitoredVm(MONITOR_NAME, GENERATION_NAME);
+
+        VmMemoryDataExtractor extractor = new VmMemoryDataExtractor(vm);
+        String returned = extractor.getGenerationName(0);
+
+        verify(vm).findByName(eq(MONITOR_NAME));
+        assertEquals(GENERATION_NAME, returned);
+    }
+
+    @Test
+    public void testGenerationCapacity() throws MonitorException {
+        final String MONITOR_NAME = "sun.gc.generation.0.capacity";
+        final Long GENERATION_CAPACITY = 99l;
+        MonitoredVm vm = buildLongMonitoredVm(MONITOR_NAME, GENERATION_CAPACITY);
+
+        VmMemoryDataExtractor extractor = new VmMemoryDataExtractor(vm);
+        Long returned = extractor.getGenerationCapacity(0);
+
+        verify(vm).findByName(eq(MONITOR_NAME));
+        assertEquals(GENERATION_CAPACITY, returned);
+    }
+
+    @Test
+    public void testGenerationMaxCapacity() throws MonitorException {
+        final String MONITOR_NAME = "sun.gc.generation.0.maxCapacity";
+        final Long GENERATION_MAX_CAPACITY = 99l;
+        MonitoredVm vm = buildLongMonitoredVm(MONITOR_NAME, GENERATION_MAX_CAPACITY);
+
+
+        VmMemoryDataExtractor extractor = new VmMemoryDataExtractor(vm);
+        Long returned = extractor.getGenerationMaxCapacity(0);
+
+        verify(vm).findByName(eq(MONITOR_NAME));
+        assertEquals(GENERATION_MAX_CAPACITY, returned);
+    }
+
+    @Test
+    public void testGenerationCollector() throws MonitorException {
+        final String MONITOR_NAME = "sun.gc.collector.0.name";
+        final String GENERATION_COLLECTOR = "generation collector";
+        MonitoredVm vm = buildStringMonitoredVm(MONITOR_NAME, GENERATION_COLLECTOR);
+
+        VmMemoryDataExtractor extractor = new VmMemoryDataExtractor(vm);
+        String returned = extractor.getGenerationCollector(0);
+
+        verify(vm).findByName(eq(MONITOR_NAME));
+        assertEquals(GENERATION_COLLECTOR, returned);
+    }
+
+    @Test
+    public void testTotalSpaces() throws MonitorException {
+        final Long TOTAL_SPACES = 99l;
+        final LongMonitor monitor = mock(LongMonitor.class);
+        when(monitor.getValue()).thenReturn(TOTAL_SPACES);
+        MonitoredVm vm = mock(MonitoredVm.class);
+        when(vm.findByName("sun.gc.generation.0.spaces")).thenReturn(monitor);
+
+        VmMemoryDataExtractor extractor = new VmMemoryDataExtractor(vm);
+        Long returned = extractor.getTotalSpaces(0);
+
+        verify(vm).findByName(eq("sun.gc.generation.0.spaces"));
+        assertEquals(TOTAL_SPACES, returned);
+    }
+
+
+    @Test
+    public void testSpaceName() throws MonitorException {
+        final String MONITOR_NAME = "sun.gc.generation.0.space.0.name";
+        final String SPACE_NAME = "Hilbert";
+        MonitoredVm vm = buildStringMonitoredVm(MONITOR_NAME, SPACE_NAME);
+
+        VmMemoryDataExtractor extractor = new VmMemoryDataExtractor(vm);
+        String returned = extractor.getSpaceName(0,0);
+
+        verify(vm).findByName(eq(MONITOR_NAME));
+        assertEquals(SPACE_NAME, returned);
+    }
+
+    @Test
+    public void testSpaceCapacity() throws MonitorException {
+        final String MONITOR_NAME = "sun.gc.generation.0.space.0.capacity";
+        final Long SPACE_CAPACITY = 99l;
+        MonitoredVm vm = buildLongMonitoredVm(MONITOR_NAME, SPACE_CAPACITY);
+
+        VmMemoryDataExtractor extractor = new VmMemoryDataExtractor(vm);
+        Long returned = extractor.getSpaceCapacity(0,0);
+
+        verify(vm).findByName(eq(MONITOR_NAME));
+        assertEquals(SPACE_CAPACITY, returned);
+    }
+
+    @Test
+    public void testSpaceMaxCapacity() throws MonitorException {
+        final String MONITOR_NAME = "sun.gc.generation.0.space.0.maxCapacity";
+        final Long SPACE_MAX_CAPACITY = 99l;
+        MonitoredVm vm = buildLongMonitoredVm(MONITOR_NAME, SPACE_MAX_CAPACITY);
+
+        VmMemoryDataExtractor extractor = new VmMemoryDataExtractor(vm);
+        Long returned = extractor.getSpaceMaxCapacity(0,0);
+
+        verify(vm).findByName(eq(MONITOR_NAME));
+        assertEquals(SPACE_MAX_CAPACITY, returned);
+    }
+
+    @Test
+    public void testSpaceUsed() throws MonitorException {
+        final String MONITOR_NAME = "sun.gc.generation.0.space.0.used";
+        final Long SPACE_USED = 99l;
+        MonitoredVm vm = buildLongMonitoredVm(MONITOR_NAME, SPACE_USED);
+
+        VmMemoryDataExtractor extractor = new VmMemoryDataExtractor(vm);
+        Long returned = extractor.getSpaceUsed(0,0);
+
+        verify(vm).findByName(eq(MONITOR_NAME));
+        assertEquals(SPACE_USED, returned);
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-memory/agent/src/test/java/com/redhat/thermostat/vm/memory/agent/internal/VmMemoryHostListenerTest.java	Mon Jan 07 15:50:03 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.memory.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.memory.common.VmMemoryStatDAO;
+
+public class VmMemoryHostListenerTest {
+    
+    private VmMemoryHostListener hostListener;
+    private MonitoredHost host;
+    private MonitoredVm monitoredVm1;
+    private MonitoredVm monitoredVm2;
+
+    @Before
+    public void setup() throws MonitorException, URISyntaxException {
+        VmMemoryStatDAO vmMemoryStatDAO = mock(VmMemoryStatDAO.class);
+        hostListener = new VmMemoryHostListener(vmMemoryStatDAO, 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-memory/agent/src/test/java/com/redhat/thermostat/vm/memory/agent/internal/VmMemoryVmListenerTest.java	Mon Jan 07 15:50:03 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.memory.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.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+
+import sun.jvmstat.monitor.MonitorException;
+import sun.jvmstat.monitor.MonitoredVm;
+
+import com.redhat.thermostat.storage.model.VmMemoryStat;
+import com.redhat.thermostat.storage.model.VmMemoryStat.Generation;
+import com.redhat.thermostat.storage.model.VmMemoryStat.Space;
+import com.redhat.thermostat.vm.memory.common.VmMemoryStatDAO;
+
+public class VmMemoryVmListenerTest {
+    private static final String[] GEN_NAMES = new String[] { "Gen1", "Gen2" };
+    private static final Long[] GEN_CAPS = new Long[] { 500L, 1000L };
+    private static final Long[] GEN_MAX_CAPS = new Long[] { 5000L, 10000L };
+    private static final String[] GEN_GCS = new String[] { "GC1", "GC2" };
+    private static final Long[] GEN_SPACES = new Long[] { 2L, 1L };
+    private static final String[][] SPACE_NAME = new String[][] { 
+        { "Space1", "Space2" },
+        { "Space3" }
+    };
+    private static final Long[][] SPACE_CAPS = new Long[][] {
+        { 225L, 275L },
+        { 1000L }
+    };
+    private static final Long[][] SPACE_MAX_CAPS = new Long[][] {
+        { 2250L, 2750L },
+        { 10000L }
+    };
+    private static final Long[][] SPACE_USED = new Long[][] {
+        { 125L, 175L },
+        { 900L }
+    };
+    
+    private VmMemoryVmListener vmListener;
+    private MonitoredVm monitoredVm;
+    private VmMemoryDataExtractor extractor;
+    private VmMemoryStatDAO vmMemoryStatDAO;
+    
+    @Before
+    public void setup() throws MonitorException {
+        final int numGens = 2;
+        vmMemoryStatDAO = mock(VmMemoryStatDAO.class);
+        vmListener = new VmMemoryVmListener(vmMemoryStatDAO, 0);
+        
+        monitoredVm = mock(MonitoredVm.class);
+        extractor = mock(VmMemoryDataExtractor.class);
+        
+        for (int i = 0; i < numGens; i++) {
+            mockGenerationName(i);
+            mockGenerationCapacity(i);
+            mockGenerationMaxCapacity(i);
+            mockGenerationGC(i);
+            mockTotalSpaces(i);
+            int numSpaces = GEN_SPACES[i].intValue();
+            for (int j = 0; j < numSpaces; j++) {
+                mockSpaceName(i, j);
+                mockSpaceCapacity(i, j);
+                mockSpaceMaxCapacity(i, j);
+                mockSpaceUsed(i, j);
+            }
+        }
+    }
+
+    private void mockGenerationName(int gen) throws MonitorException {
+        when(extractor.getGenerationName(gen)).thenReturn(GEN_NAMES[gen]);
+    }
+    
+    private void mockGenerationCapacity(int gen) throws MonitorException {
+        when(extractor.getGenerationCapacity(gen)).thenReturn(GEN_CAPS[gen]);
+    }
+
+    private void mockGenerationMaxCapacity(int gen) throws MonitorException {
+        when(extractor.getGenerationMaxCapacity(gen)).thenReturn(GEN_MAX_CAPS[gen]);
+    }
+    
+    private void mockGenerationGC(int gen) throws MonitorException {
+        when(extractor.getGenerationCollector(gen)).thenReturn(GEN_GCS[gen]);
+    }
+    
+    private void mockTotalSpaces(int gen) throws MonitorException {
+        when(extractor.getTotalSpaces(gen)).thenReturn(GEN_SPACES[gen]);
+    }
+    
+    private void mockSpaceName(int gen, int space) throws MonitorException {
+        when(extractor.getSpaceName(gen, space)).thenReturn(SPACE_NAME[gen][space]);
+    }
+    
+    private void mockSpaceCapacity(int gen, int space) throws MonitorException {
+        when(extractor.getSpaceCapacity(gen, space)).thenReturn(SPACE_CAPS[gen][space]);
+    }
+    
+    private void mockSpaceMaxCapacity(int gen, int space) throws MonitorException {
+        when(extractor.getSpaceMaxCapacity(gen, space)).thenReturn(SPACE_MAX_CAPS[gen][space]);
+    }
+    
+    private void mockSpaceUsed(int gen, int space) throws MonitorException {
+        when(extractor.getSpaceUsed(gen, space)).thenReturn(SPACE_USED[gen][space]);
+    }
+    
+    @Test
+    public void testRecordMemoryStat() {
+        vmListener.recordMemoryStat(monitoredVm, extractor);
+        ArgumentCaptor<VmMemoryStat> captor = ArgumentCaptor.forClass(VmMemoryStat.class);
+        verify(vmMemoryStatDAO).putVmMemoryStat(captor.capture());
+        VmMemoryStat memoryStat = captor.getValue();
+        
+        Generation[] gens = memoryStat.getGenerations();
+        for (int i = 0; i < gens.length; i++) {
+            Generation gen = gens[i];
+            assertEquals(GEN_NAMES[i], gen.getName());
+            assertEquals(GEN_CAPS[i], (Long) gen.getCapacity());
+            assertEquals(GEN_MAX_CAPS[i], (Long) gen.getMaxCapacity());
+            assertEquals(GEN_GCS[i], gen.getCollector());
+            assertEquals(GEN_SPACES[i], Long.valueOf(gen.getSpaces().length));
+            Space[] spaces = gen.getSpaces();
+            for (int j = 0; j < spaces.length; j++) {
+                Space space = spaces[j];
+                assertEquals(SPACE_NAME[i][j], space.getName());
+                assertEquals(SPACE_CAPS[i][j], (Long) space.getCapacity());
+                assertEquals(SPACE_MAX_CAPS[i][j], (Long) space.getMaxCapacity());
+                assertEquals(SPACE_USED[i][j], (Long) space.getUsed());
+            }
+        }
+    }
+}
--- a/vm-memory/client-core/pom.xml	Mon Jan 07 15:48:38 2013 -0500
+++ b/vm-memory/client-core/pom.xml	Mon Jan 07 15:50:03 2013 -0500
@@ -67,6 +67,11 @@
     </dependency>
     <dependency>
       <groupId>com.redhat.thermostat</groupId>
+      <artifactId>thermostat-vm-memory-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-memory/client-core/src/main/java/com/redhat/thermostat/vm/memory/client/core/MemoryStatsService.java	Mon Jan 07 15:48:38 2013 -0500
+++ b/vm-memory/client-core/src/main/java/com/redhat/thermostat/vm/memory/client/core/MemoryStatsService.java	Mon Jan 07 15:50:03 2013 -0500
@@ -43,11 +43,11 @@
 import com.redhat.thermostat.common.ApplicationService;
 import com.redhat.thermostat.common.dao.AgentInfoDAO;
 import com.redhat.thermostat.common.dao.VmInfoDAO;
-import com.redhat.thermostat.common.dao.VmMemoryStatDAO;
 import com.redhat.thermostat.common.dao.VmRef;
 import com.redhat.thermostat.common.utils.OSGIUtils;
 import com.redhat.thermostat.gc.remote.common.GCRequest;
 import com.redhat.thermostat.vm.memory.client.core.internal.MemoryStatsController;
+import com.redhat.thermostat.vm.memory.common.VmMemoryStatDAO;
 
 public class MemoryStatsService implements InformationService<VmRef> {
     
--- a/vm-memory/client-core/src/main/java/com/redhat/thermostat/vm/memory/client/core/internal/Activator.java	Mon Jan 07 15:48:38 2013 -0500
+++ b/vm-memory/client-core/src/main/java/com/redhat/thermostat/vm/memory/client/core/internal/Activator.java	Mon Jan 07 15:50:03 2013 -0500
@@ -51,10 +51,10 @@
 import com.redhat.thermostat.common.MultipleServiceTracker.Action;
 import com.redhat.thermostat.common.dao.AgentInfoDAO;
 import com.redhat.thermostat.common.dao.VmInfoDAO;
-import com.redhat.thermostat.common.dao.VmMemoryStatDAO;
 import com.redhat.thermostat.common.dao.VmRef;
 import com.redhat.thermostat.gc.remote.common.GCRequest;
 import com.redhat.thermostat.vm.memory.client.core.MemoryStatsService;
+import com.redhat.thermostat.vm.memory.common.VmMemoryStatDAO;
 
 public class Activator implements BundleActivator {
 
--- a/vm-memory/client-core/src/main/java/com/redhat/thermostat/vm/memory/client/core/internal/MemoryStatsController.java	Mon Jan 07 15:48:38 2013 -0500
+++ b/vm-memory/client-core/src/main/java/com/redhat/thermostat/vm/memory/client/core/internal/MemoryStatsController.java	Mon Jan 07 15:50:03 2013 -0500
@@ -49,8 +49,8 @@
 import com.redhat.thermostat.common.ApplicationService;
 import com.redhat.thermostat.common.NotImplementedException;
 import com.redhat.thermostat.common.Size;
+import com.redhat.thermostat.common.Size.Unit;
 import com.redhat.thermostat.common.Timer;
-import com.redhat.thermostat.common.Size.Unit;
 import com.redhat.thermostat.common.Timer.SchedulingType;
 import com.redhat.thermostat.common.command.Request;
 import com.redhat.thermostat.common.command.RequestResponseListener;
@@ -58,7 +58,6 @@
 import com.redhat.thermostat.common.command.Response.ResponseType;
 import com.redhat.thermostat.common.dao.AgentInfoDAO;
 import com.redhat.thermostat.common.dao.VmInfoDAO;
-import com.redhat.thermostat.common.dao.VmMemoryStatDAO;
 import com.redhat.thermostat.common.dao.VmRef;
 import com.redhat.thermostat.common.locale.Translate;
 import com.redhat.thermostat.gc.remote.common.GCRequest;
@@ -71,6 +70,7 @@
 import com.redhat.thermostat.vm.memory.client.core.Payload;
 import com.redhat.thermostat.vm.memory.client.core.StatsModel;
 import com.redhat.thermostat.vm.memory.client.locale.LocaleResources;
+import com.redhat.thermostat.vm.memory.common.VmMemoryStatDAO;
 
 public class MemoryStatsController implements InformationServiceController<VmRef> {
 
--- a/vm-memory/client-core/src/test/java/com/redhat/thermostat/vm/memory/client/core/internal/ActivatorTest.java	Mon Jan 07 15:48:38 2013 -0500
+++ b/vm-memory/client-core/src/test/java/com/redhat/thermostat/vm/memory/client/core/internal/ActivatorTest.java	Mon Jan 07 15:50:03 2013 -0500
@@ -47,10 +47,10 @@
 import com.redhat.thermostat.common.ApplicationService;
 import com.redhat.thermostat.common.dao.AgentInfoDAO;
 import com.redhat.thermostat.common.dao.VmInfoDAO;
-import com.redhat.thermostat.common.dao.VmMemoryStatDAO;
 import com.redhat.thermostat.gc.remote.common.GCRequest;
 import com.redhat.thermostat.test.StubBundleContext;
 import com.redhat.thermostat.vm.memory.client.core.MemoryStatsService;
+import com.redhat.thermostat.vm.memory.common.VmMemoryStatDAO;
 
 public class ActivatorTest {
     
--- a/vm-memory/client-core/src/test/java/com/redhat/thermostat/vm/memory/client/core/internal/MemoryStatsControllerTest.java	Mon Jan 07 15:48:38 2013 -0500
+++ b/vm-memory/client-core/src/test/java/com/redhat/thermostat/vm/memory/client/core/internal/MemoryStatsControllerTest.java	Mon Jan 07 15:50:03 2013 -0500
@@ -67,7 +67,6 @@
 import com.redhat.thermostat.common.command.RequestResponseListener;
 import com.redhat.thermostat.common.dao.AgentInfoDAO;
 import com.redhat.thermostat.common.dao.VmInfoDAO;
-import com.redhat.thermostat.common.dao.VmMemoryStatDAO;
 import com.redhat.thermostat.common.dao.VmRef;
 import com.redhat.thermostat.gc.remote.common.GCRequest;
 import com.redhat.thermostat.gc.remote.common.command.GCCommand;
@@ -78,6 +77,7 @@
 import com.redhat.thermostat.vm.memory.client.core.MemoryStatsView;
 import com.redhat.thermostat.vm.memory.client.core.MemoryStatsViewProvider;
 import com.redhat.thermostat.vm.memory.client.core.Payload;
+import com.redhat.thermostat.vm.memory.common.VmMemoryStatDAO;
 
 public class MemoryStatsControllerTest {
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-memory/common/pom.xml	Mon Jan 07 15:50:03 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-memory</artifactId>
+    <groupId>com.redhat.thermostat</groupId>
+    <version>0.5.0-SNAPSHOT</version>
+  </parent>
+  <artifactId>thermostat-vm-memory-common</artifactId>
+  <packaging>bundle</packaging>
+  <name>Thermostat VM Memory 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.memory.common</Bundle-SymbolicName>
+            <Bundle-Activator>com.redhat.thermostat.vm.memory.common.internal.Activator</Bundle-Activator>
+            <Export-Package>
+              com.redhat.thermostat.vm.memory.common
+            </Export-Package>
+            <Private-Package>
+              com.redhat.thermostat.vm.memory.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-memory/common/src/main/java/com/redhat/thermostat/vm/memory/common/VmMemoryStatDAO.java	Mon Jan 07 15:50:03 2013 -0500
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2012 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.vm.memory.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.VmMemoryStat;
+import com.redhat.thermostat.storage.model.VmMemoryStat.Generation;
+
+public interface VmMemoryStatDAO {
+
+    static final Key<Generation[]> generationsKey = new Key<>("generations", false);
+
+    static final Category vmMemoryStatsCategory = new Category("vm-memory-stats",
+            Key.AGENT_ID, Key.VM_ID, Key.TIMESTAMP, generationsKey);
+
+    public VmMemoryStat getLatestMemoryStat(VmRef ref);
+
+    public List<VmMemoryStat> getLatestVmMemoryStats(VmRef vm, long since);
+
+    public void putVmMemoryStat(VmMemoryStat stat);
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-memory/common/src/main/java/com/redhat/thermostat/vm/memory/common/internal/Activator.java	Mon Jan 07 15:50:03 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.memory.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.memory.common.VmMemoryStatDAO;
+
+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);
+                VmMemoryStatDAO vmMemoryStatDao = new VmMemoryStatDAOImpl(storage);
+                reg = context.registerService(VmMemoryStatDAO.class.getName(), vmMemoryStatDao, 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-memory/common/src/main/java/com/redhat/thermostat/vm/memory/common/internal/VmMemoryStatDAOImpl.java	Mon Jan 07 15:50:03 2013 -0500
@@ -0,0 +1,87 @@
+/*
+ * 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.memory.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.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.VmMemoryStat;
+import com.redhat.thermostat.vm.memory.common.VmMemoryStatDAO;
+
+class VmMemoryStatDAOImpl implements VmMemoryStatDAO {
+
+    private final Storage storage;
+    private final VmLatestPojoListGetter<VmMemoryStat> getter;
+
+    VmMemoryStatDAOImpl(Storage storage) {
+        this.storage = storage;
+        storage.registerCategory(vmMemoryStatsCategory);
+        getter = new VmLatestPojoListGetter<>(storage, vmMemoryStatsCategory, VmMemoryStat.class);
+    }
+
+    @Override
+    public VmMemoryStat getLatestMemoryStat(VmRef ref) {
+        Query query = storage.createQuery()
+                .from(vmMemoryStatsCategory)
+                .where(Key.AGENT_ID, Criteria.EQUALS, ref.getAgent().getAgentId())
+                .where(Key.VM_ID, Criteria.EQUALS, ref.getId())
+                .sort(Key.TIMESTAMP, Query.SortDirection.DESCENDING)
+                .limit(1);
+        Cursor<VmMemoryStat> cursor = storage.findAllPojos(query, VmMemoryStat.class);
+        if (cursor.hasNext()) {
+            return cursor.next();
+        }
+        return null;
+    }
+
+    @Override
+    public void putVmMemoryStat(VmMemoryStat stat) {
+        storage.putPojo(vmMemoryStatsCategory, false, stat);
+    }
+
+    @Override
+    public List<VmMemoryStat> getLatestVmMemoryStats(VmRef ref, long since) {
+        return getter.getLatest(ref, since);
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-memory/common/src/test/java/com/redhat/thermostat/vm/memory/common/internal/ActivatorTest.java	Mon Jan 07 15:50:03 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.memory.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.memory.common.VmMemoryStatDAO;
+
+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(VmMemoryStatDAO.class.getName(), VmMemoryStatDAOImpl.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-memory/common/src/test/java/com/redhat/thermostat/vm/memory/common/internal/VmMemoryStatDAOTest.java	Mon Jan 07 15:50:03 2013 -0500
@@ -0,0 +1,202 @@
+/*
+ * 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.memory.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.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+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.VmMemoryStat;
+import com.redhat.thermostat.storage.model.VmMemoryStat.Generation;
+import com.redhat.thermostat.storage.model.VmMemoryStat.Space;
+import com.redhat.thermostat.test.MockQuery;
+import com.redhat.thermostat.vm.memory.common.VmMemoryStatDAO;
+import com.redhat.thermostat.vm.memory.common.internal.VmMemoryStatDAOImpl;
+
+public class VmMemoryStatDAOTest {
+
+    private static final int VM_ID = 0xcafe;
+    private static final String AGENT_ID = "agent";
+
+    private Storage storage;
+    private VmRef vmRef;
+
+    private MockQuery query;
+    private Cursor<VmMemoryStat> cursor;
+
+    @SuppressWarnings("unchecked")
+    @Before
+    public void setUp() {
+        
+
+        HostRef hostRef = mock(HostRef.class);
+        when(hostRef.getAgentId()).thenReturn(AGENT_ID);
+
+        vmRef = mock(VmRef.class);
+        when(vmRef.getAgent()).thenReturn(hostRef);
+        when(vmRef.getId()).thenReturn(VM_ID);
+
+        storage = mock(Storage.class);
+        query = new MockQuery();
+        when(storage.createQuery()).thenReturn(query);
+
+        cursor = mock(Cursor.class);
+        when(storage.findAllPojos(any(Query.class), same(VmMemoryStat.class))).thenReturn(cursor);
+
+        when(cursor.hasNext()).thenReturn(false);
+
+    }
+
+    @After
+    public void tearDown() {
+        query = null;
+        vmRef = null;
+        cursor = null;
+        storage = null;
+    }
+
+    @Test
+    public void testCategories() {
+        Collection<Key<?>> keys;
+
+        assertEquals("vm-memory-stats", VmMemoryStatDAO.vmMemoryStatsCategory.getName());
+        keys = VmMemoryStatDAO.vmMemoryStatsCategory.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<Generation[]>("generations", false)));
+        assertEquals(4, keys.size());
+    }
+
+    @Test
+    public void testGetLatest() {
+        VmMemoryStatDAO impl = new VmMemoryStatDAOImpl(storage);
+        impl.getLatestMemoryStat(vmRef);
+
+        verifyQuery();
+    }
+
+    @Test
+    public void testGetLatestSince() {
+        VmMemoryStatDAO impl = new VmMemoryStatDAOImpl(storage);
+        impl.getLatestVmMemoryStats(vmRef, 123);
+
+        verifyQuery();
+
+        assertTrue(query.hasWhereClause(Key.TIMESTAMP, Criteria.GREATER_THAN, 123l));
+    }
+
+    private void verifyQuery() {
+
+        assertTrue(query.hasWhereClause(Key.AGENT_ID, Criteria.EQUALS, AGENT_ID));
+        assertTrue(query.hasWhereClause(Key.VM_ID, Criteria.EQUALS, VM_ID));
+        assertTrue(query.hasSort(Key.TIMESTAMP, Query.SortDirection.DESCENDING));
+    }
+
+    @Test
+    public void testGetLatestReturnsNullWhenStorageEmpty() {
+        when(cursor.hasNext()).thenReturn(false);
+        when(cursor.next()).thenReturn(null);
+
+        Storage storage = mock(Storage.class);
+        when(storage.createQuery()).thenReturn(new MockQuery());
+        when(storage.findAllPojos(any(Query.class), same(VmMemoryStat.class))).thenReturn(cursor);
+
+        VmMemoryStatDAO impl = new VmMemoryStatDAOImpl(storage);
+        VmMemoryStat latest = impl.getLatestMemoryStat(vmRef);
+        assertTrue(latest == null);
+    }
+
+    @Test
+    public void testPutVmMemoryStat() {
+
+        List<Generation> generations = new ArrayList<Generation>();
+
+        int i = 0;
+        for (String genName: new String[] { "new", "old", "perm" }) {
+            Generation gen = new Generation();
+            gen.setName(genName);
+            gen.setCollector(gen.getName());
+            generations.add(gen);
+            List<Space> spaces = new ArrayList<Space>();
+            String[] spaceNames = null;
+            if (genName.equals("new")) {
+                spaceNames = new String[] { "eden", "s0", "s1" };
+            } else if (genName.equals("old")) {
+                spaceNames = new String[] { "old" };
+            } else {
+                spaceNames = new String[] { "perm" };
+            }
+            for (String spaceName: spaceNames) {
+                Space space = new Space();
+                space.setName(spaceName);
+                space.setIndex(0);
+                space.setUsed(i++);
+                space.setCapacity(i++);
+                space.setMaxCapacity(i++);
+                spaces.add(space);
+            }
+            gen.setSpaces(spaces.toArray(new Space[spaces.size()]));
+        }
+        VmMemoryStat stat = new VmMemoryStat(1, 2, generations.toArray(new Generation[generations.size()]));
+
+        Storage storage = mock(Storage.class);
+        VmMemoryStatDAO dao = new VmMemoryStatDAOImpl(storage);
+        dao.putVmMemoryStat(stat);
+
+        verify(storage).putPojo(VmMemoryStatDAO.vmMemoryStatsCategory, false, stat);
+    }
+}
--- a/vm-memory/pom.xml	Mon Jan 07 15:48:38 2013 -0500
+++ b/vm-memory/pom.xml	Mon Jan 07 15:50:03 2013 -0500
@@ -51,8 +51,10 @@
   <name>Thermostat VM Memory plugin</name>
 
   <modules>
+    <module>agent</module>
     <module>client-core</module>
     <module>client-swing</module>
+    <module>common</module>
   </modules>
 
 </project>