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