changeset 893:9e6bcfc40ea1

Create VM CPU agent and common bundles This commit extracts VM CPU data collection from SystemBackend into a vm-cpu-agent bundle. It also moves the VmCpuStatDAO from common-core into a vm-cpu-common bundle that registers the DAO once Storage is available. This also removes the DAO from DAOFactory. One side-effect is changing the visibility of VmLatestPojoListGetter to public in order for this DAO implementation (and DAOs in the other commits in this series) to access it. This also temporarily adds the vm-cpu-common bundle as a dependency to client-cli until my later dynamic vm-stat patch. Reviewed-by: omajid Review-thread: http://icedtea.classpath.org/pipermail/thermostat/2013-January/004996.html
author Elliott Baron <ebaron@redhat.com>
date Mon, 07 Jan 2013 15:48:38 -0500
parents 4c8a876b9dc0
children 0ba74f790a8a
files client/cli/pom.xml client/cli/src/main/java/com/redhat/thermostat/client/cli/internal/VMStatCommand.java client/cli/src/main/java/com/redhat/thermostat/client/cli/internal/VMStatPrinter.java client/cli/src/test/java/com/redhat/thermostat/client/cli/internal/VmStatCommandTest.java common/core/src/main/java/com/redhat/thermostat/common/dao/DAOFactory.java common/core/src/main/java/com/redhat/thermostat/common/dao/DAOFactoryImpl.java common/core/src/main/java/com/redhat/thermostat/common/dao/VmCpuStatDAO.java common/core/src/main/java/com/redhat/thermostat/common/dao/VmCpuStatDAOImpl.java common/core/src/main/java/com/redhat/thermostat/common/dao/VmLatestPojoListGetter.java common/core/src/test/java/com/redhat/thermostat/common/dao/MongoDAOFactoryTest.java common/core/src/test/java/com/redhat/thermostat/common/dao/VmCpuStatDAOTest.java distribution/config/commands/agent.properties distribution/config/commands/gui.properties distribution/config/commands/vm-stat.properties distribution/pom.xml eclipse/com.redhat.thermostat.client.feature/feature.xml system-backend/src/main/java/com/redhat/thermostat/backend/system/JvmStatHostListener.java system-backend/src/main/java/com/redhat/thermostat/backend/system/JvmStatVmListener.java system-backend/src/main/java/com/redhat/thermostat/backend/system/ProcessStatusInfo.java system-backend/src/main/java/com/redhat/thermostat/backend/system/ProcessStatusInfoBuilder.java system-backend/src/main/java/com/redhat/thermostat/backend/system/SystemBackend.java system-backend/src/main/java/com/redhat/thermostat/backend/system/VmCpuStatBuilder.java system-backend/src/test/java/com/redhat/thermostat/backend/system/ProcessStatusInfoBuilderTest.java system-backend/src/test/java/com/redhat/thermostat/backend/system/SystemBackendTest.java system-backend/src/test/java/com/redhat/thermostat/backend/system/VmCpuStatBuilderTest.java vm-cpu/agent/pom.xml vm-cpu/agent/src/main/java/com/redhat/thermostat/vm/cpu/agent/internal/Activator.java vm-cpu/agent/src/main/java/com/redhat/thermostat/vm/cpu/agent/internal/ProcessStatusInfo.java vm-cpu/agent/src/main/java/com/redhat/thermostat/vm/cpu/agent/internal/ProcessStatusInfoBuilder.java vm-cpu/agent/src/main/java/com/redhat/thermostat/vm/cpu/agent/internal/VmCpuBackend.java vm-cpu/agent/src/main/java/com/redhat/thermostat/vm/cpu/agent/internal/VmCpuHostListener.java vm-cpu/agent/src/main/java/com/redhat/thermostat/vm/cpu/agent/internal/VmCpuStatBuilder.java vm-cpu/agent/src/test/java/com/redhat/thermostat/vm/cpu/agent/internal/ActivatorTest.java vm-cpu/agent/src/test/java/com/redhat/thermostat/vm/cpu/agent/internal/ProcessStatusInfoBuilderTest.java vm-cpu/agent/src/test/java/com/redhat/thermostat/vm/cpu/agent/internal/VmCpuBackendTest.java vm-cpu/agent/src/test/java/com/redhat/thermostat/vm/cpu/agent/internal/VmCpuHostListenerTest.java vm-cpu/agent/src/test/java/com/redhat/thermostat/vm/cpu/agent/internal/VmCpuStatBuilderTest.java vm-cpu/client-core/pom.xml vm-cpu/client-core/src/main/java/com/redhat/thermostat/vm/cpu/client/core/VmCpuService.java vm-cpu/client-core/src/main/java/com/redhat/thermostat/vm/cpu/client/core/internal/Activator.java vm-cpu/client-core/src/main/java/com/redhat/thermostat/vm/cpu/client/core/internal/VmCpuController.java vm-cpu/client-core/src/test/java/com/redhat/thermostat/vm/cpu/client/core/internal/ActivatorTest.java vm-cpu/client-core/src/test/java/com/redhat/thermostat/vm/cpu/client/core/internal/VmCpuControllerTest.java vm-cpu/common/pom.xml vm-cpu/common/src/main/java/com/redhat/thermostat/vm/cpu/common/VmCpuStatDAO.java vm-cpu/common/src/main/java/com/redhat/thermostat/vm/cpu/common/internal/Activator.java vm-cpu/common/src/main/java/com/redhat/thermostat/vm/cpu/common/internal/VmCpuStatDAOImpl.java vm-cpu/common/src/test/java/com/redhat/thermostat/vm/cpu/common/internal/ActivatorTest.java vm-cpu/common/src/test/java/com/redhat/thermostat/vm/cpu/common/internal/VmCpuStatDAOTest.java vm-cpu/pom.xml
diffstat 50 files changed, 1989 insertions(+), 945 deletions(-) [+]
line wrap: on
line diff
--- a/client/cli/pom.xml	Mon Jan 07 15:46:47 2013 -0500
+++ b/client/cli/pom.xml	Mon Jan 07 15:48:38 2013 -0500
@@ -100,6 +100,12 @@
       <groupId>org.osgi</groupId>
       <artifactId>org.osgi.core</artifactId>
     </dependency>
+    <!-- Only temporary dependency -->
+    <dependency>
+      <groupId>com.redhat.thermostat</groupId>
+      <artifactId>thermostat-vm-cpu-common</artifactId>
+      <version>${project.version}</version>
+    </dependency>
   </dependencies>
 
   <build>
@@ -119,6 +125,8 @@
             </Private-Package>
             <!-- Do not autogenerate uses clauses in Manifests -->
             <_nouses>true</_nouses>
+            <!-- Remove this later -->
+            <Import-Package>*,com.redhat.thermostat.vm.cpu.common;resolution:=optional</Import-Package>
           </instructions>
         </configuration>
       </plugin>
--- a/client/cli/src/main/java/com/redhat/thermostat/client/cli/internal/VMStatCommand.java	Mon Jan 07 15:46:47 2013 -0500
+++ b/client/cli/src/main/java/com/redhat/thermostat/client/cli/internal/VMStatCommand.java	Mon Jan 07 15:48:38 2013 -0500
@@ -48,12 +48,12 @@
 import com.redhat.thermostat.common.cli.CommandException;
 import com.redhat.thermostat.common.cli.HostVMArguments;
 import com.redhat.thermostat.common.cli.SimpleCommand;
-import com.redhat.thermostat.common.dao.VmCpuStatDAO;
 import com.redhat.thermostat.common.dao.VmMemoryStatDAO;
 import com.redhat.thermostat.common.dao.VmRef;
 import com.redhat.thermostat.common.locale.Translate;
 import com.redhat.thermostat.common.utils.LoggingUtils;
 import com.redhat.thermostat.common.utils.OSGIUtils;
