changeset 892:4c8a876b9dc0

Create Host Memory agent and common bundles This commit extracts Host Memory data collection from SystemBackend into a host-memory-agent bundle. It also moves the MemoryStatDAO from common-core into a host-memory-common bundle that registers the DAO once Storage is available. This also removes the DAO from DAOFactory. Reviewed-by: omajid Review-thread: http://icedtea.classpath.org/pipermail/thermostat/2013-January/004994.html
author Elliott Baron <ebaron@redhat.com>
date Mon, 07 Jan 2013 15:46:47 -0500
parents 3477dae0e6da
children 9e6bcfc40ea1
files common/core/src/main/java/com/redhat/thermostat/common/dao/DAOFactory.java common/core/src/main/java/com/redhat/thermostat/common/dao/DAOFactoryImpl.java common/core/src/main/java/com/redhat/thermostat/common/dao/MemoryStatDAO.java common/core/src/main/java/com/redhat/thermostat/common/dao/MemoryStatDAOImpl.java common/core/src/test/java/com/redhat/thermostat/common/dao/MemoryStatDAOTest.java common/core/src/test/java/com/redhat/thermostat/common/dao/MongoDAOFactoryTest.java distribution/config/commands/agent.properties distribution/config/commands/gui.properties distribution/pom.xml eclipse/com.redhat.thermostat.client.feature/feature.xml host-memory/agent/pom.xml host-memory/agent/src/main/java/com/redhat/thermostat/host/memory/agent/internal/Activator.java host-memory/agent/src/main/java/com/redhat/thermostat/host/memory/agent/internal/HostMemoryBackend.java host-memory/agent/src/main/java/com/redhat/thermostat/host/memory/agent/internal/MemoryStatBuilder.java host-memory/agent/src/test/java/com/redhat/thermostat/host/memory/agent/internal/ActivatorTest.java host-memory/agent/src/test/java/com/redhat/thermostat/host/memory/agent/internal/HostMemoryBackendTest.java host-memory/agent/src/test/java/com/redhat/thermostat/host/memory/agent/internal/MemoryStatBuilderTest.java host-memory/client-core/pom.xml host-memory/client-core/src/main/java/com/redhat/thermostat/host/memory/client/core/HostMemoryService.java host-memory/client-core/src/main/java/com/redhat/thermostat/host/memory/client/core/internal/Activator.java host-memory/client-core/src/main/java/com/redhat/thermostat/host/memory/client/core/internal/HostMemoryController.java host-memory/client-core/src/test/java/com/redhat/thermostat/host/memory/client/core/internal/ActivatorTest.java host-memory/client-core/src/test/java/com/redhat/thermostat/host/memory/client/core/internal/HostMemoryControllerTest.java host-memory/common/pom.xml host-memory/common/src/main/java/com/redhat/thermostat/host/memory/common/MemoryStatDAO.java host-memory/common/src/main/java/com/redhat/thermostat/host/memory/common/internal/Activator.java host-memory/common/src/main/java/com/redhat/thermostat/host/memory/common/internal/MemoryStatDAOImpl.java host-memory/common/src/test/java/com/redhat/thermostat/host/memory/common/internal/ActivatorTest.java host-memory/common/src/test/java/com/redhat/thermostat/host/memory/common/internal/MemoryStatDAOTest.java host-memory/pom.xml system-backend/src/main/java/com/redhat/thermostat/backend/system/MemoryStatBuilder.java system-backend/src/main/java/com/redhat/thermostat/backend/system/SystemBackend.java system-backend/src/test/java/com/redhat/thermostat/backend/system/MemoryStatBuilderTest.java system-backend/src/test/java/com/redhat/thermostat/backend/system/SystemBackendTest.java
diffstat 34 files changed, 1297 insertions(+), 570 deletions(-) [+]
line wrap: on
line diff
--- a/common/core/src/main/java/com/redhat/thermostat/common/dao/DAOFactory.java	Mon Jan 07 15:44:59 2013 -0500
+++ b/common/core/src/main/java/com/redhat/thermostat/common/dao/DAOFactory.java	Mon Jan 07 15:46:47 2013 -0500
@@ -52,8 +52,6 @@
 
     public HostInfoDAO getHostInfoDAO();
 
-    public MemoryStatDAO getMemoryStatDAO();
-
     public NetworkInterfaceInfoDAO getNetworkInterfaceInfoDAO();
 
     public VmInfoDAO getVmInfoDAO();
--- a/common/core/src/main/java/com/redhat/thermostat/common/dao/DAOFactoryImpl.java	Mon Jan 07 15:44:59 2013 -0500
+++ b/common/core/src/main/java/com/redhat/thermostat/common/dao/DAOFactoryImpl.java	Mon Jan 07 15:46:47 2013 -0500
@@ -57,7 +57,6 @@
     private AgentInfoDAO agentDAO;
     private BackendInfoDAO backendInfoDAO;
     private HostInfoDAO hostInfoDAO;
-    private MemoryStatDAO memoryStatDAO;
     private NetworkInterfaceInfoDAO networkInfoDAO;
     private VmInfoDAO vmInfoDAO;
     private VmCpuStatDAO vmCpuStatDAO;
@@ -98,12 +97,6 @@
     }
 
     @Override
