changeset 571:e8838f118b34

Move agent-specific thread-fluff into agent-specific bundle Reviewed-by: neugens Review-thread: http://icedtea.classpath.org/pipermail/thermostat/2012-August/002985.html
author Jon VanAlten <jon.vanalten@redhat.com>
date Wed, 29 Aug 2012 19:42:32 -0400
parents 7d4bdce24e21
children 6f6d09025f91
files thread/client-common/src/main/java/com/redhat/thermostat/thread/client/common/VMThreadCapabilitiesView.java thread/client-controllers/src/main/java/com/redhat/thermostat/thread/client/controller/impl/ThreadInformationController.java thread/client-controllers/src/main/java/com/redhat/thermostat/thread/client/controller/impl/ThreadTableController.java thread/client-controllers/src/main/java/com/redhat/thermostat/thread/client/controller/impl/VMThreadCapabilitiesController.java thread/client-controllers/src/test/java/com/redhat/thermostat/thread/client/controller/impl/ThreadTableControllerTest.java thread/client-controllers/src/test/java/com/redhat/thermostat/thread/client/controller/impl/VMThreadCapabilitiesControllerTest.java thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/impl/SwingVMThreadCapabilitiesView.java thread/collector/pom.xml thread/collector/src/main/java/com/redhat/thermostat/thread/collector/HarvesterCommand.java thread/collector/src/main/java/com/redhat/thermostat/thread/collector/ThreadCollector.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/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/dao/ThreadDao.java thread/collector/src/main/java/com/redhat/thermostat/thread/dao/impl/ThreadDaoImpl.java thread/collector/src/main/java/com/redhat/thermostat/thread/harvester/Harvester.java thread/collector/src/main/java/com/redhat/thermostat/thread/harvester/HarvesterCommand.java thread/collector/src/main/java/com/redhat/thermostat/thread/harvester/ThreadHarvester.java thread/collector/src/main/java/com/redhat/thermostat/thread/model/ThreadInfoData.java thread/collector/src/main/java/com/redhat/thermostat/thread/model/ThreadSummary.java thread/collector/src/main/java/com/redhat/thermostat/thread/model/VMThreadCapabilities.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/impl/ThreadCollectorTest.java thread/collector/src/test/java/com/redhat/thermostat/thread/dao/impl/ThreadDaoImplTest.java thread/collector/src/test/java/com/redhat/thermostat/thread/harvester/HarvesterTest.java thread/collector/src/test/java/com/redhat/thermostat/thread/harvester/ThreadHarvesterTest.java thread/harvester/pom.xml thread/harvester/src/main/java/com/redhat/thermostat/thread/harvester/Harvester.java thread/harvester/src/main/java/com/redhat/thermostat/thread/harvester/ThreadHarvester.java thread/harvester/src/main/java/com/redhat/thermostat/thread/harvester/management/MXBeanConnection.java thread/harvester/src/main/java/com/redhat/thermostat/thread/harvester/management/MXBeanConnector.java thread/harvester/src/test/java/com/redhat/thermostat/thread/harvester/HarvesterTest.java thread/harvester/src/test/java/com/redhat/thermostat/thread/harvester/ThreadHarvesterTest.java
diffstat 38 files changed, 1528 insertions(+), 1722 deletions(-) [+]
line wrap: on
line diff
--- a/thread/client-common/src/main/java/com/redhat/thermostat/thread/client/common/VMThreadCapabilitiesView.java	Wed Aug 29 21:22:47 2012 +0200
+++ b/thread/client-common/src/main/java/com/redhat/thermostat/thread/client/common/VMThreadCapabilitiesView.java	Wed Aug 29 19:42:32 2012 -0400
@@ -37,7 +37,7 @@
 package com.redhat.thermostat.thread.client.common;
 
 import com.redhat.thermostat.client.osgi.service.BasicView;
