changeset 965:f6bc84590701

Unregister VmListeners in Backend.deactivate There has been a sporadic exception when shutting down the service command (this also occurs with the GC plugin). This is caused by the Backends trying to store information in the DAOs after the storage has shutdown. Currently, VmListeners are only unregistered when a JVM stops. They still remain registered even after the Backend is deactivated, which seems like a good candidate for causing this error. I have added a new test that verifies VmListeners are unregistered after a call to deactivate. This fails before this commit and passes afterward. I also took this opportunity to refactor the plugin Backends a bit. There are 3 different plugin backends that listen to status changes in VMs and attach/detach VmListeners in response. I have created a superclass for these in agent-core called VmListenerBackend that removes a lot of code duplication. I have also moved the bulk of those Backends' tests to a new test class for VmListenerBackend. Reviewed-by: vanaltj Review-thread: http://icedtea.classpath.org/pipermail/thermostat/2013-January/005481.html
author Elliott Baron <ebaron@redhat.com>
date Mon, 11 Feb 2013 16:37:36 -0500
parents e11acc79c432
children bab2eabfab98
files agent/core/src/main/java/com/redhat/thermostat/backend/Backend.java agent/core/src/main/java/com/redhat/thermostat/backend/BackendRegistry.java agent/core/src/main/java/com/redhat/thermostat/backend/VmListenerBackend.java agent/core/src/test/java/com/redhat/thermostat/backend/VmListenerBackendTest.java vm-classstat/agent/src/main/java/com/redhat/thermostat/vm/classstat/agent/internal/VmClassStatBackend.java vm-classstat/agent/src/test/java/com/redhat/thermostat/vm/classstat/agent/internal/VmClassStatBackendTest.java vm-gc/agent/src/main/java/com/redhat/thermostat/vm/gc/agent/internal/VmGcBackend.java vm-gc/agent/src/test/java/com/redhat/thermostat/vm/gc/agent/internal/VmGcBackendTest.java vm-memory/agent/src/main/java/com/redhat/thermostat/vm/memory/agent/internal/Activator.java vm-memory/agent/src/main/java/com/redhat/thermostat/vm/memory/agent/internal/VmMemoryBackend.java vm-memory/agent/src/test/java/com/redhat/thermostat/vm/memory/agent/internal/ActivatorTest.java vm-memory/agent/src/test/java/com/redhat/thermostat/vm/memory/agent/internal/VmMemoryBackendTest.java
diffstat 12 files changed, 492 insertions(+), 728 deletions(-) [+]
line wrap: on
line diff
--- a/agent/core/src/main/java/com/redhat/thermostat/backend/Backend.java	Mon Feb 11 16:19:03 2013 -0500
+++ b/agent/core/src/main/java/com/redhat/thermostat/backend/Backend.java	Mon Feb 11 16:37:36 2013 -0500
@@ -51,6 +51,8 @@
  * <p>
  * To register a new backend, register an instance of the class with the OSGi
  * service registry.