-    public MemoryStatDAO getMemoryStatDAO() {
-        ensureStorageConnected();
-        return memoryStatDAO;
-    }
-
-    @Override
     public NetworkInterfaceInfoDAO getNetworkInterfaceInfoDAO() {
         ensureStorageConnected();
         return networkInfoDAO;
@@ -161,7 +154,6 @@
 
         registerAndRecordService(HostInfoDAO.class, getHostInfoDAO());
         registerAndRecordService(NetworkInterfaceInfoDAO.class, getNetworkInterfaceInfoDAO());
-        registerAndRecordService(MemoryStatDAO.class, getMemoryStatDAO());
 
         registerAndRecordService(VmInfoDAO.class, getVmInfoDAO());
         registerAndRecordService(VmClassStatDAO.class, getVmClassStatsDAO());
@@ -177,7 +169,6 @@
         agentDAO = new AgentInfoDAOImpl(storage);
         backendInfoDAO = new BackendInfoDAOImpl(storage);
         hostInfoDAO = new HostInfoDAOImpl(storage, agentDAO);
-        memoryStatDAO = new MemoryStatDAOImpl(storage);
         networkInfoDAO = new NetworkInterfaceInfoDAOImpl(storage);
         vmInfoDAO = new VmInfoDAOImpl(storage);
         vmCpuStatDAO = new VmCpuStatDAOImpl(storage);
--- a/common/core/src/main/java/com/redhat/thermostat/common/dao/MemoryStatDAO.java	Mon Jan 07 15:44:59 2013 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,62 +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.MemoryStat;
-
-public interface MemoryStatDAO extends Countable {
-
-    static Key<Long> memoryTotalKey = new Key<>("total", false);
-    static Key<Long> memoryFreeKey = new Key<>("free", false);
-    static Key<Long> memoryBuffersKey = new Key<>("buffers", false);
-    static Key<Long> memoryCachedKey = new Key<>("cached", false);
-    static Key<Long> memorySwapTotalKey = new Key<>("swapTotal", false);
-    static Key<Long> memorySwapFreeKey = new Key<>("swapFree", false);
-    static Key<Long> memoryCommitLimitKey = new Key<>("commitLimit", false);
-
-    static final Category memoryStatCategory = new Category("memory-stats",
-            Key.AGENT_ID, Key.TIMESTAMP, memoryTotalKey, memoryFreeKey, memoryBuffersKey,
-            memoryCachedKey, memorySwapTotalKey, memorySwapFreeKey, memoryCommitLimitKey);
-
-    public List<MemoryStat> getLatestMemoryStats(HostRef ref, long since);
-
-    void putMemoryStat(MemoryStat stat);
-}
--- a/common/core/src/main/java/com/redhat/thermostat/common/dao/MemoryStatDAOImpl.java	Mon Jan 07 15:44:59 2013 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,70 +0,0 @@
-/*
- * Copyright 2012 Red Hat, Inc.
- *
- * This file is part of Thermostat.
- *
- * Thermostat is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published
- * by the Free Software Foundation; either version 2, or (at your
- * option) any later version.
- *
- * Thermostat is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Thermostat; see the file COPYING.  If not see
- * <http://www.gnu.org/licenses/>.
- *
- * Linking this code with other modules is making a combined work
- * based on this code.  Thus, the terms and conditions of the GNU
- * General Public License cover the whole combination.
- *
- * As a special exception, the copyright holders of this code give
- * you permission to link this code with independent modules to
- * produce an executable, regardless of the license terms of these
- * independent modules, and to copy and distribute the resulting
- * executable under terms of your choice, provided that you also
- * meet, for each linked independent module, the terms and conditions
- * of the license of that module.  An independent module is a module
- * which is not derived from or based on this code.  If you modify
- * this code, you may extend this exception to your version of the
- * library, but you are not obligated to do so.  If you do not wish
- * to do so, delete this exception statement from your version.
- */
-
-package com.redhat.thermostat.common.dao;
-
-import java.util.List;
-
-import com.redhat.thermostat.storage.core.Storage;
-import com.redhat.thermostat.storage.model.MemoryStat;
-
-class MemoryStatDAOImpl implements MemoryStatDAO {
-
-    private final Storage storage;
-
-    private final HostLatestPojoListGetter<MemoryStat> getter;
-
-    MemoryStatDAOImpl(Storage storage) {
-        this.storage = storage;
-        storage.registerCategory(memoryStatCategory);
-        this.getter = new HostLatestPojoListGetter<>(storage, memoryStatCategory, MemoryStat.class);
-    }
-
-    @Override
-    public List<MemoryStat> getLatestMemoryStats(HostRef ref, long lastTimeStamp) {
-        return getter.getLatest(ref, lastTimeStamp);
-    }
-
-    @Override
-    public void putMemoryStat(MemoryStat stat) {
-        storage.putPojo(memoryStatCategory, false, stat);
-    }
-
-    @Override
-    public long getCount() {
-        return storage.getCount(memoryStatCategory);
-    }
-}
--- a/common/core/src/test/java/com/redhat/thermostat/common/dao/MemoryStatDAOTest.java	Mon Jan 07 15:44:59 2013 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,151 +0,0 @@
-/*
- * Copyright 2012 Red Hat, Inc.
- *
- * This file is part of Thermostat.
- *
- * Thermostat is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published
- * by the Free Software Foundation; either version 2, or (at your
- * option) any later version.
- *
- * Thermostat is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Thermostat; see the file COPYING.  If not see
- * <http://www.gnu.org/licenses/>.
- *
- * Linking this code with other modules is making a combined work
- * based on this code.  Thus, the terms and conditions of the GNU
- * General Public License cover the whole combination.
- *
- * As a special exception, the copyright holders of this code give
- * you permission to link this code with independent modules to
- * produce an executable, regardless of the license terms of these
- * independent modules, and to copy and distribute the resulting
- * executable under terms of your choice, provided that you also
- * meet, for each linked independent module, the terms and conditions
- * of the license of that module.  An independent module is a module
- * which is not derived from or based on this code.  If you modify
- * this code, you may extend this exception to your version of the
- * library, but you are not obligated to do so.  If you do not wish
- * to do so, delete this exception statement from your version.
- */
-
-package com.redhat.thermostat.common.dao;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.same;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import java.util.Collection;
-import java.util.List;
-
-import org.junit.Test;
-import org.mockito.ArgumentCaptor;
-import org.mockito.invocation.InvocationOnMock;
-import org.mockito.stubbing.Answer;
-
-import com.redhat.thermostat.storage.core.Category;
-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.MemoryStat;
-import com.redhat.thermostat.test.MockQuery;
-
-public class MemoryStatDAOTest {
-
-    private static long TIMESTAMP = 1;
-    private static long TOTAL = 2;
-    private static long FREE = 3;
-    private static long BUFFERS = 4;
-    private static long CACHED = 5;
-    private static long SWAP_TOTAL = 6;
-    private static long SWAP_FREE = 7;
-    private static long COMMIT_LIMIT = 8;
-
-    @Test
-    public void testCategory() {
-        assertEquals("memory-stats", MemoryStatDAO.memoryStatCategory.getName());
-        Collection<Key<?>> keys = MemoryStatDAO.memoryStatCategory.getKeys();
-        assertTrue(keys.contains(new Key<>("agentId", true)));
-        assertTrue(keys.contains(new Key<Long>("timeStamp", false)));
-        assertTrue(keys.contains(new Key<Long>("total", false)));
-        assertTrue(keys.contains(new Key<Long>("free", false)));
-        assertTrue(keys.contains(new Key<Long>("buffers", false)));
-        assertTrue(keys.contains(new Key<Long>("cached", false)));
-        assertTrue(keys.contains(new Key<Long>("swapTotal", false)));
-        assertTrue(keys.contains(new Key<Long>("swapFree", false)));
-        assertTrue(keys.contains(new Key<Long>("commitLimit", false)));
-        assertEquals(9, keys.size());
-    }
-
-    @Test
-    public void testGetLatestMemoryStats() {
-
-        MemoryStat memStat1 = new MemoryStat(TIMESTAMP, TOTAL, FREE, BUFFERS, CACHED, SWAP_TOTAL, SWAP_FREE, COMMIT_LIMIT);
-
-        @SuppressWarnings("unchecked")
-        Cursor<MemoryStat> cursor = mock(Cursor.class);
-        when(cursor.hasNext()).thenReturn(true).thenReturn(false);
-        when(cursor.next()).thenReturn(memStat1);
-
-        Storage storage = mock(Storage.class);
-        when(storage.createQuery()).then(new Answer<Query>() {
-            @Override
-            public Query answer(InvocationOnMock invocation) throws Throwable {
-                return new MockQuery();
-            }
-        });
-        when(storage.findAllPojos(any(Query.class), same(MemoryStat.class))).thenReturn(cursor);
-
-        HostRef hostRef = mock(HostRef.class);
-        when(hostRef.getAgentId()).thenReturn("system");
-
-        MemoryStatDAO dao = new MemoryStatDAOImpl(storage);
-        List<MemoryStat> memoryStats = dao.getLatestMemoryStats(hostRef, Long.MIN_VALUE);
-
-        ArgumentCaptor<MockQuery> arg = ArgumentCaptor.forClass(MockQuery.class);
-        verify(storage).findAllPojos(arg.capture(), same(MemoryStat.class));
-        assertTrue(arg.getValue().hasWhereClause(Key.TIMESTAMP, Criteria.GREATER_THAN, Long.MIN_VALUE));
-
-        assertEquals(1, memoryStats.size());
-        MemoryStat stat = memoryStats.get(0);
-
-        assertEquals(TIMESTAMP, stat.getTimeStamp());
-        assertEquals(TOTAL, stat.getTotal());
-        assertEquals(FREE, stat.getFree());
-        assertEquals(BUFFERS, stat.getBuffers());
-        assertEquals(CACHED, stat.getCached());
-        assertEquals(SWAP_TOTAL, stat.getSwapTotal());
-        assertEquals(SWAP_FREE, stat.getSwapFree());
-        assertEquals(COMMIT_LIMIT, stat.getCommitLimit());
-    }
-
-    @Test
-    public void testPutMemoryStat() {
-        Storage storage = mock(Storage.class);
-        MemoryStat stat = new MemoryStat(TIMESTAMP, TOTAL, FREE, BUFFERS, CACHED, SWAP_TOTAL, SWAP_FREE, COMMIT_LIMIT);
-        MemoryStatDAO dao = new MemoryStatDAOImpl(storage);
-        dao.putMemoryStat(stat);
-
-        verify(storage).putPojo(MemoryStatDAO.memoryStatCategory, false, stat);
-    }
-
-    @Test
-    public void testGetCount() {
-        Storage storage = mock(Storage.class);
-        when(storage.getCount(any(Category.class))).thenReturn(5L);
-        MemoryStatDAO dao = new MemoryStatDAOImpl(storage);
-        Long count = dao.getCount();
-        assertEquals((Long) 5L, count);
-    }
-}
--- a/common/core/src/test/java/com/redhat/thermostat/common/dao/MongoDAOFactoryTest.java	Mon Jan 07 15:44:59 2013 -0500
+++ b/common/core/src/test/java/com/redhat/thermostat/common/dao/MongoDAOFactoryTest.java	Mon Jan 07 15:46:47 2013 -0500
@@ -120,12 +120,6 @@
     }
 
     @Test
