# HG changeset patch # User Omair Majid # Date 1358794130 18000 # Node ID 259c606241748808ef11da190383b887067a582b # Parent c739844c6d6827faf087cd8ddbf2124336f64b6c Update all Backends to use new API for Vm start/stop Reviewed-by: ebaron Review-thread: http://icedtea.classpath.org/pipermail/thermostat/2013-January/005252.html PR 1258 diff -r c739844c6d68 -r 259c60624174 agent/core/src/main/java/com/redhat/thermostat/backend/Backend.java --- a/agent/core/src/main/java/com/redhat/thermostat/backend/Backend.java Mon Jan 21 12:17:46 2013 -0500 +++ b/agent/core/src/main/java/com/redhat/thermostat/backend/Backend.java Mon Jan 21 13:48:50 2013 -0500 @@ -181,7 +181,8 @@ * This method is called by the framework when the {@link Backend} is * registered. * - * @return true on success, false if there was an error + * @return {@code true} if the backend was activated successfully or + * already active. {@code false} if there was an error */ public abstract boolean activate(); @@ -196,7 +197,8 @@ * This method is called by the framework when the {@link Backend} is * deregistered. * - * @return true on success + * @return {@code true} if the backend was successfully deactivated or + * already inactive. {@code false} if the backend is still active. */ public abstract boolean deactivate(); diff -r c739844c6d68 -r 259c60624174 common/core/src/main/java/com/redhat/thermostat/common/Pair.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/common/core/src/main/java/com/redhat/thermostat/common/Pair.java Mon Jan 21 13:48:50 2013 -0500 @@ -0,0 +1,84 @@ +/* + * Copyright 2013 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * . + * + * 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.common; + +import java.util.Objects; + +/** + * A container that hold two values. + *

+ * The values may be related or unrelated. + *

