changeset 708:da7f134762f6

Make backends OSGi services (and port system backend to its own sub module) review-thread: http://icedtea.classpath.org/pipermail/thermostat/2012-October/003762.html reviewed-by: omajid
author Mario Torre <neugens.limasoftware@gmail.com>
date Fri, 19 Oct 2012 21:58:18 +0200
parents 3d4608e2e01f
children decac7295693
files agent/cli/src/main/java/com/redhat/thermostat/agent/cli/AgentApplication.java agent/cli/src/main/java/com/redhat/thermostat/agent/cli/impl/Activator.java agent/cli/src/test/java/com/redhat/thermostat/agent/cli/AgentApplicationTest.java agent/core/pom.xml agent/core/src/main/java/com/redhat/thermostat/agent/Agent.java agent/core/src/main/java/com/redhat/thermostat/agent/config/AgentConfigsUtils.java agent/core/src/main/java/com/redhat/thermostat/agent/config/AgentProperties.java agent/core/src/main/java/com/redhat/thermostat/agent/config/AgentStartupConfiguration.java agent/core/src/main/java/com/redhat/thermostat/backend/Backend.java agent/core/src/main/java/com/redhat/thermostat/backend/BackendConfigurationLoader.java agent/core/src/main/java/com/redhat/thermostat/backend/BackendID.java agent/core/src/main/java/com/redhat/thermostat/backend/BackendRegistry.java agent/core/src/main/java/com/redhat/thermostat/backend/BackendService.java agent/core/src/main/java/com/redhat/thermostat/backend/sample/SampleBackend.java agent/core/src/main/java/com/redhat/thermostat/backend/system/CpuStatBuilder.java agent/core/src/main/java/com/redhat/thermostat/backend/system/DistributionInformation.java agent/core/src/main/java/com/redhat/thermostat/backend/system/DistributionInformationSource.java agent/core/src/main/java/com/redhat/thermostat/backend/system/EtcOsRelease.java agent/core/src/main/java/com/redhat/thermostat/backend/system/HostInfoBuilder.java agent/core/src/main/java/com/redhat/thermostat/backend/system/JvmStatDataExtractor.java agent/core/src/main/java/com/redhat/thermostat/backend/system/JvmStatHostListener.java agent/core/src/main/java/com/redhat/thermostat/backend/system/JvmStatVmClassListener.java agent/core/src/main/java/com/redhat/thermostat/backend/system/JvmStatVmListener.java agent/core/src/main/java/com/redhat/thermostat/backend/system/LsbRelease.java agent/core/src/main/java/com/redhat/thermostat/backend/system/MemoryStatBuilder.java agent/core/src/main/java/com/redhat/thermostat/backend/system/NetworkInfoBuilder.java agent/core/src/main/java/com/redhat/thermostat/backend/system/ProcDataSource.java agent/core/src/main/java/com/redhat/thermostat/backend/system/ProcessEnvironmentBuilder.java agent/core/src/main/java/com/redhat/thermostat/backend/system/ProcessStatusInfo.java agent/core/src/main/java/com/redhat/thermostat/backend/system/ProcessStatusInfoBuilder.java agent/core/src/main/java/com/redhat/thermostat/backend/system/SysConf.java agent/core/src/main/java/com/redhat/thermostat/backend/system/SystemBackend.java agent/core/src/main/java/com/redhat/thermostat/backend/system/VmCpuStatBuilder.java agent/core/src/main/java/com/redhat/thermostat/utils/ProcDataSource.java agent/core/src/test/java/com/redhat/thermostat/agent/AgentTest.java agent/core/src/test/java/com/redhat/thermostat/agent/TestUtils.java agent/core/src/test/java/com/redhat/thermostat/agent/config/AgentConfigsUtilsTest.java agent/core/src/test/java/com/redhat/thermostat/agent/config/AgentOptionParserTest.java agent/core/src/test/java/com/redhat/thermostat/backend/BackendConfigurationLoaderTest.java agent/core/src/test/java/com/redhat/thermostat/backend/BackendRegistryTest.java agent/core/src/test/java/com/redhat/thermostat/backend/sample/SampleBackendTest.java agent/core/src/test/java/com/redhat/thermostat/backend/system/CpuStatBuilderTest.java agent/core/src/test/java/com/redhat/thermostat/backend/system/DistributionInformationTest.java agent/core/src/test/java/com/redhat/thermostat/backend/system/EtcOsReleaseTest.java agent/core/src/test/java/com/redhat/thermostat/backend/system/HostInfoBuilderTest.java agent/core/src/test/java/com/redhat/thermostat/backend/system/JvmStatDataExtractorTest.java agent/core/src/test/java/com/redhat/thermostat/backend/system/JvmStatHostListenerTest.java agent/core/src/test/java/com/redhat/thermostat/backend/system/JvmStatVmClassListenerTest.java agent/core/src/test/java/com/redhat/thermostat/backend/system/LsbReleaseTest.java agent/core/src/test/java/com/redhat/thermostat/backend/system/MemoryStatBuilderTest.java agent/core/src/test/java/com/redhat/thermostat/backend/system/NetworkInfoBuilderTest.java agent/core/src/test/java/com/redhat/thermostat/backend/system/ProcDataSourceTest.java agent/core/src/test/java/com/redhat/thermostat/backend/system/ProcessEnvironmentBuilderTest.java agent/core/src/test/java/com/redhat/thermostat/backend/system/ProcessStatusInfoBuilderTest.java agent/core/src/test/java/com/redhat/thermostat/backend/system/SysConfTest.java agent/core/src/test/java/com/redhat/thermostat/backend/system/SystemBackendTest.java agent/core/src/test/java/com/redhat/thermostat/backend/system/VmCpuStatBuilderTest.java agent/core/src/test/java/com/redhat/thermostat/utils/ProcDataSourceTest.java client/core/src/main/java/com/redhat/thermostat/client/internal/HostFilterRegistry.java client/core/src/main/java/com/redhat/thermostat/client/internal/HostTreeDecoratorRegistry.java client/core/src/main/java/com/redhat/thermostat/client/internal/MainWindowControllerImpl.java client/core/src/main/java/com/redhat/thermostat/client/internal/MenuRegistry.java client/core/src/main/java/com/redhat/thermostat/client/internal/ThermostatExtensionRegistry.java client/core/src/main/java/com/redhat/thermostat/client/internal/VMInformationRegistry.java client/core/src/main/java/com/redhat/thermostat/client/internal/VMTreeDecoratorRegistry.java client/core/src/main/java/com/redhat/thermostat/client/internal/VmFilterRegistry.java client/core/src/test/java/com/redhat/thermostat/client/internal/MainWindowControllerImplTest.java client/core/src/test/java/com/redhat/thermostat/client/internal/MenuRegistryTest.java common/core/src/main/java/com/redhat/thermostat/common/TestUtils.java common/core/src/main/java/com/redhat/thermostat/common/ThermostatExtensionRegistry.java common/core/src/main/java/com/redhat/thermostat/common/dao/Countable.java common/core/src/test/java/com/redhat/thermostat/common/TestUtils.java distribution/config/commands/agent.properties distribution/pom.xml pom.xml system-backend/pom.xml system-backend/src/main/java/com/redhat/thermostat/backend/system/CpuStatBuilder.java system-backend/src/main/java/com/redhat/thermostat/backend/system/DistributionInformation.java system-backend/src/main/java/com/redhat/thermostat/backend/system/DistributionInformationSource.java system-backend/src/main/java/com/redhat/thermostat/backend/system/EtcOsRelease.java system-backend/src/main/java/com/redhat/thermostat/backend/system/HostInfoBuilder.java system-backend/src/main/java/com/redhat/thermostat/backend/system/JvmStatDataExtractor.java system-backend/src/main/java/com/redhat/thermostat/backend/system/JvmStatHostListener.java system-backend/src/main/java/com/redhat/thermostat/backend/system/JvmStatVmClassListener.java system-backend/src/main/java/com/redhat/thermostat/backend/system/JvmStatVmListener.java system-backend/src/main/java/com/redhat/thermostat/backend/system/LsbRelease.java system-backend/src/main/java/com/redhat/thermostat/backend/system/MemoryStatBuilder.java system-backend/src/main/java/com/redhat/thermostat/backend/system/NetworkInfoBuilder.java system-backend/src/main/java/com/redhat/thermostat/backend/system/ProcessEnvironmentBuilder.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/SysConf.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/main/java/com/redhat/thermostat/backend/system/osgi/SystemBackendActivator.java system-backend/src/test/java/com/redhat/thermostat/backend/system/CpuStatBuilderTest.java system-backend/src/test/java/com/redhat/thermostat/backend/system/DistributionInformationTest.java system-backend/src/test/java/com/redhat/thermostat/backend/system/EtcOsReleaseTest.java system-backend/src/test/java/com/redhat/thermostat/backend/system/HostInfoBuilderTest.java system-backend/src/test/java/com/redhat/thermostat/backend/system/JvmStatDataExtractorTest.java system-backend/src/test/java/com/redhat/thermostat/backend/system/JvmStatHostListenerTest.java system-backend/src/test/java/com/redhat/thermostat/backend/system/JvmStatVmClassListenerTest.java system-backend/src/test/java/com/redhat/thermostat/backend/system/LsbReleaseTest.java system-backend/src/test/java/com/redhat/thermostat/backend/system/MemoryStatBuilderTest.java system-backend/src/test/java/com/redhat/thermostat/backend/system/NetworkInfoBuilderTest.java system-backend/src/test/java/com/redhat/thermostat/backend/system/ProcessEnvironmentBuilderTest.java system-backend/src/test/java/com/redhat/thermostat/backend/system/ProcessStatusInfoBuilderTest.java system-backend/src/test/java/com/redhat/thermostat/backend/system/SysConfTest.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
diffstat 110 files changed, 5068 insertions(+), 5389 deletions(-) [+]
line wrap: on
line diff
--- a/agent/cli/src/main/java/com/redhat/thermostat/agent/cli/AgentApplication.java	Fri Oct 19 15:33:45 2012 -0400
+++ b/agent/cli/src/main/java/com/redhat/thermostat/agent/cli/AgentApplication.java	Fri Oct 19 21:58:18 2012 +0200
@@ -40,15 +40,18 @@
 import java.util.logging.Level;
 import java.util.logging.Logger;
 
-import org.apache.commons.cli.Options;
+import org.osgi.framework.BundleContext;
+
+import sun.misc.Signal;
+import sun.misc.SignalHandler;
 
 import com.redhat.thermostat.agent.Agent;
 import com.redhat.thermostat.agent.command.ConfigurationServer;
 import com.redhat.thermostat.agent.config.AgentConfigsUtils;
 import com.redhat.thermostat.agent.config.AgentOptionParser;
 import com.redhat.thermostat.agent.config.AgentStartupConfiguration;
-import com.redhat.thermostat.backend.BackendLoadException;
 import com.redhat.thermostat.backend.BackendRegistry;
+import com.redhat.thermostat.backend.BackendService;
 import com.redhat.thermostat.common.Constants;
 import com.redhat.thermostat.common.LaunchException;
 import com.redhat.thermostat.common.ThreadPoolTimerFactory;
@@ -69,9 +72,6 @@
 import com.redhat.thermostat.common.utils.LoggingUtils;
 import com.redhat.thermostat.common.utils.OSGIUtils;
 