-    public void testGetMemoryStatDAO() {
-        MemoryStatDAO dao = daoFactory.getMemoryStatDAO();
-        assertNotNull(dao);
-    }
-
-    @Test
     public void testGetNetworkInterfaceInfoDAO() {
         NetworkInterfaceInfoDAO dao = daoFactory.getNetworkInterfaceInfoDAO();
         assertNotNull(dao);
@@ -137,8 +131,8 @@
 
         daoFactory.registerDAOsAndStorageAsOSGiServices();
 
-        // currently 12 DAOs and Storage are registered
-        assertEquals(11, bundleContext.getAllServices().size());
+        // currently 10 DAOs and Storage are registered
+        assertEquals(10, bundleContext.getAllServices().size());
     }
 
     @Test
--- a/distribution/config/commands/agent.properties	Mon Jan 07 15:44:59 2013 -0500
+++ b/distribution/config/commands/agent.properties	Mon Jan 07 15:46:47 2013 -0500
@@ -11,6 +11,8 @@
           thermostat-agent-command-@project.version@.jar, \
           thermostat-host-cpu-common-@project.version@.jar, \
           thermostat-host-cpu-agent-@project.version@.jar, \
+          thermostat-host-memory-common-@project.version@.jar, \
+          thermostat-host-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:44:59 2013 -0500
+++ b/distribution/config/commands/gui.properties	Mon Jan 07 15:46:47 2013 -0500
@@ -17,6 +17,7 @@
           thermostat-host-cpu-common-@project.version@.jar, \
           thermostat-host-cpu-client-core-@project.version@.jar, \
           thermostat-host-cpu-client-swing-@project.version@.jar, \
+          thermostat-host-memory-common-@project.version@.jar, \
           thermostat-host-memory-client-core-@project.version@.jar, \
           thermostat-host-memory-client-swing-@project.version@.jar, \
           thermostat-vm-overview-client-core-@project.version@.jar, \
--- a/distribution/pom.xml	Mon Jan 07 15:44:59 2013 -0500
+++ b/distribution/pom.xml	Mon Jan 07 15:46:47 2013 -0500
@@ -387,6 +387,11 @@
     </dependency>
     <dependency>
       <groupId>com.redhat.thermostat</groupId>
+      <artifactId>thermostat-host-memory-agent</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>com.redhat.thermostat</groupId>
       <artifactId>thermostat-vm-overview-client-core</artifactId>
       <version>${project.version}</version>
     </dependency>
--- a/eclipse/com.redhat.thermostat.client.feature/feature.xml	Mon Jan 07 15:44:59 2013 -0500
+++ b/eclipse/com.redhat.thermostat.client.feature/feature.xml	Mon Jan 07 15:46:47 2013 -0500
@@ -189,4 +189,11 @@
          version="0.0.0"
          unpack="false"/>
 
+   <plugin
+         id="com.redhat.thermostat.host.memory.common"
+         download-size="0"
+         install-size="0"
+         version="0.0.0"
+         unpack="false"/>
+
 </feature>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/host-memory/agent/pom.xml	Mon Jan 07 15:46:47 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-host-memory</artifactId>