+ * For most predictable results, the params should be immutable. If the value of + * {@link #hashCode()} is relevant, the two values must provide sane + * implementations of hashCode too. + * + * @param the type of the first value + * @param the type of the second value + */ +public class Pair { + + private final F first; + private final S second; + + public Pair(F first, S second) { + this.first = first; + this.second = second; + } + + public F getFirst() { + return first; + } + + public S getSecond() { + return second; + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof Pair)) { + return false; + } + Pair other = (Pair) obj; + return Objects.equals(first, other.first) && Objects.equals(second, other.second); + } + + @Override + public int hashCode() { + return Objects.hash(first, second); + } +} diff -r c739844c6d68 -r 259c60624174 common/core/src/test/java/com/redhat/thermostat/common/PairTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/common/core/src/test/java/com/redhat/thermostat/common/PairTest.java Mon Jan 21 13:48:50 2013 -0500 @@ -0,0 +1,78 @@ +/* + * Copyright 2013 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * . + * + * 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.common; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +public class PairTest { + + @Test + public void testCreateAndExtractParts() { + Pair pair = new Pair<>("1", "2"); + + assertEquals("1", pair.getFirst()); + assertEquals("2", pair.getSecond()); + } + + @Test + public void testEquals() { + Pair pair = new Pair<>("1", "2"); + Pair samePair = new Pair<>("1", "2"); + + Pair differentPair = new Pair<>("2", "1"); + + assertFalse(pair.equals(null)); + assertTrue(pair.equals(samePair)); + assertTrue(samePair.equals(pair)); + assertFalse(pair.equals(differentPair)); + assertFalse(pair.equals(new Object())); + } + + @Test + public void testHashCode() { + Pair pair = new Pair<>("1", "2"); + Pair samePair = new Pair<>("1", "2"); + Pair differentPair = new Pair<>("lol", "code"); + + assertFalse(pair.hashCode() == differentPair.hashCode()); + assertTrue(pair.hashCode() == samePair.hashCode()); + } +} diff -r c739844c6d68 -r 259c60624174 vm-classstat/agent/src/main/java/com/redhat/thermostat/vm/classstat/agent/internal/Activator.java --- a/vm-classstat/agent/src/main/java/com/redhat/thermostat/vm/classstat/agent/internal/Activator.java Mon Jan 21 12:17:46 2013 -0500 +++ b/vm-classstat/agent/src/main/java/com/redhat/thermostat/vm/classstat/agent/internal/Activator.java Mon Jan 21 13:48:50 2013 -0500 @@ -42,6 +42,7 @@ import org.osgi.framework.BundleContext; import org.osgi.framework.ServiceRegistration; +import com.redhat.thermostat.agent.VmStatusListenerRegistrar; import com.redhat.thermostat.backend.Backend; import com.redhat.thermostat.backend.BackendService; import com.redhat.thermostat.common.MultipleServiceTracker; @@ -57,6 +58,9 @@ @Override public void start(final BundleContext context) throws Exception { + + final VmStatusListenerRegistrar registrar = new VmStatusListenerRegistrar(context); + Class[] deps = new Class[] { BackendService.class, VmClassStatDAO.class @@ -67,7 +71,7 @@ public void dependenciesAvailable(Map services) { VmClassStatDAO vmClassStatDao = (VmClassStatDAO) services.get(VmClassStatDAO.class.getName()); Version version = new Version(context.getBundle()); - backend = new VmClassStatBackend(vmClassStatDao, version); + backend = new VmClassStatBackend(vmClassStatDao, version, registrar); reg = context.registerService(Backend.class.getName(), backend, null); } diff -r c739844c6d68 -r 259c60624174 vm-classstat/agent/src/main/java/com/redhat/thermostat/vm/classstat/agent/internal/VmClassStatBackend.java --- a/vm-classstat/agent/src/main/java/com/redhat/thermostat/vm/classstat/agent/internal/VmClassStatBackend.java Mon Jan 21 12:17:46 2013 -0500 +++ b/vm-classstat/agent/src/main/java/com/redhat/thermostat/vm/classstat/agent/internal/VmClassStatBackend.java Mon Jan 21 13:48:50 2013 -0500 @@ -37,42 +37,53 @@ package com.redhat.thermostat.vm.classstat.agent.internal; import java.net.URISyntaxException; +import java.util.HashMap; +import java.util.Map; import java.util.logging.Level; import java.util.logging.Logger; import sun.jvmstat.monitor.HostIdentifier; import sun.jvmstat.monitor.MonitorException; import sun.jvmstat.monitor.MonitoredHost; +import sun.jvmstat.monitor.MonitoredVm; +import sun.jvmstat.monitor.VmIdentifier; +import sun.jvmstat.monitor.event.VmListener; +import com.redhat.thermostat.agent.VmStatusListener; +import com.redhat.thermostat.agent.VmStatusListenerRegistrar; import com.redhat.thermostat.backend.Backend; import com.redhat.thermostat.backend.BackendID; import com.redhat.thermostat.backend.BackendsProperties; +import com.redhat.thermostat.common.Pair; import com.redhat.thermostat.common.Version; import com.redhat.thermostat.common.utils.LoggingUtils; import com.redhat.thermostat.vm.classstat.common.VmClassStatDAO; -public class VmClassStatBackend extends Backend { +public class VmClassStatBackend extends Backend implements VmStatusListener { private static final Logger LOGGER = LoggingUtils.getLogger(VmClassStatBackend.class); - private VmClassStatDAO vmClassStats; - private HostIdentifier hostId; + private final VmClassStatDAO vmClassStats; + private final VmStatusListenerRegistrar registrar; + + private final Map> pidToVmAndListener = new HashMap<>(); + private MonitoredHost host; - private VmClassStatHostListener hostListener; + private boolean started; - public VmClassStatBackend(VmClassStatDAO vmClassStatDAO, Version version) { + public VmClassStatBackend(VmClassStatDAO vmClassStatDAO, Version version, VmStatusListenerRegistrar registrar) { super(new BackendID("VM Classes Backend", VmClassStatBackend.class.getName())); this.vmClassStats = vmClassStatDAO; + this.registrar = registrar; setConfigurationValue(BackendsProperties.VENDOR.name(), "Red Hat, Inc."); setConfigurationValue(BackendsProperties.DESCRIPTION.name(), "Gathers class loading statistics about a JVM"); setConfigurationValue(BackendsProperties.VERSION.name(), version.getVersionNumber()); try { - hostId = new HostIdentifier((String) null); + HostIdentifier hostId = new HostIdentifier((String) null); host = MonitoredHost.getMonitoredHost(hostId); - hostListener = new VmClassStatHostListener(vmClassStats, attachToNewProcessByDefault()); } catch (MonitorException me) { LOGGER.log(Level.WARNING, "Problems with connecting jvmstat to local machine", me); } catch (URISyntaxException use) { @@ -80,29 +91,24 @@ } } + /* + * Methods from Backend + */ + @Override public boolean activate() { if (!started && host != null) { - try { - host.addHostListener(hostListener); - started = true; - } catch (MonitorException me) { - LOGGER.log(Level.WARNING, "Failed to add host listener", me); - } - + registrar.register(this); + started = true; } return started; } @Override public boolean deactivate() { - if (started && host != null) { - try { - host.removeHostListener(hostListener); - started = false; - } catch (MonitorException me) { - LOGGER.log(Level.INFO, "Failed to remove host listener"); - } + if (started) { + registrar.unregister(this); + started = false; } return !started; } @@ -128,10 +134,69 @@ } /* + * Methods from VmStatusListener + */ + + @Override + public void vmStatusChanged(Status newStatus, int pid) { + switch (newStatus) { + case VM_STARTED: + /* fall-through */ + case VM_ACTIVE: + vmStarted(pid); + break; + case VM_STOPPED: + vmStopped(pid); + break; + } + } + + private void vmStarted(int pid) { + if (attachToNewProcessByDefault()) { + try { + MonitoredVm vm = host.getMonitoredVm(host.getHostIdentifier().resolve(new VmIdentifier(String.valueOf(pid)))); + VmClassStatVmListener listener = new VmClassStatVmListener(vmClassStats, pid); + vm.addVmListener(listener); + + pidToVmAndListener.put(pid, new Pair<>(vm, listener)); + LOGGER.finer("Attached VmListener for VM: " + pid); + } catch (MonitorException | URISyntaxException e) { + LOGGER.log(Level.WARNING, "Could not attach to new vm " + pid, e); + } + } else { + LOGGER.log(Level.FINE, "skipping new vm " + pid); + } + } + + private void vmStopped(Integer pid) { + Pair data = pidToVmAndListener.remove(pid); + // if there is no data, we must never have attached to the vm. Nothing to do. + if (data == null) { + return; + } + + MonitoredVm vm = data.getFirst(); + VmListener listener = data.getSecond(); + try { + vm.removeVmListener(listener); + } catch (MonitorException e) { + LOGGER.log(Level.WARNING, "can't remove vm listener", e); + } + vm.detach(); + } + + /* * For testing purposes only. */ void setHost(MonitoredHost host) { this.host = host; } - + + /* + * For testing purposes only. + */ + Map> getPidToDataMap() { + return pidToVmAndListener; + } + } diff -r c739844c6d68 -r 259c60624174 vm-classstat/agent/src/main/java/com/redhat/thermostat/vm/classstat/agent/internal/VmClassStatHostListener.java --- a/vm-classstat/agent/src/main/java/com/redhat/thermostat/vm/classstat/agent/internal/VmClassStatHostListener.java Mon Jan 21 12:17:46 2013 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,170 +0,0 @@ -/* - * Copyright 2013 Red Hat, Inc. - * - * This file is part of Thermostat. - * - * Thermostat is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published - * by the Free Software Foundation; either version 2, or (at your - * option) any later version. - * - * Thermostat is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Thermostat; see the file COPYING. If not see - * . - * - * 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.vm.classstat.agent.internal; - -import java.net.URISyntaxException; -import java.util.HashMap; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; -import java.util.logging.Level; -import java.util.logging.Logger; - -import sun.jvmstat.monitor.MonitorException; -import sun.jvmstat.monitor.MonitoredHost; -import sun.jvmstat.monitor.MonitoredVm; -import sun.jvmstat.monitor.VmIdentifier; -import sun.jvmstat.monitor.event.HostEvent; -import sun.jvmstat.monitor.event.HostListener; -import sun.jvmstat.monitor.event.VmListener; -import sun.jvmstat.monitor.event.VmStatusChangeEvent; - -import com.redhat.thermostat.common.utils.LoggingUtils; -import com.redhat.thermostat.vm.classstat.common.VmClassStatDAO; - -public class VmClassStatHostListener implements HostListener { - - private static final Logger logger = LoggingUtils.getLogger(VmClassStatHostListener.class); - - private boolean attachNew; - - private final VmClassStatDAO vmClassStatDAO; - - private Map monitoredVms = new HashMap<>(); - private Map registeredListeners = new ConcurrentHashMap<>(); - - VmClassStatHostListener(VmClassStatDAO vmClassStatDAO, boolean attachNew) { - this.vmClassStatDAO = vmClassStatDAO; - this.attachNew = attachNew; - } - - void removeAllListeners() { - for (MonitoredVm vm : monitoredVms.values()) { - VmListener listener = registeredListeners.get(vm); - try { - if (listener != null) { - vm.removeVmListener(listener); - } - } catch (MonitorException e) { - logger.log(Level.WARNING, "can't remove vm listener", e); - } - } - } - - @Override - public void disconnected(HostEvent event) { - logger.warning("Disconnected from host"); - } - - @SuppressWarnings("unchecked") // Unchecked casts to (Set). - @Override - public void vmStatusChanged(VmStatusChangeEvent event) { - MonitoredHost host = event.getMonitoredHost(); - - for (Integer newVm : (Set) event.getStarted()) { - try { - logger.fine("New vm: " + newVm); - sendNewVM(newVm, host); - } catch (MonitorException e) { - logger.log(Level.WARNING, "error getting info for new vm" + newVm, e); - } catch (URISyntaxException e) { - logger.log(Level.WARNING, "error getting info for new vm" + newVm, e); - } - } - - for (Integer stoppedVm : (Set) event.getTerminated()) { - try { - logger.fine("stopped vm: " + stoppedVm); - sendStoppedVM(stoppedVm, host); - } catch (URISyntaxException e) { - logger.log(Level.WARNING, "error getting info for stopped vm" + stoppedVm, e); - } catch (MonitorException e) { - logger.log(Level.WARNING, "error getting info for stopped vm" + stoppedVm, e); - } - } - } - - private void sendNewVM(Integer vmId, MonitoredHost host) - throws MonitorException, URISyntaxException { - MonitoredVm vm = host.getMonitoredVm(host.getHostIdentifier().resolve( - new VmIdentifier(vmId.toString()))); - if (vm != null) { - if (attachNew) { - VmClassStatVmListener listener = new VmClassStatVmListener(vmClassStatDAO, vmId); - vm.addVmListener(listener); - - registeredListeners.put(vm, listener); - logger.finer("Attached VmListener for VM: " + vmId); - } else { - logger.log(Level.FINE, "skipping new vm " + vmId); - } - - monitoredVms.put(vmId, vm); - } - } - - private void sendStoppedVM(Integer vmId, MonitoredHost host) throws URISyntaxException, MonitorException { - - VmIdentifier resolvedVmID = host.getHostIdentifier().resolve(new VmIdentifier(vmId.toString())); - if (resolvedVmID != null) { - MonitoredVm vm = monitoredVms.remove(vmId); - VmListener listener = registeredListeners.remove(vm); - try { - if (listener != null) { - vm.removeVmListener(listener); - } - } catch (MonitorException e) { - logger.log(Level.WARNING, "can't remove vm listener", e); - } - vm.detach(); - } - } - - /* - * For testing purposes only. - */ - Map getMonitoredVms() { - return monitoredVms; - } - - /* - * For testing purposes only. - */ - Map getRegisteredListeners() { - return registeredListeners; - } -} diff -r c739844c6d68 -r 259c60624174 vm-classstat/agent/src/test/java/com/redhat/thermostat/vm/classstat/agent/internal/ActivatorTest.java --- a/vm-classstat/agent/src/test/java/com/redhat/thermostat/vm/classstat/agent/internal/ActivatorTest.java Mon Jan 21 12:17:46 2013 -0500 +++ b/vm-classstat/agent/src/test/java/com/redhat/thermostat/vm/classstat/agent/internal/ActivatorTest.java Mon Jan 21 13:48:50 2013 -0500 @@ -92,6 +92,9 @@ VmClassStatBackend backend = activator.getBackend(); assertNotNull(backend); + // something in core thermostat activates the backend; do it manually here + backend.activate(); + activator.stop(context); assertFalse(backend.isActive()); diff -r c739844c6d68 -r 259c60624174 vm-classstat/agent/src/test/java/com/redhat/thermostat/vm/classstat/agent/internal/VmClassStatBackendTest.java --- a/vm-classstat/agent/src/test/java/com/redhat/thermostat/vm/classstat/agent/internal/VmClassStatBackendTest.java Mon Jan 21 12:17:46 2013 -0500 +++ b/vm-classstat/agent/src/test/java/com/redhat/thermostat/vm/classstat/agent/internal/VmClassStatBackendTest.java Mon Jan 21 13:48:50 2013 -0500 @@ -38,20 +38,30 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; -import static org.mockito.Matchers.any; +import static org.junit.Assert.fail; +import static org.mockito.Matchers.isA; +import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; import java.net.URISyntaxException; import org.junit.Before; import org.junit.Test; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; +import sun.jvmstat.monitor.HostIdentifier; import sun.jvmstat.monitor.MonitorException; import sun.jvmstat.monitor.MonitoredHost; -import sun.jvmstat.monitor.event.HostListener; +import sun.jvmstat.monitor.MonitoredVm; +import sun.jvmstat.monitor.VmIdentifier; +import com.redhat.thermostat.agent.VmStatusListenerRegistrar; +import com.redhat.thermostat.agent.VmStatusListener.Status; +import com.redhat.thermostat.common.Ordered; import com.redhat.thermostat.common.Version; import com.redhat.thermostat.vm.classstat.common.VmClassStatDAO; @@ -59,6 +69,9 @@ private VmClassStatBackend backend; private MonitoredHost host; + private VmStatusListenerRegistrar registrar; + private HostIdentifier hostIdentifier; + private MonitoredVm monitoredVm1; @Before public void setup() throws MonitorException, URISyntaxException { @@ -67,25 +80,139 @@ Version version = mock(Version.class); when(version.getVersionNumber()).thenReturn("0.0.0"); - backend = new VmClassStatBackend(vmClassStatDao, version); + registrar = mock(VmStatusListenerRegistrar.class); + + hostIdentifier = mock(HostIdentifier.class); + when(hostIdentifier.resolve(isA(VmIdentifier.class))).then(new Answer() { + @Override + public VmIdentifier answer(InvocationOnMock invocation) throws Throwable { + return (VmIdentifier) invocation.getArguments()[0]; + } + }); + host = mock(MonitoredHost.class); + when(host.getHostIdentifier()).thenReturn(hostIdentifier); + + monitoredVm1 = mock(MonitoredVm.class); + + backend = new VmClassStatBackend(vmClassStatDao, version, registrar); - host = mock(MonitoredHost.class); backend.setHost(host); } @Test - public void testStart() throws MonitorException { + public void testActivate() { backend.activate(); - verify(host).addHostListener(any(HostListener.class)); + assertTrue(backend.isActive()); + verify(registrar).register(backend); + } + + @Test + public void testActivateTwice() { + assertTrue(backend.activate()); + assertTrue(backend.isActive()); + + assertTrue(backend.activate()); assertTrue(backend.isActive()); } @Test - public void testStop() throws MonitorException { - backend.activate(); - backend.deactivate(); - verify(host).removeHostListener(any(HostListener.class)); + public void testCanNotActivateWithoutMonitoredHost() { + backend.setHost(null); + + assertFalse(backend.activate()); assertFalse(backend.isActive()); } + @Test + public void testDeactivate() { + backend.activate(); + backend.deactivate(); + verify(registrar).unregister(backend); + assertFalse(backend.isActive()); + } + + @Test + public void testDeactivateTwice() { + backend.activate(); + + assertTrue(backend.deactivate()); + assertFalse(backend.isActive()); + assertTrue(backend.deactivate()); + } + + @Test + public void testNewVM() throws MonitorException, URISyntaxException { + int VM_PID = 1; + VmIdentifier VM_ID = new VmIdentifier(String.valueOf(VM_PID)); + when(host.getMonitoredVm(VM_ID)).thenReturn(monitoredVm1); + + backend.vmStatusChanged(Status.VM_STARTED, 1); + + verify(monitoredVm1).addVmListener(isA(VmClassStatVmListener.class)); + } + + @Test + public void testAlreadyRunningVM() throws MonitorException, URISyntaxException { + int VM_PID = 1; + VmIdentifier VM_ID = new VmIdentifier(String.valueOf(VM_PID)); + when(host.getMonitoredVm(VM_ID)).thenReturn(monitoredVm1); + + backend.vmStatusChanged(Status.VM_ACTIVE, 1); + + verify(monitoredVm1).addVmListener(isA(VmClassStatVmListener.class)); + } + + @Test + public void testStatVMGetMonitoredVmFails() throws MonitorException { + MonitorException monitorException = new MonitorException(); + when(host.getMonitoredVm(isA(VmIdentifier.class))).thenThrow(monitorException); + + backend.vmStatusChanged(Status.VM_STARTED, 1); + + assertFalse(backend.getPidToDataMap().containsKey(1)); + } + + @Test + public void testStoppedVM() throws MonitorException, URISyntaxException { + int VM_PID = 1; + VmIdentifier VM_ID = new VmIdentifier(String.valueOf(VM_PID)); + when(host.getMonitoredVm(VM_ID)).thenReturn(monitoredVm1); + + backend.vmStatusChanged(Status.VM_STARTED, 1); + backend.vmStatusChanged(Status.VM_STOPPED, 1); + + verify(monitoredVm1).removeVmListener(isA(VmClassStatVmListener.class)); + } + + @Test + public void testUnknownVMStopped() throws URISyntaxException, MonitorException { + int VM_PID = 1; + VmIdentifier VM_ID = new VmIdentifier(String.valueOf(VM_PID)); + when(host.getMonitoredVm(VM_ID)).thenReturn(monitoredVm1); + + backend.vmStatusChanged(Status.VM_STOPPED, 1); + + verifyNoMoreInteractions(monitoredVm1); + } + + @Test + public void testErrorRemovingVmListener() throws URISyntaxException, MonitorException { + int VM_PID = 1; + VmIdentifier VM_ID = new VmIdentifier(String.valueOf(VM_PID)); + when(host.getMonitoredVm(VM_ID)).thenReturn(monitoredVm1); + MonitorException monitorException = new MonitorException(); + doThrow(monitorException).when(monitoredVm1).removeVmListener(isA(VmClassStatVmListener.class)); + + backend.vmStatusChanged(Status.VM_STARTED, 1); + backend.vmStatusChanged(Status.VM_STOPPED, 1); + + verify(monitoredVm1).detach(); + } + + @Test + public void testOrderValue() { + int orderValue = backend.getOrderValue(); + assertTrue(orderValue > Ordered.ORDER_MEMORY_GROUP); + assertTrue(orderValue < Ordered.ORDER_NETWORK_GROUP); + } } diff -r c739844c6d68 -r 259c60624174 vm-classstat/agent/src/test/java/com/redhat/thermostat/vm/classstat/agent/internal/VmClassStatHostListenerTest.java --- a/vm-classstat/agent/src/test/java/com/redhat/thermostat/vm/classstat/agent/internal/VmClassStatHostListenerTest.java Mon Jan 21 12:17:46 2013 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,136 +0,0 @@ -/* - * Copyright 2013 Red Hat, Inc. - * - * This file is part of Thermostat. - * - * Thermostat is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published - * by the Free Software Foundation; either version 2, or (at your - * option) any later version. - * - * Thermostat is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Thermostat; see the file COPYING. If not see - * . - * - * 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.vm.classstat.agent.internal; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; -import static org.mockito.Matchers.eq; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -import java.net.URISyntaxException; -import java.util.Collections; -import java.util.HashSet; -import java.util.Set; - -import org.junit.Before; -import org.junit.Test; - -import sun.jvmstat.monitor.HostIdentifier; -import sun.jvmstat.monitor.MonitorException; -import sun.jvmstat.monitor.MonitoredHost; -import sun.jvmstat.monitor.MonitoredVm; -import sun.jvmstat.monitor.VmIdentifier; -import sun.jvmstat.monitor.event.VmStatusChangeEvent; - -import com.redhat.thermostat.vm.classstat.common.VmClassStatDAO; - -public class VmClassStatHostListenerTest { - - private VmClassStatHostListener hostListener; - private MonitoredHost host; - private MonitoredVm monitoredVm1; - private MonitoredVm monitoredVm2; - - @Before - public void setup() throws MonitorException, URISyntaxException { - VmClassStatDAO vmGcStatDAO = mock(VmClassStatDAO.class); - hostListener = new VmClassStatHostListener(vmGcStatDAO, true); - - host = mock(MonitoredHost.class); - HostIdentifier hostId = mock(HostIdentifier.class); - monitoredVm1 = mock(MonitoredVm.class); - monitoredVm2 = mock(MonitoredVm.class); - VmIdentifier vmId1 = new VmIdentifier("1"); - VmIdentifier vmId2 = new VmIdentifier("2"); - when(host.getHostIdentifier()).thenReturn(hostId); - when(host.getMonitoredVm(eq(vmId1))).thenReturn(monitoredVm1); - when(host.getMonitoredVm(eq(vmId2))).thenReturn(monitoredVm2); - when(hostId.resolve(eq(vmId1))).thenReturn(vmId1); - when(hostId.resolve(eq(vmId2))).thenReturn(vmId2); - } - - @Test - public void testNewVM() throws InterruptedException, MonitorException { - startVMs(); - - assertTrue(hostListener.getMonitoredVms().containsKey(1)); - assertTrue(hostListener.getMonitoredVms().containsKey(2)); - assertEquals(monitoredVm1, hostListener.getMonitoredVms().get(1)); - assertEquals(monitoredVm2, hostListener.getMonitoredVms().get(2)); - - assertTrue(hostListener.getRegisteredListeners().containsKey(monitoredVm1)); - assertTrue(hostListener.getRegisteredListeners().containsKey(monitoredVm2)); - } - - @Test - public void testStoppedVM() throws InterruptedException, MonitorException { - final Set stopped = new HashSet<>(); - stopped.add(1); - - startVMs(); - - // Trigger a change event - VmStatusChangeEvent event = mock(VmStatusChangeEvent.class); - when(event.getMonitoredHost()).thenReturn(host); - when(event.getStarted()).thenReturn(Collections.emptySet()); - when(event.getTerminated()).thenReturn(stopped); - hostListener.vmStatusChanged(event); - - // Ensure only 1 removed - assertFalse(hostListener.getMonitoredVms().containsKey(1)); - assertTrue(hostListener.getMonitoredVms().containsKey(2)); - assertEquals(monitoredVm2, hostListener.getMonitoredVms().get(2)); - - assertFalse(hostListener.getRegisteredListeners().containsKey(monitoredVm1)); - assertTrue(hostListener.getRegisteredListeners().containsKey(monitoredVm2)); - } - - private void startVMs() throws InterruptedException, MonitorException { - final Set started = new HashSet<>(); - started.add(1); - started.add(2); - - // Trigger a change event - VmStatusChangeEvent event = mock(VmStatusChangeEvent.class); - when(event.getMonitoredHost()).thenReturn(host); - when(event.getStarted()).thenReturn(started); - when(event.getTerminated()).thenReturn(Collections.emptySet()); - hostListener.vmStatusChanged(event); - } -} diff -r c739844c6d68 -r 259c60624174 vm-classstat/agent/src/test/java/com/redhat/thermostat/vm/classstat/agent/internal/VmClassStatVmListenerTest.java --- a/vm-classstat/agent/src/test/java/com/redhat/thermostat/vm/classstat/agent/internal/VmClassStatVmListenerTest.java Mon Jan 21 12:17:46 2013 -0500 +++ b/vm-classstat/agent/src/test/java/com/redhat/thermostat/vm/classstat/agent/internal/VmClassStatVmListenerTest.java Mon Jan 21 13:48:50 2013 -0500 @@ -37,15 +37,20 @@ package com.redhat.thermostat.vm.classstat.agent.internal; import static org.junit.Assert.assertEquals; +import static org.mockito.Matchers.anyString; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; +import org.junit.Before; import org.junit.Test; import org.mockito.ArgumentCaptor; import sun.jvmstat.monitor.Monitor; +import sun.jvmstat.monitor.MonitorException; import sun.jvmstat.monitor.MonitoredVm; +import sun.jvmstat.monitor.event.MonitorStatusChangeEvent; import sun.jvmstat.monitor.event.VmEvent; import com.redhat.thermostat.storage.model.VmClassStat; @@ -57,12 +62,35 @@ private static final Integer VM_ID = 123; private static final Long LOADED_CLASSES = 1234L; + private VmClassStatDAO dao; + private VmClassStatVmListener listener; + + @Before + public void setUp() { + dao = mock(VmClassStatDAO.class); + listener = new VmClassStatVmListener(dao, VM_ID); + } + + @Test + public void testDisconnected() { + VmEvent vmEvent = mock(VmEvent.class); + + listener.disconnected(vmEvent); + + verifyNoMoreInteractions(vmEvent, dao); + } + + @Test + public void testMonitorStatusChanged() { + MonitorStatusChangeEvent statusChangeEvent = mock(MonitorStatusChangeEvent.class); + + listener.monitorStatusChanged(statusChangeEvent); + + verifyNoMoreInteractions(statusChangeEvent, dao); + } + @Test public void testMonitorUpdatedClassStat() throws Exception { - - VmClassStatDAO dao = mock(VmClassStatDAO.class); - - VmClassStatVmListener l = new VmClassStatVmListener(dao, VM_ID); VmEvent vmEvent = mock(VmEvent.class); MonitoredVm monitoredVm = mock(MonitoredVm.class); Monitor m = mock(Monitor.class); @@ -70,7 +98,7 @@ when(monitoredVm.findByName("java.cls.loadedClasses")).thenReturn(m); when(vmEvent.getMonitoredVm()).thenReturn(monitoredVm); - l.monitorsUpdated(vmEvent); + listener.monitorsUpdated(vmEvent); ArgumentCaptor arg = ArgumentCaptor.forClass(VmClassStat.class); verify(dao).putVmClassStat(arg.capture()); @@ -81,10 +109,6 @@ @Test public void testMonitorUpdatedClassStatTwice() throws Exception { - - VmClassStatDAO dao = mock(VmClassStatDAO.class); - - VmClassStatVmListener l = new VmClassStatVmListener(dao, VM_ID); VmEvent vmEvent = mock(VmEvent.class); MonitoredVm monitoredVm = mock(MonitoredVm.class); Monitor m = mock(Monitor.class); @@ -92,10 +116,24 @@ when(monitoredVm.findByName("java.cls.loadedClasses")).thenReturn(m); when(vmEvent.getMonitoredVm()).thenReturn(monitoredVm); - l.monitorsUpdated(vmEvent); - l.monitorsUpdated(vmEvent); + listener.monitorsUpdated(vmEvent); + listener.monitorsUpdated(vmEvent); // This checks a bug where the Category threw an IllegalStateException because the DAO // created a new one on each call, thus violating the unique guarantee of Category. } + + @Test + public void testMonitorUpdateFails() throws MonitorException { + VmEvent vmEvent = mock(VmEvent.class); + MonitoredVm monitoredVm = mock(MonitoredVm.class); + MonitorException monitorException = new MonitorException(); + + when(monitoredVm.findByName(anyString())).thenThrow(monitorException); + when(vmEvent.getMonitoredVm()).thenReturn(monitoredVm); + + listener.monitorsUpdated(vmEvent); + + verifyNoMoreInteractions(dao); + } } diff -r c739844c6d68 -r 259c60624174 vm-cpu/agent/src/main/java/com/redhat/thermostat/vm/cpu/agent/internal/Activator.java --- a/vm-cpu/agent/src/main/java/com/redhat/thermostat/vm/cpu/agent/internal/Activator.java Mon Jan 21 12:17:46 2013 -0500 +++ b/vm-cpu/agent/src/main/java/com/redhat/thermostat/vm/cpu/agent/internal/Activator.java Mon Jan 21 13:48:50 2013 -0500 @@ -44,6 +44,7 @@ import org.osgi.framework.BundleContext; import org.osgi.framework.ServiceRegistration; +import com.redhat.thermostat.agent.VmStatusListenerRegistrar; import com.redhat.thermostat.backend.Backend; import com.redhat.thermostat.backend.BackendService; import com.redhat.thermostat.common.MultipleServiceTracker; @@ -60,6 +61,8 @@ @Override public void start(final BundleContext context) throws Exception { + final VmStatusListenerRegistrar registrar = new VmStatusListenerRegistrar(context); + executor = Executors.newSingleThreadScheduledExecutor(); Class[] deps = new Class[] { @@ -72,7 +75,7 @@ public void dependenciesAvailable(Map services) { VmCpuStatDAO vmCpuStatDao = (VmCpuStatDAO) services.get(VmCpuStatDAO.class.getName()); Version version = new Version(context.getBundle()); - backend = new VmCpuBackend(executor, vmCpuStatDao, version); + backend = new VmCpuBackend(executor, vmCpuStatDao, version, registrar); reg = context.registerService(Backend.class.getName(), backend, null); } diff -r c739844c6d68 -r 259c60624174 vm-cpu/agent/src/main/java/com/redhat/thermostat/vm/cpu/agent/internal/VmCpuBackend.java --- a/vm-cpu/agent/src/main/java/com/redhat/thermostat/vm/cpu/agent/internal/VmCpuBackend.java Mon Jan 21 12:17:46 2013 -0500 +++ b/vm-cpu/agent/src/main/java/com/redhat/thermostat/vm/cpu/agent/internal/VmCpuBackend.java Mon Jan 21 13:48:50 2013 -0500 @@ -38,16 +38,15 @@ import java.io.BufferedReader; import java.io.IOException; -import java.net.URISyntaxException; +import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import java.util.logging.Level; import java.util.logging.Logger; -import sun.jvmstat.monitor.HostIdentifier; -import sun.jvmstat.monitor.MonitorException; -import sun.jvmstat.monitor.MonitoredHost; - +import com.redhat.thermostat.agent.VmStatusListener; +import com.redhat.thermostat.agent.VmStatusListenerRegistrar; import com.redhat.thermostat.backend.Backend; import com.redhat.thermostat.backend.BackendID; import com.redhat.thermostat.backend.BackendsProperties; @@ -60,7 +59,7 @@ import com.redhat.thermostat.utils.SysConf; import com.redhat.thermostat.vm.cpu.common.VmCpuStatDAO; -public class VmCpuBackend extends Backend { +public class VmCpuBackend extends Backend implements VmStatusListener { private static final Logger LOGGER = LoggingUtils.getLogger(VmCpuBackend.class); static final long PROC_CHECK_INTERVAL = 1000; // TODO make this configurable. @@ -68,15 +67,17 @@ private VmCpuStatBuilder vmCpuStatBuilder; private VmCpuStatDAO vmCpuStats; private ScheduledExecutorService executor; - private HostIdentifier hostId; - private MonitoredHost host; - private VmCpuHostListener hostListener; + private VmStatusListenerRegistrar registrar; private boolean started; - public VmCpuBackend(ScheduledExecutorService executor, VmCpuStatDAO vmCpuStatDao, Version version) { + private final List pidsToMonitor = new CopyOnWriteArrayList<>(); + + public VmCpuBackend(ScheduledExecutorService executor, VmCpuStatDAO vmCpuStatDao, Version version, + VmStatusListenerRegistrar registrar) { super(new BackendID("VM CPU Backend", VmCpuBackend.class.getName())); this.executor = executor; this.vmCpuStats = vmCpuStatDao; + this.registrar = registrar; setConfigurationValue(BackendsProperties.VENDOR.name(), "Red Hat, Inc."); setConfigurationValue(BackendsProperties.DESCRIPTION.name(), "Gathers CPU statistics about a JVM"); @@ -88,25 +89,17 @@ ProcessStatusInfoBuilder builder = new ProcessStatusInfoBuilder(new ProcDataSource()); int numCpus = getCpuCount(source); vmCpuStatBuilder = new VmCpuStatBuilder(clock, numCpus, ticksPerSecond, builder); - - try { - hostId = new HostIdentifier((String) null); - host = MonitoredHost.getMonitoredHost(hostId); - hostListener = new VmCpuHostListener(vmCpuStatBuilder); - } catch (MonitorException me) { - LOGGER.log(Level.WARNING, "Problems with connecting jvmstat to local machine", me); - } catch (URISyntaxException use) { - LOGGER.log(Level.WARNING, "Failed to create host identifier", use); - } } @Override public boolean activate() { - if (!started && host != null) { + if (!started) { + registrar.register(this); + executor.scheduleAtFixedRate(new Runnable() { @Override public void run() { - for (Integer pid : hostListener.getPidsToMonitor()) { + for (Integer pid : pidsToMonitor) { if (vmCpuStatBuilder.knowsAbout(pid)) { VmCpuStat dataBuilt = vmCpuStatBuilder.build(pid); if (dataBuilt != null) { @@ -119,28 +112,18 @@ } }, 0, PROC_CHECK_INTERVAL, TimeUnit.MILLISECONDS); - try { - host.addHostListener(hostListener); - started = true; - } catch (MonitorException me) { - LOGGER.log(Level.WARNING, "Failed to add host listener", me); - } - + started = true; } return started; } @Override public boolean deactivate() { - if (started && host != null) { + if (started) { executor.shutdown(); + registrar.unregister(this); - try { - host.removeHostListener(hostListener); - started = false; - } catch (MonitorException me) { - LOGGER.log(Level.INFO, "Failed to remove host listener"); - } + started = false; } return !started; } @@ -183,10 +166,23 @@ } /* - * For testing purposes only. + * Methods implementing VmStatusListener */ - void setHost(MonitoredHost host) { - this.host = host; + @Override + public void vmStatusChanged(Status newStatus, int pid) { + switch (newStatus) { + case VM_STARTED: + /* fall-through */ + case VM_ACTIVE: + pidsToMonitor.add(pid); + break; + case VM_STOPPED: + // the cast is important because it changes the call from remove(index) to remove(Object) + pidsToMonitor.remove((Integer) pid); + vmCpuStatBuilder.forgetAbout(pid); + break; + } + } /* @@ -195,12 +191,5 @@ void setVmCpuStatBuilder(VmCpuStatBuilder vmCpuStatBuilder) { this.vmCpuStatBuilder = vmCpuStatBuilder; } - - /* - * For testing purposes only. - */ - void setHostListener(VmCpuHostListener hostListener) { - this.hostListener = hostListener; - } - + } diff -r c739844c6d68 -r 259c60624174 vm-cpu/agent/src/main/java/com/redhat/thermostat/vm/cpu/agent/internal/VmCpuHostListener.java --- a/vm-cpu/agent/src/main/java/com/redhat/thermostat/vm/cpu/agent/internal/VmCpuHostListener.java Mon Jan 21 12:17:46 2013 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,85 +0,0 @@ -/* - * Copyright 2013 Red Hat, Inc. - * - * This file is part of Thermostat. - * - * Thermostat is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published - * by the Free Software Foundation; either version 2, or (at your - * option) any later version. - * - * Thermostat is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Thermostat; see the file COPYING. If not see - * . - * - * 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.vm.cpu.agent.internal; - -import java.util.Set; -import java.util.concurrent.CopyOnWriteArraySet; -import java.util.logging.Logger; - -import sun.jvmstat.monitor.event.HostEvent; -import sun.jvmstat.monitor.event.HostListener; -import sun.jvmstat.monitor.event.VmStatusChangeEvent; - -import com.redhat.thermostat.common.utils.LoggingUtils; - -public class VmCpuHostListener implements HostListener { - - private static final Logger LOGGER = LoggingUtils.getLogger(VmCpuHostListener.class); - - private final Set pidsToMonitor = new CopyOnWriteArraySet(); - private VmCpuStatBuilder vmCpuStatBuilder; - - public VmCpuHostListener(VmCpuStatBuilder builder) { - this.vmCpuStatBuilder = builder; - } - - @Override - public void vmStatusChanged(VmStatusChangeEvent event) { - for (Object newVm : event.getStarted()) { - Integer vmId = (Integer) newVm; - LOGGER.fine("New vm: " + vmId); - pidsToMonitor.add(vmId); - } - - for (Object stoppedVm : event.getTerminated()) { - Integer vmId = (Integer) stoppedVm; - LOGGER.fine("stopped vm: " + vmId); - pidsToMonitor.remove(vmId); - vmCpuStatBuilder.forgetAbout(vmId); - } - } - - @Override - public void disconnected(HostEvent event) { - LOGGER.warning("Disconnected from host"); - } - - public Set getPidsToMonitor() { - return pidsToMonitor; - } - -} diff -r c739844c6d68 -r 259c60624174 vm-cpu/agent/src/test/java/com/redhat/thermostat/vm/cpu/agent/internal/ActivatorTest.java --- a/vm-cpu/agent/src/test/java/com/redhat/thermostat/vm/cpu/agent/internal/ActivatorTest.java Mon Jan 21 12:17:46 2013 -0500 +++ b/vm-cpu/agent/src/test/java/com/redhat/thermostat/vm/cpu/agent/internal/ActivatorTest.java Mon Jan 21 13:48:50 2013 -0500 @@ -93,6 +93,9 @@ VmCpuBackend backend = activator.getBackend(); assertNotNull(backend); + // core thermostat will activate the backend once it's registered + backend.activate(); + activator.stop(context); assertFalse(backend.isActive()); diff -r c739844c6d68 -r 259c60624174 vm-cpu/agent/src/test/java/com/redhat/thermostat/vm/cpu/agent/internal/VmCpuBackendTest.java --- a/vm-cpu/agent/src/test/java/com/redhat/thermostat/vm/cpu/agent/internal/VmCpuBackendTest.java Mon Jan 21 12:17:46 2013 -0500 +++ b/vm-cpu/agent/src/test/java/com/redhat/thermostat/vm/cpu/agent/internal/VmCpuBackendTest.java Mon Jan 21 13:48:50 2013 -0500 @@ -40,8 +40,11 @@ import static org.junit.Assert.assertTrue; import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyInt; +import static org.mockito.Matchers.eq; +import static org.mockito.Matchers.isA; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; import java.util.HashSet; @@ -53,10 +56,9 @@ import org.junit.Test; import org.mockito.ArgumentCaptor; -import sun.jvmstat.monitor.MonitorException; -import sun.jvmstat.monitor.MonitoredHost; -import sun.jvmstat.monitor.event.HostListener; - +import com.redhat.thermostat.agent.VmStatusListener.Status; +import com.redhat.thermostat.agent.VmStatusListenerRegistrar; +import com.redhat.thermostat.common.Ordered; import com.redhat.thermostat.common.Version; import com.redhat.thermostat.storage.model.VmCpuStat; import com.redhat.thermostat.vm.cpu.common.VmCpuStatDAO; @@ -65,8 +67,8 @@ private VmCpuBackend backend; private ScheduledExecutorService executor; - private MonitoredHost host; private VmCpuStatDAO vmCpuStatDao; + private VmStatusListenerRegistrar registrar; @Before public void setup() { @@ -76,21 +78,58 @@ Version version = mock(Version.class); when(version.getVersionNumber()).thenReturn("0.0.0"); - backend = new VmCpuBackend(executor, vmCpuStatDao, version); + registrar = mock(VmStatusListenerRegistrar.class); - host = mock(MonitoredHost.class); - backend.setHost(host); + backend = new VmCpuBackend(executor, vmCpuStatDao, version, registrar); + } + + @Test + public void testActivate() { + backend.activate(); + + verify(executor).scheduleAtFixedRate(isA(Runnable.class), eq(0l), eq(1000l), eq(TimeUnit.MILLISECONDS)); + verify(registrar).register(backend); + assertTrue(backend.isActive()); } @Test - public void testStart() throws MonitorException { + public void testActivateTwice() { + assertTrue(backend.activate()); + assertTrue(backend.isActive()); + assertTrue(backend.activate()); + assertTrue(backend.isActive()); + + assertTrue(backend.deactivate()); + } + + @Test + public void testDeactivate() { + backend.activate(); + backend.deactivate(); + + verify(executor).shutdown(); + verify(registrar).unregister(backend); + assertFalse(backend.isActive()); + } + + + @Test + public void testDeactivateTwice() { + assertTrue(backend.activate()); + assertTrue(backend.isActive()); + + assertTrue(backend.deactivate()); + assertFalse(backend.isActive()); + assertTrue(backend.deactivate()); + assertFalse(backend.isActive()); + } + + @Test + public void testStart() { // Setup Runnable mocks final Set pids = new HashSet<>(); pids.add(0); pids.add(1); - VmCpuHostListener listener = mock(VmCpuHostListener.class); - when(listener.getPidsToMonitor()).thenReturn(pids); - backend.setHostListener(listener); VmCpuStatBuilder builder = mock(VmCpuStatBuilder.class); VmCpuStat stat0 = mock(VmCpuStat.class); @@ -100,11 +139,15 @@ backend.setVmCpuStatBuilder(builder); backend.activate(); + + verify(registrar).register(backend); ArgumentCaptor captor = ArgumentCaptor.forClass(Runnable.class); verify(executor).scheduleAtFixedRate(captor.capture(), any(Long.class), any(Long.class), any(TimeUnit.class)); assertTrue(backend.isActive()); - verify(host).addHostListener(any(HostListener.class)); + backend.vmStatusChanged(Status.VM_ACTIVE, 0); + backend.vmStatusChanged(Status.VM_STARTED, 1); + Runnable runnable = captor.getValue(); runnable.run(); verify(builder).learnAbout(0); @@ -114,15 +157,24 @@ runnable.run(); verify(vmCpuStatDao).putVmCpuStat(stat0); verify(vmCpuStatDao).putVmCpuStat(stat1); + + backend.vmStatusChanged(Status.VM_STOPPED, 0); + backend.vmStatusChanged(Status.VM_STOPPED, 1); + + verify(builder).forgetAbout(0); + verify(builder).forgetAbout(1); + + when(builder.knowsAbout(anyInt())).thenReturn(false); + runnable.run(); + + verifyNoMoreInteractions(vmCpuStatDao); } - + @Test - public void testStop() throws MonitorException { - backend.activate(); - backend.deactivate(); - verify(executor).shutdown(); - assertFalse(backend.isActive()); - verify(host).removeHostListener(any(HostListener.class)); + public void testOrderValue() { + int orderValue = backend.getOrderValue(); + + assertTrue(orderValue > Ordered.ORDER_CPU_GROUP); + assertTrue(orderValue < Ordered.ORDER_MEMORY_GROUP); } - } diff -r c739844c6d68 -r 259c60624174 vm-cpu/agent/src/test/java/com/redhat/thermostat/vm/cpu/agent/internal/VmCpuHostListenerTest.java --- a/vm-cpu/agent/src/test/java/com/redhat/thermostat/vm/cpu/agent/internal/VmCpuHostListenerTest.java Mon Jan 21 12:17:46 2013 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,106 +0,0 @@ -/* - * Copyright 2013 Red Hat, Inc. - * - * This file is part of Thermostat. - * - * Thermostat is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published - * by the Free Software Foundation; either version 2, or (at your - * option) any later version. - * - * Thermostat is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Thermostat; see the file COPYING. If not see - * . - * - * 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.vm.cpu.agent.internal; - -import static org.junit.Assert.assertTrue; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import java.util.Collections; -import java.util.HashSet; -import java.util.Set; - -import org.junit.Before; -import org.junit.Test; - -import sun.jvmstat.monitor.event.VmStatusChangeEvent; - -public class VmCpuHostListenerTest { - - private VmCpuHostListener hostListener; - private VmCpuStatBuilder builder; - - @Before - public void setup() { - builder = mock(VmCpuStatBuilder.class); - - hostListener = new VmCpuHostListener(builder); - } - - @Test - public void testNewVM() throws InterruptedException { - startVMs(); - - // Check that pids are added to set - Set pids = hostListener.getPidsToMonitor(); - assertTrue(pids.contains(1)); - assertTrue(pids.contains(2)); - } - - @Test - public void testStoppedVM() throws InterruptedException { - final Set stopped = new HashSet<>(); - stopped.add(1); - - startVMs(); - - // Trigger a change event - VmStatusChangeEvent event = mock(VmStatusChangeEvent.class); - when(event.getStarted()).thenReturn(Collections.emptySet()); - when(event.getTerminated()).thenReturn(stopped); - hostListener.vmStatusChanged(event); - - // Ensure only 1 removed - verify(builder).forgetAbout(1); - verify(builder, never()).forgetAbout(2); - } - - private void startVMs() throws InterruptedException { - final Set started = new HashSet<>(); - started.add(1); - started.add(2); - - // Trigger a change event - VmStatusChangeEvent event = mock(VmStatusChangeEvent.class); - when(event.getStarted()).thenReturn(started); - when(event.getTerminated()).thenReturn(Collections.emptySet()); - hostListener.vmStatusChanged(event); - } - -} diff -r c739844c6d68 -r 259c60624174 vm-gc/agent/src/main/java/com/redhat/thermostat/vm/gc/agent/internal/VmGcBackend.java --- a/vm-gc/agent/src/main/java/com/redhat/thermostat/vm/gc/agent/internal/VmGcBackend.java Mon Jan 21 12:17:46 2013 -0500 +++ b/vm-gc/agent/src/main/java/com/redhat/thermostat/vm/gc/agent/internal/VmGcBackend.java Mon Jan 21 13:48:50 2013 -0500 @@ -54,6 +54,7 @@ import com.redhat.thermostat.backend.Backend; import com.redhat.thermostat.backend.BackendID; import com.redhat.thermostat.backend.BackendsProperties; +import com.redhat.thermostat.common.Pair; import com.redhat.thermostat.common.Version; import com.redhat.thermostat.common.utils.LoggingUtils; import com.redhat.thermostat.vm.gc.common.VmGcStatDAO; @@ -65,7 +66,7 @@ private final VmGcStatDAO vmGcStats; private final VmStatusListenerRegistrar registerer; - private final Map registeredListeners = new HashMap<>(); + private final Map> pidToData = new HashMap<>(); private MonitoredHost host; private boolean started; @@ -92,7 +93,7 @@ @Override public boolean activate() { - if (!started) { + if (!started && host != null) { registerer.register(this); started = true; } @@ -151,7 +152,7 @@ if (vm != null) { VmGcVmListener listener = new VmGcVmListener(vmGcStats, pid); vm.addVmListener(listener); - registeredListeners.put(pid, new VmAndListener(vm, listener)); + pidToData.put(pid, new Pair<>(vm, listener)); LOGGER.finer("Attached VmListener for VM: " + pid); } else { LOGGER.warning("could not connect to vm " + pid); @@ -165,14 +166,14 @@ } private void vmStopped(int pid) { - VmAndListener tuple = registeredListeners.remove(pid); - if (tuple == null) { - LOGGER.warning("received vm stopped for an unknown VM"); + Pair data = pidToData.remove(pid); + // if there is no data, we must never have attached to it. Nothing to do. + if (data == null) { return; } - MonitoredVm vm = tuple.vm; - VmListener listener = tuple.listener; + MonitoredVm vm = data.getFirst(); + VmListener listener = data.getSecond(); try { if (listener != null) { vm.removeVmListener(listener); @@ -190,14 +191,4 @@ this.host = host; } - private static class VmAndListener { - private MonitoredVm vm; - private VmListener listener; - - public VmAndListener(MonitoredVm vm, VmListener listener) { - this.vm = vm; - this.listener = listener; - } - - } } diff -r c739844c6d68 -r 259c60624174 vm-memory/agent/src/main/java/com/redhat/thermostat/vm/memory/agent/internal/Activator.java --- a/vm-memory/agent/src/main/java/com/redhat/thermostat/vm/memory/agent/internal/Activator.java Mon Jan 21 12:17:46 2013 -0500 +++ b/vm-memory/agent/src/main/java/com/redhat/thermostat/vm/memory/agent/internal/Activator.java Mon Jan 21 13:48:50 2013 -0500 @@ -42,6 +42,7 @@ import org.osgi.framework.BundleContext; import org.osgi.framework.ServiceRegistration; +import com.redhat.thermostat.agent.VmStatusListenerRegistrar; import com.redhat.thermostat.backend.Backend; import com.redhat.thermostat.backend.BackendService; import com.redhat.thermostat.common.MultipleServiceTracker; @@ -57,6 +58,9 @@ @Override public void start(final BundleContext context) throws Exception { + + final VmStatusListenerRegistrar registrar = new VmStatusListenerRegistrar(context); + Class[] deps = new Class[] { BackendService.class, VmMemoryStatDAO.class @@ -67,7 +71,7 @@ public void dependenciesAvailable(Map services) { VmMemoryStatDAO vmMemoryStatDao = (VmMemoryStatDAO) services.get(VmMemoryStatDAO.class.getName()); Version version = new Version(context.getBundle()); - backend = new VmMemoryBackend(vmMemoryStatDao, version); + backend = new VmMemoryBackend(vmMemoryStatDao, version, registrar); reg = context.registerService(Backend.class.getName(), backend, null); } diff -r c739844c6d68 -r 259c60624174 vm-memory/agent/src/main/java/com/redhat/thermostat/vm/memory/agent/internal/VmMemoryBackend.java --- a/vm-memory/agent/src/main/java/com/redhat/thermostat/vm/memory/agent/internal/VmMemoryBackend.java Mon Jan 21 12:17:46 2013 -0500 +++ b/vm-memory/agent/src/main/java/com/redhat/thermostat/vm/memory/agent/internal/VmMemoryBackend.java Mon Jan 21 13:48:50 2013 -0500 @@ -37,42 +37,51 @@ package com.redhat.thermostat.vm.memory.agent.internal; import java.net.URISyntaxException; +import java.util.HashMap; +import java.util.Map; import java.util.logging.Level; import java.util.logging.Logger; import sun.jvmstat.monitor.HostIdentifier; import sun.jvmstat.monitor.MonitorException; import sun.jvmstat.monitor.MonitoredHost; +import sun.jvmstat.monitor.MonitoredVm; +import sun.jvmstat.monitor.VmIdentifier; +import sun.jvmstat.monitor.event.VmListener; +import com.redhat.thermostat.agent.VmStatusListener; +import com.redhat.thermostat.agent.VmStatusListenerRegistrar; import com.redhat.thermostat.backend.Backend; import com.redhat.thermostat.backend.BackendID; import com.redhat.thermostat.backend.BackendsProperties; +import com.redhat.thermostat.common.Pair; import com.redhat.thermostat.common.Version; import com.redhat.thermostat.common.utils.LoggingUtils; import com.redhat.thermostat.vm.memory.common.VmMemoryStatDAO; -public class VmMemoryBackend extends Backend { +public class VmMemoryBackend extends Backend implements VmStatusListener { private static final Logger LOGGER = LoggingUtils.getLogger(VmMemoryBackend.class); private VmMemoryStatDAO vmMemoryStats; - private HostIdentifier hostId; private MonitoredHost host; - private VmMemoryHostListener hostListener; + private final VmStatusListenerRegistrar registrar; + private boolean started; + private Map> pidToData = new HashMap<>(); - public VmMemoryBackend(VmMemoryStatDAO vmMemoryStatDAO, Version version) { + public VmMemoryBackend(VmMemoryStatDAO vmMemoryStatDAO, Version version, VmStatusListenerRegistrar registrar) { super(new BackendID("VM Memory Backend", VmMemoryBackend.class.getName())); this.vmMemoryStats = vmMemoryStatDAO; + this.registrar = registrar; setConfigurationValue(BackendsProperties.VENDOR.name(), "Red Hat, Inc."); setConfigurationValue(BackendsProperties.DESCRIPTION.name(), "Gathers memory statistics about a JVM"); setConfigurationValue(BackendsProperties.VERSION.name(), version.getVersionNumber()); try { - hostId = new HostIdentifier((String) null); + HostIdentifier hostId = new HostIdentifier((String) null); host = MonitoredHost.getMonitoredHost(hostId); - hostListener = new VmMemoryHostListener(vmMemoryStats, attachToNewProcessByDefault()); } catch (MonitorException me) { LOGGER.log(Level.WARNING, "Problems with connecting jvmstat to local machine", me); } catch (URISyntaxException use) { @@ -83,25 +92,17 @@ @Override public boolean activate() { if (!started && host != null) { - try { - host.addHostListener(hostListener); - started = true; - } catch (MonitorException me) { - LOGGER.log(Level.WARNING, "Failed to add host listener", me); - } + registrar.register(this); + started = true; } return started; } @Override public boolean deactivate() { - if (started && host != null) { - try { - host.removeHostListener(hostListener); - started = false; - } catch (MonitorException me) { - LOGGER.log(Level.INFO, "Failed to remove host listener"); - } + if (started) { + registrar.unregister(this); + started = false; } return !started; } @@ -127,6 +128,58 @@ } /* + * Methods for VmStatusListener + */ + public void vmStatusChanged(Status newStatus, int pid) { + switch (newStatus) { + case VM_STARTED: + /* fall-through */ + case VM_ACTIVE: + handleNewVm(pid); + break; + case VM_STOPPED: + handleStoppedVm(pid); + break; + default: + break; + } + }; + + private void handleNewVm(int pid) { + if (attachToNewProcessByDefault()) { + try { + MonitoredVm vm = host.getMonitoredVm(host.getHostIdentifier().resolve(new VmIdentifier(String.valueOf(pid)))); + VmMemoryVmListener listener = new VmMemoryVmListener(vmMemoryStats, pid); + vm.addVmListener(listener); + + pidToData.put(pid, new Pair<>(vm, listener)); + LOGGER.finer("Attached VmListener for VM: " + pid); + } catch (MonitorException | URISyntaxException e) { + LOGGER.log(Level.WARNING, "unable to attach to vm " + pid, e); + } + } else { + LOGGER.log(Level.FINE, "skipping new vm " + pid); + } + } + + private void handleStoppedVm(int pid) { + Pair data = pidToData.remove(pid); + // we were not monitoring pid at all, so nothing to do + if (data == null) { + return; + } + + MonitoredVm vm = data.getFirst(); + VmListener listener = data.getSecond(); + try { + vm.removeVmListener(listener); + } catch (MonitorException e) { + LOGGER.log(Level.WARNING, "can't remove vm listener", e); + } + vm.detach(); + } + + /* * For testing purposes only. */ void setHost(MonitoredHost host) { diff -r c739844c6d68 -r 259c60624174 vm-memory/agent/src/main/java/com/redhat/thermostat/vm/memory/agent/internal/VmMemoryHostListener.java --- a/vm-memory/agent/src/main/java/com/redhat/thermostat/vm/memory/agent/internal/VmMemoryHostListener.java Mon Jan 21 12:17:46 2013 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,172 +0,0 @@ -/* - * Copyright 2013 Red Hat, Inc. - * - * This file is part of Thermostat. - * - * Thermostat is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published - * by the Free Software Foundation; either version 2, or (at your - * option) any later version. - * - * Thermostat is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Thermostat; see the file COPYING. If not see - * . - * - * 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.vm.memory.agent.internal; - -import java.net.URISyntaxException; -import java.util.HashMap; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; -import java.util.logging.Level; -import java.util.logging.Logger; - -import sun.jvmstat.monitor.MonitorException; -import sun.jvmstat.monitor.MonitoredHost; -import sun.jvmstat.monitor.MonitoredVm; -import sun.jvmstat.monitor.VmIdentifier; -import sun.jvmstat.monitor.event.HostEvent; -import sun.jvmstat.monitor.event.HostListener; -import sun.jvmstat.monitor.event.VmListener; -import sun.jvmstat.monitor.event.VmStatusChangeEvent; - -import com.redhat.thermostat.common.utils.LoggingUtils; -import com.redhat.thermostat.vm.memory.common.VmMemoryStatDAO; - -public class VmMemoryHostListener implements HostListener { - - private static final Logger logger = LoggingUtils.getLogger(VmMemoryHostListener.class); - - private boolean attachNew; - - private final VmMemoryStatDAO vmMemoryStatDAO; - - private Map monitoredVms = new HashMap<>(); - private Map registeredListeners = new ConcurrentHashMap<>(); - - VmMemoryHostListener(VmMemoryStatDAO vmMemoryStatDAO, boolean attachNew) { - this.vmMemoryStatDAO = vmMemoryStatDAO; - this.attachNew = attachNew; - } - - void removeAllListeners() { - for (MonitoredVm vm : monitoredVms.values()) { - VmListener listener = registeredListeners.get(vm); - try { - if (listener != null) { - vm.removeVmListener(listener); - } - } catch (MonitorException e) { - logger.log(Level.WARNING, "can't remove vm listener", e); - } - } - } - - @Override - public void disconnected(HostEvent event) { - logger.warning("Disconnected from host"); - } - - @SuppressWarnings("unchecked") // Unchecked casts to (Set). - @Override - public void vmStatusChanged(VmStatusChangeEvent event) { - MonitoredHost host = event.getMonitoredHost(); - - for (Integer newVm : (Set) event.getStarted()) { - try { - logger.fine("New vm: " + newVm); - sendNewVM(newVm, host); - } catch (MonitorException e) { - logger.log(Level.WARNING, "error getting info for new vm" + newVm, e); - } catch (URISyntaxException e) { - logger.log(Level.WARNING, "error getting info for new vm" + newVm, e); - } - } - - for (Integer stoppedVm : (Set) event.getTerminated()) { - try { - logger.fine("stopped vm: " + stoppedVm); - sendStoppedVM(stoppedVm, host); - } catch (URISyntaxException e) { - logger.log(Level.WARNING, "error getting info for stopped vm" + stoppedVm, e); - } catch (MonitorException e) { - logger.log(Level.WARNING, "error getting info for stopped vm" + stoppedVm, e); - } - } - } - - private void sendNewVM(Integer vmId, MonitoredHost host) - throws MonitorException, URISyntaxException { - MonitoredVm vm = host.getMonitoredVm(host.getHostIdentifier().resolve( - new VmIdentifier(vmId.toString()))); - if (vm != null) { - - if (attachNew) { - VmMemoryVmListener listener = new VmMemoryVmListener(vmMemoryStatDAO, vmId); - vm.addVmListener(listener); - registeredListeners.put(vm, listener); - logger.finer("Attached VmListener for VM: " + vmId); - - } else { - logger.log(Level.FINE, "skipping new vm " + vmId); - } - - monitoredVms.put(vmId, vm); - } - } - - private void sendStoppedVM(Integer vmId, MonitoredHost host) throws URISyntaxException, MonitorException { - - VmIdentifier resolvedVmID = host.getHostIdentifier().resolve(new VmIdentifier(vmId.toString())); - if (resolvedVmID != null) { - MonitoredVm vm = monitoredVms.remove(vmId); - VmMemoryVmListener listener = registeredListeners.remove(vm); - try { - if (listener != null) { - vm.removeVmListener(listener); - } - } catch (MonitorException e) { - logger.log(Level.WARNING, "can't remove vm listener", e); - } - vm.detach(); - } - } - - /* - * For testing purposes only. - */ - Map getMonitoredVms() { - return monitoredVms; - } - - /* - * For testing purposes only. - */ - Map getRegisteredListeners() { - return registeredListeners; - } - -} diff -r c739844c6d68 -r 259c60624174 vm-memory/agent/src/main/java/com/redhat/thermostat/vm/memory/agent/internal/VmMemoryVmListener.java --- a/vm-memory/agent/src/main/java/com/redhat/thermostat/vm/memory/agent/internal/VmMemoryVmListener.java Mon Jan 21 12:17:46 2013 -0500 +++ b/vm-memory/agent/src/main/java/com/redhat/thermostat/vm/memory/agent/internal/VmMemoryVmListener.java Mon Jan 21 13:48:50 2013 -0500 @@ -76,15 +76,12 @@ @Override public void monitorsUpdated(VmEvent event) { MonitoredVm vm = event.getMonitoredVm(); - if (vm == null) { - throw new NullPointerException(); - } - + VmMemoryDataExtractor extractor = new VmMemoryDataExtractor(vm); - recordMemoryStat(vm, extractor); + recordMemoryStat(extractor); } - void recordMemoryStat(MonitoredVm vm, VmMemoryDataExtractor extractor) { + void recordMemoryStat(VmMemoryDataExtractor extractor) { try { long timestamp = System.currentTimeMillis(); int maxGenerations = (int) extractor.getTotalGcGenerations(); diff -r c739844c6d68 -r 259c60624174 vm-memory/agent/src/test/java/com/redhat/thermostat/vm/memory/agent/internal/ActivatorTest.java --- a/vm-memory/agent/src/test/java/com/redhat/thermostat/vm/memory/agent/internal/ActivatorTest.java Mon Jan 21 12:17:46 2013 -0500 +++ b/vm-memory/agent/src/test/java/com/redhat/thermostat/vm/memory/agent/internal/ActivatorTest.java Mon Jan 21 13:48:50 2013 -0500 @@ -93,6 +93,10 @@ VmMemoryBackend backend = activator.getBackend(); assertNotNull(backend); + // core thermostat activates the backend when the backend is detected + // do it manually for the test + backend.activate(); + activator.stop(context); assertFalse(backend.isActive()); diff -r c739844c6d68 -r 259c60624174 vm-memory/agent/src/test/java/com/redhat/thermostat/vm/memory/agent/internal/VmMemoryBackendTest.java --- a/vm-memory/agent/src/test/java/com/redhat/thermostat/vm/memory/agent/internal/VmMemoryBackendTest.java Mon Jan 21 12:17:46 2013 -0500 +++ b/vm-memory/agent/src/test/java/com/redhat/thermostat/vm/memory/agent/internal/VmMemoryBackendTest.java Mon Jan 21 13:48:50 2013 -0500 @@ -38,20 +38,31 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; -import static org.mockito.Matchers.any; +import static org.mockito.Matchers.isA; +import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; import java.net.URISyntaxException; import org.junit.Before; import org.junit.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; +import sun.jvmstat.monitor.HostIdentifier; import sun.jvmstat.monitor.MonitorException; import sun.jvmstat.monitor.MonitoredHost; -import sun.jvmstat.monitor.event.HostListener; +import sun.jvmstat.monitor.MonitoredVm; +import sun.jvmstat.monitor.VmIdentifier; +import sun.jvmstat.monitor.event.VmListener; +import com.redhat.thermostat.agent.VmStatusListener.Status; +import com.redhat.thermostat.agent.VmStatusListenerRegistrar; +import com.redhat.thermostat.common.Ordered; import com.redhat.thermostat.common.Version; import com.redhat.thermostat.vm.memory.common.VmMemoryStatDAO; @@ -59,32 +70,158 @@ private VmMemoryBackend backend; private MonitoredHost host; + private VmStatusListenerRegistrar registrar; + private VmMemoryStatDAO vmMemoryStatDao; @Before public void setup() throws MonitorException, URISyntaxException { - VmMemoryStatDAO vmMemoryStatDao = mock(VmMemoryStatDAO.class); + vmMemoryStatDao = mock(VmMemoryStatDAO.class); Version version = mock(Version.class); when(version.getVersionNumber()).thenReturn("0.0.0"); - backend = new VmMemoryBackend(vmMemoryStatDao, version); + + registrar = mock(VmStatusListenerRegistrar.class); + + backend = new VmMemoryBackend(vmMemoryStatDao, version, registrar); + HostIdentifier hostIdentifier = mock(HostIdentifier.class); + when(hostIdentifier.resolve(isA(VmIdentifier.class))).then(new Answer() { + @Override + public VmIdentifier answer(InvocationOnMock invocation) throws Throwable { + return (VmIdentifier) invocation.getArguments()[0]; + } + }); host = mock(MonitoredHost.class); + when(host.getHostIdentifier()).thenReturn(hostIdentifier); + backend.setHost(host); } @Test - public void testStart() throws MonitorException { - backend.activate(); - verify(host).addHostListener(any(HostListener.class)); + public void testActivate() { + assertTrue(backend.activate()); + + verify(registrar).register(backend); assertTrue(backend.isActive()); } @Test - public void testStop() throws MonitorException { + public void testActivateTwice() { + assertTrue(backend.activate()); + assertTrue(backend.isActive()); + + assertTrue(backend.activate()); + assertTrue(backend.isActive()); + + verify(registrar).register(backend); + } + + @Test + public void testActivateFailsIfHostIsNull() { + backend.setHost(null); + + assertFalse(backend.activate()); + } + + @Test + public void testDeactivate() { backend.activate(); backend.deactivate(); - verify(host).removeHostListener(any(HostListener.class)); + + verify(registrar).unregister(backend); assertFalse(backend.isActive()); } - + + @Test + public void testDeactiveTwice() { + assertTrue(backend.activate()); + + assertTrue(backend.deactivate()); + assertFalse(backend.isActive()); + + assertTrue(backend.deactivate()); + assertFalse(backend.isActive()); + + verify(registrar).unregister(backend); + } + + @Test + public void testNewVmStarted() throws URISyntaxException, MonitorException { + int VM_PID = 10; + VmIdentifier VM_ID = new VmIdentifier(String.valueOf(VM_PID)); + MonitoredVm monitoredVm = mock(MonitoredVm.class); + + when(host.getMonitoredVm(VM_ID)).thenReturn(monitoredVm); + + backend.vmStatusChanged(Status.VM_STARTED, VM_PID); + + verify(monitoredVm).addVmListener(isA(VmMemoryVmListener.class)); + } + + @Test + public void testErrorInAttachingToNewVm() throws MonitorException, URISyntaxException { + int VM_PID = 10; + VmIdentifier VM_ID = new VmIdentifier(String.valueOf(VM_PID)); + + when(host.getMonitoredVm(VM_ID)).thenThrow(new MonitorException()); + + backend.vmStatusChanged(Status.VM_STARTED, VM_PID); + } + + @Test + public void testVmStopped() throws URISyntaxException, MonitorException { + int VM_PID = 10; + VmIdentifier VM_ID = new VmIdentifier(String.valueOf(VM_PID)); + MonitoredVm monitoredVm = mock(MonitoredVm.class); + + when(host.getMonitoredVm(VM_ID)).thenReturn(monitoredVm); + + backend.vmStatusChanged(Status.VM_STARTED, VM_PID); + + ArgumentCaptor listenerCaptor = ArgumentCaptor.forClass(VmListener.class); + verify(monitoredVm).addVmListener(listenerCaptor.capture()); + + backend.vmStatusChanged(Status.VM_STOPPED, VM_PID); + + verify(monitoredVm).removeVmListener(listenerCaptor.getValue()); + verify(monitoredVm).detach(); + } + + @Test + public void testUnknownVmStoppedIsIgnored() { + int VM_PID = 10; + + backend.vmStatusChanged(Status.VM_STOPPED, VM_PID); + + verifyNoMoreInteractions(host, vmMemoryStatDao); + } + + @Test + public void testStoppedVmIsDetachedEvenInPresenceOfErrors() throws URISyntaxException, MonitorException { + int VM_PID = 10; + VmIdentifier VM_ID = new VmIdentifier(String.valueOf(VM_PID)); + MonitoredVm monitoredVm = mock(MonitoredVm.class); + + when(host.getMonitoredVm(VM_ID)).thenReturn(monitoredVm); + + backend.vmStatusChanged(Status.VM_STARTED, VM_PID); + + ArgumentCaptor listenerCaptor = ArgumentCaptor.forClass(VmListener.class); + verify(monitoredVm).addVmListener(listenerCaptor.capture()); + + VmListener vmListener = listenerCaptor.getValue(); + doThrow(new MonitorException("test")).when(monitoredVm).removeVmListener(vmListener); + + backend.vmStatusChanged(Status.VM_STOPPED, VM_PID); + + verify(monitoredVm).detach(); + } + + @Test + public void testOrderValue() { + int orderValue = backend.getOrderValue(); + + assertTrue(orderValue > Ordered.ORDER_MEMORY_GROUP); + assertTrue(orderValue < Ordered.ORDER_NETWORK_GROUP); + } } diff -r c739844c6d68 -r 259c60624174 vm-memory/agent/src/test/java/com/redhat/thermostat/vm/memory/agent/internal/VmMemoryDataExtractorTest.java --- a/vm-memory/agent/src/test/java/com/redhat/thermostat/vm/memory/agent/internal/VmMemoryDataExtractorTest.java Mon Jan 21 12:17:46 2013 -0500 +++ b/vm-memory/agent/src/test/java/com/redhat/thermostat/vm/memory/agent/internal/VmMemoryDataExtractorTest.java Mon Jan 21 13:48:50 2013 -0500 @@ -44,6 +44,8 @@ import org.junit.Test; +import com.redhat.thermostat.storage.model.VmMemoryStat.Generation; + import sun.jvmstat.monitor.LongMonitor; import sun.jvmstat.monitor.MonitorException; import sun.jvmstat.monitor.MonitoredVm; @@ -136,6 +138,18 @@ } @Test + public void testGenerationCollectorNone() throws MonitorException { + final String MONITOR_NAME = "sun.gc.collector.0.name"; + MonitoredVm vm = mock(MonitoredVm.class); + when(vm.findByName(MONITOR_NAME)).thenReturn(null); + + VmMemoryDataExtractor extractor = new VmMemoryDataExtractor(vm); + String returned = extractor.getGenerationCollector(0); + + assertEquals(Generation.COLLECTOR_NONE, returned); + } + + @Test public void testTotalSpaces() throws MonitorException { final Long TOTAL_SPACES = 99l; final LongMonitor monitor = mock(LongMonitor.class); diff -r c739844c6d68 -r 259c60624174 vm-memory/agent/src/test/java/com/redhat/thermostat/vm/memory/agent/internal/VmMemoryHostListenerTest.java --- a/vm-memory/agent/src/test/java/com/redhat/thermostat/vm/memory/agent/internal/VmMemoryHostListenerTest.java Mon Jan 21 12:17:46 2013 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,136 +0,0 @@ -/* - * Copyright 2013 Red Hat, Inc. - * - * This file is part of Thermostat. - * - * Thermostat is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published - * by the Free Software Foundation; either version 2, or (at your - * option) any later version. - * - * Thermostat is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Thermostat; see the file COPYING. If not see - * . - * - * 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.vm.memory.agent.internal; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; -import static org.mockito.Matchers.eq; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -import java.net.URISyntaxException; -import java.util.Collections; -import java.util.HashSet; -import java.util.Set; - -import org.junit.Before; -import org.junit.Test; - -import sun.jvmstat.monitor.HostIdentifier; -import sun.jvmstat.monitor.MonitorException; -import sun.jvmstat.monitor.MonitoredHost; -import sun.jvmstat.monitor.MonitoredVm; -import sun.jvmstat.monitor.VmIdentifier; -import sun.jvmstat.monitor.event.VmStatusChangeEvent; - -import com.redhat.thermostat.vm.memory.common.VmMemoryStatDAO; - -public class VmMemoryHostListenerTest { - - private VmMemoryHostListener hostListener; - private MonitoredHost host; - private MonitoredVm monitoredVm1; - private MonitoredVm monitoredVm2; - - @Before - public void setup() throws MonitorException, URISyntaxException { - VmMemoryStatDAO vmMemoryStatDAO = mock(VmMemoryStatDAO.class); - hostListener = new VmMemoryHostListener(vmMemoryStatDAO, true); - - host = mock(MonitoredHost.class); - HostIdentifier hostId = mock(HostIdentifier.class); - monitoredVm1 = mock(MonitoredVm.class); - monitoredVm2 = mock(MonitoredVm.class); - VmIdentifier vmId1 = new VmIdentifier("1"); - VmIdentifier vmId2 = new VmIdentifier("2"); - when(host.getHostIdentifier()).thenReturn(hostId); - when(host.getMonitoredVm(eq(vmId1))).thenReturn(monitoredVm1); - when(host.getMonitoredVm(eq(vmId2))).thenReturn(monitoredVm2); - when(hostId.resolve(eq(vmId1))).thenReturn(vmId1); - when(hostId.resolve(eq(vmId2))).thenReturn(vmId2); - } - - @Test - public void testNewVM() throws InterruptedException, MonitorException { - startVMs(); - - assertTrue(hostListener.getMonitoredVms().containsKey(1)); - assertTrue(hostListener.getMonitoredVms().containsKey(2)); - assertEquals(monitoredVm1, hostListener.getMonitoredVms().get(1)); - assertEquals(monitoredVm2, hostListener.getMonitoredVms().get(2)); - - assertTrue(hostListener.getRegisteredListeners().containsKey(monitoredVm1)); - assertTrue(hostListener.getRegisteredListeners().containsKey(monitoredVm2)); - } - - @Test - public void testStoppedVM() throws InterruptedException, MonitorException { - final Set stopped = new HashSet<>(); - stopped.add(1); - - startVMs(); - - // Trigger a change event - VmStatusChangeEvent event = mock(VmStatusChangeEvent.class); - when(event.getMonitoredHost()).thenReturn(host); - when(event.getStarted()).thenReturn(Collections.emptySet()); - when(event.getTerminated()).thenReturn(stopped); - hostListener.vmStatusChanged(event); - - // Ensure only 1 removed - assertFalse(hostListener.getMonitoredVms().containsKey(1)); - assertTrue(hostListener.getMonitoredVms().containsKey(2)); - assertEquals(monitoredVm2, hostListener.getMonitoredVms().get(2)); - - assertFalse(hostListener.getRegisteredListeners().containsKey(monitoredVm1)); - assertTrue(hostListener.getRegisteredListeners().containsKey(monitoredVm2)); - } - - private void startVMs() throws InterruptedException, MonitorException { - final Set started = new HashSet<>(); - started.add(1); - started.add(2); - - // Trigger a change event - VmStatusChangeEvent event = mock(VmStatusChangeEvent.class); - when(event.getMonitoredHost()).thenReturn(host); - when(event.getStarted()).thenReturn(started); - when(event.getTerminated()).thenReturn(Collections.emptySet()); - hostListener.vmStatusChanged(event); - } -} diff -r c739844c6d68 -r 259c60624174 vm-memory/agent/src/test/java/com/redhat/thermostat/vm/memory/agent/internal/VmMemoryVmListenerTest.java --- a/vm-memory/agent/src/test/java/com/redhat/thermostat/vm/memory/agent/internal/VmMemoryVmListenerTest.java Mon Jan 21 12:17:46 2013 -0500 +++ b/vm-memory/agent/src/test/java/com/redhat/thermostat/vm/memory/agent/internal/VmMemoryVmListenerTest.java Mon Jan 21 13:48:50 2013 -0500 @@ -37,16 +37,21 @@ package com.redhat.thermostat.vm.memory.agent.internal; import static org.junit.Assert.assertEquals; +import static org.mockito.Matchers.anyString; +import static org.mockito.Matchers.isA; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; import org.junit.Before; import org.junit.Test; import org.mockito.ArgumentCaptor; +import sun.jvmstat.monitor.Monitor; import sun.jvmstat.monitor.MonitorException; import sun.jvmstat.monitor.MonitoredVm; +import sun.jvmstat.monitor.event.VmEvent; import com.redhat.thermostat.storage.model.VmMemoryStat; import com.redhat.thermostat.storage.model.VmMemoryStat.Generation; @@ -77,19 +82,20 @@ }; private VmMemoryVmListener vmListener; - private MonitoredVm monitoredVm; private VmMemoryDataExtractor extractor; private VmMemoryStatDAO vmMemoryStatDAO; + private MonitoredVm monitoredVm; @Before public void setup() throws MonitorException { final int numGens = 2; vmMemoryStatDAO = mock(VmMemoryStatDAO.class); vmListener = new VmMemoryVmListener(vmMemoryStatDAO, 0); - monitoredVm = mock(MonitoredVm.class); extractor = mock(VmMemoryDataExtractor.class); - + + mockTotalGenerations(numGens); + for (int i = 0; i < numGens; i++) { mockGenerationName(i); mockGenerationCapacity(i); @@ -106,6 +112,10 @@ } } + private void mockTotalGenerations(long gens) throws MonitorException { + when(extractor.getTotalGcGenerations()).thenReturn(gens); + } + private void mockGenerationName(int gen) throws MonitorException { when(extractor.getGenerationName(gen)).thenReturn(GEN_NAMES[gen]); } @@ -141,15 +151,43 @@ private void mockSpaceUsed(int gen, int space) throws MonitorException { when(extractor.getSpaceUsed(gen, space)).thenReturn(SPACE_USED[gen][space]); } - + + @Test + public void testDisconnectedIsNoOp() { + vmListener.disconnected(null); + + verifyNoMoreInteractions(vmMemoryStatDAO, extractor); + } + + @Test + public void testMonitorStatusChangeIsNoOp() { + vmListener.monitorStatusChanged(null); + + verifyNoMoreInteractions(vmMemoryStatDAO, extractor); + } + + @Test + public void testMonitorsUpdated() throws MonitorException { + Monitor monitor = mock(Monitor.class); + when(monitor.getValue()).thenReturn(Long.valueOf(0)); + when(monitoredVm.findByName(anyString())).thenReturn(monitor); + VmEvent monitorUpdateEvent = mock(VmEvent.class); + when(monitorUpdateEvent.getMonitoredVm()).thenReturn(monitoredVm); + + vmListener.monitorsUpdated(monitorUpdateEvent); + + verify(vmMemoryStatDAO).putVmMemoryStat(isA(VmMemoryStat.class)); + } + @Test public void testRecordMemoryStat() { - vmListener.recordMemoryStat(monitoredVm, extractor); + vmListener.recordMemoryStat(extractor); ArgumentCaptor captor = ArgumentCaptor.forClass(VmMemoryStat.class); verify(vmMemoryStatDAO).putVmMemoryStat(captor.capture()); VmMemoryStat memoryStat = captor.getValue(); Generation[] gens = memoryStat.getGenerations(); + assertEquals(2, gens.length); for (int i = 0; i < gens.length; i++) { Generation gen = gens[i]; assertEquals(GEN_NAMES[i], gen.getName()); @@ -167,4 +205,12 @@ } } } + + @Test + public void testRecordingMemoryInPresenseOfExtrationErrors() throws MonitorException { + when(extractor.getTotalGcGenerations()).thenThrow(new MonitorException()); + vmListener.recordMemoryStat(extractor); + + verifyNoMoreInteractions(vmMemoryStatDAO); + } }