+ * 
+ * @see VmListenerBackend
  */
 @ExtensionPoint
 public abstract class Backend implements Ordered {
--- a/agent/core/src/main/java/com/redhat/thermostat/backend/BackendRegistry.java	Mon Feb 11 16:19:03 2013 -0500
+++ b/agent/core/src/main/java/com/redhat/thermostat/backend/BackendRegistry.java	Mon Feb 11 16:37:36 2013 -0500
@@ -42,7 +42,6 @@
 import org.osgi.framework.Constants;
 import org.osgi.framework.InvalidSyntaxException;
 
-import com.redhat.thermostat.backend.Backend;
 import com.redhat.thermostat.common.ThermostatExtensionRegistry;
 import com.redhat.thermostat.common.utils.LoggingUtils;
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/agent/core/src/main/java/com/redhat/thermostat/backend/VmListenerBackend.java	Mon Feb 11 16:37:36 2013 -0500
@@ -0,0 +1,213 @@
+/*
+ * Copyright 2012, 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
+ * <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.backend;
+
+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.common.Pair;
+import com.redhat.thermostat.common.utils.LoggingUtils;
+
+/**
+ * This class is a convenient subclass of {@link Backend} for those that need to
+ * attach {@link VmListener} in response to starting and stopping of JVMs on a
+ * host.
+ * 
+ * @see VmStatusListener
+ * @see Backend
+ */
+public abstract class VmListenerBackend extends Backend implements VmStatusListener {
+    
+    private static final Logger logger = LoggingUtils.getLogger(VmListenerBackend.class);
+
+    private MonitoredHost host;
+    private Map<Integer, Pair<MonitoredVm, ? extends VmListener>> pidToData = new HashMap<>();
+    private final VmStatusListenerRegistrar registrar;
+    private boolean started;
+
+    public VmListenerBackend(BackendID id, VmStatusListenerRegistrar registrar) {
+        super(id);
+        this.registrar = registrar;
+        
+        try {
+            HostIdentifier hostId = new HostIdentifier((String) null);
+            host = MonitoredHost.getMonitoredHost(hostId);
+        } 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);
+        }
+    }
+    
+    /**
+     * {@inheritDoc}
+     * 
+     * <p>
+     * Registers a VmListener to begin receiving VM lifecycle events.
+     * Subclasses should call <code>super.activate()</code> when overriding this method.
+     * </p>
+     */
+    @Override
+    public boolean activate() {
+        if (!started && host != null) {
+            registrar.register(this);
+            started = true;
+        }
+        return started;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * <p>
+     * Unregisters the VmListener to stop receiving VM lifecycle events.
+     * Subclasses should call <code>super.deactivate()</code> when overriding this method.
+     * </p>
+     */
+    @Override
+    public boolean deactivate() {
+        if (started) {
+            registrar.unregister(this);
+            removeVmListeners();
+            started = false;
+        }
+        return !started;
+    }
+    
+    @Override
+    public boolean isActive() {
+        return started;
+    }
+
+    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))));
+                VmListener listener = createVmListener(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<MonitoredVm, ? extends VmListener> 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();
+    }
+
+    private void removeVmListeners() {
+        for (Pair<MonitoredVm, ? extends VmListener> data : pidToData.values()) {
+            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);
+            }
+        }
+    }
+    
+    /**
+     * Creates a new {@link VmListener} for the virtual machine
+     * specified by the pid. This method is called when a new
+     * JVM is started or for JVMs already active when this Backend
+     * was activated.
+     * @param pid the process ID of the JVM
+     * @return a new VmListener for the VM specified by pid
+     */
+    protected abstract VmListener createVmListener(int pid);
+    
+    /*
+     * For testing purposes only.
+     */
+    void setHost(MonitoredHost host) {
+        this.host = host;
+    }
+    
+    /*
+     * For testing purposes only.
+     */
+    Map<Integer, Pair<MonitoredVm, ? extends VmListener>> getPidToDataMap() {
+        return pidToData;
+    }
+
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/agent/core/src/test/java/com/redhat/thermostat/backend/VmListenerBackendTest.java	Mon Feb 11 16:37:36 2013 -0500
@@ -0,0 +1,242 @@
+/*
+ * Copyright 2012, 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
+ * <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.backend;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+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.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;
+
+public class VmListenerBackendTest {
+    
+    private VmListenerBackend backend;
+    private HostIdentifier hostIdentifier;
+    private MonitoredHost host;
+    private MonitoredVm monitoredVm;
+    private VmListener listener;
+    private VmStatusListenerRegistrar registrar;
+
+    @Before
+    public void setup() throws URISyntaxException, MonitorException {
+        BackendID id = mock(BackendID.class);
+        registrar = mock(VmStatusListenerRegistrar.class);
+        listener = mock(VmListener.class);
+        
+        hostIdentifier = mock(HostIdentifier.class);
+        when(hostIdentifier.resolve(isA(VmIdentifier.class))).then(new Answer<VmIdentifier>() {
+            @Override
+            public VmIdentifier answer(InvocationOnMock invocation) throws Throwable {
+                return (VmIdentifier) invocation.getArguments()[0];
+            }
+        });
+        host = mock(MonitoredHost.class);
+        when(host.getHostIdentifier()).thenReturn(hostIdentifier);
+        
+        monitoredVm = mock(MonitoredVm.class);
+        
+        backend = new TestBackend(id, registrar);
+        backend.setHost(host);
+    }
+    
+    @Test
+    public void testActivate() {
+        backend.activate();
+        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 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 {
+        final int VM_PID = 1;
+        VmIdentifier VM_ID = new VmIdentifier(String.valueOf(VM_PID));
+        when(host.getMonitoredVm(VM_ID)).thenReturn(monitoredVm);
+
+        backend.vmStatusChanged(Status.VM_STARTED, 1);
+
+        verify(monitoredVm).addVmListener(listener);
+    }
+
+    @Test
+    public void testAlreadyRunningVM() throws MonitorException, URISyntaxException {
+        final int VM_PID = 1;
+        VmIdentifier VM_ID = new VmIdentifier(String.valueOf(VM_PID));
+        when(host.getMonitoredVm(VM_ID)).thenReturn(monitoredVm);
+
+        backend.vmStatusChanged(Status.VM_ACTIVE, 1);
+
+        verify(monitoredVm).addVmListener(listener);
+    }
+
+    @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 {
+        final int VM_PID = 1;
+        VmIdentifier VM_ID = new VmIdentifier(String.valueOf(VM_PID));
+        when(host.getMonitoredVm(VM_ID)).thenReturn(monitoredVm);
+
+        backend.vmStatusChanged(Status.VM_STARTED, 1);
+        backend.vmStatusChanged(Status.VM_STOPPED, 1);
+
+        verify(monitoredVm).removeVmListener(listener);
+    }
+
+    @Test
+    public void testUnknownVMStopped() throws URISyntaxException, MonitorException {
+        final int VM_PID = 1;
+        VmIdentifier VM_ID = new VmIdentifier(String.valueOf(VM_PID));
+        when(host.getMonitoredVm(VM_ID)).thenReturn(monitoredVm);
+
+        backend.vmStatusChanged(Status.VM_STOPPED, 1);
+
+        verifyNoMoreInteractions(monitoredVm);
+    }
+
+    @Test
+    public void testErrorRemovingVmListener() throws URISyntaxException, MonitorException {
+        final int VM_PID = 1;
+        VmIdentifier VM_ID = new VmIdentifier(String.valueOf(VM_PID));
+        when(host.getMonitoredVm(VM_ID)).thenReturn(monitoredVm);
+        MonitorException monitorException = new MonitorException();
+        doThrow(monitorException).when(monitoredVm).removeVmListener(listener);
+
+        backend.vmStatusChanged(Status.VM_STARTED, 1);
+        backend.vmStatusChanged(Status.VM_STOPPED, 1);
+
+        verify(monitoredVm).detach();
+    }
+    
+    @Test
+    public void testDeactivateUnregistersListener() throws URISyntaxException, MonitorException {
+        final int VM_PID = 1;
+        backend.activate();
+        
+        VmIdentifier VM_ID = new VmIdentifier(String.valueOf(VM_PID));
+        when(host.getMonitoredVm(VM_ID)).thenReturn(monitoredVm);
+
+        backend.vmStatusChanged(Status.VM_STARTED, 1);
+        backend.deactivate();
+        verify(monitoredVm).removeVmListener(listener);
+    }
+    
+    private class TestBackend extends VmListenerBackend {
+
+        public TestBackend(BackendID id, VmStatusListenerRegistrar registrar) {
+            super(id, registrar);
+        }
+
+        @Override
+        public int getOrderValue() {
+            return 0;
+        }
+
+        @Override
+        protected VmListener createVmListener(int pid) {
+            return listener;
+        }
+
+        @Override
+        public boolean attachToNewProcessByDefault() {
+            return true;
+        }
+        
+    }
+
+}
--- a/vm-classstat/agent/src/main/java/com/redhat/thermostat/vm/classstat/agent/internal/VmClassStatBackend.java	Mon Feb 11 16:19:03 2013 -0500
+++ b/vm-classstat/agent/src/main/java/com/redhat/thermostat/vm/classstat/agent/internal/VmClassStatBackend.java	Mon Feb 11 16:37:36 2013 -0500
@@ -36,86 +36,26 @@
 
 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.backend.VmListenerBackend;
 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 implements VmStatusListener {
-
-    private static final Logger LOGGER = LoggingUtils.getLogger(VmClassStatBackend.class);
+public class VmClassStatBackend extends VmListenerBackend {
 
     private final VmClassStatDAO vmClassStats;
-    private final VmStatusListenerRegistrar registrar;
-
-    private final Map<Integer, Pair<MonitoredVm, ? extends VmListener>> pidToVmAndListener = new HashMap<>();
-
-    private MonitoredHost host;
-
-    private boolean started;
 
     public VmClassStatBackend(VmClassStatDAO vmClassStatDAO, Version version, VmStatusListenerRegistrar registrar) {
-        super(new BackendID("VM Classes Backend", VmClassStatBackend.class.getName()));
+        super(new BackendID("VM Classes Backend", VmClassStatBackend.class.getName()), registrar);
         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 {
-            HostIdentifier hostId = new HostIdentifier((String) null);
-            host = MonitoredHost.getMonitoredHost(hostId);
-        } 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);
-        }
-    }
-
-    /*
-     * Methods from Backend
-     */
-
-    @Override
-    public boolean activate() {
-        if (!started && host != null) {
-            registrar.register(this);
-            started = true;
-        }
-        return started;
-    }
-
-    @Override
-    public boolean deactivate() {
-        if (started) {
-            registrar.unregister(this);
-            started = false;
-        }
-        return !started;
-    }
-    
-    @Override
-    public boolean isActive() {
-        return started;
     }
     
     @Override
