Mercurial > hg > release > thermostat-0.4
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
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(); + } +}