changeset 912:6815308bb362

Initial NUMA agent. Reviewed-by: vanaltj Review-thread: http://icedtea.classpath.org/pipermail/thermostat/2013-January/005182.html
author Roman Kennke <rkennke@redhat.com>
date Tue, 15 Jan 2013 23:00:52 +0100
parents 6da66da9e8ac
children bf46bf74d05c
files agent/core/src/main/java/com/redhat/thermostat/backend/Backend.java distribution/config/commands/agent.properties distribution/pom.xml host-cpu/agent/src/main/java/com/redhat/thermostat/host/cpu/agent/internal/HostCpuBackend.java host-memory/agent/src/main/java/com/redhat/thermostat/host/memory/agent/internal/HostMemoryBackend.java numa/agent/pom.xml numa/agent/src/main/java/com/redhat/thermostat/numa/agent/internal/Activator.java numa/agent/src/main/java/com/redhat/thermostat/numa/agent/internal/NumaBackend.java numa/agent/src/main/java/com/redhat/thermostat/numa/agent/internal/NumaCollector.java numa/agent/src/main/java/com/redhat/thermostat/numa/agent/internal/NumaStatBuilder.java numa/agent/src/test/java/com/redhat/thermostat/numa/agent/internal/ActivatorTest.java numa/agent/src/test/java/com/redhat/thermostat/numa/agent/internal/NumaBackendTest.java numa/agent/src/test/java/com/redhat/thermostat/numa/agent/internal/NumaCollectorTest.java numa/agent/src/test/java/com/redhat/thermostat/numa/agent/internal/NumaStatBuilderTest.java numa/common/pom.xml numa/common/src/main/java/com/redhat/thermostat/numa/common/NumaDAO.java numa/common/src/main/java/com/redhat/thermostat/numa/common/NumaStat.java numa/common/src/main/java/com/redhat/thermostat/numa/common/internal/Activator.java numa/common/src/main/java/com/redhat/thermostat/numa/common/internal/NumaDAOImpl.java numa/common/src/test/java/com/redhat/thermostat/numa/common/NumaDAOTest.java numa/common/src/test/java/com/redhat/thermostat/numa/common/NumaStatTest.java numa/common/src/test/java/com/redhat/thermostat/numa/common/internal/ActivatorTest.java numa/common/src/test/java/com/redhat/thermostat/numa/common/internal/NumaDAOImplTest.java numa/pom.xml pom.xml vm-classstat/agent/src/main/java/com/redhat/thermostat/vm/classstat/agent/internal/VmClassStatBackend.java vm-cpu/agent/src/main/java/com/redhat/thermostat/vm/cpu/agent/internal/VmCpuBackend.java vm-gc/agent/src/main/java/com/redhat/thermostat/vm/gc/agent/internal/VmGcBackend.java vm-memory/agent/src/main/java/com/redhat/thermostat/vm/memory/agent/internal/VmMemoryBackend.java
diffstat 29 files changed, 1841 insertions(+), 60 deletions(-) [+]
line wrap: on
line diff
--- a/agent/core/src/main/java/com/redhat/thermostat/backend/Backend.java	Thu Jan 10 21:48:04 2013 +0100
+++ b/agent/core/src/main/java/com/redhat/thermostat/backend/Backend.java	Tue Jan 15 23:00:52 2013 +0100
@@ -55,9 +55,7 @@
     protected DAOFactory df = null;
     private boolean observeNewJvm = attachToNewProcessByDefault();
 
-    private String version;
-    private String vendor;
-    private String description;
+    private Map<String,String> config = new HashMap<>();
     
     private BackendID id;
 
@@ -92,7 +90,9 @@
         setDAOFactoryAction();
     }
 