@@ -133,70 +73,9 @@
         return ORDER_MEMORY_GROUP + 30;
     }
 
-    /*
-     *  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<MonitoredVm, ? extends VmListener> 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<Integer, Pair<MonitoredVm, ? extends VmListener>> getPidToDataMap() {
-        return pidToVmAndListener;
+    protected VmListener createVmListener(int pid) {
+        return new VmClassStatVmListener(vmClassStats, pid);
     }
 
 }
--- a/vm-classstat/agent/src/test/java/com/redhat/thermostat/vm/classstat/agent/internal/VmClassStatBackendTest.java	Mon Feb 11 16:19:03 2013 -0500
+++ b/vm-classstat/agent/src/test/java/com/redhat/thermostat/vm/classstat/agent/internal/VmClassStatBackendTest.java	Mon Feb 11 16:37:36 2013 -0500
@@ -36,31 +36,18 @@
 
 package com.redhat.thermostat.vm.classstat.agent.internal;
 
-import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
-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.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;
@@ -68,10 +55,6 @@
 public class VmClassStatBackendTest {
     
     private VmClassStatBackend backend;
-    private MonitoredHost host;
-    private VmStatusListenerRegistrar registrar;
-    private HostIdentifier hostIdentifier;
-    private MonitoredVm monitoredVm1;
 
     @Before
     public void setup() throws MonitorException, URISyntaxException {
@@ -80,139 +63,15 @@
         Version version = mock(Version.class);
         when(version.getVersionNumber()).thenReturn("0.0.0");
         
-        registrar = mock(VmStatusListenerRegistrar.class);
-
-        hostIdentifier = mock(HostIdentifier.class);
-        when(hostIdentifier.resolve(isA(VmIdentifier.class))).then(new Answer<VmIdentifier>() {
-            @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);
+        VmStatusListenerRegistrar registrar = mock(VmStatusListenerRegistrar.class);
 
         backend = new VmClassStatBackend(vmClassStatDao, version, registrar);
-        
-        backend.setHost(host);
-    }
-
-    @Test
-    public void testActivate() {
-        backend.activate();
-        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 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_MEMORY_GROUP);
         assertTrue(orderValue < Ordered.ORDER_NETWORK_GROUP);
     }
 }
--- a/vm-gc/agent/src/main/java/com/redhat/thermostat/vm/gc/agent/internal/VmGcBackend.java	Mon Feb 11 16:19:03 2013 -0500
+++ b/vm-gc/agent/src/main/java/com/redhat/thermostat/vm/gc/agent/internal/VmGcBackend.java	Mon Feb 11 16:37:36 2013 -0500
@@ -36,85 +36,29 @@
 
 package com.redhat.thermostat.vm.gc.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.backend.VmListenerBackend;
 import com.redhat.thermostat.common.Version;
-import com.redhat.thermostat.common.utils.LoggingUtils;
 import com.redhat.thermostat.vm.gc.common.VmGcStatDAO;
 
-public class VmGcBackend extends Backend implements VmStatusListener {
-
-    private static final Logger LOGGER = LoggingUtils.getLogger(VmGcBackend.class);
+public class VmGcBackend extends VmListenerBackend {
 
     private final VmGcStatDAO vmGcStats;
-    private final VmStatusListenerRegistrar registerer;
 
-    private final Map<Integer, Pair<MonitoredVm, ? extends VmListener>> pidToData = new HashMap<>();
-    private MonitoredHost host;
-    private boolean started;
-
-    public VmGcBackend(VmGcStatDAO vmGcStatDAO, Version version, VmStatusListenerRegistrar registerer) {
-        super(new BackendID("VM GC Backend", VmGcBackend.class.getName()));
+    public VmGcBackend(VmGcStatDAO vmGcStatDAO, Version version, VmStatusListenerRegistrar registrar) {
+        super(new BackendID("VM GC Backend", VmGcBackend.class.getName()), registrar);
         this.vmGcStats = vmGcStatDAO;
-        this.registerer = registerer;
         
         setConfigurationValue(BackendsProperties.VENDOR.name(), "Red Hat, Inc.");
         setConfigurationValue(BackendsProperties.DESCRIPTION.name(), "Gathers garbage collection statistics about a JVM");
         setConfigurationValue(BackendsProperties.VERSION.name(), version.getVersionNumber());
-        
-        try {
-            HostIdentifier hostId = new HostIdentifier((String) null);
-            host = MonitoredHost.getMonitoredHost(hostId);
-        } 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);
-        }
-    }
-
-    // Methods from Backend
-
-    @Override
-    public boolean activate() {
-        if (!started && host != null) {
-            registerer.register(this);
-            started = true;
-        }
-        return started;
-    }
-
-    @Override
-    public boolean deactivate() {
-        if (started) {
-            registerer.unregister(this);
-            started = false;
-        }
-        return !started;
     }
     
     @Override
-    public boolean isActive() {
-        return started;
-    }
-
-    @Override
     public String getConfigurationValue(String key) {
         return null;
     }
@@ -129,66 +73,9 @@
         return ORDER_MEMORY_GROUP + 20;
     }
 
-    // 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))));
-                if (vm != null) {
-                    VmGcVmListener listener = new VmGcVmListener(vmGcStats, pid);
-                    vm.addVmListener(listener);
-                    pidToData.put(pid, new Pair<>(vm, listener));
-                    LOGGER.finer("Attached VmListener for VM: " + pid);
-                } else {
-                    LOGGER.warning("could not connect to vm " + pid);
-                }
-            } catch (MonitorException me) {
-                LOGGER.log(Level.WARNING, "could not connect to vm " + pid, me);
-            } catch (URISyntaxException e) {
-                throw new AssertionError("The URI for the monitored vm must be valid, but it is not.");
-            }
-        }
-    }
-
-    private void vmStopped(int pid) {
-        Pair<MonitoredVm, ? extends VmListener> 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 = data.getFirst();
-        VmListener listener = data.getSecond();
-        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.
-     */
-    void setHost(MonitoredHost host) {
-        this.host = host;
+    protected VmListener createVmListener(int pid) {
+        return new VmGcVmListener(vmGcStats, pid);
     }
 
 }