+import com.redhat.thermostat.vm.cpu.common.VmCpuStatDAO;
 
 public class VMStatCommand extends SimpleCommand {
 
--- a/client/cli/src/main/java/com/redhat/thermostat/client/cli/internal/VMStatPrinter.java	Mon Jan 07 15:46:47 2013 -0500
+++ b/client/cli/src/main/java/com/redhat/thermostat/client/cli/internal/VMStatPrinter.java	Mon Jan 07 15:48:38 2013 -0500
@@ -48,7 +48,6 @@
 
 import com.redhat.thermostat.common.Size;
 import com.redhat.thermostat.common.cli.TableRenderer;
-import com.redhat.thermostat.common.dao.VmCpuStatDAO;
 import com.redhat.thermostat.common.dao.VmMemoryStatDAO;
 import com.redhat.thermostat.common.dao.VmRef;
 import com.redhat.thermostat.common.locale.Translate;
@@ -57,6 +56,7 @@
 import com.redhat.thermostat.storage.model.TimeStampedPojoCorrelator;
 import com.redhat.thermostat.storage.model.VmCpuStat;
 import com.redhat.thermostat.storage.model.VmMemoryStat;
+import com.redhat.thermostat.vm.cpu.common.VmCpuStatDAO;
 
 class VMStatPrinter {
 
--- a/client/cli/src/test/java/com/redhat/thermostat/client/cli/internal/VmStatCommandTest.java	Mon Jan 07 15:46:47 2013 -0500
+++ b/client/cli/src/test/java/com/redhat/thermostat/client/cli/internal/VmStatCommandTest.java	Mon Jan 07 15:48:38 2013 -0500
@@ -64,7 +64,6 @@
 import com.redhat.thermostat.common.cli.CommandException;
 import com.redhat.thermostat.common.cli.SimpleArguments;
 import com.redhat.thermostat.common.dao.HostRef;
-import com.redhat.thermostat.common.dao.VmCpuStatDAO;
 import com.redhat.thermostat.common.dao.VmMemoryStatDAO;
 import com.redhat.thermostat.common.dao.VmRef;
 import com.redhat.thermostat.common.utils.OSGIUtils;
@@ -74,6 +73,7 @@
 import com.redhat.thermostat.storage.model.VmMemoryStat.Space;
 import com.redhat.thermostat.test.TestCommandContextFactory;
 import com.redhat.thermostat.test.TestTimerFactory;
+import com.redhat.thermostat.vm.cpu.common.VmCpuStatDAO;
 
 public class VmStatCommandTest {
 
--- a/common/core/src/main/java/com/redhat/thermostat/common/dao/DAOFactory.java	Mon Jan 07 15:46:47 2013 -0500
+++ b/common/core/src/main/java/com/redhat/thermostat/common/dao/DAOFactory.java	Mon Jan 07 15:48:38 2013 -0500
@@ -56,8 +56,6 @@
 
     public VmInfoDAO getVmInfoDAO();
 
-    public VmCpuStatDAO getVmCpuStatDAO();
-
     public VmMemoryStatDAO getVmMemoryStatDAO();
 
     public VmClassStatDAO getVmClassStatsDAO();
--- a/common/core/src/main/java/com/redhat/thermostat/common/dao/DAOFactoryImpl.java	Mon Jan 07 15:46:47 2013 -0500
+++ b/common/core/src/main/java/com/redhat/thermostat/common/dao/DAOFactoryImpl.java	Mon Jan 07 15:48:38 2013 -0500
@@ -59,7 +59,6 @@
     private HostInfoDAO hostInfoDAO;
     private NetworkInterfaceInfoDAO networkInfoDAO;
     private VmInfoDAO vmInfoDAO;
-    private VmCpuStatDAO vmCpuStatDAO;
     private VmClassStatDAO vmClassStatDAO;
     private VmMemoryStatDAO vmMemStatDAO;
     private VmGcStatDAO vmGcStatDAO;
@@ -109,12 +108,6 @@
     }
 
     @Override
-    public VmCpuStatDAO getVmCpuStatDAO() {
-        ensureStorageConnected();
-        return vmCpuStatDAO;
-    }
-
-    @Override
     public VmMemoryStatDAO getVmMemoryStatDAO() {
         ensureStorageConnected();
         return vmMemStatDAO;
@@ -157,7 +150,6 @@
 
         registerAndRecordService(VmInfoDAO.class, getVmInfoDAO());
         registerAndRecordService(VmClassStatDAO.class, getVmClassStatsDAO());
-        registerAndRecordService(VmCpuStatDAO.class, getVmCpuStatDAO());
         registerAndRecordService(VmGcStatDAO.class, getVmGcStatDAO());
         registerAndRecordService(VmMemoryStatDAO.class, getVmMemoryStatDAO());
     }
@@ -171,7 +163,6 @@
         hostInfoDAO = new HostInfoDAOImpl(storage, agentDAO);
         networkInfoDAO = new NetworkInterfaceInfoDAOImpl(storage);
         vmInfoDAO = new VmInfoDAOImpl(storage);
-        vmCpuStatDAO = new VmCpuStatDAOImpl(storage);
         vmClassStatDAO = new VmClassStatDAOImpl(storage);
         vmMemStatDAO = new VmMemoryStatDAOImpl(storage);
         vmGcStatDAO = new VmGcStatDAOImpl(storage);
--- a/common/core/src/main/java/com/redhat/thermostat/common/dao/VmCpuStatDAO.java	Mon Jan 07 15:46:47 2013 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,56 +0,0 @@
-/*
- * Copyright 2012 Red Hat, Inc.
- *
- * This file is part of Thermostat.
- *
- * Thermostat is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published
- * by the Free Software Foundation; either version 2, or (at your
- * option) any later version.
- *
- * Thermostat is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Thermostat; see the file COPYING.  If not see
- * <http://www.gnu.org/licenses/>.
- *
- * Linking this code with other modules is making a combined work
- * based on this code.  Thus, the terms and conditions of the GNU
- * General Public License cover the whole combination.
- *
- * As a special exception, the copyright holders of this code give
- * you permission to link this code with independent modules to
- * produce an executable, regardless of the license terms of these
- * independent modules, and to copy and distribute the resulting
- * executable under terms of your choice, provided that you also
- * meet, for each linked independent module, the terms and conditions
- * of the license of that module.  An independent module is a module
- * which is not derived from or based on this code.  If you modify
- * this code, you may extend this exception to your version of the
- * library, but you are not obligated to do so.  If you do not wish
- * to do so, delete this exception statement from your version.
- */
-
-package com.redhat.thermostat.common.dao;
-
-import java.util.List;
-
-import com.redhat.thermostat.storage.core.Category;
-import com.redhat.thermostat.storage.core.Key;
-import com.redhat.thermostat.storage.model.VmCpuStat;
-
-public interface VmCpuStatDAO {
-
-    static final Key<Double> vmCpuLoadKey = new Key<>("cpuLoad", false);
-
-    static final Category vmCpuStatCategory = new Category("vm-cpu-stats",
-            Key.AGENT_ID, Key.VM_ID, Key.TIMESTAMP, vmCpuLoadKey);
-
-    public abstract List<VmCpuStat> getLatestVmCpuStats(VmRef ref, long since);
-
-    public abstract void putVmCpuStat(VmCpuStat stat);
-
-}
--- a/common/core/src/main/java/com/redhat/thermostat/common/dao/VmCpuStatDAOImpl.java	Mon Jan 07 15:46:47 2013 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,64 +0,0 @@
-/*
- * Copyright 2012 Red Hat, Inc.
- *
- * This file is part of Thermostat.
- *
- * Thermostat is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published
- * by the Free Software Foundation; either version 2, or (at your
- * option) any later version.
- *
- * Thermostat is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Thermostat; see the file COPYING.  If not see
- * <http://www.gnu.org/licenses/>.
- *
- * Linking this code with other modules is making a combined work
- * based on this code.  Thus, the terms and conditions of the GNU
- * General Public License cover the whole combination.
- *
- * As a special exception, the copyright holders of this code give
- * you permission to link this code with independent modules to
- * produce an executable, regardless of the license terms of these
- * independent modules, and to copy and distribute the resulting
- * executable under terms of your choice, provided that you also
- * meet, for each linked independent module, the terms and conditions
- * of the license of that module.  An independent module is a module
- * which is not derived from or based on this code.  If you modify
- * this code, you may extend this exception to your version of the
- * library, but you are not obligated to do so.  If you do not wish
- * to do so, delete this exception statement from your version.
- */
-
-package com.redhat.thermostat.common.dao;
-
-import java.util.List;
-
-import com.redhat.thermostat.storage.core.Storage;
-import com.redhat.thermostat.storage.model.VmCpuStat;
-
-class VmCpuStatDAOImpl implements VmCpuStatDAO {
-
-    private final Storage storage;
-    private final VmLatestPojoListGetter<VmCpuStat> getter;
-
-    VmCpuStatDAOImpl(Storage storage) {
-        this.storage = storage;
-        storage.registerCategory(vmCpuStatCategory);
-        this.getter = new VmLatestPojoListGetter<>(storage, vmCpuStatCategory, VmCpuStat.class);
-    }
-
-    @Override
-    public List<VmCpuStat> getLatestVmCpuStats(VmRef ref, long since) {
-        return getter.getLatest(ref, since);
-    }
-
-    @Override
-    public void putVmCpuStat(VmCpuStat stat) {
-        storage.putPojo(vmCpuStatCategory, false, stat);
-    }
-}
--- a/common/core/src/main/java/com/redhat/thermostat/common/dao/VmLatestPojoListGetter.java	Mon Jan 07 15:46:47 2013 -0500
+++ b/common/core/src/main/java/com/redhat/thermostat/common/dao/VmLatestPojoListGetter.java	Mon Jan 07 15:48:38 2013 -0500
@@ -47,13 +47,13 @@
 import com.redhat.thermostat.storage.core.Query.Criteria;
 import com.redhat.thermostat.storage.model.TimeStampedPojo;
 
-class VmLatestPojoListGetter<T extends TimeStampedPojo> {
+public class VmLatestPojoListGetter<T extends TimeStampedPojo> {
 
     private final Storage storage;
     private final Category cat;
     private final Class<T> resultClass;
 
-    VmLatestPojoListGetter(Storage storage, Category cat, Class<T> resultClass) {
+    public VmLatestPojoListGetter(Storage storage, Category cat, Class<T> resultClass) {
         this.storage = storage;
         this.cat = cat;
         this.resultClass = resultClass;
--- a/common/core/src/test/java/com/redhat/thermostat/common/dao/MongoDAOFactoryTest.java	Mon Jan 07 15:46:47 2013 -0500
+++ b/common/core/src/test/java/com/redhat/thermostat/common/dao/MongoDAOFactoryTest.java	Mon Jan 07 15:48:38 2013 -0500
@@ -131,8 +131,8 @@
 
         daoFactory.registerDAOsAndStorageAsOSGiServices();
 
-        // currently 10 DAOs and Storage are registered
-        assertEquals(10, bundleContext.getAllServices().size());
+        // currently 9 DAOs and Storage are registered
+        assertEquals(9, bundleContext.getAllServices().size());
     }
 
     @Test
--- a/common/core/src/test/java/com/redhat/thermostat/common/dao/VmCpuStatDAOTest.java	Mon Jan 07 15:46:47 2013 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,137 +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.Before;
-import org.junit.Test;
-import org.mockito.ArgumentCaptor;
-import org.mockito.invocation.InvocationOnMock;
-import org.mockito.stubbing.Answer;
-
-import com.redhat.thermostat.storage.core.Cursor;
-import com.redhat.thermostat.storage.core.Key;
-import com.redhat.thermostat.storage.core.Query;
-import com.redhat.thermostat.storage.core.Storage;
-import com.redhat.thermostat.storage.core.Query.Criteria;
-import com.redhat.thermostat.storage.model.VmCpuStat;
-import com.redhat.thermostat.test.MockQuery;
-
-public class VmCpuStatDAOTest {
-
-    private static final Long TIMESTAMP = 1234L;
-    private static final Integer VM_ID = 321;
-    private static final Double CPU_LOAD = 9.9;
-
-    private VmCpuStat cpuStat;
-
-    @Before
-    public void setUp() {
-        cpuStat = new VmCpuStat(TIMESTAMP, VM_ID, CPU_LOAD);
-    }
-
-    @Test
-    public void testCategory() {
-        assertEquals("vm-cpu-stats", VmCpuStatDAO.vmCpuStatCategory.getName());
-        Collection<Key<?>> keys = VmCpuStatDAO.vmCpuStatCategory.getKeys();
-        assertTrue(keys.contains(new Key<>("agentId", true)));
-        assertTrue(keys.contains(new Key<Long>("timeStamp", false)));
-        assertTrue(keys.contains(new Key<Integer>("vmId", true)));
-        assertTrue(keys.contains(new Key<Integer>("cpuLoad", false)));
-        assertEquals(4, keys.size());
-    }
-
-    @Test
-    public void testGetLatestCpuStatsBasic() {
-
-        @SuppressWarnings("unchecked")
-        Cursor<VmCpuStat> cursor = mock(Cursor.class);
-        when(cursor.hasNext()).thenReturn(true).thenReturn(false);
-        when(cursor.next()).thenReturn(cpuStat);
-
-        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(VmCpuStat.class))).thenReturn(cursor);
-
-        HostRef hostRef = mock(HostRef.class);
-        when(hostRef.getAgentId()).thenReturn("system");
-
-        VmRef vmRef = mock(VmRef.class);
-        when(vmRef.getAgent()).thenReturn(hostRef);
-        when(vmRef.getId()).thenReturn(VM_ID);
-
-
-        VmCpuStatDAO dao = new VmCpuStatDAOImpl(storage);
-        List<VmCpuStat> vmCpuStats = dao.getLatestVmCpuStats(vmRef, Long.MIN_VALUE);
-
-        ArgumentCaptor<MockQuery> arg = ArgumentCaptor.forClass(MockQuery.class);
-        verify(storage).findAllPojos(arg.capture(), same(VmCpuStat.class));
-        assertTrue(arg.getValue().hasWhereClause(Key.TIMESTAMP, Criteria.GREATER_THAN, Long.MIN_VALUE));
-
-        assertEquals(1, vmCpuStats.size());
-        VmCpuStat stat = vmCpuStats.get(0);
-        assertEquals(TIMESTAMP, (Long) stat.getTimeStamp());
-        assertEquals(CPU_LOAD, stat.getCpuLoad(), 0.001);
-        assertEquals(VM_ID, (Integer) stat.getVmId());
-    }
-
-    @Test
-    public void testPutVmCpuStat() {
-        Storage storage = mock(Storage.class);
-        VmCpuStat stat = new VmCpuStat(TIMESTAMP, VM_ID, CPU_LOAD);
-        VmCpuStatDAO dao = new VmCpuStatDAOImpl(storage);
-        dao.putVmCpuStat(stat);
-
-        verify(storage).putPojo(VmCpuStatDAO.vmCpuStatCategory, false, stat);
-
-    }
-}
--- a/distribution/config/commands/agent.properties	Mon Jan 07 15:46:47 2013 -0500
+++ b/distribution/config/commands/agent.properties	Mon Jan 07 15:48:38 2013 -0500
@@ -13,6 +13,8 @@
           thermostat-host-cpu-agent-@project.version@.jar, \
           thermostat-host-memory-common-@project.version@.jar, \
           thermostat-host-memory-agent-@project.version@.jar, \
+          thermostat-vm-cpu-common-@project.version@.jar, \
+          thermostat-vm-cpu-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:46:47 2013 -0500
+++ b/distribution/config/commands/gui.properties	Mon Jan 07 15:48:38 2013 -0500
@@ -22,6 +22,7 @@
           thermostat-host-memory-client-swing-@project.version@.jar, \
           thermostat-vm-overview-client-core-@project.version@.jar, \
           thermostat-vm-overview-client-swing-@project.version@.jar, \
+          thermostat-vm-cpu-common-@project.version@.jar, \
           thermostat-vm-cpu-client-core-@project.version@.jar, \
           thermostat-vm-cpu-client-swing-@project.version@.jar, \
           thermostat-vm-gc-client-core-@project.version@.jar, \
--- a/distribution/config/commands/vm-stat.properties	Mon Jan 07 15:46:47 2013 -0500
+++ b/distribution/config/commands/vm-stat.properties	Mon Jan 07 15:48:38 2013 -0500
@@ -1,4 +1,5 @@
 bundles = thermostat-client-cli-${project.version}.jar, \
+          thermostat-vm-cpu-common-${project.version}.jar, \
           thermostat-storage-mongodb-${project.version}.jar, \
           thermostat-web-common-${project.version}.jar, \
           thermostat-web-client-${project.version}.jar, \
--- a/distribution/pom.xml	Mon Jan 07 15:46:47 2013 -0500
+++ b/distribution/pom.xml	Mon Jan 07 15:48:38 2013 -0500
@@ -412,6 +412,11 @@
     </dependency>
     <dependency>
       <groupId>com.redhat.thermostat</groupId>
+      <artifactId>thermostat-vm-cpu-agent</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>com.redhat.thermostat</groupId>
       <artifactId>thermostat-vm-gc-client-core</artifactId>
       <version>${project.version}</version>
     </dependency>
--- a/eclipse/com.redhat.thermostat.client.feature/feature.xml	Mon Jan 07 15:46:47 2013 -0500
+++ b/eclipse/com.redhat.thermostat.client.feature/feature.xml	Mon Jan 07 15:48:38 2013 -0500
@@ -196,4 +196,11 @@
          version="0.0.0"
          unpack="false"/>
 
+   <plugin
+         id="com.redhat.thermostat.vm.cpu.common"
+         download-size="0"
+         install-size="0"
+         version="0.0.0"
+         unpack="false"/>
+
 </feature>
--- a/system-backend/src/main/java/com/redhat/thermostat/backend/system/JvmStatHostListener.java	Mon Jan 07 15:46:47 2013 -0500
+++ b/system-backend/src/main/java/com/redhat/thermostat/backend/system/JvmStatHostListener.java	Mon Jan 07 15:48:38 2013 -0500
@@ -37,7 +37,6 @@
 package com.redhat.thermostat.backend.system;
 
 import java.net.URISyntaxException;
-import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
--- a/system-backend/src/main/java/com/redhat/thermostat/backend/system/JvmStatVmListener.java	Mon Jan 07 15:46:47 2013 -0500
+++ b/system-backend/src/main/java/com/redhat/thermostat/backend/system/JvmStatVmListener.java	Mon Jan 07 15:48:38 2013 -0500
@@ -36,8 +36,6 @@
 
 package com.redhat.thermostat.backend.system;
 
-import java.util.ArrayList;
-import java.util.List;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 
--- a/system-backend/src/main/java/com/redhat/thermostat/backend/system/ProcessStatusInfo.java	Mon Jan 07 15:46:47 2013 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,73 +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;
-
-public class ProcessStatusInfo {
-
-    /* All times are measured in clock ticks */
-
-    private final int pid;
-    private final long userTime;
-    private final long kernelTime;
-
-    public ProcessStatusInfo(int pid, long userTime, long kernelTime) {
-        this.pid = pid;
-        this.userTime = userTime;
-        this.kernelTime = kernelTime;
-    }
-
-    public int getPid() {
-        return pid;
-    }
-
-    /**
-     * @return the time this process has spent in user-mode as a number of
-     * kernel ticks
-     */
-    public long getUserTime() {
-        return userTime;
-    }
-
-    /**
-     * @return the time this process spent in kernel-mode as a number of kernel
-     * ticks
-     */
-    public long getKernelTime() {
-        return kernelTime;
-    }
-
-}
--- a/system-backend/src/main/java/com/redhat/thermostat/backend/system/ProcessStatusInfoBuilder.java	Mon Jan 07 15:46:47 2013 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,119 +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.io.Reader;
-import java.util.Scanner;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-import com.redhat.thermostat.common.utils.LoggingUtils;
-import com.redhat.thermostat.utils.ProcDataSource;
-
-/**
- * Extract status information about the process from /proc/. This is what tools
- * like {@code ps} and {@code top} use.
- *
- * @see {@code proc(5)}
- */
-public class ProcessStatusInfoBuilder {
-
-    private static final Logger logger = LoggingUtils.getLogger(ProcessStatusInfoBuilder.class);
-
-    private final ProcDataSource dataSource;
-
-    public ProcessStatusInfoBuilder(ProcDataSource dataSource) {
-        this.dataSource = dataSource;
-    }
-
-    public ProcessStatusInfo build(int pid) {
-        try (BufferedReader reader = new BufferedReader(dataSource.getStatReader(pid))) {
-            return build(reader);
-        } catch (IOException e) {
-            logger.log(Level.WARNING, "unable to read stat info for " + pid);
-        }
-
-        return null;
-    }
-
-    private ProcessStatusInfo build(Reader r) throws IOException {
-
-        int pid = -1;
-        long utime = -1;
-        long stime = -1;
-
-        Scanner scanner = null;
-
-        /* TODO map these (effectively c) data types to java types more sanely */
-
-        try (BufferedReader reader = new BufferedReader(r)) {
-            String statusLine = reader.readLine();
-
-            /* be prepared for process names like '1 ) 2 3 4 foo 5' */
-
-            scanner = new Scanner(statusLine);
-            pid = scanner.nextInt();
-            scanner.close();
-
-            int execEndNamePos = statusLine.lastIndexOf(')');
-
-            String cleanStatusLine = statusLine.substring(execEndNamePos + 1);
-
-            scanner = new Scanner(cleanStatusLine);
-            /* state = */scanner.next();
-            /* ppid = */scanner.nextInt();
-            /* pgrp = */scanner.nextInt();
-            /* session = */scanner.nextInt();
-            /* tty_nr = */scanner.nextInt();
-            /* tpgid = */scanner.nextInt();
-            /* flags = */scanner.nextInt();
-            /* minflt = */scanner.nextLong();
-            /* cminflt = */scanner.nextLong();
-            /* majflt = */scanner.nextLong();
-            /* cmajflt = */scanner.nextLong();
-            utime = scanner.nextLong();
-            stime = scanner.nextLong();
-            scanner.close();
-        }
-
-        return new ProcessStatusInfo(pid, utime, stime);
-
-    }
-
-}
--- a/system-backend/src/main/java/com/redhat/thermostat/backend/system/SystemBackend.java	Mon Jan 07 15:46:47 2013 -0500
+++ b/system-backend/src/main/java/com/redhat/thermostat/backend/system/SystemBackend.java	Mon Jan 07 15:48:38 2013 -0500
@@ -53,23 +53,17 @@
 import com.redhat.thermostat.backend.Backend;
 import com.redhat.thermostat.backend.BackendID;
 import com.redhat.thermostat.backend.BackendsProperties;
-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.NetworkInterfaceInfoDAO;
-import com.redhat.thermostat.common.dao.VmCpuStatDAO;
 import com.redhat.thermostat.common.utils.LoggingUtils;
 import com.redhat.thermostat.storage.model.NetworkInterfaceInfo;
-import com.redhat.thermostat.storage.model.VmCpuStat;
 import com.redhat.thermostat.utils.ProcDataSource;
-import com.redhat.thermostat.utils.SysConf;
 
 public class SystemBackend extends Backend implements JvmStatusNotifier, JvmStatusListener {
 
     private static final Logger logger = LoggingUtils.getLogger(SystemBackend.class);
 
     private HostInfoDAO hostInfos;
-    private VmCpuStatDAO vmCpuStats;
     private NetworkInterfaceInfoDAO networkInterfaces;
 
     private final Set<Integer> pidsToMonitor = new CopyOnWriteArraySet<Integer>();
@@ -80,9 +74,8 @@
 
     private HostIdentifier hostId = null;
     private MonitoredHost host = null;
-    private JvmStatHostListener hostListener;
+    private JvmStatHostListener hostListener = null;
 
-    private final VmCpuStatBuilder vmCpuBuilder;
     private final HostInfoBuilder hostInfoBuilder;
 
     public SystemBackend() {
@@ -92,20 +85,13 @@
         setConfigurationValue(BackendsProperties.DESCRIPTION.name(), "Gathers basic information from the system");
         setConfigurationValue(BackendsProperties.VERSION.name(), "0.5.0");
         
-        Clock clock = new SystemClock();
-        ProcessStatusInfoBuilder builder = new ProcessStatusInfoBuilder(new ProcDataSource());
-        long ticksPerSecond = SysConf.getClockTicksPerSecond();
         ProcDataSource source = new ProcDataSource();
         hostInfoBuilder = new HostInfoBuilder(source);
-
-        int cpuCount = hostInfoBuilder.getCpuInfo().count;
-        vmCpuBuilder = new VmCpuStatBuilder(clock, cpuCount, ticksPerSecond, builder);
     }
 
     @Override
     protected void setDAOFactoryAction() {
         hostInfos = df.getHostInfoDAO();
-        vmCpuStats = df.getVmCpuStatDAO();
         networkInterfaces = df.getNetworkInterfaceInfoDAO();
         hostListener = new JvmStatHostListener(df.getVmInfoDAO(), df.getVmMemoryStatDAO(), df.getVmGcStatDAO(), df.getVmClassStatsDAO(), getObserveNewJvm());
     }
@@ -133,17 +119,6 @@
                 for (NetworkInterfaceInfo info: NetworkInfoBuilder.build()) {
                     networkInterfaces.putNetworkInterfaceInfo(info);
                 }
-
-                for (Integer pid : pidsToMonitor) {
-                    if (vmCpuBuilder.knowsAbout(pid)) {
-                        VmCpuStat dataBuilt = vmCpuBuilder.build(pid);
-                        if (dataBuilt != null) {
-                            vmCpuStats.putVmCpuStat(dataBuilt);
-                        }
-                    } else {
-                        vmCpuBuilder.learnAbout(pid);
-                    }
-                }
             }
         }, 0, procCheckInterval);
 
@@ -221,7 +196,6 @@
     @Override
     public void jvmStopped(int vmId) {
         pidsToMonitor.remove(vmId);
-        vmCpuBuilder.forgetAbout(vmId);
     }
 
     @Override
--- a/system-backend/src/main/java/com/redhat/thermostat/backend/system/VmCpuStatBuilder.java	Mon Jan 07 15:46:47 2013 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,130 +0,0 @@
-/*
- * Copyright 2012 Red Hat, Inc.
- *
- * This file is part of Thermostat.
- *
- * Thermostat is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published
- * by the Free Software Foundation; either version 2, or (at your
- * option) any later version.
- *
- * Thermostat is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Thermostat; see the file COPYING.  If not see
- * <http://www.gnu.org/licenses/>.
- *
- * Linking this code with other modules is making a combined work
- * based on this code.  Thus, the terms and conditions of the GNU
- * General Public License cover the whole combination.
- *
- * As a special exception, the copyright holders of this code give
- * you permission to link this code with independent modules to
- * produce an executable, regardless of the license terms of these
- * independent modules, and to copy and distribute the resulting
- * executable under terms of your choice, provided that you also
- * meet, for each linked independent module, the terms and conditions
- * of the license of that module.  An independent module is a module
- * which is not derived from or based on this code.  If you modify
- * this code, you may extend this exception to your version of the
- * library, but you are not obligated to do so.  If you do not wish
- * to do so, delete this exception statement from your version.
- */
-
-package com.redhat.thermostat.backend.system;
-
-import java.util.HashMap;
-import java.util.Map;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-import com.redhat.thermostat.common.Clock;
-import com.redhat.thermostat.common.utils.LoggingUtils;
-import com.redhat.thermostat.storage.model.VmCpuStat;
-
-public class VmCpuStatBuilder {
-
-    private static final Logger logger = LoggingUtils.getLogger(VmCpuStatBuilder.class);
-
-    // pid -> ticks
-    private final Map<Integer, Long> lastProcessTicks = new HashMap<Integer, Long>();
-    // pid -> last time the ticks were updated
-    private final Map<Integer, Long> lastProcessTickTime = new HashMap<Integer, Long>();
-
-    private final Clock clock;
-    private final int cpuCount;
-    private final long ticksPerSecond;
-    private final ProcessStatusInfoBuilder statusBuilder;
-
-    public VmCpuStatBuilder(Clock clock, int cpuCount, long ticksPerSecond, ProcessStatusInfoBuilder statusBuilder) {
-        this.clock = clock;
-        this.cpuCount = cpuCount;
-        this.ticksPerSecond = ticksPerSecond;
-        this.statusBuilder = statusBuilder;
-    }
-
-    /**
-     * @param pid the process id
-     * @return an object representing the cpu usage of the process, or null if
-     * the information can not be found.
-     */
-    public synchronized VmCpuStat build(Integer pid) {
-        if (!lastProcessTicks.containsKey(pid) || !lastProcessTickTime.containsKey(pid)) {
-            throw new IllegalArgumentException("unknown pid");
-        }
-
-        ProcessStatusInfo info = statusBuilder.build(pid);
-        if (info == null) {
-            return null;
-        }
-        long miliTime = clock.getRealTimeMillis();
-        long time = clock.getMonotonicTimeNanos();
-        long programTicks = (info.getKernelTime() + info.getUserTime());
-        double cpuLoad = 0.0;
-
-        double timeDelta = (time - lastProcessTickTime.get(pid)) * 1E-9;
-        long programTicksDelta = programTicks - lastProcessTicks.get(pid);
-        // 100 as in 100 percent.
-        cpuLoad = programTicksDelta * (100.0 / timeDelta / ticksPerSecond / cpuCount);
-
-        if (cpuLoad < 0.0 || cpuLoad > 100.0) {
-            logger.log(Level.WARNING, "cpu load for " + pid + " is outside [0,100]: " + cpuLoad);
-            logger.log(Level.WARNING, "  (" + pid + ") programTicks: " + programTicks);
-            logger.log(Level.WARNING, "  (" + pid + ") programTicksDelta: " + programTicksDelta);
-            logger.log(Level.WARNING, "  (" + pid + ") time: " + time);
-            logger.log(Level.WARNING, "  (" + pid + ") timeDelta: " + timeDelta);
-            logger.log(Level.WARNING, "  (" + pid + ") ticksPerSecond: " + ticksPerSecond);
-            logger.log(Level.WARNING, "  (" + pid + ") cpuCount: " + cpuCount);
-        }
-
-        lastProcessTicks.put(pid, programTicks);
-        lastProcessTickTime.put(pid, time);
-
-        return new VmCpuStat(miliTime, pid, cpuLoad);
-    }
-
-    public synchronized boolean knowsAbout(int pid) {
-        return (lastProcessTickTime.containsKey(pid) && lastProcessTicks.containsKey(pid));
-    }
-
-    public synchronized void learnAbout(int pid) {
-        long time = clock.getMonotonicTimeNanos();
-        ProcessStatusInfo info = statusBuilder.build(pid);
-        if (info == null) {
-            logger.log(Level.WARNING, "can not learn about pid " + pid + " : statusBuilder returned null");
-            return;
-        }
-
-        lastProcessTickTime.put(pid, time);
-        lastProcessTicks.put(pid, info.getUserTime()+ info.getKernelTime());
-    }
-
-    public synchronized void forgetAbout(int pid) {
-        lastProcessTicks.remove(pid);
-        lastProcessTickTime.remove(pid);
-    }
-
-}
--- a/system-backend/src/test/java/com/redhat/thermostat/backend/system/ProcessStatusInfoBuilderTest.java	Mon Jan 07 15:46:47 2013 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,142 +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.mockito.Matchers.any;
-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.utils.ProcDataSource;
-
-public class ProcessStatusInfoBuilderTest {
-
-    @Test
-    public void testSimpleProcessStatus() {
-        ProcDataSource dataSource = new ProcDataSource();
-        ProcessStatusInfo stat = new ProcessStatusInfoBuilder(dataSource).build(1);
-        assertNotNull(stat);
-    }
-
-    @Test
-    public void testKnownProcessStatus() throws IOException {
-        final int PID = 10363;
-        String PROCESS_NAME = "(bash)";
-        String STATE = "S";
-        String PPID = "1737";
-        String PROCESS_GROUP_ID = "10363";
-        String SESSION_ID = "10363";
-        String TTY_NUMBER = "34817";
-        String TTY_PROCESS_GROUP_ID = "11404";
-        String FLAGS_WORD = "4202496";
-        String MINOR_FAULTS = "8093";
-        String MINOR_FAULTS_CHILDREN = "607263";
-        String MAJOR_FAULTS = "1";
-        String MAJOR_FAULTS_CHILDREN = "251";
-        final long USER_TIME_TICKS = 21;
-        final long KERNEL_TIME_TICKS = 7;
-        final long USER_TIME_CHILDREN = 10;
-        String KERNEL_TIME_CHILDREN = "1000";
-        String PRIORITY = "20";
-        String statString = "" +
-                PID + " " + PROCESS_NAME + " " + STATE + " " + PPID + " "
-                + PROCESS_GROUP_ID + " " + SESSION_ID + " " + TTY_NUMBER + " "
-                + TTY_PROCESS_GROUP_ID + " " + FLAGS_WORD + " " + MINOR_FAULTS + " "
-                + MINOR_FAULTS_CHILDREN + " " + MAJOR_FAULTS + " " + MAJOR_FAULTS_CHILDREN + " " +
-                USER_TIME_TICKS + " " + KERNEL_TIME_TICKS + " " + USER_TIME_CHILDREN + " " +
-                KERNEL_TIME_CHILDREN + " " + PRIORITY;
-
-        ProcDataSource dataSource = mock(ProcDataSource.class);
-        when(dataSource.getStatReader(any(Integer.class))).thenReturn(new StringReader(statString));
-        ProcessStatusInfoBuilder builder = new ProcessStatusInfoBuilder(dataSource);
-        ProcessStatusInfo stat = builder.build(PID);
-
-        verify(dataSource).getStatReader(PID);
-        assertNotNull(stat);
-        assertEquals(PID, stat.getPid());
-        assertEquals(USER_TIME_TICKS, stat.getUserTime());
-        assertEquals(KERNEL_TIME_TICKS, stat.getKernelTime());
-    }
-
-    @Test
-    public void testBadProcessName() throws IOException {
-        final int PID = 10363;
-        String PROCESS_NAME = "(secretly-bad process sleep 10 20 ) 6)";
-        String STATE = "S";
-        String PPID = "1737";
-        String PROCESS_GROUP_ID = "10363";
-        String SESSION_ID = "10363";
-        String TTY_NUMBER = "34817";
-        String TTY_PROCESS_GROUP_ID = "11404";
-        String FLAGS_WORD = "4202496";
-        String MINOR_FAULTS = "8093";
-        String MINOR_FAULTS_CHILDREN = "607263";
-        String MAJOR_FAULTS = "1";
-        String MAJOR_FAULTS_CHILDREN = "251";
-        final long USER_TIME_TICKS = 21;
-        final long KERNEL_TIME_TICKS = 7;
-        final long USER_TIME_CHILDREN = 10;
-        String KERNEL_TIME_CHILDREN = "1000";
-        String PRIORITY = "20";
-        String statString = "" +
-                PID + " " + PROCESS_NAME + " " + STATE + " " + PPID + " "
-                + PROCESS_GROUP_ID + " " + SESSION_ID + " " + TTY_NUMBER + " "
-                + TTY_PROCESS_GROUP_ID + " " + FLAGS_WORD + " " + MINOR_FAULTS + " "
-                + MINOR_FAULTS_CHILDREN + " " + MAJOR_FAULTS + " " + MAJOR_FAULTS_CHILDREN + " " +
-                USER_TIME_TICKS + " " + KERNEL_TIME_TICKS + " " + USER_TIME_CHILDREN + " " +
-                KERNEL_TIME_CHILDREN + " " + PRIORITY;
-
-        ProcDataSource dataSource = mock(ProcDataSource.class);
-        when(dataSource.getStatReader(any(Integer.class))).thenReturn(new StringReader(statString));
-        ProcessStatusInfoBuilder builder = new ProcessStatusInfoBuilder(dataSource);
-        ProcessStatusInfo stat = builder.build(PID);
-
-        verify(dataSource).getStatReader(PID);
-        assertNotNull(stat);
-        assertEquals(PID, stat.getPid());
-        assertEquals(USER_TIME_TICKS, stat.getUserTime());
-        assertEquals(KERNEL_TIME_TICKS, stat.getKernelTime());
-    }
-
-}
--- a/system-backend/src/test/java/com/redhat/thermostat/backend/system/SystemBackendTest.java	Mon Jan 07 15:46:47 2013 -0500
+++ b/system-backend/src/test/java/com/redhat/thermostat/backend/system/SystemBackendTest.java	Mon Jan 07 15:48:38 2013 -0500
@@ -47,7 +47,6 @@
 import com.redhat.thermostat.common.dao.DAOFactory;
 import com.redhat.thermostat.common.dao.HostInfoDAO;
 import com.redhat.thermostat.common.dao.NetworkInterfaceInfoDAO;
-import com.redhat.thermostat.common.dao.VmCpuStatDAO;
 import com.redhat.thermostat.storage.core.Storage;
 
 public class SystemBackendTest {
@@ -58,12 +57,10 @@
     public void setUp() {
         Storage s = mock(Storage.class);
         HostInfoDAO hDAO = mock(HostInfoDAO.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.getVmCpuStatDAO()).thenReturn(vDAO);
         when(df.getNetworkInterfaceInfoDAO()).thenReturn(nDAO);
         b = new SystemBackend();
         b.setDAOFactory(df);
--- a/system-backend/src/test/java/com/redhat/thermostat/backend/system/VmCpuStatBuilderTest.java	Mon Jan 07 15:46:47 2013 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,168 +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.assertFalse;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.Matchers.any;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-import org.junit.Test;
-
-import com.redhat.thermostat.common.Clock;
-import com.redhat.thermostat.storage.model.VmCpuStat;
-import com.redhat.thermostat.test.Bug;
-
-public class VmCpuStatBuilderTest {
-
-    @Test
-    public void testBuilderKnowsNothing() {
-        Clock clock = mock(Clock.class);
-        ProcessStatusInfoBuilder statusBuilder = mock(ProcessStatusInfoBuilder.class);
-        int cpuCount = 0;
-        long ticksPerSecond = 0;
-        VmCpuStatBuilder builder = new VmCpuStatBuilder(clock, cpuCount, ticksPerSecond, statusBuilder);
-
-        assertFalse(builder.knowsAbout(0));
-        assertFalse(builder.knowsAbout(1));
-        assertFalse(builder.knowsAbout(Integer.MIN_VALUE));
-        assertFalse(builder.knowsAbout(Integer.MAX_VALUE));
-
-    }
-
-    @Test(expected = IllegalArgumentException.class)
-    public void testBuilderThrowsOnBuildOfUnknownPid() {
-        Clock clock = mock(Clock.class);
-        int cpuCount = 0;
-        long ticksPerSecond = 0;
-        ProcessStatusInfoBuilder statusBuilder = mock(ProcessStatusInfoBuilder.class);
-        VmCpuStatBuilder builder = new VmCpuStatBuilder(clock, cpuCount, ticksPerSecond, statusBuilder);
-        builder.build(0);
-    }
-
-    @Test
-    public void testBuildNullOnInsufficentInformation() {
-        int PID = 0;
-        int cpuCount = 0;
-        long ticksPerSecond = 0;
-        final long CLOCK1 = 10000;
-        final long CLOCK2 = 20000;
-        final ProcessStatusInfo initialInfo = new ProcessStatusInfo(PID, 1, 2);
-        final ProcessStatusInfo laterInfo = null;
-
-        Clock clock = mock(Clock.class);
-        when(clock.getMonotonicTimeNanos()).thenReturn((long) (CLOCK1 * 1E6)).thenReturn((long) (CLOCK2 * 1E6));
-
-        ProcessStatusInfoBuilder statusBuilder = mock(ProcessStatusInfoBuilder.class);
-        when(statusBuilder.build(any(Integer.class))).thenReturn(initialInfo).thenReturn(laterInfo).thenReturn(null);
-
-        VmCpuStatBuilder builder = new VmCpuStatBuilder(clock, cpuCount, ticksPerSecond, statusBuilder);
-
-        builder.learnAbout(PID);
-        assertEquals(null, builder.build(PID));
-    }
-
-    @Test
-    public void testSaneBuild() {
-        final int PID = 0;
-
-        final int CPU_COUNT = 3;
-
-        final long USER_INITIAL_TICKS = 1;
-        final long KERNEL_INITIAL_TICKS = 1;
-
-        final long USER_LATER_TICKS = 10;
-        final long KERNEL_LATER_TICKS = 10;
-
-        final long CLOCK1 = 10000;
-        final long CLOCK2 = 20000;
-
-        final long TICKS_PER_SECOND = 100;
-
-        final double CPU_LOAD_PERCENT =
-                100.0
-                * ((USER_LATER_TICKS + KERNEL_LATER_TICKS) - (USER_INITIAL_TICKS + KERNEL_INITIAL_TICKS))
-                / TICKS_PER_SECOND
-                / ((CLOCK2 - CLOCK1) * 1E-3 /* millis to seconds */)
-                / CPU_COUNT;
-
-        final ProcessStatusInfo initialInfo = new ProcessStatusInfo(PID, USER_INITIAL_TICKS, KERNEL_INITIAL_TICKS);
-        final ProcessStatusInfo laterInfo = new ProcessStatusInfo(PID, USER_LATER_TICKS, KERNEL_LATER_TICKS);
-
-        Clock clock = mock(Clock.class);
-        when(clock.getRealTimeMillis()).thenReturn(CLOCK2);
-        when(clock.getMonotonicTimeNanos()).thenReturn((long) (CLOCK1 * 1E6)).thenReturn((long) (CLOCK2 * 1E6));
-
-        ProcessStatusInfoBuilder statusBuilder = mock(ProcessStatusInfoBuilder.class);
-        when(statusBuilder.build(any(Integer.class))).thenReturn(initialInfo).thenReturn(laterInfo).thenReturn(null);
-
-        VmCpuStatBuilder builder = new VmCpuStatBuilder(clock, CPU_COUNT, TICKS_PER_SECOND, statusBuilder);
-
-        builder.learnAbout(PID);
-        VmCpuStat stat = builder.build(PID);
-
-        assertNotNull(stat);
-        assertEquals(PID, stat.getVmId());
-        assertEquals(CLOCK2, stat.getTimeStamp());
-        assertEquals(CPU_LOAD_PERCENT, stat.getCpuLoad(), 0.0001);
-    }
-
-    @Bug(id="1051",
-            summary="Avoid exceptions when reading /proc/ for dead processes",
-            url="http://icedtea.classpath.org/bugzilla/show_bug.cgi?id=1051")
-    @Test
-    public void testNoExceptionForBuilderLearningAboutDeadProcess() {
-        Clock clock = mock(Clock.class);
-        when(clock.getMonotonicTimeNanos()).thenReturn((long) (10000 * 1E6));
-        ProcessStatusInfoBuilder procBuilder = mock(ProcessStatusInfoBuilder.class);
-        // This thing returns null if the /proc entry goes away.  Rather than try to
-        // 'guess' at a pid that will not be present during test, just mock this.
-        when(procBuilder.build(any(Integer.class))).thenReturn(null);
-        VmCpuStatBuilder builder = new VmCpuStatBuilder(clock, 3, 100, procBuilder);
-        // If we can't handle a process' /proc entry disappearing, the next line
-        // will throw exception.  If it does not, then we are okay.
-        try {
-            builder.learnAbout(0);
-        } catch (Exception e) {
-            // Shouldn't happen.
-            assertTrue(false);
-        }
-    }
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-cpu/agent/pom.xml	Mon Jan 07 15:48:38 2013 -0500
@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <artifactId>thermostat-vm-cpu</artifactId>
+    <groupId>com.redhat.thermostat</groupId>
+    <version>0.5.0-SNAPSHOT</version>
+  </parent>
+  <artifactId>thermostat-vm-cpu-agent</artifactId>
+  <packaging>bundle</packaging>
+  <name>Thermostat VM CPU Agent plugin</name>
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.felix</groupId>
+        <artifactId>maven-bundle-plugin</artifactId>
+        <extensions>true</extensions>
+        <configuration>
+          <instructions>
+            <Bundle-Vendor>Red Hat, Inc.</Bundle-Vendor>
+            <Bundle-SymbolicName>com.redhat.thermostat.vm.cpu.agent</Bundle-SymbolicName>
+            <Bundle-Activator>com.redhat.thermostat.vm.cpu.agent.internal.Activator</Bundle-Activator>
+            <Export-Package>
+              com.redhat.thermostat.vm.cpu.agent
+            </Export-Package>
+            <Private-Package>
+              com.redhat.thermostat.vm.cpu.agent.internal
+            </Private-Package>
+            <!-- Do not autogenerate uses clauses in Manifests -->
+            <_nouses>true</_nouses>
+          </instructions>
+        </configuration>
+      </plugin>
+    </plugins>
+  </build>
+  <dependencies>
+    <dependency>
+      <groupId>junit</groupId>
+      <artifactId>junit</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.mockito</groupId>
+      <artifactId>mockito-core</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.osgi</groupId>
+      <artifactId>org.osgi.core</artifactId>
+      <scope>provided</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.osgi</groupId>
+      <artifactId>org.osgi.compendium</artifactId>
+      <scope>provided</scope>
+    </dependency>
+    <dependency>
+      <groupId>com.redhat.thermostat</groupId>
+      <artifactId>thermostat-common-core</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>com.redhat.thermostat</groupId>
+      <artifactId>thermostat-vm-cpu-common</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>com.redhat.thermostat</groupId>
+      <artifactId>thermostat-agent-core</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>com.redhat.thermostat</groupId>
+      <artifactId>thermostat-storage-core</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+  </dependencies>
+</project>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-cpu/agent/src/main/java/com/redhat/thermostat/vm/cpu/agent/internal/Activator.java	Mon Jan 07 15:48:38 2013 -0500
@@ -0,0 +1,101 @@
+/*
+ * Copyright 2013 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.vm.cpu.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.vm.cpu.common.VmCpuStatDAO;
+
+public class Activator implements BundleActivator {
+    
+    private ScheduledExecutorService executor;
+    private MultipleServiceTracker tracker;
+    private VmCpuBackend backend;
+    private ServiceRegistration reg;
+    
+    @Override
+    public void start(final BundleContext context) throws Exception {
+        executor = Executors.newSingleThreadScheduledExecutor();
+
+        Class<?>[] deps = new Class<?>[] {
+                BackendService.class,
+                VmCpuStatDAO.class
+        };
+        tracker = new MultipleServiceTracker(context, deps, new Action() {
+            
+            @Override
+            public void dependenciesAvailable(Map<String, Object> services) {
+                VmCpuStatDAO vmCpuStatDao = (VmCpuStatDAO) services.get(VmCpuStatDAO.class.getName());
+                Version version = new Version(context.getBundle());
+                backend = new VmCpuBackend(executor, vmCpuStatDao, 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.
+     */
+    VmCpuBackend getBackend() {
+        return backend;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-cpu/agent/src/main/java/com/redhat/thermostat/vm/cpu/agent/internal/ProcessStatusInfo.java	Mon Jan 07 15:48:38 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.vm.cpu.agent.internal;
+
+public class ProcessStatusInfo {
+
+    /* All times are measured in clock ticks */
+
+    private final int pid;
+    private final long userTime;
+    private final long kernelTime;
+
+    public ProcessStatusInfo(int pid, long userTime, long kernelTime) {
+        this.pid = pid;
+        this.userTime = userTime;
+        this.kernelTime = kernelTime;
+    }
+
+    public int getPid() {
+        return pid;
+    }
+
+    /**
+     * @return the time this process has spent in user-mode as a number of
+     * kernel ticks
+     */
+    public long getUserTime() {
+        return userTime;
+    }
+
+    /**
+     * @return the time this process spent in kernel-mode as a number of kernel
+     * ticks
+     */
+    public long getKernelTime() {
+        return kernelTime;
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-cpu/agent/src/main/java/com/redhat/thermostat/vm/cpu/agent/internal/ProcessStatusInfoBuilder.java	Mon Jan 07 15:48:38 2013 -0500
@@ -0,0 +1,119 @@
+/*
+ * Copyright 2012 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.vm.cpu.agent.internal;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.Reader;
+import java.util.Scanner;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import com.redhat.thermostat.common.utils.LoggingUtils;
+import com.redhat.thermostat.utils.ProcDataSource;
+
+/**
+ * Extract status information about the process from /proc/. This is what tools
+ * like {@code ps} and {@code top} use.
+ *
+ * @see {@code proc(5)}
+ */
+public class ProcessStatusInfoBuilder {
+
+    private static final Logger logger = LoggingUtils.getLogger(ProcessStatusInfoBuilder.class);
+
+    private final ProcDataSource dataSource;
+
+    public ProcessStatusInfoBuilder(ProcDataSource dataSource) {
+        this.dataSource = dataSource;
+    }
+
+    public ProcessStatusInfo build(int pid) {
+        try (BufferedReader reader = new BufferedReader(dataSource.getStatReader(pid))) {
+            return build(reader);
+        } catch (IOException e) {
+            logger.log(Level.WARNING, "unable to read stat info for " + pid);
+        }
+
+        return null;
+    }
+
+    private ProcessStatusInfo build(Reader r) throws IOException {
+
+        int pid = -1;
+        long utime = -1;
+        long stime = -1;
+
+        Scanner scanner = null;
+
+        /* TODO map these (effectively c) data types to java types more sanely */
+
+        try (BufferedReader reader = new BufferedReader(r)) {
+            String statusLine = reader.readLine();
+
+            /* be prepared for process names like '1 ) 2 3 4 foo 5' */
+
+            scanner = new Scanner(statusLine);
+            pid = scanner.nextInt();
+            scanner.close();
+
+            int execEndNamePos = statusLine.lastIndexOf(')');
+
+            String cleanStatusLine = statusLine.substring(execEndNamePos + 1);
+
+            scanner = new Scanner(cleanStatusLine);
+            /* state = */scanner.next();
+            /* ppid = */scanner.nextInt();
+            /* pgrp = */scanner.nextInt();
+            /* session = */scanner.nextInt();
+            /* tty_nr = */scanner.nextInt();
+            /* tpgid = */scanner.nextInt();
+            /* flags = */scanner.nextInt();
+            /* minflt = */scanner.nextLong();
+            /* cminflt = */scanner.nextLong();
+            /* majflt = */scanner.nextLong();
+            /* cmajflt = */scanner.nextLong();
+            utime = scanner.nextLong();
+            stime = scanner.nextLong();
+            scanner.close();
+        }
+
+        return new ProcessStatusInfo(pid, utime, stime);
+
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-cpu/agent/src/main/java/com/redhat/thermostat/vm/cpu/agent/internal/VmCpuBackend.java	Mon Jan 07 15:48:38 2013 -0500
@@ -0,0 +1,211 @@
+/*
+ * Copyright 2013 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.vm.cpu.agent.internal;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.net.URISyntaxException;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import sun.jvmstat.monitor.HostIdentifier;
+import sun.jvmstat.monitor.MonitorException;
+import sun.jvmstat.monitor.MonitoredHost;
+
+import com.redhat.thermostat.backend.Backend;
+import com.redhat.thermostat.backend.BackendID;
+import com.redhat.thermostat.backend.BackendsProperties;
+import com.redhat.thermostat.common.Clock;
+import com.redhat.thermostat.common.SystemClock;
+import com.redhat.thermostat.common.Version;
+import com.redhat.thermostat.common.utils.LoggingUtils;
+import com.redhat.thermostat.storage.model.VmCpuStat;
+import com.redhat.thermostat.utils.ProcDataSource;
+import com.redhat.thermostat.utils.SysConf;
+import com.redhat.thermostat.vm.cpu.common.VmCpuStatDAO;
+
+public class VmCpuBackend extends Backend {
+
+    private static final Logger LOGGER = LoggingUtils.getLogger(VmCpuBackend.class);
+    static final long PROC_CHECK_INTERVAL = 1000; // TODO make this configurable.
+
+    private VmCpuStatBuilder vmCpuStatBuilder;
+    private VmCpuStatDAO vmCpuStats;
+    private ScheduledExecutorService executor;
+    private HostIdentifier hostId;
+    private MonitoredHost host;
+    private VmCpuHostListener hostListener;
+    private boolean started;
+
+    public VmCpuBackend(ScheduledExecutorService executor, VmCpuStatDAO vmCpuStatDao, Version version) {
+        super(new BackendID("VM CPU Backend", VmCpuBackend.class.getName()));
+        this.executor = executor;
+        this.vmCpuStats = vmCpuStatDao;
+        
+        setConfigurationValue(BackendsProperties.VENDOR.name(), "Red Hat, Inc.");
+        setConfigurationValue(BackendsProperties.DESCRIPTION.name(), "Gathers CPU statistics about a JVM");
+        setConfigurationValue(BackendsProperties.VERSION.name(), version.getVersionNumber());
+        
+        Clock clock = new SystemClock();
+        long ticksPerSecond = SysConf.getClockTicksPerSecond();
+        ProcDataSource source = new ProcDataSource();
+        ProcessStatusInfoBuilder builder = new ProcessStatusInfoBuilder(new ProcDataSource());
+        int numCpus = getCpuCount(source);
+        vmCpuStatBuilder = new VmCpuStatBuilder(clock, numCpus, ticksPerSecond, builder);
+        
+        try {
+            hostId = new HostIdentifier((String) null);
+            host = MonitoredHost.getMonitoredHost(hostId);
+            hostListener = new VmCpuHostListener(vmCpuStatBuilder);
+        } catch (MonitorException me) {
+            LOGGER.log(Level.WARNING, "Problems with connecting jvmstat to local machine", me);
+        } catch (URISyntaxException use) {
+            LOGGER.log(Level.WARNING, "Failed to create host identifier", use);
+        }
+    }
+
+    @Override
+    public boolean activate() {
+        if (!started && host != null) {
+            executor.scheduleAtFixedRate(new Runnable() {
+                @Override
+                public void run() {
+                    for (Integer pid : hostListener.getPidsToMonitor()) {
+                        if (vmCpuStatBuilder.knowsAbout(pid)) {
+                            VmCpuStat dataBuilt = vmCpuStatBuilder.build(pid);
+                            if (dataBuilt != null) {
+                                vmCpuStats.putVmCpuStat(dataBuilt);
+                            }
+                        } else {
+                            vmCpuStatBuilder.learnAbout(pid);
+                        }
+                    }
+                }
+            }, 0, PROC_CHECK_INTERVAL, TimeUnit.MILLISECONDS);
+
+            try {
+                host.addHostListener(hostListener);
+                started = true;
+            } catch (MonitorException me) {
+                LOGGER.log(Level.WARNING, "Failed to add host listener", me);
+            }
+
+        }
+        return started;
+    }
+
+    @Override
+    public boolean deactivate() {
+        if (started && host != null) {
+            executor.shutdown();
+
+            try {
+                host.removeHostListener(hostListener);
+                started = false;
+            } catch (MonitorException me) {
+                LOGGER.log(Level.INFO, "Failed to remove host listener");
+            }
+        }
+        return !started;
+    }
+    
+    @Override
+    public boolean isActive() {
+        return started;
+    }
+
+    @Override
+    protected void setDAOFactoryAction() {
+        // No need for DAOFactory
+    }
+
+    @Override
+    public String getConfigurationValue(String key) {
+        return null;
+    }
+
+    @Override
+    public boolean attachToNewProcessByDefault() {
+        return true;
+    }
+
+    @Override
+    public int getOrderValue() {
+        return ORDER_CPU_GROUP + 50;
+    }
+
+    private int getCpuCount(ProcDataSource dataSource) {
+        final String KEY_PROCESSOR_ID = "processor";
+        int cpuCount = 0;
+        try (BufferedReader bufferedReader = new BufferedReader(dataSource.getCpuInfoReader())) {
+            String line = null;
+            while ((line = bufferedReader.readLine()) != null) {
+                if (line.startsWith(KEY_PROCESSOR_ID)) {
+                    cpuCount++;
+                }
+            }
+        } catch (IOException ioe) {
+            LOGGER.log(Level.WARNING, "Unable to read cpu info");
+        }
+        
+        return cpuCount;
+    }
+
+    /*
+     * For testing purposes only.
+     */
+    void setHost(MonitoredHost host) {
+        this.host = host;
+    }
+    
+    /*
+     * For testing purposes only.
+     */
+    void setVmCpuStatBuilder(VmCpuStatBuilder vmCpuStatBuilder) {
+        this.vmCpuStatBuilder = vmCpuStatBuilder;
+    }
+    
+    /*
+     * For testing purposes only.
+     */
+    void setHostListener(VmCpuHostListener hostListener) {
+        this.hostListener = hostListener;
+    }
+    
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-cpu/agent/src/main/java/com/redhat/thermostat/vm/cpu/agent/internal/VmCpuHostListener.java	Mon Jan 07 15:48:38 2013 -0500
@@ -0,0 +1,85 @@
+/*
+ * Copyright 2013 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.vm.cpu.agent.internal;
+
+import java.util.Set;
+import java.util.concurrent.CopyOnWriteArraySet;
+import java.util.logging.Logger;
+
+import sun.jvmstat.monitor.event.HostEvent;
+import sun.jvmstat.monitor.event.HostListener;
+import sun.jvmstat.monitor.event.VmStatusChangeEvent;
+
+import com.redhat.thermostat.common.utils.LoggingUtils;
+
+public class VmCpuHostListener implements HostListener {
+    
+    private static final Logger LOGGER = LoggingUtils.getLogger(VmCpuHostListener.class);
+    
+    private final Set<Integer> pidsToMonitor = new CopyOnWriteArraySet<Integer>();
+    private VmCpuStatBuilder vmCpuStatBuilder;
+    
+    public VmCpuHostListener(VmCpuStatBuilder builder) {
+        this.vmCpuStatBuilder = builder;
+    }
+
+    @Override
+    public void vmStatusChanged(VmStatusChangeEvent event) {
+        for (Object newVm : event.getStarted()) {
+            Integer vmId = (Integer) newVm;
+            LOGGER.fine("New vm: " + vmId);
+            pidsToMonitor.add(vmId);
+        }
+
+        for (Object stoppedVm : event.getTerminated()) {
+            Integer vmId = (Integer) stoppedVm;
+            LOGGER.fine("stopped vm: " + vmId);
+            pidsToMonitor.remove(vmId);
+            vmCpuStatBuilder.forgetAbout(vmId);
+        }
+    }
+
+    @Override
+    public void disconnected(HostEvent event) {
+        LOGGER.warning("Disconnected from host");
+    }
+    
+    public Set<Integer> getPidsToMonitor() {
+        return pidsToMonitor;
+    }
+    
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-cpu/agent/src/main/java/com/redhat/thermostat/vm/cpu/agent/internal/VmCpuStatBuilder.java	Mon Jan 07 15:48:38 2013 -0500
@@ -0,0 +1,130 @@
+/*
+ * Copyright 2012 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.vm.cpu.agent.internal;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import com.redhat.thermostat.common.Clock;
+import com.redhat.thermostat.common.utils.LoggingUtils;
+import com.redhat.thermostat.storage.model.VmCpuStat;
+
+public class VmCpuStatBuilder {
+
+    private static final Logger logger = LoggingUtils.getLogger(VmCpuStatBuilder.class);
+
+    // pid -> ticks
+    private final Map<Integer, Long> lastProcessTicks = new HashMap<Integer, Long>();
+    // pid -> last time the ticks were updated
+    private final Map<Integer, Long> lastProcessTickTime = new HashMap<Integer, Long>();
+
+    private final Clock clock;
+    private final int cpuCount;
+    private final long ticksPerSecond;
+    private final ProcessStatusInfoBuilder statusBuilder;
+
+    public VmCpuStatBuilder(Clock clock, int cpuCount, long ticksPerSecond, ProcessStatusInfoBuilder statusBuilder) {
+        this.clock = clock;
+        this.cpuCount = cpuCount;
+        this.ticksPerSecond = ticksPerSecond;
+        this.statusBuilder = statusBuilder;
+    }
+
+    /**
+     * @param pid the process id
+     * @return an object representing the cpu usage of the process, or null if
+     * the information can not be found.
+     */
+    public synchronized VmCpuStat build(Integer pid) {
+        if (!lastProcessTicks.containsKey(pid) || !lastProcessTickTime.containsKey(pid)) {
+            throw new IllegalArgumentException("unknown pid");
+        }
+
+        ProcessStatusInfo info = statusBuilder.build(pid);
+        if (info == null) {
+            return null;
+        }
+        long miliTime = clock.getRealTimeMillis();
+        long time = clock.getMonotonicTimeNanos();
+        long programTicks = (info.getKernelTime() + info.getUserTime());
+        double cpuLoad = 0.0;
+
+        double timeDelta = (time - lastProcessTickTime.get(pid)) * 1E-9;
+        long programTicksDelta = programTicks - lastProcessTicks.get(pid);
+        // 100 as in 100 percent.
+        cpuLoad = programTicksDelta * (100.0 / timeDelta / ticksPerSecond / cpuCount);
+
+        if (cpuLoad < 0.0 || cpuLoad > 100.0) {
+            logger.log(Level.WARNING, "cpu load for " + pid + " is outside [0,100]: " + cpuLoad);
+            logger.log(Level.WARNING, "  (" + pid + ") programTicks: " + programTicks);
+            logger.log(Level.WARNING, "  (" + pid + ") programTicksDelta: " + programTicksDelta);
+            logger.log(Level.WARNING, "  (" + pid + ") time: " + time);
+            logger.log(Level.WARNING, "  (" + pid + ") timeDelta: " + timeDelta);
+            logger.log(Level.WARNING, "  (" + pid + ") ticksPerSecond: " + ticksPerSecond);
+            logger.log(Level.WARNING, "  (" + pid + ") cpuCount: " + cpuCount);
+        }
+
+        lastProcessTicks.put(pid, programTicks);
+        lastProcessTickTime.put(pid, time);
+
+        return new VmCpuStat(miliTime, pid, cpuLoad);
+    }
+
+    public synchronized boolean knowsAbout(int pid) {
+        return (lastProcessTickTime.containsKey(pid) && lastProcessTicks.containsKey(pid));
+    }
+
+    public synchronized void learnAbout(int pid) {
+        long time = clock.getMonotonicTimeNanos();
+        ProcessStatusInfo info = statusBuilder.build(pid);
+        if (info == null) {
+            logger.log(Level.WARNING, "can not learn about pid " + pid + " : statusBuilder returned null");
+            return;
+        }
+
+        lastProcessTickTime.put(pid, time);
+        lastProcessTicks.put(pid, info.getUserTime()+ info.getKernelTime());
+    }
+
+    public synchronized void forgetAbout(int pid) {
+        lastProcessTicks.remove(pid);
+        lastProcessTickTime.remove(pid);
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-cpu/agent/src/test/java/com/redhat/thermostat/vm/cpu/agent/internal/ActivatorTest.java	Mon Jan 07 15:48:38 2013 -0500
@@ -0,0 +1,104 @@
+/*
+ * Copyright 2013 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */ 
+
+package com.redhat.thermostat.vm.cpu.agent.internal;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import org.junit.Test;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.Version;
+
+import com.redhat.thermostat.backend.Backend;
+import com.redhat.thermostat.backend.BackendService;
+import com.redhat.thermostat.test.StubBundleContext;
+import com.redhat.thermostat.vm.cpu.common.VmCpuStatDAO;
+
+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);
+        VmCpuStatDAO vmCpuStatDAO = mock(VmCpuStatDAO.class);
+
+        context.registerService(BackendService.class.getName(), service, null);
+        context.registerService(VmCpuStatDAO.class, vmCpuStatDAO, null);
+
+        Activator activator = new Activator();
+
+        activator.start(context);
+
+        assertTrue(context.isServiceRegistered(Backend.class.getName(), VmCpuBackend.class));
+        VmCpuBackend backend = activator.getBackend();
+        assertNotNull(backend);
+
+        activator.stop(context);
+        
+        assertFalse(backend.isActive());
+
+        assertEquals(0, context.getServiceListeners().size());
+        assertEquals(2, context.getAllServices().size());
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-cpu/agent/src/test/java/com/redhat/thermostat/vm/cpu/agent/internal/ProcessStatusInfoBuilderTest.java	Mon Jan 07 15:48:38 2013 -0500
@@ -0,0 +1,144 @@
+/*
+ * Copyright 2012 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.vm.cpu.agent.internal;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+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.io.IOException;
+import java.io.StringReader;
+
+import org.junit.Test;
+
+import com.redhat.thermostat.utils.ProcDataSource;
+import com.redhat.thermostat.vm.cpu.agent.internal.ProcessStatusInfo;
+import com.redhat.thermostat.vm.cpu.agent.internal.ProcessStatusInfoBuilder;
+
+public class ProcessStatusInfoBuilderTest {
+
+    @Test
+    public void testSimpleProcessStatus() {
+        ProcDataSource dataSource = new ProcDataSource();
+        ProcessStatusInfo stat = new ProcessStatusInfoBuilder(dataSource).build(1);
+        assertNotNull(stat);
+    }
+
+    @Test
+    public void testKnownProcessStatus() throws IOException {
+        final int PID = 10363;
+        String PROCESS_NAME = "(bash)";
+        String STATE = "S";
+        String PPID = "1737";
+        String PROCESS_GROUP_ID = "10363";
+        String SESSION_ID = "10363";
+        String TTY_NUMBER = "34817";
+        String TTY_PROCESS_GROUP_ID = "11404";
+        String FLAGS_WORD = "4202496";
+        String MINOR_FAULTS = "8093";
+        String MINOR_FAULTS_CHILDREN = "607263";
+        String MAJOR_FAULTS = "1";
+        String MAJOR_FAULTS_CHILDREN = "251";
+        final long USER_TIME_TICKS = 21;
+        final long KERNEL_TIME_TICKS = 7;
+        final long USER_TIME_CHILDREN = 10;
+        String KERNEL_TIME_CHILDREN = "1000";
+        String PRIORITY = "20";
+        String statString = "" +
+                PID + " " + PROCESS_NAME + " " + STATE + " " + PPID + " "
+                + PROCESS_GROUP_ID + " " + SESSION_ID + " " + TTY_NUMBER + " "
+                + TTY_PROCESS_GROUP_ID + " " + FLAGS_WORD + " " + MINOR_FAULTS + " "
+                + MINOR_FAULTS_CHILDREN + " " + MAJOR_FAULTS + " " + MAJOR_FAULTS_CHILDREN + " " +
+                USER_TIME_TICKS + " " + KERNEL_TIME_TICKS + " " + USER_TIME_CHILDREN + " " +
+                KERNEL_TIME_CHILDREN + " " + PRIORITY;
+
+        ProcDataSource dataSource = mock(ProcDataSource.class);
+        when(dataSource.getStatReader(any(Integer.class))).thenReturn(new StringReader(statString));
+        ProcessStatusInfoBuilder builder = new ProcessStatusInfoBuilder(dataSource);
+        ProcessStatusInfo stat = builder.build(PID);
+
+        verify(dataSource).getStatReader(PID);
+        assertNotNull(stat);
+        assertEquals(PID, stat.getPid());
+        assertEquals(USER_TIME_TICKS, stat.getUserTime());
+        assertEquals(KERNEL_TIME_TICKS, stat.getKernelTime());
+    }
+
+    @Test
+    public void testBadProcessName() throws IOException {
+        final int PID = 10363;
+        String PROCESS_NAME = "(secretly-bad process sleep 10 20 ) 6)";
+        String STATE = "S";
+        String PPID = "1737";
+        String PROCESS_GROUP_ID = "10363";
+        String SESSION_ID = "10363";
+        String TTY_NUMBER = "34817";
+        String TTY_PROCESS_GROUP_ID = "11404";
+        String FLAGS_WORD = "4202496";
+        String MINOR_FAULTS = "8093";
+        String MINOR_FAULTS_CHILDREN = "607263";
+        String MAJOR_FAULTS = "1";
+        String MAJOR_FAULTS_CHILDREN = "251";
+        final long USER_TIME_TICKS = 21;
+        final long KERNEL_TIME_TICKS = 7;
+        final long USER_TIME_CHILDREN = 10;
+        String KERNEL_TIME_CHILDREN = "1000";
+        String PRIORITY = "20";
+        String statString = "" +
+                PID + " " + PROCESS_NAME + " " + STATE + " " + PPID + " "
+                + PROCESS_GROUP_ID + " " + SESSION_ID + " " + TTY_NUMBER + " "
+                + TTY_PROCESS_GROUP_ID + " " + FLAGS_WORD + " " + MINOR_FAULTS + " "
+                + MINOR_FAULTS_CHILDREN + " " + MAJOR_FAULTS + " " + MAJOR_FAULTS_CHILDREN + " " +
+                USER_TIME_TICKS + " " + KERNEL_TIME_TICKS + " " + USER_TIME_CHILDREN + " " +
+                KERNEL_TIME_CHILDREN + " " + PRIORITY;
+
+        ProcDataSource dataSource = mock(ProcDataSource.class);
+        when(dataSource.getStatReader(any(Integer.class))).thenReturn(new StringReader(statString));
+        ProcessStatusInfoBuilder builder = new ProcessStatusInfoBuilder(dataSource);
+        ProcessStatusInfo stat = builder.build(PID);
+
+        verify(dataSource).getStatReader(PID);
+        assertNotNull(stat);
+        assertEquals(PID, stat.getPid());
+        assertEquals(USER_TIME_TICKS, stat.getUserTime());
+        assertEquals(KERNEL_TIME_TICKS, stat.getKernelTime());
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-cpu/agent/src/test/java/com/redhat/thermostat/vm/cpu/agent/internal/VmCpuBackendTest.java	Mon Jan 07 15:48:38 2013 -0500
@@ -0,0 +1,128 @@
+/*
+ * Copyright 2013 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.vm.cpu.agent.internal;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import java.util.HashSet;
+import java.util.Set;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+
+import sun.jvmstat.monitor.MonitorException;
+import sun.jvmstat.monitor.MonitoredHost;
+import sun.jvmstat.monitor.event.HostListener;
+
+import com.redhat.thermostat.common.Version;
+import com.redhat.thermostat.storage.model.VmCpuStat;
+import com.redhat.thermostat.vm.cpu.common.VmCpuStatDAO;
+
+public class VmCpuBackendTest {
+    
+    private VmCpuBackend backend;
+    private ScheduledExecutorService executor;
+    private MonitoredHost host;
+    private VmCpuStatDAO vmCpuStatDao;
+
+    @Before
+    public void setup() {
+        executor = mock(ScheduledExecutorService.class);
+        vmCpuStatDao = mock(VmCpuStatDAO.class);
+        
+        Version version = mock(Version.class);
+        when(version.getVersionNumber()).thenReturn("0.0.0");
+        
+        backend = new VmCpuBackend(executor, vmCpuStatDao, version);
+        
+        host = mock(MonitoredHost.class);
+        backend.setHost(host);
+    }
+
+    @Test
+    public void testStart() throws MonitorException {
+        // Setup Runnable mocks
+        final Set<Integer> pids = new HashSet<>();
+        pids.add(0);
+        pids.add(1);
+        VmCpuHostListener listener = mock(VmCpuHostListener.class);
+        when(listener.getPidsToMonitor()).thenReturn(pids);
+        backend.setHostListener(listener);
+        
+        VmCpuStatBuilder builder = mock(VmCpuStatBuilder.class);
+        VmCpuStat stat0 = mock(VmCpuStat.class);
+        VmCpuStat stat1 = mock(VmCpuStat.class);
+        when(builder.build(0)).thenReturn(stat0);
+        when(builder.build(1)).thenReturn(stat1);
+        backend.setVmCpuStatBuilder(builder);
+        
+        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());
+        verify(host).addHostListener(any(HostListener.class));
+        
+        Runnable runnable = captor.getValue();
+        runnable.run();
+        verify(builder).learnAbout(0);
+        verify(builder).learnAbout(1);
+        
+        when(builder.knowsAbout(anyInt())).thenReturn(true);
+        runnable.run();
+        verify(vmCpuStatDao).putVmCpuStat(stat0);
+        verify(vmCpuStatDao).putVmCpuStat(stat1);
+    }
+    
+    @Test
+    public void testStop() throws MonitorException {
+        backend.activate();
+        backend.deactivate();
+        verify(executor).shutdown();
+        assertFalse(backend.isActive());
+        verify(host).removeHostListener(any(HostListener.class));
+    }
+    
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-cpu/agent/src/test/java/com/redhat/thermostat/vm/cpu/agent/internal/VmCpuHostListenerTest.java	Mon Jan 07 15:48:38 2013 -0500
@@ -0,0 +1,106 @@
+/*
+ * Copyright 2013 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.vm.cpu.agent.internal;
+
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import sun.jvmstat.monitor.event.VmStatusChangeEvent;
+
+public class VmCpuHostListenerTest {
+    
+    private VmCpuHostListener hostListener;
+    private VmCpuStatBuilder builder;
+
+    @Before
+    public void setup() {
+        builder = mock(VmCpuStatBuilder.class);
+        
+        hostListener = new VmCpuHostListener(builder);
+    }
+
+    @Test
+    public void testNewVM() throws InterruptedException {
+        startVMs();
+        
+        // Check that pids are added to set
+        Set<Integer> pids = hostListener.getPidsToMonitor();
+        assertTrue(pids.contains(1));
+        assertTrue(pids.contains(2));
+    }
+    
+    @Test
+    public void testStoppedVM() throws InterruptedException {
+        final Set<Integer> stopped = new HashSet<>();
+        stopped.add(1);
+        
+        startVMs();
+        
+        // Trigger a change event
+        VmStatusChangeEvent event = mock(VmStatusChangeEvent.class);
+        when(event.getStarted()).thenReturn(Collections.emptySet());
+        when(event.getTerminated()).thenReturn(stopped);
+        hostListener.vmStatusChanged(event);
+        
+        // Ensure only 1 removed
+        verify(builder).forgetAbout(1);
+        verify(builder, never()).forgetAbout(2);
+    }
+
+    private void startVMs() throws InterruptedException {
+        final Set<Integer> started = new HashSet<>();
+        started.add(1);
+        started.add(2);
+        
+        // Trigger a change event
+        VmStatusChangeEvent event = mock(VmStatusChangeEvent.class);
+        when(event.getStarted()).thenReturn(started);
+        when(event.getTerminated()).thenReturn(Collections.emptySet());
+        hostListener.vmStatusChanged(event);
+    }
+    
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-cpu/agent/src/test/java/com/redhat/thermostat/vm/cpu/agent/internal/VmCpuStatBuilderTest.java	Mon Jan 07 15:48:38 2013 -0500
@@ -0,0 +1,171 @@
+/*
+ * Copyright 2012 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.vm.cpu.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.Matchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import org.junit.Test;
+
+import com.redhat.thermostat.common.Clock;
+import com.redhat.thermostat.storage.model.VmCpuStat;
+import com.redhat.thermostat.test.Bug;
+import com.redhat.thermostat.vm.cpu.agent.internal.ProcessStatusInfo;
+import com.redhat.thermostat.vm.cpu.agent.internal.ProcessStatusInfoBuilder;
+import com.redhat.thermostat.vm.cpu.agent.internal.VmCpuStatBuilder;
+
+public class VmCpuStatBuilderTest {
+
+    @Test
+    public void testBuilderKnowsNothing() {
+        Clock clock = mock(Clock.class);
+        ProcessStatusInfoBuilder statusBuilder = mock(ProcessStatusInfoBuilder.class);
+        int cpuCount = 0;
+        long ticksPerSecond = 0;
+        VmCpuStatBuilder builder = new VmCpuStatBuilder(clock, cpuCount, ticksPerSecond, statusBuilder);
+
+        assertFalse(builder.knowsAbout(0));
+        assertFalse(builder.knowsAbout(1));
+        assertFalse(builder.knowsAbout(Integer.MIN_VALUE));
+        assertFalse(builder.knowsAbout(Integer.MAX_VALUE));
+
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testBuilderThrowsOnBuildOfUnknownPid() {
+        Clock clock = mock(Clock.class);
+        int cpuCount = 0;
+        long ticksPerSecond = 0;
+        ProcessStatusInfoBuilder statusBuilder = mock(ProcessStatusInfoBuilder.class);
+        VmCpuStatBuilder builder = new VmCpuStatBuilder(clock, cpuCount, ticksPerSecond, statusBuilder);
+        builder.build(0);
+    }
+
+    @Test
+    public void testBuildNullOnInsufficentInformation() {
+        int PID = 0;
+        int cpuCount = 0;
+        long ticksPerSecond = 0;
+        final long CLOCK1 = 10000;
+        final long CLOCK2 = 20000;
+        final ProcessStatusInfo initialInfo = new ProcessStatusInfo(PID, 1, 2);
+        final ProcessStatusInfo laterInfo = null;
+
+        Clock clock = mock(Clock.class);
+        when(clock.getMonotonicTimeNanos()).thenReturn((long) (CLOCK1 * 1E6)).thenReturn((long) (CLOCK2 * 1E6));
+
+        ProcessStatusInfoBuilder statusBuilder = mock(ProcessStatusInfoBuilder.class);
+        when(statusBuilder.build(any(Integer.class))).thenReturn(initialInfo).thenReturn(laterInfo).thenReturn(null);
+
+        VmCpuStatBuilder builder = new VmCpuStatBuilder(clock, cpuCount, ticksPerSecond, statusBuilder);
+
+        builder.learnAbout(PID);
+        assertEquals(null, builder.build(PID));
+    }
+
+    @Test
+    public void testSaneBuild() {
+        final int PID = 0;
+
+        final int CPU_COUNT = 3;
+
+        final long USER_INITIAL_TICKS = 1;
+        final long KERNEL_INITIAL_TICKS = 1;
+
+        final long USER_LATER_TICKS = 10;
+        final long KERNEL_LATER_TICKS = 10;
+
+        final long CLOCK1 = 10000;
+        final long CLOCK2 = 20000;
+
+        final long TICKS_PER_SECOND = 100;
+
+        final double CPU_LOAD_PERCENT =
+                100.0
+                * ((USER_LATER_TICKS + KERNEL_LATER_TICKS) - (USER_INITIAL_TICKS + KERNEL_INITIAL_TICKS))
+                / TICKS_PER_SECOND
+                / ((CLOCK2 - CLOCK1) * 1E-3 /* millis to seconds */)
+                / CPU_COUNT;
+
+        final ProcessStatusInfo initialInfo = new ProcessStatusInfo(PID, USER_INITIAL_TICKS, KERNEL_INITIAL_TICKS);
+        final ProcessStatusInfo laterInfo = new ProcessStatusInfo(PID, USER_LATER_TICKS, KERNEL_LATER_TICKS);
+
+        Clock clock = mock(Clock.class);
+        when(clock.getRealTimeMillis()).thenReturn(CLOCK2);
+        when(clock.getMonotonicTimeNanos()).thenReturn((long) (CLOCK1 * 1E6)).thenReturn((long) (CLOCK2 * 1E6));
+
+        ProcessStatusInfoBuilder statusBuilder = mock(ProcessStatusInfoBuilder.class);
+        when(statusBuilder.build(any(Integer.class))).thenReturn(initialInfo).thenReturn(laterInfo).thenReturn(null);
+
+        VmCpuStatBuilder builder = new VmCpuStatBuilder(clock, CPU_COUNT, TICKS_PER_SECOND, statusBuilder);
+
+        builder.learnAbout(PID);
+        VmCpuStat stat = builder.build(PID);
+
+        assertNotNull(stat);
+        assertEquals(PID, stat.getVmId());
+        assertEquals(CLOCK2, stat.getTimeStamp());
+        assertEquals(CPU_LOAD_PERCENT, stat.getCpuLoad(), 0.0001);
+    }
+
+    @Bug(id="1051",
+            summary="Avoid exceptions when reading /proc/ for dead processes",
+            url="http://icedtea.classpath.org/bugzilla/show_bug.cgi?id=1051")
+    @Test
+    public void testNoExceptionForBuilderLearningAboutDeadProcess() {
+        Clock clock = mock(Clock.class);
+        when(clock.getMonotonicTimeNanos()).thenReturn((long) (10000 * 1E6));
+        ProcessStatusInfoBuilder procBuilder = mock(ProcessStatusInfoBuilder.class);
+        // This thing returns null if the /proc entry goes away.  Rather than try to
+        // 'guess' at a pid that will not be present during test, just mock this.
+        when(procBuilder.build(any(Integer.class))).thenReturn(null);
+        VmCpuStatBuilder builder = new VmCpuStatBuilder(clock, 3, 100, procBuilder);
+        // If we can't handle a process' /proc entry disappearing, the next line
+        // will throw exception.  If it does not, then we are okay.
+        try {
+            builder.learnAbout(0);
+        } catch (Exception e) {
+            // Shouldn't happen.
+            assertTrue(false);
+        }
+    }
+}
--- a/vm-cpu/client-core/pom.xml	Mon Jan 07 15:46:47 2013 -0500
+++ b/vm-cpu/client-core/pom.xml	Mon Jan 07 15:48:38 2013 -0500
@@ -71,5 +71,10 @@
       <version>${project.version}</version>
       <scope>test</scope>
     </dependency>
+    <dependency>
+      <groupId>com.redhat.thermostat</groupId>
+      <artifactId>thermostat-vm-cpu-common</artifactId>
+      <version>${project.version}</version>
+    </dependency>
   </dependencies>
 </project>
--- a/vm-cpu/client-core/src/main/java/com/redhat/thermostat/vm/cpu/client/core/VmCpuService.java	Mon Jan 07 15:46:47 2013 -0500
+++ b/vm-cpu/client-core/src/main/java/com/redhat/thermostat/vm/cpu/client/core/VmCpuService.java	Mon Jan 07 15:48:38 2013 -0500
@@ -41,10 +41,10 @@
 import com.redhat.thermostat.client.core.NameMatchingRefFilter;
 import com.redhat.thermostat.client.core.controllers.InformationServiceController;
 import com.redhat.thermostat.common.ApplicationService;
-import com.redhat.thermostat.common.dao.VmCpuStatDAO;
 import com.redhat.thermostat.common.dao.VmRef;
 import com.redhat.thermostat.common.utils.OSGIUtils;
 import com.redhat.thermostat.vm.cpu.client.core.internal.VmCpuController;
+import com.redhat.thermostat.vm.cpu.common.VmCpuStatDAO;
 
 public class VmCpuService implements InformationService<VmRef> {
     
--- a/vm-cpu/client-core/src/main/java/com/redhat/thermostat/vm/cpu/client/core/internal/Activator.java	Mon Jan 07 15:46:47 2013 -0500
+++ b/vm-cpu/client-core/src/main/java/com/redhat/thermostat/vm/cpu/client/core/internal/Activator.java	Mon Jan 07 15:48:38 2013 -0500
@@ -50,9 +50,9 @@
 import com.redhat.thermostat.common.Constants;
 import com.redhat.thermostat.common.MultipleServiceTracker;
 import com.redhat.thermostat.common.MultipleServiceTracker.Action;
-import com.redhat.thermostat.common.dao.VmCpuStatDAO;
 import com.redhat.thermostat.common.dao.VmRef;
 import com.redhat.thermostat.vm.cpu.client.core.VmCpuService;
+import com.redhat.thermostat.vm.cpu.common.VmCpuStatDAO;
 
 public class Activator implements BundleActivator {
 
--- a/vm-cpu/client-core/src/main/java/com/redhat/thermostat/vm/cpu/client/core/internal/VmCpuController.java	Mon Jan 07 15:46:47 2013 -0500
+++ b/vm-cpu/client-core/src/main/java/com/redhat/thermostat/vm/cpu/client/core/internal/VmCpuController.java	Mon Jan 07 15:48:38 2013 -0500
@@ -49,7 +49,6 @@
 import com.redhat.thermostat.common.NotImplementedException;
 import com.redhat.thermostat.common.Timer;
 import com.redhat.thermostat.common.Timer.SchedulingType;
-import com.redhat.thermostat.common.dao.VmCpuStatDAO;
 import com.redhat.thermostat.common.dao.VmRef;
 import com.redhat.thermostat.common.locale.Translate;
 import com.redhat.thermostat.storage.model.DiscreteTimeData;
@@ -57,6 +56,7 @@
 import com.redhat.thermostat.vm.cpu.client.core.VmCpuView;
 import com.redhat.thermostat.vm.cpu.client.core.VmCpuViewProvider;
 import com.redhat.thermostat.vm.cpu.client.locale.LocaleResources;
+import com.redhat.thermostat.vm.cpu.common.VmCpuStatDAO;
 
 public class VmCpuController implements InformationServiceController<VmRef> {
     private static final Translate<LocaleResources> translator = LocaleResources.createLocalizer();
--- a/vm-cpu/client-core/src/test/java/com/redhat/thermostat/vm/cpu/client/core/internal/ActivatorTest.java	Mon Jan 07 15:46:47 2013 -0500
+++ b/vm-cpu/client-core/src/test/java/com/redhat/thermostat/vm/cpu/client/core/internal/ActivatorTest.java	Mon Jan 07 15:48:38 2013 -0500
@@ -45,9 +45,9 @@
 
 import com.redhat.thermostat.client.core.InformationService;
 import com.redhat.thermostat.common.ApplicationService;
-import com.redhat.thermostat.common.dao.VmCpuStatDAO;
 import com.redhat.thermostat.test.StubBundleContext;
 import com.redhat.thermostat.vm.cpu.client.core.VmCpuService;
+import com.redhat.thermostat.vm.cpu.common.VmCpuStatDAO;
 
 public class ActivatorTest {
     
--- a/vm-cpu/client-core/src/test/java/com/redhat/thermostat/vm/cpu/client/core/internal/VmCpuControllerTest.java	Mon Jan 07 15:46:47 2013 -0500
+++ b/vm-cpu/client-core/src/test/java/com/redhat/thermostat/vm/cpu/client/core/internal/VmCpuControllerTest.java	Mon Jan 07 15:48:38 2013 -0500
@@ -54,12 +54,12 @@
 import com.redhat.thermostat.common.ApplicationService;
 import com.redhat.thermostat.common.Timer;
 import com.redhat.thermostat.common.TimerFactory;
-import com.redhat.thermostat.common.dao.VmCpuStatDAO;
 import com.redhat.thermostat.common.dao.VmRef;
 import com.redhat.thermostat.storage.model.VmCpuStat;
 import com.redhat.thermostat.vm.cpu.client.core.VmCpuView;
 import com.redhat.thermostat.vm.cpu.client.core.VmCpuViewProvider;
 import com.redhat.thermostat.vm.cpu.client.core.internal.VmCpuController;
+import com.redhat.thermostat.vm.cpu.common.VmCpuStatDAO;
 
 
 public class VmCpuControllerTest {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-cpu/common/pom.xml	Mon Jan 07 15:48:38 2013 -0500
@@ -0,0 +1,68 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <artifactId>thermostat-vm-cpu</artifactId>
+    <groupId>com.redhat.thermostat</groupId>
+    <version>0.5.0-SNAPSHOT</version>
+  </parent>
+  <artifactId>thermostat-vm-cpu-common</artifactId>
+  <packaging>bundle</packaging>
+  <name>Thermostat VM CPU Common plugin</name>
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.felix</groupId>
+        <artifactId>maven-bundle-plugin</artifactId>
+        <extensions>true</extensions>
+        <configuration>
+          <instructions>
+            <Bundle-Vendor>Red Hat, Inc.</Bundle-Vendor>
+            <Bundle-SymbolicName>com.redhat.thermostat.vm.cpu.common</Bundle-SymbolicName>
+            <Bundle-Activator>com.redhat.thermostat.vm.cpu.common.internal.Activator</Bundle-Activator>
+            <Export-Package>
+              com.redhat.thermostat.vm.cpu.common
+            </Export-Package>
+            <Private-Package>
+              com.redhat.thermostat.vm.cpu.common.internal
+            </Private-Package>
+            <!-- Do not autogenerate uses clauses in Manifests -->
+            <_nouses>true</_nouses>
+          </instructions>
+        </configuration>
+      </plugin>
+    </plugins>
+  </build>
+  <dependencies>
+    <dependency>
+      <groupId>junit</groupId>
+      <artifactId>junit</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.mockito</groupId>
+      <artifactId>mockito-core</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.osgi</groupId>
+      <artifactId>org.osgi.core</artifactId>
+      <scope>provided</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.osgi</groupId>
+      <artifactId>org.osgi.compendium</artifactId>
+      <scope>provided</scope>
+    </dependency>
+    <dependency>
+      <groupId>com.redhat.thermostat</groupId>
+      <artifactId>thermostat-common-core</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>com.redhat.thermostat</groupId>
+      <artifactId>thermostat-storage-core</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+  </dependencies>
+</project>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-cpu/common/src/main/java/com/redhat/thermostat/vm/cpu/common/VmCpuStatDAO.java	Mon Jan 07 15:48:38 2013 -0500
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2012 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.vm.cpu.common;
+
+import java.util.List;
+
+import com.redhat.thermostat.common.dao.VmRef;
+import com.redhat.thermostat.storage.core.Category;
+import com.redhat.thermostat.storage.core.Key;
+import com.redhat.thermostat.storage.model.VmCpuStat;
+
+public interface VmCpuStatDAO {
+
+    static final Key<Double> vmCpuLoadKey = new Key<>("cpuLoad", false);
+
+    static final Category vmCpuStatCategory = new Category("vm-cpu-stats",
+            Key.AGENT_ID, Key.VM_ID, Key.TIMESTAMP, vmCpuLoadKey);
+
+    public abstract List<VmCpuStat> getLatestVmCpuStats(VmRef ref, long since);
+
+    public abstract void putVmCpuStat(VmCpuStat stat);
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-cpu/common/src/main/java/com/redhat/thermostat/vm/cpu/common/internal/Activator.java	Mon Jan 07 15:48:38 2013 -0500
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2013 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.vm.cpu.common.internal;
+
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+import org.osgi.framework.ServiceRegistration;
+import org.osgi.util.tracker.ServiceTracker;
+
+import com.redhat.thermostat.storage.core.Storage;
+import com.redhat.thermostat.vm.cpu.common.VmCpuStatDAO;
+
+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);
+                VmCpuStatDAO vmCpuStatDao = new VmCpuStatDAOImpl(storage);
+                reg = context.registerService(VmCpuStatDAO.class.getName(), vmCpuStatDao, null);
+                return super.addingService(reference);
+            }
+            
+            @Override
+            public void removedService(ServiceReference reference,
+                    Object service) {
+                reg.unregister();
+                super.removedService(reference, service);
+            }
+        };
+        tracker.open();
+    }
+
+    @Override
+    public void stop(BundleContext context) throws Exception {
+        tracker.close();
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-cpu/common/src/main/java/com/redhat/thermostat/vm/cpu/common/internal/VmCpuStatDAOImpl.java	Mon Jan 07 15:48:38 2013 -0500
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2012 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.vm.cpu.common.internal;
+
+import java.util.List;
+
+import com.redhat.thermostat.common.dao.VmLatestPojoListGetter;
+import com.redhat.thermostat.common.dao.VmRef;
+import com.redhat.thermostat.storage.core.Storage;
+import com.redhat.thermostat.storage.model.VmCpuStat;
+import com.redhat.thermostat.vm.cpu.common.VmCpuStatDAO;
+
+public class VmCpuStatDAOImpl implements VmCpuStatDAO {
+
+    private final Storage storage;
+    private final VmLatestPojoListGetter<VmCpuStat> getter;
+
+    VmCpuStatDAOImpl(Storage storage) {
+        this.storage = storage;
+        storage.registerCategory(vmCpuStatCategory);
+        this.getter = new VmLatestPojoListGetter<>(storage, vmCpuStatCategory, VmCpuStat.class);
+    }
+
+    @Override
+    public List<VmCpuStat> getLatestVmCpuStats(VmRef ref, long since) {
+        return getter.getLatest(ref, since);
+    }
+
+    @Override
+    public void putVmCpuStat(VmCpuStat stat) {
+        storage.putPojo(vmCpuStatCategory, false, stat);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-cpu/common/src/test/java/com/redhat/thermostat/vm/cpu/common/internal/ActivatorTest.java	Mon Jan 07 15:48:38 2013 -0500
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2013 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */ 
+
+package com.redhat.thermostat.vm.cpu.common.internal;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
+
+import org.junit.Test;
+
+import com.redhat.thermostat.storage.core.Storage;
+import com.redhat.thermostat.test.StubBundleContext;
+import com.redhat.thermostat.vm.cpu.common.VmCpuStatDAO;
+
+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(VmCpuStatDAO.class.getName(), VmCpuStatDAOImpl.class));
+
+        activator.stop(context);
+
+        assertEquals(0, context.getServiceListeners().size());
+        assertEquals(1, context.getAllServices().size());
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm-cpu/common/src/test/java/com/redhat/thermostat/vm/cpu/common/internal/VmCpuStatDAOTest.java	Mon Jan 07 15:48:38 2013 -0500
@@ -0,0 +1,140 @@
+/*
+ * Copyright 2012 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.vm.cpu.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.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+
+import com.redhat.thermostat.common.dao.HostRef;
+import com.redhat.thermostat.common.dao.VmRef;
+import com.redhat.thermostat.storage.core.Cursor;
+import com.redhat.thermostat.storage.core.Key;
+import com.redhat.thermostat.storage.core.Query;
+import com.redhat.thermostat.storage.core.Storage;
+import com.redhat.thermostat.storage.core.Query.Criteria;
+import com.redhat.thermostat.storage.model.VmCpuStat;
+import com.redhat.thermostat.test.MockQuery;
+import com.redhat.thermostat.vm.cpu.common.VmCpuStatDAO;
+
+public class VmCpuStatDAOTest {
+
+    private static final Long TIMESTAMP = 1234L;
+    private static final Integer VM_ID = 321;
+    private static final Double CPU_LOAD = 9.9;
+
+    private VmCpuStat cpuStat;
+
+    @Before
+    public void setUp() {
+        cpuStat = new VmCpuStat(TIMESTAMP, VM_ID, CPU_LOAD);
+    }
+
+    @Test
+    public void testCategory() {
+        assertEquals("vm-cpu-stats", VmCpuStatDAO.vmCpuStatCategory.getName());
+        Collection<Key<?>> keys = VmCpuStatDAO.vmCpuStatCategory.getKeys();
+        assertTrue(keys.contains(new Key<>("agentId", true)));
+        assertTrue(keys.contains(new Key<Long>("timeStamp", false)));
+        assertTrue(keys.contains(new Key<Integer>("vmId", true)));
+        assertTrue(keys.contains(new Key<Integer>("cpuLoad", false)));
+        assertEquals(4, keys.size());
+    }
+
+    @Test
+    public void testGetLatestCpuStatsBasic() {
+
+        @SuppressWarnings("unchecked")
+        Cursor<VmCpuStat> cursor = mock(Cursor.class);
+        when(cursor.hasNext()).thenReturn(true).thenReturn(false);
+        when(cursor.next()).thenReturn(cpuStat);
+
+        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(VmCpuStat.class))).thenReturn(cursor);
+
+        HostRef hostRef = mock(HostRef.class);
+        when(hostRef.getAgentId()).thenReturn("system");
+
+        VmRef vmRef = mock(VmRef.class);
+        when(vmRef.getAgent()).thenReturn(hostRef);
+        when(vmRef.getId()).thenReturn(VM_ID);
+
+
+        VmCpuStatDAO dao = new VmCpuStatDAOImpl(storage);
+        List<VmCpuStat> vmCpuStats = dao.getLatestVmCpuStats(vmRef, Long.MIN_VALUE);
+
+        ArgumentCaptor<MockQuery> arg = ArgumentCaptor.forClass(MockQuery.class);
+        verify(storage).findAllPojos(arg.capture(), same(VmCpuStat.class));
+        assertTrue(arg.getValue().hasWhereClause(Key.TIMESTAMP, Criteria.GREATER_THAN, Long.MIN_VALUE));
+
+        assertEquals(1, vmCpuStats.size());
+        VmCpuStat stat = vmCpuStats.get(0);
+        assertEquals(TIMESTAMP, (Long) stat.getTimeStamp());
+        assertEquals(CPU_LOAD, stat.getCpuLoad(), 0.001);
+        assertEquals(VM_ID, (Integer) stat.getVmId());
+    }
+
+    @Test
+    public void testPutVmCpuStat() {
+        Storage storage = mock(Storage.class);
+        VmCpuStat stat = new VmCpuStat(TIMESTAMP, VM_ID, CPU_LOAD);
+        VmCpuStatDAO dao = new VmCpuStatDAOImpl(storage);
+        dao.putVmCpuStat(stat);
+
+        verify(storage).putPojo(VmCpuStatDAO.vmCpuStatCategory, false, stat);
+
+    }
+}
--- a/vm-cpu/pom.xml	Mon Jan 07 15:46:47 2013 -0500
+++ b/vm-cpu/pom.xml	Mon Jan 07 15:48:38 2013 -0500
@@ -51,8 +51,10 @@
   <name>Thermostat VM CPU plugin</name>
 
   <modules>
+    <module>agent</module>
     <module>client-core</module>
     <module>client-swing</module>
+    <module>common</module>
   </modules>
 
 </project>