-import com.redhat.thermostat.thread.collector.VMThreadCapabilities;
+import com.redhat.thermostat.thread.model.VMThreadCapabilities;
 
 public abstract class VMThreadCapabilitiesView extends BasicView {
 
--- a/thread/client-controllers/src/main/java/com/redhat/thermostat/thread/client/controller/impl/ThreadInformationController.java	Wed Aug 29 21:22:47 2012 +0200
+++ b/thread/client-controllers/src/main/java/com/redhat/thermostat/thread/client/controller/impl/ThreadInformationController.java	Wed Aug 29 19:42:32 2012 -0400
@@ -61,8 +61,8 @@
 
 import com.redhat.thermostat.thread.collector.ThreadCollector;
 import com.redhat.thermostat.thread.collector.ThreadCollectorFactory;
-import com.redhat.thermostat.thread.collector.ThreadSummary;
-import com.redhat.thermostat.thread.collector.VMThreadCapabilities;
+import com.redhat.thermostat.thread.model.ThreadSummary;
+import com.redhat.thermostat.thread.model.VMThreadCapabilities;
 
 public class ThreadInformationController implements VmInformationServiceController {
 
--- a/thread/client-controllers/src/main/java/com/redhat/thermostat/thread/client/controller/impl/ThreadTableController.java	Wed Aug 29 21:22:47 2012 +0200
+++ b/thread/client-controllers/src/main/java/com/redhat/thermostat/thread/client/controller/impl/ThreadTableController.java	Wed Aug 29 19:42:32 2012 -0400
@@ -51,7 +51,7 @@
 import com.redhat.thermostat.thread.client.common.ThreadTableBean;
 import com.redhat.thermostat.thread.client.common.ThreadTableView;
 import com.redhat.thermostat.thread.collector.ThreadCollector;
-import com.redhat.thermostat.thread.collector.ThreadInfo;
+import com.redhat.thermostat.thread.model.ThreadInfoData;
 
 public class ThreadTableController implements CommonController {
     
@@ -99,7 +99,7 @@
     private class ThreadTableControllerAction implements Runnable {
         @Override
         public void run() {
-            List<ThreadInfo> infos = collector.getThreadInfo();
+            List<ThreadInfoData> infos = collector.getThreadInfo();
             if(infos.size() > 0) {
                 
                 long lastPollTimestamp = infos.get(0).getTimeStamp();
@@ -110,11 +110,11 @@
                 // first, get a map of all threads with the respective info
                 // the list will contain an ordered-by-timestamp list
                 // with the known history for each thread
-                Map<ThreadInfo, List<ThreadInfo>> stats = new HashMap<>();
-                for (ThreadInfo info : infos) {
-                    List<ThreadInfo> beanList = stats.get(info);
+                Map<ThreadInfoData, List<ThreadInfoData>> stats = new HashMap<>();
+                for (ThreadInfoData info : infos) {
+                    List<ThreadInfoData> beanList = stats.get(info);
                     if (beanList == null) {
-                        beanList = new ArrayList<ThreadInfo>();
+                        beanList = new ArrayList<ThreadInfoData>();
                         stats.put(info, beanList);
                     }                    
                     beanList.add(info);
@@ -123,7 +123,7 @@
                 List<ThreadTableBean> tableBeans = new ArrayList<>();
                 
                 // now we have the list, we can do all the analysis we need
-                for (ThreadInfo key : stats.keySet()) {
+                for (ThreadInfoData key : stats.keySet()) {
                     ThreadTableBean bean = new ThreadTableBean();
                     
                     bean.setName(key.getName());
@@ -133,7 +133,7 @@
                     bean.setBlockedCount(key.getBlockedCount());
                     
                     // get start time and stop time, if any
-                    List<ThreadInfo> beanList = stats.get(key);
+                    List<ThreadInfoData> beanList = stats.get(key);
                     long last = beanList.get(0).getTimeStamp();
                     long first = beanList.get(beanList.size() - 1).getTimeStamp();
                     
@@ -146,7 +146,7 @@
                     // time for some stats
                     double running = 0;
                     double waiting = 0;
-                    for (ThreadInfo info : beanList) {
+                    for (ThreadInfoData info : beanList) {
                         State state = info.getState();
                         switch (state) {
                         case RUNNABLE:
--- a/thread/client-controllers/src/main/java/com/redhat/thermostat/thread/client/controller/impl/VMThreadCapabilitiesController.java	Wed Aug 29 21:22:47 2012 +0200
+++ b/thread/client-controllers/src/main/java/com/redhat/thermostat/thread/client/controller/impl/VMThreadCapabilitiesController.java	Wed Aug 29 19:42:32 2012 -0400
@@ -41,7 +41,7 @@
 import com.redhat.thermostat.common.ActionListener;
 import com.redhat.thermostat.thread.client.common.VMThreadCapabilitiesView;
 import com.redhat.thermostat.thread.collector.ThreadCollector;
-import com.redhat.thermostat.thread.collector.VMThreadCapabilities;
+import com.redhat.thermostat.thread.model.VMThreadCapabilities;
 
 public class VMThreadCapabilitiesController implements CommonController {
 
--- a/thread/client-controllers/src/test/java/com/redhat/thermostat/thread/client/controller/impl/ThreadTableControllerTest.java	Wed Aug 29 21:22:47 2012 +0200
+++ b/thread/client-controllers/src/test/java/com/redhat/thermostat/thread/client/controller/impl/ThreadTableControllerTest.java	Wed Aug 29 19:42:32 2012 -0400
@@ -55,7 +55,7 @@
 import com.redhat.thermostat.common.appctx.ApplicationContextUtil;
 import com.redhat.thermostat.thread.client.common.ThreadTableView;
 import com.redhat.thermostat.thread.collector.ThreadCollector;
-import com.redhat.thermostat.thread.collector.ThreadInfo;
+import com.redhat.thermostat.thread.model.ThreadInfoData;
 
 public class ThreadTableControllerTest {
 
--- a/thread/client-controllers/src/test/java/com/redhat/thermostat/thread/client/controller/impl/VMThreadCapabilitiesControllerTest.java	Wed Aug 29 21:22:47 2012 +0200
+++ b/thread/client-controllers/src/test/java/com/redhat/thermostat/thread/client/controller/impl/VMThreadCapabilitiesControllerTest.java	Wed Aug 29 19:42:32 2012 -0400
@@ -51,7 +51,7 @@
 import com.redhat.thermostat.common.appctx.ApplicationContextUtil;
 import com.redhat.thermostat.thread.client.common.VMThreadCapabilitiesView;
 import com.redhat.thermostat.thread.collector.ThreadCollector;
-import com.redhat.thermostat.thread.collector.VMThreadCapabilities;
+import com.redhat.thermostat.thread.model.VMThreadCapabilities;
 
 public class VMThreadCapabilitiesControllerTest {
 
--- a/thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/impl/SwingVMThreadCapabilitiesView.java	Wed Aug 29 21:22:47 2012 +0200
+++ b/thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/impl/SwingVMThreadCapabilitiesView.java	Wed Aug 29 19:42:32 2012 -0400
@@ -44,7 +44,7 @@
 import com.redhat.thermostat.client.ui.ComponentVisibleListener;
 import com.redhat.thermostat.client.ui.SwingComponent;
 import com.redhat.thermostat.thread.client.common.VMThreadCapabilitiesView;
-import com.redhat.thermostat.thread.collector.VMThreadCapabilities;
+import com.redhat.thermostat.thread.model.VMThreadCapabilities;
 
 public class SwingVMThreadCapabilitiesView extends VMThreadCapabilitiesView implements SwingComponent {
 
--- a/thread/collector/pom.xml	Wed Aug 29 21:22:47 2012 +0200
+++ b/thread/collector/pom.xml	Wed Aug 29 19:42:32 2012 -0400
@@ -110,13 +110,12 @@
             <Export-Package>
               com.redhat.thermostat.thread.collector,
               com.redhat.thermostat.thread.dao,
-              com.redhat.thermostat.thread.harvester,
+              com.redhat.thermostat.thread.model,
             </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>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/thread/collector/src/main/java/com/redhat/thermostat/thread/collector/HarvesterCommand.java	Wed Aug 29 19:42:32 2012 -0400
@@ -0,0 +1,14 @@
+package com.redhat.thermostat.thread.collector;
+
+public enum HarvesterCommand {
+
+    START,
+    STOP,
+    VM_CAPS,
+    
+    AGENT_ID,
+    VM_ID;
+
+    public static final String RECEIVER = "com.redhat.thermostat.thread.harvester.ThreadHarvester";
+
+}
--- a/thread/collector/src/main/java/com/redhat/thermostat/thread/collector/ThreadCollector.java	Wed Aug 29 21:22:47 2012 +0200
+++ b/thread/collector/src/main/java/com/redhat/thermostat/thread/collector/ThreadCollector.java	Wed Aug 29 19:42:32 2012 -0400
@@ -38,6 +38,10 @@
 
 import java.util.List;
 
+import com.redhat.thermostat.thread.model.ThreadInfoData;
+import com.redhat.thermostat.thread.model.ThreadSummary;
+import com.redhat.thermostat.thread.model.VMThreadCapabilities;
+
 public interface ThreadCollector {
     
     VMThreadCapabilities getVMThreadCapabilities();
@@ -50,15 +54,15 @@
     List<ThreadSummary> getThreadSummary();
     
     /**
-     * Return a list of {@link ThreadInfo}, sorted in descending order their by
-     * {@link ThreadInfo#getTimeStamp()}, whose elements are at most
+     * Return a list of {@link ThreadInfoData}, sorted in descending order their by
+     * {@link ThreadInfoData#getTimeStamp()}, whose elements are at most
      * "{@code since}" old.
      */
-    List<ThreadInfo> getThreadInfo(long since);
+    List<ThreadInfoData> getThreadInfo(long since);
 
     /**
-     * Return a list of all the {@link ThreadInfo} collected, sorted in
-     * descending order their by {@link ThreadInfo#getTimeStamp()}.
+     * Return a list of all the {@link ThreadInfoData} collected, sorted in
+     * descending order their by {@link ThreadInfoData#getTimeStamp()}.
      */
-    List<ThreadInfo> getThreadInfo();
+    List<ThreadInfoData> getThreadInfo();
 }
--- a/thread/collector/src/main/java/com/redhat/thermostat/thread/collector/ThreadInfo.java	Wed Aug 29 21:22:47 2012 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,62 +0,0 @@
-/*
- * Copyright 2012 Red Hat, Inc.
- *
- * This file is part of Thermostat.
- *
- * Thermostat is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published
- * by the Free Software Foundation; either version 2, or (at your
- * option) any later version.
- *
- * Thermostat is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Thermostat; see the file COPYING.  If not see
- * <http://www.gnu.org/licenses/>.
- *
- * Linking this code with other modules is making a combined work
- * based on this code.  Thus, the terms and conditions of the GNU
- * General Public License cover the whole combination.
- *
- * As a special exception, the copyright holders of this code give
- * you permission to link this code with independent modules to
- * produce an executable, regardless of the license terms of these
- * independent modules, and to copy and distribute the resulting
- * executable under terms of your choice, provided that you also
- * meet, for each linked independent module, the terms and conditions
- * of the license of that module.  An independent module is a module
- * which is not derived from or based on this code.  If you modify
- * this code, you may extend this exception to your version of the
- * library, but you are not obligated to do so.  If you do not wish
- * to do so, delete this exception statement from your version.
- */
-
-package com.redhat.thermostat.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();
-}
--- a/thread/collector/src/main/java/com/redhat/thermostat/thread/collector/ThreadSummary.java	Wed Aug 29 21:22:47 2012 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,71 +0,0 @@
-/*
- * Copyright 2012 Red Hat, Inc.
- *
- * This file is part of Thermostat.
- *
- * Thermostat is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published
- * by the Free Software Foundation; either version 2, or (at your
- * option) any later version.
- *
- * Thermostat is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Thermostat; see the file COPYING.  If not see
- * <http://www.gnu.org/licenses/>.
- *
- * Linking this code with other modules is making a combined work
- * based on this code.  Thus, the terms and conditions of the GNU
- * General Public License cover the whole combination.
- *
- * As a special exception, the copyright holders of this code give
- * you permission to link this code with independent modules to
- * produce an executable, regardless of the license terms of these
- * independent modules, and to copy and distribute the resulting
- * executable under terms of your choice, provided that you also
- * meet, for each linked independent module, the terms and conditions
- * of the license of that module.  An independent module is a module
- * which is not derived from or based on this code.  If you modify
- * this code, you may extend this exception to your version of the
- * library, but you are not obligated to do so.  If you do not wish
- * to do so, delete this exception statement from your version.
- */
-
-package com.redhat.thermostat.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();
-}
--- a/thread/collector/src/main/java/com/redhat/thermostat/thread/collector/VMThreadCapabilities.java	Wed Aug 29 21:22:47 2012 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,48 +0,0 @@
-/*
- * Copyright 2012 Red Hat, Inc.
- *
- * This file is part of Thermostat.
- *
- * Thermostat is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published
- * by the Free Software Foundation; either version 2, or (at your
- * option) any later version.
- *
- * Thermostat is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Thermostat; see the file COPYING.  If not see
- * <http://www.gnu.org/licenses/>.
- *
- * Linking this code with other modules is making a combined work
- * based on this code.  Thus, the terms and conditions of the GNU
- * General Public License cover the whole combination.
- *
- * As a special exception, the copyright holders of this code give
- * you permission to link this code with independent modules to
- * produce an executable, regardless of the license terms of these
- * independent modules, and to copy and distribute the resulting
- * executable under terms of your choice, provided that you also
- * meet, for each linked independent module, the terms and conditions
- * of the license of that module.  An independent module is a module
- * which is not derived from or based on this code.  If you modify
- * this code, you may extend this exception to your version of the
- * library, but you are not obligated to do so.  If you do not wish
- * to do so, delete this exception statement from your version.
- */
-
-package com.redhat.thermostat.thread.collector;
-
-import java.util.List;
-
-public interface VMThreadCapabilities {
-
-    boolean supportCPUTime();
-    boolean supportContentionMonitor();
-    boolean supportThreadAllocatedMemory();
-    
-    List<String> getSupportedFeaturesList();
-}
--- a/thread/collector/src/main/java/com/redhat/thermostat/thread/collector/impl/ThreadMXBeanCollector.java	Wed Aug 29 21:22:47 2012 +0200
+++ b/thread/collector/src/main/java/com/redhat/thermostat/thread/collector/impl/ThreadMXBeanCollector.java	Wed Aug 29 19:42:32 2012 -0400
@@ -48,12 +48,12 @@
 import com.redhat.thermostat.common.dao.HostRef;
 import com.redhat.thermostat.common.dao.VmRef;
 import com.redhat.thermostat.common.utils.OSGIUtils;
+import com.redhat.thermostat.thread.collector.HarvesterCommand;
 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.thread.harvester.HarvesterCommand;
-import com.redhat.thermostat.thread.harvester.ThreadHarvester;
+import com.redhat.thermostat.thread.model.ThreadInfoData;
+import com.redhat.thermostat.thread.model.ThreadSummary;
+import com.redhat.thermostat.thread.model.VMThreadCapabilities;
 
 public class ThreadMXBeanCollector implements ThreadCollector {
 
@@ -75,7 +75,7 @@
         InetSocketAddress target = new InetSocketAddress(host[0], Integer.parseInt(host[1]));
         Request harvester = new Request(RequestType.RESPONSE_EXPECTED, target);
 
-        harvester.setReceiver(ThreadHarvester.class.getName());
+        harvester.setReceiver(HarvesterCommand.RECEIVER);
         
         return harvester;
     }
@@ -108,7 +108,7 @@
         ThreadSummary summary = threadDao.loadLastestSummary(ref);
         if (summary == null) {
             // default to all 0
-            summary = new ThreadMXSummary();
+            summary = new ThreadSummary();
         }
         return summary;
     }
@@ -125,12 +125,12 @@
     }
     
     @Override
-    public List<com.redhat.thermostat.thread.collector.ThreadInfo> getThreadInfo() {
+    public List<ThreadInfoData> getThreadInfo() {
         return getThreadInfo(0);
     }
     
     @Override
-    public List<com.redhat.thermostat.thread.collector.ThreadInfo> getThreadInfo(long since) {
+    public List<ThreadInfoData> getThreadInfo(long since) {
         return threadDao.loadThreadInfo(ref, since);
     }
 
@@ -159,7 +159,7 @@
                 latch.await();
                 caps = threadDao.loadCapabilities(ref);
             } catch (InterruptedException ignore) {
-                caps = new VMThreadMXCapabilities();
+                caps = new VMThreadCapabilities();
             }
         }
         return caps;
--- a/thread/collector/src/main/java/com/redhat/thermostat/thread/collector/impl/ThreadMXInfo.java	Wed Aug 29 21:22:47 2012 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,188 +0,0 @@
-/*
- * Copyright 2012 Red Hat, Inc.
- *
- * This file is part of Thermostat.
- *
- * Thermostat is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published
- * by the Free Software Foundation; either version 2, or (at your
- * option) any later version.
- *
- * Thermostat is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Thermostat; see the file COPYING.  If not see
- * <http://www.gnu.org/licenses/>.
- *
- * Linking this code with other modules is making a combined work
- * based on this code.  Thus, the terms and conditions of the GNU
- * General Public License cover the whole combination.
- *
- * As a special exception, the copyright holders of this code give
- * you permission to link this code with independent modules to
- * produce an executable, regardless of the license terms of these
- * independent modules, and to copy and distribute the resulting
- * executable under terms of your choice, provided that you also
- * meet, for each linked independent module, the terms and conditions
- * of the license of that module.  An independent module is a module
- * which is not derived from or based on this code.  If you modify
- * this code, you may extend this exception to your version of the
- * library, but you are not obligated to do so.  If you do not wish
- * to do so, delete this exception statement from your version.
- */
-
-package com.redhat.thermostat.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;
-    }
-}
--- a/thread/collector/src/main/java/com/redhat/thermostat/thread/collector/impl/ThreadMXSummary.java	Wed Aug 29 21:22:47 2012 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,80 +0,0 @@
-/*
- * Copyright 2012 Red Hat, Inc.
- *
- * This file is part of Thermostat.
- *
- * Thermostat is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published
- * by the Free Software Foundation; either version 2, or (at your
- * option) any later version.
- *
- * Thermostat is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Thermostat; see the file COPYING.  If not see
- * <http://www.gnu.org/licenses/>.
- *
- * Linking this code with other modules is making a combined work
- * based on this code.  Thus, the terms and conditions of the GNU
- * General Public License cover the whole combination.
- *
- * As a special exception, the copyright holders of this code give
- * you permission to link this code with independent modules to
- * produce an executable, regardless of the license terms of these
- * independent modules, and to copy and distribute the resulting
- * executable under terms of your choice, provided that you also
- * meet, for each linked independent module, the terms and conditions
- * of the license of that module.  An independent module is a module
- * which is not derived from or based on this code.  If you modify
- * this code, you may extend this exception to your version of the
- * library, but you are not obligated to do so.  If you do not wish
- * to do so, delete this exception statement from your version.
- */
-
-package com.redhat.thermostat.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 + "]";
-    }
-}
--- a/thread/collector/src/main/java/com/redhat/thermostat/thread/collector/impl/VMThreadMXCapabilities.java	Wed Aug 29 21:22:47 2012 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,81 +0,0 @@
-/*
- * Copyright 2012 Red Hat, Inc.
- *
- * This file is part of Thermostat.
- *
- * Thermostat is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published
- * by the Free Software Foundation; either version 2, or (at your
- * option) any later version.
- *
- * Thermostat is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Thermostat; see the file COPYING.  If not see
- * <http://www.gnu.org/licenses/>.
- *
- * Linking this code with other modules is making a combined work
- * based on this code.  Thus, the terms and conditions of the GNU
- * General Public License cover the whole combination.
- *
- * As a special exception, the copyright holders of this code give
- * you permission to link this code with independent modules to
- * produce an executable, regardless of the license terms of these
- * independent modules, and to copy and distribute the resulting
- * executable under terms of your choice, provided that you also
- * meet, for each linked independent module, the terms and conditions
- * of the license of that module.  An independent module is a module
- * which is not derived from or based on this code.  If you modify
- * this code, you may extend this exception to your version of the
- * library, but you are not obligated to do so.  If you do not wish
- * to do so, delete this exception statement from your version.
- */
-
-package com.redhat.thermostat.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);
-    }
-}
--- a/thread/collector/src/main/java/com/redhat/thermostat/thread/dao/ThreadDao.java	Wed Aug 29 21:22:47 2012 +0200
+++ b/thread/collector/src/main/java/com/redhat/thermostat/thread/dao/ThreadDao.java	Wed Aug 29 19:42:32 2012 -0400
@@ -42,9 +42,9 @@
 import com.redhat.thermostat.common.storage.Category;
 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.model.ThreadInfoData;
+import com.redhat.thermostat.thread.model.ThreadSummary;
+import com.redhat.thermostat.thread.model.VMThreadCapabilities;
 
 public interface ThreadDao {
 
@@ -105,8 +105,8 @@
                          THREAD_USER_TIME_KEY, THREAD_BLOCKED_COUNT_KEY,
                          THREAD_WAIT_COUNT_KEY, THREAD_STACK_TRACE_ID_KEY);
     
-    void saveThreadInfo(String vmId, String agentId, ThreadInfo info);
-    List<ThreadInfo> loadThreadInfo(VmRef ref, long since);
+    void saveThreadInfo(String vmId, String agentId, ThreadInfoData info);
+    List<ThreadInfoData> loadThreadInfo(VmRef ref, long since);
     
     Storage getStorage();
 }
--- a/thread/collector/src/main/java/com/redhat/thermostat/thread/dao/impl/ThreadDaoImpl.java	Wed Aug 29 21:22:47 2012 +0200
+++ b/thread/collector/src/main/java/com/redhat/thermostat/thread/dao/impl/ThreadDaoImpl.java	Wed Aug 29 19:42:32 2012 -0400
@@ -45,13 +45,10 @@
 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;
+import com.redhat.thermostat.thread.model.ThreadInfoData;
+import com.redhat.thermostat.thread.model.ThreadSummary;
+import com.redhat.thermostat.thread.model.VMThreadCapabilities;
 
 public class ThreadDaoImpl implements ThreadDao {
 
@@ -66,7 +63,7 @@
     @Override
     public VMThreadCapabilities loadCapabilities(VmRef vm) {
         
-        VMThreadMXCapabilities caps = null;
+        VMThreadCapabilities caps = null;
         
         Chunk query = new Chunk(THREAD_CAPABILITIES, false);
         query.put(Key.VM_ID, vm.getId());
@@ -74,7 +71,7 @@
         
         Chunk found = storage.find(query);
         if (found != null) {
-            caps = new VMThreadMXCapabilities();
+            caps = new VMThreadCapabilities();
             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);
@@ -107,13 +104,13 @@
     
     @Override
     public ThreadSummary loadLastestSummary(VmRef ref) {
-        ThreadMXSummary summary = null;
+        ThreadSummary 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 = new ThreadSummary();
             summary.setTimestamp(found.get(Key.TIMESTAMP));
             summary.setCurrentLiveThreads(found.get(LIVE_THREADS_KEY));
             summary.setDaemonThreads(found.get(DAEMON_THREADS_KEY));
@@ -132,7 +129,7 @@
 
         Cursor cursor = storage.findAll(query).sort(Key.TIMESTAMP, Cursor.SortDirection.DESCENDING);
         while (cursor.hasNext()) {
-            ThreadMXSummary summary = new ThreadMXSummary();
+            ThreadSummary summary = new ThreadSummary();
             
             Chunk found = cursor.next();
             summary.setTimestamp(found.get(Key.TIMESTAMP));
@@ -145,7 +142,7 @@
     }
     
     @Override
-    public void saveThreadInfo(String vmId, String agentId, ThreadInfo info) {
+    public void saveThreadInfo(String vmId, String agentId, ThreadInfoData info) {
         Chunk chunk = prepareChunk(THREAD_INFO, false, vmId, agentId);
         
         chunk.put(Key.TIMESTAMP, info.getTimeStamp());
@@ -163,15 +160,15 @@
     }
 
     @Override
-    public List<ThreadInfo> loadThreadInfo(VmRef ref, long since) {
-        List<ThreadInfo> result = new ArrayList<>();
+    public List<ThreadInfoData> loadThreadInfo(VmRef ref, long since) {
+        List<ThreadInfoData> 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();
+            ThreadInfoData info = new ThreadInfoData();
             
             Chunk found = cursor.next();
             info.setTimeStamp(found.get(Key.TIMESTAMP));
--- a/thread/collector/src/main/java/com/redhat/thermostat/thread/harvester/Harvester.java	Wed Aug 29 21:22:47 2012 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,253 +0,0 @@
-/*
- * Copyright 2012 Red Hat, Inc.
- *
- * This file is part of Thermostat.
- *
- * Thermostat is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published
- * by the Free Software Foundation; either version 2, or (at your
- * option) any later version.
- *
- * Thermostat is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Thermostat; see the file COPYING.  If not see
- * <http://www.gnu.org/licenses/>.
- *
- * Linking this code with other modules is making a combined work
- * based on this code.  Thus, the terms and conditions of the GNU
- * General Public License cover the whole combination.
- *
- * As a special exception, the copyright holders of this code give
- * you permission to link this code with independent modules to
- * produce an executable, regardless of the license terms of these
- * independent modules, and to copy and distribute the resulting
- * executable under terms of your choice, provided that you also
- * meet, for each linked independent module, the terms and conditions
- * of the license of that module.  An independent module is a module
- * which is not derived from or based on this code.  If you modify
- * this code, you may extend this exception to your version of the
- * library, but you are not obligated to do so.  If you do not wish
- * to do so, delete this exception statement from your version.
- */
-
-package com.redhat.thermostat.thread.harvester;
-
-import java.io.IOException;
-import java.lang.management.ManagementFactory;
-import java.lang.management.ThreadInfo;
-import java.lang.management.ThreadMXBean;
-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.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;
-import com.redhat.thermostat.utils.management.MXBeanConnection;
-import com.redhat.thermostat.utils.management.MXBeanConnector;
-
-@SuppressWarnings("restriction")
-class Harvester {
-    
-    private boolean isConnected;
-    private ScheduledExecutorService threadPool;
-    private ScheduledFuture<?> harvester;
-    
-    MXBeanConnector connector;
-    
-    private MXBeanConnection connection;
-    private ThreadMXBean collectorBean;
-    private ThreadDao threadDao;
-    private String vmId;
-    private String agentId;
-    
-    Harvester(ThreadDao threadDao, ScheduledExecutorService threadPool, String vmId, String agentId) {
-        this.connector = new MXBeanConnector(vmId);
-        this.threadDao = threadDao;
-        this.vmId = vmId;
-        this.agentId = agentId;
-        this.threadPool = threadPool;
-    }
-    
-    synchronized void start() {
-        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 HarvesterAction(), 0, 1, TimeUnit.SECONDS);
-    }
-    
-    boolean isConnected() {
-        return isConnected;
-    }
-    
-    synchronized void stop() {
-        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;
-    }
-    
-    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;
-    }
-    
-    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(vmId, agentId, 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(vmId, agentId, info);
-          }
-          
-      } catch (MalformedObjectNameException e) {
-          e.printStackTrace();
-      }
-    }
-    
-    private class HarvesterAction implements Runnable {
-        @Override
-        public void run() {
-            harvestData();
-        }
-    }
-    
-    synchronized void saveVmCaps() {
-        
-        boolean closeAfter = false;
-        if (!connector.isAttached()) {
-            closeAfter = true; 
-            try {
-                connector.attach();
-            } catch (Exception ignore) {
-                ignore.printStackTrace();
-            }
-        }
-
-        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);
-            }
-
-            threadDao.saveCapabilities(vmId, agentId, caps);
-
-        } catch (Exception e) {
-            e.printStackTrace();
-        }
-
-        if (closeAfter) {
-            try {
-                connector.close();
-            } catch (Exception ignore) {
-                ignore.printStackTrace();
-            }
-        }
-    }
-}
--- a/thread/collector/src/main/java/com/redhat/thermostat/thread/harvester/HarvesterCommand.java	Wed Aug 29 21:22:47 2012 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,11 +0,0 @@
-package com.redhat.thermostat.thread.harvester;
-
-public enum HarvesterCommand {
-
-    START,
-    STOP,
-    VM_CAPS,
-    
-    AGENT_ID,
-    VM_ID;
-}
--- a/thread/collector/src/main/java/com/redhat/thermostat/thread/harvester/ThreadHarvester.java	Wed Aug 29 21:22:47 2012 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,119 +0,0 @@
-/*
- * Copyright 2012 Red Hat, Inc.
- *
- * This file is part of Thermostat.
- *
- * Thermostat is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published
- * by the Free Software Foundation; either version 2, or (at your
- * option) any later version.
- *
- * Thermostat is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Thermostat; see the file COPYING.  If not see
- * <http://www.gnu.org/licenses/>.
- *
- * Linking this code with other modules is making a combined work
- * based on this code.  Thus, the terms and conditions of the GNU
- * General Public License cover the whole combination.
- *
- * As a special exception, the copyright holders of this code give
- * you permission to link this code with independent modules to
- * produce an executable, regardless of the license terms of these
- * independent modules, and to copy and distribute the resulting
- * executable under terms of your choice, provided that you also
- * meet, for each linked independent module, the terms and conditions
- * of the license of that module.  An independent module is a module
- * which is not derived from or based on this code.  If you modify
- * this code, you may extend this exception to your version of the
- * library, but you are not obligated to do so.  If you do not wish
- * to do so, delete this exception statement from your version.
- */
-
-package com.redhat.thermostat.thread.harvester;
-
-import java.util.HashMap;
-import java.util.Map;
-import java.util.concurrent.ScheduledExecutorService;
-
-import com.redhat.thermostat.agent.command.RequestReceiver;
-import com.redhat.thermostat.common.command.Request;
-import com.redhat.thermostat.common.command.Response;
-import com.redhat.thermostat.common.command.Response.ResponseType;
-
-import com.redhat.thermostat.thread.dao.ThreadDao;
-
-public class ThreadHarvester implements RequestReceiver {
-
-    private ScheduledExecutorService executor;
-    Map<String, Harvester> connectors;
-
-    private ThreadDao dao;
-    
-    public ThreadHarvester(ScheduledExecutorService executor, ThreadDao dao) {
-        this.executor = executor;
-        connectors = new HashMap<>();
-        this.dao = dao;
-    }
-    
-    @Override
-    public Response receive(Request request) {
-        
-        String command = request.getParameter(HarvesterCommand.class.getName());
-        switch (HarvesterCommand.valueOf(command)) {
-        case START: {
-            String vmId = request.getParameter(HarvesterCommand.VM_ID.name());
-            String agentId = request.getParameter(HarvesterCommand.AGENT_ID.name());
-            startHarvester(vmId, agentId);
-            break;
-        }   
-        case STOP: {
-            String vmId = request.getParameter(HarvesterCommand.VM_ID.name());
-            stopHarvester(vmId);
-            break;
-        }
-        case VM_CAPS: {
-            // this is blocking
-            String vmId = request.getParameter(HarvesterCommand.VM_ID.name());
-            String agentId = request.getParameter(HarvesterCommand.AGENT_ID.name());
-            saveVmCaps(vmId, agentId);
-            break;
-        }
-        default:
-            break;
-        }
-        
-        return new Response(ResponseType.OK);
-    }
-    
-    private void startHarvester(String vmId, String agentId) {
-        Harvester harvester = getHarvester(vmId, agentId);
-        harvester.start();
-    }
-    
-    private void saveVmCaps(String vmId, String agentId) {
-        Harvester harvester = getHarvester(vmId, agentId);
-        harvester.saveVmCaps();
-    }
-    
-    private void stopHarvester(String vmId) {
-        Harvester harvester = connectors.get(vmId);
-        if (harvester != null) {
-            harvester.stop();
-        }
-    }
-    
-    Harvester getHarvester(String vmId, String agentId) {
-        Harvester harvester = connectors.get(vmId);
-        if (harvester == null) {
-            harvester = new Harvester(dao, executor, vmId, agentId);
-            connectors.put(vmId, harvester);
-        }
-        
-        return harvester;
-    }
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/thread/collector/src/main/java/com/redhat/thermostat/thread/model/ThreadInfoData.java	Wed Aug 29 19:42:32 2012 -0400
@@ -0,0 +1,178 @@
+/*
+ * 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.model;
+
+import java.lang.Thread.State;
+import java.util.Arrays;
+
+import com.redhat.thermostat.common.model.TimeStampedPojo;
+
+
+public class ThreadInfoData implements TimeStampedPojo {
+
+    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;
+    }
+
+    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;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public long getAllocatedBytes() {
+        return allocatedBytes;
+    }
+
+    public long getThreadID() {
+        return threadID;
+    }
+
+    public State getState() {
+        return threadState;
+    }
+
+    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;
+    }
+
+    public long getBlockedCount() {
+        return blockedCount;
+    }
+
+    public long getWaitedCount() {
+        return waitedCount;
+    }
+
+    public long getCpuTime() {
+        return threadCpuTime;
+    }
+
+    public long getUserTime() {
+        return threadUserTime;
+    }
+
+    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;
+        ThreadInfoData other = (ThreadInfoData) 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/model/ThreadSummary.java	Wed Aug 29 19:42:32 2012 -0400
@@ -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.model;
+
+import com.redhat.thermostat.common.model.TimeStampedPojo;
+
+
+public class ThreadSummary implements TimeStampedPojo {
+
+    private long currentLiveThreads;
+    private long daemonThreads;
+    
+    private long timestamp;
+
+    public long currentLiveThreads() {
+        return currentLiveThreads;
+    }
+    
+    public void setCurrentLiveThreads(long currentLiveThreads) {
+        this.currentLiveThreads = currentLiveThreads;
+    }
+
+    public long currentDaemonThreads() {
+        return daemonThreads;
+    }
+    
+    public void setDaemonThreads(long daemonThreads) {
+        this.daemonThreads = daemonThreads;
+    }
+
+    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/model/VMThreadCapabilities.java	Wed Aug 29 19:42:32 2012 -0400
@@ -0,0 +1,74 @@
+/*
+ * 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.model;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import com.redhat.thermostat.thread.dao.ThreadDao;
+
+public class VMThreadCapabilities {
+    
+    private Set<String> features = new HashSet<>();
+
+    public boolean supportCPUTime() {
+        return features.contains(ThreadDao.CPU_TIME);
+    }
+
+    public boolean supportContentionMonitor() {
+        return features.contains(ThreadDao.CONTENTION_MONITOR);
+    }
+
+    public String toString() {
+        return "[supportCPU: " + supportCPUTime() + ", supportContention: " + supportContentionMonitor() +
+               ", supportThreadAllocatedMemory: " + supportThreadAllocatedMemory() + "]";
+    }
+
+    public boolean supportThreadAllocatedMemory() {
+        return features.contains(ThreadDao.THREAD_ALLOCATED_MEMORY);
+    }
+
+    public List<String> getSupportedFeaturesList() {
+        return new ArrayList<>(features);
+    }
+    
+    public void addFeature(String feature) {
+        features.add(feature);
+    }
+}
--- a/thread/collector/src/main/java/com/redhat/thermostat/utils/management/MXBeanConnection.java	Wed Aug 29 21:22:47 2012 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,67 +0,0 @@
-/*
- * Copyright 2012 Red Hat, Inc.
- *
- * This file is part of Thermostat.
- *
- * Thermostat is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published
- * by the Free Software Foundation; either version 2, or (at your
- * option) any later version.
- *
- * Thermostat is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Thermostat; see the file COPYING.  If not see
- * <http://www.gnu.org/licenses/>.
- *
- * Linking this code with other modules is making a combined work
- * based on this code.  Thus, the terms and conditions of the GNU
- * General Public License cover the whole combination.
- *
- * As a special exception, the copyright holders of this code give
- * you permission to link this code with independent modules to
- * produce an executable, regardless of the license terms of these
- * independent modules, and to copy and distribute the resulting
- * executable under terms of your choice, provided that you also
- * meet, for each linked independent module, the terms and conditions
- * of the license of that module.  An independent module is a module
- * which is not derived from or based on this code.  If you modify
- * this code, you may extend this exception to your version of the
- * library, but you are not obligated to do so.  If you do not wish
- * to do so, delete this exception statement from your version.
- */
-
-package com.redhat.thermostat.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();
-    }
-}
--- a/thread/collector/src/main/java/com/redhat/thermostat/utils/management/MXBeanConnector.java	Wed Aug 29 21:22:47 2012 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,121 +0,0 @@
-/*
- * Copyright 2012 Red Hat, Inc.
- *
- * This file is part of Thermostat.
- *
- * Thermostat is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published
- * by the Free Software Foundation; either version 2, or (at your
- * option) any later version.
- *
- * Thermostat is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Thermostat; see the file COPYING.  If not see
- * <http://www.gnu.org/licenses/>.
- *
- * Linking this code with other modules is making a combined work
- * based on this code.  Thus, the terms and conditions of the GNU
- * General Public License cover the whole combination.
- *
- * As a special exception, the copyright holders of this code give
- * you permission to link this code with independent modules to
- * produce an executable, regardless of the license terms of these
- * independent modules, and to copy and distribute the resulting
- * executable under terms of your choice, provided that you also
- * meet, for each linked independent module, the terms and conditions
- * of the license of that module.  An independent module is a module
- * which is not derived from or based on this code.  If you modify
- * this code, you may extend this exception to your version of the
- * library, but you are not obligated to do so.  If you do not wish
- * to do so, delete this exception statement from your version.
- */
-
-package com.redhat.thermostat.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 VirtualMachine vm;
-    
-    private boolean attached;
-    
-    private String reference;
-    
-    public MXBeanConnector(String reference) {
-        this.reference = reference;
-    }
-    
-    public MXBeanConnector(VmRef reference) {
-        this.reference = reference.getStringID();
-    }
-    
-    public synchronized void attach() throws Exception {
-        if (attached)
-            throw new IOException("Already attached");
-        
-        vm = VirtualMachine.attach(reference);
-        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;
-        }
-    }
-}
--- a/thread/collector/src/test/java/com/redhat/thermostat/thread/collector/impl/ThreadCollectorTest.java	Wed Aug 29 21:22:47 2012 +0200
+++ b/thread/collector/src/test/java/com/redhat/thermostat/thread/collector/impl/ThreadCollectorTest.java	Wed Aug 29 19:42:32 2012 -0400
@@ -56,10 +56,10 @@
 import com.redhat.thermostat.common.command.RequestResponseListener;
 import com.redhat.thermostat.common.dao.HostRef;
 import com.redhat.thermostat.common.dao.VmRef;
+import com.redhat.thermostat.thread.collector.HarvesterCommand;
 import com.redhat.thermostat.thread.collector.ThreadCollector;
-import com.redhat.thermostat.thread.collector.VMThreadCapabilities;
 import com.redhat.thermostat.thread.dao.ThreadDao;
-import com.redhat.thermostat.thread.harvester.HarvesterCommand;
+import com.redhat.thermostat.thread.model.VMThreadCapabilities;
 
 public class ThreadCollectorTest {
 
--- a/thread/collector/src/test/java/com/redhat/thermostat/thread/dao/impl/ThreadDaoImplTest.java	Wed Aug 29 21:22:47 2012 +0200
+++ b/thread/collector/src/test/java/com/redhat/thermostat/thread/dao/impl/ThreadDaoImplTest.java	Wed Aug 29 19:42:32 2012 -0400
@@ -52,8 +52,8 @@
 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;
+import com.redhat.thermostat.thread.model.VMThreadCapabilities;
 
 public class ThreadDaoImplTest {
 
--- a/thread/collector/src/test/java/com/redhat/thermostat/thread/harvester/HarvesterTest.java	Wed Aug 29 21:22:47 2012 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,400 +0,0 @@
-/*
- * Copyright 2012 Red Hat, Inc.
- *
- * This file is part of Thermostat.
- *
- * Thermostat is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published
- * by the Free Software Foundation; either version 2, or (at your
- * option) any later version.
- *
- * Thermostat is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Thermostat; see the file COPYING.  If not see
- * <http://www.gnu.org/licenses/>.
- *
- * Linking this code with other modules is making a combined work
- * based on this code.  Thus, the terms and conditions of the GNU
- * General Public License cover the whole combination.
- *
- * As a special exception, the copyright holders of this code give
- * you permission to link this code with independent modules to
- * produce an executable, regardless of the license terms of these
- * independent modules, and to copy and distribute the resulting
- * executable under terms of your choice, provided that you also
- * meet, for each linked independent module, the terms and conditions
- * of the license of that module.  An independent module is a module
- * which is not derived from or based on this code.  If you modify
- * this code, you may extend this exception to your version of the
- * library, but you are not obligated to do so.  If you do not wish
- * to do so, delete this exception statement from your version.
- */
-
-package com.redhat.thermostat.thread.harvester;
-
-import static org.junit.Assert.assertEquals;
-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 static org.mockito.Mockito.times;
-import static org.mockito.Mockito.any;
-import static org.mockito.Mockito.anyLong;
-import static org.mockito.Mockito.anyString;
-import static org.mockito.Mockito.doNothing;
-
-import static org.junit.Assert.*;
-
-import java.lang.management.ThreadInfo;
-import java.lang.management.ThreadMXBean;
-import java.util.List;
-import java.util.concurrent.ScheduledExecutorService;
-import java.util.concurrent.ScheduledFuture;
-import java.util.concurrent.TimeUnit;
-
-import javax.management.MalformedObjectNameException;
-
-import org.junit.Test;
-import org.mockito.ArgumentCaptor;
-
-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;
-import com.redhat.thermostat.utils.management.MXBeanConnection;
-import com.redhat.thermostat.utils.management.MXBeanConnector;
-
-public class HarvesterTest {
-
-    @Test
-    public void testStart() {
-        ScheduledExecutorService executor = mock(ScheduledExecutorService.class);
-        ThreadDao dao = mock(ThreadDao.class);
-        final MXBeanConnector mockedConnector = mock(MXBeanConnector.class);
-        when(mockedConnector.isAttached()).thenReturn(false);
-        
-        ArgumentCaptor<Runnable> arg0 = ArgumentCaptor.forClass(Runnable.class);
-        ArgumentCaptor<Long> arg1 = ArgumentCaptor.forClass(Long.class);
-        ArgumentCaptor<Long> arg2 = ArgumentCaptor.forClass(Long.class);
-        ArgumentCaptor<TimeUnit> arg3 = ArgumentCaptor.forClass(TimeUnit.class);
-        
-        final boolean [] harvestDataCalled = new boolean[1];
-        
-        when(executor.scheduleAtFixedRate(arg0.capture(), arg1.capture(), arg2.capture(), arg3.capture())).thenReturn(null);
-        
-        Harvester harvester = new Harvester(dao, executor, "42", "0xcafe") {
-            { connector = mockedConnector; }
-            @Override
-            synchronized void harvestData() {
-                harvestDataCalled[0] = true;
-            }
-        };
-        
-        harvester.start();
-        
-        verify(executor).scheduleAtFixedRate(any(Runnable.class), anyLong(), anyLong(), any(TimeUnit.class));
-        
-        assertTrue(arg1.getValue() == 0);
-        assertTrue(arg2.getValue() == 1);
-        assertEquals(TimeUnit.SECONDS, arg3.getValue());
-        
-        Runnable action = arg0.getValue();
-        assertNotNull(action);
-        
-        action.run();
-        
-        assertTrue(harvestDataCalled[0]);
-        
-        assertTrue(harvester.isConnected());
-    }
-    
-    /**
-     *  Mostly the same as testStart, but we call harvester.start() twice
-     */
-    @Test
-    public void testStartOnce() throws Exception {
-
-        ScheduledExecutorService executor = mock(ScheduledExecutorService.class);
-        ThreadDao dao = mock(ThreadDao.class);
-        final MXBeanConnector mockedConnector = mock(MXBeanConnector.class);
-        when(mockedConnector.isAttached()).thenReturn(false);
-        
-        ArgumentCaptor<Runnable> arg0 = ArgumentCaptor.forClass(Runnable.class);
-        ArgumentCaptor<Long> arg1 = ArgumentCaptor.forClass(Long.class);
-        ArgumentCaptor<Long> arg2 = ArgumentCaptor.forClass(Long.class);
-        ArgumentCaptor<TimeUnit> arg3 = ArgumentCaptor.forClass(TimeUnit.class);
-        
-        final boolean [] harvestDataCalled = new boolean[1];
-        
-        when(executor.scheduleAtFixedRate(arg0.capture(), arg1.capture(), arg2.capture(), arg3.capture())).thenReturn(null);
-        
-        Harvester harvester = new Harvester(dao, executor, "42", "0xcafe") {
-            { connector = mockedConnector; }
-            @Override
-            synchronized void harvestData() {
-                harvestDataCalled[0] = true;
-            }
-        };
-        
-        harvester.start();
-        harvester.start();
-
-        verify(mockedConnector, times(1)).isAttached();
-        verify(mockedConnector, times(1)).attach();
-        verify(executor, times(1)).scheduleAtFixedRate(any(Runnable.class), anyLong(), anyLong(), any(TimeUnit.class));
-        
-        assertTrue(arg1.getValue() == 0);
-        assertTrue(arg2.getValue() == 1);
-        assertEquals(TimeUnit.SECONDS, arg3.getValue());
-        
-        Runnable action = arg0.getValue();
-        assertNotNull(action);
-        
-        action.run();
-        
-        assertTrue(harvestDataCalled[0]);
-        
-        assertTrue(harvester.isConnected());
-    }
-    
-    @Test
-    @SuppressWarnings({ "rawtypes", "unchecked" })
-    public void testStopAfterStarting() throws Exception {
-        
-        ScheduledFuture future = mock(ScheduledFuture.class);
-        
-        ScheduledExecutorService executor = mock(ScheduledExecutorService.class);
-        ThreadDao dao = mock(ThreadDao.class);
-        final MXBeanConnector mockedConnector = mock(MXBeanConnector.class);
-        
-        MXBeanConnection connection = mock(MXBeanConnection.class);
-        
-        when(mockedConnector.connect()).thenReturn(connection);
-        
-        when(mockedConnector.isAttached()).thenReturn(true);
-        
-        when(executor.scheduleAtFixedRate(any(Runnable.class), anyLong(), anyLong(), any(TimeUnit.class))).thenReturn(future);
-        
-        Harvester harvester = new Harvester(dao, executor, "42", "0xcafe")
-        {{ connector = mockedConnector; }};
-        
-        harvester.start();
-        
-        assertTrue(harvester.isConnected());
-        
-        harvester.stop();
-        
-        verify(future).cancel(false);
-        verify(connection).close();
-        
-        // needs to be 2 times, since is called once in start
-        verify(mockedConnector, times(2)).isAttached();
-        verify(mockedConnector).close();
-        
-        assertFalse(harvester.isConnected());
-    }
-    
-    /**
-     *  Mostly the same as testStopAfterStarting, but we call harvester.stop()
-     *  twice
-     */
-    @Test
-    @SuppressWarnings({ "rawtypes", "unchecked" })
-    public void testStopTwiceAfterStarting() throws Exception {
-        
-        ScheduledFuture future = mock(ScheduledFuture.class);
-        
-        ScheduledExecutorService executor = mock(ScheduledExecutorService.class);
-        ThreadDao dao = mock(ThreadDao.class);
-        final MXBeanConnector mockedConnector = mock(MXBeanConnector.class);
-        
-        MXBeanConnection connection = mock(MXBeanConnection.class);
-        
-        when(mockedConnector.connect()).thenReturn(connection);
-        
-        when(mockedConnector.isAttached()).thenReturn(true);
-        
-        when(executor.scheduleAtFixedRate(any(Runnable.class), anyLong(), anyLong(), any(TimeUnit.class))).thenReturn(future);
-        
-        Harvester harvester = new Harvester(dao, executor, "42", "0xcafe")
-        {{ connector = mockedConnector; }};
-        
-        harvester.start();
-        
-        assertTrue(harvester.isConnected());
-        
-        harvester.stop();
-        harvester.stop();
-
-        verify(future, times(1)).cancel(false);
-        verify(connection, times(1)).close();
-        
-        // needs to be 2 times, since is called once in start
-        verify(mockedConnector, times(2)).isAttached();
-        
-        verify(mockedConnector, times(1)).close();
-        
-        assertFalse(harvester.isConnected());
-    }
-    
-    @Test
-    @SuppressWarnings({ "rawtypes", "unchecked" })
-    public void testStopNotStarted() throws Exception {
-        ScheduledExecutorService executor = mock(ScheduledExecutorService.class);
-        ThreadDao dao = mock(ThreadDao.class);
-        
-        ScheduledFuture future = mock(ScheduledFuture.class);
-        when(executor.scheduleAtFixedRate(any(Runnable.class), anyLong(), anyLong(), any(TimeUnit.class))).thenReturn(future);
-        
-        final MXBeanConnector mockedConnector = mock(MXBeanConnector.class);
-        MXBeanConnection connection = mock(MXBeanConnection.class);
-        when(mockedConnector.connect()).thenReturn(connection);
-        when(mockedConnector.isAttached()).thenReturn(true);
-        
-        Harvester harvester = new Harvester(dao, executor, "42", "0xcafe")
-        {{ connector = mockedConnector; }};
-        
-        verify(executor, times(0)).scheduleAtFixedRate(any(Runnable.class), anyLong(), anyLong(), any(TimeUnit.class));
-        
-        assertFalse(harvester.isConnected());
-        
-        harvester.stop();
-
-        assertFalse(harvester.isConnected());
-        verify(mockedConnector, times(0)).isAttached();
-        
-        verify(future, times(0)).cancel(false);
-    }
-    
-    @Test
-    public void testHarvestData() {
-        
-        long ids[] = new long [] {
-            0, 1
-        };
-        
-        ThreadInfo info1 = mock(ThreadInfo.class);
-        when(info1.getThreadName()).thenReturn("fluff1");
-        when(info1.getThreadId()).thenReturn(1l);
-
-        ThreadInfo info2 = mock(ThreadInfo.class);
-        when(info2.getThreadName()).thenReturn("fluff2");
-        when(info2.getThreadId()).thenReturn(2l);
-
-        ThreadInfo[] infos = new ThreadInfo[] {
-            info1,
-            info2     
-        };
-                
-        ScheduledExecutorService executor = mock(ScheduledExecutorService.class);
-
-        ArgumentCaptor<String> vmCapture = ArgumentCaptor.forClass(String.class);
-        ArgumentCaptor<String> agentCapture = ArgumentCaptor.forClass(String.class);
-        ArgumentCaptor<ThreadMXSummary> summaryCapture = ArgumentCaptor.forClass(ThreadMXSummary.class);
-
-        ThreadDao dao = mock(ThreadDao.class);
-        doNothing().when(dao).saveSummary(vmCapture.capture(), agentCapture.capture(), summaryCapture.capture());
-        
-        ArgumentCaptor<String> vmCapture2 = ArgumentCaptor.forClass(String.class);
-        ArgumentCaptor<String> agentCapture2 = ArgumentCaptor.forClass(String.class);
-        ArgumentCaptor<ThreadMXInfo> threadInfoCapture = ArgumentCaptor.forClass(ThreadMXInfo.class);        
-        doNothing().when(dao).saveThreadInfo(vmCapture2.capture(), agentCapture2.capture(), threadInfoCapture.capture());
-
-        final ThreadMXBean collectorBean = mock(ThreadMXBean.class);
-
-        when(collectorBean.getThreadCount()).thenReturn(42);
-        when(collectorBean.getAllThreadIds()).thenReturn(ids);
-        when(collectorBean.getThreadInfo(ids, true, true)).thenReturn(infos);
-
-        final boolean [] getDataCollectorBeanCalled = new boolean[1];
-        
-        Harvester harvester = new Harvester(dao, executor, "42", "0xcafe") {
-            @Override
-            ThreadMXBean getDataCollectorBean(MXBeanConnection connection)
-                    throws MalformedObjectNameException {
-                getDataCollectorBeanCalled[0] = true;
-                return collectorBean;
-            }
-        };
-
-        harvester.harvestData();
-        
-        assertTrue(getDataCollectorBeanCalled[0]);
-        
-        verify(collectorBean).getThreadInfo(ids, true, true);
-        
-        verify(dao).saveSummary(anyString(), anyString(), any(ThreadMXSummary.class));
-        
-        // once for each thread info
-        verify(dao, times(2)).saveThreadInfo(anyString(), anyString(), any(ThreadMXInfo.class));
-        
-        assertEquals(42, summaryCapture.getValue().currentLiveThreads());
-        assertEquals("42", vmCapture.getValue());
-        assertEquals("0xcafe", agentCapture.getValue());
-        
-        assertEquals(42, summaryCapture.getValue().currentLiveThreads());
-        assertEquals("42", vmCapture2.getAllValues().get(0));
-        assertEquals("42", vmCapture2.getAllValues().get(1));
-
-        assertEquals("0xcafe", agentCapture2.getAllValues().get(0));
-        assertEquals("0xcafe", agentCapture2.getAllValues().get(1));
-        
-        List<ThreadMXInfo> threadInfos = threadInfoCapture.getAllValues();
-        assertEquals(2, threadInfos.size());
-        
-        assertEquals("fluff1", threadInfos.get(0).getName());
-        assertEquals("fluff2", threadInfos.get(1).getName());
-        
-        verify(collectorBean, times(1)).getThreadCpuTime(1l);
-        verify(collectorBean, times(1)).getThreadCpuTime(2l);
-    }
-    
-    @Test
-    public void testSaveVmCaps() {
-
-        final MXBeanConnector mockedConnector = mock(MXBeanConnector.class);
-        when(mockedConnector.isAttached()).thenReturn(true);
-        
-        ScheduledExecutorService executor = mock(ScheduledExecutorService.class);
-
-        ArgumentCaptor<String> vmCapture = ArgumentCaptor.forClass(String.class);
-        ArgumentCaptor<String> agentCapture = ArgumentCaptor.forClass(String.class);
-        ArgumentCaptor<VMThreadMXCapabilities> capsCapture = ArgumentCaptor.forClass(VMThreadMXCapabilities.class);
-
-        ThreadDao dao = mock(ThreadDao.class);
-        doNothing().when(dao).saveCapabilities(vmCapture.capture(), agentCapture.capture(), capsCapture.capture());
-      
-        final ThreadMXBean collectorBean = mock(ThreadMXBean.class);
-        when(collectorBean.isThreadCpuTimeSupported()).thenReturn(true);
-        when(collectorBean.isThreadContentionMonitoringSupported()).thenReturn(true);
-
-        final boolean [] getDataCollectorBeanCalled = new boolean[1];
-        
-        Harvester harvester = new Harvester(dao, executor, "42", "0xcafe") {
-            { connector = mockedConnector; }
-            @Override
-            ThreadMXBean getDataCollectorBean(MXBeanConnection connection)
-                    throws MalformedObjectNameException {
-                getDataCollectorBeanCalled[0] = true;
-                return collectorBean;
-            }
-        };
-
-        harvester.saveVmCaps();
-        assertTrue(getDataCollectorBeanCalled[0]);
-        
-        verify(dao, times(1)).saveCapabilities(anyString(), anyString(), any(VMThreadMXCapabilities.class));
-        assertEquals("42", vmCapture.getValue());
-        assertEquals("0xcafe", agentCapture.getValue());
-
-        List<String> features = capsCapture.getValue().getSupportedFeaturesList();
-        assertEquals(2, features.size());
-        assertTrue(features.contains(ThreadDao.CPU_TIME));
-        assertTrue(features.contains(ThreadDao.CONTENTION_MONITOR));
-        assertFalse(features.contains(ThreadDao.THREAD_ALLOCATED_MEMORY));
-    }    
-}
--- a/thread/collector/src/test/java/com/redhat/thermostat/thread/harvester/ThreadHarvesterTest.java	Wed Aug 29 21:22:47 2012 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,165 +0,0 @@
-/*
- * Copyright 2012 Red Hat, Inc.
- *
- * This file is part of Thermostat.
- *
- * Thermostat is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published
- * by the Free Software Foundation; either version 2, or (at your
- * option) any later version.
- *
- * Thermostat is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Thermostat; see the file COPYING.  If not see
- * <http://www.gnu.org/licenses/>.
- *
- * Linking this code with other modules is making a combined work
- * based on this code.  Thus, the terms and conditions of the GNU
- * General Public License cover the whole combination.
- *
- * As a special exception, the copyright holders of this code give
- * you permission to link this code with independent modules to
- * produce an executable, regardless of the license terms of these
- * independent modules, and to copy and distribute the resulting
- * executable under terms of your choice, provided that you also
- * meet, for each linked independent module, the terms and conditions
- * of the license of that module.  An independent module is a module
- * which is not derived from or based on this code.  If you modify
- * this code, you may extend this exception to your version of the
- * library, but you are not obligated to do so.  If you do not wish
- * to do so, delete this exception statement from your version.
- */
-
-package com.redhat.thermostat.thread.harvester;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import java.util.List;
-import java.util.concurrent.ScheduledExecutorService;
-
-import org.junit.Test;
-import org.mockito.ArgumentCaptor;
-
-import com.redhat.thermostat.common.command.Request;
-import com.redhat.thermostat.thread.dao.ThreadDao;
-
-public class ThreadHarvesterTest {
-
-    @Test
-    public void testStart() {
-        ScheduledExecutorService executor = mock(ScheduledExecutorService.class);
-        ThreadDao dao = mock(ThreadDao.class);
-        Request request = mock(Request.class);
-        
-        final boolean[] getHarvesterCalled = new boolean[1];
-        final Harvester harverster = mock(Harvester.class);
-        
-        ArgumentCaptor<String> captor = ArgumentCaptor.forClass(String.class);
-        
-        when(request.getParameter(captor.capture())).
-            thenReturn(HarvesterCommand.START.name()).
-            thenReturn("42").
-            thenReturn("0xcafe");
-        
-        ThreadHarvester threadHarvester = new ThreadHarvester(executor, dao) {
-            @Override
-            Harvester getHarvester(String vmId, String agentId) {
-                
-                getHarvesterCalled[0] = true;
-                assertEquals("42", vmId);
-                assertEquals("0xcafe", agentId);
-                
-                return harverster;
-            }
-        };
-        threadHarvester.receive(request);
-        
-        List<String> values = captor.getAllValues();
-        assertEquals(3, values.size());
-        
-        assertEquals(HarvesterCommand.class.getName(), values.get(0));
-        assertEquals(HarvesterCommand.VM_ID.name(), values.get(1));
-        assertEquals(HarvesterCommand.AGENT_ID.name(), values.get(2));
-        
-        assertTrue(getHarvesterCalled[0]);
-        
-        verify(harverster).start();
-    }
-
-    @Test
-    public void testStop() {
-        ScheduledExecutorService executor = mock(ScheduledExecutorService.class);
-        ThreadDao dao = mock(ThreadDao.class);
-        Request request = mock(Request.class);
-        
-        final Harvester harverster = mock(Harvester.class);
-        
-        ArgumentCaptor<String> captor = ArgumentCaptor.forClass(String.class);
-        
-        when(request.getParameter(captor.capture())).
-            thenReturn(HarvesterCommand.STOP.name()).
-            thenReturn("42");
-        
-        ThreadHarvester threadHarvester = new ThreadHarvester(executor, dao) {
-            { connectors.put("42", harverster); }
-        };
-        threadHarvester.receive(request);
-        
-        List<String> values = captor.getAllValues();
-        assertEquals(2, values.size());
-        
-        assertEquals(HarvesterCommand.class.getName(), values.get(0));
-        assertEquals(HarvesterCommand.VM_ID.name(), values.get(1));
-                
-        verify(harverster).stop();        
-    }
-    
-    @Test
-    public void testSaveVmCaps() {
-        ScheduledExecutorService executor = mock(ScheduledExecutorService.class);
-        ThreadDao dao = mock(ThreadDao.class);
-        Request request = mock(Request.class);
-        
-        final boolean[] getHarvesterCalled = new boolean[1];
-        final Harvester harverster = mock(Harvester.class);
-        
-        ArgumentCaptor<String> captor = ArgumentCaptor.forClass(String.class);
-        
-        when(request.getParameter(captor.capture())).
-            thenReturn(HarvesterCommand.VM_CAPS.name()).
-            thenReturn("42").
-            thenReturn("0xcafe");
-        
-        ThreadHarvester threadHarvester = new ThreadHarvester(executor, dao) {
-            @Override
-            Harvester getHarvester(String vmId, String agentId) {
-                
-                getHarvesterCalled[0] = true;
-                assertEquals("42", vmId);
-                assertEquals("0xcafe", agentId);
-                
-                return harverster;
-            }
-        };
-        threadHarvester.receive(request);
-        
-        List<String> values = captor.getAllValues();
-        assertEquals(3, values.size());
-        
-        assertEquals(HarvesterCommand.class.getName(), values.get(0));
-        assertEquals(HarvesterCommand.VM_ID.name(), values.get(1));
-        assertEquals(HarvesterCommand.AGENT_ID.name(), values.get(2));
-        
-        assertTrue(getHarvesterCalled[0]);
-        
-        verify(harverster).saveVmCaps();
-    }    
-}
--- a/thread/harvester/pom.xml	Wed Aug 29 21:22:47 2012 +0200
+++ b/thread/harvester/pom.xml	Wed Aug 29 19:42:32 2012 -0400
@@ -92,6 +92,8 @@
             <Bundle-Activator>com.redhat.thermostat.thread.harvester.osgi.Activator</Bundle-Activator>
             <Bundle-SymbolicName>com.redhat.thermostat.thread.harvester</Bundle-SymbolicName>
             <Private-Package>
+              com.redhat.thermostat.thread.harvester,
+              com.redhat.thermostat.thread.harvester.management,
               com.redhat.thermostat.thread.harvester.osgi,
             </Private-Package>
             <!-- Do not autogenerate uses clauses in Manifests -->
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/thread/harvester/src/main/java/com/redhat/thermostat/thread/harvester/Harvester.java	Wed Aug 29 19:42:32 2012 -0400
@@ -0,0 +1,252 @@
+/*
+ * 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.harvester;
+
+import java.io.IOException;
+import java.lang.management.ManagementFactory;
+import java.lang.management.ThreadInfo;
+import java.lang.management.ThreadMXBean;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
+
+import javax.management.MalformedObjectNameException;
+
+import com.redhat.thermostat.thread.dao.ThreadDao;
+import com.redhat.thermostat.thread.harvester.management.MXBeanConnection;
+import com.redhat.thermostat.thread.harvester.management.MXBeanConnector;
+import com.redhat.thermostat.thread.model.ThreadInfoData;
+import com.redhat.thermostat.thread.model.ThreadSummary;
+import com.redhat.thermostat.thread.model.VMThreadCapabilities;
+
+@SuppressWarnings("restriction")
+class Harvester {
+    
+    private boolean isConnected;
+    private ScheduledExecutorService threadPool;
+    private ScheduledFuture<?> harvester;
+    
+    MXBeanConnector connector;
+    
+    private MXBeanConnection connection;
+    private ThreadMXBean collectorBean;
+    private ThreadDao threadDao;
+    private String vmId;
+    private String agentId;
+    
+    Harvester(ThreadDao threadDao, ScheduledExecutorService threadPool, String vmId, String agentId) {
+        this.connector = new MXBeanConnector(vmId);
+        this.threadDao = threadDao;
+        this.vmId = vmId;
+        this.agentId = agentId;
+        this.threadPool = threadPool;
+    }
+    
+    synchronized void start() {
+        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 HarvesterAction(), 0, 1, TimeUnit.SECONDS);
+    }
+    
+    boolean isConnected() {
+        return isConnected;
+    }
+    
+    synchronized void stop() {
+        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;
+    }
+    
+    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;
+    }
+    
+    synchronized void harvestData() {
+      try {
+          long timestamp = System.currentTimeMillis();
+          
+          ThreadSummary summary = new ThreadSummary();
+          
+          collectorBean = getDataCollectorBean(connection);
+          
+          summary.setCurrentLiveThreads(collectorBean.getThreadCount());
+          summary.setDaemonThreads(collectorBean.getDaemonThreadCount());
+          summary.setTimestamp(timestamp);
+          
+          threadDao.saveSummary(vmId, agentId, 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++) {
+              ThreadInfoData info = new ThreadInfoData();
+              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(vmId, agentId, info);
+          }
+          
+      } catch (MalformedObjectNameException e) {
+          e.printStackTrace();
+      }
+    }
+    
+    private class HarvesterAction implements Runnable {
+        @Override
+        public void run() {
+            harvestData();
+        }
+    }
+    
+    synchronized void saveVmCaps() {
+        
+        boolean closeAfter = false;
+        if (!connector.isAttached()) {
+            closeAfter = true; 
+            try {
+                connector.attach();
+            } catch (Exception ignore) {
+                ignore.printStackTrace();
+            }
+        }
+
+        try (MXBeanConnection connection = connector.connect()) {
+
+            ThreadMXBean bean = getDataCollectorBean(connection);
+            VMThreadCapabilities caps = new VMThreadCapabilities();
+
+            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);
+            }
+
+            threadDao.saveCapabilities(vmId, agentId, caps);
+
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+
+        if (closeAfter) {
+            try {
+                connector.close();
+            } catch (Exception ignore) {
+                ignore.printStackTrace();
+            }
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/thread/harvester/src/main/java/com/redhat/thermostat/thread/harvester/ThreadHarvester.java	Wed Aug 29 19:42:32 2012 -0400
@@ -0,0 +1,120 @@
+/*
+ * 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.harvester;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.ScheduledExecutorService;
+
+import com.redhat.thermostat.agent.command.RequestReceiver;
+import com.redhat.thermostat.common.command.Request;
+import com.redhat.thermostat.common.command.Response;
+import com.redhat.thermostat.common.command.Response.ResponseType;
+
+import com.redhat.thermostat.thread.collector.HarvesterCommand;
+import com.redhat.thermostat.thread.dao.ThreadDao;
+
+public class ThreadHarvester implements RequestReceiver {
+
+    private ScheduledExecutorService executor;
+    Map<String, Harvester> connectors;
+
+    private ThreadDao dao;
+    
+    public ThreadHarvester(ScheduledExecutorService executor, ThreadDao dao) {
+        this.executor = executor;
+        connectors = new HashMap<>();
+        this.dao = dao;
+    }
+    
+    @Override
+    public Response receive(Request request) {
+        
+        String command = request.getParameter(HarvesterCommand.class.getName());
+        switch (HarvesterCommand.valueOf(command)) {
+        case START: {
+            String vmId = request.getParameter(HarvesterCommand.VM_ID.name());
+            String agentId = request.getParameter(HarvesterCommand.AGENT_ID.name());
+            startHarvester(vmId, agentId);
+            break;
+        }   
+        case STOP: {
+            String vmId = request.getParameter(HarvesterCommand.VM_ID.name());
+            stopHarvester(vmId);
+            break;
+        }
+        case VM_CAPS: {
+            // this is blocking
+            String vmId = request.getParameter(HarvesterCommand.VM_ID.name());
+            String agentId = request.getParameter(HarvesterCommand.AGENT_ID.name());
+            saveVmCaps(vmId, agentId);
+            break;
+        }
+        default:
+            break;
+        }
+        
+        return new Response(ResponseType.OK);
+    }
+    
+    private void startHarvester(String vmId, String agentId) {
+        Harvester harvester = getHarvester(vmId, agentId);
+        harvester.start();
+    }
+    
+    private void saveVmCaps(String vmId, String agentId) {
+        Harvester harvester = getHarvester(vmId, agentId);
+        harvester.saveVmCaps();
+    }
+    
+    private void stopHarvester(String vmId) {
+        Harvester harvester = connectors.get(vmId);
+        if (harvester != null) {
+            harvester.stop();
+        }
+    }
+    
+    Harvester getHarvester(String vmId, String agentId) {
+        Harvester harvester = connectors.get(vmId);
+        if (harvester == null) {
+            harvester = new Harvester(dao, executor, vmId, agentId);
+            connectors.put(vmId, harvester);
+        }
+        
+        return harvester;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/thread/harvester/src/main/java/com/redhat/thermostat/thread/harvester/management/MXBeanConnection.java	Wed Aug 29 19:42:32 2012 -0400
@@ -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.thread.harvester.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/harvester/src/main/java/com/redhat/thermostat/thread/harvester/management/MXBeanConnector.java	Wed Aug 29 19:42:32 2012 -0400
@@ -0,0 +1,121 @@
+/*
+ * 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.harvester.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 VirtualMachine vm;
+    
+    private boolean attached;
+    
+    private String reference;
+    
+    public MXBeanConnector(String reference) {
+        this.reference = reference;
+    }
+    
+    public MXBeanConnector(VmRef reference) {
+        this.reference = reference.getStringID();
+    }
+    
+    public synchronized void attach() throws Exception {
+        if (attached)
+            throw new IOException("Already attached");
+        
+        vm = VirtualMachine.attach(reference);
+        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/harvester/src/test/java/com/redhat/thermostat/thread/harvester/HarvesterTest.java	Wed Aug 29 19:42:32 2012 -0400
@@ -0,0 +1,400 @@
+/*
+ * 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.harvester;
+
+import static org.junit.Assert.assertEquals;
+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 static org.mockito.Mockito.times;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyLong;
+import static org.mockito.Mockito.anyString;
+import static org.mockito.Mockito.doNothing;
+
+import static org.junit.Assert.*;
+
+import java.lang.management.ThreadInfo;
+import java.lang.management.ThreadMXBean;
+import java.util.List;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
+
+import javax.management.MalformedObjectNameException;
+
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+
+import com.redhat.thermostat.thread.dao.ThreadDao;
+import com.redhat.thermostat.thread.harvester.management.MXBeanConnection;
+import com.redhat.thermostat.thread.harvester.management.MXBeanConnector;
+import com.redhat.thermostat.thread.model.ThreadInfoData;
+import com.redhat.thermostat.thread.model.ThreadSummary;
+import com.redhat.thermostat.thread.model.VMThreadCapabilities;
+
+public class HarvesterTest {
+
+    @Test
+    public void testStart() {
+        ScheduledExecutorService executor = mock(ScheduledExecutorService.class);
+        ThreadDao dao = mock(ThreadDao.class);
+        final MXBeanConnector mockedConnector = mock(MXBeanConnector.class);
+        when(mockedConnector.isAttached()).thenReturn(false);
+        
+        ArgumentCaptor<Runnable> arg0 = ArgumentCaptor.forClass(Runnable.class);
+        ArgumentCaptor<Long> arg1 = ArgumentCaptor.forClass(Long.class);
+        ArgumentCaptor<Long> arg2 = ArgumentCaptor.forClass(Long.class);
+        ArgumentCaptor<TimeUnit> arg3 = ArgumentCaptor.forClass(TimeUnit.class);
+        
+        final boolean [] harvestDataCalled = new boolean[1];
+        
+        when(executor.scheduleAtFixedRate(arg0.capture(), arg1.capture(), arg2.capture(), arg3.capture())).thenReturn(null);
+        
+        Harvester harvester = new Harvester(dao, executor, "42", "0xcafe") {
+            { connector = mockedConnector; }
+            @Override
+            synchronized void harvestData() {
+                harvestDataCalled[0] = true;
+            }
+        };
+        
+        harvester.start();
+        
+        verify(executor).scheduleAtFixedRate(any(Runnable.class), anyLong(), anyLong(), any(TimeUnit.class));
+        
+        assertTrue(arg1.getValue() == 0);
+        assertTrue(arg2.getValue() == 1);
+        assertEquals(TimeUnit.SECONDS, arg3.getValue());
+        
+        Runnable action = arg0.getValue();
+        assertNotNull(action);
+        
+        action.run();
+        
+        assertTrue(harvestDataCalled[0]);
+        
+        assertTrue(harvester.isConnected());
+    }
+    
+    /**
+     *  Mostly the same as testStart, but we call harvester.start() twice
+     */
+    @Test
+    public void testStartOnce() throws Exception {
+
+        ScheduledExecutorService executor = mock(ScheduledExecutorService.class);
+        ThreadDao dao = mock(ThreadDao.class);
+        final MXBeanConnector mockedConnector = mock(MXBeanConnector.class);
+        when(mockedConnector.isAttached()).thenReturn(false);
+        
+        ArgumentCaptor<Runnable> arg0 = ArgumentCaptor.forClass(Runnable.class);
+        ArgumentCaptor<Long> arg1 = ArgumentCaptor.forClass(Long.class);
+        ArgumentCaptor<Long> arg2 = ArgumentCaptor.forClass(Long.class);
+        ArgumentCaptor<TimeUnit> arg3 = ArgumentCaptor.forClass(TimeUnit.class);
+        
+        final boolean [] harvestDataCalled = new boolean[1];
+        
+        when(executor.scheduleAtFixedRate(arg0.capture(), arg1.capture(), arg2.capture(), arg3.capture())).thenReturn(null);
+        
+        Harvester harvester = new Harvester(dao, executor, "42", "0xcafe") {
+            { connector = mockedConnector; }
+            @Override
+            synchronized void harvestData() {
+                harvestDataCalled[0] = true;
+            }
+        };
+        
+        harvester.start();
+        harvester.start();
+
+        verify(mockedConnector, times(1)).isAttached();
+        verify(mockedConnector, times(1)).attach();
+        verify(executor, times(1)).scheduleAtFixedRate(any(Runnable.class), anyLong(), anyLong(), any(TimeUnit.class));
+        
+        assertTrue(arg1.getValue() == 0);
+        assertTrue(arg2.getValue() == 1);
+        assertEquals(TimeUnit.SECONDS, arg3.getValue());
+        
+        Runnable action = arg0.getValue();
+        assertNotNull(action);
+        
+        action.run();
+        
+        assertTrue(harvestDataCalled[0]);
+        
+        assertTrue(harvester.isConnected());
+    }
+    
+    @Test
+    @SuppressWarnings({ "rawtypes", "unchecked" })
+    public void testStopAfterStarting() throws Exception {
+        
+        ScheduledFuture future = mock(ScheduledFuture.class);
+        
+        ScheduledExecutorService executor = mock(ScheduledExecutorService.class);
+        ThreadDao dao = mock(ThreadDao.class);
+        final MXBeanConnector mockedConnector = mock(MXBeanConnector.class);
+        
+        MXBeanConnection connection = mock(MXBeanConnection.class);
+        
+        when(mockedConnector.connect()).thenReturn(connection);
+        
+        when(mockedConnector.isAttached()).thenReturn(true);
+        
+        when(executor.scheduleAtFixedRate(any(Runnable.class), anyLong(), anyLong(), any(TimeUnit.class))).thenReturn(future);
+        
+        Harvester harvester = new Harvester(dao, executor, "42", "0xcafe")
+        {{ connector = mockedConnector; }};
+        
+        harvester.start();
+        
+        assertTrue(harvester.isConnected());
+        
+        harvester.stop();
+        
+        verify(future).cancel(false);
+        verify(connection).close();
+        
+        // needs to be 2 times, since is called once in start
+        verify(mockedConnector, times(2)).isAttached();
+        verify(mockedConnector).close();
+        
+        assertFalse(harvester.isConnected());
+    }
+    
+    /**
+     *  Mostly the same as testStopAfterStarting, but we call harvester.stop()
+     *  twice
+     */
+    @Test
+    @SuppressWarnings({ "rawtypes", "unchecked" })
+    public void testStopTwiceAfterStarting() throws Exception {
+        
+        ScheduledFuture future = mock(ScheduledFuture.class);
+        
+        ScheduledExecutorService executor = mock(ScheduledExecutorService.class);
+        ThreadDao dao = mock(ThreadDao.class);
+        final MXBeanConnector mockedConnector = mock(MXBeanConnector.class);
+        
+        MXBeanConnection connection = mock(MXBeanConnection.class);
+        
+        when(mockedConnector.connect()).thenReturn(connection);
+        
+        when(mockedConnector.isAttached()).thenReturn(true);
+        
+        when(executor.scheduleAtFixedRate(any(Runnable.class), anyLong(), anyLong(), any(TimeUnit.class))).thenReturn(future);
+        
+        Harvester harvester = new Harvester(dao, executor, "42", "0xcafe")
+        {{ connector = mockedConnector; }};
+        
+        harvester.start();
+        
+        assertTrue(harvester.isConnected());
+        
+        harvester.stop();
+        harvester.stop();
+
+        verify(future, times(1)).cancel(false);
+        verify(connection, times(1)).close();
+        
+        // needs to be 2 times, since is called once in start
+        verify(mockedConnector, times(2)).isAttached();
+        
+        verify(mockedConnector, times(1)).close();
+        
+        assertFalse(harvester.isConnected());
+    }
+    
+    @Test
+    @SuppressWarnings({ "rawtypes", "unchecked" })
+    public void testStopNotStarted() throws Exception {
+        ScheduledExecutorService executor = mock(ScheduledExecutorService.class);
+        ThreadDao dao = mock(ThreadDao.class);
+        
+        ScheduledFuture future = mock(ScheduledFuture.class);
+        when(executor.scheduleAtFixedRate(any(Runnable.class), anyLong(), anyLong(), any(TimeUnit.class))).thenReturn(future);
+        
+        final MXBeanConnector mockedConnector = mock(MXBeanConnector.class);
+        MXBeanConnection connection = mock(MXBeanConnection.class);
+        when(mockedConnector.connect()).thenReturn(connection);
+        when(mockedConnector.isAttached()).thenReturn(true);
+        
+        Harvester harvester = new Harvester(dao, executor, "42", "0xcafe")
+        {{ connector = mockedConnector; }};
+        
+        verify(executor, times(0)).scheduleAtFixedRate(any(Runnable.class), anyLong(), anyLong(), any(TimeUnit.class));
+        
+        assertFalse(harvester.isConnected());
+        
+        harvester.stop();
+
+        assertFalse(harvester.isConnected());
+        verify(mockedConnector, times(0)).isAttached();
+        
+        verify(future, times(0)).cancel(false);
+    }
+    
+    @Test
+    public void testHarvestData() {
+        
+        long ids[] = new long [] {
+            0, 1
+        };
+        
+        ThreadInfo info1 = mock(ThreadInfo.class);
+        when(info1.getThreadName()).thenReturn("fluff1");
+        when(info1.getThreadId()).thenReturn(1l);
+
+        ThreadInfo info2 = mock(ThreadInfo.class);
+        when(info2.getThreadName()).thenReturn("fluff2");
+        when(info2.getThreadId()).thenReturn(2l);
+
+        ThreadInfo[] infos = new ThreadInfo[] {
+            info1,
+            info2     
+        };
+                
+        ScheduledExecutorService executor = mock(ScheduledExecutorService.class);
+
+        ArgumentCaptor<String> vmCapture = ArgumentCaptor.forClass(String.class);
+        ArgumentCaptor<String> agentCapture = ArgumentCaptor.forClass(String.class);
+        ArgumentCaptor<ThreadSummary> summaryCapture = ArgumentCaptor.forClass(ThreadSummary.class);
+
+        ThreadDao dao = mock(ThreadDao.class);
+        doNothing().when(dao).saveSummary(vmCapture.capture(), agentCapture.capture(), summaryCapture.capture());
+        
+        ArgumentCaptor<String> vmCapture2 = ArgumentCaptor.forClass(String.class);
+        ArgumentCaptor<String> agentCapture2 = ArgumentCaptor.forClass(String.class);
+        ArgumentCaptor<ThreadInfoData> threadInfoCapture = ArgumentCaptor.forClass(ThreadInfoData.class);        
+        doNothing().when(dao).saveThreadInfo(vmCapture2.capture(), agentCapture2.capture(), threadInfoCapture.capture());
+
+        final ThreadMXBean collectorBean = mock(ThreadMXBean.class);
+
+        when(collectorBean.getThreadCount()).thenReturn(42);
+        when(collectorBean.getAllThreadIds()).thenReturn(ids);
+        when(collectorBean.getThreadInfo(ids, true, true)).thenReturn(infos);
+
+        final boolean [] getDataCollectorBeanCalled = new boolean[1];
+        
+        Harvester harvester = new Harvester(dao, executor, "42", "0xcafe") {
+            @Override
+            ThreadMXBean getDataCollectorBean(MXBeanConnection connection)
+                    throws MalformedObjectNameException {
+                getDataCollectorBeanCalled[0] = true;
+                return collectorBean;
+            }
+        };
+
+        harvester.harvestData();
+        
+        assertTrue(getDataCollectorBeanCalled[0]);
+        
+        verify(collectorBean).getThreadInfo(ids, true, true);
+        
+        verify(dao).saveSummary(anyString(), anyString(), any(ThreadSummary.class));
+        
+        // once for each thread info
+        verify(dao, times(2)).saveThreadInfo(anyString(), anyString(), any(ThreadInfoData.class));
+        
+        assertEquals(42, summaryCapture.getValue().currentLiveThreads());
+        assertEquals("42", vmCapture.getValue());
+        assertEquals("0xcafe", agentCapture.getValue());
+        
+        assertEquals(42, summaryCapture.getValue().currentLiveThreads());
+        assertEquals("42", vmCapture2.getAllValues().get(0));
+        assertEquals("42", vmCapture2.getAllValues().get(1));
+
+        assertEquals("0xcafe", agentCapture2.getAllValues().get(0));
+        assertEquals("0xcafe", agentCapture2.getAllValues().get(1));
+        
+        List<ThreadInfoData> threadInfos = threadInfoCapture.getAllValues();
+        assertEquals(2, threadInfos.size());
+        
+        assertEquals("fluff1", threadInfos.get(0).getName());
+        assertEquals("fluff2", threadInfos.get(1).getName());
+        
+        verify(collectorBean, times(1)).getThreadCpuTime(1l);
+        verify(collectorBean, times(1)).getThreadCpuTime(2l);
+    }
+    
+    @Test
+    public void testSaveVmCaps() {
+
+        final MXBeanConnector mockedConnector = mock(MXBeanConnector.class);
+        when(mockedConnector.isAttached()).thenReturn(true);
+        
+        ScheduledExecutorService executor = mock(ScheduledExecutorService.class);
+
+        ArgumentCaptor<String> vmCapture = ArgumentCaptor.forClass(String.class);
+        ArgumentCaptor<String> agentCapture = ArgumentCaptor.forClass(String.class);
+        ArgumentCaptor<VMThreadCapabilities> capsCapture = ArgumentCaptor.forClass(VMThreadCapabilities.class);
+
+        ThreadDao dao = mock(ThreadDao.class);
+        doNothing().when(dao).saveCapabilities(vmCapture.capture(), agentCapture.capture(), capsCapture.capture());
+      
+        final ThreadMXBean collectorBean = mock(ThreadMXBean.class);
+        when(collectorBean.isThreadCpuTimeSupported()).thenReturn(true);
+        when(collectorBean.isThreadContentionMonitoringSupported()).thenReturn(true);
+
+        final boolean [] getDataCollectorBeanCalled = new boolean[1];
+        
+        Harvester harvester = new Harvester(dao, executor, "42", "0xcafe") {
+            { connector = mockedConnector; }
+            @Override
+            ThreadMXBean getDataCollectorBean(MXBeanConnection connection)
+                    throws MalformedObjectNameException {
+                getDataCollectorBeanCalled[0] = true;
+                return collectorBean;
+            }
+        };
+
+        harvester.saveVmCaps();
+        assertTrue(getDataCollectorBeanCalled[0]);
+        
+        verify(dao, times(1)).saveCapabilities(anyString(), anyString(), any(VMThreadCapabilities.class));
+        assertEquals("42", vmCapture.getValue());
+        assertEquals("0xcafe", agentCapture.getValue());
+
+        List<String> features = capsCapture.getValue().getSupportedFeaturesList();
+        assertEquals(2, features.size());
+        assertTrue(features.contains(ThreadDao.CPU_TIME));
+        assertTrue(features.contains(ThreadDao.CONTENTION_MONITOR));
+        assertFalse(features.contains(ThreadDao.THREAD_ALLOCATED_MEMORY));
+    }    
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/thread/harvester/src/test/java/com/redhat/thermostat/thread/harvester/ThreadHarvesterTest.java	Wed Aug 29 19:42:32 2012 -0400
@@ -0,0 +1,166 @@
+/*
+ * 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.harvester;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import java.util.List;
+import java.util.concurrent.ScheduledExecutorService;
+
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+
+import com.redhat.thermostat.common.command.Request;
+import com.redhat.thermostat.thread.collector.HarvesterCommand;
+import com.redhat.thermostat.thread.dao.ThreadDao;
+
+public class ThreadHarvesterTest {
+
+    @Test
+    public void testStart() {
+        ScheduledExecutorService executor = mock(ScheduledExecutorService.class);
+        ThreadDao dao = mock(ThreadDao.class);
+        Request request = mock(Request.class);
+        
+        final boolean[] getHarvesterCalled = new boolean[1];
+        final Harvester harverster = mock(Harvester.class);
+        
+        ArgumentCaptor<String> captor = ArgumentCaptor.forClass(String.class);
+        
+        when(request.getParameter(captor.capture())).
+            thenReturn(HarvesterCommand.START.name()).
+            thenReturn("42").
+            thenReturn("0xcafe");
+        
+        ThreadHarvester threadHarvester = new ThreadHarvester(executor, dao) {
+            @Override
+            Harvester getHarvester(String vmId, String agentId) {
+                
+                getHarvesterCalled[0] = true;
+                assertEquals("42", vmId);
+                assertEquals("0xcafe", agentId);
+                
+                return harverster;
+            }
+        };
+        threadHarvester.receive(request);
+        
+        List<String> values = captor.getAllValues();
+        assertEquals(3, values.size());
+        
+        assertEquals(HarvesterCommand.class.getName(), values.get(0));
+        assertEquals(HarvesterCommand.VM_ID.name(), values.get(1));
+        assertEquals(HarvesterCommand.AGENT_ID.name(), values.get(2));
+        
+        assertTrue(getHarvesterCalled[0]);
+        
+        verify(harverster).start();
+    }
+
+    @Test
+    public void testStop() {
+        ScheduledExecutorService executor = mock(ScheduledExecutorService.class);
+        ThreadDao dao = mock(ThreadDao.class);
+        Request request = mock(Request.class);
+        
+        final Harvester harverster = mock(Harvester.class);
+        
+        ArgumentCaptor<String> captor = ArgumentCaptor.forClass(String.class);
+        
+        when(request.getParameter(captor.capture())).
+            thenReturn(HarvesterCommand.STOP.name()).
+            thenReturn("42");
+        
+        ThreadHarvester threadHarvester = new ThreadHarvester(executor, dao) {
+            { connectors.put("42", harverster); }
+        };
+        threadHarvester.receive(request);
+        
+        List<String> values = captor.getAllValues();
+        assertEquals(2, values.size());
+        
+        assertEquals(HarvesterCommand.class.getName(), values.get(0));
+        assertEquals(HarvesterCommand.VM_ID.name(), values.get(1));
+                
+        verify(harverster).stop();        
+    }
+    
+    @Test
+    public void testSaveVmCaps() {
+        ScheduledExecutorService executor = mock(ScheduledExecutorService.class);
+        ThreadDao dao = mock(ThreadDao.class);
+        Request request = mock(Request.class);
+        
+        final boolean[] getHarvesterCalled = new boolean[1];
+        final Harvester harverster = mock(Harvester.class);
+        
+        ArgumentCaptor<String> captor = ArgumentCaptor.forClass(String.class);
+        
+        when(request.getParameter(captor.capture())).
+            thenReturn(HarvesterCommand.VM_CAPS.name()).
+            thenReturn("42").
+            thenReturn("0xcafe");
+        
+        ThreadHarvester threadHarvester = new ThreadHarvester(executor, dao) {
+            @Override
+            Harvester getHarvester(String vmId, String agentId) {
+                
+                getHarvesterCalled[0] = true;
+                assertEquals("42", vmId);
+                assertEquals("0xcafe", agentId);
+                
+                return harverster;
+            }
+        };
+        threadHarvester.receive(request);
+        
+        List<String> values = captor.getAllValues();
+        assertEquals(3, values.size());
+        
+        assertEquals(HarvesterCommand.class.getName(), values.get(0));
+        assertEquals(HarvesterCommand.VM_ID.name(), values.get(1));
+        assertEquals(HarvesterCommand.AGENT_ID.name(), values.get(2));
+        
+        assertTrue(getHarvesterCalled[0]);
+        
+        verify(harverster).saveVmCaps();
+    }    
+}