--- a/vm-gc/agent/src/test/java/com/redhat/thermostat/vm/gc/agent/internal/VmGcBackendTest.java	Mon Feb 11 16:19:03 2013 -0500
+++ b/vm-gc/agent/src/test/java/com/redhat/thermostat/vm/gc/agent/internal/VmGcBackendTest.java	Mon Feb 11 16:37:36 2013 -0500
@@ -36,39 +36,25 @@
 
 package com.redhat.thermostat.vm.gc.agent.internal;
 
-import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
-import static org.mockito.Matchers.isA;
 import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
 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.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.gc.common.VmGcStatDAO;
 
 public class VmGcBackendTest {
 
     private VmGcBackend backend;
-    private VmStatusListenerRegistrar registerer;
-
-    private MonitoredHost host;
-    private HostIdentifier hostIdentifier;
-    private MonitoredVm monitoredVm1;
 
     @Before
     public void setup() throws MonitorException, URISyntaxException {
@@ -77,62 +63,16 @@
         Version version = mock(Version.class);
         when(version.getVersionNumber()).thenReturn("0.0.0");
 
-        registerer = mock(VmStatusListenerRegistrar.class);
-
-        hostIdentifier = mock(HostIdentifier.class);
-        when(hostIdentifier.resolve(isA(VmIdentifier.class))).then(new Answer<VmIdentifier>() {
-            @Override
-            public VmIdentifier answer(InvocationOnMock invocation) throws Throwable {
-                return (VmIdentifier) invocation.getArguments()[0];
-            }
-        });
-        host = mock(MonitoredHost.class);
-        when(host.getHostIdentifier()).thenReturn(hostIdentifier);
+        VmStatusListenerRegistrar registrar = mock(VmStatusListenerRegistrar.class);
 
-        monitoredVm1 = mock(MonitoredVm.class);
-
-        backend = new VmGcBackend(vmGcStatDao, version, registerer);
-
-        backend.setHost(host);
-    }
-
-    @Test
-    public void testStart() throws MonitorException {
-        backend.activate();
-        verify(registerer).register(backend);
-        assertTrue(backend.isActive());
+        backend = new VmGcBackend(vmGcStatDao, version, registrar);
     }
 
     @Test
-    public void testStop() throws MonitorException {
-        backend.activate();
-        backend.deactivate();
-        verify(registerer).unregister(backend);
-        assertFalse(backend.isActive());
+    public void testOrderValue() {
+        int order = backend.getOrderValue();
+        assertTrue(order >= Ordered.ORDER_MEMORY_GROUP);
+        assertTrue(order < Ordered.ORDER_NETWORK_GROUP);
     }
-
-    @Test
-    public void testNewVM() throws InterruptedException, 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(VmGcVmListener.class));
-    }
-
-    @Test
-    public void testStoppedVM() throws InterruptedException, 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(VmGcVmListener.class));
-    }
-
 }
 