-    protected abstract void setDAOFactoryAction();
+    protected void setDAOFactoryAction() {
+        // Default implementation does nothing.
+    }
 
     /**
      * Set the named configuration to the given value.
@@ -112,25 +112,10 @@
      *                                  for this backend or the value is not valid for the key
      */
     protected void setConfigurationValue(String name, String value) {
-        
-        if (name.equals(BackendsProperties.DESCRIPTION.name())) {
-            this.description = value;
-        } else if (name.equals(BackendsProperties.VERSION.name())) {
-            this.version = value;
-        } else if (name.equals(BackendsProperties.VENDOR.name())) {
-            this.vendor = value;
-        } else {
-            setConfigurationValueImpl(name, value);
-        }
+        config.put(name, value);
     }
     
     /**
-     * Set the named configuration to the given value.
-     * By default, does nothing.
-     */
-    protected void setConfigurationValueImpl(String name, String value) {}
-    
-    /**
      * @return the name of the {@link Backend}
      */
     public String getName() {
@@ -141,21 +126,21 @@
      * @returns the description of the {@link Backend}
      */
     public String getDescription() {
-        return description;
+        return config.get(BackendsProperties.DESCRIPTION.name());
     }
 
     /**
      * @return the vendor of the {@link Backend}
      */
     public String getVendor() {
-        return vendor;
+        return config.get(BackendsProperties.VENDOR.name());
     }
 
     /** 
      * @return the version of the {@link Backend}
      */
     public String getVersion() {
-        return version;
+        return config.get(BackendsProperties.VERSION.name());
     }
 
     /** Get a map containing the current settings of this backend.
@@ -176,7 +161,9 @@
      * @throws IllegalArgumentException if the key does not refer to a valid configuration option for
      *                                  this backend
      */
-    public abstract String getConfigurationValue(String key);
+    public String getConfigurationValue(String key) {
+        return config.get(key);
+    }
 
     /**
      * Activate the {@link Backend}.  Based on the current configuration,
@@ -246,6 +233,8 @@
     
     @Override
     public int hashCode() {
+        String vendor = getVendor();
+        String version = getVersion();
         final int prime = 31;
         int result = 1;
         result = prime * result + ((id == null) ? 0 : id.hashCode());
@@ -256,6 +245,8 @@
 
     @Override
     public boolean equals(Object obj) {
+        String vendor = getVendor();
+        String version = getVersion();
         if (this == obj)
             return true;
         if (obj == null)
@@ -269,21 +260,21 @@
         } else if (!id.equals(other.id))
             return false;
         if (vendor == null) {
-            if (other.vendor != null)
+            if (other.getVendor() != null)
                 return false;
-        } else if (!vendor.equals(other.vendor))
+        } else if (!vendor.equals(other.getVendor()))
             return false;
         if (version == null) {
-            if (other.version != null)
+            if (other.getVersion() != null)
                 return false;
-        } else if (!version.equals(other.version))
+        } else if (!version.equals(other.getVersion()))
             return false;
         return true;
     }
 
     @Override
     public String toString() {
-        return "Backend [version=" + version + ", vendor=" + vendor
-                + ", description=" + description + ", id=" + id + "]";
+        return "Backend [version=" + getVersion() + ", vendor=" + getVendor()
+                + ", description=" + getDescription() + ", id=" + id + "]";
     }
 }
--- a/distribution/config/commands/agent.properties	Thu Jan 10 21:48:04 2013 +0100
+++ b/distribution/config/commands/agent.properties	Tue Jan 15 23:00:52 2013 +0100
@@ -30,6 +30,8 @@
           thermostat-gc-remote-collector-common-@project.version@.jar, \
           thermostat-gc-remote-collector-command-@project.version@.jar, \
           thermostat-storage-mongodb-${project.version}.jar, \
+          thermostat-numa-common-${project.version}.jar, \
+          thermostat-numa-agent-${project.version}.jar, \
           mongo.jar, \
           commons-beanutils.jar, \
           commons-codec.jar, \
--- a/distribution/pom.xml	Thu Jan 10 21:48:04 2013 +0100
+++ b/distribution/pom.xml	Tue Jan 15 23:00:52 2013 +0100
@@ -586,5 +586,10 @@
         <version>${project.version}</version>
     </dependency>
          
+    <dependency>
+      <groupId>com.redhat.thermostat</groupId>
+      <artifactId>thermostat-numa-agent</artifactId>
+      <version>${project.version}</version>
+    </dependency>
   </dependencies>
 </project>
--- a/host-cpu/agent/src/main/java/com/redhat/thermostat/host/cpu/agent/internal/HostCpuBackend.java	Thu Jan 10 21:48:04 2013 +0100
+++ b/host-cpu/agent/src/main/java/com/redhat/thermostat/host/cpu/agent/internal/HostCpuBackend.java	Tue Jan 15 23:00:52 2013 +0100
@@ -104,11 +104,6 @@
     }
 
     @Override
-    protected void setDAOFactoryAction() {
-        // No use for DAOFactory
-    }
-
-    @Override
     public String getConfigurationValue(String key) {
         return null;
     }
--- a/host-memory/agent/src/main/java/com/redhat/thermostat/host/memory/agent/internal/HostMemoryBackend.java	Thu Jan 10 21:48:04 2013 +0100
+++ b/host-memory/agent/src/main/java/com/redhat/thermostat/host/memory/agent/internal/HostMemoryBackend.java	Tue Jan 15 23:00:52 2013 +0100
@@ -95,11 +95,6 @@
     }
 
     @Override
-    protected void setDAOFactoryAction() {
-        // No use for DAOFactory
-    }
-
-    @Override
     public String getConfigurationValue(String key) {
         return null;
     }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/numa/agent/pom.xml	Tue Jan 15 23:00:52 2013 +0100
@@ -0,0 +1,73 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <artifactId>thermostat-numa</artifactId>
+    <groupId>com.redhat.thermostat</groupId>
+    <version>0.5.0-SNAPSHOT</version>
+  </parent>
+  <artifactId>thermostat-numa-agent</artifactId>
+  <packaging>bundle</packaging>
+  <name>Thermostat NUMA Agent plugin</name>
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.felix</groupId>
+        <artifactId>maven-bundle-plugin</artifactId>
+        <extensions>true</extensions>
+        <configuration>
+          <instructions>
+            <Bundle-Vendor>Red Hat, Inc.</Bundle-Vendor>
+            <Bundle-SymbolicName>com.redhat.thermostat.numa.agent</Bundle-SymbolicName>
+            <Bundle-Activator>com.redhat.thermostat.numa.agent.internal.Activator</Bundle-Activator>
+            <Export-Package>
+              com.redhat.thermostat.numa.agent
+            </Export-Package>
+            <Private-Package>
+              com.redhat.thermostat.numa.agent.internal
+            </Private-Package>
+            <!-- Do not autogenerate uses clauses in Manifests -->
+            <_nouses>true</_nouses>
+          </instructions>
+        </configuration>
+      </plugin>
+    </plugins>
+  </build>
+  <dependencies>
+    <dependency>
+      <groupId>junit</groupId>
+      <artifactId>junit</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.mockito</groupId>
+      <artifactId>mockito-core</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.osgi</groupId>
+      <artifactId>org.osgi.core</artifactId>
+      <scope>provided</scope>
+    </dependency>
+    <dependency>
+      <groupId>com.redhat.thermostat</groupId>
+      <artifactId>thermostat-common-core</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>com.redhat.thermostat</groupId>
+      <artifactId>thermostat-numa-common</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>com.redhat.thermostat</groupId>
+      <artifactId>thermostat-agent-core</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>com.redhat.thermostat</groupId>
+      <artifactId>thermostat-storage-core</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+  </dependencies>
+</project>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/numa/agent/src/main/java/com/redhat/thermostat/numa/agent/internal/Activator.java	Tue Jan 15 23:00:52 2013 +0100
@@ -0,0 +1,99 @@
+/*
+ * Copyright 2013 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.numa.agent.internal;
+
+import java.util.Map;
+
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceRegistration;
+
+import com.redhat.thermostat.backend.Backend;
+import com.redhat.thermostat.backend.BackendService;
+import com.redhat.thermostat.common.ApplicationService;
+import com.redhat.thermostat.common.MultipleServiceTracker;
+import com.redhat.thermostat.common.MultipleServiceTracker.Action;
+import com.redhat.thermostat.common.Version;
+import com.redhat.thermostat.numa.common.NumaDAO;
+
+public class Activator implements BundleActivator {
+    
+    private MultipleServiceTracker tracker;
+    private NumaBackend backend;
+    private ServiceRegistration reg;
+    
+    @Override
+    public void start(final BundleContext context) throws Exception {
+
+        Class<?>[] deps = new Class<?>[] {
+                BackendService.class, NumaDAO.class, ApplicationService.class
+        };
+        tracker = new MultipleServiceTracker(context, deps, new Action() {
+            
+            @Override
+            public void dependenciesAvailable(Map<String, Object> services) {
+                ApplicationService appService = (ApplicationService) services.get(ApplicationService.class.getName());
+                NumaDAO numaDAO = (NumaDAO) services.get(NumaDAO.class.getName());
+                Version version = new Version(context.getBundle());
+                NumaCollector collector = new NumaCollector();
+                backend = new NumaBackend(appService, numaDAO, collector, version);
+                reg = context.registerService(Backend.class.getName(), backend, null);
+            }
+
+            @Override
+            public void dependenciesUnavailable() {
+                if (backend.isActive()) {
+                    backend.deactivate();
+                }
+                reg.unregister();
+            }
+        });
+        tracker.open();
+    }
+
+    @Override
+    public void stop(BundleContext context) throws Exception {
+        tracker.close();
+    }
+    
+    /*
+     * For testing purposes only.
+     */
+    NumaBackend getBackend() {
+        return backend;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/numa/agent/src/main/java/com/redhat/thermostat/numa/agent/internal/NumaBackend.java	Tue Jan 15 23:00:52 2013 +0100
@@ -0,0 +1,130 @@
+/*
+ * Copyright 2013 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.numa.agent.internal;
+
+import java.io.IOException;
+import java.util.concurrent.TimeUnit;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import com.redhat.thermostat.backend.Backend;
+import com.redhat.thermostat.backend.BackendID;
+import com.redhat.thermostat.backend.BackendsProperties;
+import com.redhat.thermostat.common.ApplicationService;
+import com.redhat.thermostat.common.Timer;
+import com.redhat.thermostat.common.Timer.SchedulingType;
+import com.redhat.thermostat.common.TimerFactory;
+import com.redhat.thermostat.common.Version;
+import com.redhat.thermostat.numa.common.NumaDAO;
+import com.redhat.thermostat.numa.common.NumaStat;
+
+public class NumaBackend extends Backend {
+
+    private static final Logger log = Logger.getLogger(NumaBackend.class.getName());
+
+    private static final long NUMA_CHECK_INTERVAL_SECONDS = 1;
+    
+    private NumaDAO numaDAO;
+    private ApplicationService appService;
+    private boolean started;
+    private NumaCollector numaCollector;
+    private Timer timer;
+
+    public NumaBackend(ApplicationService appService, NumaDAO numaDAO, NumaCollector numaCollector, Version version) {
+        super(new BackendID("NUMA Backend", NumaBackend.class.getName()));
+        this.appService = appService;
+        this.numaDAO = numaDAO;
+        this.numaCollector = numaCollector;
+        
+        setConfigurationValue(BackendsProperties.VENDOR.name(), "Red Hat, Inc.");
+        setConfigurationValue(BackendsProperties.DESCRIPTION.name(), "Gathers NUMA statistics about a host");
+        setConfigurationValue(BackendsProperties.VERSION.name(), version.getVersionNumber());
+        
+    }
+
+    @Override
+    public boolean activate() {
+        TimerFactory timerFactory = appService.getTimerFactory();
+        timer = timerFactory.createTimer();
+        timer.setDelay(NUMA_CHECK_INTERVAL_SECONDS);
+        timer.setInitialDelay(0);
+        timer.setSchedulingType(SchedulingType.FIXED_RATE);
+        timer.setTimeUnit(TimeUnit.SECONDS);
+        timer.setAction(new Runnable() {
+
+            @Override
+            public void run() {
+                NumaStat[] stats;
+                try {
+                    stats = numaCollector.collectData();
+                    for (NumaStat stat : stats) {
+                        numaDAO.putNumaStat(stat);
+                    }
+                } catch (IOException e) {
+                    log.log(Level.WARNING, e.getMessage(), e);
+                }
+            }
+        });
+        timer.start();
+        started = true;
+
+        return true;
+    }
+
+    @Override
+    public boolean deactivate() {
+        started = false;
+        timer.stop();
+        return true;
+    }
+    
+    @Override
+    public boolean isActive() {
+        return started;
+    }
+
+    @Override
+    public boolean attachToNewProcessByDefault() {
+        return false;
+    }
+
+    @Override
+    public int getOrderValue() {
+        return ORDER_CPU_GROUP;
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/numa/agent/src/main/java/com/redhat/thermostat/numa/agent/internal/NumaCollector.java	Tue Jan 15 23:00:52 2013 +0100
@@ -0,0 +1,97 @@
+/*
+ * Copyright 2013 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+
+package com.redhat.thermostat.numa.agent.internal;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FilenameFilter;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.Reader;
+
+import com.redhat.thermostat.numa.common.NumaStat;
+
+class NumaCollector {
+
+    private static final String NUMA_BASE_DIR = "/sys/devices/system/node";
+
+    private static final String NODE_DIR_PREFIX = "node";
+
+    private static final String NUMA_STAT_FILE = "numastat";
+
+    private int numberOfNodes;
+
+    private File baseDir;
+
+    NumaCollector() {
+        this(NUMA_BASE_DIR);
+    }
+
+    NumaCollector(String baseDirectory) {
+        baseDir = new File(baseDirectory);
+        FilenameFilter filter = new FilenameFilter() {
+            
+            @Override
+            public boolean accept(File dir, String name) {
+                return name.startsWith(NODE_DIR_PREFIX);
+            }
+        };
+        String[] nodeFiles = baseDir.list(filter);
+        numberOfNodes = nodeFiles.length;
+    }
+
+    NumaStat[] collectData() throws IOException {
+        NumaStat[] stat = new NumaStat[numberOfNodes];
+        for (int i = 0; i < numberOfNodes; i++) {
+            File nodeDir = new File(baseDir, NODE_DIR_PREFIX + i);
+            File numaStatFile = new File(nodeDir, NUMA_STAT_FILE);
+            try (FileInputStream in = new FileInputStream(numaStatFile)) {
+                Reader reader = new InputStreamReader(in);
+                NumaStatBuilder builder = new NumaStatBuilder(reader);
+                stat[i] = builder.build();
+                stat[i].setNode(i);
+            }
+        }
+        return stat;
+    }
+
+    // This is here for testing.
+    String getBaseDir() {
+        return baseDir.getAbsolutePath();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/numa/agent/src/main/java/com/redhat/thermostat/numa/agent/internal/NumaStatBuilder.java	Tue Jan 15 23:00:52 2013 +0100
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2013 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.numa.agent.internal;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.Reader;
+
+import com.redhat.thermostat.numa.common.NumaStat;
+
+class NumaStatBuilder {
+
+    private Reader in;
+
+    NumaStatBuilder(Reader in) {
+        this.in = in;
+    }
+
+    NumaStat build() throws IOException {
+        NumaStat numaStat = new NumaStat();
+        readNumaData(numaStat);
+        return numaStat;
+    }
+
+    private void readNumaData(NumaStat numaStat) throws IOException {
+        BufferedReader bufIn = new BufferedReader(in);
+        String line = bufIn.readLine();
+        while (line != null) {
+            String[] keyValue = line.split(" ");
+            String key = keyValue[0];
+            long value = Long.parseLong(keyValue[1]);
+            if (key.equals("numa_hit")) {
+                numaStat.setNumaHit(value);
+            } else if (key.equals("numa_miss")) {
+                numaStat.setNumaMiss(value);
+            } else if (key.equals("numa_foreign")) {
+                numaStat.setNumaForeign(value);
+            } else if (key.equals("interleave_hit")) {
+                numaStat.setInterleaveHit(value);
+            } else if (key.equals("local_node")) {
+                numaStat.setLocalNode(value);
+            } else if (key.equals("other_node")) {
+                numaStat.setOtherNode(value);
+            }
+            line = bufIn.readLine();
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/numa/agent/src/test/java/com/redhat/thermostat/numa/agent/internal/ActivatorTest.java	Tue Jan 15 23:00:52 2013 +0100
@@ -0,0 +1,144 @@
+/*
+ * Copyright 2013 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */ 
+
+package com.redhat.thermostat.numa.agent.internal;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.Version;
+
+import com.redhat.thermostat.backend.Backend;
+import com.redhat.thermostat.backend.BackendService;
+import com.redhat.thermostat.common.ApplicationService;
+import com.redhat.thermostat.common.Timer;
+import com.redhat.thermostat.common.TimerFactory;
+import com.redhat.thermostat.numa.common.NumaDAO;
+import com.redhat.thermostat.test.StubBundleContext;
+
+public class ActivatorTest {
+    
+    private StubBundleContext context;
+
+    private BackendService backendService;
+    private NumaDAO numaDAO;
+    private ApplicationService appService;
+
+    @Before
+    public void setUp() {
+        context = new StubBundleContext() {
+            @Override
+            public Bundle getBundle() {
+                Bundle result = mock(Bundle.class);
+                when(result.getVersion()).thenReturn(Version.emptyVersion);
+                return result;
+            }
+        };
+        
+        backendService = mock(BackendService.class);
+        numaDAO = mock(NumaDAO.class);
+        appService = mock(ApplicationService.class);
+        TimerFactory timerFactory = mock(TimerFactory.class);
+        Timer timer = mock(Timer.class);
+        when(timerFactory.createTimer()).thenReturn(timer);
+        when(appService.getTimerFactory()).thenReturn(timerFactory);
+    }
+
+    @Test
+    public void verifyActivatorDoesNotRegisterServiceOnMissingDeps() throws Exception {
+        StubBundleContext context = new StubBundleContext();
+
+        Activator activator = new Activator();
+
+        activator.start(context);
+
+        assertEquals(0, context.getAllServices().size());
+        assertEquals(3, context.getServiceListeners().size());
+
+        activator.stop(context);
+    }
+
+    @Test
+    public void verifyActivatorRegistersServicesWithActivate() throws Exception {
+
+        Activator activator = verifyActivatorRegistersServiceCommon();
+
+        NumaBackend backend = activator.getBackend();
+        backend.activate();
+
+        verifyActivatorUnregistersServiceCommon(activator, backend);
+    }
+
+    @Test
+    public void verifyActivatorRegistersServicesWithoutActivate() throws Exception {
+
+        Activator activator = verifyActivatorRegistersServiceCommon();
+
+        NumaBackend backend = activator.getBackend();
+
+        verifyActivatorUnregistersServiceCommon(activator, backend);
+    }
+
+    private Activator verifyActivatorRegistersServiceCommon() throws Exception {
+        context.registerService(BackendService.class, backendService, null);
+        context.registerService(NumaDAO.class, numaDAO, null);
+        context.registerService(ApplicationService.class, appService, null);
+
+        Activator activator = new Activator();
+
+        activator.start(context);
+
+        assertTrue(context.isServiceRegistered(Backend.class.getName(), NumaBackend.class));
+        assertNotNull(activator.getBackend());
+        return activator;
+    }
+
+    private void verifyActivatorUnregistersServiceCommon(Activator activator, NumaBackend backend) throws Exception {
+        activator.stop(context);
+        
+        assertFalse(backend.isActive());
+
+        assertEquals(0, context.getServiceListeners().size());
+        assertEquals(3, context.getAllServices().size());
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/numa/agent/src/test/java/com/redhat/thermostat/numa/agent/internal/NumaBackendTest.java	Tue Jan 15 23:00:52 2013 +0100
@@ -0,0 +1,149 @@
+/*
+ * Copyright 2013 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.numa.agent.internal;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.when;
+
+import java.io.IOException;
+import java.util.concurrent.TimeUnit;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+
+import com.redhat.thermostat.common.ApplicationService;
+import com.redhat.thermostat.common.Ordered;
+import com.redhat.thermostat.common.Timer;
+import com.redhat.thermostat.common.TimerFactory;
+import com.redhat.thermostat.common.Version;
+import com.redhat.thermostat.numa.common.NumaDAO;
+import com.redhat.thermostat.numa.common.NumaStat;
+
+public class NumaBackendTest {
+    
+    private NumaBackend backend;
+    private ApplicationService appService;
+    private NumaDAO numaDAO;
+    private NumaCollector collector;
+    private Timer timer;
+
+    @Before
+    public void setup() {
+        appService = mock(ApplicationService.class);
+        TimerFactory timerFactory = mock(TimerFactory.class);
+        timer = mock(Timer.class);        
+        when(timerFactory.createTimer()).thenReturn(timer);
+        when(appService.getTimerFactory()).thenReturn(timerFactory);
+        collector = mock(NumaCollector.class);
+        Version version = mock(Version.class);
+        when(version.getVersionNumber()).thenReturn("0.0.0");
+        numaDAO = mock(NumaDAO.class);
+        backend = new NumaBackend(appService, numaDAO, collector, version);
+    }
+
+    @After
+    public void tearDown() {
+        backend = null;
+        collector = null;
+        timer = null;
+        appService = null;
+        numaDAO = null;
+    }
+
+    @Test
+    public void testActivate() throws IOException {
+        ArgumentCaptor<Runnable> actionCaptor = ArgumentCaptor.forClass(Runnable.class);
+        doNothing().when(timer).setAction(actionCaptor.capture());
+        NumaStat stat1 = mock(NumaStat.class);
+        NumaStat stat2 = mock(NumaStat.class);
+        NumaStat[] stats = new NumaStat[] { stat1, stat2 };
+        when(collector.collectData()).thenReturn(stats);
+
+        boolean activated = backend.activate();
+        assertTrue(activated);
+        assertTrue(backend.isActive());
+        verify(timer).setDelay(1);
+        verify(timer).setInitialDelay(1);
+        verify(timer).setTimeUnit(TimeUnit.SECONDS);
+        verify(timer).setSchedulingType(Timer.SchedulingType.FIXED_RATE);
+        verify(timer).start();
+        verify(timer).setAction(any(Runnable.class));
+        verifyNoMoreInteractions(timer);
+
+        Runnable action = actionCaptor.getValue();
+        verifyZeroInteractions(numaDAO);
+        verifyZeroInteractions(collector);
+
+        action.run();
+        verify(collector).collectData();
+        verify(numaDAO).putNumaStat(stat1);
+        verify(numaDAO).putNumaStat(stat2);
+        verifyNoMoreInteractions(numaDAO);
+        verifyNoMoreInteractions(collector);
+
+        when(collector.collectData()).thenThrow(new IOException());
+        action.run();
+        verify(collector, times(2)).collectData();
+        verifyNoMoreInteractions(collector);
+        verifyNoMoreInteractions(numaDAO);
+
+        boolean deactivated = backend.deactivate();
+        assertTrue(deactivated);
+        verify(timer).stop();
+    }
+
+    @Test
+    public void testAttachToNewProcessByDefault() {
+        assertFalse(backend.attachToNewProcessByDefault());
+    }
+
+    @Test
+    public void testOrderValue() {
+        assertEquals(Ordered.ORDER_CPU_GROUP, backend.getOrderValue());
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/numa/agent/src/test/java/com/redhat/thermostat/numa/agent/internal/NumaCollectorTest.java	Tue Jan 15 23:00:52 2013 +0100
@@ -0,0 +1,166 @@
+/*
+ * Copyright 2013 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+
+package com.redhat.thermostat.numa.agent.internal;
+
+import static org.junit.Assert.assertEquals;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.nio.file.FileVisitResult;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.SimpleFileVisitor;
+import java.nio.file.attribute.BasicFileAttributes;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import com.redhat.thermostat.numa.common.NumaStat;
+
+public class NumaCollectorTest {
+
+    private static final String TEST_STAT0 = "numa_hit 11\n" +
+            "numa_miss 12\n" +
+            "numa_foreign 13\n" +
+            "interleave_hit 14\n" +
+            "local_node 15\n" +
+            "other_node 16\n";
+
+    private static final String TEST_STAT1 = "numa_hit 21\n" +
+            "numa_miss 22\n" +
+            "numa_foreign 23\n" +
+            "interleave_hit 24\n" +
+            "local_node 25\n" +
+            "other_node 26\n";
+
+    private static final String TEST_STAT2 = "numa_hit 31\n" +
+            "numa_miss 32\n" +
+            "numa_foreign 33\n" +
+            "interleave_hit 34\n" +
+            "local_node 35\n" +
+            "other_node 36\n";
+
+    private File tmpDir;
+
+    @Before
+    public void setUp() throws IOException {
+        tmpDir = Files.createTempDirectory("numa-test").toFile();
+
+        File nodeDir0 = new File(tmpDir, "node0");
+        nodeDir0.mkdir();
+        File numaStatFile0 = new File(nodeDir0, "numastat");
+        FileOutputStream out0 = new FileOutputStream(numaStatFile0);
+        out0.write(TEST_STAT0.getBytes());
+        out0.close();
+
+        File nodeDir1 = new File(tmpDir, "node1");
+        nodeDir1.mkdir();
+        File numaStatFile1 = new File(nodeDir1, "numastat");
+        FileOutputStream out1 = new FileOutputStream(numaStatFile1);
+        out1.write(TEST_STAT1.getBytes());
+        out1.close();
+
+        File nodeDir2 = new File(tmpDir, "node2");
+        nodeDir2.mkdir();
+        File numaStatFile2 = new File(nodeDir2, "numastat");
+        FileOutputStream out2 = new FileOutputStream(numaStatFile2);
+        out2.write(TEST_STAT2.getBytes());
+        out2.close();
+
+        File fluff = new File(tmpDir, "fluff");
+        fluff.mkdir();
+
+    }
+
+    @After
+    public void tearDown() throws IOException {
+        Files.walkFileTree(tmpDir.toPath(), new SimpleFileVisitor<Path>() {
+            @Override
+            public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
+                Files.delete(file);
+                return super.visitFile(file, attrs);
+            }
+            @Override
+            public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
+                Files.delete(dir);
+                return super.postVisitDirectory(dir, exc);
+            }
+        });
+    }
+
+    @Test
+    public void testStats() throws IOException {
+        NumaCollector coll = new NumaCollector(tmpDir.getAbsolutePath());
+        NumaStat[] stats = coll.collectData();
+
+        assertEquals(3, stats.length);
+
+        assertEquals(0, stats[0].getNode());
+        assertEquals(11, stats[0].getNumaHit());
+        assertEquals(12, stats[0].getNumaMiss());
+        assertEquals(13, stats[0].getNumaForeign());
+        assertEquals(14, stats[0].getInterleaveHit());
+        assertEquals(15, stats[0].getLocalNode());
+        assertEquals(16, stats[0].getOtherNode());
+
+        assertEquals(1, stats[1].getNode());
+        assertEquals(21, stats[1].getNumaHit());
+        assertEquals(22, stats[1].getNumaMiss());
+        assertEquals(23, stats[1].getNumaForeign());
+        assertEquals(24, stats[1].getInterleaveHit());
+        assertEquals(25, stats[1].getLocalNode());
+        assertEquals(26, stats[1].getOtherNode());
+
+        assertEquals(2, stats[2].getNode());
+        assertEquals(31, stats[2].getNumaHit());
+        assertEquals(32, stats[2].getNumaMiss());
+        assertEquals(33, stats[2].getNumaForeign());
+        assertEquals(34, stats[2].getInterleaveHit());
+        assertEquals(35, stats[2].getLocalNode());
+        assertEquals(36, stats[2].getOtherNode());
+
+    }
+
+    @Test
+    public void testDefaultDir() {
+        NumaCollector coll = new NumaCollector();
+        assertEquals("/sys/devices/system/node", coll.getBaseDir());
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/numa/agent/src/test/java/com/redhat/thermostat/numa/agent/internal/NumaStatBuilderTest.java	Tue Jan 15 23:00:52 2013 +0100
@@ -0,0 +1,71 @@
+/*
+ * Copyright 2013 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.numa.agent.internal;
+
+import static org.junit.Assert.assertEquals;
+
+import java.io.IOException;
+import java.io.Reader;
+import java.io.StringReader;
+
+import org.junit.Test;
+
+import com.redhat.thermostat.numa.common.NumaStat;
+
+public class NumaStatBuilderTest {
+
+    private static final String TEST_STAT = "numa_hit 32931\n" +
+    		"numa_miss 819\n" +
+    		"numa_foreign 918\n" +
+    		"interleave_hit 5704\n" +
+    		"local_node 3293\n" +
+    		"other_node 1819\n";
+
+    @Test
+    public void test() throws IOException {
+        Reader in = new StringReader(TEST_STAT);
+        NumaStatBuilder builder = new NumaStatBuilder(in);
+        NumaStat stat = builder.build();
+        assertEquals(32931L, stat.getNumaHit());
+        assertEquals(819L, stat.getNumaMiss());
+        assertEquals(918L, stat.getNumaForeign());
+        assertEquals(5704L, stat.getInterleaveHit());
+        assertEquals(3293L, stat.getLocalNode());
+        assertEquals(1819L, stat.getOtherNode());
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/numa/common/pom.xml	Tue Jan 15 23:00:52 2013 +0100
@@ -0,0 +1,68 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <artifactId>thermostat-numa</artifactId>
+    <groupId>com.redhat.thermostat</groupId>
+    <version>0.5.0-SNAPSHOT</version>
+  </parent>
+  <artifactId>thermostat-numa-common</artifactId>
+  <packaging>bundle</packaging>
+  <name>Thermostat NUMA Common plugin</name>
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.felix</groupId>
+        <artifactId>maven-bundle-plugin</artifactId>
+        <extensions>true</extensions>
+        <configuration>
+          <instructions>
+            <Bundle-Vendor>Red Hat, Inc.</Bundle-Vendor>
+            <Bundle-SymbolicName>com.redhat.thermostat.numa.common</Bundle-SymbolicName>
+            <Bundle-Activator>com.redhat.thermostat.numa.common.internal.Activator</Bundle-Activator>
+            <Export-Package>
+              com.redhat.thermostat.numa.common
+            </Export-Package>
+            <Private-Package>
+              com.redhat.thermostat.numa.common.internal
+            </Private-Package>
+            <!-- Do not autogenerate uses clauses in Manifests -->
+            <_nouses>true</_nouses>
+          </instructions>
+        </configuration>
+      </plugin>
+    </plugins>
+  </build>
+  <dependencies>
+    <dependency>
+      <groupId>junit</groupId>
+      <artifactId>junit</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.mockito</groupId>
+      <artifactId>mockito-core</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.osgi</groupId>
+      <artifactId>org.osgi.core</artifactId>
+      <scope>provided</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.osgi</groupId>
+      <artifactId>org.osgi.compendium</artifactId>
+      <scope>provided</scope>
+    </dependency>
+    <dependency>
+      <groupId>com.redhat.thermostat</groupId>
+      <artifactId>thermostat-common-core</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>com.redhat.thermostat</groupId>
+      <artifactId>thermostat-storage-core</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+  </dependencies>
+</project>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/numa/common/src/main/java/com/redhat/thermostat/numa/common/NumaDAO.java	Tue Jan 15 23:00:52 2013 +0100
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2013 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.numa.common;
+
+import com.redhat.thermostat.storage.core.Category;
+import com.redhat.thermostat.storage.core.Key;
+
+public interface NumaDAO {
+
+    static final Key<Integer> nodeKey = new Key<>("node", true);
+    static final Key<Long> numaHitKey = new Key<>("numaHit", false);
+    static final Key<Long> numaMissKey = new Key<>("numaMiss", false);
+    static final Key<Long> numaForeignKey = new Key<>("numaForeign", false);
+    static final Key<Long> interleaveHitKey = new Key<>("interleaveHit", false);
+    static final Key<Long> localNodeKey = new Key<>("localNode", false);
+    static final Key<Long> otherNodeKey = new Key<>("otherNode", false);
+    
+    static final Category<NumaStat> numaStatCategory = new Category<>("numa-stat", NumaStat.class, Key.AGENT_ID, Key.TIMESTAMP,
+            nodeKey, numaHitKey, numaMissKey, numaForeignKey, interleaveHitKey, localNodeKey, otherNodeKey);
+
+    void putNumaStat(NumaStat stat);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/numa/common/src/main/java/com/redhat/thermostat/numa/common/NumaStat.java	Tue Jan 15 23:00:52 2013 +0100
@@ -0,0 +1,110 @@
+/*
+ * Copyright 2013 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.numa.common;
+
+import com.redhat.thermostat.storage.model.BasePojo;
+
+public class NumaStat extends BasePojo {
+
+    private long numaHit = -1;
+    private long numaMiss = -1;
+    private long numaForeign = -1;
+    private long interleaveHit = -1;
+    private long localNode = -1;
+    private long otherNode = -1;
+    private int node = -1;
+
+    public long getNumaHit() {
+        return numaHit;
+    }
+
+    public void setNumaHit(long numaHit) {
+        this.numaHit = numaHit;
+    }
+
+    public long getNumaMiss() {
+        return numaMiss;
+    }
+
+    public void setNumaMiss(long numaMiss) {
+        this.numaMiss = numaMiss;
+    }
+
+    public long getNumaForeign() {
+        return numaForeign;
+    }
+
+    public void setNumaForeign(long numaForeign) {
+        this.numaForeign = numaForeign;
+    }
+
+    public long getInterleaveHit() {
+        return interleaveHit;
+    }
+
+    public void setInterleaveHit(long interleaveHit) {
+        this.interleaveHit = interleaveHit;
+    }
+
+    public long getLocalNode() {
+        return localNode;
+    }
+
+    public void setLocalNode(long localNode) {
+        this.localNode = localNode;
+    }
+
+    public long getOtherNode() {
+        return otherNode;
+    }
+
+    public void setOtherNode(long otherNode) {
+        this.otherNode = otherNode;
+    }
+
+    public int getNode() {
+        return node;
+    }
+
+    public void setNode(int node) {
+        this.node = node;
+    }
+
+    public String toString() {
+        return "NumaStat: node: " + node + ", numaHit: " + numaHit + ", numaMiss: " + numaMiss + ", numaForeign: " + numaForeign + ", interleaveHit: " + interleaveHit + ", localNode: " + localNode + ", otherNode: " + otherNode;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/numa/common/src/main/java/com/redhat/thermostat/numa/common/internal/Activator.java	Tue Jan 15 23:00:52 2013 +0100
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2013 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.numa.common.internal;
+
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+import org.osgi.framework.ServiceRegistration;
+import org.osgi.util.tracker.ServiceTracker;
+
+import com.redhat.thermostat.numa.common.NumaDAO;
+import com.redhat.thermostat.storage.core.Storage;
+
+public class Activator implements BundleActivator {
+    
+    private ServiceTracker tracker;
+    private ServiceRegistration reg;
+
+    @Override
+    public void start(BundleContext context) throws Exception {
+        tracker = new ServiceTracker(context, Storage.class.getName(), null) {
+            @Override
+            public Object addingService(ServiceReference reference) {
+                Storage storage = (Storage) context.getService(reference);
+                NumaDAO memoryStatDao = new NumaDAOImpl(storage);
+                reg = context.registerService(NumaDAO.class.getName(), memoryStatDao, null);
+                return super.addingService(reference);
+            }
+            
+            @Override
+            public void removedService(ServiceReference reference,
+                    Object service) {
+                reg.unregister();
+                super.removedService(reference, service);
+            }
+        };
+        tracker.open();
+    }
+
+    @Override
+    public void stop(BundleContext context) throws Exception {
+        tracker.close();
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/numa/common/src/main/java/com/redhat/thermostat/numa/common/internal/NumaDAOImpl.java	Tue Jan 15 23:00:52 2013 +0100
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2013 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.numa.common.internal;
+
+import com.redhat.thermostat.numa.common.NumaDAO;
+import com.redhat.thermostat.numa.common.NumaStat;
+import com.redhat.thermostat.storage.core.Put;
+import com.redhat.thermostat.storage.core.Storage;
+
+public class NumaDAOImpl implements NumaDAO {
+
+    private final Storage storage;
+
+    NumaDAOImpl(Storage storage) {
+        this.storage = storage;
+        storage.registerCategory(numaStatCategory);
+    }
+
+    @Override
+    public void putNumaStat(NumaStat stat) {
+        Put add = storage.createAdd(numaStatCategory);
+        add.setPojo(stat);
+        add.apply();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/numa/common/src/test/java/com/redhat/thermostat/numa/common/NumaDAOTest.java	Tue Jan 15 23:00:52 2013 +0100
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2013 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+
+package com.redhat.thermostat.numa.common;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import com.redhat.thermostat.storage.core.Category;
+
+public class NumaDAOTest {
+
+    @Test
+    public void testCategory() {
+        Category<?> cat = NumaDAO.numaStatCategory;
+        assertEquals("numa-stat", cat.getName());
+        assertEquals(NumaStat.class, cat.getDataClass());
+        assertEquals(9, cat.getKeys().size());
+
+        assertEquals("agentId", cat.getKey("agentId").getName());
+        assertTrue(cat.getKey("agentId").isPartialCategoryKey());
+        assertEquals("timeStamp", cat.getKey("timeStamp").getName());
+        assertFalse(cat.getKey("timeStamp").isPartialCategoryKey());
+
+        assertEquals("node", cat.getKey("node").getName());
+        assertTrue(cat.getKey("node").isPartialCategoryKey());
+
+        assertEquals("numaHit", cat.getKey("numaHit").getName());
+        assertFalse(cat.getKey("numaHit").isPartialCategoryKey());
+        assertEquals("numaMiss", cat.getKey("numaMiss").getName());
+        assertFalse(cat.getKey("numaMiss").isPartialCategoryKey());
+        assertEquals("numaForeign", cat.getKey("numaForeign").getName());
+        assertFalse(cat.getKey("numaForeign").isPartialCategoryKey());
+        assertEquals("interleaveHit", cat.getKey("interleaveHit").getName());
+        assertFalse(cat.getKey("interleaveHit").isPartialCategoryKey());
+        assertEquals("localNode", cat.getKey("localNode").getName());
+        assertFalse(cat.getKey("localNode").isPartialCategoryKey());
+        assertEquals("otherNode", cat.getKey("otherNode").getName());
+        assertFalse(cat.getKey("otherNode").isPartialCategoryKey());
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/numa/common/src/test/java/com/redhat/thermostat/numa/common/NumaStatTest.java	Tue Jan 15 23:00:52 2013 +0100
@@ -0,0 +1,107 @@
+/*
+ * Copyright 2013 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+
+package com.redhat.thermostat.numa.common;
+
+import static org.junit.Assert.assertEquals;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+public class NumaStatTest {
+
+    private NumaStat stat;
+
+    @Before
+    public void setUp() {
+        stat = new NumaStat();
+        stat.setNode(1);
+        stat.setNumaHit(2);
+        stat.setNumaMiss(3);
+        stat.setNumaForeign(4);
+        stat.setInterleaveHit(5);
+        stat.setLocalNode(6);
+        stat.setOtherNode(7);
+    }
+
+    @After
+    public void tearDown() {
+        stat = null;
+    }
+
+    @Test
+    public void testDefaults() {
+        NumaStat stat = new NumaStat();
+        assertEquals(-1, stat.getNode());
+        assertEquals(-1, stat.getNumaHit());
+        assertEquals(-1, stat.getNumaMiss());
+        assertEquals(-1, stat.getNumaForeign());
+        assertEquals(-1, stat.getInterleaveHit());
+        assertEquals(-1, stat.getLocalNode());
+        assertEquals(-1, stat.getOtherNode());
+    }
+
+    @Test
+    public void testProperties() {
+
+        assertEquals(1, stat.getNode());
+        assertEquals(2, stat.getNumaHit());
+        assertEquals(3, stat.getNumaMiss());
+        assertEquals(4, stat.getNumaForeign());
+        assertEquals(5, stat.getInterleaveHit());
+        assertEquals(6, stat.getLocalNode());
+        assertEquals(7, stat.getOtherNode());
+        
+    }
+
+    @Test
+    public void testToString() {
+        NumaStat stat = new NumaStat();
+        stat.setNode(1);
+        stat.setNumaHit(2);
+        stat.setNumaMiss(3);
+        stat.setNumaForeign(4);
+        stat.setInterleaveHit(5);
+        stat.setLocalNode(6);
+        stat.setOtherNode(7);
+
+        String str = stat.toString();
+        String expected = "NumaStat: node: 1, numaHit: 2, numaMiss: 3, numaForeign: 4, interleaveHit: 5, localNode: 6, otherNode: 7";
+        assertEquals(expected, str);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/numa/common/src/test/java/com/redhat/thermostat/numa/common/internal/ActivatorTest.java	Tue Jan 15 23:00:52 2013 +0100
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2013 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */ 
+
+package com.redhat.thermostat.numa.common.internal;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
+
+import org.junit.Test;
+
+import com.redhat.thermostat.numa.common.NumaDAO;
+import com.redhat.thermostat.numa.common.internal.Activator;
+import com.redhat.thermostat.numa.common.internal.NumaDAOImpl;
+import com.redhat.thermostat.storage.core.Storage;
+import com.redhat.thermostat.test.StubBundleContext;
+
+public class ActivatorTest {
+    
+    @Test
+    public void verifyActivatorDoesNotRegisterServiceOnMissingDeps() throws Exception {
+        StubBundleContext context = new StubBundleContext();
+
+        Activator activator = new Activator();
+
+        activator.start(context);
+
+        assertEquals(0, context.getAllServices().size());
+        assertEquals(1, context.getServiceListeners().size());
+
+        activator.stop(context);
+    }
+
+    @Test
+    public void verifyActivatorRegistersServices() throws Exception {
+        StubBundleContext context = new StubBundleContext();
+        Storage storage = mock(Storage.class);
+
+        context.registerService(Storage.class, storage, null);
+
+        Activator activator = new Activator();
+
+        activator.start(context);
+
+        assertTrue(context.isServiceRegistered(NumaDAO.class.getName(), NumaDAOImpl.class));
+
+        activator.stop(context);
+
+        assertEquals(0, context.getServiceListeners().size());
+        assertEquals(1, context.getAllServices().size());
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/numa/common/src/test/java/com/redhat/thermostat/numa/common/internal/NumaDAOImplTest.java	Tue Jan 15 23:00:52 2013 +0100
@@ -0,0 +1,100 @@
+/*
+ * Copyright 2013 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+
+package com.redhat.thermostat.numa.common.internal;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import com.redhat.thermostat.numa.common.NumaDAO;
+import com.redhat.thermostat.numa.common.NumaStat;
+import com.redhat.thermostat.storage.core.Add;
+import com.redhat.thermostat.storage.core.Storage;
+
+public class NumaDAOImplTest {
+
+    private NumaDAO numaDAO;
+    private Storage storage;
+
+    @Before
+    public void setUp() {
+        storage = mock(Storage.class);
+        numaDAO = new NumaDAOImpl(storage);
+    }
+
+    @After
+    public void tearDown() {
+        numaDAO = null;
+        storage = null;
+    }
+
+    @Test
+    public void testRegisterCategory() {
+        verify(storage).registerCategory(NumaDAO.numaStatCategory);
+    }
+
+    @Test
+    public void testPutNumaStat() {
+
+        Add add = mock(Add.class);
+        when(storage.createAdd(NumaDAO.numaStatCategory)).thenReturn(add);
+
+        NumaStat stat = new NumaStat();
+        stat.setNode(1);
+        stat.setNumaHit(2);
+        stat.setNumaMiss(3);
+        stat.setNumaForeign(4);
+        stat.setInterleaveHit(5);
+        stat.setLocalNode(6);
+        stat.setOtherNode(7);
+
+        numaDAO.putNumaStat(stat);
+
+        verify(storage).registerCategory(NumaDAO.numaStatCategory);
+        verify(storage).createAdd(NumaDAO.numaStatCategory);
+        verifyNoMoreInteractions(storage);
+        verify(add).setPojo(stat);
+        verify(add).apply();
+        verifyNoMoreInteractions(add);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/numa/pom.xml	Tue Jan 15 23:00:52 2013 +0100
@@ -0,0 +1,58 @@
+<?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-numa</artifactId>
+  <packaging>pom</packaging>
+
+  <name>Thermostat NUMA plugin</name>
+
+  <modules>
+    <module>agent</module>
+    <module>common</module>
+  </modules>
+
+</project>
--- a/pom.xml	Thu Jan 10 21:48:04 2013 +0100
+++ b/pom.xml	Tue Jan 15 23:00:52 2013 +0100
@@ -140,6 +140,7 @@
     <module>vm-classstat</module>
     <module>vm-memory</module>
     <module>vm-heap-analysis</module>
+    <module>numa</module>
     <!-- development related modules -->
     <module>dev</module>
   </modules>
--- a/vm-classstat/agent/src/main/java/com/redhat/thermostat/vm/classstat/agent/internal/VmClassStatBackend.java	Thu Jan 10 21:48:04 2013 +0100
+++ b/vm-classstat/agent/src/main/java/com/redhat/thermostat/vm/classstat/agent/internal/VmClassStatBackend.java	Tue Jan 15 23:00:52 2013 +0100
@@ -113,11 +113,6 @@
     }
     
     @Override
-    protected void setDAOFactoryAction() {
-        // No need for DAOFactory
-    }
-
-    @Override
     public String getConfigurationValue(String key) {
         return null;
     }
--- a/vm-cpu/agent/src/main/java/com/redhat/thermostat/vm/cpu/agent/internal/VmCpuBackend.java	Thu Jan 10 21:48:04 2013 +0100
+++ b/vm-cpu/agent/src/main/java/com/redhat/thermostat/vm/cpu/agent/internal/VmCpuBackend.java	Tue Jan 15 23:00:52 2013 +0100
@@ -151,11 +151,6 @@
     }
 
     @Override
-    protected void setDAOFactoryAction() {
-        // No need for DAOFactory
-    }
-
-    @Override
     public String getConfigurationValue(String key) {
         return null;
     }
--- a/vm-gc/agent/src/main/java/com/redhat/thermostat/vm/gc/agent/internal/VmGcBackend.java	Thu Jan 10 21:48:04 2013 +0100
+++ b/vm-gc/agent/src/main/java/com/redhat/thermostat/vm/gc/agent/internal/VmGcBackend.java	Tue Jan 15 23:00:52 2013 +0100
@@ -110,11 +110,6 @@
     public boolean isActive() {
         return started;
     }
-    
-    @Override
-    protected void setDAOFactoryAction() {
-        // No need for DAOFactory
-    }
 
     @Override
     public String getConfigurationValue(String key) {
--- a/vm-memory/agent/src/main/java/com/redhat/thermostat/vm/memory/agent/internal/VmMemoryBackend.java	Thu Jan 10 21:48:04 2013 +0100
+++ b/vm-memory/agent/src/main/java/com/redhat/thermostat/vm/memory/agent/internal/VmMemoryBackend.java	Tue Jan 15 23:00:52 2013 +0100
@@ -110,11 +110,6 @@
     public boolean isActive() {
         return started;
     }
-    
-    @Override
-    protected void setDAOFactoryAction() {
-        // No need for DAOFactory
-    }
 
     @Override
     public String getConfigurationValue(String key) {