changeset 542:f04463bcfa11

Data harvester and collectors review-thread: http://icedtea.classpath.org/pipermail/thermostat/2012-August/002719.html reviewed-by: omajid PR 934 PR 1107
author Mario Torre <neugens.limasoftware@gmail.com>
date Fri, 17 Aug 2012 12:50:53 +0200
parents 86e7f31bcd0c
children 7c48230daefb
files thread/collector/pom.xml thread/collector/src/main/java/com/redhat/thermostat/thread/collector/ThreadCollector.java thread/collector/src/main/java/com/redhat/thermostat/thread/collector/ThreadCollectorFactory.java thread/collector/src/main/java/com/redhat/thermostat/thread/collector/ThreadInfo.java thread/collector/src/main/java/com/redhat/thermostat/thread/collector/ThreadSummary.java thread/collector/src/main/java/com/redhat/thermostat/thread/collector/VMThreadCapabilities.java thread/collector/src/main/java/com/redhat/thermostat/thread/collector/impl/ThreadCollectorFactoryImpl.java thread/collector/src/main/java/com/redhat/thermostat/thread/collector/impl/ThreadMXBeanCollector.java thread/collector/src/main/java/com/redhat/thermostat/thread/collector/impl/ThreadMXInfo.java thread/collector/src/main/java/com/redhat/thermostat/thread/collector/impl/ThreadMXSummary.java thread/collector/src/main/java/com/redhat/thermostat/thread/collector/impl/VMThreadMXCapabilities.java thread/collector/src/main/java/com/redhat/thermostat/thread/collector/osgi/Activator.java thread/collector/src/main/java/com/redhat/thermostat/thread/dao/ThreadDao.java thread/collector/src/main/java/com/redhat/thermostat/thread/dao/impl/ThreadDaoImpl.java thread/collector/src/main/java/com/redhat/thermostat/utils/management/MXBeanConnection.java thread/collector/src/main/java/com/redhat/thermostat/utils/management/MXBeanConnector.java thread/collector/src/test/java/com/redhat/thermostat/thread/collector/ThreadCollectorFactoryTest.java thread/collector/src/test/java/com/redhat/thermostat/thread/collector/ThreadCollectorTest.java thread/collector/src/test/java/com/redhat/thermostat/thread/dao/impl/ThreadDaoImplTest.java
diffstat 19 files changed, 2109 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/thread/collector/pom.xml	Fri Aug 17 12:50:53 2012 +0200
@@ -0,0 +1,117 @@
+<?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-thread</artifactId>
+    <version>0.4.0-SNAPSHOT</version>
+  </parent>
+
+  <artifactId>thermostat-thread-collector</artifactId>
+  <packaging>bundle</packaging>
+
+  <name>Thermostat Thread Info Collector</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>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.sun</groupId>
+      <artifactId>tools</artifactId>
+      <scope>system</scope>
+      <systemPath>${java.home}/../lib/tools.jar</systemPath>
+    </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.thread.collector.osgi.Activator</Bundle-Activator>
+            <Export-Package>
+              com.redhat.thermostat.thread.collector,
+              com.redhat.thermostat.thread.dao,
+            </Export-Package>
+            <Private-Package>
+              com.redhat.thermostat.thread.collector.osgi,
+              com.redhat.thermostat.thread.collector.impl,
+              com.redhat.thermostat.thread.dao.impl,
+              com.redhat.thermostat.utils.management,
+            </Private-Package>
+            <!-- Do not autogenerate uses clauses in Manifests -->
+            <_nouses>true</_nouses>
+          </instructions>
+        </configuration>
+      </plugin>
+    </plugins>
+  </build>
+
+</project>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/thread/collector/src/main/java/com/redhat/thermostat/thread/collector/ThreadCollector.java	Fri Aug 17 12:50:53 2012 +0200
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2012 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.thread.collector;
+
+import java.util.List;
+
+public interface ThreadCollector {
+    
+    VMThreadCapabilities getVMThreadCapabilities();
+    
+    void startHarvester();
+    void stopHarvester();
+
+    ThreadSummary getLatestThreadSummary();
+    List<ThreadSummary> getThreadSummary(long since);
+    List<ThreadSummary> getThreadSummary();
+    
+    /**
+     * Return a list of {@link ThreadInfo}, sorted in descending order their by
+     * {@link ThreadInfo#getTimeStamp()}, whose elements are at most
+     * "{@code since}" old.
+     */
+    List<ThreadInfo> getThreadInfo(long since);
+
+    /**
+     * Return a list of all the {@link ThreadInfo} collected, sorted in
+     * descending order their by {@link ThreadInfo#getTimeStamp()}.
+     */
+    List<ThreadInfo> getThreadInfo();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/thread/collector/src/main/java/com/redhat/thermostat/thread/collector/ThreadCollectorFactory.java	Fri Aug 17 12:50:53 2012 +0200
@@ -0,0 +1,44 @@
+/*
+ * 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.thread.collector;
+
+import com.redhat.thermostat.common.dao.VmRef;
+
+public interface ThreadCollectorFactory {
+
+    ThreadCollector getCollector(VmRef reference);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/thread/collector/src/main/java/com/redhat/thermostat/thread/collector/ThreadInfo.java	Fri Aug 17 12:50:53 2012 +0200
@@ -0,0 +1,62 @@
+/*
+ * 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.thread.collector;
+
+import java.lang.Thread.State;
+
+import com.redhat.thermostat.common.model.TimeStampedPojo;
+
+public interface ThreadInfo extends TimeStampedPojo {
+
+    StackTraceElement[] getStackTrace();
+
+    String getName();
+
+    long getAllocatedBytes();
+
+    long getThreadID();
+
+    State getState();
+
+    long getBlockedCount();
+
+    long getWaitedCount();
+
+    long getCpuTime();
+
+    long getUserTime();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/thread/collector/src/main/java/com/redhat/thermostat/thread/collector/ThreadSummary.java	Fri Aug 17 12:50:53 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.thread.collector;
+
+import com.redhat.thermostat.common.model.TimeStampedPojo;
+
+public interface ThreadSummary extends TimeStampedPojo {
+
+    /**
+     * Represents the number of living {@link Thread}s, including daemon
+     * {@link Thread}s, currently running.
+     * 
+     * <br /><br />
+     * 
+     * A {@link Thread} is alive if it has been created, started and is not
+     * dead.
+     * 
+     * @see Thread#isAlive()
+     * @see Thread.State
+     */
+    long currentLiveThreads();
+    
+    /**
+     * Represents the number of living {@link Thread}s which are also daemon
+     * {@link Thread}s, currently running.
+     * 
+     * <br /><br />
+     * 
+     * A {@link Thread} is alive if it has been created, started and is not
+     * dead.
+     * 
+     * @see #currentLiveThreads()
+     * @see Thread#isAlive()
+     * @see Thread.State
+     */
+    long currentDaemonThreads();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/thread/collector/src/main/java/com/redhat/thermostat/thread/collector/VMThreadCapabilities.java	Fri Aug 17 12:50:53 2012 +0200
@@ -0,0 +1,48 @@
+/*
+ * 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.thread.collector;
+
+import java.util.List;
+
+public interface VMThreadCapabilities {
+
+    boolean supportCPUTime();
+    boolean supportContentionMonitor();
+    boolean supportThreadAllocatedMemory();
+    
+    List<String> getSupportedFeaturesList();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/thread/collector/src/main/java/com/redhat/thermostat/thread/collector/impl/ThreadCollectorFactoryImpl.java	Fri Aug 17 12:50:53 2012 +0200
@@ -0,0 +1,78 @@
+/*
+ * 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.thread.collector.impl;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.ScheduledExecutorService;
+
+import com.redhat.thermostat.common.dao.VmRef;
+import com.redhat.thermostat.thread.collector.ThreadCollector;
+import com.redhat.thermostat.thread.collector.ThreadCollectorFactory;
+import com.redhat.thermostat.thread.dao.ThreadDao;
+import com.redhat.thermostat.utils.management.MXBeanConnector;
+
+public class ThreadCollectorFactoryImpl implements ThreadCollectorFactory {
+
+    private Map<VmRef, ThreadMXBeanCollector> collectors;
+    
+    private ThreadDao threadDao;
+    private ScheduledExecutorService threadPool;
+    
+    public ThreadCollectorFactoryImpl(ThreadDao threadDao, ScheduledExecutorService threadPool) {
+        this.threadDao = threadDao;
+        this.threadPool = threadPool;
+        
+        collectors = new HashMap<VmRef, ThreadMXBeanCollector>();
+    }
+    
+    @Override
+    public synchronized ThreadCollector getCollector(VmRef reference) {
+        ThreadMXBeanCollector collector = collectors.get(reference);
+        if (collector == null) {
+            collector = new ThreadMXBeanCollector(threadDao, reference, new MXBeanConnector(reference), threadPool);
+            collectors.put(reference, collector);
+        }
+        return collector;
+    }
+    
+    public void shutdown() {
+        for (ThreadMXBeanCollector collector : collectors.values()) {
+            collector.stopHarvester();
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/thread/collector/src/main/java/com/redhat/thermostat/thread/collector/impl/ThreadMXBeanCollector.java	Fri Aug 17 12:50:53 2012 +0200
@@ -0,0 +1,286 @@
+/*
+ * 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.thread.collector.impl;
+
+import java.io.IOException;
+import java.lang.management.ManagementFactory;
+import java.lang.management.ThreadInfo;
+import java.lang.management.ThreadMXBean;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
+
+import javax.management.MalformedObjectNameException;
+
+import com.redhat.thermostat.common.dao.VmRef;
+import com.redhat.thermostat.thread.collector.ThreadCollector;
+import com.redhat.thermostat.thread.collector.ThreadSummary;
+import com.redhat.thermostat.thread.collector.VMThreadCapabilities;
+import com.redhat.thermostat.thread.dao.ThreadDao;
+import com.redhat.thermostat.utils.management.MXBeanConnection;
+import com.redhat.thermostat.utils.management.MXBeanConnector;
+
+@SuppressWarnings("restriction")
+public class ThreadMXBeanCollector implements ThreadCollector {
+
+    private ThreadDao threadDao;
+    private VmRef ref;
+    private MXBeanConnector connector;
+
+    private MXBeanConnection connection;
+    private ThreadMXBean collectorBean;
+    
+    private boolean isConnected;
+    private ScheduledExecutorService threadPool;
+    
+    private ScheduledFuture<?> harvester;
+    
+    public ThreadMXBeanCollector(ThreadDao threadDao, VmRef ref, MXBeanConnector connector, ScheduledExecutorService threadPool) {
+        this.threadDao = threadDao;
+        this.ref = ref;
+        this.connector = connector;
+        this.threadPool = threadPool;
+    }
+
+    @Override
+    public synchronized void startHarvester() {
+        if (isConnected) return;
+        
+        if (!connector.isAttached()) {
+            try {
+                connector.attach();
+            } catch (Exception ignore) {
+                ignore.printStackTrace();
+            }
+        }
+        
+        try {
+            connection = connector.connect();
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        isConnected = true;
+        
+        harvester = threadPool.scheduleAtFixedRate(new Harvester(), 0, 1, TimeUnit.SECONDS);
+    }
+    
+    private class Harvester implements Runnable {
+        @Override
+        public void run() {
+            harvestData();
+        }
+    }
+    
+    @Override
+    public synchronized void stopHarvester() {
+        if (!isConnected) return;
+        
+        harvester.cancel(false);
+        
+        try {
+            connection.close();
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+        
+        if (connector.isAttached()) {
+            try {
+                connector.close();
+            } catch (Exception ignore) {
+                ignore.printStackTrace();
+            }
+        }
+        
+        isConnected = false;
+    }
+    
+    @Override
+    public ThreadSummary getLatestThreadSummary() {
+        ThreadSummary summary = threadDao.loadLastestSummary(ref);
+        if (summary == null) {
+            // default to all 0
+            summary = new ThreadMXSummary();
+        }
+        return summary;
+    }
+    
+    @Override
+    public List<ThreadSummary> getThreadSummary(long since) {
+        List<ThreadSummary> summary = threadDao.loadSummary(ref, since);
+        return summary;
+    }
+    
+    @Override
+    public List<ThreadSummary> getThreadSummary() {
+        return getThreadSummary(0);
+    }
+    
+    @Override
+    public List<com.redhat.thermostat.thread.collector.ThreadInfo> getThreadInfo() {
+        return getThreadInfo(0);
+    }
+    
+    @Override
+    public List<com.redhat.thermostat.thread.collector.ThreadInfo> getThreadInfo(long since) {
+        return threadDao.loadThreadInfo(ref, since);
+    }
+    
+    public synchronized void harvestData() {
+        try {
+            
+            long timestamp = System.currentTimeMillis();
+            
+            ThreadMXSummary summary = new ThreadMXSummary();
+            
+            collectorBean = getDataCollectorBean(connection);
+            
+            summary.setCurrentLiveThreads(collectorBean.getThreadCount());
+            summary.setDaemonThreads(collectorBean.getDaemonThreadCount());
+            summary.setTimestamp(timestamp);
+            
+            threadDao.saveSummary(ref, summary);
+            
+            long [] ids = collectorBean.getAllThreadIds();
+            long[] allocatedBytes = null;
+            
+            // now the details for the threads
+            if (collectorBean instanceof com.sun.management.ThreadMXBean) {
+                com.sun.management.ThreadMXBean sunBean = (com.sun.management.ThreadMXBean) collectorBean;
+                boolean wasEnabled = false;
+                if (sunBean.isThreadAllocatedMemorySupported()) {
+                    wasEnabled = sunBean.isThreadAllocatedMemoryEnabled();
+                    sunBean.setThreadAllocatedMemoryEnabled(true);
+                    allocatedBytes = sunBean.getThreadAllocatedBytes(ids);
+                    sunBean.setThreadAllocatedMemoryEnabled(wasEnabled);
+                }
+            }
+
+            ThreadInfo[] threadInfos = collectorBean.getThreadInfo(ids, true, true);
+            
+            for (int i = 0; i < ids.length; i++) {
+                ThreadMXInfo info = new ThreadMXInfo();
+                ThreadInfo beanInfo = threadInfos[i];
+
+                info.setTimeStamp(timestamp);
+
+                info.setName(beanInfo.getThreadName());
+                info.setID(beanInfo.getThreadId());
+                info.setState(beanInfo.getThreadState());
+                info.setStackTrace(beanInfo.getStackTrace());
+
+                info.setCPUTime(collectorBean.getThreadCpuTime(info.getThreadID()));
+                info.setUserTime(collectorBean.getThreadUserTime(info.getThreadID()));
+                
+                info.setBlockedCount(beanInfo.getBlockedCount());
+                info.setWaitedCount(beanInfo.getWaitedCount());
+                
+                if (allocatedBytes != null) {
+                    info.setAllocatedBytes(allocatedBytes[i]);
+                }
+
+                threadDao.saveThreadInfo(ref, info);
+            }
+            
+        } catch (MalformedObjectNameException e) {
+            e.printStackTrace();
+        }
+    }
+    
+    @Override
+    public synchronized VMThreadCapabilities getVMThreadCapabilities() {
+        
+        VMThreadCapabilities caps = threadDao.loadCapabilities(ref);
+        if (caps == null) {
+            if (!connector.isAttached()) {
+                try {
+                    connector.attach();
+                } catch (Exception ignore) {
+                    ignore.printStackTrace();
+                }
+            }
+            
+            // query caps to the vm, then save for later
+            try (MXBeanConnection connection = connector.connect()) {
+                             
+                ThreadMXBean bean = getDataCollectorBean(connection);
+                VMThreadMXCapabilities _caps = new VMThreadMXCapabilities();
+                
+                if (bean.isThreadCpuTimeSupported()) _caps.addFeature(ThreadDao.CPU_TIME);
+                if (bean.isThreadContentionMonitoringSupported()) _caps.addFeature(ThreadDao.CONTENTION_MONITOR);
+                
+                if (bean instanceof com.sun.management.ThreadMXBean) {
+                    com.sun.management.ThreadMXBean sunBean = (com.sun.management.ThreadMXBean) bean;
+                    if (sunBean.isThreadAllocatedMemorySupported())
+                        _caps.addFeature(ThreadDao.THREAD_ALLOCATED_MEMORY);
+                }
+                
+                caps = _caps;
+                
+                threadDao.saveCapabilities(ref, caps);
+                
+            } catch (Exception e) {
+                e.printStackTrace();
+            }
+
+            if (connector.isAttached()) {
+                try {
+                    connector.close();
+                } catch (Exception ignore) {
+                    ignore.printStackTrace();
+                }
+            }
+        }
+        
+        return caps;
+    }
+    
+    private ThreadMXBean getDataCollectorBean(MXBeanConnection connection) throws MalformedObjectNameException {
+        ThreadMXBean bean = null;
+        try {
+            bean = connection.createProxy(ManagementFactory.THREAD_MXBEAN_NAME,
+                                          com.sun.management.ThreadMXBean.class);
+        } catch (MalformedObjectNameException ignore) {}
+        
+        if (bean == null) {
+            bean = connection.createProxy(ManagementFactory.THREAD_MXBEAN_NAME,
+                                          ThreadMXBean.class);
+        }
+        return bean;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/thread/collector/src/main/java/com/redhat/thermostat/thread/collector/impl/ThreadMXInfo.java	Fri Aug 17 12:50:53 2012 +0200
@@ -0,0 +1,188 @@
+/*
+ * 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.thread.collector.impl;
+
+import java.lang.Thread.State;
+import java.util.Arrays;
+
+import com.redhat.thermostat.thread.collector.ThreadInfo;
+
+public class ThreadMXInfo implements ThreadInfo {
+
+    private StackTraceElement[] stackTrace;
+    private long threadID;
+    private State threadState;
+    private String name;
+    private long allocatedBytes;
+    
+    private long threadCpuTime;
+    private long threadUserTime;
+    private long blockedCount;
+    private long waitedCount;
+    
+    private long timestamp;
+    
+    public void setStackTrace(StackTraceElement[] stackTrace) {
+        this.stackTrace = stackTrace;
+    }
+    
+    @Override
+    public StackTraceElement[] getStackTrace() {
+        return stackTrace;
+    }
+    
+    @Override
+    public String toString() {
+        return "ThreadMXInfo [name=" + name
+                + ", threadID=" + threadID + ", threadState=" + threadState
+                + ", stackTrace=" + Arrays.toString(stackTrace)
+                + ", allocatedBytes=" + allocatedBytes
+                + ", threadCpuTime=" + threadCpuTime + ", threadUserTime="
+                + threadUserTime + ", blockedCount=" + blockedCount
+                + ", waitedCount=" + waitedCount + ", timestamp=" + timestamp
+                + "]";
+    }
+
+    public void setName(String threadName) {
+        this.name = threadName;
+    }
+
+    public void setID(long threadID) {
+        this.threadID = threadID;
+    }
+
+    public void setState(State threadState) {
+        this.threadState = threadState;
+    }
+
+    public void setAllocatedBytes(long allocatedBytes) {
+        this.allocatedBytes = allocatedBytes;
+    }
+
+    @Override
+    public String getName() {
+        return name;
+    }
+    
+    @Override
+    public long getAllocatedBytes() {
+        return allocatedBytes;
+    }
+    
+    @Override
+    public long getThreadID() {
+        return threadID;
+    }
+    
+    @Override
+    public State getState() {
+        return threadState;
+    }
+    
+    @Override
+    public long getTimeStamp() {
+        return timestamp;
+    }
+    
+    public void setTimeStamp(long timestamp) {
+        this.timestamp = timestamp;
+    }
+
+    public void setCPUTime(long threadCpuTime) {
+        this.threadCpuTime = threadCpuTime;
+    }
+
+    public void setUserTime(long threadUserTime) {
+       this.threadUserTime = threadUserTime;
+    }
+
+    public void setBlockedCount(long blockedCount) {
+        this.blockedCount = blockedCount;
+    }
+
+    public void setWaitedCount(long waitedCount) {
+        this.waitedCount = waitedCount;
+    }
+    
+    @Override
+    public long getBlockedCount() {
+        return blockedCount;
+    }
+    
+    @Override
+    public long getWaitedCount() {
+        return waitedCount;
+    }
+    
+    @Override
+    public long getCpuTime() {
+        return threadCpuTime;
+    }
+    
+    @Override
+    public long getUserTime() {
+        return threadUserTime;
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + ((name == null) ? 0 : name.hashCode());
+        result = prime * result + (int) (threadID ^ (threadID >>> 32));
+        return result;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj)
+            return true;
+        if (obj == null)
+            return false;
+        if (getClass() != obj.getClass())
+            return false;
+        ThreadMXInfo other = (ThreadMXInfo) obj;
+        if (name == null) {
+            if (other.name != null)
+                return false;
+        } else if (!name.equals(other.name))
+            return false;
+        if (threadID != other.threadID)
+            return false;
+        return true;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/thread/collector/src/main/java/com/redhat/thermostat/thread/collector/impl/ThreadMXSummary.java	Fri Aug 17 12:50:53 2012 +0200
@@ -0,0 +1,80 @@
+/*
+ * 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.thread.collector.impl;
+
+import com.redhat.thermostat.thread.collector.ThreadSummary;
+
+public class ThreadMXSummary implements ThreadSummary {
+
+    private long currentLiveThreads;
+    private long daemonThreads;
+    
+    private long timestamp;
+    
+    @Override
+    public long currentLiveThreads() {
+        return currentLiveThreads;
+    }
+    
+    public void setCurrentLiveThreads(long currentLiveThreads) {
+        this.currentLiveThreads = currentLiveThreads;
+    }
+    
+    @Override
+    public long currentDaemonThreads() {
+        return daemonThreads;
+    }
+    
+    public void setDaemonThreads(long daemonThreads) {
+        this.daemonThreads = daemonThreads;
+    }
+    
+    @Override
+    public long getTimeStamp() {
+        return timestamp;
+    }
+    
+    public void setTimestamp(long timestamp) {
+        this.timestamp = timestamp;
+    }
+    
+    @Override
+    public String toString() {
+        return "[timestamp: " + timestamp + ", currentLiveThreads: " +
+               currentLiveThreads + ", daemonThreads: " + daemonThreads + "]";
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/thread/collector/src/main/java/com/redhat/thermostat/thread/collector/impl/VMThreadMXCapabilities.java	Fri Aug 17 12:50:53 2012 +0200
@@ -0,0 +1,81 @@
+/*
+ * 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.thread.collector.impl;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import com.redhat.thermostat.common.storage.Key;
+import com.redhat.thermostat.thread.collector.VMThreadCapabilities;
+import com.redhat.thermostat.thread.dao.ThreadDao;
+
+public class VMThreadMXCapabilities implements VMThreadCapabilities {
+    
+    private Set<String> features = new HashSet<>();
+    
+    @Override
+    public boolean supportCPUTime() {
+        return features.contains(ThreadDao.CPU_TIME);
+    }
+      
+    @Override
+    public boolean supportContentionMonitor() {
+        return features.contains(ThreadDao.CONTENTION_MONITOR);
+    }
+    
+    @Override
+    public String toString() {
+        return "[supportCPU: " + supportCPUTime() + ", supportContention: " + supportContentionMonitor() +
+               ", supportThreadAllocatedMemory: " + supportThreadAllocatedMemory() + "]";
+    }
+    
+    @Override
+    public boolean supportThreadAllocatedMemory() {
+        return features.contains(ThreadDao.THREAD_ALLOCATED_MEMORY);
+    }
+    
+    @Override
+    public List<String> getSupportedFeaturesList() {
+        return new ArrayList<>(features);
+    }
+    
+    public void addFeature(String feature) {
+        features.add(feature);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/thread/collector/src/main/java/com/redhat/thermostat/thread/collector/osgi/Activator.java	Fri Aug 17 12:50:53 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.thread.collector.osgi;
+
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+
+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.common.storage.Storage;
+import com.redhat.thermostat.thread.collector.ThreadCollectorFactory;
+import com.redhat.thermostat.thread.collector.impl.ThreadCollectorFactoryImpl;
+import com.redhat.thermostat.thread.dao.ThreadDao;
+import com.redhat.thermostat.thread.dao.impl.ThreadDaoImpl;
+
+public class Activator implements BundleActivator {
+    
+    private ScheduledExecutorService executor = Executors.newScheduledThreadPool(24);
+    private ThreadCollectorFactoryImpl collectorFactory;
+    
+    @Override
+    public void start(final BundleContext context) throws Exception {
+        @SuppressWarnings({ "rawtypes", "unchecked" })
+        ServiceTracker tracker = new ServiceTracker(context, Storage.class.getName(), null) {
+            @Override
+            public Object addingService(ServiceReference reference) {
+                
+                Storage storage = (Storage) context.getService(reference);
+                
+                ThreadDao threadDao = new ThreadDaoImpl(storage);
+                collectorFactory = new ThreadCollectorFactoryImpl(threadDao, executor);
+
+                context.registerService(ThreadCollectorFactory.class.getName(), collectorFactory, null);
+                
+                return super.addingService(reference);
+            }
+        };
+        tracker.open();
+    }
+
+    @Override
+    public void stop(BundleContext context) throws Exception {
+        if (collectorFactory != null) {
+            collectorFactory.shutdown();
+        }
+        if (executor != null) {
+            executor.shutdown();
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/thread/collector/src/main/java/com/redhat/thermostat/thread/dao/ThreadDao.java	Fri Aug 17 12:50:53 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.thread.dao;
+
+import java.util.List;
+
+import com.redhat.thermostat.common.dao.VmRef;
+import com.redhat.thermostat.common.storage.Category;
+import com.redhat.thermostat.common.storage.Key;
+import com.redhat.thermostat.thread.collector.ThreadInfo;
+import com.redhat.thermostat.thread.collector.ThreadSummary;
+import com.redhat.thermostat.thread.collector.VMThreadCapabilities;
+
+public interface ThreadDao {
+
+    static final String CPU_TIME = "thread-cpu-time";
+    static final String CONTENTION_MONITOR = "thread-contention-monitor";
+    static final String THREAD_ALLOCATED_MEMORY = "thread-allocated-memory";
+
+    static final Key<Boolean> CPU_TIME_KEY = new Key<Boolean>(CPU_TIME, false);
+    static final Key<Boolean> CONTENTION_MONITOR_KEY = new Key<Boolean>(CONTENTION_MONITOR, false);
+    static final Key<Boolean> THREAD_ALLOCATED_MEMORY_KEY = new Key<Boolean>(THREAD_ALLOCATED_MEMORY, false);
+
+    static final Category THREAD_CAPABILITIES =
+            new Category("vm-thread-capabilities", Key.AGENT_ID, Key.VM_ID,
+                         CPU_TIME_KEY, CONTENTION_MONITOR_KEY,
+                         THREAD_ALLOCATED_MEMORY_KEY);
+
+
+    VMThreadCapabilities loadCapabilities(VmRef ref);
+    void saveCapabilities(VmRef ref, VMThreadCapabilities caps);
+
+    static final String LIVE_THREADS = "thread-living";
+    static final Key<Long> LIVE_THREADS_KEY = new Key<Long>(LIVE_THREADS, false);
+    static final String DAEMON_THREADS = "thread-daemons";
+    static final Key<Long> DAEMON_THREADS_KEY = new Key<Long>(DAEMON_THREADS, false);
+    
+    static final Category THREAD_SUMMARY =
+            new Category("vm-thread-summary", Key.AGENT_ID, Key.VM_ID,
+                         Key.TIMESTAMP,
+                         LIVE_THREADS_KEY, DAEMON_THREADS_KEY);
+    
+    void saveSummary(VmRef vm, ThreadSummary summary);
+    ThreadSummary loadLastestSummary(VmRef ref);
+    List<ThreadSummary> loadSummary(VmRef ref, long since);
+
+    static final String THREAD_STATE = "thread-state";
+    static final Key<String> THREAD_STATE_KEY = new Key<String>(THREAD_STATE, false);
+    static final String THREAD_ID = "thread-id";
+    static final Key<Long> THREAD_ID_KEY = new Key<Long>(THREAD_ID, false);
+    static final String THREAD_NAME = "thread-name";
+    static final Key<String> THREAD_NAME_KEY = new Key<String>(THREAD_NAME, false);
+    static final String THREAD_HEAP = "thread-id";
+    static final Key<Long> THREAD_HEAP_KEY = new Key<Long>(THREAD_HEAP, false);
+    static final String THREAD_CPU_TIME = "thread-cpu-time";
+    static final Key<Long> THREAD_CPU_TIME_KEY = new Key<Long>(THREAD_CPU_TIME, false);
+    static final String THREAD_USER_TIME = "thread-user-time";
+    static final Key<Long> THREAD_USER_TIME_KEY = new Key<Long>(THREAD_USER_TIME, false);
+    static final String THREAD_BLOCKED_COUNT = "thread-blocked-count";
+    static final Key<Long> THREAD_BLOCKED_COUNT_KEY = new Key<Long>(THREAD_BLOCKED_COUNT, false);
+    static final String THREAD_WAIT_COUNT = "thread-wait-count";
+    static final Key<Long> THREAD_WAIT_COUNT_KEY = new Key<Long>(THREAD_WAIT_COUNT, false);
+    static final String THREAD_STACK_TRACE_ID = "thread-stacktrace-id";
+    static final Key<Long> THREAD_STACK_TRACE_ID_KEY = new Key<Long>(THREAD_STACK_TRACE_ID, false);
+            
+    static final Category THREAD_INFO =
+            new Category("vm-thread-info", Key.AGENT_ID, Key.VM_ID,
+                         Key.TIMESTAMP, THREAD_NAME_KEY, THREAD_ID_KEY,
+                         THREAD_STATE_KEY, THREAD_HEAP_KEY, THREAD_CPU_TIME_KEY,
+                         THREAD_USER_TIME_KEY, THREAD_BLOCKED_COUNT_KEY,
+                         THREAD_WAIT_COUNT_KEY, THREAD_STACK_TRACE_ID_KEY);
+    
+    void saveThreadInfo(VmRef ref, ThreadInfo info);
+    List<ThreadInfo> loadThreadInfo(VmRef ref, long since);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/thread/collector/src/main/java/com/redhat/thermostat/thread/dao/impl/ThreadDaoImpl.java	Fri Aug 17 12:50:53 2012 +0200
@@ -0,0 +1,200 @@
+/*
+ * 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.thread.dao.impl;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import com.redhat.thermostat.common.dao.VmRef;
+import com.redhat.thermostat.common.storage.Category;
+import com.redhat.thermostat.common.storage.Chunk;
+import com.redhat.thermostat.common.storage.Cursor;
+import com.redhat.thermostat.common.storage.Key;
+import com.redhat.thermostat.common.storage.Storage;
+import com.redhat.thermostat.thread.collector.ThreadInfo;
+import com.redhat.thermostat.thread.collector.ThreadSummary;
+import com.redhat.thermostat.thread.collector.VMThreadCapabilities;
+import com.redhat.thermostat.thread.collector.impl.ThreadMXInfo;
+import com.redhat.thermostat.thread.collector.impl.ThreadMXSummary;
+import com.redhat.thermostat.thread.collector.impl.VMThreadMXCapabilities;
+import com.redhat.thermostat.thread.dao.ThreadDao;
+
+public class ThreadDaoImpl implements ThreadDao {
+
+    private Storage storage; 
+    public ThreadDaoImpl(Storage storage) {
+        this.storage = storage;
+        storage.createConnectionKey(THREAD_CAPABILITIES);
+        storage.createConnectionKey(THREAD_SUMMARY);
+        storage.createConnectionKey(THREAD_INFO);
+    }
+
+    @Override
+    public VMThreadCapabilities loadCapabilities(VmRef vm) {
+        
+        VMThreadMXCapabilities caps = null;
+        
+        Chunk query = new Chunk(THREAD_CAPABILITIES, false);
+        query.put(Key.VM_ID, vm.getId());
+        query.put(Key.AGENT_ID, vm.getAgent().getAgentId());
+        
+        Chunk found = storage.find(query);
+        if (found != null) {
+            caps = new VMThreadMXCapabilities();
+            if (found.get(CONTENTION_MONITOR_KEY)) caps.addFeature(CONTENTION_MONITOR);
+            if (found.get(CPU_TIME_KEY)) caps.addFeature(CPU_TIME);
+            if (found.get(THREAD_ALLOCATED_MEMORY_KEY)) caps.addFeature(THREAD_ALLOCATED_MEMORY);
+        }
+        
+        return caps;
+    }
+    
+    @Override
+    public void saveCapabilities(VmRef vm, VMThreadCapabilities caps) {
+        Chunk chunk = prepareChunk(THREAD_CAPABILITIES, true, vm);
+        
+        chunk.put(CONTENTION_MONITOR_KEY, caps.supportContentionMonitor());
+        chunk.put(CPU_TIME_KEY, caps.supportCPUTime());
+        chunk.put(THREAD_ALLOCATED_MEMORY_KEY, caps.supportThreadAllocatedMemory());
+        
+        storage.putChunk(chunk);
+    }
+    
+    @Override
+    public void saveSummary(VmRef vm, ThreadSummary summary) {
+        Chunk chunk = prepareChunk(THREAD_SUMMARY, false, vm);
+        
+        chunk.put(LIVE_THREADS_KEY, summary.currentLiveThreads());
+        chunk.put(DAEMON_THREADS_KEY, summary.currentDaemonThreads());
+        chunk.put(Key.TIMESTAMP, summary.getTimeStamp());
+        
+        storage.putChunk(chunk);
+    }
+    
+    @Override
+    public ThreadSummary loadLastestSummary(VmRef ref) {
+        ThreadMXSummary summary = null;
+
+        Chunk query = prepareChunk(THREAD_SUMMARY, false, ref);
+        Cursor cursor = storage.findAll(query).sort(Key.TIMESTAMP, Cursor.SortDirection.DESCENDING).limit(1);
+        if (cursor.hasNext()) {
+            Chunk found = cursor.next();
+            summary = new ThreadMXSummary();
+            summary.setTimestamp(found.get(Key.TIMESTAMP));
+            summary.setCurrentLiveThreads(found.get(LIVE_THREADS_KEY));
+            summary.setDaemonThreads(found.get(DAEMON_THREADS_KEY));
+        }
+        
+        return summary;
+    }
+    
+    @Override
+    public List<ThreadSummary> loadSummary(VmRef ref, long since) {
+        
+        List<ThreadSummary> result = new ArrayList<>();
+        
+        Chunk query = prepareChunk(THREAD_SUMMARY, false, ref);
+        query.put(Key.WHERE, "this.timestamp > " + since);
+
+        Cursor cursor = storage.findAll(query).sort(Key.TIMESTAMP, Cursor.SortDirection.DESCENDING);
+        while (cursor.hasNext()) {
+            ThreadMXSummary summary = new ThreadMXSummary();
+            
+            Chunk found = cursor.next();
+            summary.setTimestamp(found.get(Key.TIMESTAMP));
+            summary.setCurrentLiveThreads(found.get(LIVE_THREADS_KEY));
+            summary.setDaemonThreads(found.get(DAEMON_THREADS_KEY));
+            result.add(summary);
+        }
+        
+        return result;
+    }
+    
+    @Override
+    public void saveThreadInfo(VmRef ref, ThreadInfo info) {
+        Chunk chunk = prepareChunk(THREAD_INFO, false, ref);
+        
+        chunk.put(Key.TIMESTAMP, info.getTimeStamp());
+
+        chunk.put(THREAD_ID_KEY, info.getThreadID());
+        chunk.put(THREAD_NAME_KEY, info.getName());
+        chunk.put(THREAD_STATE_KEY, info.getState().name());
+        
+        chunk.put(THREAD_BLOCKED_COUNT_KEY, info.getBlockedCount());
+        chunk.put(THREAD_WAIT_COUNT_KEY, info.getWaitedCount());
+        chunk.put(THREAD_CPU_TIME_KEY, info.getCpuTime());
+        chunk.put(THREAD_USER_TIME_KEY, info.getUserTime());
+
+        storage.putChunk(chunk);
+    }
+
+    @Override
+    public List<ThreadInfo> loadThreadInfo(VmRef ref, long since) {
+        List<ThreadInfo> result = new ArrayList<>();
+        
+        Chunk query = prepareChunk(THREAD_INFO, false, ref);
+        query.put(Key.WHERE, "this.timestamp > " + since);
+        
+        Cursor cursor = storage.findAll(query).sort(Key.TIMESTAMP, Cursor.SortDirection.DESCENDING);
+        while (cursor.hasNext()) {
+            ThreadMXInfo info = new ThreadMXInfo();
+            
+            Chunk found = cursor.next();
+            info.setTimeStamp(found.get(Key.TIMESTAMP));
+            
+            info.setID(found.get(THREAD_ID_KEY));
+            info.setName(found.get(THREAD_NAME_KEY));
+            info.setState(Thread.State.valueOf(found.get(THREAD_STATE_KEY)));
+
+            info.setBlockedCount(found.get(THREAD_BLOCKED_COUNT_KEY));
+            info.setWaitedCount(found.get(THREAD_WAIT_COUNT_KEY));
+            info.setCPUTime(found.get(THREAD_CPU_TIME_KEY));
+            info.setUserTime(found.get(THREAD_USER_TIME_KEY));
+
+            result.add(info);
+        }
+        
+        return result;
+    }
+    
+    private Chunk prepareChunk(Category category, boolean replace, VmRef vm) {
+        Chunk chunk = new Chunk(category, replace);
+        chunk.put(Key.AGENT_ID, vm.getAgent().getAgentId());
+        chunk.put(Key.VM_ID, vm.getId());
+        return chunk;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/thread/collector/src/main/java/com/redhat/thermostat/utils/management/MXBeanConnection.java	Fri Aug 17 12:50:53 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.utils.management;
+
+import java.io.Closeable;
+import java.io.IOException;
+
+import javax.management.JMX;
+import javax.management.MBeanServerConnection;
+import javax.management.MalformedObjectNameException;
+import javax.management.ObjectName;
+import javax.management.remote.JMXConnector;
+
+public class MXBeanConnection implements Closeable {
+
+    private JMXConnector connection;
+    private MBeanServerConnection mbsc;
+    
+    MXBeanConnection(JMXConnector connection, MBeanServerConnection mbsc) {
+        this.connection = connection;
+        this.mbsc = mbsc;
+    }
+    
+    public synchronized <E> E createProxy(String name, Class<? extends E> proxyClass) throws MalformedObjectNameException {
+        ObjectName objectName = new ObjectName(name);
+        return JMX.newMXBeanProxy(mbsc, objectName, proxyClass);
+    }
+    
+    @Override
+    public void close() throws IOException {
+        connection.close();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/thread/collector/src/main/java/com/redhat/thermostat/utils/management/MXBeanConnector.java	Fri Aug 17 12:50:53 2012 +0200
@@ -0,0 +1,116 @@
+/*
+ * 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.management;
+
+import java.io.Closeable;
+import java.io.File;
+import java.io.IOException;
+import java.util.Properties;
+
+import javax.management.MBeanServerConnection;
+import javax.management.remote.JMXConnector;
+import javax.management.remote.JMXConnectorFactory;
+import javax.management.remote.JMXServiceURL;
+
+import com.redhat.thermostat.common.dao.VmRef;
+import com.sun.tools.attach.VirtualMachine;
+
+public class MXBeanConnector implements Closeable {
+
+    private static final String CONNECTOR_ADDRESS_PROPERTY = "com.sun.management.jmxremote.localConnectorAddress";
+    private String connectorAddress;
+    
+    private VmRef reference;
+    private VirtualMachine vm;
+    
+    private boolean attached;
+    
+    public MXBeanConnector(VmRef reference) {
+        this.reference = reference;
+    }
+    
+    public synchronized void attach() throws Exception {
+        if (attached)
+            throw new IOException("Already attached");
+        
+        vm = VirtualMachine.attach(reference.getStringID());
+        attached = true;
+        
+        Properties props = vm.getAgentProperties();
+        connectorAddress = props.getProperty(CONNECTOR_ADDRESS_PROPERTY);
+        if (connectorAddress == null) {
+           props = vm.getSystemProperties();
+           String home = props.getProperty("java.home");
+           String agent = home + File.separator + "lib" + File.separator + "management-agent.jar";
+           vm.loadAgent(agent);
+           
+           props = vm.getAgentProperties();
+           connectorAddress = props.getProperty(CONNECTOR_ADDRESS_PROPERTY);
+        }
+    }
+    
+    public synchronized MXBeanConnection connect() throws Exception {
+        
+        if (!attached)
+            throw new IOException("Agent not attached to target VM");
+        
+        JMXServiceURL url = new JMXServiceURL(connectorAddress);
+        JMXConnector connection = JMXConnectorFactory.connect(url);
+        MBeanServerConnection mbsc = null;
+        try {
+            mbsc = connection.getMBeanServerConnection();
+            
+        } catch (IOException e) {
+            connection.close();
+            throw e;
+        }
+        
+        return new MXBeanConnection(connection, mbsc);
+    }
+    
+    public boolean isAttached() {
+        return attached;
+    }
+    
+    @Override
+    public synchronized void close() throws IOException {
+        if (attached) {
+            vm.detach();
+            attached = false;
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/thread/collector/src/test/java/com/redhat/thermostat/thread/collector/ThreadCollectorFactoryTest.java	Fri Aug 17 12:50:53 2012 +0200
@@ -0,0 +1,72 @@
+/*
+ * 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.thread.collector;
+
+import static org.junit.Assert.*;
+import static org.mockito.Mockito.mock;
+
+import java.util.concurrent.ScheduledExecutorService;
+
+import org.junit.Test;
+
+import com.redhat.thermostat.common.dao.VmRef;
+import com.redhat.thermostat.thread.collector.impl.ThreadCollectorFactoryImpl;
+import com.redhat.thermostat.thread.dao.ThreadDao;
+
+public class ThreadCollectorFactoryTest {
+
+    @Test
+    public void testThreadCollectorFactory() {
+        ThreadDao threadDao = mock(ThreadDao.class);
+        VmRef reference = mock(VmRef.class);
+        
+        ScheduledExecutorService threadPool = mock(ScheduledExecutorService.class);
+        
+        ThreadCollectorFactory factory = new ThreadCollectorFactoryImpl(threadDao, threadPool);
+        ThreadCollector collector = factory.getCollector(reference);
+        assertNotNull(collector);
+        
+        // ask again, it must be the same instance as before
+        ThreadCollector collector2 = factory.getCollector(reference);
+        assertSame(collector, collector2);
+        
+        // and now of course it must be different
+        VmRef reference2 = mock(VmRef.class);
+        ThreadCollector collector3 = factory.getCollector(reference2);
+        assertNotSame(collector, collector3);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/thread/collector/src/test/java/com/redhat/thermostat/thread/collector/ThreadCollectorTest.java	Fri Aug 17 12:50:53 2012 +0200
@@ -0,0 +1,204 @@
+/*
+ * 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.thread.collector;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+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.lang.management.ManagementFactory;
+import java.lang.management.ThreadMXBean;
+import java.util.concurrent.ScheduledExecutorService;
+
+import javax.management.MalformedObjectNameException;
+
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+
+import com.redhat.thermostat.common.dao.VmRef;
+import com.redhat.thermostat.thread.collector.impl.ThreadMXBeanCollector;
+import com.redhat.thermostat.thread.dao.ThreadDao;
+import com.redhat.thermostat.utils.management.MXBeanConnection;
+import com.redhat.thermostat.utils.management.MXBeanConnector;
+
+public class ThreadCollectorTest {
+
+    @Test
+    public void testVMCapabilitiesNotInDAO() throws Exception {
+        
+        VMThreadCapabilities referenceCaps = mock(VMThreadCapabilities.class);
+        when(referenceCaps.supportContentionMonitor()).thenReturn(true);
+        when(referenceCaps.supportCPUTime()).thenReturn(false);
+        
+        VmRef reference = mock(VmRef.class);
+        
+        ThreadDao threadDao = mock(ThreadDao.class);
+        when(threadDao.loadCapabilities(reference)).thenReturn(null);
+        
+        MXBeanConnector connector = mock(MXBeanConnector.class);
+        when(connector.isAttached()).thenReturn(false).thenReturn(true);
+        
+        MXBeanConnection connection = mock(MXBeanConnection.class);
+        when(connector.connect()).thenReturn(connection);
+                
+        ArgumentCaptor<String> captor1 = ArgumentCaptor.forClass(String.class);
+        ArgumentCaptor<Class> captor2 = ArgumentCaptor.forClass(Class.class);
+        
+        ThreadMXBean bean = mock(ThreadMXBean.class);
+        when(bean.isThreadCpuTimeSupported()).thenReturn(false);
+        when(bean.isThreadContentionMonitoringSupported()).thenReturn(true);
+        
+        when(connection.createProxy(captor1.capture(), captor2.capture())).thenThrow(new MalformedObjectNameException()).thenReturn(bean);
+        
+        ScheduledExecutorService threadPool = mock(ScheduledExecutorService.class);
+        
+        /* ************* */
+        
+        ThreadCollector collector = new ThreadMXBeanCollector(threadDao, reference, connector, threadPool);
+        VMThreadCapabilities caps = collector.getVMThreadCapabilities();
+
+        String beanName = captor1.getValue();
+        assertEquals(ManagementFactory.THREAD_MXBEAN_NAME, beanName);
+        
+        Class clazz = captor2.getValue();
+        assertEquals(ThreadMXBean.class.getName(), clazz.getName());
+        
+        verify(threadDao).loadCapabilities(reference);
+        
+        verify(connector).attach();
+        verify(connector).connect();
+        verify(connector).close();
+        
+        verify(threadDao).saveCapabilities(reference, caps);
+        
+        assertTrue(caps.supportContentionMonitor());
+        assertFalse(caps.supportCPUTime());
+        assertFalse(caps.supportThreadAllocatedMemory());
+    }
+    
+    @SuppressWarnings("restriction")
+    @Test
+    public void testHasThreadMemorySupport() throws Exception {
+        
+        VMThreadCapabilities referenceCaps = mock(VMThreadCapabilities.class);
+        when(referenceCaps.supportContentionMonitor()).thenReturn(true);
+        when(referenceCaps.supportCPUTime()).thenReturn(false);
+        
+        VmRef reference = mock(VmRef.class);
+        
+        ThreadDao threadDao = mock(ThreadDao.class);
+        when(threadDao.loadCapabilities(reference)).thenReturn(null);
+        
+        MXBeanConnector connector = mock(MXBeanConnector.class);
+        when(connector.isAttached()).thenReturn(false).thenReturn(true);
+        
+        MXBeanConnection connection = mock(MXBeanConnection.class);
+        when(connector.connect()).thenReturn(connection);
+                
+        ArgumentCaptor<String> captor1 = ArgumentCaptor.forClass(String.class);
+        ArgumentCaptor<Class> captor2 = ArgumentCaptor.forClass(Class.class);
+        
+        com.sun.management.ThreadMXBean bean = mock(com.sun.management.ThreadMXBean.class);
+        when(bean.isThreadCpuTimeSupported()).thenReturn(false);
+        when(bean.isThreadContentionMonitoringSupported()).thenReturn(true);
+        when(bean.isThreadAllocatedMemorySupported()).thenReturn(true);
+        
+        when(connection.createProxy(captor1.capture(), captor2.capture())).thenReturn(bean);
+        
+        ScheduledExecutorService threadPool = mock(ScheduledExecutorService.class);
+        
+        /* ************* */
+        
+        ThreadCollector collector = new ThreadMXBeanCollector(threadDao, reference, connector, threadPool);
+        VMThreadCapabilities caps = collector.getVMThreadCapabilities();
+
+        String beanName = captor1.getValue();
+        assertEquals(ManagementFactory.THREAD_MXBEAN_NAME, beanName);
+        
+        Class clazz = captor2.getValue();
+        assertEquals(com.sun.management.ThreadMXBean.class.getName(), clazz.getName());
+        
+        verify(threadDao).loadCapabilities(reference);
+        
+        verify(connector).attach();
+        verify(connector).connect();
+        verify(connector).close();
+        
+        verify(threadDao).saveCapabilities(reference, caps);
+        
+        assertTrue(caps.supportContentionMonitor());
+        assertFalse(caps.supportCPUTime());
+        assertTrue(caps.supportThreadAllocatedMemory());
+    }
+    
+    @Test
+    public void testVMCapabilitiesInDAO() throws Exception {
+        
+        VMThreadCapabilities referenceCaps = mock(VMThreadCapabilities.class);
+        when(referenceCaps.supportContentionMonitor()).thenReturn(true);
+        when(referenceCaps.supportCPUTime()).thenReturn(false);
+        
+        VmRef reference = mock(VmRef.class);
+        
+        ThreadDao threadDao = mock(ThreadDao.class);
+        when(threadDao.loadCapabilities(reference)).thenReturn(referenceCaps);
+        
+        MXBeanConnector connector = mock(MXBeanConnector.class);
+        
+        ScheduledExecutorService threadPool = mock(ScheduledExecutorService.class);
+        
+        /* ************* */
+        
+        ThreadCollector collector = new ThreadMXBeanCollector(threadDao, reference, connector, threadPool);
+        VMThreadCapabilities caps = collector.getVMThreadCapabilities();
+
+        verify(threadDao).loadCapabilities(reference);
+        
+        verify(connector, times(0)).attach();
+        verify(connector, times(0)).connect();
+        verify(connector, times(0)).close();
+        
+        verify(threadDao, times(0)).saveCapabilities(reference, caps);
+        
+        assertTrue(caps.supportContentionMonitor());
+        assertFalse(caps.supportCPUTime());
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/thread/collector/src/test/java/com/redhat/thermostat/thread/dao/impl/ThreadDaoImplTest.java	Fri Aug 17 12:50:53 2012 +0200
@@ -0,0 +1,135 @@
+/*
+ * 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.thread.dao.impl;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+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 org.junit.Test;
+import org.mockito.ArgumentCaptor;
+
+import com.redhat.thermostat.common.dao.HostRef;
+import com.redhat.thermostat.common.dao.VmRef;
+import com.redhat.thermostat.common.storage.Chunk;
+import com.redhat.thermostat.common.storage.Key;
+import com.redhat.thermostat.common.storage.Storage;
+import com.redhat.thermostat.thread.collector.VMThreadCapabilities;
+import com.redhat.thermostat.thread.dao.ThreadDao;
+
+public class ThreadDaoImplTest {
+
+    @Test
+    public void testCreateConnectionKey() {
+        Storage storage = mock(Storage.class);
+        
+        ThreadDaoImpl dao = new ThreadDaoImpl(storage);
+        verify(storage).createConnectionKey(ThreadDao.THREAD_CAPABILITIES);
+        verify(storage).createConnectionKey(ThreadDao.THREAD_INFO);
+        verify(storage).createConnectionKey(ThreadDao.THREAD_SUMMARY);
+    }
+    
+    @Test
+    public void testLoadVMCapabilities() {
+        Storage storage = mock(Storage.class);
+        VmRef ref = mock(VmRef.class);
+        when(ref.getId()).thenReturn(42);
+        
+        HostRef agent = mock(HostRef.class);
+        when(agent.getAgentId()).thenReturn("0xcafe");
+        
+        when(ref.getAgent()).thenReturn(agent);
+        
+        Chunk answer = mock(Chunk.class);
+        when(answer.get(ThreadDao.CONTENTION_MONITOR_KEY)).thenReturn(false);
+        when(answer.get(ThreadDao.CPU_TIME_KEY)).thenReturn(true);
+        when(answer.get(ThreadDao.THREAD_ALLOCATED_MEMORY_KEY)).thenReturn(true);
+        
+        ArgumentCaptor<Chunk> queryCaptor = ArgumentCaptor.forClass(Chunk.class);
+        when(storage.find(queryCaptor.capture())).thenReturn(answer);
+        
+        ThreadDaoImpl dao = new ThreadDaoImpl(storage);
+        VMThreadCapabilities caps = dao.loadCapabilities(ref);
+
+        Chunk query = queryCaptor.getValue();
+        assertEquals(42, (int) query.get(Key.VM_ID));
+        assertEquals("0xcafe", query.get(Key.AGENT_ID));
+        
+        assertFalse(caps.supportContentionMonitor());
+        assertTrue(caps.supportCPUTime());
+        assertTrue(caps.supportThreadAllocatedMemory());
+    }
+    
+    @Test
+    public void testSaveVMCapabilities() {
+        Storage storage = mock(Storage.class);
+        
+        VmRef ref = mock(VmRef.class);
+        when(ref.getId()).thenReturn(42);
+        
+        HostRef agent = mock(HostRef.class);
+        when(agent.getAgentId()).thenReturn("0xcafe");
+
+        when(ref.getAgent()).thenReturn(agent);
+
+        Chunk answer = mock(Chunk.class);
+        when(answer.get(ThreadDao.CONTENTION_MONITOR_KEY)).thenReturn(false);
+        when(answer.get(ThreadDao.CPU_TIME_KEY)).thenReturn(true);
+        
+        VMThreadCapabilities caps = mock(VMThreadCapabilities.class);
+        when(caps.supportContentionMonitor()).thenReturn(true);
+        when(caps.supportCPUTime()).thenReturn(true);
+        when(caps.supportThreadAllocatedMemory()).thenReturn(true);
+        
+        ThreadDaoImpl dao = new ThreadDaoImpl(storage);
+        dao.saveCapabilities(ref, caps);
+
+        ArgumentCaptor<Chunk> queryCaptor = ArgumentCaptor.forClass(Chunk.class);
+        verify(storage).putChunk(queryCaptor.capture());
+
+        Chunk query = queryCaptor.getValue();
+        assertEquals(42, (int) query.get(Key.VM_ID));
+        assertEquals("0xcafe", query.get(Key.AGENT_ID));
+        assertTrue((boolean) query.get(ThreadDao.CONTENTION_MONITOR_KEY));
+        assertTrue((boolean) query.get(ThreadDao.CPU_TIME_KEY));
+        assertTrue((boolean) query.get(ThreadDao.THREAD_ALLOCATED_MEMORY_KEY));
+    }
+}