--- a/vm-memory/agent/src/main/java/com/redhat/thermostat/vm/memory/agent/internal/Activator.java	Mon Feb 11 16:19:03 2013 -0500
+++ b/vm-memory/agent/src/main/java/com/redhat/thermostat/vm/memory/agent/internal/Activator.java	Mon Feb 11 16:37:36 2013 -0500
@@ -45,6 +45,7 @@
 import com.redhat.thermostat.agent.VmStatusListenerRegistrar;
 import com.redhat.thermostat.backend.Backend;
 import com.redhat.thermostat.backend.BackendService;
+import com.redhat.thermostat.backend.VmListenerBackend;
 import com.redhat.thermostat.common.MultipleServiceTracker;
 import com.redhat.thermostat.common.MultipleServiceTracker.Action;
 import com.redhat.thermostat.common.Version;
@@ -53,7 +54,7 @@
 public class Activator implements BundleActivator {
     
     private MultipleServiceTracker tracker;
-    private VmMemoryBackend backend;
+    private VmListenerBackend backend;
     private ServiceRegistration reg;
     
     @Override
@@ -94,7 +95,7 @@
     /*
      * For testing purposes only.
      */
-    VmMemoryBackend getBackend() {
+    VmListenerBackend getBackend() {
         return backend;
     }
 }
--- a/vm-memory/agent/src/main/java/com/redhat/thermostat/vm/memory/agent/internal/VmMemoryBackend.java	Mon Feb 11 16:19:03 2013 -0500
+++ b/vm-memory/agent/src/main/java/com/redhat/thermostat/vm/memory/agent/internal/VmMemoryBackend.java	Mon Feb 11 16:37:36 2013 -0500
@@ -36,83 +36,27 @@
 
 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.backend.VmListenerBackend;
 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 implements VmStatusListener {
-
-    private static final Logger LOGGER = LoggingUtils.getLogger(VmMemoryBackend.class);
+public class VmMemoryBackend extends VmListenerBackend {
 
     private VmMemoryStatDAO vmMemoryStats;
-    private MonitoredHost host;
-    private final VmStatusListenerRegistrar registrar;
-
-    private boolean started;
-    private Map<Integer, Pair<MonitoredVm, ? extends VmListener>> pidToData = new HashMap<>();
-
+    
     public VmMemoryBackend(VmMemoryStatDAO vmMemoryStatDAO, Version version, VmStatusListenerRegistrar registrar) {
-        super(new BackendID("VM Memory Backend", VmMemoryBackend.class.getName()));
+        super(new BackendID("VM Memory Backend", VmMemoryBackend.class.getName()), registrar);
         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 {
-            HostIdentifier hostId = new HostIdentifier((String) null);
-            host = MonitoredHost.getMonitoredHost(hostId);
-        } 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) {
-            registrar.register(this);
-            started = true;
-        }
-        return started;
-    }
-
-    @Override
-    public boolean deactivate() {
-        if (started) {
-            registrar.unregister(this);
-            started = false;
-        }
-        return !started;
     }
     
     @Override
-    public boolean isActive() {
-        return started;
-    }
-
-    @Override
     public String getConfigurationValue(String key) {
         return null;
     }
@@ -127,63 +71,9 @@
         return ORDER_MEMORY_GROUP + 40;
     }
 
-    /*
-     * 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<MonitoredVm, ? extends VmListener> 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) {
-        this.host = host;
+    @Override
+    protected VmMemoryVmListener createVmListener(int pid) {
+        return new VmMemoryVmListener(vmMemoryStats, pid);
     }
     
 }
--- a/vm-memory/agent/src/test/java/com/redhat/thermostat/vm/memory/agent/internal/ActivatorTest.java	Mon Feb 11 16:19:03 2013 -0500
+++ b/vm-memory/agent/src/test/java/com/redhat/thermostat/vm/memory/agent/internal/ActivatorTest.java	Mon Feb 11 16:37:36 2013 -0500
@@ -49,6 +49,7 @@
 
 import com.redhat.thermostat.backend.Backend;
 import com.redhat.thermostat.backend.BackendService;
+import com.redhat.thermostat.backend.VmListenerBackend;
 import com.redhat.thermostat.testutils.StubBundleContext;
 import com.redhat.thermostat.vm.memory.common.VmMemoryStatDAO;
 
@@ -90,7 +91,7 @@
         activator.start(context);
 
         assertTrue(context.isServiceRegistered(Backend.class.getName(), VmMemoryBackend.class));
-        VmMemoryBackend backend = activator.getBackend();
+        VmListenerBackend backend = activator.getBackend();
         assertNotNull(backend);
 
         // core thermostat activates the backend when the backend is detected
--- a/vm-memory/agent/src/test/java/com/redhat/thermostat/vm/memory/agent/internal/VmMemoryBackendTest.java	Mon Feb 11 16:19:03 2013 -0500
+++ b/vm-memory/agent/src/test/java/com/redhat/thermostat/vm/memory/agent/internal/VmMemoryBackendTest.java	Mon Feb 11 16:37:36 2013 -0500
@@ -36,31 +36,17 @@
 
 package com.redhat.thermostat.vm.memory.agent.internal;
 
-import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
-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.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;
@@ -69,159 +55,24 @@
 public class VmMemoryBackendTest {
     
     private VmMemoryBackend backend;
-    private MonitoredHost host;
-    private VmStatusListenerRegistrar registrar;
-    private VmMemoryStatDAO vmMemoryStatDao;
 
     @Before
     public void setup() throws MonitorException, URISyntaxException {
-        vmMemoryStatDao = mock(VmMemoryStatDAO.class);
+        VmMemoryStatDAO vmMemoryStatDao = mock(VmMemoryStatDAO.class);
         
         Version version = mock(Version.class);
         when(version.getVersionNumber()).thenReturn("0.0.0");
 
-        registrar = mock(VmStatusListenerRegistrar.class);
+        VmStatusListenerRegistrar registrar = mock(VmStatusListenerRegistrar.class);
 
         backend = new VmMemoryBackend(vmMemoryStatDao, version, registrar);
-        
-        HostIdentifier hostIdentifier = mock(HostIdentifier.class);
-        when(hostIdentifier.resolve(isA(VmIdentifier.class))).then(new Answer<VmIdentifier>() {
-            @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 testActivate() {
-        assertTrue(backend.activate());
-
-        verify(registrar).register(backend);
-        assertTrue(backend.isActive());
-    }
-
-    @Test
-    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(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<VmListener> 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<VmListener> 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_MEMORY_GROUP);
         assertTrue(orderValue < Ordered.ORDER_NETWORK_GROUP);
     }
 }