-import sun.misc.Signal;
-import sun.misc.SignalHandler;
-
 @SuppressWarnings("restriction")
 public final class AgentApplication extends BasicCommand {
 
@@ -80,6 +80,12 @@
     private AgentStartupConfiguration configuration;
     private AgentOptionParser parser;
     
+    private BundleContext bundleContext;
+    
+    public AgentApplication(BundleContext bundleContext) {
+        this.bundleContext = bundleContext;
+    }
+    
     private void parseArguments(Arguments args) throws InvalidConfigurationException {
         configuration = AgentConfigsUtils.createAgentConfigs();
         parser = new AgentOptionParser(configuration, args);
@@ -135,19 +141,23 @@
 
         final ConfigurationServer configServer = OSGIUtils.getInstance().getService(ConfigurationServer.class);
         configServer.startListening(configuration.getConfigListenAddress());
-
+        
         BackendRegistry backendRegistry = null;
         try {
-            backendRegistry = new BackendRegistry(configuration, daoFactory);
-        } catch (BackendLoadException ble) {
-            logger.log(Level.SEVERE, "Could not get BackendRegistry instance.", ble);
+            backendRegistry = new BackendRegistry(bundleContext);
+            
+        } catch (Exception e) {
+            logger.log(Level.SEVERE, "Could not get BackendRegistry instance.", e);
             System.exit(Constants.EXIT_BACKEND_LOAD_ERROR);
         }
 
-        final Agent agent = new Agent(backendRegistry, configuration, daoFactory.getStorage(), daoFactory.getAgentInfoDAO(), daoFactory.getBackendInfoDAO());
+        final Agent agent = new Agent(backendRegistry, configuration, daoFactory);
         try {
             logger.fine("Starting agent.");
             agent.start();
+            
+            bundleContext.registerService(BackendService.class, new BackendService(), null);
+            
         } catch (LaunchException le) {
             logger.log(Level.SEVERE,
                     "Agent could not start, probably because a configured backend could not be activated.",
--- a/agent/cli/src/main/java/com/redhat/thermostat/agent/cli/impl/Activator.java	Fri Oct 19 15:33:45 2012 -0400
+++ b/agent/cli/src/main/java/com/redhat/thermostat/agent/cli/impl/Activator.java	Fri Oct 19 21:58:18 2012 +0200
@@ -54,7 +54,7 @@
     @Override
     public void start(BundleContext context) throws Exception {
         reg = new CommandRegistryImpl(context);
-        reg.registerCommands(Arrays.asList(new ServiceCommand(), new StorageCommand(), new AgentApplication()));
+        reg.registerCommands(Arrays.asList(new ServiceCommand(), new StorageCommand(), new AgentApplication(context)));
     }
 
     @Override
--- a/agent/cli/src/test/java/com/redhat/thermostat/agent/cli/AgentApplicationTest.java	Fri Oct 19 15:33:45 2012 -0400
+++ b/agent/cli/src/test/java/com/redhat/thermostat/agent/cli/AgentApplicationTest.java	Fri Oct 19 21:58:18 2012 +0200
@@ -37,18 +37,13 @@
 package com.redhat.thermostat.agent.cli;
 
 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 org.apache.commons.cli.Option;
-import org.apache.commons.cli.Options;
 import org.junit.After;
 import org.junit.Before;
-import org.junit.Ignore;
 import org.junit.Test;
-
-import com.redhat.thermostat.agent.cli.AgentApplication;
+import org.osgi.framework.BundleContext;
 
 public class AgentApplicationTest {
 
@@ -56,9 +51,14 @@
 
     private AgentApplication agent;
 
+    private BundleContext context;
+    
     @Before
     public void setUp() {
-        agent = new AgentApplication();
+        
+        context = mock(BundleContext.class);
+        
+        agent = new AgentApplication(context);
     }
 
     @After
@@ -77,47 +77,4 @@
         assertNotNull(agent.getDescription());
         assertNotNull(agent.getUsage());
     }
-
-    // TODO These options are provided by CommandInfo, we should check that an injected CommandInfo does
-    // result in correct results, but we should also have some test that the properties file that
-    // CommandInfo uses contains the correct set of options.
-    @Ignore
-    @Test
-    public void testOptions() {
-        Options options = agent.getOptions();
-        assertNotNull(options);
-        assertEquals(5, options.getOptions().size());
-
-        assertTrue(options.hasOption("saveOnExit"));
-        Option save = options.getOption("saveOnExit");
-        assertEquals("s", save.getOpt());
-        assertEquals("save the data on exit", save.getDescription());
-        assertFalse(save.isRequired());
-        assertFalse(save.hasArg());
-
-        assertTrue(options.hasOption("debug"));
-        Option debug = options.getOption("debug");
-        assertEquals("launch with debug console enabled", debug.getDescription());
-        assertFalse(debug.isRequired());
-        assertFalse(debug.hasArg());
-
-        assertTrue(options.hasOption("dbUrl"));
-        Option db = options.getOption("dbUrl");
-        assertEquals("d", db.getOpt());
-        assertEquals("connect to the given url", db.getDescription());
-        assertTrue(db.isRequired());
-        assertTrue(db.hasArg());
-
-        assertTrue(options.hasOption("username"));
-        Option user = options.getOption("username");
-        assertEquals("the username to use for authentication", user.getDescription());
-        assertFalse(user.isRequired());
-        assertTrue(user.hasArg());
-
-        assertTrue(options.hasOption("password"));
-        Option pass = options.getOption("password");
-        assertEquals("the password to use for authentication", pass.getDescription());
-        assertFalse(pass.isRequired());
-        assertTrue(pass.hasArg());
-    }
 }
--- a/agent/core/pom.xml	Fri Oct 19 15:33:45 2012 -0400
+++ b/agent/core/pom.xml	Fri Oct 19 21:58:18 2012 +0200
@@ -102,11 +102,11 @@
               com.redhat.thermostat.agent,
               com.redhat.thermostat.agent.config,
               com.redhat.thermostat.backend,
+              com.redhat.thermostat.utils.hostname,
+              com.redhat.thermostat.utils,
             </Export-Package>
             <Private-Package>
-              com.redhat.thermostat.utils.hostname,
               com.redhat.thermostat.backend.sample,
-              com.redhat.thermostat.backend.system,
             </Private-Package>
             <!-- Do not autogenerate uses clauses in Manifests -->
             <_nouses>true</_nouses>
--- a/agent/core/src/main/java/com/redhat/thermostat/agent/Agent.java	Fri Oct 19 15:33:45 2012 -0400
+++ b/agent/core/src/main/java/com/redhat/thermostat/agent/Agent.java	Fri Oct 19 21:58:18 2012 +0200
@@ -37,16 +37,22 @@
 package com.redhat.thermostat.agent;
 
 import java.util.ArrayList;
-import java.util.List;
+import java.util.Map;
 import java.util.UUID;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.logging.Level;
 import java.util.logging.Logger;
 
 import com.redhat.thermostat.agent.config.AgentStartupConfiguration;
 import com.redhat.thermostat.backend.Backend;
 import com.redhat.thermostat.backend.BackendRegistry;
+import com.redhat.thermostat.common.ActionEvent;
+import com.redhat.thermostat.common.ActionListener;
 import com.redhat.thermostat.common.LaunchException;
+import com.redhat.thermostat.common.ThermostatExtensionRegistry;
 import com.redhat.thermostat.common.dao.AgentInfoDAO;
 import com.redhat.thermostat.common.dao.BackendInfoDAO;
+import com.redhat.thermostat.common.dao.DAOFactory;
 import com.redhat.thermostat.common.model.AgentInformation;
 import com.redhat.thermostat.common.model.BackendInformation;
 import com.redhat.thermostat.common.storage.Storage;
@@ -63,67 +69,101 @@
     private final BackendRegistry backendRegistry;
     private final AgentStartupConfiguration config;
 
+    private final DAOFactory daoFactory;
+    
     private AgentInformation agentInfo;
     
-    private List<BackendInformation> backendInfos;
+    private Map<Backend, BackendInformation> backendInfos;
 
     private Storage storage;
     private AgentInfoDAO agentDao;
     private BackendInfoDAO backendDao;
     private boolean started = false;
 
-    public Agent(BackendRegistry backendRegistry, AgentStartupConfiguration config, Storage storage, AgentInfoDAO agentDao, BackendInfoDAO backendDao) {
-        this(backendRegistry, UUID.randomUUID(), config, storage, agentDao, backendDao);
+    private ActionListener<ThermostatExtensionRegistry.Action> backendRegistryListener =
+            new ActionListener<ThermostatExtensionRegistry.Action>()
+    {
+        @Override
+        public void actionPerformed(ActionEvent<ThermostatExtensionRegistry.Action> actionEvent) {
+            Backend backend = (Backend) actionEvent.getPayload();
+            
+            switch (actionEvent.getActionId()) {
+            case SERVICE_ADDED: {
+                // TODO: this backed has been already added, we should
+                // probably signal the user this
+                if (!backendInfos.containsKey(backend)) {
+
+                    logger.info("Adding backend: " + backend);
+                    
+                    backend.setDAOFactory(daoFactory);
+                    backend.activate();
+
+                    BackendInformation info = createBackendInformation(backend);
+                    backendDao.addBackendInformation(info);
+                    backendInfos.put(backend, info);                    
+                }
+            }
+            break;
+
+            case SERVICE_REMOVED: {
+                BackendInformation info = backendInfos.get(backend);
+                if (info != null) {
+                    logger.info("removing backend: " + backend);
+                    
+                    backend.deactivate();
+                    
+                    backendDao.removeBackendInformation(info);
+                    backendInfos.remove(backend); 
+                }
+            }
+            break;
+                
+            default:
+                logger.log(Level.WARNING, "received unknown event from BackendRegistry: " + actionEvent.getActionId());
+                break;
+            }
+        }
+    };
+    
+    public Agent(BackendRegistry backendRegistry, AgentStartupConfiguration config, DAOFactory daoFactory)
+    {
+        this(backendRegistry, UUID.randomUUID(), config, daoFactory);
     }
 
-    public Agent(BackendRegistry registry, UUID agentId, AgentStartupConfiguration config, Storage storage, AgentInfoDAO agentDao, BackendInfoDAO backendDao) {
+    public Agent(BackendRegistry registry, UUID agentId, AgentStartupConfiguration config, DAOFactory daoFactory)
+    {
         this.id = agentId;
         this.backendRegistry = registry;
         this.config = config;
-        this.storage = storage;
+        this.storage = daoFactory.getStorage();
         this.storage.setAgentId(agentId);
-        this.agentDao = agentDao;
-        this.backendDao = backendDao;
-    }
-
-    private void startBackends() throws LaunchException {
-        for (Backend be : backendRegistry.getAll()) {
-            logger.fine("Attempting to start backend: " + be.getName());
-            if (!be.activate()) {
-                logger.warning("Issue while starting backend: " + be.getName());
-                // When encountering issues during startup, we should not attempt to continue activating.
-                stopBackends();
-                throw new LaunchException("Could not activate backend: " + be.getName());
-            }
-        }
-    }
-
-    private void stopBackends() {
-        for (Backend be : backendRegistry.getAll()) {
-            logger.fine("Attempting to stop backend: " +be.getName());
-            if (!be.deactivate()) {
-                // When encountering issues during shutdown, we should attempt to shut down remaining backends.
-                logger.warning("Issue while deactivating backend: " + be.getName());
-            }
-        }
+        this.agentDao = daoFactory.getAgentInfoDAO();
+        this.backendDao = daoFactory.getBackendInfoDAO();
+        
+        this.daoFactory = daoFactory;
+        
+        backendInfos = new ConcurrentHashMap<>();
+        
+        backendRegistry.addActionListener(backendRegistryListener);
     }
 
     public synchronized void start() throws LaunchException {
         if (!started) {
-            startBackends();
             agentInfo = createAgentInformation();
             agentDao.addAgentInformation(agentInfo);
-
-            backendInfos = createBackendInformation();
-            for (BackendInformation backendInfo : backendInfos) {
-                backendDao.addBackendInformation(backendInfo);
-            }
+            
+            backendRegistry.start();
+            
             started = true;
         } else {
             logger.warning("Attempt to start agent when already started.");
         }
     }
 
+    Map<Backend, BackendInformation> getBackendInfos() {
+        return backendInfos;
+    }
+    
     private AgentInformation createAgentInformation() {
         AgentInformation agentInfo = new AgentInformation();
         agentInfo.setStartTime(config.getStartTime());
@@ -132,46 +172,34 @@
         return agentInfo;
     }
 
-    private List<BackendInformation> createBackendInformation() {
-        List<BackendInformation> results = new ArrayList<>();
+    private BackendInformation createBackendInformation(Backend backend) {
 
-        for (Backend backend : backendRegistry.getAll()) {
-            BackendInformation backendInfo = new BackendInformation();
-            backendInfo.setName(backend.getName());
-            backendInfo.setDescription(backend.getDescription());
-            backendInfo.setObserveNewJvm(backend.getObserveNewJvm());
-            backendInfo.setActive(true);
-            backendInfo.setPids(new ArrayList<Integer>());
-
-            results.add(backendInfo);
-
-        }
-        return results;
+        BackendInformation backendInfo = new BackendInformation();
+        backendInfo.setName(backend.getName());
+        backendInfo.setDescription(backend.getDescription());
+        backendInfo.setObserveNewJvm(backend.getObserveNewJvm());
+        backendInfo.setActive(true);
+        backendInfo.setPids(new ArrayList<Integer>());
+        
+        return backendInfo;
     }
 
     public synchronized void stop() {
         if (started) {
-
-            stopBackends();
-            removeBackendInformation();
+            
+            backendRegistry.stop();
+            
             if (config.purge()) {
                 removeAllAgentRelatedInformation();
             } else {
                 updateAgentStatusToStopped();
             }
-
             started = false;
         } else {
             logger.warning("Attempt to stop agent which is not active");
         }
     }
 
-    private void removeBackendInformation() {
-        for (BackendInformation info : backendInfos) {
-            backendDao.removeBackendInformation(info);
-        }
-    }
-
     private void removeAllAgentRelatedInformation() {
         System.out.println("purging database");
         logger.info("purging database");
--- a/agent/core/src/main/java/com/redhat/thermostat/agent/config/AgentConfigsUtils.java	Fri Oct 19 15:33:45 2012 -0400
+++ b/agent/core/src/main/java/com/redhat/thermostat/agent/config/AgentConfigsUtils.java	Fri Oct 19 21:58:18 2012 +0200
@@ -67,15 +67,6 @@
             throw new InvalidConfigurationException(e);
         }
         
-        if (properties.containsKey(AgentProperties.BACKENDS.name())) {
-            // this is a command separated list of backends
-            String backends = properties.getProperty(AgentProperties.BACKENDS.name());
-            configuration.parseBackends(backends.split(","));
-            
-        } else {
-            throw new InvalidConfigurationException(AgentProperties.BACKENDS + " property missing");
-        }
-        
         if (properties.containsKey(AgentProperties.DB_URL.name())) {
             String db = properties.getProperty(AgentProperties.DB_URL.name());
             configuration.setDatabaseURL(db);
--- a/agent/core/src/main/java/com/redhat/thermostat/agent/config/AgentProperties.java	Fri Oct 19 15:33:45 2012 -0400
+++ b/agent/core/src/main/java/com/redhat/thermostat/agent/config/AgentProperties.java	Fri Oct 19 21:58:18 2012 +0200
@@ -38,8 +38,6 @@
 
 public enum AgentProperties {
 
-    // backend list, comma separated
-    BACKENDS,
     DEBUG_CONSOLE,
     DB_URL,
     SAVE_ON_EXIT,
--- a/agent/core/src/main/java/com/redhat/thermostat/agent/config/AgentStartupConfiguration.java	Fri Oct 19 15:33:45 2012 -0400
+++ b/agent/core/src/main/java/com/redhat/thermostat/agent/config/AgentStartupConfiguration.java	Fri Oct 19 21:58:18 2012 +0200
@@ -51,8 +51,6 @@
 import com.redhat.thermostat.common.config.StartupConfiguration;
 
 public class AgentStartupConfiguration implements StartupConfiguration, AuthenticationConfiguration {
-
-    private List<BackendID> backends;
     
     private boolean debugConsole;
     private boolean purge;
@@ -65,49 +63,13 @@
     private String address;
     
     AgentStartupConfiguration() {
-        this.backends = new ArrayList<>();
     }
     
     @Override
     public String getDBConnectionString() {
         return url;
     }
-
-    void parseBackends(String[] backendsList) throws InvalidConfigurationException {
-        backends.clear();
-        
-        for (String simpleName : backendsList) {
-            String backendName = simpleName.trim();
-            
-            // a file must exist, at least with the class name
-            File backendSettings = ConfigUtils.getBackendPropertyFile(backendName);
-            if (!backendSettings.exists())
-                throw new InvalidConfigurationException("backends configuration " +
-                                                        "directory doesn't exist: " +
-                                                        backendSettings);
-            Properties backendProps = new Properties();
-            try {
-                backendProps.load(new FileInputStream(backendSettings));
-                
-            } catch (IOException e) {
-                throw new InvalidConfigurationException(e);
-            }
-            
-            String backendClass = backendProps.getProperty(BackendsProperties.BACKEND_CLASS.name());
-            if (backendClass == null) {
-                throw new InvalidConfigurationException("Class name not found for backend: " +
-                                                        backendName);
-            }
-            
-            BackendID backend = new BackendID(backendName, backendClass);
-            backends.add(backend);
-        }
-    }
     
-    public List<BackendID> getBackends() {
-        return backends;
-    }
-
     void setDebugConsole(boolean debugConsole) {
         this.debugConsole = debugConsole;
     }
--- a/agent/core/src/main/java/com/redhat/thermostat/backend/Backend.java	Fri Oct 19 15:33:45 2012 -0400
+++ b/agent/core/src/main/java/com/redhat/thermostat/backend/Backend.java	Fri Oct 19 21:58:18 2012 +0200
@@ -40,13 +40,13 @@
 import java.util.Map;
 import java.util.Map.Entry;
 
+import com.redhat.thermostat.backend.BackendRegistry;
 import com.redhat.thermostat.common.LaunchException;
 import com.redhat.thermostat.common.dao.DAOFactory;
 import com.redhat.thermostat.common.storage.Storage;
 
 /**
- * Represents a monitoring back-end. All the {@link Backend}s should be
- * registered with the {@link BackendRegistry}.
+ * Represents a monitoring back-end.
  */
 public abstract class Backend {
 
@@ -61,6 +61,10 @@
     
     private BackendID id;
 
+    public Backend(BackendID id) {
+        this.id = id;
+    }
+    
     /**
      * 
      * @param configMap a map containing the settings that this backend has been configured with.
@@ -177,8 +181,14 @@
 
     /**
      * Activate the {@link Backend}.  Based on the current configuration,
-     * begin pushing data to the {@link Storage} layer.  If the {@link Backend} is
-     * already active, this method should have no effect
+     * begin pushing data to the {@link Storage} layer.
+     * If the {@link Backend} is already active, this method should have no
+     * effect.
+     * 
+     * <br /><br />
+     * 
+     * This method is called by the framework when the {@link Backend} is
+     * registered.
      *
      * @return true on success, false if there was an error
      */
@@ -190,6 +200,11 @@
      * {@link #activate()}.  If the {@link Backend} is not active, this
      * method should have no effect
      *
+     * <br /><br />
+     * 
+     * This method is called by the framework when the {@link Backend} is
+     * deregistered.
+     *
      * @return true on success
      */
     public abstract boolean deactivate();
@@ -225,12 +240,51 @@
     public void setObserveNewJvm(boolean newValue) {
         observeNewJvm = newValue;
     }
-
-    void setID(BackendID backendID) {
-        this.id = backendID;
-    }
     
     public BackendID getID() {
         return id;
     }
+    
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + ((id == null) ? 0 : id.hashCode());
+        result = prime * result + ((vendor == null) ? 0 : vendor.hashCode());
+        result = prime * result + ((version == null) ? 0 : version.hashCode());
+        return result;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj)
+            return true;
+        if (obj == null)
+            return false;
+        if (getClass() != obj.getClass())
+            return false;
+        Backend other = (Backend) obj;
+        if (id == null) {
+            if (other.id != null)
+                return false;
+        } else if (!id.equals(other.id))
+            return false;
+        if (vendor == null) {
+            if (other.vendor != null)
+                return false;
+        } else if (!vendor.equals(other.vendor))
+            return false;
+        if (version == null) {
+            if (other.version != null)
+                return false;
+        } else if (!version.equals(other.version))
+            return false;
+        return true;
+    }
+
+    @Override
+    public String toString() {
+        return "Backend [version=" + version + ", vendor=" + vendor
+                + ", description=" + description + ", id=" + id + "]";
+    }
 }
--- a/agent/core/src/main/java/com/redhat/thermostat/backend/BackendConfigurationLoader.java	Fri Oct 19 15:33:45 2012 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,82 +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;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.Properties;
-
-import com.redhat.thermostat.common.config.ConfigUtils;
-import com.redhat.thermostat.common.config.InvalidConfigurationException;
-
-class BackendConfigurationLoader {
-
-    public Map<String, String> retrieveBackendConfigs(String name) throws InvalidConfigurationException {
-        
-        // reads the backend
-        File backend = new File(ConfigUtils.getBackendsBaseDirectory(), name);
-        backend = new File(backend, BackendsProperties.PROPERTY_FILE);
-        if (!backend.isFile() || !backend.canRead()) {
-            throw new InvalidConfigurationException("invalid backend configuration file: " + backend);
-        }
-        
-        Properties props = new Properties();
-        try {
-            props.load(new FileInputStream(backend));
-        } catch (IOException e) {
-            throw new InvalidConfigurationException("invalid backend configuration file", e);
-        }
-        
-        return toMap(props);
-    }
-
-    private static Map<String, String> toMap(Properties props) {
-
-        Map<String, String> configMap = new HashMap<>();
-        for (Entry<Object, Object> e : props.entrySet()) {
-            String key = (String) e.getKey();
-            String value = (String) e.getValue();
-            
-            configMap.put(key, value);
-        }
-        return configMap;
-    }
-}
--- a/agent/core/src/main/java/com/redhat/thermostat/backend/BackendID.java	Fri Oct 19 15:33:45 2012 -0400
+++ b/agent/core/src/main/java/com/redhat/thermostat/backend/BackendID.java	Fri Oct 19 21:58:18 2012 +0200
@@ -58,4 +58,37 @@
     public String toString() {
         return simpleName + " = " + className;
     }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result
+                + ((className == null) ? 0 : className.hashCode());
+        result = prime * result
+                + ((simpleName == null) ? 0 : simpleName.hashCode());
+        return result;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj)
+            return true;
+        if (obj == null)
+            return false;
+        if (getClass() != obj.getClass())
+            return false;
+        BackendID other = (BackendID) obj;
+        if (className == null) {
+            if (other.className != null)
+                return false;
+        } else if (!className.equals(other.className))
+            return false;
+        if (simpleName == null) {
+            if (other.simpleName != null)
+                return false;
+        } else if (!simpleName.equals(other.simpleName))
+            return false;
+        return true;
+    }
 }
--- a/agent/core/src/main/java/com/redhat/thermostat/backend/BackendRegistry.java	Fri Oct 19 15:33:45 2012 -0400
+++ b/agent/core/src/main/java/com/redhat/thermostat/backend/BackendRegistry.java	Fri Oct 19 21:58:18 2012 +0200
@@ -36,82 +36,27 @@
  
 package com.redhat.thermostat.backend;
 
-import java.lang.reflect.Constructor;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.logging.Level;
 import java.util.logging.Logger;
 
-import com.redhat.thermostat.agent.config.AgentStartupConfiguration;
-import com.redhat.thermostat.common.dao.DAOFactory;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.framework.InvalidSyntaxException;
+
+import com.redhat.thermostat.backend.Backend;
+import com.redhat.thermostat.common.ThermostatExtensionRegistry;
 import com.redhat.thermostat.common.utils.LoggingUtils;
 
 /**
- * A registry for {@link Backend}s. Each {@link Backend} should call
- * {@link #register(Backend)} to register itself.
+ * A registry for {@link Backend}s. {@link Backend}s are responsible to be
+ * registered as OSGi services into the framework.
  */
-public class BackendRegistry {
+public class BackendRegistry extends ThermostatExtensionRegistry<Backend> {
 
     private static final Logger logger = LoggingUtils.getLogger(BackendRegistry.class);
 
-    private final Map<String, Backend> registeredBackends;
-
-    public BackendRegistry(AgentStartupConfiguration config, DAOFactory daoFactory) throws BackendLoadException {
-        this(config, new BackendConfigurationLoader(), daoFactory);
-    }
-
-    public BackendRegistry(AgentStartupConfiguration config, BackendConfigurationLoader backendConfigLoader, DAOFactory daoFactory) throws BackendLoadException {
-
-        registeredBackends = new HashMap<String, Backend>();
-        
-        List<BackendID> backends = config.getBackends();
-
-        /*
-         * Configure the dynamic/custom backends
-         */
-        for (BackendID backendID : backends) {
-            logger.log(Level.FINE, "Initializing backend: \"" + backendID.getClassName() + "\"");
-            Backend backend = null;
-            try {
-                Class<? > c = Class.forName(backendID.getClassName());
-                Class<? extends Backend> narrowed = c.asSubclass(Backend.class);
-                Constructor<? extends Backend> backendConstructor = narrowed.getConstructor();
-                backend = backendConstructor.newInstance();
+    private static final String FILTER = "(" + Constants.OBJECTCLASS + "=" + Backend.class.getName() + ")";
 
-                backend.setDAOFactory(daoFactory);
-                backend.setID(backendID);
-                
-                backend.setInitialConfiguration(backendConfigLoader.retrieveBackendConfigs(backend.getName()));
-            } catch (Exception e) {
-                throw new BackendLoadException("Could not instantiate configured backend class: " + backendID.getClassName(), e);
-            }
-            register(backend);
-        }
-    }
-
-    private synchronized void register(Backend backend) throws BackendLoadException {
-        if (registeredBackends.containsKey(backend.getName())) {
-            throw new BackendLoadException("Attempt to register two backends with the same name: " + backend.getName());
-        }
-        registeredBackends.put(backend.getName(), backend);
-    }
-
-    private synchronized void unregister(Backend backend) {
-        registeredBackends.remove(backend.getName());
-    }
-
-    public synchronized Collection<Backend> getAll() {
-        return registeredBackends.values();
-    }
-
-    public synchronized Backend getByName(String name) {
-        for (Backend backend : registeredBackends.values()) {
-            if (backend.getName().equals((name))) {
-                return backend;
-            }
-        }
-        return null;
+    public BackendRegistry(BundleContext context) throws InvalidSyntaxException {
+        super(context, FILTER, Backend.class);
     }
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/agent/core/src/main/java/com/redhat/thermostat/backend/BackendService.java	Fri Oct 19 21:58:18 2012 +0200
@@ -0,0 +1,45 @@
+/*
+ * 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;
+
+/**
+ * Class used to signal that {@link Backend}s can start registering themselves
+ * into the Framework. It is registered by the Agent as soon as it is ready to
+ * accept new {@link Backend}s. 
+ */
+public class BackendService {
+}
--- a/agent/core/src/main/java/com/redhat/thermostat/backend/sample/SampleBackend.java	Fri Oct 19 15:33:45 2012 -0400
+++ /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.sample;
-
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-import com.redhat.thermostat.backend.Backend;
-import com.redhat.thermostat.common.utils.LoggingUtils;
-
-/**
- * Just an example backend implementation.  This is really just to test the loading and configuration mechanisms
- */
-public class SampleBackend extends Backend {
-    private final String NAME = "sample-backend";
-    private final String DESCRIPTION = "A backend which does nothing at all.";
-    private final String VENDOR = "Nobody";
-    private final String VERSION = "0.1";
-    private boolean currentlyActive = false;
-
-    private Logger logger = LoggingUtils.getLogger(SampleBackend.class);
-
-    private final Map<String, String> config = new HashMap<>();
-
-    public SampleBackend() {
-        super();
-    }
-
-    @Override
-    protected void setConfigurationValue(String name, String value) {
-        logger.log(Level.FINE, "Setting configuration value for backend: " + this.NAME);
-        logger.log(Level.FINE, "key: " + name + "    value: " + value);
-        config.put(name, value);
-    }
-
-    @Override
-    public String getName() {
-        return NAME;
-    }
-
-    @Override
-    public String getDescription() {
-        return DESCRIPTION;
-    }
-
-    @Override
-    public String getVendor() {
-        return VENDOR;
-    }
-
-    @Override
-    public String getVersion() {
-        return VERSION;
-    }
-
-    @Override
-    public Map<String, String> getConfigurationMap() {
-        return Collections.unmodifiableMap(config);
-    }
-
-    @Override
-    public String getConfigurationValue(String key) {
-        return config.get(key);
-    }
-
-    @Override
-    public boolean activate() {
-        currentlyActive = true;
-        return true;
-    }
-
-    @Override
-    public boolean deactivate() {
-        currentlyActive = false;
-        return true;
-    }
-
-    @Override
-    public boolean isActive() {
-        return currentlyActive;
-    }
-
-    @Override
-    public boolean attachToNewProcessByDefault() {
-        return false;
-    }
-
-    @Override
-    protected void setDAOFactoryAction() {
-        /* NO-OP */
-    }
-
-}
--- a/agent/core/src/main/java/com/redhat/thermostat/backend/system/CpuStatBuilder.java	Fri Oct 19 15:33:45 2012 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,136 +0,0 @@
-/*
- * Copyright 2012 Red Hat, Inc.
- *
- * This file is part of Thermostat.
- *
- * Thermostat is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published
- * by the Free Software Foundation; either version 2, or (at your
- * option) any later version.
- *
- * Thermostat is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Thermostat; see the file COPYING.  If not see
- * <http://www.gnu.org/licenses/>.
- *
- * Linking this code with other modules is making a combined work
- * based on this code.  Thus, the terms and conditions of the GNU
- * General Public License cover the whole combination.
- *
- * As a special exception, the copyright holders of this code give
- * you permission to link this code with independent modules to
- * produce an executable, regardless of the license terms of these
- * independent modules, and to copy and distribute the resulting
- * executable under terms of your choice, provided that you also
- * meet, for each linked independent module, the terms and conditions
- * of the license of that module.  An independent module is a module
- * which is not derived from or based on this code.  If you modify
- * this code, you may extend this exception to your version of the
- * library, but you are not obligated to do so.  If you do not wish
- * to do so, delete this exception statement from your version.
- */
-
-package com.redhat.thermostat.backend.system;
-
-import java.io.BufferedReader;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-import com.redhat.thermostat.common.Clock;
-import com.redhat.thermostat.common.model.CpuStat;
-import com.redhat.thermostat.common.utils.LoggingUtils;
-
-public class CpuStatBuilder {
-
-    private static final Logger logger = LoggingUtils.getLogger(CpuStatBuilder.class);
-
-    private final ProcDataSource dataSource;
-    private final Clock clock;
-    private final long ticksPerSecond;
-
-    private boolean initialized = false;
-
-    private long[] previousCpuTicks;
-    private long previousTime;
-
-    public CpuStatBuilder(Clock clock, ProcDataSource dataSource, long ticksPerSecond) {
-        this.dataSource = dataSource;
-        this.clock = clock;
-        this.ticksPerSecond = ticksPerSecond;
-    }
-
-    public void initialize() {
-        if (initialized) {
-            throw new IllegalStateException("already initialized");
-        }
-
-        previousTime = clock.getMonotonicTimeNanos();
-        previousCpuTicks = getCurrentCpuTicks();
-        initialized = true;
-    }
-
-    public CpuStat build() {
-        if (!initialized) {
-            throw new IllegalStateException("not initialized yet");
-        }
-
-        long currentRealTime = clock.getRealTimeMillis();
-        long currentTime = clock.getMonotonicTimeNanos();
-        long[] currentValues = getCurrentCpuTicks();
-
-        List<Double> cpuUsage = new ArrayList<Double>(currentValues.length);
-
-        double timeDelta = (currentTime - previousTime) * 1E-9;
-        for (int i = 0; i < currentValues.length; i++) {
-            long cpuTicksDelta = currentValues[i] - previousCpuTicks[i];
-            // 100 as in 100 percent.
-            cpuUsage.add(cpuTicksDelta * (100.0 / timeDelta / ticksPerSecond));
-        }
-        previousTime = currentTime;
-        previousCpuTicks = currentValues;
-
-        return new CpuStat(currentRealTime, cpuUsage);
-    }
-
-    private long[] getCurrentCpuTicks() {
-        int maxIndex = 0;
-        long[] values = new long[1];
-        try (BufferedReader reader = new BufferedReader(dataSource.getStatReader())) {
-            String line;
-            while ((line = reader.readLine()) != null) {
-                if (!line.startsWith("cpu")) {
-                    continue;
-                }
-                String[] parts = line.split("\\s");
-                if (!parts[0].matches("cpu\\d+")) {
-                    continue;
-                }
-
-                int cpuIndex = Integer.valueOf(parts[0].substring("cpu".length()));
-                if (cpuIndex > maxIndex) {
-                    long[] newValues = new long[cpuIndex+1];
-                    System.arraycopy(values, 0, newValues, 0, cpuIndex);
-                    values = newValues;
-                    maxIndex = cpuIndex;
-                }
-                values[cpuIndex] = Long.valueOf(parts[1]) + Long.valueOf(parts[2]) + Long.valueOf(parts[3]);
-            }
-        } catch (IOException e) {
-            logger.log(Level.WARNING, "error reading stat file", e);
-        }
-
-        return values;
-    }
-
-    public boolean isInitialized() {
-        return initialized;
-    }
-
-}
--- a/agent/core/src/main/java/com/redhat/thermostat/backend/system/DistributionInformation.java	Fri Oct 19 15:33:45 2012 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,90 +0,0 @@
-/*
- * Copyright 2012 Red Hat, Inc.
- *
- * This file is part of Thermostat.
- *
- * Thermostat is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published
- * by the Free Software Foundation; either version 2, or (at your
- * option) any later version.
- *
- * Thermostat is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Thermostat; see the file COPYING.  If not see
- * <http://www.gnu.org/licenses/>.
- *
- * Linking this code with other modules is making a combined work
- * based on this code.  Thus, the terms and conditions of the GNU
- * General Public License cover the whole combination.
- *
- * As a special exception, the copyright holders of this code give
- * you permission to link this code with independent modules to
- * produce an executable, regardless of the license terms of these
- * independent modules, and to copy and distribute the resulting
- * executable under terms of your choice, provided that you also
- * meet, for each linked independent module, the terms and conditions
- * of the license of that module.  An independent module is a module
- * which is not derived from or based on this code.  If you modify
- * this code, you may extend this exception to your version of the
- * library, but you are not obligated to do so.  If you do not wish
- * to do so, delete this exception statement from your version.
- */
-
-package com.redhat.thermostat.backend.system;
-
-import java.io.IOException;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-import com.redhat.thermostat.common.utils.LoggingUtils;
-
-public class DistributionInformation {
-
-    public static final String UNKNOWN_NAME = "Unknown Distribution";
-    public static final String UNKNOWN_VERSION = "Unknown Version";
-
-    private static final Logger logger = LoggingUtils.getLogger(DistributionInformation.class);
-
-    private final String name;
-    private final String version;
-
-    public DistributionInformation(String name, String version) {
-        this.name = name;
-        this.version = version;
-    }
-
-    public static DistributionInformation get() {
-        try {
-            return new EtcOsRelease().getDistributionInformation();
-        } catch (IOException e) {
-            logger.log(Level.WARNING, "unable to use os-release", e);
-        }
-        try {
-            return new LsbRelease().getDistributionInformation();
-        } catch (IOException e) {
-            logger.log(Level.WARNING, "unable to use lsb_release", e);
-        }
-        return new DistributionInformation(UNKNOWN_NAME, UNKNOWN_VERSION);
-    }
-
-    /**
-     * @return the name of the distribution, or {@link #UNKNOWN_NAME} if it can not be
-     * identified
-     */
-    public String getName() {
-        return name;
-    }
-
-    /**
-     * @return the release of the distribution or {@link #UNKNOWN_VERSION} if it can not be
-     * identified
-     */
-    public String getVersion() {
-        return version;
-    }
-
-}
\ No newline at end of file
--- a/agent/core/src/main/java/com/redhat/thermostat/backend/system/DistributionInformationSource.java	Fri Oct 19 15:33:45 2012 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,45 +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.IOException;
-
-public interface DistributionInformationSource {
-
-    public DistributionInformation getDistributionInformation() throws IOException;
-
-}
--- a/agent/core/src/main/java/com/redhat/thermostat/backend/system/EtcOsRelease.java	Fri Oct 19 15:33:45 2012 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,109 +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.FileReader;
-import java.io.IOException;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-import com.redhat.thermostat.common.utils.LoggingUtils;
-
-public class EtcOsRelease implements DistributionInformationSource {
-
-    private static final Logger logger = LoggingUtils.getLogger(EtcOsRelease.class);
-
-    private static final String OS_RELEASE = "/etc/os-release";
-
-    @Override
-    public DistributionInformation getDistributionInformation() throws IOException {
-        return getFromOsRelease();
-    }
-
-    public DistributionInformation getFromOsRelease() throws IOException {
-        return getFromOsRelease(OS_RELEASE);
-    }
-
-    public DistributionInformation getFromOsRelease(String releaseFile) throws IOException {
-        BufferedReader reader = null;
-        try {
-            reader = new BufferedReader(new FileReader(releaseFile));
-            return getFromOsRelease(reader);
-        } finally {
-            try {
-                if (reader != null) {
-                    reader.close();
-                }
-            } catch (IOException e) {
-                logger.log(Level.WARNING, "unable to close input stream", e);
-            }
-        }
-    }
-
-    public DistributionInformation getFromOsRelease(BufferedReader reader) throws IOException {
-        String name = "Linux";
-        String version = DistributionInformation.UNKNOWN_VERSION;
-        String line = null;
-        while ((line = reader.readLine()) != null) {
-            if (line.matches("^NAME *=.*")) {
-                name = readShellVariable(line);
-            }
-            if (line.matches("^VERSION *=.*")) {
-                version = readShellVariable(line);
-            }
-        }
-        return new DistributionInformation(name, version);
-    }
-
-    /** Reads and parses a shell variable declaration: {@code FOO="bar"}
-     *
-     * @return the value of the shell variable
-     */
-    private String readShellVariable(String line) {
-        // TODO we should try to handle shell quotes better
-        String result = line.substring(line.indexOf("=")+1);
-        result = result.trim();
-        if (result.startsWith("\"") && result.endsWith("\"")) {
-            result = result.substring(1, result.length()-1);
-            result = result.trim();
-        }
-        return result;
-    }
-
-
-}
--- a/agent/core/src/main/java/com/redhat/thermostat/backend/system/HostInfoBuilder.java	Fri Oct 19 15:33:45 2012 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,183 +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.net.InetAddress;
-import java.net.UnknownHostException;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-import com.redhat.thermostat.common.Constants;
-import com.redhat.thermostat.common.model.HostInfo;
-import com.redhat.thermostat.common.utils.LoggingUtils;
-import com.redhat.thermostat.utils.hostname.HostName;
-
-public class HostInfoBuilder {
-
-    private static final Logger logger = LoggingUtils.getLogger(HostInfoBuilder.class);
-
-    static class HostCpuInfo {
-        public final String model;
-        public final int count;
-
-        public HostCpuInfo(String model, int count) {
-            this.count = count;
-            this.model = model;
-        }
-    }
-
-    static class HostOsInfo {
-        public final String kernel;
-        public final String distribution;
-
-        public HostOsInfo(String kernel, String distribution) {
-            this.kernel = kernel;
-            this.distribution = distribution;
-        }
-    }
-
-    static class HostMemoryInfo {
-        public final long totalMemory;
-
-        public HostMemoryInfo(long totalMemory) {
-            this.totalMemory = totalMemory;
-        }
-    }
-
-    private final ProcDataSource dataSource;
-
-    public HostInfoBuilder(ProcDataSource dataSource) {
-        this.dataSource = dataSource;
-    }
-
-    public HostInfo build() {
-        String hostname = getHostName();
-        HostCpuInfo cpuInfo = getCpuInfo();
-        HostMemoryInfo memoryInfo = getMemoryInfo();
-        HostOsInfo osInfo = getOsInfo();
-
-        return new HostInfo(hostname, osInfo.distribution, osInfo.kernel, cpuInfo.model, cpuInfo.count, memoryInfo.totalMemory);
-    }
-
-    HostCpuInfo getCpuInfo() {
-        final String KEY_PROCESSOR_ID = "processor";
-        final String KEY_CPU_MODEL = "model name";
-        int cpuCount = 0;
-        String cpuModel = null;
-        try (BufferedReader bufferedReader = new BufferedReader(dataSource.getCpuInfoReader())) {
-            String line = null;
-            while ((line = bufferedReader.readLine()) != null) {
-                if (line.startsWith(KEY_PROCESSOR_ID)) {
-                    cpuCount++;
-                } else if (line.startsWith(KEY_CPU_MODEL)) {
-                    cpuModel = line.substring(line.indexOf(":") + 1).trim();
-                }
-            }
-        } catch (IOException ioe) {
-            logger.log(Level.WARNING, "unable to read cpu info");
-        }
-
-        logger.log(Level.FINEST, "cpuModel: " + cpuModel);
-        logger.log(Level.FINEST, "cpuCount: " + cpuCount);
-
-        return new HostCpuInfo(cpuModel, cpuCount);
-    }
-
-    HostMemoryInfo getMemoryInfo() {
-        long totalMemory = -1;
-        try (BufferedReader bufferedReader = new BufferedReader(dataSource.getMemInfoReader())) {
-            String[] memTotalParts = bufferedReader.readLine().split(" +");
-            long data = Long.valueOf(memTotalParts[1]);
-            String units = memTotalParts[2];
-            if (units.equals("kB")) {
-                totalMemory = data * Constants.KILOBYTES_TO_BYTES;
-            }
-        } catch (IOException ioe) {
-            logger.log(Level.WARNING, "unable to read memory info");
-        }
-
-        logger.log(Level.FINEST, "totalMemory: " + totalMemory + " bytes");
-        return new HostMemoryInfo(totalMemory);
-    }
-
-    HostOsInfo getOsInfo() {
-        return getOsInfo(DistributionInformation.get());
-    }
-
-    HostOsInfo getOsInfo(DistributionInformation distroInfo) {
-        String osName = distroInfo.getName() + " " + distroInfo.getVersion();
-        logger.log(Level.FINEST, "osName: " + osName);
-
-        String osKernel = System.getProperty("os.name") + " " + System.getProperty("os.version");
-        logger.log(Level.FINEST, "osKernel: " + osKernel);
-
-        return new HostOsInfo(osKernel, osName);
-    }
-
-    String getHostName() {
-        String hostname = null;
-        
-        try {
-            InetAddress localAddress = null;
-            localAddress = InetAddress.getLocalHost();
-            hostname = getHostName(localAddress);
-        } catch (UnknownHostException uhe) {
-            logger.log(Level.WARNING, "unable to get hostname", uhe);
-        }
-        
-        // if fails, try to get hostname without dns lookup
-        if (hostname == null) {
-            hostname = HostName.getLocalHostName();
-        }
-        
-        // still null, use localhost
-        if (hostname == null) {
-            hostname = Constants.AGENT_LOCAL_HOSTNAME;
-        }
-        
-        return hostname;
-    }
-
-    String getHostName(InetAddress localAddress) {
-        String hostname = localAddress.getCanonicalHostName();
-        logger.log(Level.FINEST, "hostname: " + hostname);
-        return hostname;
-    }
-
-}
--- a/agent/core/src/main/java/com/redhat/thermostat/backend/system/JvmStatDataExtractor.java	Fri Oct 19 15:33:45 2012 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,170 +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 com.redhat.thermostat.common.model.VmMemoryStat.Generation;
-
-import sun.jvmstat.monitor.Monitor;
-import sun.jvmstat.monitor.MonitorException;
-import sun.jvmstat.monitor.MonitoredVm;
-import sun.jvmstat.monitor.MonitoredVmUtil;
-
-/**
- * A helper class to provide type-safe access to commonly used jvmstat monitors
- * <p>
- * Implementation details: For local vms, jvmstat uses a ByteBuffer
- * corresponding to mmap()ed hsperfdata file. The hsperfdata file is updated
- * asynchronously by the vm that created the file. The polling that jvmstat api
- * provides is merely an abstraction over this (possibly always up-to-date)
- * ByteBuffer. So the data this class extracts is as current as possible, and
- * does not correspond to when the jvmstat update events fired.
- */
-public class JvmStatDataExtractor {
-
-    /*
-     * Note, there may be a performance issue to consider here. We have a lot of
-     * string constants. When we start adding some of the more heavyweight
-     * features, and running into CPU issues this may need to be reconsidered in
-     * order to avoid the String pool overhead. See also:
-     * http://docs.oracle.com/javase/6/docs/api/java/lang/String.html#intern()
-     */
-
-    private final MonitoredVm vm;
-
-    public JvmStatDataExtractor(MonitoredVm vm) {
-        this.vm = vm;
-    }
-
-    public String getCommandLine() throws MonitorException {
-        return MonitoredVmUtil.commandLine(vm);
-    }
-
-    public String getMainClass() throws MonitorException {
-        return MonitoredVmUtil.mainClass(vm, true);
-    }
-
-    public String getJavaVersion() throws MonitorException {
-        return (String) vm.findByName("java.property.java.version").getValue();
-    }
-
-    public String getJavaHome() throws MonitorException {
-        return (String) vm.findByName("java.property.java.home").getValue();
-    }
-
-    public String getVmName() throws MonitorException {
-        return (String) vm.findByName("java.property.java.vm.name").getValue();
-    }
-
-    public String getVmInfo() throws MonitorException {
-        return (String) vm.findByName("java.property.java.vm.info").getValue();
-    }
-
-    public String getVmVersion() throws MonitorException {
-        return (String) vm.findByName("java.property.java.vm.version").getValue();
-    }
-
-    public String getVmArguments() throws MonitorException {
-        return MonitoredVmUtil.jvmArgs(vm);
-    }
-
-    public long getTotalCollectors() throws MonitorException {
-        return (Long) vm.findByName("sun.gc.policy.collectors").getValue();
-    }
-
-    public String getCollectorName(long collector) throws MonitorException {
-        return (String) vm.findByName("sun.gc.collector." + collector + ".name").getValue();
-    }
-
-    public long getCollectorTime(long collector) throws MonitorException {
-        return (Long) vm.findByName("sun.gc.collector." + collector + ".time").getValue();
-    }
-
-    public long getCollectorInvocations(long collector) throws MonitorException {
-        return (Long) vm.findByName("sun.gc.collector." + collector + ".invocations").getValue();
-    }
-
-    public long getTotalGcGenerations() throws MonitorException {
-        return (Long) vm.findByName("sun.gc.policy.generations").getValue();
-    }
-
-    public String getGenerationName(long generation) throws MonitorException {
-        return (String) vm.findByName("sun.gc.generation." + generation + ".name").getValue();
-    }
-
-    public long getGenerationCapacity(long generation) throws MonitorException {
-        return (Long) vm.findByName("sun.gc.generation." + generation + ".capacity").getValue();
-    }
-
-    public long getGenerationMaxCapacity(long generation) throws MonitorException {
-        return (Long) vm.findByName("sun.gc.generation." + generation + ".maxCapacity").getValue();
-    }
-
-    public String getGenerationCollector(long generation) throws MonitorException {
-        // this is just re-implementing getCollectorName()
-        // TODO check generation number and collector number are always associated
-        Monitor m = vm.findByName("sun.gc.collector." + generation + ".name");
-        if (m == null) {
-            return Generation.COLLECTOR_NONE;
-        }
-        return (String) m.getValue();
-    }
-
-    public long getTotalSpaces(long generation) throws MonitorException {
-        return (Long) vm.findByName("sun.gc.generation." + generation + ".spaces").getValue();
-    }
-
-    public String getSpaceName(long generation, long space) throws MonitorException {
-        return (String) vm.findByName("sun.gc.generation." + generation + ".space." + space + ".name").getValue();
-    }
-
-    public long getSpaceCapacity(long generation, long space) throws MonitorException {
-        return (Long) vm.findByName("sun.gc.generation." + generation + ".space." + space + ".capacity").getValue();
-    }
-
-    public long getSpaceMaxCapacity(long generation, long space) throws MonitorException {
-        return (Long) vm.findByName("sun.gc.generation." + generation + ".space." + space + ".maxCapacity").getValue();
-    }
-
-    public long getSpaceUsed(long generation, long space) throws MonitorException {
-        return (Long) vm.findByName("sun.gc.generation." + generation + ".space." + space + ".used").getValue();
-    }
-
-    public long getLoadedClasses() throws MonitorException {
-        return (Long) vm.findByName("java.cls.loadedClasses").getValue();
-    }
-
-}
--- a/agent/core/src/main/java/com/redhat/thermostat/backend/system/JvmStatHostListener.java	Fri Oct 19 15:33:45 2012 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,226 +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.net.URISyntaxException;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.CopyOnWriteArrayList;
-import java.util.concurrent.CopyOnWriteArraySet;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-import sun.jvmstat.monitor.MonitorException;
-import sun.jvmstat.monitor.MonitoredHost;
-import sun.jvmstat.monitor.MonitoredVm;
-import sun.jvmstat.monitor.VmIdentifier;
-import sun.jvmstat.monitor.event.HostEvent;
-import sun.jvmstat.monitor.event.HostListener;
-import sun.jvmstat.monitor.event.VmListener;
-import sun.jvmstat.monitor.event.VmStatusChangeEvent;
-
-import com.redhat.thermostat.agent.JvmStatusListener;
-import com.redhat.thermostat.agent.JvmStatusNotifier;
-import com.redhat.thermostat.common.dao.VmClassStatDAO;
-import com.redhat.thermostat.common.dao.VmGcStatDAO;
-import com.redhat.thermostat.common.dao.VmInfoDAO;
-import com.redhat.thermostat.common.dao.VmMemoryStatDAO;
-import com.redhat.thermostat.common.model.VmInfo;
-import com.redhat.thermostat.common.utils.LoggingUtils;
-
-public class JvmStatHostListener implements HostListener, JvmStatusNotifier {
-
-    private static final Logger logger = LoggingUtils.getLogger(JvmStatHostListener.class);
-
-    private boolean attachNew;
-
-    private final VmInfoDAO vmInfoDAO;
-    private final VmMemoryStatDAO vmMemoryStatDAO;
-    private final VmClassStatDAO vmClassStatDAO;
-    private final VmGcStatDAO vmGcStatDAO;
-
-    private Map<Integer, MonitoredVm> monitoredVms  = new HashMap<>();
-    private Map<MonitoredVm, List<VmListener>> registeredListeners  = new ConcurrentHashMap<>();
-    
-    private Set<JvmStatusListener> statusListeners = new CopyOnWriteArraySet<JvmStatusListener>();
-
-    JvmStatHostListener(VmInfoDAO vmInfoDAO, VmMemoryStatDAO vmMemoryStatDAO, VmGcStatDAO vmGcStatDAO,
-            VmClassStatDAO vmClassStatDAO, boolean attachNew) {
-        this.vmInfoDAO = vmInfoDAO;
-        this.vmMemoryStatDAO = vmMemoryStatDAO;
-        this.vmGcStatDAO = vmGcStatDAO;
-        this.vmClassStatDAO = vmClassStatDAO;
-        this.attachNew = attachNew;        
-    }
-
-    void removeAllListeners() {
-        for (MonitoredVm vm : monitoredVms.values()) {
-            for (VmListener listener : registeredListeners.get(vm)) {
-                try {
-                    if (listener != null) vm.removeVmListener(listener);
-                
-                } catch (MonitorException e) {
-                    logger.log(Level.WARNING, "can't remove vm listener", e);
-                }
-            }
-        }
-    }
-    
-    @Override
-    public void disconnected(HostEvent event) {
-        logger.warning("Disconnected from host");
-    }
-
-    @SuppressWarnings("unchecked") // Unchecked casts to (Set<Integer>).
-    @Override
-    public void vmStatusChanged(VmStatusChangeEvent event) {
-        MonitoredHost host = event.getMonitoredHost();
-
-        for (Integer newVm : (Set<Integer>) event.getStarted()) {
-            try {
-                logger.fine("New vm: " + newVm);
-                sendNewVM(newVm, host);
-            } catch (MonitorException e) {
-                logger.log(Level.WARNING, "error getting info for new vm" + newVm, e);
-            } catch (URISyntaxException e) {
-                logger.log(Level.WARNING, "error getting info for new vm" + newVm, e);
-            }
-        }
-
-        for (Integer stoppedVm : (Set<Integer>) event.getTerminated()) {
-            try {
-                logger.fine("stopped vm: " + stoppedVm);
-                sendStoppedVM(stoppedVm, host);
-            } catch (URISyntaxException e) {
-                logger.log(Level.WARNING, "error getting info for stopped vm" + stoppedVm, e);
-            } catch (MonitorException e) {
-                logger.log(Level.WARNING, "error getting info for stopped vm" + stoppedVm, e);
-            }
-        }
-    }
-
-    private void sendNewVM(Integer vmId, MonitoredHost host)
-            throws MonitorException, URISyntaxException {
-        MonitoredVm vm = host.getMonitoredVm(host.getHostIdentifier().resolve(
-                new VmIdentifier(vmId.toString())));
-        if (vm != null) {
-            VmInfo info = null;
-            try {
-                long startTime = System.currentTimeMillis();
-                long stopTime = Long.MIN_VALUE;
-                JvmStatDataExtractor extractor = new JvmStatDataExtractor(vm);
-                Map<String, String> properties = new HashMap<String, String>();
-                ProcDataSource dataSource = new ProcDataSource();
-                Map<String, String> environment = new ProcessEnvironmentBuilder(dataSource).build(vmId);
-                // TODO actually figure out the loaded libraries.
-                List<String> loadedNativeLibraries = new ArrayList<String>();
-                info = new VmInfo(vmId, startTime, stopTime,
-                        extractor.getJavaVersion(), extractor.getJavaHome(),
-                        extractor.getMainClass(), extractor.getCommandLine(),
-                        extractor.getVmName(), extractor.getVmInfo(), extractor.getVmVersion(), extractor.getVmArguments(),
-                        properties, environment, loadedNativeLibraries);
-                vmInfoDAO.putVmInfo(info);
-                logger.finer("Sent VM_STARTED messsage");
-            } catch (MonitorException me) {
-                logger.log(Level.WARNING, "error getting vm info for " + vmId, me);
-            }
-
-            if (attachNew) {
-                List<VmListener> listeners = registeredListeners.get(vm);
-                if (listeners == null) {
-                    listeners = new CopyOnWriteArrayList<>();
-                }
-                
-                VmListener listener =  new JvmStatVmListener(vmMemoryStatDAO, vmGcStatDAO, vmId);
-                vm.addVmListener(listener);
-                listeners.add(listener);
-                
-                listener = new JvmStatVmClassListener(vmClassStatDAO, vmId);
-                vm.addVmListener(listener);
-                listeners.add(listener);
-                
-                registeredListeners.put(vm, listeners);
-                
-            } else {
-                logger.log(Level.FINE, "skipping new vm " + vmId);
-            }
-            for (JvmStatusListener statusListener : statusListeners) {
-                statusListener.jvmStarted(vmId);
-            }
-
-            monitoredVms.put(vmId, vm);
-        }
-    }
-
-    private void sendStoppedVM(Integer vmId, MonitoredHost host)
-            throws URISyntaxException, MonitorException {
-        VmIdentifier resolvedVmID = host.getHostIdentifier().resolve(
-                new VmIdentifier(vmId.toString()));
-        if (resolvedVmID != null) {
-            long stopTime = System.currentTimeMillis();
-            for (JvmStatusListener statusListener : statusListeners) {
-                statusListener.jvmStopped(vmId);
-            }
-            vmInfoDAO.putVmStoppedTime(vmId, stopTime);
-
-            MonitoredVm vm = monitoredVms.remove(vmId);
-            List<VmListener> listeners = registeredListeners.remove(vm);
-            for (VmListener listener : listeners) {
-                try {
-                    if (listener != null) vm.removeVmListener(listener);
-                } catch (MonitorException e) {
-                    logger.log(Level.WARNING, "can't remove vm listener", e);
-                }
-            }
-            vm.detach();
-        }
-    }
-
-    @Override
-    public void addJvmStatusListener(JvmStatusListener listener) {
-        statusListeners.add(listener);
-    }
-
-    @Override
-    public void removeJvmStatusListener(JvmStatusListener listener) {
-        statusListeners.remove(listener);
-    }
-}
--- a/agent/core/src/main/java/com/redhat/thermostat/backend/system/JvmStatVmClassListener.java	Fri Oct 19 15:33:45 2012 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,90 +0,0 @@
-/*
- * Copyright 2012 Red Hat, Inc.
- *
- * This file is part of Thermostat.
- *
- * Thermostat is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published
- * by the Free Software Foundation; either version 2, or (at your
- * option) any later version.
- *
- * Thermostat is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Thermostat; see the file COPYING.  If not see
- * <http://www.gnu.org/licenses/>.
- *
- * Linking this code with other modules is making a combined work
- * based on this code.  Thus, the terms and conditions of the GNU
- * General Public License cover the whole combination.
- *
- * As a special exception, the copyright holders of this code give
- * you permission to link this code with independent modules to
- * produce an executable, regardless of the license terms of these
- * independent modules, and to copy and distribute the resulting
- * executable under terms of your choice, provided that you also
- * meet, for each linked independent module, the terms and conditions
- * of the license of that module.  An independent module is a module
- * which is not derived from or based on this code.  If you modify
- * this code, you may extend this exception to your version of the
- * library, but you are not obligated to do so.  If you do not wish
- * to do so, delete this exception statement from your version.
- */
-
-package com.redhat.thermostat.backend.system;
-
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-import sun.jvmstat.monitor.MonitorException;
-import sun.jvmstat.monitor.MonitoredVm;
-import sun.jvmstat.monitor.event.MonitorStatusChangeEvent;
-import sun.jvmstat.monitor.event.VmEvent;
-import sun.jvmstat.monitor.event.VmListener;
-
-import com.redhat.thermostat.common.dao.VmClassStatDAO;
-import com.redhat.thermostat.common.model.VmClassStat;
-import com.redhat.thermostat.common.utils.LoggingUtils;
-
-class JvmStatVmClassListener implements VmListener {
-
-    private static final Logger logger = LoggingUtils.getLogger(JvmStatVmClassListener.class);
-
-    private VmClassStatDAO dao;
-    private int vmId;
-
-    JvmStatVmClassListener(VmClassStatDAO dao, int vmId) {
-        this.dao = dao;
-        this.vmId = vmId;
-    }
-
-    @Override
-    public void disconnected(VmEvent vmEvent) {
-        /* nothing to do here */
-    }
-
-    @Override
-    public void monitorStatusChanged(MonitorStatusChangeEvent vmEvent) {
-        /* nothing to do here */
-    }
-
-    @Override
-    public void monitorsUpdated(VmEvent vmEvent) {
-        MonitoredVm vm = vmEvent.getMonitoredVm();
-        try {
-            JvmStatDataExtractor extractor = new JvmStatDataExtractor(vm);
-            long loadedClasses = extractor.getLoadedClasses();
-            long timestamp = System.currentTimeMillis();
-            VmClassStat stat = new VmClassStat(vmId, timestamp, loadedClasses);
-            dao.putVmClassStat(stat);
-        } catch (MonitorException e) {
-            logger.log(Level.WARNING, "error gathering class info for vm " + vmId, e);
-        }
-
-
-    }
-
-}
--- a/agent/core/src/main/java/com/redhat/thermostat/backend/system/JvmStatVmListener.java	Fri Oct 19 15:33:45 2012 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,144 +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.ArrayList;
-import java.util.List;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-import sun.jvmstat.monitor.MonitorException;
-import sun.jvmstat.monitor.MonitoredVm;
-import sun.jvmstat.monitor.event.MonitorStatusChangeEvent;
-import sun.jvmstat.monitor.event.VmEvent;
-import sun.jvmstat.monitor.event.VmListener;
-
-import com.redhat.thermostat.common.dao.VmGcStatDAO;
-import com.redhat.thermostat.common.dao.VmMemoryStatDAO;
-import com.redhat.thermostat.common.model.VmGcStat;
-import com.redhat.thermostat.common.model.VmMemoryStat;
-import com.redhat.thermostat.common.model.VmMemoryStat.Generation;
-import com.redhat.thermostat.common.model.VmMemoryStat.Space;
-import com.redhat.thermostat.common.utils.LoggingUtils;
-
-public class JvmStatVmListener implements VmListener {
-
-    private static final Logger logger = LoggingUtils.getLogger(JvmStatVmListener.class);
-
-    private final int vmId;
-    private final VmGcStatDAO gcDAO;
-    private final VmMemoryStatDAO memDAO;
-
-    public JvmStatVmListener(VmMemoryStatDAO vmMemoryStatDao, VmGcStatDAO vmGcStatDao, int vmId) {
-        gcDAO = vmGcStatDao;
-        memDAO = vmMemoryStatDao;
-        this.vmId = vmId;
-    }
-
-    @Override
-    public void disconnected(VmEvent event) {
-        /* nothing to do here */
-    }
-
-    @Override
-    public void monitorStatusChanged(MonitorStatusChangeEvent event) {
-        /* nothing to do here */
-    }
-
-    @Override
-    public void monitorsUpdated(VmEvent event) {
-        MonitoredVm vm = event.getMonitoredVm();
-        if (vm == null) {
-            throw new NullPointerException();
-        }
-        recordMemoryStat(vm);
-        recordGcStat(vm);
-    }
-
-    private void recordGcStat(MonitoredVm vm) {
-        try {
-            JvmStatDataExtractor extractor = new JvmStatDataExtractor(vm);
-            long collectors = extractor.getTotalCollectors();
-            for (int i = 0; i < collectors; i++) {
-                long timestamp = System.currentTimeMillis();
-                VmGcStat stat = new VmGcStat(vmId, timestamp,
-                        extractor.getCollectorName(i),
-                        extractor.getCollectorInvocations(i),
-                        extractor.getCollectorTime(i));
-                gcDAO.putVmGcStat(stat);
-            }
-        } catch (MonitorException e) {
-            logger.log(Level.WARNING, "error gathering gc info for vm " + vmId, e);
-        }
-
-    }
-
-    private void recordMemoryStat(MonitoredVm vm) {
-        try {
-            long timestamp = System.currentTimeMillis();
-            JvmStatDataExtractor extractor = new JvmStatDataExtractor(vm);
-            long maxGenerations = extractor.getTotalGcGenerations();
-            List<Generation> generations = new ArrayList<Generation>();
-            VmMemoryStat stat = new VmMemoryStat(timestamp, vmId, generations);
-            for (long generation = 0; generation < maxGenerations; generation++) {
-                Generation g = new Generation();
-                generations.add(g);
-                g.name = extractor.getGenerationName(generation);
-                g.capacity = extractor.getGenerationCapacity(generation);
-                g.maxCapacity = extractor.getGenerationMaxCapacity(generation);
-                g.collector = extractor.getGenerationCollector(generation);
-                long maxSpaces = extractor.getTotalSpaces(generation);
-                List<Space> spaces = new ArrayList<Space>();
-                g.spaces = spaces;
-                for (long space = 0; space < maxSpaces; space++) {
-                    Space s = new Space();
-                    spaces.add(s);
-                    s.index = (int) space;
-                    s.name = extractor.getSpaceName(generation, space);
-                    s.capacity = extractor.getSpaceCapacity(generation, space);
-                    s.maxCapacity = extractor.getSpaceMaxCapacity(generation, space);
-                    s.used = extractor.getSpaceUsed(generation, space);
-                }
-            }
-            memDAO.putVmMemoryStat(stat);
-        } catch (MonitorException e) {
-            logger.log(Level.WARNING, "error gathering memory info for vm " + vmId, e);
-        }
-    }
-
-
-}
--- a/agent/core/src/main/java/com/redhat/thermostat/backend/system/LsbRelease.java	Fri Oct 19 15:33:45 2012 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,111 +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.InputStream;
-import java.io.InputStreamReader;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-import com.redhat.thermostat.common.utils.LoggingUtils;
-
-public class LsbRelease implements DistributionInformationSource {
-
-    private static final Logger logger = LoggingUtils.getLogger(LsbRelease.class);
-
-    private static final String DISTRIBUTION_NAME = "distributor id";
-    private static final String DISTRIBUTION_VERSION = "release";
-
-    @Override
-    public DistributionInformation getDistributionInformation()
-            throws IOException {
-        return getFromLsbRelease();
-    }
-
-    public DistributionInformation getFromLsbRelease() throws IOException {
-
-        BufferedReader reader = null;
-        try {
-            Process lsbProc = Runtime.getRuntime().exec(new String[] { "lsb_release", "-a" });
-            InputStream progOutput = lsbProc.getInputStream();
-            reader = new BufferedReader(new InputStreamReader(progOutput));
-            DistributionInformation result = getFromLsbRelease(reader);
-            int exitValue = lsbProc.waitFor();
-            if (exitValue != 0) {
-                logger.log(Level.WARNING, "unable to identify distribution, problems running 'lsb_release'");
-            }
-            return result;
-        } catch (InterruptedException e) {
-            throw new IOException(e);
-        } finally {
-            if (reader != null) {
-                try {
-                    reader.close();
-                } catch (IOException e) {
-                    logger.log(Level.WARNING, "unable to close a child's output stream");
-                }
-            }
-        }
-
-    }
-
-    public DistributionInformation getFromLsbRelease(BufferedReader reader) throws IOException {
-        String name = DistributionInformation.UNKNOWN_NAME;
-        String version = DistributionInformation.UNKNOWN_VERSION;
-
-        String line;
-        while ((line = reader.readLine()) != null) {
-            int sepLocation = line.indexOf(":");
-            if (sepLocation != -1) {
-                String key = line.substring(0, sepLocation).toLowerCase();
-                if (key.equals(DISTRIBUTION_NAME)) {
-                    name = line.substring(sepLocation + 1).trim();
-                } else if (key.equals(DISTRIBUTION_VERSION)) {
-                    version = line.substring(sepLocation + 1).trim();
-                }
-            }
-        }
-
-        logger.log(Level.FINE, "distro-name: " + name);
-        logger.log(Level.FINE, "distro-version: " + version);
-
-        return new DistributionInformation(name, version);
-    }
-
-}
--- a/agent/core/src/main/java/com/redhat/thermostat/backend/system/MemoryStatBuilder.java	Fri Oct 19 15:33:45 2012 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,139 +0,0 @@
-/*
- * Copyright 2012 Red Hat, Inc.
- *
- * This file is part of Thermostat.
- *
- * Thermostat is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published
- * by the Free Software Foundation; either version 2, or (at your
- * option) any later version.
- *
- * Thermostat is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Thermostat; see the file COPYING.  If not see
- * <http://www.gnu.org/licenses/>.
- *
- * Linking this code with other modules is making a combined work
- * based on this code.  Thus, the terms and conditions of the GNU
- * General Public License cover the whole combination.
- *
- * As a special exception, the copyright holders of this code give
- * you permission to link this code with independent modules to
- * produce an executable, regardless of the license terms of these
- * independent modules, and to copy and distribute the resulting
- * executable under terms of your choice, provided that you also
- * meet, for each linked independent module, the terms and conditions
- * of the license of that module.  An independent module is a module
- * which is not derived from or based on this code.  If you modify
- * this code, you may extend this exception to your version of the
- * library, but you are not obligated to do so.  If you do not wish
- * to do so, delete this exception statement from your version.
- */
-
-package com.redhat.thermostat.backend.system;
-
-import java.io.BufferedReader;
-import java.io.IOException;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-import com.redhat.thermostat.common.Constants;
-import com.redhat.thermostat.common.NotImplementedException;
-import com.redhat.thermostat.common.model.MemoryStat;
-import com.redhat.thermostat.common.utils.LoggingUtils;
-
-/**
- * Implementation note: uses information from /proc/
- */
-public class MemoryStatBuilder {
-
-    private static final long UNAVAILABLE = -1;
-
-    private static final String KEY_MEMORY_TOTAL = "MemTotal";
-    private static final String KEY_MEMORY_FREE = "MemFree";
-    private static final String KEY_BUFFERS = "Buffers";
-    private static final String KEY_CACHED = "Cached";
-    private static final String KEY_SWAP_TOTAL = "SwapTotal";
-    private static final String KEY_SWAP_FREE = "SwapFree";
-    private static final String KEY_COMMIT_LIMIT = "CommitLimit";
-
-    private static final Logger logger = LoggingUtils.getLogger(MemoryStatBuilder.class);
-
-    private final ProcDataSource dataSource;
-
-    public MemoryStatBuilder(ProcDataSource dataSource) {
-        this.dataSource = dataSource;
-    }
-
-    protected MemoryStat build() {
-        long timestamp = System.currentTimeMillis();
-
-        long total = UNAVAILABLE;
-        long free = UNAVAILABLE;
-        long swapTotal = UNAVAILABLE;
-        long swapFree = UNAVAILABLE;
-        long buffers = UNAVAILABLE;
-        long cached = UNAVAILABLE;
-        long commitLimit = UNAVAILABLE;
-
-        try (BufferedReader reader = new BufferedReader(dataSource.getMemInfoReader())) {
-            String line = null;
-            while ((line = reader.readLine()) != null) {
-                String[] parts = line.split(":");
-                if (parts.length == 2) {
-                    String key = parts[0].trim();
-                    long value = getValue(parts[1].trim());
-                    if (key.equals(KEY_MEMORY_TOTAL)) {
-                        total = value;
-                    } else if (key.equals(KEY_MEMORY_FREE)) {
-                        free = value;
-                    } else if (key.equals(KEY_SWAP_TOTAL)) {
-                        swapTotal = value;
-                    } else if (key.equals(KEY_SWAP_FREE)) {
-                        swapFree = value;
-                    } else if (key.equals(KEY_BUFFERS)) {
-                        buffers = value;
-                    } else if (key.equals(KEY_CACHED)) {
-                        cached = value;
-                    } else if (key.equals(KEY_COMMIT_LIMIT)) {
-                        commitLimit = value;
-                    }
-                }
-            }
-        } catch (IOException ioe) {
-            logger.log(Level.WARNING, "unable to read memory info");
-        }
-
-        return new MemoryStat(timestamp, total, free, buffers, cached, swapTotal, swapFree, commitLimit);
-    }
-
-    private long getValue(String rawValue) {
-        String[] parts = rawValue.split(" +");
-        String value = rawValue;
-        String units = null;
-        if (parts.length > 1) {
-            value = parts[0];
-            units = parts[1];
-        }
-
-        long result = UNAVAILABLE;
-        try {
-            result = Long.parseLong(value);
-            if (units != null) {
-                if (units.equals("kB") || units.equals("KB")) {
-                    result = result * Constants.KILOBYTES_TO_BYTES;
-                } else {
-                    throw new NotImplementedException("unit conversion from " + units + " not implemented");
-                }
-            }
-        } catch (NumberFormatException nfe) {
-            logger.log(Level.WARNING, "error extracting memory info");
-        }
-
-        return result;
-    }
-}
--- a/agent/core/src/main/java/com/redhat/thermostat/backend/system/NetworkInfoBuilder.java	Fri Oct 19 15:33:45 2012 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,79 +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.net.Inet4Address;
-import java.net.Inet6Address;
-import java.net.InetAddress;
-import java.net.NetworkInterface;
-import java.net.SocketException;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Enumeration;
-import java.util.List;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-import com.redhat.thermostat.common.model.NetworkInterfaceInfo;
-import com.redhat.thermostat.common.utils.LoggingUtils;
-
-public class NetworkInfoBuilder {
-
-    private static final Logger logger = LoggingUtils.getLogger(NetworkInfoBuilder.class);
-
-    public static List<NetworkInterfaceInfo> build() {
-        List<NetworkInterfaceInfo> infos = new ArrayList<NetworkInterfaceInfo>();
-        try {
-            Enumeration<NetworkInterface> ifaces = NetworkInterface.getNetworkInterfaces();
-            for (NetworkInterface iface : Collections.list(ifaces)) {
-                NetworkInterfaceInfo info = new NetworkInterfaceInfo(iface.getName());
-                for (InetAddress addr : Collections.list(iface.getInetAddresses())) {
-                    if (addr instanceof Inet4Address) {
-                        info.setIp4Addr(addr.getHostAddress());
-                    } else if (addr instanceof Inet6Address) {
-                        info.setIp6Addr(addr.getHostAddress());
-                    }
-                }
-                infos.add(info);
-            }
-        } catch (SocketException e) {
-            logger.log(Level.WARNING, "error enumerating network interfaces");
-        }
-        return infos;
-    }
-
-}
--- a/agent/core/src/main/java/com/redhat/thermostat/backend/system/ProcDataSource.java	Fri Oct 19 15:33:45 2012 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,102 +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.FileReader;
-import java.io.IOException;
-import java.io.Reader;
-
-/**
- * Wrapper for files under /proc. See proc(5) for details about this.
- */
-public class ProcDataSource {
-
-    private static final String LOAD_FILE = "/proc/loadavg";
-    private static final String STAT_FILE = "/proc/stat";
-    private static final String MEMINFO_FILE = "/proc/meminfo";
-    private static final String CPUINFO_FILE = "/proc/cpuinfo";
-
-    private static final String PID_STAT_FILE = "/proc/${pid}/stat";
-    private static final String PID_ENVIRON_FILE = "/proc/${pid}/environ";
-
-    /**
-     * Returns a reader for /proc/cpuinfo
-     */
-    public Reader getCpuInfoReader() throws IOException {
-        return new FileReader(CPUINFO_FILE);
-    }
-
-    /**
-     * Returns a reader for /proc/loadavg
-     */
-    public Reader getCpuLoadReader() throws IOException {
-        return new FileReader(LOAD_FILE);
-    }
-
-    /**
-     * Returns a reader for /proc/stat. Kernel/System statistics.
-     */
-    public Reader getStatReader() throws IOException {
-        return new FileReader(STAT_FILE);
-    }
-
-    /**
-     * Returns a reader for /proc/meminfo
-     */
-    public Reader getMemInfoReader() throws IOException {
-        return new FileReader(MEMINFO_FILE);
-    }
-
-    /**
-     * Returns a reader for /proc/$PID/stat
-     */
-    public Reader getStatReader(int pid) throws IOException {
-        return new FileReader(getPidFile(PID_STAT_FILE, pid));
-    }
-
-    /**
-     * Returns a reader for /proc/$PID/environ
-     */
-    public Reader getEnvironReader(int pid) throws IOException {
-        return new FileReader(getPidFile(PID_ENVIRON_FILE, pid));
-    }
-
-    private String getPidFile(String fileName, int pid) {
-        return fileName.replace("${pid}", Integer.toString(pid));
-    }
-
-}
--- a/agent/core/src/main/java/com/redhat/thermostat/backend/system/ProcessEnvironmentBuilder.java	Fri Oct 19 15:33:45 2012 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,124 +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.IOException;
-import java.io.Reader;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-import com.redhat.thermostat.common.utils.LoggingUtils;
-
-public class ProcessEnvironmentBuilder {
-
-    private static final Logger logger = LoggingUtils.getLogger(ProcessEnvironmentBuilder.class);
-
-    private final ProcDataSource dataSource;
-
-    public ProcessEnvironmentBuilder(ProcDataSource dataSource) {
-        this.dataSource = dataSource;
-    }
-
-    public Map<String, String> build(int pid) {
-        try (Reader reader = dataSource.getEnvironReader(pid)) {
-            return build(reader);
-        } catch (IOException ioe) {
-            logger.log(Level.WARNING, "error reading env", ioe);
-        }
-
-        return Collections.emptyMap();
-    }
-
-    private Map<String,String> build(Reader reader) throws IOException {
-
-        Map<String, String> env = new HashMap<String, String>();
-
-        char[] fileBuffer = new char[1024];
-        int fileBufferIndex = 0;
-        char[] buffer = new char[1024];
-        int read = 0;
-        while (true) {
-            read = reader.read(buffer);
-            if (read == -1) {
-                break;
-            }
-
-            if (read + fileBufferIndex > fileBuffer.length) {
-                char[] newFileBuffer = new char[fileBuffer.length * 2];
-                System.arraycopy(fileBuffer, 0, newFileBuffer, 0, fileBufferIndex);
-                fileBuffer = newFileBuffer;
-            }
-            System.arraycopy(buffer, 0, fileBuffer, fileBufferIndex, read);
-            fileBufferIndex = fileBufferIndex + read;
-
-        }
-        List<String> parts = getParts(fileBuffer, fileBufferIndex);
-        for (String part : parts) {
-            int splitterPos = part.indexOf("=");
-            String key = part.substring(0, splitterPos);
-            String value = part.substring(splitterPos + 1);
-            env.put(key, value);
-        }
-
-        return env;
-    }
-
-    /**
-     * Split a char array, where items are separated by a null into into a list
-     * of strings
-     */
-    private List<String> getParts(char[] nullSeparatedBuffer, int bufferLength) {
-        int maxLength = Math.min(nullSeparatedBuffer.length, bufferLength);
-        List<String> parts = new ArrayList<String>();
-
-        int lastStart = 0;
-        for (int i = 0; i < maxLength; i++) {
-            if (nullSeparatedBuffer[i] == '\0') {
-                String string = new String(nullSeparatedBuffer, lastStart, (i - lastStart));
-                parts.add(string);
-                lastStart = i + 1;
-            }
-        }
-        return parts;
-    }
-
-}
--- a/agent/core/src/main/java/com/redhat/thermostat/backend/system/ProcessStatusInfo.java	Fri Oct 19 15:33:45 2012 -0400
+++ /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/agent/core/src/main/java/com/redhat/thermostat/backend/system/ProcessStatusInfoBuilder.java	Fri Oct 19 15:33:45 2012 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,118 +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;
-
-/**
- * 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/agent/core/src/main/java/com/redhat/thermostat/backend/system/SysConf.java	Fri Oct 19 15:33:45 2012 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,87 +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.InputStreamReader;
-
-/**
- * A wrapper over POSIX's sysconf.
- * <p>
- * Implementation notes: uses {@code getconf(1)}
- */
-public class SysConf {
-
-    private SysConf() {
-        /* do not initialize */
-    }
-
-    public static long getClockTicksPerSecond() {
-        String ticks = sysConf("CLK_TCK");
-        try {
-            return Long.valueOf(ticks);
-        } catch (NumberFormatException nfe) {
-            return 0;
-        }
-    }
-
-    private static String sysConf(String arg) {
-        BufferedReader reader = null;
-        try {
-            Process process = Runtime.getRuntime().exec(new String[] { "getconf", arg });
-            int result = process.waitFor();
-            if (result != 0) {
-                return null;
-            }
-            reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
-            return reader.readLine();
-        } catch (IOException e) {
-            return null;
-        } catch (InterruptedException e) {
-            return null;
-        } finally {
-            if (reader != null) {
-                try {
-                    reader.close();
-                } catch (IOException e) {
-                    // TODO
-                }
-            }
-        }
-    }
-}
--- a/agent/core/src/main/java/com/redhat/thermostat/backend/system/SystemBackend.java	Fri Oct 19 15:33:45 2012 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,243 +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.net.URISyntaxException;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-import java.util.Set;
-import java.util.Timer;
-import java.util.TimerTask;
-import java.util.concurrent.CopyOnWriteArraySet;
-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.agent.JvmStatusListener;
-import com.redhat.thermostat.agent.JvmStatusNotifier;
-import com.redhat.thermostat.backend.Backend;
-import com.redhat.thermostat.common.Clock;
-import com.redhat.thermostat.common.SystemClock;
-import com.redhat.thermostat.common.dao.CpuStatDAO;
-import com.redhat.thermostat.common.dao.HostInfoDAO;
-import com.redhat.thermostat.common.dao.MemoryStatDAO;
-import com.redhat.thermostat.common.dao.NetworkInterfaceInfoDAO;
-import com.redhat.thermostat.common.dao.VmClassStatDAO;
-import com.redhat.thermostat.common.dao.VmCpuStatDAO;
-import com.redhat.thermostat.common.dao.VmGcStatDAO;
-import com.redhat.thermostat.common.dao.VmInfoDAO;
-import com.redhat.thermostat.common.dao.VmMemoryStatDAO;
-import com.redhat.thermostat.common.model.NetworkInterfaceInfo;
-import com.redhat.thermostat.common.model.VmCpuStat;
-import com.redhat.thermostat.common.storage.Category;
-import com.redhat.thermostat.common.utils.LoggingUtils;
-
-public class SystemBackend extends Backend implements JvmStatusNotifier, JvmStatusListener {
-
-    private static final Logger logger = LoggingUtils.getLogger(SystemBackend.class);
-
-    private CpuStatDAO cpuStats;
-    private HostInfoDAO hostInfos;
-    private MemoryStatDAO memoryStats;
-    private VmCpuStatDAO vmCpuStats;
-    private NetworkInterfaceInfoDAO networkInterfaces;
-
-    private final Set<Integer> pidsToMonitor = new CopyOnWriteArraySet<Integer>();
-
-    private long procCheckInterval = 1000; // TODO make this configurable.
-
-    private Timer timer = null;
-
-    private HostIdentifier hostId = null;
-    private MonitoredHost host = null;
-    private JvmStatHostListener hostListener;
-
-    private final VmCpuStatBuilder vmCpuBuilder;
-    private final HostInfoBuilder hostInfoBuilder;
-    private final CpuStatBuilder cpuStatBuilder;
-    private final MemoryStatBuilder memoryStatBuilder;
-
-    public SystemBackend() {
-        super();
-
-        Clock clock = new SystemClock();
-        ProcessStatusInfoBuilder builder = new ProcessStatusInfoBuilder(new ProcDataSource());
-        long ticksPerSecond = SysConf.getClockTicksPerSecond();
-        ProcDataSource source = new ProcDataSource();
-        hostInfoBuilder = new HostInfoBuilder(source);
-        cpuStatBuilder = new CpuStatBuilder(clock, source, ticksPerSecond);
-        memoryStatBuilder = new MemoryStatBuilder(source);
-
-        int cpuCount = hostInfoBuilder.getCpuInfo().count;
-        vmCpuBuilder = new VmCpuStatBuilder(clock, cpuCount, ticksPerSecond, builder);
-    }
-
-    @Override
-    protected void setDAOFactoryAction() {
-        cpuStats = df.getCpuStatDAO();
-        hostInfos = df.getHostInfoDAO();
-        memoryStats = df.getMemoryStatDAO();
-        vmCpuStats = df.getVmCpuStatDAO();
-        networkInterfaces = df.getNetworkInterfaceInfoDAO();
-        hostListener = new JvmStatHostListener(df.getVmInfoDAO(), df.getVmMemoryStatDAO(), df.getVmGcStatDAO(), df.getVmClassStatsDAO(), getObserveNewJvm());
-    }
-
-    @Override
-    public synchronized boolean activate() {
-        if (timer != null) {
-            return true;
-        }
-        if (df == null) {
-            throw new IllegalStateException("Cannot activate backend without DAOFactory.");
-        }
-
-        addJvmStatusListener(this);
-
-        if (!getObserveNewJvm()) {
-            logger.fine("not monitoring new vms");
-        }
-        hostInfos.putHostInfo(hostInfoBuilder.build());
-
-        timer = new Timer();
-        timer.scheduleAtFixedRate(new TimerTask() {
-            @Override
-            public void run() {
-                if (!cpuStatBuilder.isInitialized()) {
-                    cpuStatBuilder.initialize();
-                } else {
-                    cpuStats.putCpuStat(cpuStatBuilder.build());
-                }
-                for (NetworkInterfaceInfo info: NetworkInfoBuilder.build()) {
-                    networkInterfaces.putNetworkInterfaceInfo(info);
-                }
-                memoryStats.putMemoryStat(memoryStatBuilder.build());
-
-                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);
-
-        try {
-            hostId = new HostIdentifier((String) null);
-            host = MonitoredHost.getMonitoredHost(hostId);
-            host.addHostListener(hostListener);
-        } catch (MonitorException me) {
-            logger.log(Level.WARNING, "problems with connecting jvmstat to local machine", me);
-        } catch (URISyntaxException use) {
-            logger.log(Level.WARNING, "problems with connecting jvmstat to local machine", use);
-        }
-
-        return true;
-    }
-
-    @Override
-    public synchronized boolean deactivate() {
-        if (timer == null) {
-            return true;
-        }
-
-        timer.cancel();
-        timer = null;
-
-        removeJvmStatusListener(this);
-
-        try {
-            
-            // remove all listener from the host listener
-            hostListener.removeAllListeners();
-            
-            host.removeHostListener(hostListener);
-        } catch (MonitorException me) {
-            logger.log(Level.INFO, "something went wrong in jvmstat's listening to this host");
-        }
-        host = null;
-        hostId = null;
-
-        return true;
-    }
-
-    @Override
-    public synchronized boolean isActive() {
-        return (timer != null);
-    }
-
-    @Override
-    public String getConfigurationValue(String key) {
-        return null;
-    }
-
-    @Override
-    public boolean attachToNewProcessByDefault() {
-        return true;
-    }
-
-    @Override
-    public void addJvmStatusListener(JvmStatusListener listener) {
-        hostListener.addJvmStatusListener(listener);
-    }
-
-    @Override
-    public void removeJvmStatusListener(JvmStatusListener listener) {
-        hostListener.removeJvmStatusListener(listener);
-    }
-
-    @Override
-    public void jvmStarted(int vmId) {
-        if (getObserveNewJvm()) {
-            pidsToMonitor.add(vmId);
-        }
-    }
-
-    @Override
-    public void jvmStopped(int vmId) {
-        pidsToMonitor.remove(vmId);
-        vmCpuBuilder.forgetAbout(vmId);
-    }
-}
--- a/agent/core/src/main/java/com/redhat/thermostat/backend/system/VmCpuStatBuilder.java	Fri Oct 19 15:33:45 2012 -0400
+++ /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.model.VmCpuStat;
-import com.redhat.thermostat.common.utils.LoggingUtils;
-
-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/agent/core/src/main/java/com/redhat/thermostat/utils/ProcDataSource.java	Fri Oct 19 21:58:18 2012 +0200
@@ -0,0 +1,102 @@
+/*
+ * 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.utils;
+
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.Reader;
+
+/**
+ * Wrapper for files under /proc. See proc(5) for details about this.
+ */
+public class ProcDataSource {
+
+    private static final String LOAD_FILE = "/proc/loadavg";
+    private static final String STAT_FILE = "/proc/stat";
+    private static final String MEMINFO_FILE = "/proc/meminfo";
+    private static final String CPUINFO_FILE = "/proc/cpuinfo";
+
+    private static final String PID_STAT_FILE = "/proc/${pid}/stat";
+    private static final String PID_ENVIRON_FILE = "/proc/${pid}/environ";
+
+    /**
+     * Returns a reader for /proc/cpuinfo
+     */
+    public Reader getCpuInfoReader() throws IOException {
+        return new FileReader(CPUINFO_FILE);
+    }
+
+    /**
+     * Returns a reader for /proc/loadavg
+     */
+    public Reader getCpuLoadReader() throws IOException {
+        return new FileReader(LOAD_FILE);
+    }
+
+    /**
+     * Returns a reader for /proc/stat. Kernel/System statistics.
+     */
+    public Reader getStatReader() throws IOException {
+        return new FileReader(STAT_FILE);
+    }
+
+    /**
+     * Returns a reader for /proc/meminfo
+     */
+    public Reader getMemInfoReader() throws IOException {
+        return new FileReader(MEMINFO_FILE);
+    }
+
+    /**
+     * Returns a reader for /proc/$PID/stat
+     */
+    public Reader getStatReader(int pid) throws IOException {
+        return new FileReader(getPidFile(PID_STAT_FILE, pid));
+    }
+
+    /**
+     * Returns a reader for /proc/$PID/environ
+     */
+    public Reader getEnvironReader(int pid) throws IOException {
+        return new FileReader(getPidFile(PID_ENVIRON_FILE, pid));
+    }
+
+    private String getPidFile(String fileName, int pid) {
+        return fileName.replace("${pid}", Integer.toString(pid));
+    }
+
+}
--- a/agent/core/src/test/java/com/redhat/thermostat/agent/AgentTest.java	Fri Oct 19 15:33:45 2012 -0400
+++ b/agent/core/src/test/java/com/redhat/thermostat/agent/AgentTest.java	Fri Oct 19 21:58:18 2012 +0200
@@ -37,17 +37,20 @@
 package com.redhat.thermostat.agent;
 
 import static org.junit.Assert.assertEquals;
-import static org.mockito.Mockito.atLeast;
-import static org.mockito.Mockito.isA;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertFalse;
+
+import static org.mockito.Matchers.any;
 import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.isA;
+import static org.mockito.Mockito.atLeast;
 
 import java.util.ArrayList;
 import java.util.Collection;
-import java.util.List;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -55,19 +58,27 @@
 
 import com.redhat.thermostat.agent.config.AgentStartupConfiguration;
 import com.redhat.thermostat.backend.Backend;
+import com.redhat.thermostat.backend.BackendID;
 import com.redhat.thermostat.backend.BackendRegistry;
+import com.redhat.thermostat.common.ActionEvent;
+import com.redhat.thermostat.common.ActionListener;
+import com.redhat.thermostat.common.ThermostatExtensionRegistry;
 import com.redhat.thermostat.common.dao.AgentInfoDAO;
 import com.redhat.thermostat.common.dao.BackendInfoDAO;
+import com.redhat.thermostat.common.dao.DAOFactory;
 import com.redhat.thermostat.common.model.AgentInformation;
 import com.redhat.thermostat.common.model.BackendInformation;
 import com.redhat.thermostat.common.storage.Storage;
 
+@SuppressWarnings({ "rawtypes", "unchecked" })
 public class AgentTest {
 
     private AgentStartupConfiguration config;
     private BackendRegistry backendRegistry;
     private Backend backend;
 
+    private DAOFactory daoFactory;
+    
     private Storage storage;
     private AgentInfoDAO agentInfoDao;
     private BackendInfoDAO backendInfoDao;
@@ -82,6 +93,11 @@
         agentInfoDao = mock(AgentInfoDAO.class);
         backendInfoDao = mock(BackendInfoDAO.class);
         
+        daoFactory = mock(DAOFactory.class);
+        when(daoFactory.getStorage()).thenReturn(storage);
+        when(daoFactory.getAgentInfoDAO()).thenReturn(agentInfoDao);
+        when(daoFactory.getBackendInfoDAO()).thenReturn(backendInfoDao);
+
         backend = mock(Backend.class);
         when(backend.getName()).thenReturn("testname");
         when(backend.getDescription()).thenReturn("testdesc");
@@ -91,52 +107,100 @@
         backends.add(backend);
         
         backendRegistry = mock(BackendRegistry.class);
-        when(backendRegistry.getAll()).thenReturn(backends);
+    }
+    
+    @SuppressWarnings("unused")
+    @Test
+    public void testAgentInit() throws Exception {
+        Agent agent = new Agent(backendRegistry, config, daoFactory);
+
+        verify(daoFactory).getStorage();
+        verify(daoFactory).getAgentInfoDAO();
+        verify(daoFactory).getBackendInfoDAO();
+        
+        verify(backendRegistry).addActionListener(any(ActionListener.class));
     }
     
     @Test
     public void testStartAgent() throws Exception {
         
         // Start agent.
-        Agent agent = new Agent(backendRegistry, config, storage, agentInfoDao, backendInfoDao);
+        Agent agent = new Agent(backendRegistry, config, daoFactory);
+        
         agent.start();
 
-        // Verify that backend has been activated and storage received the agent information.
-        verify(backend).activate();
+        // Verify that backend registry is started
+        verify(backendRegistry).start();
+        ArgumentCaptor<AgentInformation> argument = ArgumentCaptor.forClass(AgentInformation.class);
+
+        verify(agentInfoDao).addAgentInformation(argument.capture());
+        assertEquals(123, argument.getValue().getStartTime());
+    }
+    
+    @Test
+    public void testServiceAddedRemovedFromOSGi() throws Exception {
+        ArgumentCaptor<ActionListener> backendListener = ArgumentCaptor.forClass(ActionListener.class);
+
+        // Start agent.
+        Agent agent = new Agent(backendRegistry, config, daoFactory);
+        verify(backendRegistry).addActionListener(backendListener.capture());
+        
+        agent.start();
+
+        // Verify that backend registry is started
+        verify(backendRegistry).start();
         ArgumentCaptor<AgentInformation> argument = ArgumentCaptor.forClass(AgentInformation.class);
 
         verify(agentInfoDao).addAgentInformation(argument.capture());
         assertEquals(123, argument.getValue().getStartTime());
 
-        ArgumentCaptor<BackendInformation> backendsArg = ArgumentCaptor.forClass(BackendInformation.class);
-        verify(backendInfoDao).addBackendInformation(backendsArg.capture());
+        ActionListener listener = backendListener.getValue();
+        
+        // add a fake backend to see if it's registered corectly
+        ActionEvent<ThermostatExtensionRegistry.Action> actionEvent =
+                new ActionEvent<>(this, ThermostatExtensionRegistry.Action.SERVICE_ADDED);
+        actionEvent.setPayload(backend);
+        
+        listener.actionPerformed(actionEvent);
+        
+        verify(backend).setDAOFactory(daoFactory);
+        verify(backend).activate();
+        
+        assertTrue(agent.getBackendInfos().containsKey(backend));
+        BackendInformation info = agent.getBackendInfos().get(backend);
+        assertEquals("testname", info.getName());
+        assertEquals("testdesc", info.getDescription());
+        assertEquals(true, info.isObserveNewJvm());
+        assertEquals(true, info.isActive());
+        
+        verify(backendInfoDao).addBackendInformation(info);
 
-        List<BackendInformation> backendInfos = backendsArg.getAllValues();
-        assertEquals(1, backendInfos.size());
-        BackendInformation backend0 = backendInfos.get(0);
-        assertEquals("testname", backend0.getName());
-        assertEquals("testdesc", backend0.getDescription());
-        assertEquals(true, backend0.isObserveNewJvm());
-        assertEquals(true, backend0.isActive());
-        // TODO: We should probably also test getPIDs() and getConfiguration(), but it's not clear to me at this point
-        // what those should really do (and it looks like they're not implemented yet).
+        actionEvent = new ActionEvent<>(this, ThermostatExtensionRegistry.Action.SERVICE_REMOVED);
+        actionEvent.setPayload(backend);
+        listener.actionPerformed(actionEvent);
+        
+        verify(backend).deactivate();
+
+        assertFalse(agent.getBackendInfos().containsKey(backend));
+        verify(backendInfoDao).removeBackendInformation(info);
     }
     
     @Test
     public void testStopAgentWithPurging() throws Exception {
-        Agent agent = new Agent(backendRegistry, config, storage, agentInfoDao, backendInfoDao);
+                
+        Agent agent = new Agent(backendRegistry, config, daoFactory);
         agent.start();
         
         // stop agent
         agent.stop();
         
-        verify(backend).deactivate();
+        verify(backendRegistry).stop();
 
         ArgumentCaptor<AgentInformation> argument = ArgumentCaptor.forClass(AgentInformation.class);        
         verify(agentInfoDao, never()).updateAgentInformation(argument.capture());
         verify(storage, times(1)).purge();
     }
-    
+   
     @Test
     public void testStopAgentWithoutPurging() throws Exception {
         
@@ -144,16 +208,15 @@
         when(config.getStartTime()).thenReturn(123L);
         when(config.purge()).thenReturn(false);
         
-        Agent agent = new Agent(backendRegistry, config, storage, agentInfoDao, backendInfoDao);
+        Agent agent = new Agent(backendRegistry, config, daoFactory);
         agent.start();
         
         // stop agent
         agent.stop();
         
-        verify(backend).deactivate();
+        verify(backendRegistry).stop();
 
         verify(agentInfoDao).updateAgentInformation(isA(AgentInformation.class));
-        verify(backendInfoDao, atLeast(1)).removeBackendInformation(isA(BackendInformation.class));
         verify(storage, times(0)).purge();
     }
 }
--- a/agent/core/src/test/java/com/redhat/thermostat/agent/TestUtils.java	Fri Oct 19 15:33:45 2012 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,103 +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.agent;
-
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.lang.management.ManagementFactory;
-import java.util.Properties;
-import java.util.Random;
-
-import com.redhat.thermostat.agent.config.AgentProperties;
-import com.redhat.thermostat.backend.BackendsProperties;
-
-public class TestUtils {
-
-    public static int getProcessId() {
-        String name = ManagementFactory.getRuntimeMXBean().getName();
-        String pidPart = name.split("@")[0];
-        return Integer.parseInt(pidPart);
-    }
-
-    public static boolean isLinux() {
-        return (System.getProperty("os.name").toLowerCase().contains("linux"));
-    }
-        
-    public static String setupAgentConfigs() throws IOException {
-        // need to create dummy config files for the tests
-        Random random = new Random();
-
-        String tmpDir = System.getProperty("java.io.tmpdir") + File.separatorChar +
-                Math.abs(random.nextInt()) + File.separatorChar;
-
-        System.setProperty("THERMOSTAT_HOME", tmpDir);
-        File agent = new File(tmpDir, "agent");
-        agent.mkdirs();
-
-        File tmpConfigs = new File(agent, "agent.properties");
-
-        new File(agent, "run").mkdirs();
-        new File(agent, "logs").mkdirs();
-
-        File backends = new File(tmpDir, "backends");
-        File system = new File(backends, "system");
-        system.mkdirs();
-        
-        Properties props = new Properties();            
-
-        props.setProperty(AgentProperties.BACKENDS.name(), "system");
-        props.setProperty(AgentProperties.SAVE_ON_EXIT.name(), "false");
-
-        props.store(new FileOutputStream(tmpConfigs), "thermostat agent test properties");
-
-        // now write the configs for the backends
-        tmpConfigs = new File(system, "backend.properties");
-        props = new Properties();
-        props.setProperty(BackendsProperties.BACKEND_CLASS.name(),
-                          "com.redhat.thermostat.backend.system.SystemBackend");
-        props.setProperty(BackendsProperties.DESCRIPTION.name(),
-                          "fluff backend for tests");
-        props.setProperty(BackendsProperties.VENDOR.name(),
-                          "Red Hat, Inc.");
-        props.setProperty(BackendsProperties.VERSION.name(),
-                          "1.0");
-        props.store(new FileOutputStream(tmpConfigs), "thermostat system backend properties");
-        
-        return tmpDir;
-    }
-}
--- a/agent/core/src/test/java/com/redhat/thermostat/agent/config/AgentConfigsUtilsTest.java	Fri Oct 19 15:33:45 2012 -0400
+++ b/agent/core/src/test/java/com/redhat/thermostat/agent/config/AgentConfigsUtilsTest.java	Fri Oct 19 21:58:18 2012 +0200
@@ -37,14 +37,12 @@
 package com.redhat.thermostat.agent.config;
 
 import java.io.IOException;
-import java.util.List;
 
 import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Test;
 
-import com.redhat.thermostat.agent.TestUtils;
-import com.redhat.thermostat.backend.BackendID;
+import com.redhat.thermostat.common.TestUtils;
 import com.redhat.thermostat.common.config.InvalidConfigurationException;
 
 public class AgentConfigsUtilsTest {
@@ -57,10 +55,8 @@
     @Test
     public void test() throws InvalidConfigurationException {
         AgentStartupConfiguration config = AgentConfigsUtils.createAgentConfigs();        
-        List<BackendID> backends = config.getBackends();
-        
-        // the test property only define the system backend so far
-        Assert.assertEquals(1, backends.size());
-        Assert.assertEquals("system", backends.get(0).getSimpleName());
+
+        Assert.assertFalse(config.purge());
+        Assert.assertEquals("42.42.42.42:42", config.getConfigListenAddress());
     }
 }
--- a/agent/core/src/test/java/com/redhat/thermostat/agent/config/AgentOptionParserTest.java	Fri Oct 19 15:33:45 2012 -0400
+++ b/agent/core/src/test/java/com/redhat/thermostat/agent/config/AgentOptionParserTest.java	Fri Oct 19 21:58:18 2012 +0200
@@ -45,7 +45,7 @@
 import org.junit.BeforeClass;
 import org.junit.Test;
 
-import com.redhat.thermostat.agent.TestUtils;
+import com.redhat.thermostat.common.TestUtils;
 import com.redhat.thermostat.common.cli.SimpleArguments;
 import com.redhat.thermostat.common.config.InvalidConfigurationException;
 
@@ -76,7 +76,7 @@
         
         Assert.assertEquals("testURL", configs.getDBConnectionString());
         Assert.assertTrue(configs.isDebugConsole());
-        Assert.assertTrue(configs.purge());
+        Assert.assertFalse(configs.purge());
     }
     
     @Test
--- a/agent/core/src/test/java/com/redhat/thermostat/backend/BackendConfigurationLoaderTest.java	Fri Oct 19 15:33:45 2012 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,66 +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;
-
-import java.io.IOException;
-import java.util.Map;
-
-import junit.framework.Assert;
-
-import org.junit.Before;
-import org.junit.Test;
-
-import com.redhat.thermostat.agent.TestUtils;
-import com.redhat.thermostat.backend.system.SystemBackend;
-import com.redhat.thermostat.common.config.InvalidConfigurationException;
-
-public class BackendConfigurationLoaderTest {
-    
-    @Before
-    public void setUp() throws IOException {
-        TestUtils.setupAgentConfigs();
-    }
-    
-    @Test
-    public void test() throws InvalidConfigurationException {
-        Map<String, String> backendProps = new BackendConfigurationLoader().retrieveBackendConfigs("system");
-        Assert.assertTrue(backendProps.containsKey(BackendsProperties.BACKEND_CLASS.name()));
-        
-        String className = backendProps.get(BackendsProperties.BACKEND_CLASS.name());
-        Assert.assertEquals(SystemBackend.class.getCanonicalName(), className);
-    }
-}
--- a/agent/core/src/test/java/com/redhat/thermostat/backend/BackendRegistryTest.java	Fri Oct 19 15:33:45 2012 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,148 +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;
-
-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.when;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-
-import com.redhat.thermostat.agent.config.AgentStartupConfiguration;
-import com.redhat.thermostat.common.config.InvalidConfigurationException;
-import com.redhat.thermostat.common.dao.DAOFactory;
-import com.redhat.thermostat.common.storage.Storage;
-
-public class BackendRegistryTest {
-
-    public static class MockBackend extends Backend {
-        public MockBackend() {
-            super();
-        }
-
-        @Override
-        public String getConfigurationValue(String key) {
-            return null;
-        }
-        @Override
-        public boolean activate() {
-            return true;
-        }
-        @Override
-        public boolean deactivate() {
-            return false;
-        }
-        @Override
-        public boolean isActive() {
-            return false;
-        }
-        @Override
-        public boolean attachToNewProcessByDefault() {
-            return false;
-        }
-
-        @Override
-        protected void setDAOFactoryAction() {
-            /* NO-OP */
-        }
-    }
-
-    private DAOFactory daoFactory;
-    private List<BackendID> backends;
-    private AgentStartupConfiguration config;
-    private BackendConfigurationLoader configLoader;
-
-    @Before
-    public void setUp() throws InvalidConfigurationException {
-        backends = new ArrayList<>();
-
-        config = mock(AgentStartupConfiguration.class);
-        when(config.getBackends()).thenReturn(backends);
-
-        configLoader = mock(BackendConfigurationLoader.class);
-        when(configLoader.retrieveBackendConfigs(any(String.class))).thenReturn(new HashMap<String,String>());
-
-        Storage storage = mock(Storage.class);
-        daoFactory = mock(DAOFactory.class);
-        when(daoFactory.getStorage()).thenReturn(storage);
-    }
-
-    @After
-    public void tearDown() {
-        backends = null;
-        config = null;
-        configLoader = null;
-    }
-
-    @Test
-    public void testRegisterBackend() throws BackendLoadException, InvalidConfigurationException {
-        /* setup fake backend */
-        backends.add(new BackendID("mock", MockBackend.class.getName()));
-
-        BackendRegistry registry = new BackendRegistry(config, configLoader, daoFactory);
-        assertEquals(1, registry.getAll().size());
-        assertNotNull(registry.getByName("mock"));
-    }
-
-    @Test
-    public void testNoBackendsRegistered() throws InvalidConfigurationException, BackendLoadException {
-        BackendRegistry registry = new BackendRegistry(config, configLoader, daoFactory);
-        assertEquals(0, registry.getAll().size());
-        assertEquals(null, registry.getByName("system"));
-        assertEquals(null, registry.getByName("mock"));
-    }
-
-    @Test (expected=BackendLoadException.class)
-    public void testRegisterBackendTwice() throws BackendLoadException, InvalidConfigurationException {
-        /* setup fake backends */
-
-        backends.add(new BackendID("mock", MockBackend.class.getClass().getName()));
-        backends.add(new BackendID("mock", MockBackend.class.getClass().getName()));
-
-        /* load the backends */
-        new BackendRegistry(config, configLoader, daoFactory);
-    }
-
-}
--- a/agent/core/src/test/java/com/redhat/thermostat/backend/sample/SampleBackendTest.java	Fri Oct 19 15:33:45 2012 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,100 +0,0 @@
-/*
- * Copyright 2012 Red Hat, Inc.
- *
- * This file is part of Thermostat.
- *
- * Thermostat is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published
- * by the Free Software Foundation; either version 2, or (at your
- * option) any later version.
- *
- * Thermostat is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Thermostat; see the file COPYING.  If not see
- * <http://www.gnu.org/licenses/>.
- *
- * Linking this code with other modules is making a combined work
- * based on this code.  Thus, the terms and conditions of the GNU
- * General Public License cover the whole combination.
- *
- * As a special exception, the copyright holders of this code give
- * you permission to link this code with independent modules to
- * produce an executable, regardless of the license terms of these
- * independent modules, and to copy and distribute the resulting
- * executable under terms of your choice, provided that you also
- * meet, for each linked independent module, the terms and conditions
- * of the license of that module.  An independent module is a module
- * which is not derived from or based on this code.  If you modify
- * this code, you may extend this exception to your version of the
- * library, but you are not obligated to do so.  If you do not wish
- * to do so, delete this exception statement from your version.
- */
-
-package com.redhat.thermostat.backend.sample;
-
-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 org.junit.Before;
-import org.junit.Test;
-
-public class SampleBackendTest {
-
-    private SampleBackend b;
-
-    @Before
-    public void setUp() {
-        b = new SampleBackend();
-    }
-
-    @Test
-    public void testBackendInfo() {
-        assertNotNull(b.getName());
-        assertNotNull(b.getVersion());
-        assertNotNull(b.getVendor());
-        assertNotNull(b.getDescription());
-    }
-
-    @Test
-    public void testBasicBackend() {
-        assertFalse(b.isActive());
-        b.activate();
-        assertTrue(b.isActive());
-        b.deactivate();
-        assertFalse(b.isActive());
-    }
-
-    @Test
-    public void testActivateTwice() {
-        b.activate();
-        b.activate();
-        assert (b.isActive());
-    }
-
-    @Test
-    public void testDeactiateWhenNotActive() {
-        b.deactivate();
-        b.deactivate();
-        assertFalse(b.isActive());
-    }
-
-    @Test
-    public void testDefaultConfiguration() {
-        assertTrue(b.getConfigurationMap().isEmpty());
-        assertTrue(b.getConfigurationValue("foo") == null);
-
-    }
-
-    @Test
-    public void testModifyConfiguration() {
-        b.setConfigurationValue("speed", "fast");
-        assertEquals("fast", b.getConfigurationValue("speed"));
-    }
-
-}
--- a/agent/core/src/test/java/com/redhat/thermostat/backend/system/CpuStatBuilderTest.java	Fri Oct 19 15:33:45 2012 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,111 +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.assertArrayEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import java.io.BufferedReader;
-import java.io.IOException;
-import java.io.StringReader;
-
-import org.junit.Test;
-
-import com.redhat.thermostat.common.Clock;
-import com.redhat.thermostat.common.SystemClock;
-import com.redhat.thermostat.common.model.CpuStat;
-import com.redhat.thermostat.common.utils.ArrayUtils;
-
-public class CpuStatBuilderTest {
-
-    @Test
-    public void testSimpleBuild() {
-        ProcDataSource dataSource = new ProcDataSource();
-        CpuStatBuilder builder= new CpuStatBuilder(new SystemClock(), dataSource, 100l);
-        builder.initialize();
-        CpuStat stat = builder.build();
-        assertNotNull(stat);
-    }
-
-    @Test (expected=IllegalStateException.class)
-    public void buildWithoutInitializeThrowsException() {
-        Clock clock = mock(Clock.class);
-        ProcDataSource dataSource = mock(ProcDataSource.class);
-        long ticksPerSecond = 1;
-        CpuStatBuilder builder = new CpuStatBuilder(clock, dataSource, ticksPerSecond);
-        builder.build();
-    }
-
-    @Test
-    public void testBuildCpuStatFromFile() throws IOException {
-        long CLOCK1 = 1000;
-        long CLOCK2 = 2000;
-
-        String firstReadContents =
-            "cpu 100 0 0 1000 1000\n" +
-            "cpu0 100 0 0 1000 1000\n" +
-            "cpu1 10 80 10 1000 1000\n";
-        BufferedReader reader1 = new BufferedReader(new StringReader(firstReadContents));
-
-        String secondReadContents =
-            "cpu 400 0 0 1000 1000\n" +
-            "cpu0 200 0 0 1000 1000\n" +
-            "cpu1 30 50 120 1000 1000\n";
-        BufferedReader reader2 = new BufferedReader(new StringReader(secondReadContents));
-
-        long ticksPerSecond = 100;
-        Clock clock = mock(Clock.class);
-        when(clock.getRealTimeMillis()).thenReturn(CLOCK2);
-        when(clock.getMonotonicTimeNanos()).thenReturn((long)(CLOCK1 * 1E6)).thenReturn((long)(CLOCK2 * 1E6));
-
-        ProcDataSource dataSource = mock(ProcDataSource.class);
-        when(dataSource.getStatReader()).thenReturn(reader1).thenReturn(reader2);
-        CpuStatBuilder builder = new CpuStatBuilder(clock, dataSource, ticksPerSecond);
-
-        builder.initialize();
-
-        CpuStat stat = builder.build();
-
-        verify(dataSource, times(2)).getStatReader();
-        assertArrayEquals(new double[] {100, 100}, ArrayUtils.toPrimitiveDoubleArray(stat.getPerProcessorUsage()), 0.01);
-    }
-
-}
--- a/agent/core/src/test/java/com/redhat/thermostat/backend/system/DistributionInformationTest.java	Fri Oct 19 15:33:45 2012 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,71 +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.*;
-
-import org.junit.Test;
-
-import com.redhat.thermostat.agent.TestUtils;
-
-public class DistributionInformationTest {
-
-    @Test
-    public void testName() {
-        if (TestUtils.isLinux()) {
-            DistributionInformation info = DistributionInformation.get();
-            String name = info.getName();
-            assertNotNull(name);
-            assertTrue(name.length() > 0);
-            assertFalse(name.startsWith(":"));
-            assertFalse(name.equals(DistributionInformation.UNKNOWN_NAME));
-        }
-    }
-
-    @Test
-    public void testVersion() {
-        if (TestUtils.isLinux()) {
-            DistributionInformation info = DistributionInformation.get();
-            String version = info.getVersion();
-            assertNotNull(version);
-            assertTrue(version.length()> 0);
-            assertFalse(version.startsWith(":"));
-            assertFalse(version.equals(DistributionInformation.UNKNOWN_VERSION));
-        }
-    }
-
-}
--- a/agent/core/src/test/java/com/redhat/thermostat/backend/system/EtcOsReleaseTest.java	Fri Oct 19 15:33:45 2012 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,84 +0,0 @@
-/*
- * Copyright 2012 Red Hat, Inc.
- *
- * This file is part of Thermostat.
- *
- * Thermostat is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published
- * by the Free Software Foundation; either version 2, or (at your
- * option) any later version.
- *
- * Thermostat is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Thermostat; see the file COPYING.  If not see
- * <http://www.gnu.org/licenses/>.
- *
- * Linking this code with other modules is making a combined work
- * based on this code.  Thus, the terms and conditions of the GNU
- * General Public License cover the whole combination.
- *
- * As a special exception, the copyright holders of this code give
- * you permission to link this code with independent modules to
- * produce an executable, regardless of the license terms of these
- * independent modules, and to copy and distribute the resulting
- * executable under terms of your choice, provided that you also
- * meet, for each linked independent module, the terms and conditions
- * of the license of that module.  An independent module is a module
- * which is not derived from or based on this code.  If you modify
- * this code, you may extend this exception to your version of the
- * library, but you are not obligated to do so.  If you do not wish
- * to do so, delete this exception statement from your version.
- */
-
-package com.redhat.thermostat.backend.system;
-
-import static org.junit.Assert.assertEquals;
-
-import java.io.BufferedReader;
-import java.io.IOException;
-import java.io.StringReader;
-
-import org.junit.Test;
-
-import com.redhat.thermostat.test.Bug;
-
-public class EtcOsReleaseTest {
-
-    @Test
-    public void testName() throws IOException, InterruptedException {
-        BufferedReader reader = new BufferedReader(new StringReader("NAME=\"Name\"\n"));
-        DistributionInformation info = new EtcOsRelease().getFromOsRelease(reader);
-        assertEquals("Name", info.getName());
-    }
-
-
-    @Test
-    public void testVersion() throws IOException {
-        BufferedReader reader = new BufferedReader(new StringReader("VERSION=\"Version\"\n"));
-        DistributionInformation info = new EtcOsRelease().getFromOsRelease(reader);
-        assertEquals("Version", info.getVersion());
-    }
-
-    @Bug(id="981",
-        summary="DistributionInformationTest fails on OpenSUSE Linux 12.1",
-        url="http://icedtea.classpath.org/bugzilla/show_bug.cgi?id=981")
-    @Test
-    public void testFormattedOutput() throws IOException {
-        String output =
-            "NAME=openSUSE\n" +
-            "VERSION = 12.1 (Asparagus)\n" +
-            "VERSION_ID=\"12.1\"\n" +
-            "PRETTY_NAME=\"openSUSE 12.1 (Asparagus) (x86_64)\"\n" +
-            "ID=opensuse";
-        BufferedReader reader = new BufferedReader(new StringReader(output));
-        DistributionInformation info = new EtcOsRelease().getFromOsRelease(reader);
-
-        assertEquals("openSUSE", info.getName());
-        assertEquals("12.1 (Asparagus)", info.getVersion());
-    }
-
-}
--- a/agent/core/src/test/java/com/redhat/thermostat/backend/system/HostInfoBuilderTest.java	Fri Oct 19 15:33:45 2012 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,121 +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.Mockito.mock;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import java.io.IOException;
-import java.io.StringReader;
-import java.net.InetAddress;
-
-import org.junit.Test;
-
-import com.redhat.thermostat.backend.system.HostInfoBuilder.HostCpuInfo;
-import com.redhat.thermostat.backend.system.HostInfoBuilder.HostMemoryInfo;
-import com.redhat.thermostat.backend.system.HostInfoBuilder.HostOsInfo;
-import com.redhat.thermostat.common.Constants;
-import com.redhat.thermostat.common.model.HostInfo;
-
-public class HostInfoBuilderTest {
-
-    @Test
-    public void testSimpleBuild() {
-        HostInfo info = new HostInfoBuilder(new ProcDataSource()).build();
-        assertNotNull(info);
-    }
-
-    @Test
-    public void testCpuInfo() throws IOException {
-        String cpuInfoString =
-                "processor: 1\n" +
-                "model name: Test Model\n" +
-                "processor: 0\n" +
-                "model name: Test Model\n";
-
-        StringReader cpuInfoReader = new StringReader(cpuInfoString);
-
-        ProcDataSource dataSource = mock(ProcDataSource.class);
-        when(dataSource.getCpuInfoReader()).thenReturn(cpuInfoReader);
-
-        HostCpuInfo cpuInfo = new HostInfoBuilder(dataSource).getCpuInfo();
-        assertEquals(2, cpuInfo.count);
-        assertEquals("Test Model", cpuInfo.model);
-        verify(dataSource).getCpuInfoReader();
-    }
-
-    @Test
-    public void testMemoryInfo() throws IOException {
-        String memoryInfoString =
-                "MemTotal: 12345 kB";
-
-        StringReader memoryInfoReader = new StringReader(memoryInfoString);
-        ProcDataSource dataSource = mock(ProcDataSource.class);
-        when(dataSource.getMemInfoReader()).thenReturn(memoryInfoReader);
-
-        HostMemoryInfo memoryInfo = new HostInfoBuilder(dataSource).getMemoryInfo();
-        assertNotNull(memoryInfo);
-        assertEquals(12345 * Constants.KILOBYTES_TO_BYTES, memoryInfo.totalMemory);
-        verify(dataSource).getMemInfoReader();
-
-    }
-
-    @Test
-    public void testOsInfo() {
-        DistributionInformation distroInfo = new DistributionInformation("distro-name", "distro-version");
-        ProcDataSource dataSource = mock(ProcDataSource.class);
-        HostOsInfo osInfo = new HostInfoBuilder(dataSource).getOsInfo(distroInfo);
-        assertEquals("distro-name distro-version", osInfo.distribution);
-        assertEquals(System.getProperty("os.name") + " " + System.getProperty("os.version"), osInfo.kernel);
-    }
-
-    @Test
-    public void testHostname() {
-
-        InetAddress address = mock(InetAddress.class);
-        when(address.getCanonicalHostName()).thenReturn("test-hostname");
-
-        ProcDataSource dataSource = mock(ProcDataSource.class);
-
-        String name = new HostInfoBuilder(dataSource).getHostName(address);
-        assertEquals("test-hostname", name);
-    }
-
-}
--- a/agent/core/src/test/java/com/redhat/thermostat/backend/system/JvmStatDataExtractorTest.java	Fri Oct 19 15:33:45 2012 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,375 +0,0 @@
-/*
- * Copyright 2012 Red Hat, Inc.
- *
- * This file is part of Thermostat.
- *
- * Thermostat is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published
- * by the Free Software Foundation; either version 2, or (at your
- * option) any later version.
- *
- * Thermostat is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Thermostat; see the file COPYING.  If not see
- * <http://www.gnu.org/licenses/>.
- *
- * Linking this code with other modules is making a combined work
- * based on this code.  Thus, the terms and conditions of the GNU
- * General Public License cover the whole combination.
- *
- * As a special exception, the copyright holders of this code give
- * you permission to link this code with independent modules to
- * produce an executable, regardless of the license terms of these
- * independent modules, and to copy and distribute the resulting
- * executable under terms of your choice, provided that you also
- * meet, for each linked independent module, the terms and conditions
- * of the license of that module.  An independent module is a module
- * which is not derived from or based on this code.  If you modify
- * this code, you may extend this exception to your version of the
- * library, but you are not obligated to do so.  If you do not wish
- * to do so, delete this exception statement from your version.
- */
-
-package com.redhat.thermostat.backend.system;
-
-import static org.junit.Assert.assertEquals;
-import static org.mockito.Matchers.eq;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import org.junit.Test;
-
-import sun.jvmstat.monitor.LongMonitor;
-import sun.jvmstat.monitor.MonitorException;
-import sun.jvmstat.monitor.MonitoredVm;
-import sun.jvmstat.monitor.StringMonitor;
-
-public class JvmStatDataExtractorTest {
-
-    private MonitoredVm buildStringMonitoredVm(String monitorName, String monitorReturn) throws MonitorException {
-        final StringMonitor monitor = mock(StringMonitor.class);
-        when(monitor.stringValue()).thenReturn(monitorReturn);
-        when(monitor.getValue()).thenReturn(monitorReturn);
-        MonitoredVm vm = mock(MonitoredVm.class);
-        when(vm.findByName(monitorName)).thenReturn(monitor);
-        return vm;
-    }
-
-    private MonitoredVm buildLongMonitoredVm(String monitorName, Long monitorReturn) throws MonitorException {
-        final LongMonitor monitor = mock(LongMonitor.class);
-        when(monitor.longValue()).thenReturn(monitorReturn);
-        when(monitor.getValue()).thenReturn(monitorReturn);
-        MonitoredVm vm = mock(MonitoredVm.class);
-        when(vm.findByName(monitorName)).thenReturn(monitor);
-        return vm;
-    }
-
-    @Test
-    public void testCommandLine() throws MonitorException {
-        final String MONITOR_NAME = "sun.rt.javaCommand";
-        final String MONITOR_VALUE = "command line java";
-        MonitoredVm vm = buildStringMonitoredVm(MONITOR_NAME, MONITOR_VALUE);
-
-        JvmStatDataExtractor extractor = new JvmStatDataExtractor(vm);
-        String returned = extractor.getCommandLine();
-
-        verify(vm).findByName(eq(MONITOR_NAME));
-        assertEquals(MONITOR_VALUE, returned);
-    }
-
-    @Test
-    public void testMainClass() throws MonitorException {
-        final String MONITOR_NAME = "sun.rt.javaCommand";
-        final String MONITOR_VALUE = "some.package.Main";
-        MonitoredVm vm = buildStringMonitoredVm(MONITOR_NAME, MONITOR_VALUE);
-
-        JvmStatDataExtractor extractor = new JvmStatDataExtractor(vm);
-        String returned = extractor.getMainClass();
-
-        verify(vm).findByName(eq(MONITOR_NAME));
-        assertEquals(MONITOR_VALUE, returned);
-    }
-
-    @Test
-    public void testJavaVersion() throws MonitorException {
-        final String MONITOR_NAME = "java.property.java.version";
-        final String MONITOR_VALUE = "some java version";
-        MonitoredVm vm = buildStringMonitoredVm(MONITOR_NAME, MONITOR_VALUE);
-
-        JvmStatDataExtractor extractor = new JvmStatDataExtractor(vm);
-        String returned = extractor.getJavaVersion();
-
-        verify(vm).findByName(eq(MONITOR_NAME));
-        assertEquals(MONITOR_VALUE, returned);
-    }
-
-    @Test
-    public void testJavaHome() throws MonitorException {
-        final String MONITOR_NAME = "java.property.java.home";
-        final String MONITOR_VALUE = "${java.home}";
-        MonitoredVm vm = buildStringMonitoredVm(MONITOR_NAME, MONITOR_VALUE);
-
-        JvmStatDataExtractor extractor = new JvmStatDataExtractor(vm);
-        String returned = extractor.getJavaHome();
-
-        verify(vm).findByName(eq(MONITOR_NAME));
-        assertEquals(MONITOR_VALUE, returned);
-    }
-
-    @Test
-    public void testVmName() throws MonitorException {
-        final String MONITOR_NAME = "java.property.java.vm.name";
-        final String MONITOR_VALUE = "${vm.name}";
-        MonitoredVm vm = buildStringMonitoredVm(MONITOR_NAME, MONITOR_VALUE);
-
-        JvmStatDataExtractor extractor = new JvmStatDataExtractor(vm);
-        String returned = extractor.getVmName();
-
-        verify(vm).findByName(eq(MONITOR_NAME));
-        assertEquals(MONITOR_VALUE, returned);
-    }
-
-    @Test
-    public void testVmInfo() throws MonitorException {
-        final String MONITOR_NAME = "java.property.java.vm.info";
-        final String MONITOR_VALUE = "${vm.info}";
-        MonitoredVm vm = buildStringMonitoredVm(MONITOR_NAME, MONITOR_VALUE);
-
-        JvmStatDataExtractor extractor = new JvmStatDataExtractor(vm);
-        String returned = extractor.getVmInfo();
-
-        verify(vm).findByName(eq(MONITOR_NAME));
-        assertEquals(MONITOR_VALUE, returned);
-    }
-
-    @Test
-    public void testVmVersion() throws MonitorException {
-        final String MONITOR_NAME = "java.property.java.vm.version";
-        final String MONITOR_VALUE = "${vm.version}";
-        MonitoredVm vm = buildStringMonitoredVm(MONITOR_NAME, MONITOR_VALUE);
-
-        JvmStatDataExtractor extractor = new JvmStatDataExtractor(vm);
-        String returned = extractor.getVmVersion();
-
-        verify(vm).findByName(eq(MONITOR_NAME));
-        assertEquals(MONITOR_VALUE, returned);
-    }
-
-    @Test
-    public void testVmArguments() throws MonitorException {
-        final String MONITOR_NAME = "java.rt.vmArgs";
-        final String MONITOR_VALUE = "${vm.arguments}";
-        MonitoredVm vm = buildStringMonitoredVm(MONITOR_NAME, MONITOR_VALUE);
-
-        JvmStatDataExtractor extractor = new JvmStatDataExtractor(vm);
-        String returned = extractor.getVmArguments();
-
-        verify(vm).findByName(eq(MONITOR_NAME));
-        assertEquals(MONITOR_VALUE, returned);
-    }
-
-    @Test
-    public void testTotalCollectors() throws MonitorException {
-        final String MONITOR_NAME = "sun.gc.policy.collectors";
-        final Long MONITOR_VALUE = 9l;
-        MonitoredVm vm = buildLongMonitoredVm(MONITOR_NAME, MONITOR_VALUE);
-
-        JvmStatDataExtractor extractor = new JvmStatDataExtractor(vm);
-        Long returned = extractor.getTotalCollectors();
-
-        verify(vm).findByName(eq(MONITOR_NAME));
-        assertEquals(MONITOR_VALUE, returned);
-    }
-
-    @Test
-    public void testCollectorName() throws MonitorException {
-        final String MONITOR_NAME = "sun.gc.collector.0.name";
-        final String COLLECTOR_NAME = "SomeMemoryCollector";
-        MonitoredVm vm = buildStringMonitoredVm(MONITOR_NAME, COLLECTOR_NAME);
-
-        JvmStatDataExtractor extractor = new JvmStatDataExtractor(vm);
-        String returned = extractor.getCollectorName(0);
-
-        verify(vm).findByName(eq(MONITOR_NAME));
-        assertEquals(COLLECTOR_NAME, returned);
-    }
-
-    @Test
-    public void testCollectorTime() throws MonitorException {
-        final String MONITOR_NAME = "sun.gc.collector.0.time";
-        final Long COLLECTOR_TIME = 99l;
-        MonitoredVm vm = buildLongMonitoredVm(MONITOR_NAME, COLLECTOR_TIME);
-
-        JvmStatDataExtractor extractor = new JvmStatDataExtractor(vm);
-        Long returned = extractor.getCollectorTime(0);
-
-        verify(vm).findByName(eq(MONITOR_NAME));
-        assertEquals(COLLECTOR_TIME, returned);
-    }
-
-    @Test
-    public void testCollectorInvocations() throws MonitorException {
-        final String MONITOR_NAME = "sun.gc.collector.0.invocations";
-        final Long COLLECTOR_INVOCATIONS = 99l;
-        MonitoredVm vm = buildLongMonitoredVm(MONITOR_NAME, COLLECTOR_INVOCATIONS);
-
-        JvmStatDataExtractor extractor = new JvmStatDataExtractor(vm);
-        Long returned = extractor.getCollectorInvocations(0);
-
-        verify(vm).findByName(eq(MONITOR_NAME));
-        assertEquals(COLLECTOR_INVOCATIONS, returned);
-    }
-
-    @Test
-    public void testTotalGcGenerations() throws MonitorException {
-        final String MONITOR_NAME = "sun.gc.policy.generations";
-        final Long GC_GENERATIONS = 99l;
-        MonitoredVm vm = buildLongMonitoredVm(MONITOR_NAME, GC_GENERATIONS);
-
-        JvmStatDataExtractor extractor = new JvmStatDataExtractor(vm);
-        Long returned = extractor.getTotalGcGenerations();
-
-        verify(vm).findByName(eq(MONITOR_NAME));
-        assertEquals(GC_GENERATIONS, returned);
-    }
-
-    @Test
-    public void testGenerationName() throws MonitorException {
-        final String MONITOR_NAME = "sun.gc.generation.0.name";
-        final String GENERATION_NAME = "Youth";
-        MonitoredVm vm = buildStringMonitoredVm(MONITOR_NAME, GENERATION_NAME);
-
-        JvmStatDataExtractor extractor = new JvmStatDataExtractor(vm);
-        String returned = extractor.getGenerationName(0);
-
-        verify(vm).findByName(eq(MONITOR_NAME));
-        assertEquals(GENERATION_NAME, returned);
-    }
-
-    @Test
-    public void testGenerationCapacity() throws MonitorException {
-        final String MONITOR_NAME = "sun.gc.generation.0.capacity";
-        final Long GENERATION_CAPACITY = 99l;
-        MonitoredVm vm = buildLongMonitoredVm(MONITOR_NAME, GENERATION_CAPACITY);
-
-        JvmStatDataExtractor extractor = new JvmStatDataExtractor(vm);
-        Long returned = extractor.getGenerationCapacity(0);
-
-        verify(vm).findByName(eq(MONITOR_NAME));
-        assertEquals(GENERATION_CAPACITY, returned);
-    }
-
-    @Test
-    public void testGenerationMaxCapacity() throws MonitorException {
-        final String MONITOR_NAME = "sun.gc.generation.0.maxCapacity";
-        final Long GENERATION_MAX_CAPACITY = 99l;
-        MonitoredVm vm = buildLongMonitoredVm(MONITOR_NAME, GENERATION_MAX_CAPACITY);
-
-
-        JvmStatDataExtractor extractor = new JvmStatDataExtractor(vm);
-        Long returned = extractor.getGenerationMaxCapacity(0);
-
-        verify(vm).findByName(eq(MONITOR_NAME));
-        assertEquals(GENERATION_MAX_CAPACITY, returned);
-    }
-
-    @Test
-    public void testGenerationCollector() throws MonitorException {
-        final String MONITOR_NAME = "sun.gc.collector.0.name";
-        final String GENERATION_COLLECTOR = "generation collector";
-        MonitoredVm vm = buildStringMonitoredVm(MONITOR_NAME, GENERATION_COLLECTOR);
-
-        JvmStatDataExtractor extractor = new JvmStatDataExtractor(vm);
-        String returned = extractor.getGenerationCollector(0);
-
-        verify(vm).findByName(eq(MONITOR_NAME));
-        assertEquals(GENERATION_COLLECTOR, returned);
-    }
-
-    @Test
-    public void testTotalSpaces() throws MonitorException {
-        final Long TOTAL_SPACES = 99l;
-        final LongMonitor monitor = mock(LongMonitor.class);
-        when(monitor.getValue()).thenReturn(TOTAL_SPACES);
-        MonitoredVm vm = mock(MonitoredVm.class);
-        when(vm.findByName("sun.gc.generation.0.spaces")).thenReturn(monitor);
-
-        JvmStatDataExtractor extractor = new JvmStatDataExtractor(vm);
-        Long returned = extractor.getTotalSpaces(0);
-
-        verify(vm).findByName(eq("sun.gc.generation.0.spaces"));
-        assertEquals(TOTAL_SPACES, returned);
-    }
-
-
-    @Test
-    public void testSpaceName() throws MonitorException {
-        final String MONITOR_NAME = "sun.gc.generation.0.space.0.name";
-        final String SPACE_NAME = "Hilbert";
-        MonitoredVm vm = buildStringMonitoredVm(MONITOR_NAME, SPACE_NAME);
-
-        JvmStatDataExtractor extractor = new JvmStatDataExtractor(vm);
-        String returned = extractor.getSpaceName(0,0);
-
-        verify(vm).findByName(eq(MONITOR_NAME));
-        assertEquals(SPACE_NAME, returned);
-    }
-
-    @Test
-    public void testSpaceCapacity() throws MonitorException {
-        final String MONITOR_NAME = "sun.gc.generation.0.space.0.capacity";
-        final Long SPACE_CAPACITY = 99l;
-        MonitoredVm vm = buildLongMonitoredVm(MONITOR_NAME, SPACE_CAPACITY);
-
-        JvmStatDataExtractor extractor = new JvmStatDataExtractor(vm);
-        Long returned = extractor.getSpaceCapacity(0,0);
-
-        verify(vm).findByName(eq(MONITOR_NAME));
-        assertEquals(SPACE_CAPACITY, returned);
-    }
-
-    @Test
-    public void testSpaceMaxCapacity() throws MonitorException {
-        final String MONITOR_NAME = "sun.gc.generation.0.space.0.maxCapacity";
-        final Long SPACE_MAX_CAPACITY = 99l;
-        MonitoredVm vm = buildLongMonitoredVm(MONITOR_NAME, SPACE_MAX_CAPACITY);
-
-        JvmStatDataExtractor extractor = new JvmStatDataExtractor(vm);
-        Long returned = extractor.getSpaceMaxCapacity(0,0);
-
-        verify(vm).findByName(eq(MONITOR_NAME));
-        assertEquals(SPACE_MAX_CAPACITY, returned);
-    }
-
-    @Test
-    public void testSpaceUsed() throws MonitorException {
-        final String MONITOR_NAME = "sun.gc.generation.0.space.0.used";
-        final Long SPACE_USED = 99l;
-        MonitoredVm vm = buildLongMonitoredVm(MONITOR_NAME, SPACE_USED);
-
-        JvmStatDataExtractor extractor = new JvmStatDataExtractor(vm);
-        Long returned = extractor.getSpaceUsed(0,0);
-
-        verify(vm).findByName(eq(MONITOR_NAME));
-        assertEquals(SPACE_USED, returned);
-    }
-
-    @Test
-    public void testLoadedClasses() throws MonitorException {
-        final String MONITOR_NAME = "java.cls.loadedClasses";
-        final Long LOADED_CLASSES = 99l;
-        MonitoredVm vm = buildLongMonitoredVm(MONITOR_NAME, LOADED_CLASSES);
-
-        JvmStatDataExtractor extractor = new JvmStatDataExtractor(vm);
-        Long returned = extractor.getLoadedClasses();
-
-        verify(vm).findByName(eq(MONITOR_NAME));
-        assertEquals(LOADED_CLASSES, returned);
-    }
-
-}
--- a/agent/core/src/test/java/com/redhat/thermostat/backend/system/JvmStatHostListenerTest.java	Fri Oct 19 15:33:45 2012 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,93 +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.mockito.Matchers.any;
-import static org.mockito.Matchers.anyString;
-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 org.junit.Test;
-import org.mockito.Matchers;
-
-import sun.jvmstat.monitor.HostIdentifier;
-import sun.jvmstat.monitor.MonitoredHost;
-import sun.jvmstat.monitor.MonitoredVm;
-import sun.jvmstat.monitor.StringMonitor;
-import sun.jvmstat.monitor.VmIdentifier;
-import sun.jvmstat.monitor.event.VmStatusChangeEvent;
-
-import com.redhat.thermostat.common.dao.VmClassStatDAO;
-import com.redhat.thermostat.common.dao.VmInfoDAO;
-
-public class JvmStatHostListenerTest {
-
-    @Test
-    public void testVmStatusChangedAddsVmClassListener() throws Exception {
-        VmStatusChangeEvent vmEvent = mock(VmStatusChangeEvent.class);
-        Set<Integer> startedVms = new HashSet<Integer>();
-        startedVms.add(123);
-        when(vmEvent.getStarted()).thenReturn(startedVms);
-
-        MonitoredVm vm = mock(MonitoredVm.class);
-        StringMonitor monitor = mock(StringMonitor.class);
-        when(monitor.stringValue()).thenReturn("test");
-        when(monitor.getValue()).thenReturn("test");
-        when(vm.findByName(anyString())).thenReturn(monitor);
-        MonitoredHost host = mock(MonitoredHost.class);
-        HostIdentifier hostId = mock(HostIdentifier.class);
-        when(host.getHostIdentifier()).thenReturn(hostId);
-        when(host.getMonitoredVm(any(VmIdentifier.class))).thenReturn(vm);
-        when(vmEvent.getMonitoredHost()).thenReturn(host);
-
-        VmClassStatDAO vmClassDAO = mock(VmClassStatDAO.class);
-        VmInfoDAO vmInfoDAO = mock(VmInfoDAO.class);
-
-        JvmStatHostListener l = new JvmStatHostListener(vmInfoDAO, null, null, vmClassDAO ,true);
-        SystemBackend backend = mock(SystemBackend.class);
-        when(backend.getObserveNewJvm()).thenReturn(true);
-
-        l.vmStatusChanged(vmEvent);
-
-        verify(vm).addVmListener(Matchers.isA(JvmStatVmClassListener.class));
-    }
-
-}
\ No newline at end of file
--- a/agent/core/src/test/java/com/redhat/thermostat/backend/system/JvmStatVmClassListenerTest.java	Fri Oct 19 15:33:45 2012 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,100 +0,0 @@
-/*
- * Copyright 2012 Red Hat, Inc.
- *
- * This file is part of Thermostat.
- *
- * Thermostat is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published
- * by the Free Software Foundation; either version 2, or (at your
- * option) any later version.
- *
- * Thermostat is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Thermostat; see the file COPYING.  If not see
- * <http://www.gnu.org/licenses/>.
- *
- * Linking this code with other modules is making a combined work
- * based on this code.  Thus, the terms and conditions of the GNU
- * General Public License cover the whole combination.
- *
- * As a special exception, the copyright holders of this code give
- * you permission to link this code with independent modules to
- * produce an executable, regardless of the license terms of these
- * independent modules, and to copy and distribute the resulting
- * executable under terms of your choice, provided that you also
- * meet, for each linked independent module, the terms and conditions
- * of the license of that module.  An independent module is a module
- * which is not derived from or based on this code.  If you modify
- * this code, you may extend this exception to your version of the
- * library, but you are not obligated to do so.  If you do not wish
- * to do so, delete this exception statement from your version.
- */
-
-package com.redhat.thermostat.backend.system;
-
-import static org.junit.Assert.assertEquals;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import org.junit.Test;
-import org.mockito.ArgumentCaptor;
-
-import sun.jvmstat.monitor.Monitor;
-import sun.jvmstat.monitor.MonitoredVm;
-import sun.jvmstat.monitor.event.VmEvent;
-
-import com.redhat.thermostat.common.dao.VmClassStatDAO;
-import com.redhat.thermostat.common.model.VmClassStat;
-
-public class JvmStatVmClassListenerTest {
-
-    private static final Integer VM_ID = 123;
-    private static final Long LOADED_CLASSES = 1234L;
-
-    @Test
-    public void testMonitorUpdatedClassStat() throws Exception {
-
-        VmClassStatDAO dao = mock(VmClassStatDAO.class);
-
-        JvmStatVmClassListener l = new JvmStatVmClassListener(dao, VM_ID);
-        VmEvent vmEvent = mock(VmEvent.class);
-        MonitoredVm monitoredVm = mock(MonitoredVm.class);
-        Monitor m = mock(Monitor.class);
-        when(m.getValue()).thenReturn(LOADED_CLASSES);
-        when(monitoredVm.findByName("java.cls.loadedClasses")).thenReturn(m);
-        when(vmEvent.getMonitoredVm()).thenReturn(monitoredVm);
-
-        l.monitorsUpdated(vmEvent);
-
-        ArgumentCaptor<VmClassStat> arg = ArgumentCaptor.forClass(VmClassStat.class);
-        verify(dao).putVmClassStat(arg.capture());
-        VmClassStat stat = arg.getValue();
-        assertEquals(LOADED_CLASSES, (Long) stat.getLoadedClasses());
-        assertEquals(VM_ID, (Integer) stat.getVmId());
-    }
-
-    @Test
-    public void testMonitorUpdatedClassStatTwice() throws Exception {
-
-        VmClassStatDAO dao = mock(VmClassStatDAO.class);
-
-        JvmStatVmClassListener l = new JvmStatVmClassListener(dao, VM_ID);
-        VmEvent vmEvent = mock(VmEvent.class);
-        MonitoredVm monitoredVm = mock(MonitoredVm.class);
-        Monitor m = mock(Monitor.class);
-        when(m.getValue()).thenReturn(LOADED_CLASSES);
-        when(monitoredVm.findByName("java.cls.loadedClasses")).thenReturn(m);
-        when(vmEvent.getMonitoredVm()).thenReturn(monitoredVm);
-
-        l.monitorsUpdated(vmEvent);
-        l.monitorsUpdated(vmEvent);
-
-        // This checks a bug where the Category threw an IllegalStateException because the DAO
-        // created a new one on each call, thus violating the unique guarantee of Category.
-    }
-}
--- a/agent/core/src/test/java/com/redhat/thermostat/backend/system/LsbReleaseTest.java	Fri Oct 19 15:33:45 2012 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,63 +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 java.io.BufferedReader;
-import java.io.IOException;
-import java.io.StringReader;
-
-import org.junit.Test;
-
-public class LsbReleaseTest {
-
-    @Test
-    public void testName() throws IOException, InterruptedException {
-        BufferedReader reader = new BufferedReader(new StringReader("Distributor ID: Name"));
-        DistributionInformation info = new LsbRelease().getFromLsbRelease(reader);
-        assertEquals("Name", info.getName());
-    }
-
-    @Test
-    public void testVersion() throws IOException {
-        BufferedReader reader = new BufferedReader(new StringReader("Release: Version"));
-        DistributionInformation info = new LsbRelease().getFromLsbRelease(reader);
-        assertEquals("Version", info.getVersion());
-    }
-
-}
--- a/agent/core/src/test/java/com/redhat/thermostat/backend/system/MemoryStatBuilderTest.java	Fri Oct 19 15:33:45 2012 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,111 +0,0 @@
-/*
- * Copyright 2012 Red Hat, Inc.
- *
- * This file is part of Thermostat.
- *
- * Thermostat is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published
- * by the Free Software Foundation; either version 2, or (at your
- * option) any later version.
- *
- * Thermostat is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Thermostat; see the file COPYING.  If not see
- * <http://www.gnu.org/licenses/>.
- *
- * Linking this code with other modules is making a combined work
- * based on this code.  Thus, the terms and conditions of the GNU
- * General Public License cover the whole combination.
- *
- * As a special exception, the copyright holders of this code give
- * you permission to link this code with independent modules to
- * produce an executable, regardless of the license terms of these
- * independent modules, and to copy and distribute the resulting
- * executable under terms of your choice, provided that you also
- * meet, for each linked independent module, the terms and conditions
- * of the license of that module.  An independent module is a module
- * which is not derived from or based on this code.  If you modify
- * this code, you may extend this exception to your version of the
- * library, but you are not obligated to do so.  If you do not wish
- * to do so, delete this exception statement from your version.
- */
-
-package com.redhat.thermostat.backend.system;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import java.io.IOException;
-import java.io.StringReader;
-
-import org.junit.Test;
-
-import com.redhat.thermostat.common.Constants;
-import com.redhat.thermostat.common.model.MemoryStat;
-
-public class MemoryStatBuilderTest {
-
-    @Test
-    public void testSimpleBuild() {
-        MemoryStat stat = new MemoryStatBuilder(new ProcDataSource()).build();
-        assertNotNull(stat);
-    }
-
-    @Test
-    public void testEmptyBuild() throws IOException {
-        String memory = "";
-        StringReader memoryReader = new StringReader(memory);
-        ProcDataSource dataSource = mock(ProcDataSource.class);
-        when(dataSource.getMemInfoReader()).thenReturn(memoryReader);
-
-        MemoryStat stat = new MemoryStatBuilder(dataSource).build();
-        assertNotNull(stat);
-        verify(dataSource).getMemInfoReader();
-    }
-
-    @Test
-    public void testBuild() throws IOException {
-        int i = 1;
-        final long TOTAL = i++;
-        final long FREE = i++;
-        final long BUFFERS = i++;
-        final long CACHED = i++;
-        final long COMMIT_LIMIT = i++;
-        final long SWAP_TOTAL = i++;
-        final long SWAP_FREE = i++;
-
-        String memory = "" +
-                "MemTotal: " + TOTAL + " kB\n" +
-                "MemFree:  " + FREE + " kB\n" +
-                "Buffers:" + BUFFERS + " kB\n" +
-                "Cached: " + CACHED + " kB\n" +
-                "CommitLimit: " + COMMIT_LIMIT + " kB\n" +
-                "SwapTotal: " + SWAP_TOTAL + " kB\n" +
-                "SwapFree: " + SWAP_FREE + " kB\n";
-
-        StringReader memoryReader = new StringReader(memory);
-        ProcDataSource dataSource = mock(ProcDataSource.class);
-        when(dataSource.getMemInfoReader()).thenReturn(memoryReader);
-
-        MemoryStat stat = new MemoryStatBuilder(dataSource).build();
-
-        assertEquals(BUFFERS * Constants.KILOBYTES_TO_BYTES, stat.getBuffers());
-        assertEquals(CACHED * Constants.KILOBYTES_TO_BYTES, stat.getCached());
-        assertEquals(COMMIT_LIMIT * Constants.KILOBYTES_TO_BYTES, stat.getCommitLimit());
-        assertEquals(FREE * Constants.KILOBYTES_TO_BYTES, stat.getFree());
-        assertEquals(SWAP_FREE * Constants.KILOBYTES_TO_BYTES, stat.getSwapFree());
-        assertEquals(SWAP_TOTAL * Constants.KILOBYTES_TO_BYTES, stat.getSwapTotal());
-        assertEquals(TOTAL * Constants.KILOBYTES_TO_BYTES, stat.getTotal());
-        assertTrue(stat.getTimeStamp() != 0 && stat.getTimeStamp() != Long.MIN_VALUE);
-        assertTrue(stat.getTimeStamp() <= System.currentTimeMillis());
-        verify(dataSource).getMemInfoReader();
-    }
-}
--- a/agent/core/src/test/java/com/redhat/thermostat/backend/system/NetworkInfoBuilderTest.java	Fri Oct 19 15:33:45 2012 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,67 +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.assertNotNull;
-import static org.junit.Assert.assertTrue;
-
-import java.util.List;
-
-import org.junit.Test;
-
-import com.redhat.thermostat.common.model.NetworkInterfaceInfo;
-
-public class NetworkInfoBuilderTest {
-
-    @Test
-    public void testBuilder() {
-
-        List<NetworkInterfaceInfo> info = NetworkInfoBuilder.build();
-        assertNotNull(info);
-        for (NetworkInterfaceInfo iface: info) {
-            assertNotNull(iface);
-            assertNotNull(iface.getInterfaceName());
-            if (iface.getIp4Addr() != null) {
-                // ipv4 address matches the form XX.XX.XX.XX
-                assertTrue(iface.getIp4Addr().matches("\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}"));
-            }
-            // TODO check for sane ipv6 address
-        }
-
-    }
-
-}
--- a/agent/core/src/test/java/com/redhat/thermostat/backend/system/ProcDataSourceTest.java	Fri Oct 19 15:33:45 2012 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,83 +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.assertNotNull;
-
-import java.io.IOException;
-import java.io.Reader;
-
-import org.junit.Test;
-
-import com.redhat.thermostat.agent.TestUtils;
-
-public class ProcDataSourceTest {
-
-    @Test
-    public void testGetCpuInfoReader() throws IOException {
-        Reader r = new ProcDataSource().getCpuInfoReader();
-        assertNotNull(r);
-    }
-
-    @Test
-    public void testGetCpuLoadReader() throws IOException {
-        Reader r = new ProcDataSource().getCpuLoadReader();
-        assertNotNull(r);
-    }
-
-    @Test
-    public void testGetMemInfoReader() throws IOException {
-        Reader r = new ProcDataSource().getMemInfoReader();
-        assertNotNull(r);
-    }
-
-    @Test
-    public void testGetStatReader() throws IOException {
-        int pid = TestUtils.getProcessId();
-        Reader r = new ProcDataSource().getStatReader(pid);
-        assertNotNull(r);
-    }
-
-
-    @Test
-    public void testGetEnvironReader() throws IOException {
-        int pid = TestUtils.getProcessId();
-        Reader r = new ProcDataSource().getEnvironReader(pid);
-        assertNotNull(r);
-    }
-
-}
--- a/agent/core/src/test/java/com/redhat/thermostat/backend/system/ProcessEnvironmentBuilderTest.java	Fri Oct 19 15:33:45 2012 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,133 +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.Mockito.any;
-import static org.mockito.Mockito.eq;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import java.io.ByteArrayInputStream;
-import java.io.IOException;
-import java.io.InputStreamReader;
-import java.io.Reader;
-import java.util.Map;
-import java.util.Random;
-
-import org.junit.Test;
-
-import com.redhat.thermostat.agent.TestUtils;
-
-public class ProcessEnvironmentBuilderTest {
-
-    private final Random r = new Random();
-
-    @Test
-    public void testBasicBuild() {
-        ProcDataSource dataSource = new ProcDataSource();
-        Map<String, String> result = new ProcessEnvironmentBuilder(dataSource).build(TestUtils.getProcessId());
-        assertNotNull(result);
-        assertFalse(result.isEmpty());
-        assertTrue(result.containsKey("USER"));
-    }
-
-    @Test
-    public void testCustomEnvironment() throws IOException {
-        byte[] data = ("USER=test\000HOME=house\000").getBytes();
-
-        Reader r = new InputStreamReader(new ByteArrayInputStream(data));
-        ProcDataSource dataSource = mock(ProcDataSource.class);
-        when(dataSource.getEnvironReader(any(Integer.class))).thenReturn(r);
-
-        Map<String, String> result = new ProcessEnvironmentBuilder(dataSource).build(0);
-
-        verify(dataSource).getEnvironReader(eq(0));
-        assertEquals("test", result.get("USER"));
-        assertEquals("house", result.get("HOME"));
-    }
-
-    @Test
-    public void testLargeRandomEnvironment() throws IOException {
-        int TEST_ENV_SIZE = 1024 * 1024;
-        byte[] data = new byte[TEST_ENV_SIZE];
-        int currentPosition = 0;
-        do {
-            byte[] key = generateRandomBytes();
-            byte[] value = generateRandomBytes();
-            if (currentPosition + key.length + value.length + 2 >= data.length) {
-                break;
-            }
-            System.arraycopy(key, 0, data, currentPosition, key.length);
-            currentPosition += key.length;
-            data[currentPosition] = (byte) '=';
-            currentPosition++;
-            System.arraycopy(value, 0, data, currentPosition, value.length);
-            currentPosition += value.length;
-            data[currentPosition] = 0x00;
-            currentPosition++;
-        } while (true);
-        Reader r = new InputStreamReader(new ByteArrayInputStream(data, 0, currentPosition));
-        ProcDataSource dataSource = mock(ProcDataSource.class);
-        when(dataSource.getEnvironReader(any(Integer.class))).thenReturn(r);
-
-        Map<String, String> result = new ProcessEnvironmentBuilder(dataSource).build(0);
-
-        verify(dataSource).getEnvironReader(eq(0));
-        assertNotNull(result);
-    }
-
-    private byte[] generateRandomBytes() {
-        byte start = (byte) 'a';
-        byte end = (byte) 'z' + 1;
-
-        byte[] alphabet = new byte[end - start];
-        for (int i = 0; i < (end-start); i++) {
-            alphabet[i] = (byte) (i + start);
-        }
-        int size = r.nextInt(15) + 10;
-        byte[] result = new byte[size];
-        for (int i = 0; i < result.length; i++) {
-            result[i] = alphabet[r.nextInt(alphabet.length)];
-        }
-        return result;
-    }
-}
--- a/agent/core/src/test/java/com/redhat/thermostat/backend/system/ProcessStatusInfoBuilderTest.java	Fri Oct 19 15:33:45 2012 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,140 +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;
-
-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/agent/core/src/test/java/com/redhat/thermostat/backend/system/SysConfTest.java	Fri Oct 19 15:33:45 2012 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,50 +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.assertTrue;
-
-import org.junit.Test;
-
-public class SysConfTest {
-
-    @Test
-    public void test() {
-        long ticksPerSecond = SysConf.getClockTicksPerSecond();
-        assertTrue(ticksPerSecond >= 1);
-    }
-}
--- a/agent/core/src/test/java/com/redhat/thermostat/backend/system/SystemBackendTest.java	Fri Oct 19 15:33:45 2012 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,101 +0,0 @@
-/*
- * Copyright 2012 Red Hat, Inc.
- *
- * This file is part of Thermostat.
- *
- * Thermostat is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published
- * by the Free Software Foundation; either version 2, or (at your
- * option) any later version.
- *
- * Thermostat is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Thermostat; see the file COPYING.  If not see
- * <http://www.gnu.org/licenses/>.
- *
- * Linking this code with other modules is making a combined work
- * based on this code.  Thus, the terms and conditions of the GNU
- * General Public License cover the whole combination.
- *
- * As a special exception, the copyright holders of this code give
- * you permission to link this code with independent modules to
- * produce an executable, regardless of the license terms of these
- * independent modules, and to copy and distribute the resulting
- * executable under terms of your choice, provided that you also
- * meet, for each linked independent module, the terms and conditions
- * of the license of that module.  An independent module is a module
- * which is not derived from or based on this code.  If you modify
- * this code, you may extend this exception to your version of the
- * library, but you are not obligated to do so.  If you do not wish
- * to do so, delete this exception statement from your version.
- */
-
-package com.redhat.thermostat.backend.system;
-
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-import org.junit.Before;
-import org.junit.Test;
-
-import com.redhat.thermostat.common.dao.CpuStatDAO;
-import com.redhat.thermostat.common.dao.DAOFactory;
-import com.redhat.thermostat.common.dao.HostInfoDAO;
-import com.redhat.thermostat.common.dao.MemoryStatDAO;
-import com.redhat.thermostat.common.dao.NetworkInterfaceInfoDAO;
-import com.redhat.thermostat.common.dao.VmCpuStatDAO;
-import com.redhat.thermostat.common.storage.Storage;
-
-public class SystemBackendTest {
-
-    private SystemBackend b;
-
-    @Before
-    public void setUp() {
-        Storage s = mock(Storage.class);
-        CpuStatDAO cDAO = mock(CpuStatDAO.class);
-        HostInfoDAO hDAO = mock(HostInfoDAO.class);
-        MemoryStatDAO mDAO = mock(MemoryStatDAO.class);
-        VmCpuStatDAO vDAO = mock(VmCpuStatDAO.class);
-        NetworkInterfaceInfoDAO nDAO = mock(NetworkInterfaceInfoDAO.class);
-        DAOFactory df = mock(DAOFactory.class);
-        when(df.getStorage()).thenReturn(s);
-        when(df.getCpuStatDAO()).thenReturn(cDAO);
-        when(df.getHostInfoDAO()).thenReturn(hDAO);
-        when(df.getMemoryStatDAO()).thenReturn(mDAO);
-        when(df.getVmCpuStatDAO()).thenReturn(vDAO);
-        when(df.getNetworkInterfaceInfoDAO()).thenReturn(nDAO);
-        b = new SystemBackend();
-        b.setDAOFactory(df);
-    }
-
-    @Test
-    public void testBasicBackend() {
-        assertFalse(b.isActive());
-        b.activate();
-        assertTrue(b.isActive());
-        b.deactivate();
-        assertFalse(b.isActive());
-    }
-
-    @Test
-    public void testActivateTwice() {
-        b.activate();
-        b.activate();
-        assert(b.isActive());
-    }
-
-    @Test
-    public void testDeactiateWhenNotActive() {
-        b.deactivate();
-        b.deactivate();
-        assertFalse(b.isActive());
-    }
-
-}
--- a/agent/core/src/test/java/com/redhat/thermostat/backend/system/VmCpuStatBuilderTest.java	Fri Oct 19 15:33:45 2012 -0400
+++ /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.common.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/agent/core/src/test/java/com/redhat/thermostat/utils/ProcDataSourceTest.java	Fri Oct 19 21:58:18 2012 +0200
@@ -0,0 +1,84 @@
+/*
+ * 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.utils;
+
+import static org.junit.Assert.assertNotNull;
+
+import java.io.IOException;
+import java.io.Reader;
+
+import org.junit.Test;
+
+import com.redhat.thermostat.common.TestUtils;
+import com.redhat.thermostat.utils.ProcDataSource;
+
+public class ProcDataSourceTest {
+
+    @Test
+    public void testGetCpuInfoReader() throws IOException {
+        Reader r = new ProcDataSource().getCpuInfoReader();
+        assertNotNull(r);
+    }
+
+    @Test
+    public void testGetCpuLoadReader() throws IOException {
+        Reader r = new ProcDataSource().getCpuLoadReader();
+        assertNotNull(r);
+    }
+
+    @Test
+    public void testGetMemInfoReader() throws IOException {
+        Reader r = new ProcDataSource().getMemInfoReader();
+        assertNotNull(r);
+    }
+
+    @Test
+    public void testGetStatReader() throws IOException {
+        int pid = TestUtils.getProcessId();
+        Reader r = new ProcDataSource().getStatReader(pid);
+        assertNotNull(r);
+    }
+
+
+    @Test
+    public void testGetEnvironReader() throws IOException {
+        int pid = TestUtils.getProcessId();
+        Reader r = new ProcDataSource().getEnvironReader(pid);
+        assertNotNull(r);
+    }
+
+}
--- a/client/core/src/main/java/com/redhat/thermostat/client/internal/HostFilterRegistry.java	Fri Oct 19 15:33:45 2012 -0400
+++ b/client/core/src/main/java/com/redhat/thermostat/client/internal/HostFilterRegistry.java	Fri Oct 19 21:58:18 2012 +0200
@@ -41,6 +41,7 @@
 import org.osgi.framework.InvalidSyntaxException;
 
 import com.redhat.thermostat.client.core.HostFilter;
+import com.redhat.thermostat.common.ThermostatExtensionRegistry;
 
 class HostFilterRegistry extends ThermostatExtensionRegistry<HostFilter> {
 
--- a/client/core/src/main/java/com/redhat/thermostat/client/internal/HostTreeDecoratorRegistry.java	Fri Oct 19 15:33:45 2012 -0400
+++ b/client/core/src/main/java/com/redhat/thermostat/client/internal/HostTreeDecoratorRegistry.java	Fri Oct 19 21:58:18 2012 +0200
@@ -41,6 +41,7 @@
 import org.osgi.framework.InvalidSyntaxException;
 
 import com.redhat.thermostat.client.osgi.service.HostDecorator;
+import com.redhat.thermostat.common.ThermostatExtensionRegistry;
 
 class HostTreeDecoratorRegistry extends ThermostatExtensionRegistry<HostDecorator> {
 
--- a/client/core/src/main/java/com/redhat/thermostat/client/internal/MainWindowControllerImpl.java	Fri Oct 19 15:33:45 2012 -0400
+++ b/client/core/src/main/java/com/redhat/thermostat/client/internal/MainWindowControllerImpl.java	Fri Oct 19 21:58:18 2012 +0200
@@ -74,6 +74,7 @@
 import com.redhat.thermostat.common.ApplicationInfo;
 import com.redhat.thermostat.common.DefaultHostsVMsLoader;
 import com.redhat.thermostat.common.HostsVMsLoader;
+import com.redhat.thermostat.common.ThermostatExtensionRegistry;
 import com.redhat.thermostat.common.Timer;
 import com.redhat.thermostat.common.Timer.SchedulingType;
 import com.redhat.thermostat.common.appctx.ApplicationContext;
@@ -457,7 +458,7 @@
 
         @SuppressWarnings("unchecked")
         @Override
-        public void actionPerformed(ActionEvent<com.redhat.thermostat.client.internal.ThermostatExtensionRegistry.Action> actionEvent) {
+        public void actionPerformed(ActionEvent<com.redhat.thermostat.common.ThermostatExtensionRegistry.Action> actionEvent) {
 
             Object payload = actionEvent.getPayload();
             if (!extensionClass.isInstance(payload)) {
--- a/client/core/src/main/java/com/redhat/thermostat/client/internal/MenuRegistry.java	Fri Oct 19 15:33:45 2012 -0400
+++ b/client/core/src/main/java/com/redhat/thermostat/client/internal/MenuRegistry.java	Fri Oct 19 21:58:18 2012 +0200
@@ -40,6 +40,7 @@
 import org.osgi.framework.InvalidSyntaxException;
 
 import com.redhat.thermostat.client.osgi.service.MenuAction;
+import com.redhat.thermostat.common.ThermostatExtensionRegistry;
 
 public class MenuRegistry extends ThermostatExtensionRegistry<MenuAction> {
 
--- a/client/core/src/main/java/com/redhat/thermostat/client/internal/ThermostatExtensionRegistry.java	Fri Oct 19 15:33:45 2012 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,102 +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.client.internal;
-
-import org.osgi.framework.BundleContext;
-import org.osgi.framework.FrameworkUtil;
-import org.osgi.framework.InvalidSyntaxException;
-import org.osgi.framework.ServiceReference;
-import org.osgi.util.tracker.ServiceTracker;
-
-import com.redhat.thermostat.common.ActionListener;
-import com.redhat.thermostat.common.ActionNotifier;
-
-/**
- * A helper class to make it easier to implement registeries for osgi-services.
- */
-public class ThermostatExtensionRegistry<E> {
-
-    public enum Action {
-        SERVICE_ADDED,
-        SERVICE_REMOVED
-    }
-    
-    private ActionNotifier<Action> actionNotifier = new ActionNotifier<>(this);
-    
-    private ServiceTracker tracker;
-    
-    public ThermostatExtensionRegistry(BundleContext context, String filter, final Class<E> classType) throws InvalidSyntaxException {
-                
-        tracker = new ServiceTracker(context, FrameworkUtil.createFilter(filter), null) {
-            
-            @Override
-            public Object addingService(ServiceReference reference) {
-                @SuppressWarnings("unchecked")
-                E service = (E) super.addingService(reference);
-                
-                actionNotifier.fireAction(Action.SERVICE_ADDED, service);
-                return service;
-            }
-            
-            @Override
-            public void removedService(ServiceReference reference, Object service) {
-                if (!classType.isAssignableFrom(service.getClass())) {
-                    throw new AssertionError("removing a non-Filter service");
-                }
-                
-                actionNotifier.fireAction(Action.SERVICE_REMOVED, service);
-                super.removedService(reference, service);
-            }
-        };
-    }
-    
-    public void start() {
-        tracker.open();
-    }
-
-    public void stop() {
-        tracker.close();
-    }
-    
-    public void addActionListener(ActionListener<Action> l) {
-        actionNotifier.addActionListener(l);
-    }
-
-    public void removeActionListener(ActionListener<Action> l) {
-        actionNotifier.removeActionListener(l);
-    }
-}
--- a/client/core/src/main/java/com/redhat/thermostat/client/internal/VMInformationRegistry.java	Fri Oct 19 15:33:45 2012 -0400
+++ b/client/core/src/main/java/com/redhat/thermostat/client/internal/VMInformationRegistry.java	Fri Oct 19 21:58:18 2012 +0200
@@ -41,6 +41,7 @@
 import org.osgi.framework.InvalidSyntaxException;
 
 import com.redhat.thermostat.client.core.VmInformationService;
+import com.redhat.thermostat.common.ThermostatExtensionRegistry;
 
 class VMInformationRegistry extends ThermostatExtensionRegistry<VmInformationService> {
 
--- a/client/core/src/main/java/com/redhat/thermostat/client/internal/VMTreeDecoratorRegistry.java	Fri Oct 19 15:33:45 2012 -0400
+++ b/client/core/src/main/java/com/redhat/thermostat/client/internal/VMTreeDecoratorRegistry.java	Fri Oct 19 21:58:18 2012 +0200
@@ -41,6 +41,7 @@
 import org.osgi.framework.InvalidSyntaxException;
 
 import com.redhat.thermostat.client.osgi.service.VmDecorator;
+import com.redhat.thermostat.common.ThermostatExtensionRegistry;
 
 class VMTreeDecoratorRegistry extends ThermostatExtensionRegistry<VmDecorator> {
 
--- a/client/core/src/main/java/com/redhat/thermostat/client/internal/VmFilterRegistry.java	Fri Oct 19 15:33:45 2012 -0400
+++ b/client/core/src/main/java/com/redhat/thermostat/client/internal/VmFilterRegistry.java	Fri Oct 19 21:58:18 2012 +0200
@@ -41,6 +41,7 @@
 import org.osgi.framework.InvalidSyntaxException;
 
 import com.redhat.thermostat.client.core.VmFilter;
+import com.redhat.thermostat.common.ThermostatExtensionRegistry;
 
 class VmFilterRegistry extends ThermostatExtensionRegistry<VmFilter> {
 
--- a/client/core/src/test/java/com/redhat/thermostat/client/internal/MainWindowControllerImplTest.java	Fri Oct 19 15:33:45 2012 -0400
+++ b/client/core/src/test/java/com/redhat/thermostat/client/internal/MainWindowControllerImplTest.java	Fri Oct 19 21:58:18 2012 +0200
@@ -66,7 +66,6 @@
 
 import com.redhat.thermostat.client.core.VmFilter;
 import com.redhat.thermostat.client.core.views.BasicView;
-import com.redhat.thermostat.client.internal.ThermostatExtensionRegistry.Action;
 import com.redhat.thermostat.client.osgi.service.MenuAction;
 import com.redhat.thermostat.client.osgi.service.VMContextAction;
 import com.redhat.thermostat.client.osgi.service.VmDecorator;
@@ -77,7 +76,9 @@
 import com.redhat.thermostat.common.ActionEvent;
 import com.redhat.thermostat.common.ActionListener;
 import com.redhat.thermostat.common.HostsVMsLoader;
+import com.redhat.thermostat.common.ThermostatExtensionRegistry;
 import com.redhat.thermostat.common.Timer;
+import com.redhat.thermostat.common.ThermostatExtensionRegistry.Action;
 import com.redhat.thermostat.common.Timer.SchedulingType;
 import com.redhat.thermostat.common.TimerFactory;
 import com.redhat.thermostat.common.appctx.ApplicationContext;
--- a/client/core/src/test/java/com/redhat/thermostat/client/internal/MenuRegistryTest.java	Fri Oct 19 15:33:45 2012 -0400
+++ b/client/core/src/test/java/com/redhat/thermostat/client/internal/MenuRegistryTest.java	Fri Oct 19 21:58:18 2012 +0200
@@ -55,6 +55,7 @@
 import com.redhat.thermostat.client.osgi.service.MenuAction;
 import com.redhat.thermostat.common.ActionEvent;
 import com.redhat.thermostat.common.ActionListener;
+import com.redhat.thermostat.common.ThermostatExtensionRegistry;
 
 public class MenuRegistryTest {
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/common/core/src/main/java/com/redhat/thermostat/common/TestUtils.java	Fri Oct 19 21:58:18 2012 +0200
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2012 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.common;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.lang.management.ManagementFactory;
+import java.util.Properties;
+import java.util.Random;
+
+public class TestUtils {
+
+    public static int getProcessId() {
+        String name = ManagementFactory.getRuntimeMXBean().getName();
+        String pidPart = name.split("@")[0];
+        return Integer.parseInt(pidPart);
+    }
+
+    public static boolean isLinux() {
+        return (System.getProperty("os.name").toLowerCase().contains("linux"));
+    }
+        
+    public static String setupAgentConfigs() throws IOException {
+        // need to create dummy config files for the tests
+        Random random = new Random();
+
+        String tmpDir = System.getProperty("java.io.tmpdir") + File.separatorChar +
+                Math.abs(random.nextInt()) + File.separatorChar;
+
+        System.setProperty("THERMOSTAT_HOME", tmpDir);
+        File agent = new File(tmpDir, "agent");
+        agent.mkdirs();
+
+        File tmpConfigs = new File(agent, "agent.properties");
+
+        new File(agent, "run").mkdirs();
+        new File(agent, "logs").mkdirs();
+
+        File backends = new File(tmpDir, "backends");
+        File system = new File(backends, "system");
+        system.mkdirs();
+
+        Properties props = new Properties();
+        
+        props.setProperty("SAVE_ON_EXIT", "true");
+        props.setProperty("CONFIG_LISTEN_ADDRESS", "42.42.42.42:42");
+        
+        props.store(new FileOutputStream(tmpConfigs), "thermostat agent test properties");
+        
+        return tmpDir;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/common/core/src/main/java/com/redhat/thermostat/common/ThermostatExtensionRegistry.java	Fri Oct 19 21:58:18 2012 +0200
@@ -0,0 +1,100 @@
+/*
+ * 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;
+
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.FrameworkUtil;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceReference;
+import org.osgi.util.tracker.ServiceTracker;
+
+/**
+ * A helper class to make it easier to implement registeries for osgi-services.
+ */
+public class ThermostatExtensionRegistry<E> {
+
+    public enum Action {
+        SERVICE_ADDED,
+        SERVICE_REMOVED
+    }
+    
+    private ActionNotifier<Action> actionNotifier = new ActionNotifier<>(this);
+    
+    @SuppressWarnings("rawtypes")
+    private ServiceTracker tracker;
+    
+    @SuppressWarnings({ "unchecked", "rawtypes" })
+    public ThermostatExtensionRegistry(BundleContext context, String filter, final Class<E> classType) throws InvalidSyntaxException {
+
+        tracker = new ServiceTracker(context, FrameworkUtil.createFilter(filter), null) {
+            
+            @Override
+            public Object addingService(ServiceReference reference) {
+                E service = (E) super.addingService(reference);
+                
+                actionNotifier.fireAction(Action.SERVICE_ADDED, service);
+                return service;
+            }
+            
+            @Override
+            public void removedService(ServiceReference reference, Object service) {
+                if (!classType.isAssignableFrom(service.getClass())) {
+                    throw new AssertionError("removing a non-Filter service");
+                }
+                
+                actionNotifier.fireAction(Action.SERVICE_REMOVED, service);
+                super.removedService(reference, service);
+            }
+        };
+    }
+    
+    public void start() {
+        tracker.open();
+    }
+
+    public void stop() {
+        tracker.close();
+    }
+    
+    public void addActionListener(ActionListener<Action> l) {
+        actionNotifier.addActionListener(l);
+    }
+
+    public void removeActionListener(ActionListener<Action> l) {
+        actionNotifier.removeActionListener(l);
+    }
+}
--- a/common/core/src/main/java/com/redhat/thermostat/common/dao/Countable.java	Fri Oct 19 15:33:45 2012 -0400
+++ b/common/core/src/main/java/com/redhat/thermostat/common/dao/Countable.java	Fri Oct 19 21:58:18 2012 +0200
@@ -36,7 +36,7 @@
 
 package com.redhat.thermostat.common.dao;
 
-interface Countable {
+public interface Countable {
 
     public long getCount();
 
--- a/common/core/src/test/java/com/redhat/thermostat/common/TestUtils.java	Fri Oct 19 15:33:45 2012 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,100 +0,0 @@
-/*
- * Copyright 2012 Red Hat, Inc.
- *
- * This file is part of Thermostat.
- *
- * Thermostat is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published
- * by the Free Software Foundation; either version 2, or (at your
- * option) any later version.
- *
- * Thermostat is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Thermostat; see the file COPYING.  If not see
- * <http://www.gnu.org/licenses/>.
- *
- * Linking this code with other modules is making a combined work
- * based on this code.  Thus, the terms and conditions of the GNU
- * General Public License cover the whole combination.
- *
- * As a special exception, the copyright holders of this code give
- * you permission to link this code with independent modules to
- * produce an executable, regardless of the license terms of these
- * independent modules, and to copy and distribute the resulting
- * executable under terms of your choice, provided that you also
- * meet, for each linked independent module, the terms and conditions
- * of the license of that module.  An independent module is a module
- * which is not derived from or based on this code.  If you modify
- * this code, you may extend this exception to your version of the
- * library, but you are not obligated to do so.  If you do not wish
- * to do so, delete this exception statement from your version.
- */
-
-package com.redhat.thermostat.common;
-
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.lang.management.ManagementFactory;
-import java.util.Properties;
-import java.util.Random;
-
-public class TestUtils {
-
-    public static int getProcessId() {
-        String name = ManagementFactory.getRuntimeMXBean().getName();
-        String pidPart = name.split("@")[0];
-        return Integer.parseInt(pidPart);
-    }
-
-    public static boolean isLinux() {
-        return (System.getProperty("os.name").toLowerCase().contains("linux"));
-    }
-        
-    public static String setupAgentConfigs() throws IOException {
-        // need to create dummy config files for the tests
-        Random random = new Random();
-
-        String tmpDir = System.getProperty("java.io.tmpdir") + File.separatorChar +
-                Math.abs(random.nextInt()) + File.separatorChar;
-
-        System.setProperty("THERMOSTAT_HOME", tmpDir);
-        File agent = new File(tmpDir, "agent");
-        agent.mkdirs();
-
-        File tmpConfigs = new File(agent, "agent.properties");
-
-        new File(agent, "run").mkdirs();
-        new File(agent, "logs").mkdirs();
-
-        File backends = new File(tmpDir, "backends");
-        File system = new File(backends, "system");
-        system.mkdirs();
-        
-        Properties props = new Properties();            
-
-        props.setProperty("BACKENDS", "system");
-        props.setProperty("LOG_LEVEL", "WARNING");
-
-        props.store(new FileOutputStream(tmpConfigs), "thermostat agent test properties");
-
-        // now write the configs for the backends
-        tmpConfigs = new File(system, "backend.properties");
-        props = new Properties();
-        props.setProperty("BACKEND_CLASS",
-                          "com.redhat.thermostat.backend.system.SystemBackend");
-        props.setProperty("DESCRIPTION",
-                          "fluff backend for tests");
-        props.setProperty("VENDOR",
-                          "Red Hat, Inc.");
-        props.setProperty("VERSION",
-                          "1.0");
-        props.store(new FileOutputStream(tmpConfigs), "thermostat system backend properties");
-        
-        return tmpDir;
-    }
-}
--- a/distribution/config/commands/agent.properties	Fri Oct 19 15:33:45 2012 -0400
+++ b/distribution/config/commands/agent.properties	Fri Oct 19 21:58:18 2012 +0200
@@ -7,7 +7,8 @@
           thermostat-agent-heapdumper-@project.version@.jar, \
           thermostat-killvm-agent-@project.version@.jar, \
           thermostat-thread-collector-@project.version@.jar, \
-          thermostat-thread-harvester-@project.version@.jar
+          thermostat-thread-harvester-@project.version@.jar, \
+          thermostat-system-backend-@project.version@.jar
 
 description = starts and stops the thermostat agent
 
--- a/distribution/pom.xml	Fri Oct 19 15:33:45 2012 -0400
+++ b/distribution/pom.xml	Fri Oct 19 21:58:18 2012 +0200
@@ -385,5 +385,11 @@
         <version>${project.version}</version>
     </dependency>
      
+    <dependency>
+        <groupId>com.redhat.thermostat</groupId>
+        <artifactId>thermostat-system-backend</artifactId>
+        <version>${project.version}</version>
+    </dependency>
+     
   </dependencies>
 </project>
--- a/pom.xml	Fri Oct 19 15:33:45 2012 -0400
+++ b/pom.xml	Fri Oct 19 21:58:18 2012 +0200
@@ -111,6 +111,7 @@
     <module>thread</module>
     <module>killvm</module>
     <module>web</module>
+    <module>system-backend</module>
   </modules>
 
   <build>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/system-backend/pom.xml	Fri Oct 19 21:58:18 2012 +0200
@@ -0,0 +1,124 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+
+ 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.
+
+-->
+<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>
+    <groupId>com.redhat.thermostat</groupId>
+    <artifactId>thermostat</artifactId>
+    <version>0.5.0-SNAPSHOT</version>
+  </parent>
+
+  <artifactId>thermostat-system-backend</artifactId>
+  <packaging>bundle</packaging>
+
+  <name>Thermostat System Backend</name>
+
+  <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>com.redhat.thermostat</groupId>
+      <artifactId>thermostat-common-core</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+       
+    <dependency>
+      <groupId>com.redhat.thermostat</groupId>
+      <artifactId>thermostat-agent-core</artifactId>
+      <version>${project.version}</version>
+    </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>
+
+  </dependencies>
+
+  <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-Activator>com.redhat.thermostat.backend.system.osgi.SystemBackendActivator</Bundle-Activator>
+            <Bundle-SymbolicName>com.redhat.thermostat.backend.system</Bundle-SymbolicName>
+            <Private-Package>
+                com.redhat.thermostat.backend.system,
+                com.redhat.thermostat.backend.system.osgi,
+            </Private-Package>
+            <!-- Do not autogenerate uses clauses in Manifests -->
+            <_nouses>true</_nouses>
+          </instructions>
+        </configuration>
+      </plugin>
+      
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+          <artifactId>maven-surefire-plugin</artifactId>
+          <configuration>
+            <!-- FIXME: the test need to link to agent-core native libraries -->
+            <argLine>-Djava.library.path=${project.build.directory}/../../agent/core/target/</argLine>
+          </configuration>
+      </plugin>      
+      
+    </plugins>
+  </build>
+
+</project>
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/system-backend/src/main/java/com/redhat/thermostat/backend/system/CpuStatBuilder.java	Fri Oct 19 21:58:18 2012 +0200
@@ -0,0 +1,137 @@
+/*
+ * Copyright 2012 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.backend.system;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import com.redhat.thermostat.common.Clock;
+import com.redhat.thermostat.common.model.CpuStat;
+import com.redhat.thermostat.common.utils.LoggingUtils;
+import com.redhat.thermostat.utils.ProcDataSource;
+
+public class CpuStatBuilder {
+
+    private static final Logger logger = LoggingUtils.getLogger(CpuStatBuilder.class);
+
+    private final ProcDataSource dataSource;
+    private final Clock clock;
+    private final long ticksPerSecond;
+
+    private boolean initialized = false;
+
+    private long[] previousCpuTicks;
+    private long previousTime;
+
+    public CpuStatBuilder(Clock clock, ProcDataSource dataSource, long ticksPerSecond) {
+        this.dataSource = dataSource;
+        this.clock = clock;
+        this.ticksPerSecond = ticksPerSecond;
+    }
+
+    public void initialize() {
+        if (initialized) {
+            throw new IllegalStateException("already initialized");
+        }
+
+        previousTime = clock.getMonotonicTimeNanos();
+        previousCpuTicks = getCurrentCpuTicks();
+        initialized = true;
+    }
+
+    public CpuStat build() {
+        if (!initialized) {
+            throw new IllegalStateException("not initialized yet");
+        }
+
+        long currentRealTime = clock.getRealTimeMillis();
+        long currentTime = clock.getMonotonicTimeNanos();
+        long[] currentValues = getCurrentCpuTicks();
+
+        List<Double> cpuUsage = new ArrayList<Double>(currentValues.length);
+
+        double timeDelta = (currentTime - previousTime) * 1E-9;
+        for (int i = 0; i < currentValues.length; i++) {
+            long cpuTicksDelta = currentValues[i] - previousCpuTicks[i];
+            // 100 as in 100 percent.
+            cpuUsage.add(cpuTicksDelta * (100.0 / timeDelta / ticksPerSecond));
+        }
+        previousTime = currentTime;
+        previousCpuTicks = currentValues;
+
+        return new CpuStat(currentRealTime, cpuUsage);
+    }
+
+    private long[] getCurrentCpuTicks() {
+        int maxIndex = 0;
+        long[] values = new long[1];
+        try (BufferedReader reader = new BufferedReader(dataSource.getStatReader())) {
+            String line;
+            while ((line = reader.readLine()) != null) {
+                if (!line.startsWith("cpu")) {
+                    continue;
+                }
+                String[] parts = line.split("\\s");
+                if (!parts[0].matches("cpu\\d+")) {
+                    continue;
+                }
+
+                int cpuIndex = Integer.valueOf(parts[0].substring("cpu".length()));
+                if (cpuIndex > maxIndex) {
+                    long[] newValues = new long[cpuIndex+1];
+                    System.arraycopy(values, 0, newValues, 0, cpuIndex);
+                    values = newValues;
+                    maxIndex = cpuIndex;
+                }
+                values[cpuIndex] = Long.valueOf(parts[1]) + Long.valueOf(parts[2]) + Long.valueOf(parts[3]);
+            }
+        } catch (IOException e) {
+            logger.log(Level.WARNING, "error reading stat file", e);
+        }
+
+        return values;
+    }
+
+    public boolean isInitialized() {
+        return initialized;
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/system-backend/src/main/java/com/redhat/thermostat/backend/system/DistributionInformation.java	Fri Oct 19 21:58:18 2012 +0200
@@ -0,0 +1,90 @@
+/*
+ * 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.IOException;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import com.redhat.thermostat.common.utils.LoggingUtils;
+
+public class DistributionInformation {
+
+    public static final String UNKNOWN_NAME = "Unknown Distribution";
+    public static final String UNKNOWN_VERSION = "Unknown Version";
+
+    private static final Logger logger = LoggingUtils.getLogger(DistributionInformation.class);
+
+    private final String name;
+    private final String version;
+
+    public DistributionInformation(String name, String version) {
+        this.name = name;
+        this.version = version;
+    }
+
+    public static DistributionInformation get() {
+        try {
+            return new EtcOsRelease().getDistributionInformation();
+        } catch (IOException e) {
+            logger.log(Level.WARNING, "unable to use os-release", e);
+        }
+        try {
+            return new LsbRelease().getDistributionInformation();
+        } catch (IOException e) {
+            logger.log(Level.WARNING, "unable to use lsb_release", e);
+        }
+        return new DistributionInformation(UNKNOWN_NAME, UNKNOWN_VERSION);
+    }
+
+    /**
+     * @return the name of the distribution, or {@link #UNKNOWN_NAME} if it can not be
+     * identified
+     */
+    public String getName() {
+        return name;
+    }
+
+    /**
+     * @return the release of the distribution or {@link #UNKNOWN_VERSION} if it can not be
+     * identified
+     */
+    public String getVersion() {
+        return version;
+    }
+
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/system-backend/src/main/java/com/redhat/thermostat/backend/system/DistributionInformationSource.java	Fri Oct 19 21:58:18 2012 +0200
@@ -0,0 +1,45 @@
+/*
+ * 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.IOException;
+
+public interface DistributionInformationSource {
+
+    public DistributionInformation getDistributionInformation() throws IOException;
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/system-backend/src/main/java/com/redhat/thermostat/backend/system/EtcOsRelease.java	Fri Oct 19 21:58:18 2012 +0200
@@ -0,0 +1,109 @@
+/*
+ * 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.FileReader;
+import java.io.IOException;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import com.redhat.thermostat.common.utils.LoggingUtils;
+
+public class EtcOsRelease implements DistributionInformationSource {
+
+    private static final Logger logger = LoggingUtils.getLogger(EtcOsRelease.class);
+
+    private static final String OS_RELEASE = "/etc/os-release";
+
+    @Override
+    public DistributionInformation getDistributionInformation() throws IOException {
+        return getFromOsRelease();
+    }
+
+    public DistributionInformation getFromOsRelease() throws IOException {
+        return getFromOsRelease(OS_RELEASE);
+    }
+
+    public DistributionInformation getFromOsRelease(String releaseFile) throws IOException {
+        BufferedReader reader = null;
+        try {
+            reader = new BufferedReader(new FileReader(releaseFile));
+            return getFromOsRelease(reader);
+        } finally {
+            try {
+                if (reader != null) {
+                    reader.close();
+                }
+            } catch (IOException e) {
+                logger.log(Level.WARNING, "unable to close input stream", e);
+            }
+        }
+    }
+
+    public DistributionInformation getFromOsRelease(BufferedReader reader) throws IOException {
+        String name = "Linux";
+        String version = DistributionInformation.UNKNOWN_VERSION;
+        String line = null;
+        while ((line = reader.readLine()) != null) {
+            if (line.matches("^NAME *=.*")) {
+                name = readShellVariable(line);
+            }
+            if (line.matches("^VERSION *=.*")) {
+                version = readShellVariable(line);
+            }
+        }
+        return new DistributionInformation(name, version);
+    }
+
+    /** Reads and parses a shell variable declaration: {@code FOO="bar"}
+     *
+     * @return the value of the shell variable
+     */
+    private String readShellVariable(String line) {
+        // TODO we should try to handle shell quotes better
+        String result = line.substring(line.indexOf("=")+1);
+        result = result.trim();
+        if (result.startsWith("\"") && result.endsWith("\"")) {
+            result = result.substring(1, result.length()-1);
+            result = result.trim();
+        }
+        return result;
+    }
+
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/system-backend/src/main/java/com/redhat/thermostat/backend/system/HostInfoBuilder.java	Fri Oct 19 21:58:18 2012 +0200
@@ -0,0 +1,184 @@
+/*
+ * 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.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import com.redhat.thermostat.common.Constants;
+import com.redhat.thermostat.common.model.HostInfo;
+import com.redhat.thermostat.common.utils.LoggingUtils;
+import com.redhat.thermostat.utils.ProcDataSource;
+import com.redhat.thermostat.utils.hostname.HostName;
+
+public class HostInfoBuilder {
+
+    private static final Logger logger = LoggingUtils.getLogger(HostInfoBuilder.class);
+
+    static class HostCpuInfo {
+        public final String model;
+        public final int count;
+
+        public HostCpuInfo(String model, int count) {
+            this.count = count;
+            this.model = model;
+        }
+    }
+
+    static class HostOsInfo {
+        public final String kernel;
+        public final String distribution;
+
+        public HostOsInfo(String kernel, String distribution) {
+            this.kernel = kernel;
+            this.distribution = distribution;
+        }
+    }
+
+    static class HostMemoryInfo {
+        public final long totalMemory;
+
+        public HostMemoryInfo(long totalMemory) {
+            this.totalMemory = totalMemory;
+        }
+    }
+
+    private final ProcDataSource dataSource;
+
+    public HostInfoBuilder(ProcDataSource dataSource) {
+        this.dataSource = dataSource;
+    }
+
+    public HostInfo build() {
+        String hostname = getHostName();
+        HostCpuInfo cpuInfo = getCpuInfo();
+        HostMemoryInfo memoryInfo = getMemoryInfo();
+        HostOsInfo osInfo = getOsInfo();
+
+        return new HostInfo(hostname, osInfo.distribution, osInfo.kernel, cpuInfo.model, cpuInfo.count, memoryInfo.totalMemory);
+    }
+
+    HostCpuInfo getCpuInfo() {
+        final String KEY_PROCESSOR_ID = "processor";
+        final String KEY_CPU_MODEL = "model name";
+        int cpuCount = 0;
+        String cpuModel = null;
+        try (BufferedReader bufferedReader = new BufferedReader(dataSource.getCpuInfoReader())) {
+            String line = null;
+            while ((line = bufferedReader.readLine()) != null) {
+                if (line.startsWith(KEY_PROCESSOR_ID)) {
+                    cpuCount++;
+                } else if (line.startsWith(KEY_CPU_MODEL)) {
+                    cpuModel = line.substring(line.indexOf(":") + 1).trim();
+                }
+            }
+        } catch (IOException ioe) {
+            logger.log(Level.WARNING, "unable to read cpu info");
+        }
+
+        logger.log(Level.FINEST, "cpuModel: " + cpuModel);
+        logger.log(Level.FINEST, "cpuCount: " + cpuCount);
+
+        return new HostCpuInfo(cpuModel, cpuCount);
+    }
+
+    HostMemoryInfo getMemoryInfo() {
+        long totalMemory = -1;
+        try (BufferedReader bufferedReader = new BufferedReader(dataSource.getMemInfoReader())) {
+            String[] memTotalParts = bufferedReader.readLine().split(" +");
+            long data = Long.valueOf(memTotalParts[1]);
+            String units = memTotalParts[2];
+            if (units.equals("kB")) {
+                totalMemory = data * Constants.KILOBYTES_TO_BYTES;
+            }
+        } catch (IOException ioe) {
+            logger.log(Level.WARNING, "unable to read memory info");
+        }
+
+        logger.log(Level.FINEST, "totalMemory: " + totalMemory + " bytes");
+        return new HostMemoryInfo(totalMemory);
+    }
+
+    HostOsInfo getOsInfo() {
+        return getOsInfo(DistributionInformation.get());
+    }
+
+    HostOsInfo getOsInfo(DistributionInformation distroInfo) {
+        String osName = distroInfo.getName() + " " + distroInfo.getVersion();
+        logger.log(Level.FINEST, "osName: " + osName);
+
+        String osKernel = System.getProperty("os.name") + " " + System.getProperty("os.version");
+        logger.log(Level.FINEST, "osKernel: " + osKernel);
+
+        return new HostOsInfo(osKernel, osName);
+    }
+
+    String getHostName() {
+        String hostname = null;
+        
+        try {
+            InetAddress localAddress = null;
+            localAddress = InetAddress.getLocalHost();
+            hostname = getHostName(localAddress);
+        } catch (UnknownHostException uhe) {
+            logger.log(Level.WARNING, "unable to get hostname", uhe);
+        }
+        
+        // if fails, try to get hostname without dns lookup
+        if (hostname == null) {
+            hostname = HostName.getLocalHostName();
+        }
+        
+        // still null, use localhost
+        if (hostname == null) {
+            hostname = Constants.AGENT_LOCAL_HOSTNAME;
+        }
+        
+        return hostname;
+    }
+
+    String getHostName(InetAddress localAddress) {
+        String hostname = localAddress.getCanonicalHostName();
+        logger.log(Level.FINEST, "hostname: " + hostname);
+        return hostname;
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/system-backend/src/main/java/com/redhat/thermostat/backend/system/JvmStatDataExtractor.java	Fri Oct 19 21:58:18 2012 +0200
@@ -0,0 +1,170 @@
+/*
+ * 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 com.redhat.thermostat.common.model.VmMemoryStat.Generation;
+
+import sun.jvmstat.monitor.Monitor;
+import sun.jvmstat.monitor.MonitorException;
+import sun.jvmstat.monitor.MonitoredVm;
+import sun.jvmstat.monitor.MonitoredVmUtil;
+
+/**
+ * A helper class to provide type-safe access to commonly used jvmstat monitors
+ * <p>
+ * Implementation details: For local vms, jvmstat uses a ByteBuffer
+ * corresponding to mmap()ed hsperfdata file. The hsperfdata file is updated
+ * asynchronously by the vm that created the file. The polling that jvmstat api
+ * provides is merely an abstraction over this (possibly always up-to-date)
+ * ByteBuffer. So the data this class extracts is as current as possible, and
+ * does not correspond to when the jvmstat update events fired.
+ */
+public class JvmStatDataExtractor {
+
+    /*
+     * Note, there may be a performance issue to consider here. We have a lot of
+     * string constants. When we start adding some of the more heavyweight
+     * features, and running into CPU issues this may need to be reconsidered in
+     * order to avoid the String pool overhead. See also:
+     * http://docs.oracle.com/javase/6/docs/api/java/lang/String.html#intern()
+     */
+
+    private final MonitoredVm vm;
+
+    public JvmStatDataExtractor(MonitoredVm vm) {
+        this.vm = vm;
+    }
+
+    public String getCommandLine() throws MonitorException {
+        return MonitoredVmUtil.commandLine(vm);
+    }
+
+    public String getMainClass() throws MonitorException {
+        return MonitoredVmUtil.mainClass(vm, true);
+    }
+
+    public String getJavaVersion() throws MonitorException {
+        return (String) vm.findByName("java.property.java.version").getValue();
+    }
+
+    public String getJavaHome() throws MonitorException {
+        return (String) vm.findByName("java.property.java.home").getValue();
+    }
+
+    public String getVmName() throws MonitorException {
+        return (String) vm.findByName("java.property.java.vm.name").getValue();
+    }
+
+    public String getVmInfo() throws MonitorException {
+        return (String) vm.findByName("java.property.java.vm.info").getValue();
+    }
+
+    public String getVmVersion() throws MonitorException {
+        return (String) vm.findByName("java.property.java.vm.version").getValue();
+    }
+
+    public String getVmArguments() throws MonitorException {
+        return MonitoredVmUtil.jvmArgs(vm);
+    }
+
+    public long getTotalCollectors() throws MonitorException {
+        return (Long) vm.findByName("sun.gc.policy.collectors").getValue();
+    }
+
+    public String getCollectorName(long collector) throws MonitorException {
+        return (String) vm.findByName("sun.gc.collector." + collector + ".name").getValue();
+    }
+
+    public long getCollectorTime(long collector) throws MonitorException {
+        return (Long) vm.findByName("sun.gc.collector." + collector + ".time").getValue();
+    }
+
+    public long getCollectorInvocations(long collector) throws MonitorException {
+        return (Long) vm.findByName("sun.gc.collector." + collector + ".invocations").getValue();
+    }
+
+    public long getTotalGcGenerations() throws MonitorException {
+        return (Long) vm.findByName("sun.gc.policy.generations").getValue();
+    }
+
+    public String getGenerationName(long generation) throws MonitorException {
+        return (String) vm.findByName("sun.gc.generation." + generation + ".name").getValue();
+    }
+
+    public long getGenerationCapacity(long generation) throws MonitorException {
+        return (Long) vm.findByName("sun.gc.generation." + generation + ".capacity").getValue();
+    }
+
+    public long getGenerationMaxCapacity(long generation) throws MonitorException {
+        return (Long) vm.findByName("sun.gc.generation." + generation + ".maxCapacity").getValue();
+    }
+
+    public String getGenerationCollector(long generation) throws MonitorException {
+        // this is just re-implementing getCollectorName()
+        // TODO check generation number and collector number are always associated
+        Monitor m = vm.findByName("sun.gc.collector." + generation + ".name");
+        if (m == null) {
+            return Generation.COLLECTOR_NONE;
+        }
+        return (String) m.getValue();
+    }
+
+    public long getTotalSpaces(long generation) throws MonitorException {
+        return (Long) vm.findByName("sun.gc.generation." + generation + ".spaces").getValue();
+    }
+
+    public String getSpaceName(long generation, long space) throws MonitorException {
+        return (String) vm.findByName("sun.gc.generation." + generation + ".space." + space + ".name").getValue();
+    }
+
+    public long getSpaceCapacity(long generation, long space) throws MonitorException {
+        return (Long) vm.findByName("sun.gc.generation." + generation + ".space." + space + ".capacity").getValue();
+    }
+
+    public long getSpaceMaxCapacity(long generation, long space) throws MonitorException {
+        return (Long) vm.findByName("sun.gc.generation." + generation + ".space." + space + ".maxCapacity").getValue();
+    }
+
+    public long getSpaceUsed(long generation, long space) throws MonitorException {
+        return (Long) vm.findByName("sun.gc.generation." + generation + ".space." + space + ".used").getValue();
+    }
+
+    public long getLoadedClasses() throws MonitorException {
+        return (Long) vm.findByName("java.cls.loadedClasses").getValue();
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/system-backend/src/main/java/com/redhat/thermostat/backend/system/JvmStatHostListener.java	Fri Oct 19 21:58:18 2012 +0200
@@ -0,0 +1,226 @@
+/*
+ * 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.net.URISyntaxException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.concurrent.CopyOnWriteArraySet;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import sun.jvmstat.monitor.MonitorException;
+import sun.jvmstat.monitor.MonitoredHost;
+import sun.jvmstat.monitor.MonitoredVm;
+import sun.jvmstat.monitor.VmIdentifier;
+import sun.jvmstat.monitor.event.HostEvent;
+import sun.jvmstat.monitor.event.HostListener;
+import sun.jvmstat.monitor.event.VmListener;
+import sun.jvmstat.monitor.event.VmStatusChangeEvent;
+
+import com.redhat.thermostat.agent.JvmStatusListener;
+import com.redhat.thermostat.agent.JvmStatusNotifier;
+import com.redhat.thermostat.common.dao.VmClassStatDAO;
+import com.redhat.thermostat.common.dao.VmGcStatDAO;
+import com.redhat.thermostat.common.dao.VmInfoDAO;
+import com.redhat.thermostat.common.dao.VmMemoryStatDAO;
+import com.redhat.thermostat.common.model.VmInfo;
+import com.redhat.thermostat.common.utils.LoggingUtils;
+import com.redhat.thermostat.utils.ProcDataSource;
+
+public class JvmStatHostListener implements HostListener, JvmStatusNotifier {
+
+    private static final Logger logger = LoggingUtils.getLogger(JvmStatHostListener.class);
+
+    private boolean attachNew;
+
+    private final VmInfoDAO vmInfoDAO;
+    private final VmMemoryStatDAO vmMemoryStatDAO;
+    private final VmClassStatDAO vmClassStatDAO;
+    private final VmGcStatDAO vmGcStatDAO;
+
+    private Map<Integer, MonitoredVm> monitoredVms  = new HashMap<>();
+    private Map<MonitoredVm, List<VmListener>> registeredListeners  = new ConcurrentHashMap<>();
+    
+    private Set<JvmStatusListener> statusListeners = new CopyOnWriteArraySet<JvmStatusListener>();
+
+    JvmStatHostListener(VmInfoDAO vmInfoDAO, VmMemoryStatDAO vmMemoryStatDAO, VmGcStatDAO vmGcStatDAO,
+            VmClassStatDAO vmClassStatDAO, boolean attachNew) {
+        this.vmInfoDAO = vmInfoDAO;
+        this.vmMemoryStatDAO = vmMemoryStatDAO;
+        this.vmGcStatDAO = vmGcStatDAO;
+        this.vmClassStatDAO = vmClassStatDAO;
+        this.attachNew = attachNew;        
+    }
+
+    void removeAllListeners() {
+        for (MonitoredVm vm : monitoredVms.values()) {
+            for (VmListener listener : registeredListeners.get(vm)) {
+                try {
+                    if (listener != null) vm.removeVmListener(listener);
+                
+                } catch (MonitorException e) {
+                    logger.log(Level.WARNING, "can't remove vm listener", e);
+                }
+            }
+        }
+    }
+    
+    @Override
+    public void disconnected(HostEvent event) {
+        logger.warning("Disconnected from host");
+    }
+
+    @SuppressWarnings("unchecked") // Unchecked casts to (Set<Integer>).
+    @Override
+    public void vmStatusChanged(VmStatusChangeEvent event) {
+        MonitoredHost host = event.getMonitoredHost();
+
+        for (Integer newVm : (Set<Integer>) event.getStarted()) {
+            try {
+                logger.fine("New vm: " + newVm);
+                sendNewVM(newVm, host);
+            } catch (MonitorException e) {
+                logger.log(Level.WARNING, "error getting info for new vm" + newVm, e);
+            } catch (URISyntaxException e) {
+                logger.log(Level.WARNING, "error getting info for new vm" + newVm, e);
+            }
+        }
+
+        for (Integer stoppedVm : (Set<Integer>) event.getTerminated()) {
+            try {
+                logger.fine("stopped vm: " + stoppedVm);
+                sendStoppedVM(stoppedVm, host);
+            } catch (URISyntaxException e) {
+                logger.log(Level.WARNING, "error getting info for stopped vm" + stoppedVm, e);
+            } catch (MonitorException e) {
+                logger.log(Level.WARNING, "error getting info for stopped vm" + stoppedVm, e);
+            }
+        }
+    }
+
+    private void sendNewVM(Integer vmId, MonitoredHost host)
+            throws MonitorException, URISyntaxException {
+        MonitoredVm vm = host.getMonitoredVm(host.getHostIdentifier().resolve(
+                new VmIdentifier(vmId.toString())));
+        if (vm != null) {
+            VmInfo info = null;
+            try {
+                long startTime = System.currentTimeMillis();
+                long stopTime = Long.MIN_VALUE;
+                JvmStatDataExtractor extractor = new JvmStatDataExtractor(vm);
+                Map<String, String> properties = new HashMap<String, String>();
+                ProcDataSource dataSource = new ProcDataSource();
+                Map<String, String> environment = new ProcessEnvironmentBuilder(dataSource).build(vmId);
+                // TODO actually figure out the loaded libraries.
+                List<String> loadedNativeLibraries = new ArrayList<String>();
+                info = new VmInfo(vmId, startTime, stopTime,
+                        extractor.getJavaVersion(), extractor.getJavaHome(),
+                        extractor.getMainClass(), extractor.getCommandLine(),
+                        extractor.getVmName(), extractor.getVmInfo(), extractor.getVmVersion(), extractor.getVmArguments(),
+                        properties, environment, loadedNativeLibraries);
+                vmInfoDAO.putVmInfo(info);
+                logger.finer("Sent VM_STARTED messsage");
+            } catch (MonitorException me) {
+                logger.log(Level.WARNING, "error getting vm info for " + vmId, me);
+            }
+
+            if (attachNew) {
+                List<VmListener> listeners = registeredListeners.get(vm);
+                if (listeners == null) {
+                    listeners = new CopyOnWriteArrayList<>();
+                }
+                
+                VmListener listener =  new JvmStatVmListener(vmMemoryStatDAO, vmGcStatDAO, vmId);
+                vm.addVmListener(listener);
+                listeners.add(listener);
+                
+                listener = new JvmStatVmClassListener(vmClassStatDAO, vmId);
+                vm.addVmListener(listener);
+                listeners.add(listener);
+                
+                registeredListeners.put(vm, listeners);
+                
+            } else {
+                logger.log(Level.FINE, "skipping new vm " + vmId);
+            }
+            for (JvmStatusListener statusListener : statusListeners) {
+                statusListener.jvmStarted(vmId);
+            }
+
+            monitoredVms.put(vmId, vm);
+        }
+    }
+
+    private void sendStoppedVM(Integer vmId, MonitoredHost host) throws URISyntaxException, MonitorException {
+        
+        VmIdentifier resolvedVmID = host.getHostIdentifier().resolve(new VmIdentifier(vmId.toString()));
+        if (resolvedVmID != null) {
+            long stopTime = System.currentTimeMillis();
+            for (JvmStatusListener statusListener : statusListeners) {
+                statusListener.jvmStopped(vmId);
+            }
+            vmInfoDAO.putVmStoppedTime(vmId, stopTime);
+
+            MonitoredVm vm = monitoredVms.remove(vmId);
+            List<VmListener> listeners = registeredListeners.remove(vm);
+            for (VmListener listener : listeners) {
+                try {
+                    if (listener != null) vm.removeVmListener(listener);
+                } catch (MonitorException e) {
+                    logger.log(Level.WARNING, "can't remove vm listener", e);
+                }
+            }
+            vm.detach();
+        }
+    }
+
+    @Override
+    public void addJvmStatusListener(JvmStatusListener listener) {
+        statusListeners.add(listener);
+    }
+
+    @Override
+    public void removeJvmStatusListener(JvmStatusListener listener) {
+        statusListeners.remove(listener);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/system-backend/src/main/java/com/redhat/thermostat/backend/system/JvmStatVmClassListener.java	Fri Oct 19 21:58:18 2012 +0200
@@ -0,0 +1,90 @@
+/*
+ * Copyright 2012 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.backend.system;
+
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import sun.jvmstat.monitor.MonitorException;
+import sun.jvmstat.monitor.MonitoredVm;
+import sun.jvmstat.monitor.event.MonitorStatusChangeEvent;
+import sun.jvmstat.monitor.event.VmEvent;
+import sun.jvmstat.monitor.event.VmListener;
+
+import com.redhat.thermostat.common.dao.VmClassStatDAO;
+import com.redhat.thermostat.common.model.VmClassStat;
+import com.redhat.thermostat.common.utils.LoggingUtils;
+
+class JvmStatVmClassListener implements VmListener {
+
+    private static final Logger logger = LoggingUtils.getLogger(JvmStatVmClassListener.class);
+
+    private VmClassStatDAO dao;
+    private int vmId;
+
+    JvmStatVmClassListener(VmClassStatDAO dao, int vmId) {
+        this.dao = dao;
+        this.vmId = vmId;
+    }
+
+    @Override
+    public void disconnected(VmEvent vmEvent) {
+        /* nothing to do here */
+    }
+
+    @Override
+    public void monitorStatusChanged(MonitorStatusChangeEvent vmEvent) {
+        /* nothing to do here */
+    }
+
+    @Override
+    public void monitorsUpdated(VmEvent vmEvent) {
+        MonitoredVm vm = vmEvent.getMonitoredVm();
+        try {
+            JvmStatDataExtractor extractor = new JvmStatDataExtractor(vm);
+            long loadedClasses = extractor.getLoadedClasses();
+            long timestamp = System.currentTimeMillis();
+            VmClassStat stat = new VmClassStat(vmId, timestamp, loadedClasses);
+            dao.putVmClassStat(stat);
+        } catch (MonitorException e) {
+            logger.log(Level.WARNING, "error gathering class info for vm " + vmId, e);
+        }
+
+
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/system-backend/src/main/java/com/redhat/thermostat/backend/system/JvmStatVmListener.java	Fri Oct 19 21:58:18 2012 +0200
@@ -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.backend.system;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import sun.jvmstat.monitor.MonitorException;
+import sun.jvmstat.monitor.MonitoredVm;
+import sun.jvmstat.monitor.event.MonitorStatusChangeEvent;
+import sun.jvmstat.monitor.event.VmEvent;
+import sun.jvmstat.monitor.event.VmListener;
+
+import com.redhat.thermostat.common.dao.VmGcStatDAO;
+import com.redhat.thermostat.common.dao.VmMemoryStatDAO;
+import com.redhat.thermostat.common.model.VmGcStat;
+import com.redhat.thermostat.common.model.VmMemoryStat;
+import com.redhat.thermostat.common.model.VmMemoryStat.Generation;
+import com.redhat.thermostat.common.model.VmMemoryStat.Space;
+import com.redhat.thermostat.common.utils.LoggingUtils;
+
+public class JvmStatVmListener implements VmListener {
+
+    private static final Logger logger = LoggingUtils.getLogger(JvmStatVmListener.class);
+
+    private final int vmId;
+    private final VmGcStatDAO gcDAO;
+    private final VmMemoryStatDAO memDAO;
+
+    public JvmStatVmListener(VmMemoryStatDAO vmMemoryStatDao, VmGcStatDAO vmGcStatDao, int vmId) {
+        gcDAO = vmGcStatDao;
+        memDAO = vmMemoryStatDao;
+        this.vmId = vmId;
+    }
+
+    @Override
+    public void disconnected(VmEvent event) {
+        /* nothing to do here */
+    }
+
+    @Override
+    public void monitorStatusChanged(MonitorStatusChangeEvent event) {
+        /* nothing to do here */
+    }
+
+    @Override
+    public void monitorsUpdated(VmEvent event) {
+        MonitoredVm vm = event.getMonitoredVm();
+        if (vm == null) {
+            throw new NullPointerException();
+        }
+        recordMemoryStat(vm);
+        recordGcStat(vm);
+    }
+
+    private void recordGcStat(MonitoredVm vm) {
+        try {
+            JvmStatDataExtractor extractor = new JvmStatDataExtractor(vm);
+            long collectors = extractor.getTotalCollectors();
+            for (int i = 0; i < collectors; i++) {
+                long timestamp = System.currentTimeMillis();
+                VmGcStat stat = new VmGcStat(vmId, timestamp,
+                        extractor.getCollectorName(i),
+                        extractor.getCollectorInvocations(i),
+                        extractor.getCollectorTime(i));
+                gcDAO.putVmGcStat(stat);
+            }
+        } catch (MonitorException e) {
+            logger.log(Level.WARNING, "error gathering gc info for vm " + vmId, e);
+        }
+
+    }
+
+    private void recordMemoryStat(MonitoredVm vm) {
+        try {
+            long timestamp = System.currentTimeMillis();
+            JvmStatDataExtractor extractor = new JvmStatDataExtractor(vm);
+            long maxGenerations = extractor.getTotalGcGenerations();
+            List<Generation> generations = new ArrayList<Generation>();
+            VmMemoryStat stat = new VmMemoryStat(timestamp, vmId, generations);
+            for (long generation = 0; generation < maxGenerations; generation++) {
+                Generation g = new Generation();
+                generations.add(g);
+                g.name = extractor.getGenerationName(generation);
+                g.capacity = extractor.getGenerationCapacity(generation);
+                g.maxCapacity = extractor.getGenerationMaxCapacity(generation);
+                g.collector = extractor.getGenerationCollector(generation);
+                long maxSpaces = extractor.getTotalSpaces(generation);
+                List<Space> spaces = new ArrayList<Space>();
+                g.spaces = spaces;
+                for (long space = 0; space < maxSpaces; space++) {
+                    Space s = new Space();
+                    spaces.add(s);
+                    s.index = (int) space;
+                    s.name = extractor.getSpaceName(generation, space);
+                    s.capacity = extractor.getSpaceCapacity(generation, space);
+                    s.maxCapacity = extractor.getSpaceMaxCapacity(generation, space);
+                    s.used = extractor.getSpaceUsed(generation, space);
+                }
+            }
+            memDAO.putVmMemoryStat(stat);
+        } catch (MonitorException e) {
+            logger.log(Level.WARNING, "error gathering memory info for vm " + vmId, e);
+        }
+    }
+
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/system-backend/src/main/java/com/redhat/thermostat/backend/system/LsbRelease.java	Fri Oct 19 21:58:18 2012 +0200
@@ -0,0 +1,111 @@
+/*
+ * 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.InputStream;
+import java.io.InputStreamReader;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import com.redhat.thermostat.common.utils.LoggingUtils;
+
+public class LsbRelease implements DistributionInformationSource {
+
+    private static final Logger logger = LoggingUtils.getLogger(LsbRelease.class);
+
+    private static final String DISTRIBUTION_NAME = "distributor id";
+    private static final String DISTRIBUTION_VERSION = "release";
+
+    @Override
+    public DistributionInformation getDistributionInformation()
+            throws IOException {
+        return getFromLsbRelease();
+    }
+
+    public DistributionInformation getFromLsbRelease() throws IOException {
+
+        BufferedReader reader = null;
+        try {
+            Process lsbProc = Runtime.getRuntime().exec(new String[] { "lsb_release", "-a" });
+            InputStream progOutput = lsbProc.getInputStream();
+            reader = new BufferedReader(new InputStreamReader(progOutput));
+            DistributionInformation result = getFromLsbRelease(reader);
+            int exitValue = lsbProc.waitFor();
+            if (exitValue != 0) {
+                logger.log(Level.WARNING, "unable to identify distribution, problems running 'lsb_release'");
+            }
+            return result;
+        } catch (InterruptedException e) {
+            throw new IOException(e);
+        } finally {
+            if (reader != null) {
+                try {
+                    reader.close();
+                } catch (IOException e) {
+                    logger.log(Level.WARNING, "unable to close a child's output stream");
+                }
+            }
+        }
+
+    }
+
+    public DistributionInformation getFromLsbRelease(BufferedReader reader) throws IOException {
+        String name = DistributionInformation.UNKNOWN_NAME;
+        String version = DistributionInformation.UNKNOWN_VERSION;
+
+        String line;
+        while ((line = reader.readLine()) != null) {
+            int sepLocation = line.indexOf(":");
+            if (sepLocation != -1) {
+                String key = line.substring(0, sepLocation).toLowerCase();
+                if (key.equals(DISTRIBUTION_NAME)) {
+                    name = line.substring(sepLocation + 1).trim();
+                } else if (key.equals(DISTRIBUTION_VERSION)) {
+                    version = line.substring(sepLocation + 1).trim();
+                }
+            }
+        }
+
+        logger.log(Level.FINE, "distro-name: " + name);
+        logger.log(Level.FINE, "distro-version: " + version);
+
+        return new DistributionInformation(name, version);
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/system-backend/src/main/java/com/redhat/thermostat/backend/system/MemoryStatBuilder.java	Fri Oct 19 21:58:18 2012 +0200
@@ -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.backend.system;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import com.redhat.thermostat.common.Constants;
+import com.redhat.thermostat.common.NotImplementedException;
+import com.redhat.thermostat.common.model.MemoryStat;
+import com.redhat.thermostat.common.utils.LoggingUtils;
+import com.redhat.thermostat.utils.ProcDataSource;
+
+/**
+ * Implementation note: uses information from /proc/
+ */
+public class MemoryStatBuilder {
+
+    private static final long UNAVAILABLE = -1;
+
+    private static final String KEY_MEMORY_TOTAL = "MemTotal";
+    private static final String KEY_MEMORY_FREE = "MemFree";
+    private static final String KEY_BUFFERS = "Buffers";
+    private static final String KEY_CACHED = "Cached";
+    private static final String KEY_SWAP_TOTAL = "SwapTotal";
+    private static final String KEY_SWAP_FREE = "SwapFree";
+    private static final String KEY_COMMIT_LIMIT = "CommitLimit";
+
+    private static final Logger logger = LoggingUtils.getLogger(MemoryStatBuilder.class);
+
+    private final ProcDataSource dataSource;
+
+    public MemoryStatBuilder(ProcDataSource dataSource) {
+        this.dataSource = dataSource;
+    }
+
+    protected MemoryStat build() {
+        long timestamp = System.currentTimeMillis();
+
+        long total = UNAVAILABLE;
+        long free = UNAVAILABLE;
+        long swapTotal = UNAVAILABLE;
+        long swapFree = UNAVAILABLE;
+        long buffers = UNAVAILABLE;
+        long cached = UNAVAILABLE;
+        long commitLimit = UNAVAILABLE;
+
+        try (BufferedReader reader = new BufferedReader(dataSource.getMemInfoReader())) {
+            String line = null;
+            while ((line = reader.readLine()) != null) {
+                String[] parts = line.split(":");
+                if (parts.length == 2) {
+                    String key = parts[0].trim();
+                    long value = getValue(parts[1].trim());
+                    if (key.equals(KEY_MEMORY_TOTAL)) {
+                        total = value;
+                    } else if (key.equals(KEY_MEMORY_FREE)) {
+                        free = value;
+                    } else if (key.equals(KEY_SWAP_TOTAL)) {
+                        swapTotal = value;
+                    } else if (key.equals(KEY_SWAP_FREE)) {
+                        swapFree = value;
+                    } else if (key.equals(KEY_BUFFERS)) {
+                        buffers = value;
+                    } else if (key.equals(KEY_CACHED)) {
+                        cached = value;
+                    } else if (key.equals(KEY_COMMIT_LIMIT)) {
+                        commitLimit = value;
+                    }
+                }
+            }
+        } catch (IOException ioe) {
+            logger.log(Level.WARNING, "unable to read memory info");
+        }
+
+        return new MemoryStat(timestamp, total, free, buffers, cached, swapTotal, swapFree, commitLimit);
+    }
+
+    private long getValue(String rawValue) {
+        String[] parts = rawValue.split(" +");
+        String value = rawValue;
+        String units = null;
+        if (parts.length > 1) {
+            value = parts[0];
+            units = parts[1];
+        }
+
+        long result = UNAVAILABLE;
+        try {
+            result = Long.parseLong(value);
+            if (units != null) {
+                if (units.equals("kB") || units.equals("KB")) {
+                    result = result * Constants.KILOBYTES_TO_BYTES;
+                } else {
+                    throw new NotImplementedException("unit conversion from " + units + " not implemented");
+                }
+            }
+        } catch (NumberFormatException nfe) {
+            logger.log(Level.WARNING, "error extracting memory info");
+        }
+
+        return result;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/system-backend/src/main/java/com/redhat/thermostat/backend/system/NetworkInfoBuilder.java	Fri Oct 19 21:58:18 2012 +0200
@@ -0,0 +1,79 @@
+/*
+ * 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.net.Inet4Address;
+import java.net.Inet6Address;
+import java.net.InetAddress;
+import java.net.NetworkInterface;
+import java.net.SocketException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.List;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import com.redhat.thermostat.common.model.NetworkInterfaceInfo;
+import com.redhat.thermostat.common.utils.LoggingUtils;
+
+public class NetworkInfoBuilder {
+
+    private static final Logger logger = LoggingUtils.getLogger(NetworkInfoBuilder.class);
+
+    public static List<NetworkInterfaceInfo> build() {
+        List<NetworkInterfaceInfo> infos = new ArrayList<NetworkInterfaceInfo>();
+        try {
+            Enumeration<NetworkInterface> ifaces = NetworkInterface.getNetworkInterfaces();
+            for (NetworkInterface iface : Collections.list(ifaces)) {
+                NetworkInterfaceInfo info = new NetworkInterfaceInfo(iface.getName());
+                for (InetAddress addr : Collections.list(iface.getInetAddresses())) {
+                    if (addr instanceof Inet4Address) {
+                        info.setIp4Addr(addr.getHostAddress());
+                    } else if (addr instanceof Inet6Address) {
+                        info.setIp6Addr(addr.getHostAddress());
+                    }
+                }
+                infos.add(info);
+            }
+        } catch (SocketException e) {
+            logger.log(Level.WARNING, "error enumerating network interfaces");
+        }
+        return infos;
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/system-backend/src/main/java/com/redhat/thermostat/backend/system/ProcessEnvironmentBuilder.java	Fri Oct 19 21:58:18 2012 +0200
@@ -0,0 +1,125 @@
+/*
+ * 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.IOException;
+import java.io.Reader;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import com.redhat.thermostat.common.utils.LoggingUtils;
+import com.redhat.thermostat.utils.ProcDataSource;
+
+public class ProcessEnvironmentBuilder {
+
+    private static final Logger logger = LoggingUtils.getLogger(ProcessEnvironmentBuilder.class);
+
+    private final ProcDataSource dataSource;
+
+    public ProcessEnvironmentBuilder(ProcDataSource dataSource) {
+        this.dataSource = dataSource;
+    }
+
+    public Map<String, String> build(int pid) {
+        try (Reader reader = dataSource.getEnvironReader(pid)) {
+            return build(reader);
+        } catch (IOException ioe) {
+            logger.log(Level.WARNING, "error reading env", ioe);
+        }
+
+        return Collections.emptyMap();
+    }
+
+    private Map<String,String> build(Reader reader) throws IOException {
+
+        Map<String, String> env = new HashMap<String, String>();
+
+        char[] fileBuffer = new char[1024];
+        int fileBufferIndex = 0;
+        char[] buffer = new char[1024];
+        int read = 0;
+        while (true) {
+            read = reader.read(buffer);
+            if (read == -1) {
+                break;
+            }
+
+            if (read + fileBufferIndex > fileBuffer.length) {
+                char[] newFileBuffer = new char[fileBuffer.length * 2];
+                System.arraycopy(fileBuffer, 0, newFileBuffer, 0, fileBufferIndex);
+                fileBuffer = newFileBuffer;
+            }
+            System.arraycopy(buffer, 0, fileBuffer, fileBufferIndex, read);
+            fileBufferIndex = fileBufferIndex + read;
+
+        }
+        List<String> parts = getParts(fileBuffer, fileBufferIndex);
+        for (String part : parts) {
+            int splitterPos = part.indexOf("=");
+            String key = part.substring(0, splitterPos);
+            String value = part.substring(splitterPos + 1);
+            env.put(key, value);
+        }
+
+        return env;
+    }
+
+    /**
+     * Split a char array, where items are separated by a null into into a list
+     * of strings
+     */
+    private List<String> getParts(char[] nullSeparatedBuffer, int bufferLength) {
+        int maxLength = Math.min(nullSeparatedBuffer.length, bufferLength);
+        List<String> parts = new ArrayList<String>();
+
+        int lastStart = 0;
+        for (int i = 0; i < maxLength; i++) {
+            if (nullSeparatedBuffer[i] == '\0') {
+                String string = new String(nullSeparatedBuffer, lastStart, (i - lastStart));
+                parts.add(string);
+                lastStart = i + 1;
+            }
+        }
+        return parts;
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/system-backend/src/main/java/com/redhat/thermostat/backend/system/ProcessStatusInfo.java	Fri Oct 19 21:58:18 2012 +0200
@@ -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.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;
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/system-backend/src/main/java/com/redhat/thermostat/backend/system/ProcessStatusInfoBuilder.java	Fri Oct 19 21:58:18 2012 +0200
@@ -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.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);
+
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/system-backend/src/main/java/com/redhat/thermostat/backend/system/SysConf.java	Fri Oct 19 21:58:18 2012 +0200
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2012 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.backend.system;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+
+/**
+ * A wrapper over POSIX's sysconf.
+ * <p>
+ * Implementation notes: uses {@code getconf(1)}
+ */
+public class SysConf {
+
+    private SysConf() {
+        /* do not initialize */
+    }
+
+    public static long getClockTicksPerSecond() {
+        String ticks = sysConf("CLK_TCK");
+        try {
+            return Long.valueOf(ticks);
+        } catch (NumberFormatException nfe) {
+            return 0;
+        }
+    }
+
+    private static String sysConf(String arg) {
+        BufferedReader reader = null;
+        try {
+            Process process = Runtime.getRuntime().exec(new String[] { "getconf", arg });
+            int result = process.waitFor();
+            if (result != 0) {
+                return null;
+            }
+            reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
+            return reader.readLine();
+        } catch (IOException e) {
+            return null;
+        } catch (InterruptedException e) {
+            return null;
+        } finally {
+            if (reader != null) {
+                try {
+                    reader.close();
+                } catch (IOException e) {
+                    // TODO
+                }
+            }
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/system-backend/src/main/java/com/redhat/thermostat/backend/system/SystemBackend.java	Fri Oct 19 21:58:18 2012 +0200
@@ -0,0 +1,242 @@
+/*
+ * 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.net.URISyntaxException;
+import java.util.Set;
+import java.util.Timer;
+import java.util.TimerTask;
+import java.util.concurrent.CopyOnWriteArraySet;
+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.agent.JvmStatusListener;
+import com.redhat.thermostat.agent.JvmStatusNotifier;
+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.CpuStatDAO;
+import com.redhat.thermostat.common.dao.HostInfoDAO;
+import com.redhat.thermostat.common.dao.MemoryStatDAO;
+import com.redhat.thermostat.common.dao.NetworkInterfaceInfoDAO;
+import com.redhat.thermostat.common.dao.VmCpuStatDAO;
+import com.redhat.thermostat.common.model.BackendInformation;
+import com.redhat.thermostat.common.model.NetworkInterfaceInfo;
+import com.redhat.thermostat.common.model.VmCpuStat;
+import com.redhat.thermostat.common.utils.LoggingUtils;
+import com.redhat.thermostat.utils.ProcDataSource;
+
+public class SystemBackend extends Backend implements JvmStatusNotifier, JvmStatusListener {
+
+    private static final Logger logger = LoggingUtils.getLogger(SystemBackend.class);
+
+    private CpuStatDAO cpuStats;
+    private HostInfoDAO hostInfos;
+    private MemoryStatDAO memoryStats;
+    private VmCpuStatDAO vmCpuStats;
+    private NetworkInterfaceInfoDAO networkInterfaces;
+
+    private final Set<Integer> pidsToMonitor = new CopyOnWriteArraySet<Integer>();
+
+    private long procCheckInterval = 1000; // TODO make this configurable.
+
+    private Timer timer = null;
+
+    private HostIdentifier hostId = null;
+    private MonitoredHost host = null;
+    private JvmStatHostListener hostListener;
+
+    private final VmCpuStatBuilder vmCpuBuilder;
+    private final HostInfoBuilder hostInfoBuilder;
+    private final CpuStatBuilder cpuStatBuilder;
+    private final MemoryStatBuilder memoryStatBuilder;
+
+    public SystemBackend() {
+        super(new BackendID("System Backend", SystemBackend.class.getName()));
+
+        setConfigurationValue(BackendsProperties.VENDOR.name(), "Red Hat, Inc.");
+        setConfigurationValue(BackendsProperties.DESCRIPTION.name(), "Gathers basic information from the system");
+        setConfigurationValue(BackendsProperties.VERSION.name(), "0.1.0");
+        
+        Clock clock = new SystemClock();
+        ProcessStatusInfoBuilder builder = new ProcessStatusInfoBuilder(new ProcDataSource());
+        long ticksPerSecond = SysConf.getClockTicksPerSecond();
+        ProcDataSource source = new ProcDataSource();
+        hostInfoBuilder = new HostInfoBuilder(source);
+        cpuStatBuilder = new CpuStatBuilder(clock, source, ticksPerSecond);
+        memoryStatBuilder = new MemoryStatBuilder(source);
+
+        int cpuCount = hostInfoBuilder.getCpuInfo().count;
+        vmCpuBuilder = new VmCpuStatBuilder(clock, cpuCount, ticksPerSecond, builder);
+    }
+
+    @Override
+    protected void setDAOFactoryAction() {
+        cpuStats = df.getCpuStatDAO();
+        hostInfos = df.getHostInfoDAO();
+        memoryStats = df.getMemoryStatDAO();
+        vmCpuStats = df.getVmCpuStatDAO();
+        networkInterfaces = df.getNetworkInterfaceInfoDAO();
+        hostListener = new JvmStatHostListener(df.getVmInfoDAO(), df.getVmMemoryStatDAO(), df.getVmGcStatDAO(), df.getVmClassStatsDAO(), getObserveNewJvm());
+    }
+
+    @Override
+    public synchronized boolean activate() {
+        if (timer != null) {
+            return true;
+        }
+        if (df == null) {
+            throw new IllegalStateException("Cannot activate backend without DAOFactory.");
+        }
+
+        addJvmStatusListener(this);
+
+        if (!getObserveNewJvm()) {
+            logger.fine("not monitoring new vms");
+        }
+        hostInfos.putHostInfo(hostInfoBuilder.build());
+
+        timer = new Timer();
+        timer.scheduleAtFixedRate(new TimerTask() {
+            @Override
+            public void run() {
+                if (!cpuStatBuilder.isInitialized()) {
+                    cpuStatBuilder.initialize();
+                } else {
+                    cpuStats.putCpuStat(cpuStatBuilder.build());
+                }
+                for (NetworkInterfaceInfo info: NetworkInfoBuilder.build()) {
+                    networkInterfaces.putNetworkInterfaceInfo(info);
+                }
+                memoryStats.putMemoryStat(memoryStatBuilder.build());
+
+                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);
+
+        try {
+            hostId = new HostIdentifier((String) null);
+            host = MonitoredHost.getMonitoredHost(hostId);
+            host.addHostListener(hostListener);
+        } catch (MonitorException me) {
+            logger.log(Level.WARNING, "problems with connecting jvmstat to local machine", me);
+        } catch (URISyntaxException use) {
+            logger.log(Level.WARNING, "problems with connecting jvmstat to local machine", use);
+        }
+
+        return true;
+    }
+
+    @Override
+    public synchronized boolean deactivate() {
+        if (timer == null) {
+            return true;
+        }
+
+        timer.cancel();
+        timer = null;
+
+        removeJvmStatusListener(this);
+
+        try {
+            
+            // remove all listener from the host listener
+            hostListener.removeAllListeners();
+            
+            host.removeHostListener(hostListener);
+        } catch (MonitorException me) {
+            logger.log(Level.INFO, "something went wrong in jvmstat's listening to this host");
+        }
+        host = null;
+        hostId = null;
+
+        return true;
+    }
+
+    @Override
+    public synchronized boolean isActive() {
+        return (timer != null);
+    }
+
+    @Override
+    public String getConfigurationValue(String key) {
+        return null;
+    }
+
+    @Override
+    public boolean attachToNewProcessByDefault() {
+        return true;
+    }
+
+    @Override
+    public void addJvmStatusListener(JvmStatusListener listener) {
+        hostListener.addJvmStatusListener(listener);
+    }
+
+    @Override
+    public void removeJvmStatusListener(JvmStatusListener listener) {
+        hostListener.removeJvmStatusListener(listener);
+    }
+
+    @Override
+    public void jvmStarted(int vmId) {
+        if (getObserveNewJvm()) {
+            pidsToMonitor.add(vmId);
+        }
+    }
+
+    @Override
+    public void jvmStopped(int vmId) {
+        pidsToMonitor.remove(vmId);
+        vmCpuBuilder.forgetAbout(vmId);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/system-backend/src/main/java/com/redhat/thermostat/backend/system/VmCpuStatBuilder.java	Fri Oct 19 21:58:18 2012 +0200
@@ -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.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.model.VmCpuStat;
+import com.redhat.thermostat.common.utils.LoggingUtils;
+
+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/system-backend/src/main/java/com/redhat/thermostat/backend/system/osgi/SystemBackendActivator.java	Fri Oct 19 21:58:18 2012 +0200
@@ -0,0 +1,89 @@
+/*
+ * 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.osgi;
+
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+import org.osgi.util.tracker.ServiceTracker;
+
+import com.redhat.thermostat.backend.Backend;
+import com.redhat.thermostat.backend.BackendService;
+import com.redhat.thermostat.backend.system.SystemBackend;
+
+@SuppressWarnings("rawtypes")
+public class SystemBackendActivator implements BundleActivator {
+
+    private ServiceTracker tracker;
+    private SystemBackend backend;
+    
+    @SuppressWarnings("unchecked")
+    @Override
+    public void start(BundleContext context) throws Exception {
+        System.err.println("loading SystemBackendActivator");
+        
+        backend = new SystemBackend();
+        
+        tracker = new ServiceTracker(context, BackendService.class, null) {
+            @Override
+            public Object addingService(ServiceReference reference) {
+                context.registerService(Backend.class, backend, null);
+                return super.addingService(reference);
+            }
+            
+            @Override
+            public void removedService(ServiceReference reference, Object service) {
+                
+                if (backend.isActive()) {
+                    backend.deactivate();
+                }
+                context.ungetService(reference);
+                super.removedService(reference, service);
+            }
+        };
+        
+        tracker.open();
+    }
+    
+    @Override
+    public void stop(BundleContext context) throws Exception {
+        if (backend.isActive()) {
+            backend.deactivate();
+        }
+        tracker.close();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/system-backend/src/test/java/com/redhat/thermostat/backend/system/CpuStatBuilderTest.java	Fri Oct 19 21:58:18 2012 +0200
@@ -0,0 +1,112 @@
+/*
+ * 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.assertArrayEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.StringReader;
+
+import org.junit.Test;
+
+import com.redhat.thermostat.common.Clock;
+import com.redhat.thermostat.common.SystemClock;
+import com.redhat.thermostat.common.model.CpuStat;
+import com.redhat.thermostat.common.utils.ArrayUtils;
+import com.redhat.thermostat.utils.ProcDataSource;
+
+public class CpuStatBuilderTest {
+
+    @Test
+    public void testSimpleBuild() {
+        ProcDataSource dataSource = new ProcDataSource();
+        CpuStatBuilder builder= new CpuStatBuilder(new SystemClock(), dataSource, 100l);
+        builder.initialize();
+        CpuStat stat = builder.build();
+        assertNotNull(stat);
+    }
+
+    @Test (expected=IllegalStateException.class)
+    public void buildWithoutInitializeThrowsException() {
+        Clock clock = mock(Clock.class);
+        ProcDataSource dataSource = mock(ProcDataSource.class);
+        long ticksPerSecond = 1;
+        CpuStatBuilder builder = new CpuStatBuilder(clock, dataSource, ticksPerSecond);
+        builder.build();
+    }
+
+    @Test
+    public void testBuildCpuStatFromFile() throws IOException {
+        long CLOCK1 = 1000;
+        long CLOCK2 = 2000;
+
+        String firstReadContents =
+            "cpu 100 0 0 1000 1000\n" +
+            "cpu0 100 0 0 1000 1000\n" +
+            "cpu1 10 80 10 1000 1000\n";
+        BufferedReader reader1 = new BufferedReader(new StringReader(firstReadContents));
+
+        String secondReadContents =
+            "cpu 400 0 0 1000 1000\n" +
+            "cpu0 200 0 0 1000 1000\n" +
+            "cpu1 30 50 120 1000 1000\n";
+        BufferedReader reader2 = new BufferedReader(new StringReader(secondReadContents));
+
+        long ticksPerSecond = 100;
+        Clock clock = mock(Clock.class);
+        when(clock.getRealTimeMillis()).thenReturn(CLOCK2);
+        when(clock.getMonotonicTimeNanos()).thenReturn((long)(CLOCK1 * 1E6)).thenReturn((long)(CLOCK2 * 1E6));
+
+        ProcDataSource dataSource = mock(ProcDataSource.class);
+        when(dataSource.getStatReader()).thenReturn(reader1).thenReturn(reader2);
+        CpuStatBuilder builder = new CpuStatBuilder(clock, dataSource, ticksPerSecond);
+
+        builder.initialize();
+
+        CpuStat stat = builder.build();
+
+        verify(dataSource, times(2)).getStatReader();
+        assertArrayEquals(new double[] {100, 100}, ArrayUtils.toPrimitiveDoubleArray(stat.getPerProcessorUsage()), 0.01);
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/system-backend/src/test/java/com/redhat/thermostat/backend/system/DistributionInformationTest.java	Fri Oct 19 21:58:18 2012 +0200
@@ -0,0 +1,71 @@
+/*
+ * 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.*;
+
+import org.junit.Test;
+
+import com.redhat.thermostat.common.TestUtils;
+
+public class DistributionInformationTest {
+
+    @Test
+    public void testName() {
+        if (TestUtils.isLinux()) {
+            DistributionInformation info = DistributionInformation.get();
+            String name = info.getName();
+            assertNotNull(name);
+            assertTrue(name.length() > 0);
+            assertFalse(name.startsWith(":"));
+            assertFalse(name.equals(DistributionInformation.UNKNOWN_NAME));
+        }
+    }
+
+    @Test
+    public void testVersion() {
+        if (TestUtils.isLinux()) {
+            DistributionInformation info = DistributionInformation.get();
+            String version = info.getVersion();
+            assertNotNull(version);
+            assertTrue(version.length()> 0);
+            assertFalse(version.startsWith(":"));
+            assertFalse(version.equals(DistributionInformation.UNKNOWN_VERSION));
+        }
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/system-backend/src/test/java/com/redhat/thermostat/backend/system/EtcOsReleaseTest.java	Fri Oct 19 21:58:18 2012 +0200
@@ -0,0 +1,84 @@
+/*
+ * 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 java.io.BufferedReader;
+import java.io.IOException;
+import java.io.StringReader;
+
+import org.junit.Test;
+
+import com.redhat.thermostat.test.Bug;
+
+public class EtcOsReleaseTest {
+
+    @Test
+    public void testName() throws IOException, InterruptedException {
+        BufferedReader reader = new BufferedReader(new StringReader("NAME=\"Name\"\n"));
+        DistributionInformation info = new EtcOsRelease().getFromOsRelease(reader);
+        assertEquals("Name", info.getName());
+    }
+
+
+    @Test
+    public void testVersion() throws IOException {
+        BufferedReader reader = new BufferedReader(new StringReader("VERSION=\"Version\"\n"));
+        DistributionInformation info = new EtcOsRelease().getFromOsRelease(reader);
+        assertEquals("Version", info.getVersion());
+    }
+
+    @Bug(id="981",
+        summary="DistributionInformationTest fails on OpenSUSE Linux 12.1",
+        url="http://icedtea.classpath.org/bugzilla/show_bug.cgi?id=981")
+    @Test
+    public void testFormattedOutput() throws IOException {
+        String output =
+            "NAME=openSUSE\n" +
+            "VERSION = 12.1 (Asparagus)\n" +
+            "VERSION_ID=\"12.1\"\n" +
+            "PRETTY_NAME=\"openSUSE 12.1 (Asparagus) (x86_64)\"\n" +
+            "ID=opensuse";
+        BufferedReader reader = new BufferedReader(new StringReader(output));
+        DistributionInformation info = new EtcOsRelease().getFromOsRelease(reader);
+
+        assertEquals("openSUSE", info.getName());
+        assertEquals("12.1 (Asparagus)", info.getVersion());
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/system-backend/src/test/java/com/redhat/thermostat/backend/system/HostInfoBuilderTest.java	Fri Oct 19 21:58:18 2012 +0200
@@ -0,0 +1,122 @@
+/*
+ * 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.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import java.io.IOException;
+import java.io.StringReader;
+import java.net.InetAddress;
+
+import org.junit.Test;
+
+import com.redhat.thermostat.backend.system.HostInfoBuilder.HostCpuInfo;
+import com.redhat.thermostat.backend.system.HostInfoBuilder.HostMemoryInfo;
+import com.redhat.thermostat.backend.system.HostInfoBuilder.HostOsInfo;
+import com.redhat.thermostat.common.Constants;
+import com.redhat.thermostat.common.model.HostInfo;
+import com.redhat.thermostat.utils.ProcDataSource;
+
+public class HostInfoBuilderTest {
+
+    @Test
+    public void testSimpleBuild() {
+        HostInfo info = new HostInfoBuilder(new ProcDataSource()).build();
+        assertNotNull(info);
+    }
+
+    @Test
+    public void testCpuInfo() throws IOException {
+        String cpuInfoString =
+                "processor: 1\n" +
+                "model name: Test Model\n" +
+                "processor: 0\n" +
+                "model name: Test Model\n";
+
+        StringReader cpuInfoReader = new StringReader(cpuInfoString);
+
+        ProcDataSource dataSource = mock(ProcDataSource.class);
+        when(dataSource.getCpuInfoReader()).thenReturn(cpuInfoReader);
+
+        HostCpuInfo cpuInfo = new HostInfoBuilder(dataSource).getCpuInfo();
+        assertEquals(2, cpuInfo.count);
+        assertEquals("Test Model", cpuInfo.model);
+        verify(dataSource).getCpuInfoReader();
+    }
+
+    @Test
+    public void testMemoryInfo() throws IOException {
+        String memoryInfoString =
+                "MemTotal: 12345 kB";
+
+        StringReader memoryInfoReader = new StringReader(memoryInfoString);
+        ProcDataSource dataSource = mock(ProcDataSource.class);
+        when(dataSource.getMemInfoReader()).thenReturn(memoryInfoReader);
+
+        HostMemoryInfo memoryInfo = new HostInfoBuilder(dataSource).getMemoryInfo();
+        assertNotNull(memoryInfo);
+        assertEquals(12345 * Constants.KILOBYTES_TO_BYTES, memoryInfo.totalMemory);
+        verify(dataSource).getMemInfoReader();
+
+    }
+
+    @Test
+    public void testOsInfo() {
+        DistributionInformation distroInfo = new DistributionInformation("distro-name", "distro-version");
+        ProcDataSource dataSource = mock(ProcDataSource.class);
+        HostOsInfo osInfo = new HostInfoBuilder(dataSource).getOsInfo(distroInfo);
+        assertEquals("distro-name distro-version", osInfo.distribution);
+        assertEquals(System.getProperty("os.name") + " " + System.getProperty("os.version"), osInfo.kernel);
+    }
+
+    @Test
+    public void testHostname() {
+
+        InetAddress address = mock(InetAddress.class);
+        when(address.getCanonicalHostName()).thenReturn("test-hostname");
+
+        ProcDataSource dataSource = mock(ProcDataSource.class);
+
+        String name = new HostInfoBuilder(dataSource).getHostName(address);
+        assertEquals("test-hostname", name);
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/system-backend/src/test/java/com/redhat/thermostat/backend/system/JvmStatDataExtractorTest.java	Fri Oct 19 21:58:18 2012 +0200
@@ -0,0 +1,375 @@
+/*
+ * Copyright 2012 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.backend.system;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import org.junit.Test;
+
+import sun.jvmstat.monitor.LongMonitor;
+import sun.jvmstat.monitor.MonitorException;
+import sun.jvmstat.monitor.MonitoredVm;
+import sun.jvmstat.monitor.StringMonitor;
+
+public class JvmStatDataExtractorTest {
+
+    private MonitoredVm buildStringMonitoredVm(String monitorName, String monitorReturn) throws MonitorException {
+        final StringMonitor monitor = mock(StringMonitor.class);
+        when(monitor.stringValue()).thenReturn(monitorReturn);
+        when(monitor.getValue()).thenReturn(monitorReturn);
+        MonitoredVm vm = mock(MonitoredVm.class);
+        when(vm.findByName(monitorName)).thenReturn(monitor);
+        return vm;
+    }
+
+    private MonitoredVm buildLongMonitoredVm(String monitorName, Long monitorReturn) throws MonitorException {
+        final LongMonitor monitor = mock(LongMonitor.class);
+        when(monitor.longValue()).thenReturn(monitorReturn);
+        when(monitor.getValue()).thenReturn(monitorReturn);
+        MonitoredVm vm = mock(MonitoredVm.class);
+        when(vm.findByName(monitorName)).thenReturn(monitor);
+        return vm;
+    }
+
+    @Test
+    public void testCommandLine() throws MonitorException {
+        final String MONITOR_NAME = "sun.rt.javaCommand";
+        final String MONITOR_VALUE = "command line java";
+        MonitoredVm vm = buildStringMonitoredVm(MONITOR_NAME, MONITOR_VALUE);
+
+        JvmStatDataExtractor extractor = new JvmStatDataExtractor(vm);
+        String returned = extractor.getCommandLine();
+
+        verify(vm).findByName(eq(MONITOR_NAME));
+        assertEquals(MONITOR_VALUE, returned);
+    }
+
+    @Test
+    public void testMainClass() throws MonitorException {
+        final String MONITOR_NAME = "sun.rt.javaCommand";
+        final String MONITOR_VALUE = "some.package.Main";
+        MonitoredVm vm = buildStringMonitoredVm(MONITOR_NAME, MONITOR_VALUE);
+
+        JvmStatDataExtractor extractor = new JvmStatDataExtractor(vm);
+        String returned = extractor.getMainClass();
+
+        verify(vm).findByName(eq(MONITOR_NAME));
+        assertEquals(MONITOR_VALUE, returned);
+    }
+
+    @Test
+    public void testJavaVersion() throws MonitorException {
+        final String MONITOR_NAME = "java.property.java.version";
+        final String MONITOR_VALUE = "some java version";
+        MonitoredVm vm = buildStringMonitoredVm(MONITOR_NAME, MONITOR_VALUE);
+
+        JvmStatDataExtractor extractor = new JvmStatDataExtractor(vm);
+        String returned = extractor.getJavaVersion();
+
+        verify(vm).findByName(eq(MONITOR_NAME));
+        assertEquals(MONITOR_VALUE, returned);
+    }
+
+    @Test
+    public void testJavaHome() throws MonitorException {
+        final String MONITOR_NAME = "java.property.java.home";
+        final String MONITOR_VALUE = "${java.home}";
+        MonitoredVm vm = buildStringMonitoredVm(MONITOR_NAME, MONITOR_VALUE);
+
+        JvmStatDataExtractor extractor = new JvmStatDataExtractor(vm);
+        String returned = extractor.getJavaHome();
+
+        verify(vm).findByName(eq(MONITOR_NAME));
+        assertEquals(MONITOR_VALUE, returned);
+    }
+
+    @Test
+    public void testVmName() throws MonitorException {
+        final String MONITOR_NAME = "java.property.java.vm.name";
+        final String MONITOR_VALUE = "${vm.name}";
+        MonitoredVm vm = buildStringMonitoredVm(MONITOR_NAME, MONITOR_VALUE);
+
+        JvmStatDataExtractor extractor = new JvmStatDataExtractor(vm);
+        String returned = extractor.getVmName();
+
+        verify(vm).findByName(eq(MONITOR_NAME));
+        assertEquals(MONITOR_VALUE, returned);
+    }
+
+    @Test
+    public void testVmInfo() throws MonitorException {
+        final String MONITOR_NAME = "java.property.java.vm.info";
+        final String MONITOR_VALUE = "${vm.info}";
+        MonitoredVm vm = buildStringMonitoredVm(MONITOR_NAME, MONITOR_VALUE);
+
+        JvmStatDataExtractor extractor = new JvmStatDataExtractor(vm);
+        String returned = extractor.getVmInfo();
+
+        verify(vm).findByName(eq(MONITOR_NAME));
+        assertEquals(MONITOR_VALUE, returned);
+    }
+
+    @Test
+    public void testVmVersion() throws MonitorException {
+        final String MONITOR_NAME = "java.property.java.vm.version";
+        final String MONITOR_VALUE = "${vm.version}";
+        MonitoredVm vm = buildStringMonitoredVm(MONITOR_NAME, MONITOR_VALUE);
+
+        JvmStatDataExtractor extractor = new JvmStatDataExtractor(vm);
+        String returned = extractor.getVmVersion();
+
+        verify(vm).findByName(eq(MONITOR_NAME));
+        assertEquals(MONITOR_VALUE, returned);
+    }
+
+    @Test
+    public void testVmArguments() throws MonitorException {
+        final String MONITOR_NAME = "java.rt.vmArgs";
+        final String MONITOR_VALUE = "${vm.arguments}";
+        MonitoredVm vm = buildStringMonitoredVm(MONITOR_NAME, MONITOR_VALUE);
+
+        JvmStatDataExtractor extractor = new JvmStatDataExtractor(vm);
+        String returned = extractor.getVmArguments();
+
+        verify(vm).findByName(eq(MONITOR_NAME));
+        assertEquals(MONITOR_VALUE, returned);
+    }
+
+    @Test
+    public void testTotalCollectors() throws MonitorException {
+        final String MONITOR_NAME = "sun.gc.policy.collectors";
+        final Long MONITOR_VALUE = 9l;
+        MonitoredVm vm = buildLongMonitoredVm(MONITOR_NAME, MONITOR_VALUE);
+
+        JvmStatDataExtractor extractor = new JvmStatDataExtractor(vm);
+        Long returned = extractor.getTotalCollectors();
+
+        verify(vm).findByName(eq(MONITOR_NAME));
+        assertEquals(MONITOR_VALUE, returned);
+    }
+
+    @Test
+    public void testCollectorName() throws MonitorException {
+        final String MONITOR_NAME = "sun.gc.collector.0.name";
+        final String COLLECTOR_NAME = "SomeMemoryCollector";
+        MonitoredVm vm = buildStringMonitoredVm(MONITOR_NAME, COLLECTOR_NAME);
+
+        JvmStatDataExtractor extractor = new JvmStatDataExtractor(vm);
+        String returned = extractor.getCollectorName(0);
+
+        verify(vm).findByName(eq(MONITOR_NAME));
+        assertEquals(COLLECTOR_NAME, returned);
+    }
+
+    @Test
+    public void testCollectorTime() throws MonitorException {
+        final String MONITOR_NAME = "sun.gc.collector.0.time";
+        final Long COLLECTOR_TIME = 99l;
+        MonitoredVm vm = buildLongMonitoredVm(MONITOR_NAME, COLLECTOR_TIME);
+
+        JvmStatDataExtractor extractor = new JvmStatDataExtractor(vm);
+        Long returned = extractor.getCollectorTime(0);
+
+        verify(vm).findByName(eq(MONITOR_NAME));
+        assertEquals(COLLECTOR_TIME, returned);
+    }
+
+    @Test
+    public void testCollectorInvocations() throws MonitorException {
+        final String MONITOR_NAME = "sun.gc.collector.0.invocations";
+        final Long COLLECTOR_INVOCATIONS = 99l;
+        MonitoredVm vm = buildLongMonitoredVm(MONITOR_NAME, COLLECTOR_INVOCATIONS);
+
+        JvmStatDataExtractor extractor = new JvmStatDataExtractor(vm);
+        Long returned = extractor.getCollectorInvocations(0);
+
+        verify(vm).findByName(eq(MONITOR_NAME));
+        assertEquals(COLLECTOR_INVOCATIONS, returned);
+    }
+
+    @Test
+    public void testTotalGcGenerations() throws MonitorException {
+        final String MONITOR_NAME = "sun.gc.policy.generations";
+        final Long GC_GENERATIONS = 99l;
+        MonitoredVm vm = buildLongMonitoredVm(MONITOR_NAME, GC_GENERATIONS);
+
+        JvmStatDataExtractor extractor = new JvmStatDataExtractor(vm);
+        Long returned = extractor.getTotalGcGenerations();
+
+        verify(vm).findByName(eq(MONITOR_NAME));
+        assertEquals(GC_GENERATIONS, returned);
+    }
+
+    @Test
+    public void testGenerationName() throws MonitorException {
+        final String MONITOR_NAME = "sun.gc.generation.0.name";
+        final String GENERATION_NAME = "Youth";
+        MonitoredVm vm = buildStringMonitoredVm(MONITOR_NAME, GENERATION_NAME);
+
+        JvmStatDataExtractor extractor = new JvmStatDataExtractor(vm);
+        String returned = extractor.getGenerationName(0);
+
+        verify(vm).findByName(eq(MONITOR_NAME));
+        assertEquals(GENERATION_NAME, returned);
+    }
+
+    @Test
+    public void testGenerationCapacity() throws MonitorException {
+        final String MONITOR_NAME = "sun.gc.generation.0.capacity";
+        final Long GENERATION_CAPACITY = 99l;
+        MonitoredVm vm = buildLongMonitoredVm(MONITOR_NAME, GENERATION_CAPACITY);
+
+        JvmStatDataExtractor extractor = new JvmStatDataExtractor(vm);
+        Long returned = extractor.getGenerationCapacity(0);
+
+        verify(vm).findByName(eq(MONITOR_NAME));
+        assertEquals(GENERATION_CAPACITY, returned);
+    }
+
+    @Test
+    public void testGenerationMaxCapacity() throws MonitorException {
+        final String MONITOR_NAME = "sun.gc.generation.0.maxCapacity";
+        final Long GENERATION_MAX_CAPACITY = 99l;
+        MonitoredVm vm = buildLongMonitoredVm(MONITOR_NAME, GENERATION_MAX_CAPACITY);
+
+
+        JvmStatDataExtractor extractor = new JvmStatDataExtractor(vm);
+        Long returned = extractor.getGenerationMaxCapacity(0);
+
+        verify(vm).findByName(eq(MONITOR_NAME));
+        assertEquals(GENERATION_MAX_CAPACITY, returned);
+    }
+
+    @Test
+    public void testGenerationCollector() throws MonitorException {
+        final String MONITOR_NAME = "sun.gc.collector.0.name";
+        final String GENERATION_COLLECTOR = "generation collector";
+        MonitoredVm vm = buildStringMonitoredVm(MONITOR_NAME, GENERATION_COLLECTOR);
+
+        JvmStatDataExtractor extractor = new JvmStatDataExtractor(vm);
+        String returned = extractor.getGenerationCollector(0);
+
+        verify(vm).findByName(eq(MONITOR_NAME));
+        assertEquals(GENERATION_COLLECTOR, returned);
+    }
+
+    @Test
+    public void testTotalSpaces() throws MonitorException {
+        final Long TOTAL_SPACES = 99l;
+        final LongMonitor monitor = mock(LongMonitor.class);
+        when(monitor.getValue()).thenReturn(TOTAL_SPACES);
+        MonitoredVm vm = mock(MonitoredVm.class);
+        when(vm.findByName("sun.gc.generation.0.spaces")).thenReturn(monitor);
+
+        JvmStatDataExtractor extractor = new JvmStatDataExtractor(vm);
+        Long returned = extractor.getTotalSpaces(0);
+
+        verify(vm).findByName(eq("sun.gc.generation.0.spaces"));
+        assertEquals(TOTAL_SPACES, returned);
+    }
+
+
+    @Test
+    public void testSpaceName() throws MonitorException {
+        final String MONITOR_NAME = "sun.gc.generation.0.space.0.name";
+        final String SPACE_NAME = "Hilbert";
+        MonitoredVm vm = buildStringMonitoredVm(MONITOR_NAME, SPACE_NAME);
+
+        JvmStatDataExtractor extractor = new JvmStatDataExtractor(vm);
+        String returned = extractor.getSpaceName(0,0);
+
+        verify(vm).findByName(eq(MONITOR_NAME));
+        assertEquals(SPACE_NAME, returned);
+    }
+
+    @Test
+    public void testSpaceCapacity() throws MonitorException {
+        final String MONITOR_NAME = "sun.gc.generation.0.space.0.capacity";
+        final Long SPACE_CAPACITY = 99l;
+        MonitoredVm vm = buildLongMonitoredVm(MONITOR_NAME, SPACE_CAPACITY);
+
+        JvmStatDataExtractor extractor = new JvmStatDataExtractor(vm);
+        Long returned = extractor.getSpaceCapacity(0,0);
+
+        verify(vm).findByName(eq(MONITOR_NAME));
+        assertEquals(SPACE_CAPACITY, returned);
+    }
+
+    @Test
+    public void testSpaceMaxCapacity() throws MonitorException {
+        final String MONITOR_NAME = "sun.gc.generation.0.space.0.maxCapacity";
+        final Long SPACE_MAX_CAPACITY = 99l;
+        MonitoredVm vm = buildLongMonitoredVm(MONITOR_NAME, SPACE_MAX_CAPACITY);
+
+        JvmStatDataExtractor extractor = new JvmStatDataExtractor(vm);
+        Long returned = extractor.getSpaceMaxCapacity(0,0);
+
+        verify(vm).findByName(eq(MONITOR_NAME));
+        assertEquals(SPACE_MAX_CAPACITY, returned);
+    }
+
+    @Test
+    public void testSpaceUsed() throws MonitorException {
+        final String MONITOR_NAME = "sun.gc.generation.0.space.0.used";
+        final Long SPACE_USED = 99l;
+        MonitoredVm vm = buildLongMonitoredVm(MONITOR_NAME, SPACE_USED);
+
+        JvmStatDataExtractor extractor = new JvmStatDataExtractor(vm);
+        Long returned = extractor.getSpaceUsed(0,0);
+
+        verify(vm).findByName(eq(MONITOR_NAME));
+        assertEquals(SPACE_USED, returned);
+    }
+
+    @Test
+    public void testLoadedClasses() throws MonitorException {
+        final String MONITOR_NAME = "java.cls.loadedClasses";
+        final Long LOADED_CLASSES = 99l;
+        MonitoredVm vm = buildLongMonitoredVm(MONITOR_NAME, LOADED_CLASSES);
+
+        JvmStatDataExtractor extractor = new JvmStatDataExtractor(vm);
+        Long returned = extractor.getLoadedClasses();
+
+        verify(vm).findByName(eq(MONITOR_NAME));
+        assertEquals(LOADED_CLASSES, returned);
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/system-backend/src/test/java/com/redhat/thermostat/backend/system/JvmStatHostListenerTest.java	Fri Oct 19 21:58:18 2012 +0200
@@ -0,0 +1,93 @@
+/*
+ * 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.mockito.Matchers.any;
+import static org.mockito.Matchers.anyString;
+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 org.junit.Test;
+import org.mockito.Matchers;
+
+import sun.jvmstat.monitor.HostIdentifier;
+import sun.jvmstat.monitor.MonitoredHost;
+import sun.jvmstat.monitor.MonitoredVm;
+import sun.jvmstat.monitor.StringMonitor;
+import sun.jvmstat.monitor.VmIdentifier;
+import sun.jvmstat.monitor.event.VmStatusChangeEvent;
+
+import com.redhat.thermostat.common.dao.VmClassStatDAO;
+import com.redhat.thermostat.common.dao.VmInfoDAO;
+
+public class JvmStatHostListenerTest {
+
+    @Test
+    public void testVmStatusChangedAddsVmClassListener() throws Exception {
+        VmStatusChangeEvent vmEvent = mock(VmStatusChangeEvent.class);
+        Set<Integer> startedVms = new HashSet<Integer>();
+        startedVms.add(123);
+        when(vmEvent.getStarted()).thenReturn(startedVms);
+
+        MonitoredVm vm = mock(MonitoredVm.class);
+        StringMonitor monitor = mock(StringMonitor.class);
+        when(monitor.stringValue()).thenReturn("test");
+        when(monitor.getValue()).thenReturn("test");
+        when(vm.findByName(anyString())).thenReturn(monitor);
+        MonitoredHost host = mock(MonitoredHost.class);
+        HostIdentifier hostId = mock(HostIdentifier.class);
+        when(host.getHostIdentifier()).thenReturn(hostId);
+        when(host.getMonitoredVm(any(VmIdentifier.class))).thenReturn(vm);
+        when(vmEvent.getMonitoredHost()).thenReturn(host);
+
+        VmClassStatDAO vmClassDAO = mock(VmClassStatDAO.class);
+        VmInfoDAO vmInfoDAO = mock(VmInfoDAO.class);
+
+        JvmStatHostListener l = new JvmStatHostListener(vmInfoDAO, null, null, vmClassDAO ,true);
+        SystemBackend backend = mock(SystemBackend.class);
+        when(backend.getObserveNewJvm()).thenReturn(true);
+
+        l.vmStatusChanged(vmEvent);
+
+        verify(vm).addVmListener(Matchers.isA(JvmStatVmClassListener.class));
+    }
+
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/system-backend/src/test/java/com/redhat/thermostat/backend/system/JvmStatVmClassListenerTest.java	Fri Oct 19 21:58:18 2012 +0200
@@ -0,0 +1,100 @@
+/*
+ * Copyright 2012 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.backend.system;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+
+import sun.jvmstat.monitor.Monitor;
+import sun.jvmstat.monitor.MonitoredVm;
+import sun.jvmstat.monitor.event.VmEvent;
+
+import com.redhat.thermostat.common.dao.VmClassStatDAO;
+import com.redhat.thermostat.common.model.VmClassStat;
+
+public class JvmStatVmClassListenerTest {
+
+    private static final Integer VM_ID = 123;
+    private static final Long LOADED_CLASSES = 1234L;
+
+    @Test
+    public void testMonitorUpdatedClassStat() throws Exception {
+
+        VmClassStatDAO dao = mock(VmClassStatDAO.class);
+
+        JvmStatVmClassListener l = new JvmStatVmClassListener(dao, VM_ID);
+        VmEvent vmEvent = mock(VmEvent.class);
+        MonitoredVm monitoredVm = mock(MonitoredVm.class);
+        Monitor m = mock(Monitor.class);
+        when(m.getValue()).thenReturn(LOADED_CLASSES);
+        when(monitoredVm.findByName("java.cls.loadedClasses")).thenReturn(m);
+        when(vmEvent.getMonitoredVm()).thenReturn(monitoredVm);
+
+        l.monitorsUpdated(vmEvent);
+
+        ArgumentCaptor<VmClassStat> arg = ArgumentCaptor.forClass(VmClassStat.class);
+        verify(dao).putVmClassStat(arg.capture());
+        VmClassStat stat = arg.getValue();
+        assertEquals(LOADED_CLASSES, (Long) stat.getLoadedClasses());
+        assertEquals(VM_ID, (Integer) stat.getVmId());
+    }
+
+    @Test
+    public void testMonitorUpdatedClassStatTwice() throws Exception {
+
+        VmClassStatDAO dao = mock(VmClassStatDAO.class);
+
+        JvmStatVmClassListener l = new JvmStatVmClassListener(dao, VM_ID);
+        VmEvent vmEvent = mock(VmEvent.class);
+        MonitoredVm monitoredVm = mock(MonitoredVm.class);
+        Monitor m = mock(Monitor.class);
+        when(m.getValue()).thenReturn(LOADED_CLASSES);
+        when(monitoredVm.findByName("java.cls.loadedClasses")).thenReturn(m);
+        when(vmEvent.getMonitoredVm()).thenReturn(monitoredVm);
+
+        l.monitorsUpdated(vmEvent);
+        l.monitorsUpdated(vmEvent);
+
+        // This checks a bug where the Category threw an IllegalStateException because the DAO
+        // created a new one on each call, thus violating the unique guarantee of Category.
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/system-backend/src/test/java/com/redhat/thermostat/backend/system/LsbReleaseTest.java	Fri Oct 19 21:58:18 2012 +0200
@@ -0,0 +1,63 @@
+/*
+ * 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 java.io.BufferedReader;
+import java.io.IOException;
+import java.io.StringReader;
+
+import org.junit.Test;
+
+public class LsbReleaseTest {
+
+    @Test
+    public void testName() throws IOException, InterruptedException {
+        BufferedReader reader = new BufferedReader(new StringReader("Distributor ID: Name"));
+        DistributionInformation info = new LsbRelease().getFromLsbRelease(reader);
+        assertEquals("Name", info.getName());
+    }
+
+    @Test
+    public void testVersion() throws IOException {
+        BufferedReader reader = new BufferedReader(new StringReader("Release: Version"));
+        DistributionInformation info = new LsbRelease().getFromLsbRelease(reader);
+        assertEquals("Version", info.getVersion());
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/system-backend/src/test/java/com/redhat/thermostat/backend/system/MemoryStatBuilderTest.java	Fri Oct 19 21:58:18 2012 +0200
@@ -0,0 +1,112 @@
+/*
+ * Copyright 2012 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.backend.system;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import java.io.IOException;
+import java.io.StringReader;
+
+import org.junit.Test;
+
+import com.redhat.thermostat.common.Constants;
+import com.redhat.thermostat.common.model.MemoryStat;
+import com.redhat.thermostat.utils.ProcDataSource;
+
+public class MemoryStatBuilderTest {
+
+    @Test
+    public void testSimpleBuild() {
+        MemoryStat stat = new MemoryStatBuilder(new ProcDataSource()).build();
+        assertNotNull(stat);
+    }
+
+    @Test
+    public void testEmptyBuild() throws IOException {
+        String memory = "";
+        StringReader memoryReader = new StringReader(memory);
+        ProcDataSource dataSource = mock(ProcDataSource.class);
+        when(dataSource.getMemInfoReader()).thenReturn(memoryReader);
+
+        MemoryStat stat = new MemoryStatBuilder(dataSource).build();
+        assertNotNull(stat);
+        verify(dataSource).getMemInfoReader();
+    }
+
+    @Test
+    public void testBuild() throws IOException {
+        int i = 1;
+        final long TOTAL = i++;
+        final long FREE = i++;
+        final long BUFFERS = i++;
+        final long CACHED = i++;
+        final long COMMIT_LIMIT = i++;
+        final long SWAP_TOTAL = i++;
+        final long SWAP_FREE = i++;
+
+        String memory = "" +
+                "MemTotal: " + TOTAL + " kB\n" +
+                "MemFree:  " + FREE + " kB\n" +
+                "Buffers:" + BUFFERS + " kB\n" +
+                "Cached: " + CACHED + " kB\n" +
+                "CommitLimit: " + COMMIT_LIMIT + " kB\n" +
+                "SwapTotal: " + SWAP_TOTAL + " kB\n" +
+                "SwapFree: " + SWAP_FREE + " kB\n";
+
+        StringReader memoryReader = new StringReader(memory);
+        ProcDataSource dataSource = mock(ProcDataSource.class);
+        when(dataSource.getMemInfoReader()).thenReturn(memoryReader);
+
+        MemoryStat stat = new MemoryStatBuilder(dataSource).build();
+
+        assertEquals(BUFFERS * Constants.KILOBYTES_TO_BYTES, stat.getBuffers());
+        assertEquals(CACHED * Constants.KILOBYTES_TO_BYTES, stat.getCached());
+        assertEquals(COMMIT_LIMIT * Constants.KILOBYTES_TO_BYTES, stat.getCommitLimit());
+        assertEquals(FREE * Constants.KILOBYTES_TO_BYTES, stat.getFree());
+        assertEquals(SWAP_FREE * Constants.KILOBYTES_TO_BYTES, stat.getSwapFree());
+        assertEquals(SWAP_TOTAL * Constants.KILOBYTES_TO_BYTES, stat.getSwapTotal());
+        assertEquals(TOTAL * Constants.KILOBYTES_TO_BYTES, stat.getTotal());
+        assertTrue(stat.getTimeStamp() != 0 && stat.getTimeStamp() != Long.MIN_VALUE);
+        assertTrue(stat.getTimeStamp() <= System.currentTimeMillis());
+        verify(dataSource).getMemInfoReader();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/system-backend/src/test/java/com/redhat/thermostat/backend/system/NetworkInfoBuilderTest.java	Fri Oct 19 21:58:18 2012 +0200
@@ -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.backend.system;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import java.util.List;
+
+import org.junit.Test;
+
+import com.redhat.thermostat.common.model.NetworkInterfaceInfo;
+
+public class NetworkInfoBuilderTest {
+
+    @Test
+    public void testBuilder() {
+
+        List<NetworkInterfaceInfo> info = NetworkInfoBuilder.build();
+        assertNotNull(info);
+        for (NetworkInterfaceInfo iface: info) {
+            assertNotNull(iface);
+            assertNotNull(iface.getInterfaceName());
+            if (iface.getIp4Addr() != null) {
+                // ipv4 address matches the form XX.XX.XX.XX
+                assertTrue(iface.getIp4Addr().matches("\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}"));
+            }
+            // TODO check for sane ipv6 address
+        }
+
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/system-backend/src/test/java/com/redhat/thermostat/backend/system/ProcessEnvironmentBuilderTest.java	Fri Oct 19 21:58:18 2012 +0200
@@ -0,0 +1,134 @@
+/*
+ * 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.Mockito.any;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.util.Map;
+import java.util.Random;
+
+import org.junit.Test;
+
+import com.redhat.thermostat.common.TestUtils;
+import com.redhat.thermostat.utils.ProcDataSource;
+
+public class ProcessEnvironmentBuilderTest {
+
+    private final Random r = new Random();
+
+    @Test
+    public void testBasicBuild() {
+        ProcDataSource dataSource = new ProcDataSource();
+        Map<String, String> result = new ProcessEnvironmentBuilder(dataSource).build(TestUtils.getProcessId());
+        assertNotNull(result);
+        assertFalse(result.isEmpty());
+        assertTrue(result.containsKey("USER"));
+    }
+
+    @Test
+    public void testCustomEnvironment() throws IOException {
+        byte[] data = ("USER=test\000HOME=house\000").getBytes();
+
+        Reader r = new InputStreamReader(new ByteArrayInputStream(data));
+        ProcDataSource dataSource = mock(ProcDataSource.class);
+        when(dataSource.getEnvironReader(any(Integer.class))).thenReturn(r);
+
+        Map<String, String> result = new ProcessEnvironmentBuilder(dataSource).build(0);
+
+        verify(dataSource).getEnvironReader(eq(0));
+        assertEquals("test", result.get("USER"));
+        assertEquals("house", result.get("HOME"));
+    }
+
+    @Test
+    public void testLargeRandomEnvironment() throws IOException {
+        int TEST_ENV_SIZE = 1024 * 1024;
+        byte[] data = new byte[TEST_ENV_SIZE];
+        int currentPosition = 0;
+        do {
+            byte[] key = generateRandomBytes();
+            byte[] value = generateRandomBytes();
+            if (currentPosition + key.length + value.length + 2 >= data.length) {
+                break;
+            }
+            System.arraycopy(key, 0, data, currentPosition, key.length);
+            currentPosition += key.length;
+            data[currentPosition] = (byte) '=';
+            currentPosition++;
+            System.arraycopy(value, 0, data, currentPosition, value.length);
+            currentPosition += value.length;
+            data[currentPosition] = 0x00;
+            currentPosition++;
+        } while (true);
+        Reader r = new InputStreamReader(new ByteArrayInputStream(data, 0, currentPosition));
+        ProcDataSource dataSource = mock(ProcDataSource.class);
+        when(dataSource.getEnvironReader(any(Integer.class))).thenReturn(r);
+
+        Map<String, String> result = new ProcessEnvironmentBuilder(dataSource).build(0);
+
+        verify(dataSource).getEnvironReader(eq(0));
+        assertNotNull(result);
+    }
+
+    private byte[] generateRandomBytes() {
+        byte start = (byte) 'a';
+        byte end = (byte) 'z' + 1;
+
+        byte[] alphabet = new byte[end - start];
+        for (int i = 0; i < (end-start); i++) {
+            alphabet[i] = (byte) (i + start);
+        }
+        int size = r.nextInt(15) + 10;
+        byte[] result = new byte[size];
+        for (int i = 0; i < result.length; i++) {
+            result[i] = alphabet[r.nextInt(alphabet.length)];
+        }
+        return result;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/system-backend/src/test/java/com/redhat/thermostat/backend/system/ProcessStatusInfoBuilderTest.java	Fri Oct 19 21:58:18 2012 +0200
@@ -0,0 +1,142 @@
+/*
+ * 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());
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/system-backend/src/test/java/com/redhat/thermostat/backend/system/SysConfTest.java	Fri Oct 19 21:58:18 2012 +0200
@@ -0,0 +1,50 @@
+/*
+ * 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.assertTrue;
+
+import org.junit.Test;
+
+public class SysConfTest {
+
+    @Test
+    public void test() {
+        long ticksPerSecond = SysConf.getClockTicksPerSecond();
+        assertTrue(ticksPerSecond >= 1);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/system-backend/src/test/java/com/redhat/thermostat/backend/system/SystemBackendTest.java	Fri Oct 19 21:58:18 2012 +0200
@@ -0,0 +1,101 @@
+/*
+ * 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.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import com.redhat.thermostat.common.dao.CpuStatDAO;
+import com.redhat.thermostat.common.dao.DAOFactory;
+import com.redhat.thermostat.common.dao.HostInfoDAO;
+import com.redhat.thermostat.common.dao.MemoryStatDAO;
+import com.redhat.thermostat.common.dao.NetworkInterfaceInfoDAO;
+import com.redhat.thermostat.common.dao.VmCpuStatDAO;
+import com.redhat.thermostat.common.storage.Storage;
+
+public class SystemBackendTest {
+
+    private SystemBackend b;
+
+    @Before
+    public void setUp() {
+        Storage s = mock(Storage.class);
+        CpuStatDAO cDAO = mock(CpuStatDAO.class);
+        HostInfoDAO hDAO = mock(HostInfoDAO.class);
+        MemoryStatDAO mDAO = mock(MemoryStatDAO.class);
+        VmCpuStatDAO vDAO = mock(VmCpuStatDAO.class);
+        NetworkInterfaceInfoDAO nDAO = mock(NetworkInterfaceInfoDAO.class);
+        DAOFactory df = mock(DAOFactory.class);
+        when(df.getStorage()).thenReturn(s);
+        when(df.getCpuStatDAO()).thenReturn(cDAO);
+        when(df.getHostInfoDAO()).thenReturn(hDAO);
+        when(df.getMemoryStatDAO()).thenReturn(mDAO);
+        when(df.getVmCpuStatDAO()).thenReturn(vDAO);
+        when(df.getNetworkInterfaceInfoDAO()).thenReturn(nDAO);
+        b = new SystemBackend();
+        b.setDAOFactory(df);
+    }
+
+    @Test
+    public void testBasicBackend() {
+        assertFalse(b.isActive());
+        b.activate();
+        assertTrue(b.isActive());
+        b.deactivate();
+        assertFalse(b.isActive());
+    }
+
+    @Test
+    public void testActivateTwice() {
+        b.activate();
+        b.activate();
+        assert(b.isActive());
+    }
+
+    @Test
+    public void testDeactiateWhenNotActive() {
+        b.deactivate();
+        b.deactivate();
+        assertFalse(b.isActive());
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/system-backend/src/test/java/com/redhat/thermostat/backend/system/VmCpuStatBuilderTest.java	Fri Oct 19 21:58:18 2012 +0200
@@ -0,0 +1,168 @@
+/*
+ * 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.common.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);
+        }
+    }
+}