+    <groupId>com.redhat.thermostat</groupId>
+    <version>0.5.0-SNAPSHOT</version>
+  </parent>
+  <artifactId>thermostat-host-memory-agent</artifactId>
+  <packaging>bundle</packaging>
+  <name>Thermostat Host 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.host.memory.agent</Bundle-SymbolicName>
+            <Bundle-Activator>com.redhat.thermostat.host.memory.agent.internal.Activator</Bundle-Activator>
+            <Export-Package>
+              com.redhat.thermostat.host.memory.agent
+            </Export-Package>
+            <Private-Package>
+              com.redhat.thermostat.host.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-host-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/host-memory/agent/src/main/java/com/redhat/thermostat/host/memory/agent/internal/Activator.java	Mon Jan 07 15:46:47 2013 -0500
@@ -0,0 +1,101 @@
+/*
+ * Copyright 2013 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.host.memory.agent.internal;
+
+import java.util.Map;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+
+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.host.memory.common.MemoryStatDAO;
+
+public class Activator implements BundleActivator {
+    
+    private ScheduledExecutorService executor;
+    private MultipleServiceTracker tracker;
+    private HostMemoryBackend backend;
+    private ServiceRegistration reg;
+    
+    @Override
+    public void start(final BundleContext context) throws Exception {
+        executor = Executors.newSingleThreadScheduledExecutor();
+
+        Class<?>[] deps = new Class<?>[] {
+                BackendService.class,
+                MemoryStatDAO.class
+        };
+        tracker = new MultipleServiceTracker(context, deps, new Action() {
+            
+            @Override
+            public void dependenciesAvailable(Map<String, Object> services) {
+                MemoryStatDAO memoryStatDao = (MemoryStatDAO) services.get(MemoryStatDAO.class.getName());
+                Version version = new Version(context.getBundle());
+                backend = new HostMemoryBackend(executor, memoryStatDao, 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.
+     */
+    HostMemoryBackend getBackend() {
+        return backend;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/host-memory/agent/src/main/java/com/redhat/thermostat/host/memory/agent/internal/HostMemoryBackend.java	Mon Jan 07 15:46:47 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.host.memory.agent.internal;
+
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+
+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.host.memory.common.MemoryStatDAO;
+import com.redhat.thermostat.utils.ProcDataSource;
+
+public class HostMemoryBackend extends Backend {
+
+    private static final long PROC_CHECK_INTERVAL = 1000; // TODO make this configurable.
+    
+    private final MemoryStatBuilder memoryStatBuilder;
+    private MemoryStatDAO memoryStats;
+    private ScheduledExecutorService executor;
+    private boolean started;
+
+    public HostMemoryBackend(ScheduledExecutorService executor, MemoryStatDAO memoryStatDAO, Version version) {
+        super(new BackendID("Host Memory Backend", HostMemoryBackend.class.getName()));
+        this.executor = executor;
+        this.memoryStats = memoryStatDAO;
+        
+        setConfigurationValue(BackendsProperties.VENDOR.name(), "Red Hat, Inc.");
+        setConfigurationValue(BackendsProperties.DESCRIPTION.name(), "Gathers memory statistics about a host");
+        setConfigurationValue(BackendsProperties.VERSION.name(), version.getVersionNumber());
+        
+        ProcDataSource source = new ProcDataSource();
+        memoryStatBuilder = new MemoryStatBuilder(source);
+    }
+
+    @Override
+    public boolean activate() {
+        if (!started) {
+            executor.scheduleAtFixedRate(new Runnable() {
+                @Override
+                public void run() {
+                    memoryStats.putMemoryStat(memoryStatBuilder.build());
+                }
+            }, 0, PROC_CHECK_INTERVAL, TimeUnit.MILLISECONDS);
+            started = true;
+        }
+        return true;
+    }
+
+    @Override
+    public boolean deactivate() {
+        executor.shutdown();
+        started = false;
+        return true;
+    }
+    
+    @Override
+    public boolean isActive() {
+        return started;
+    }
+
+    @Override
+    protected void setDAOFactoryAction() {
+        // No use for DAOFactory
+    }
+
+    @Override
+    public String getConfigurationValue(String key) {
+        return null;
+    }
+
+    @Override
+    public boolean attachToNewProcessByDefault() {
+        return true;
+    }
+
+    @Override
+    public int getOrderValue() {
+        return ORDER_MEMORY_GROUP;
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/host-memory/agent/src/main/java/com/redhat/thermostat/host/memory/agent/internal/MemoryStatBuilder.java	Mon Jan 07 15:46:47 2013 -0500
@@ -0,0 +1,141 @@
+/*
+ * 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.host.memory.agent.internal;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import com.redhat.thermostat.common.NotImplementedException;
+import com.redhat.thermostat.common.Size;
+import com.redhat.thermostat.common.utils.LoggingUtils;
+import com.redhat.thermostat.storage.model.MemoryStat;
+import com.redhat.thermostat.utils.ProcDataSource;
+
+/**
+ * Implementation note: uses information from /proc/
+ */
+public class MemoryStatBuilder {
+
+    private static final long UNAVAILABLE = -1;
+
+    private static final String KEY_MEMORY_TOTAL = "MemTotal";
+    private static final String KEY_MEMORY_FREE = "MemFree";
+    private static final String KEY_BUFFERS = "Buffers";
+    private static final String KEY_CACHED = "Cached";
+    private static final String KEY_SWAP_TOTAL = "SwapTotal";
+    private static final String KEY_SWAP_FREE = "SwapFree";
+    private static final String KEY_COMMIT_LIMIT = "CommitLimit";
+
+    private static final Logger logger = LoggingUtils.getLogger(MemoryStatBuilder.class);
+
+    private final ProcDataSource dataSource;
+
+    public MemoryStatBuilder(ProcDataSource dataSource) {
+        this.dataSource = dataSource;
+    }
+
+    protected MemoryStat build() {
+        long timestamp = System.currentTimeMillis();
+
+        long total = UNAVAILABLE;
+        long free = UNAVAILABLE;
+        long swapTotal = UNAVAILABLE;
+        long swapFree = UNAVAILABLE;
+        long buffers = UNAVAILABLE;
+        long cached = UNAVAILABLE;
+        long commitLimit = UNAVAILABLE;
+
+        try (BufferedReader reader = new BufferedReader(dataSource.getMemInfoReader())) {
+            String line = null;
+            while ((line = reader.readLine()) != null) {
+                String[] parts = line.split(":");
+                if (parts.length == 2) {
+                    String key = parts[0].trim();
+                    long value = getValue(parts[1].trim());
+                    if (key.equals(KEY_MEMORY_TOTAL)) {
+                        total = value;
+                    } else if (key.equals(KEY_MEMORY_FREE)) {
+                        free = value;
+                    } else if (key.equals(KEY_SWAP_TOTAL)) {
+                        swapTotal = value;
+                    } else if (key.equals(KEY_SWAP_FREE)) {
+                        swapFree = value;
+                    } else if (key.equals(KEY_BUFFERS)) {
+                        buffers = value;
+                    } else if (key.equals(KEY_CACHED)) {
+                        cached = value;
+                    } else if (key.equals(KEY_COMMIT_LIMIT)) {
+                        commitLimit = value;
+                    }
+                }
+            }
+        } catch (IOException ioe) {
+            logger.log(Level.WARNING, "unable to read memory info");
+        }
+
+        return new MemoryStat(timestamp, total, free, buffers, cached, swapTotal, swapFree, commitLimit);
+    }
+
+    private long getValue(String rawValue) {
+        String[] parts = rawValue.split(" +");
+        String value = rawValue;
+        String units = null;
+        if (parts.length > 1) {
+            value = parts[0];
+            units = parts[1];
+        }
+
+        long result = UNAVAILABLE;
+        try {
+            result = Long.parseLong(value);
+            if (units != null) {
+                // /proc/meminfo uses kB instead of KiB, incorrectly
+                if (units.equals("kB") || units.equals("KB")) {
+                    result = (long) new Size(result, Size.Unit.KiB).convertTo(Size.Unit.B).getValue();
+                } else {
+                    throw new NotImplementedException("unit conversion from " + units + " not implemented");
+                }
+            }
+        } catch (NumberFormatException nfe) {
+            logger.log(Level.WARNING, "error extracting memory info");
+        }
+
+        return result;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/host-memory/agent/src/test/java/com/redhat/thermostat/host/memory/agent/internal/ActivatorTest.java	Mon Jan 07 15:46:47 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.host.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.host.memory.common.MemoryStatDAO;
+import com.redhat.thermostat.test.StubBundleContext;
+
+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);
+        MemoryStatDAO cpuStatDAO = mock(MemoryStatDAO.class);
+
+        context.registerService(BackendService.class, service, null);
+        context.registerService(MemoryStatDAO.class, cpuStatDAO, null);
+
+        Activator activator = new Activator();
+
+        activator.start(context);
+
+        assertTrue(context.isServiceRegistered(Backend.class.getName(), HostMemoryBackend.class));
+        HostMemoryBackend 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/host-memory/agent/src/test/java/com/redhat/thermostat/host/memory/agent/internal/HostMemoryBackendTest.java	Mon Jan 07 15:46:47 2013 -0500
@@ -0,0 +1,93 @@
+/*
+ * 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.host.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.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+
+import com.redhat.thermostat.common.Version;
+import com.redhat.thermostat.host.memory.common.MemoryStatDAO;
+import com.redhat.thermostat.storage.model.MemoryStat;
+
+public class HostMemoryBackendTest {
+    
+    private HostMemoryBackend backend;
+    private ScheduledExecutorService executor;
+    private MemoryStatDAO memoryStatDao;
+
+    @Before
+    public void setup() {
+        executor = mock(ScheduledExecutorService.class);
+        memoryStatDao = mock(MemoryStatDAO.class);
+        
+        Version version = mock(Version.class);
+        when(version.getVersionNumber()).thenReturn("0.0.0");
+        
+        backend = new HostMemoryBackend(executor, memoryStatDao, version);
+    }
+
+    @Test
+    public void testStart() {
+        backend.activate();
+        ArgumentCaptor<Runnable> captor = ArgumentCaptor.forClass(Runnable.class);
+        verify(executor).scheduleAtFixedRate(captor.capture(), any(Long.class), any(Long.class), any(TimeUnit.class));
+        assertTrue(backend.isActive());
+        
+        Runnable runnable = captor.getValue();
+        runnable.run();
+        verify(memoryStatDao).putMemoryStat(any(MemoryStat.class));
+    }
+    
+    @Test
+    public void testStop() {
+        backend.activate();
+        backend.deactivate();
+        verify(executor).shutdown();
+        assertFalse(backend.isActive());
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/host-memory/agent/src/test/java/com/redhat/thermostat/host/memory/agent/internal/MemoryStatBuilderTest.java	Mon Jan 07 15:46:47 2013 -0500
@@ -0,0 +1,113 @@
+/*
+ * 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.host.memory.agent.internal;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import java.io.IOException;
+import java.io.StringReader;
+
+import org.junit.Test;
+
+import com.redhat.thermostat.storage.model.MemoryStat;
+import com.redhat.thermostat.utils.ProcDataSource;
+
+public class MemoryStatBuilderTest {
+
+    private static final int KILOBYTES_TO_BYTES = 1024;
+
+    @Test
+    public void testSimpleBuild() {
+        MemoryStat stat = new MemoryStatBuilder(new ProcDataSource()).build();
+        assertNotNull(stat);
+    }
+
+    @Test
+    public void testEmptyBuild() throws IOException {
+        String memory = "";
+        StringReader memoryReader = new StringReader(memory);
+        ProcDataSource dataSource = mock(ProcDataSource.class);
+        when(dataSource.getMemInfoReader()).thenReturn(memoryReader);
+
+        MemoryStat stat = new MemoryStatBuilder(dataSource).build();
+        assertNotNull(stat);
+        verify(dataSource).getMemInfoReader();
+    }
+
+    @Test
+    public void testBuild() throws IOException {
+        int i = 1;
+        final long TOTAL = i++;
+        final long FREE = i++;
+        final long BUFFERS = i++;
+        final long CACHED = i++;
+        final long COMMIT_LIMIT = i++;
+        final long SWAP_TOTAL = i++;
+        final long SWAP_FREE = i++;
+
+        String memory = "" +
+                "MemTotal: " + TOTAL + " kB\n" +
+                "MemFree:  " + FREE + " kB\n" +
+                "Buffers:" + BUFFERS + " kB\n" +
+                "Cached: " + CACHED + " kB\n" +
+                "CommitLimit: " + COMMIT_LIMIT + " kB\n" +
+                "SwapTotal: " + SWAP_TOTAL + " kB\n" +
+                "SwapFree: " + SWAP_FREE + " kB\n";
+
+        StringReader memoryReader = new StringReader(memory);
+        ProcDataSource dataSource = mock(ProcDataSource.class);
+        when(dataSource.getMemInfoReader()).thenReturn(memoryReader);
+
+        MemoryStat stat = new MemoryStatBuilder(dataSource).build();
+
+        assertEquals(BUFFERS * KILOBYTES_TO_BYTES, stat.getBuffers());
+        assertEquals(CACHED * KILOBYTES_TO_BYTES, stat.getCached());
+        assertEquals(COMMIT_LIMIT * KILOBYTES_TO_BYTES, stat.getCommitLimit());
+        assertEquals(FREE * KILOBYTES_TO_BYTES, stat.getFree());
+        assertEquals(SWAP_FREE * KILOBYTES_TO_BYTES, stat.getSwapFree());
+        assertEquals(SWAP_TOTAL * KILOBYTES_TO_BYTES, stat.getSwapTotal());
+        assertEquals(TOTAL * KILOBYTES_TO_BYTES, stat.getTotal());
+        assertTrue(stat.getTimeStamp() != 0 && stat.getTimeStamp() != Long.MIN_VALUE);
+        assertTrue(stat.getTimeStamp() <= System.currentTimeMillis());
+        verify(dataSource).getMemInfoReader();
+    }
+}
--- a/host-memory/client-core/pom.xml	Mon Jan 07 15:44:59 2013 -0500
+++ b/host-memory/client-core/pom.xml	Mon Jan 07 15:46:47 2013 -0500
@@ -71,5 +71,10 @@
       <version>${project.version}</version>
       <scope>test</scope>
     </dependency>
+    <dependency>
+      <groupId>com.redhat.thermostat</groupId>
+      <artifactId>thermostat-host-memory-common</artifactId>
+      <version>${project.version}</version>
+    </dependency>
   </dependencies>
 </project>
--- a/host-memory/client-core/src/main/java/com/redhat/thermostat/host/memory/client/core/HostMemoryService.java	Mon Jan 07 15:44:59 2013 -0500
+++ b/host-memory/client-core/src/main/java/com/redhat/thermostat/host/memory/client/core/HostMemoryService.java	Mon Jan 07 15:46:47 2013 -0500
@@ -43,9 +43,9 @@
 import com.redhat.thermostat.common.ApplicationService;
 import com.redhat.thermostat.common.dao.HostInfoDAO;
 import com.redhat.thermostat.common.dao.HostRef;
-import com.redhat.thermostat.common.dao.MemoryStatDAO;
 import com.redhat.thermostat.common.utils.OSGIUtils;
 import com.redhat.thermostat.host.memory.client.core.internal.HostMemoryController;
+import com.redhat.thermostat.host.memory.common.MemoryStatDAO;
 
 public class HostMemoryService implements InformationService<HostRef> {
     
--- a/host-memory/client-core/src/main/java/com/redhat/thermostat/host/memory/client/core/internal/Activator.java	Mon Jan 07 15:44:59 2013 -0500
+++ b/host-memory/client-core/src/main/java/com/redhat/thermostat/host/memory/client/core/internal/Activator.java	Mon Jan 07 15:46:47 2013 -0500
@@ -52,8 +52,8 @@
 import com.redhat.thermostat.common.MultipleServiceTracker.Action;
 import com.redhat.thermostat.common.dao.HostInfoDAO;
 import com.redhat.thermostat.common.dao.HostRef;
-import com.redhat.thermostat.common.dao.MemoryStatDAO;
 import com.redhat.thermostat.host.memory.client.core.HostMemoryService;
+import com.redhat.thermostat.host.memory.common.MemoryStatDAO;
 
 public class Activator implements BundleActivator {
     
--- a/host-memory/client-core/src/main/java/com/redhat/thermostat/host/memory/client/core/internal/HostMemoryController.java	Mon Jan 07 15:44:59 2013 -0500
+++ b/host-memory/client-core/src/main/java/com/redhat/thermostat/host/memory/client/core/internal/HostMemoryController.java	Mon Jan 07 15:46:47 2013 -0500
@@ -52,12 +52,12 @@
 import com.redhat.thermostat.common.Timer.SchedulingType;
 import com.redhat.thermostat.common.dao.HostInfoDAO;
 import com.redhat.thermostat.common.dao.HostRef;
-import com.redhat.thermostat.common.dao.MemoryStatDAO;
 import com.redhat.thermostat.common.locale.Translate;
 import com.redhat.thermostat.host.memory.client.core.HostMemoryView;
 import com.redhat.thermostat.host.memory.client.core.HostMemoryViewProvider;
 import com.redhat.thermostat.host.memory.client.core.HostMemoryView.GraphVisibilityChangeListener;
 import com.redhat.thermostat.host.memory.client.locale.LocaleResources;
+import com.redhat.thermostat.host.memory.common.MemoryStatDAO;
 import com.redhat.thermostat.storage.model.DiscreteTimeData;
 import com.redhat.thermostat.storage.model.MemoryStat;
 import com.redhat.thermostat.storage.model.MemoryType;
--- a/host-memory/client-core/src/test/java/com/redhat/thermostat/host/memory/client/core/internal/ActivatorTest.java	Mon Jan 07 15:44:59 2013 -0500
+++ b/host-memory/client-core/src/test/java/com/redhat/thermostat/host/memory/client/core/internal/ActivatorTest.java	Mon Jan 07 15:46:47 2013 -0500
@@ -46,8 +46,8 @@
 import com.redhat.thermostat.client.core.InformationService;
 import com.redhat.thermostat.common.ApplicationService;
 import com.redhat.thermostat.common.dao.HostInfoDAO;
-import com.redhat.thermostat.common.dao.MemoryStatDAO;
 import com.redhat.thermostat.host.memory.client.core.HostMemoryService;
+import com.redhat.thermostat.host.memory.common.MemoryStatDAO;
 import com.redhat.thermostat.test.StubBundleContext;
 
 public class ActivatorTest {
--- a/host-memory/client-core/src/test/java/com/redhat/thermostat/host/memory/client/core/internal/HostMemoryControllerTest.java	Mon Jan 07 15:44:59 2013 -0500
+++ b/host-memory/client-core/src/test/java/com/redhat/thermostat/host/memory/client/core/internal/HostMemoryControllerTest.java	Mon Jan 07 15:46:47 2013 -0500
@@ -58,10 +58,10 @@
 import com.redhat.thermostat.common.TimerFactory;
 import com.redhat.thermostat.common.dao.HostInfoDAO;
 import com.redhat.thermostat.common.dao.HostRef;
-import com.redhat.thermostat.common.dao.MemoryStatDAO;
 import com.redhat.thermostat.host.memory.client.core.HostMemoryView;
 import com.redhat.thermostat.host.memory.client.core.HostMemoryViewProvider;
 import com.redhat.thermostat.host.memory.client.core.internal.HostMemoryController;
+import com.redhat.thermostat.host.memory.common.MemoryStatDAO;
 import com.redhat.thermostat.storage.model.HostInfo;
 import com.redhat.thermostat.storage.model.MemoryStat;
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/host-memory/common/pom.xml	Mon Jan 07 15:46:47 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-host-memory</artifactId>
+    <groupId>com.redhat.thermostat</groupId>
+    <version>0.5.0-SNAPSHOT</version>
+  </parent>
+  <artifactId>thermostat-host-memory-common</artifactId>
+  <packaging>bundle</packaging>
+  <name>Thermostat Host 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.host.memory.common</Bundle-SymbolicName>
+            <Bundle-Activator>com.redhat.thermostat.host.memory.common.internal.Activator</Bundle-Activator>
+            <Export-Package>
+              com.redhat.thermostat.host.memory.common
+            </Export-Package>
+            <Private-Package>
+              com.redhat.thermostat.host.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/host-memory/common/src/main/java/com/redhat/thermostat/host/memory/common/MemoryStatDAO.java	Mon Jan 07 15:46:47 2013 -0500
@@ -0,0 +1,64 @@
+/*
+ * 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.host.memory.common;
+
+import java.util.List;
+
+import com.redhat.thermostat.common.dao.Countable;
+import com.redhat.thermostat.common.dao.HostRef;
+import com.redhat.thermostat.storage.core.Category;
+import com.redhat.thermostat.storage.core.Key;
+import com.redhat.thermostat.storage.model.MemoryStat;
+
+public interface MemoryStatDAO extends Countable {
+
+    static Key<Long> memoryTotalKey = new Key<>("total", false);
+    static Key<Long> memoryFreeKey = new Key<>("free", false);
+    static Key<Long> memoryBuffersKey = new Key<>("buffers", false);
+    static Key<Long> memoryCachedKey = new Key<>("cached", false);
+    static Key<Long> memorySwapTotalKey = new Key<>("swapTotal", false);
+    static Key<Long> memorySwapFreeKey = new Key<>("swapFree", false);
+    static Key<Long> memoryCommitLimitKey = new Key<>("commitLimit", false);
+
+    static final Category memoryStatCategory = new Category("memory-stats",
+            Key.AGENT_ID, Key.TIMESTAMP, memoryTotalKey, memoryFreeKey, memoryBuffersKey,
+            memoryCachedKey, memorySwapTotalKey, memorySwapFreeKey, memoryCommitLimitKey);
+
+    public List<MemoryStat> getLatestMemoryStats(HostRef ref, long since);
+
+    void putMemoryStat(MemoryStat stat);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/host-memory/common/src/main/java/com/redhat/thermostat/host/memory/common/internal/Activator.java	Mon Jan 07 15:46:47 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.host.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.host.memory.common.MemoryStatDAO;
+import com.redhat.thermostat.storage.core.Storage;
+
+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);
+                MemoryStatDAO memoryStatDao = new MemoryStatDAOImpl(storage);
+                reg = context.registerService(MemoryStatDAO.class.getName(), memoryStatDao, 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/host-memory/common/src/main/java/com/redhat/thermostat/host/memory/common/internal/MemoryStatDAOImpl.java	Mon Jan 07 15:46:47 2013 -0500
@@ -0,0 +1,73 @@
+/*
+ * 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.host.memory.common.internal;
+
+import java.util.List;
+
+import com.redhat.thermostat.common.dao.HostLatestPojoListGetter;
+import com.redhat.thermostat.common.dao.HostRef;
+import com.redhat.thermostat.host.memory.common.MemoryStatDAO;
+import com.redhat.thermostat.storage.core.Storage;
+import com.redhat.thermostat.storage.model.MemoryStat;
+
+public class MemoryStatDAOImpl implements MemoryStatDAO {
+
+    private final Storage storage;
+
+    private final HostLatestPojoListGetter<MemoryStat> getter;
+
+    MemoryStatDAOImpl(Storage storage) {
+        this.storage = storage;
+        storage.registerCategory(memoryStatCategory);
+        this.getter = new HostLatestPojoListGetter<>(storage, memoryStatCategory, MemoryStat.class);
+    }
+
+    @Override
+    public List<MemoryStat> getLatestMemoryStats(HostRef ref, long lastTimeStamp) {
+        return getter.getLatest(ref, lastTimeStamp);
+    }
+
+    @Override
+    public void putMemoryStat(MemoryStat stat) {
+        storage.putPojo(memoryStatCategory, false, stat);
+    }
+
+    @Override
+    public long getCount() {
+        return storage.getCount(memoryStatCategory);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/host-memory/common/src/test/java/com/redhat/thermostat/host/memory/common/internal/ActivatorTest.java	Mon Jan 07 15:46:47 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.host.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.host.memory.common.MemoryStatDAO;
+import com.redhat.thermostat.storage.core.Storage;
+import com.redhat.thermostat.test.StubBundleContext;
+
+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(MemoryStatDAO.class.getName(), MemoryStatDAOImpl.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/host-memory/common/src/test/java/com/redhat/thermostat/host/memory/common/internal/MemoryStatDAOTest.java	Mon Jan 07 15:46:47 2013 -0500
@@ -0,0 +1,153 @@
+/*
+ * 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.host.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.Collection;
+import java.util.List;
+
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+
+import com.redhat.thermostat.common.dao.HostRef;
+import com.redhat.thermostat.host.memory.common.MemoryStatDAO;
+import com.redhat.thermostat.storage.core.Category;
+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.Query.Criteria;
+import com.redhat.thermostat.storage.core.Storage;
+import com.redhat.thermostat.storage.model.MemoryStat;
+import com.redhat.thermostat.test.MockQuery;
+
+public class MemoryStatDAOTest {
+
+    private static long TIMESTAMP = 1;
+    private static long TOTAL = 2;
+    private static long FREE = 3;
+    private static long BUFFERS = 4;
+    private static long CACHED = 5;
+    private static long SWAP_TOTAL = 6;
+    private static long SWAP_FREE = 7;
+    private static long COMMIT_LIMIT = 8;
+
+    @Test
+    public void testCategory() {
+        assertEquals("memory-stats", MemoryStatDAO.memoryStatCategory.getName());
+        Collection<Key<?>> keys = MemoryStatDAO.memoryStatCategory.getKeys();
+        assertTrue(keys.contains(new Key<>("agentId", true)));
+        assertTrue(keys.contains(new Key<Long>("timeStamp", false)));
+        assertTrue(keys.contains(new Key<Long>("total", false)));
+        assertTrue(keys.contains(new Key<Long>("free", false)));
+        assertTrue(keys.contains(new Key<Long>("buffers", false)));
+        assertTrue(keys.contains(new Key<Long>("cached", false)));
+        assertTrue(keys.contains(new Key<Long>("swapTotal", false)));
+        assertTrue(keys.contains(new Key<Long>("swapFree", false)));
+        assertTrue(keys.contains(new Key<Long>("commitLimit", false)));
+        assertEquals(9, keys.size());
+    }
+
+    @Test
+    public void testGetLatestMemoryStats() {
+
+        MemoryStat memStat1 = new MemoryStat(TIMESTAMP, TOTAL, FREE, BUFFERS, CACHED, SWAP_TOTAL, SWAP_FREE, COMMIT_LIMIT);
+
+        @SuppressWarnings("unchecked")
+        Cursor<MemoryStat> cursor = mock(Cursor.class);
+        when(cursor.hasNext()).thenReturn(true).thenReturn(false);
+        when(cursor.next()).thenReturn(memStat1);
+
+        Storage storage = mock(Storage.class);
+        when(storage.createQuery()).then(new Answer<Query>() {
+            @Override
+            public Query answer(InvocationOnMock invocation) throws Throwable {
+                return new MockQuery();
+            }
+        });
+        when(storage.findAllPojos(any(Query.class), same(MemoryStat.class))).thenReturn(cursor);
+
+        HostRef hostRef = mock(HostRef.class);
+        when(hostRef.getAgentId()).thenReturn("system");
+
+        MemoryStatDAO dao = new MemoryStatDAOImpl(storage);
+        List<MemoryStat> memoryStats = dao.getLatestMemoryStats(hostRef, Long.MIN_VALUE);
+
+        ArgumentCaptor<MockQuery> arg = ArgumentCaptor.forClass(MockQuery.class);
+        verify(storage).findAllPojos(arg.capture(), same(MemoryStat.class));
+        assertTrue(arg.getValue().hasWhereClause(Key.TIMESTAMP, Criteria.GREATER_THAN, Long.MIN_VALUE));
+
+        assertEquals(1, memoryStats.size());
+        MemoryStat stat = memoryStats.get(0);
+
+        assertEquals(TIMESTAMP, stat.getTimeStamp());
+        assertEquals(TOTAL, stat.getTotal());
+        assertEquals(FREE, stat.getFree());
+        assertEquals(BUFFERS, stat.getBuffers());
+        assertEquals(CACHED, stat.getCached());
+        assertEquals(SWAP_TOTAL, stat.getSwapTotal());
+        assertEquals(SWAP_FREE, stat.getSwapFree());
+        assertEquals(COMMIT_LIMIT, stat.getCommitLimit());
+    }
+
+    @Test
+    public void testPutMemoryStat() {
+        Storage storage = mock(Storage.class);
+        MemoryStat stat = new MemoryStat(TIMESTAMP, TOTAL, FREE, BUFFERS, CACHED, SWAP_TOTAL, SWAP_FREE, COMMIT_LIMIT);
+        MemoryStatDAO dao = new MemoryStatDAOImpl(storage);
+        dao.putMemoryStat(stat);
+
+        verify(storage).putPojo(MemoryStatDAO.memoryStatCategory, false, stat);
+    }
+
+    @Test
+    public void testGetCount() {
+        Storage storage = mock(Storage.class);
+        when(storage.getCount(any(Category.class))).thenReturn(5L);
+        MemoryStatDAO dao = new MemoryStatDAOImpl(storage);
+        Long count = dao.getCount();
+        assertEquals((Long) 5L, count);
+    }
+}
--- a/host-memory/pom.xml	Mon Jan 07 15:44:59 2013 -0500
+++ b/host-memory/pom.xml	Mon Jan 07 15:46:47 2013 -0500
@@ -51,8 +51,10 @@
   <name>Thermostat Host Memory plugin</name>
 
   <modules>
+    <module>agent</module>
     <module>client-core</module>
     <module>client-swing</module>
+    <module>common</module>
   </modules>
 
 </project>
--- a/system-backend/src/main/java/com/redhat/thermostat/backend/system/MemoryStatBuilder.java	Mon Jan 07 15:44:59 2013 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,141 +0,0 @@
-/*
- * Copyright 2012 Red Hat, Inc.
- *
- * This file is part of Thermostat.
- *
- * Thermostat is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published
- * by the Free Software Foundation; either version 2, or (at your
- * option) any later version.
- *
- * Thermostat is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Thermostat; see the file COPYING.  If not see
- * <http://www.gnu.org/licenses/>.
- *
- * Linking this code with other modules is making a combined work
- * based on this code.  Thus, the terms and conditions of the GNU
- * General Public License cover the whole combination.
- *
- * As a special exception, the copyright holders of this code give
- * you permission to link this code with independent modules to
- * produce an executable, regardless of the license terms of these
- * independent modules, and to copy and distribute the resulting
- * executable under terms of your choice, provided that you also
- * meet, for each linked independent module, the terms and conditions
- * of the license of that module.  An independent module is a module
- * which is not derived from or based on this code.  If you modify
- * this code, you may extend this exception to your version of the
- * library, but you are not obligated to do so.  If you do not wish
- * to do so, delete this exception statement from your version.
- */
-
-package com.redhat.thermostat.backend.system;
-
-import java.io.BufferedReader;
-import java.io.IOException;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-import com.redhat.thermostat.common.NotImplementedException;
-import com.redhat.thermostat.common.Size;
-import com.redhat.thermostat.common.utils.LoggingUtils;
-import com.redhat.thermostat.storage.model.MemoryStat;
-import com.redhat.thermostat.utils.ProcDataSource;
-
-/**
- * Implementation note: uses information from /proc/
- */
-public class MemoryStatBuilder {
-
-    private static final long UNAVAILABLE = -1;
-
-    private static final String KEY_MEMORY_TOTAL = "MemTotal";
-    private static final String KEY_MEMORY_FREE = "MemFree";
-    private static final String KEY_BUFFERS = "Buffers";
-    private static final String KEY_CACHED = "Cached";
-    private static final String KEY_SWAP_TOTAL = "SwapTotal";
-    private static final String KEY_SWAP_FREE = "SwapFree";
-    private static final String KEY_COMMIT_LIMIT = "CommitLimit";
-
-    private static final Logger logger = LoggingUtils.getLogger(MemoryStatBuilder.class);
-
-    private final ProcDataSource dataSource;
-
-    public MemoryStatBuilder(ProcDataSource dataSource) {
-        this.dataSource = dataSource;
-    }
-
-    protected MemoryStat build() {
-        long timestamp = System.currentTimeMillis();
-
-        long total = UNAVAILABLE;
-        long free = UNAVAILABLE;
-        long swapTotal = UNAVAILABLE;
-        long swapFree = UNAVAILABLE;
-        long buffers = UNAVAILABLE;
-        long cached = UNAVAILABLE;
-        long commitLimit = UNAVAILABLE;
-
-        try (BufferedReader reader = new BufferedReader(dataSource.getMemInfoReader())) {
-            String line = null;
-            while ((line = reader.readLine()) != null) {
-                String[] parts = line.split(":");
-                if (parts.length == 2) {
-                    String key = parts[0].trim();
-                    long value = getValue(parts[1].trim());
-                    if (key.equals(KEY_MEMORY_TOTAL)) {
-                        total = value;
-                    } else if (key.equals(KEY_MEMORY_FREE)) {
-                        free = value;
-                    } else if (key.equals(KEY_SWAP_TOTAL)) {
-                        swapTotal = value;
-                    } else if (key.equals(KEY_SWAP_FREE)) {
-                        swapFree = value;
-                    } else if (key.equals(KEY_BUFFERS)) {
-                        buffers = value;
-                    } else if (key.equals(KEY_CACHED)) {
-                        cached = value;
-                    } else if (key.equals(KEY_COMMIT_LIMIT)) {
-                        commitLimit = value;
-                    }
-                }
-            }
-        } catch (IOException ioe) {
-            logger.log(Level.WARNING, "unable to read memory info");
-        }
-
-        return new MemoryStat(timestamp, total, free, buffers, cached, swapTotal, swapFree, commitLimit);
-    }
-
-    private long getValue(String rawValue) {
-        String[] parts = rawValue.split(" +");
-        String value = rawValue;
-        String units = null;
-        if (parts.length > 1) {
-            value = parts[0];
-            units = parts[1];
-        }
-
-        long result = UNAVAILABLE;
-        try {
-            result = Long.parseLong(value);
-            if (units != null) {
-                // /proc/meminfo uses kB instead of KiB, incorrectly
-                if (units.equals("kB") || units.equals("KB")) {
-                    result = (long) new Size(result, Size.Unit.KiB).convertTo(Size.Unit.B).getValue();
-                } else {
-                    throw new NotImplementedException("unit conversion from " + units + " not implemented");
-                }
-            }
-        } catch (NumberFormatException nfe) {
-            logger.log(Level.WARNING, "error extracting memory info");
-        }
-
-        return result;
-    }
-}
--- a/system-backend/src/main/java/com/redhat/thermostat/backend/system/SystemBackend.java	Mon Jan 07 15:44:59 2013 -0500
+++ b/system-backend/src/main/java/com/redhat/thermostat/backend/system/SystemBackend.java	Mon Jan 07 15:46:47 2013 -0500
@@ -56,7 +56,6 @@
 import com.redhat.thermostat.common.Clock;
 import com.redhat.thermostat.common.SystemClock;
 import com.redhat.thermostat.common.dao.HostInfoDAO;
-import com.redhat.thermostat.common.dao.MemoryStatDAO;
 import com.redhat.thermostat.common.dao.NetworkInterfaceInfoDAO;
 import com.redhat.thermostat.common.dao.VmCpuStatDAO;
 import com.redhat.thermostat.common.utils.LoggingUtils;
@@ -70,7 +69,6 @@
     private static final Logger logger = LoggingUtils.getLogger(SystemBackend.class);
 
     private HostInfoDAO hostInfos;
-    private MemoryStatDAO memoryStats;
     private VmCpuStatDAO vmCpuStats;
     private NetworkInterfaceInfoDAO networkInterfaces;
 
@@ -86,7 +84,6 @@
 
     private final VmCpuStatBuilder vmCpuBuilder;
     private final HostInfoBuilder hostInfoBuilder;
-    private final MemoryStatBuilder memoryStatBuilder;
 
     public SystemBackend() {
         super(new BackendID("System Backend", SystemBackend.class.getName()));
@@ -100,7 +97,6 @@
         long ticksPerSecond = SysConf.getClockTicksPerSecond();
         ProcDataSource source = new ProcDataSource();
         hostInfoBuilder = new HostInfoBuilder(source);
-        memoryStatBuilder = new MemoryStatBuilder(source);
 
         int cpuCount = hostInfoBuilder.getCpuInfo().count;
         vmCpuBuilder = new VmCpuStatBuilder(clock, cpuCount, ticksPerSecond, builder);
@@ -109,7 +105,6 @@
     @Override
     protected void setDAOFactoryAction() {
         hostInfos = df.getHostInfoDAO();
-        memoryStats = df.getMemoryStatDAO();
         vmCpuStats = df.getVmCpuStatDAO();
         networkInterfaces = df.getNetworkInterfaceInfoDAO();
         hostListener = new JvmStatHostListener(df.getVmInfoDAO(), df.getVmMemoryStatDAO(), df.getVmGcStatDAO(), df.getVmClassStatsDAO(), getObserveNewJvm());
@@ -138,7 +133,6 @@
                 for (NetworkInterfaceInfo info: NetworkInfoBuilder.build()) {
                     networkInterfaces.putNetworkInterfaceInfo(info);
                 }
-                memoryStats.putMemoryStat(memoryStatBuilder.build());
 
                 for (Integer pid : pidsToMonitor) {
                     if (vmCpuBuilder.knowsAbout(pid)) {
--- a/system-backend/src/test/java/com/redhat/thermostat/backend/system/MemoryStatBuilderTest.java	Mon Jan 07 15:44:59 2013 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,113 +0,0 @@
-/*
- * Copyright 2012 Red Hat, Inc.
- *
- * This file is part of Thermostat.
- *
- * Thermostat is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published
- * by the Free Software Foundation; either version 2, or (at your
- * option) any later version.
- *
- * Thermostat is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Thermostat; see the file COPYING.  If not see
- * <http://www.gnu.org/licenses/>.
- *
- * Linking this code with other modules is making a combined work
- * based on this code.  Thus, the terms and conditions of the GNU
- * General Public License cover the whole combination.
- *
- * As a special exception, the copyright holders of this code give
- * you permission to link this code with independent modules to
- * produce an executable, regardless of the license terms of these
- * independent modules, and to copy and distribute the resulting
- * executable under terms of your choice, provided that you also
- * meet, for each linked independent module, the terms and conditions
- * of the license of that module.  An independent module is a module
- * which is not derived from or based on this code.  If you modify
- * this code, you may extend this exception to your version of the
- * library, but you are not obligated to do so.  If you do not wish
- * to do so, delete this exception statement from your version.
- */
-
-package com.redhat.thermostat.backend.system;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import java.io.IOException;
-import java.io.StringReader;
-
-import org.junit.Test;
-
-import com.redhat.thermostat.storage.model.MemoryStat;
-import com.redhat.thermostat.utils.ProcDataSource;
-
-public class MemoryStatBuilderTest {
-
-    private static final int KILOBYTES_TO_BYTES = 1024;
-
-    @Test
-    public void testSimpleBuild() {
-        MemoryStat stat = new MemoryStatBuilder(new ProcDataSource()).build();
-        assertNotNull(stat);
-    }
-
-    @Test
-    public void testEmptyBuild() throws IOException {
-        String memory = "";
-        StringReader memoryReader = new StringReader(memory);
-        ProcDataSource dataSource = mock(ProcDataSource.class);
-        when(dataSource.getMemInfoReader()).thenReturn(memoryReader);
-
-        MemoryStat stat = new MemoryStatBuilder(dataSource).build();
-        assertNotNull(stat);
-        verify(dataSource).getMemInfoReader();
-    }
-
-    @Test
-    public void testBuild() throws IOException {
-        int i = 1;
-        final long TOTAL = i++;
-        final long FREE = i++;
-        final long BUFFERS = i++;
-        final long CACHED = i++;
-        final long COMMIT_LIMIT = i++;
-        final long SWAP_TOTAL = i++;
-        final long SWAP_FREE = i++;
-
-        String memory = "" +
-                "MemTotal: " + TOTAL + " kB\n" +
-                "MemFree:  " + FREE + " kB\n" +
-                "Buffers:" + BUFFERS + " kB\n" +
-                "Cached: " + CACHED + " kB\n" +
-                "CommitLimit: " + COMMIT_LIMIT + " kB\n" +
-                "SwapTotal: " + SWAP_TOTAL + " kB\n" +
-                "SwapFree: " + SWAP_FREE + " kB\n";
-
-        StringReader memoryReader = new StringReader(memory);
-        ProcDataSource dataSource = mock(ProcDataSource.class);
-        when(dataSource.getMemInfoReader()).thenReturn(memoryReader);
-
-        MemoryStat stat = new MemoryStatBuilder(dataSource).build();
-
-        assertEquals(BUFFERS * KILOBYTES_TO_BYTES, stat.getBuffers());
-        assertEquals(CACHED * KILOBYTES_TO_BYTES, stat.getCached());
-        assertEquals(COMMIT_LIMIT * KILOBYTES_TO_BYTES, stat.getCommitLimit());
-        assertEquals(FREE * KILOBYTES_TO_BYTES, stat.getFree());
-        assertEquals(SWAP_FREE * KILOBYTES_TO_BYTES, stat.getSwapFree());
-        assertEquals(SWAP_TOTAL * KILOBYTES_TO_BYTES, stat.getSwapTotal());
-        assertEquals(TOTAL * KILOBYTES_TO_BYTES, stat.getTotal());
-        assertTrue(stat.getTimeStamp() != 0 && stat.getTimeStamp() != Long.MIN_VALUE);
-        assertTrue(stat.getTimeStamp() <= System.currentTimeMillis());
-        verify(dataSource).getMemInfoReader();
-    }
-}
--- a/system-backend/src/test/java/com/redhat/thermostat/backend/system/SystemBackendTest.java	Mon Jan 07 15:44:59 2013 -0500
+++ b/system-backend/src/test/java/com/redhat/thermostat/backend/system/SystemBackendTest.java	Mon Jan 07 15:46:47 2013 -0500
@@ -46,7 +46,6 @@
 
 import com.redhat.thermostat.common.dao.DAOFactory;
 import com.redhat.thermostat.common.dao.HostInfoDAO;
-import com.redhat.thermostat.common.dao.MemoryStatDAO;
 import com.redhat.thermostat.common.dao.NetworkInterfaceInfoDAO;
 import com.redhat.thermostat.common.dao.VmCpuStatDAO;
 import com.redhat.thermostat.storage.core.Storage;
@@ -59,13 +58,11 @@
     public void setUp() {
         Storage s = mock(Storage.class);
         HostInfoDAO hDAO = mock(HostInfoDAO.class);
-        MemoryStatDAO mDAO = mock(MemoryStatDAO.class);
         VmCpuStatDAO vDAO = mock(VmCpuStatDAO.class);
         NetworkInterfaceInfoDAO nDAO = mock(NetworkInterfaceInfoDAO.class);
         DAOFactory df = mock(DAOFactory.class);
         when(df.getStorage()).thenReturn(s);
         when(df.getHostInfoDAO()).thenReturn(hDAO);
-        when(df.getMemoryStatDAO()).thenReturn(mDAO);
         when(df.getVmCpuStatDAO()).thenReturn(vDAO);
         when(df.getNetworkInterfaceInfoDAO()).thenReturn(nDAO);
         b = new SystemBackend();