changeset 2763:845c5af7b42f

Add VM Mapper service review-thread: http://icedtea.classpath.org/pipermail/thermostat/2017-September/025088.html reviwed-by: jerboaa
author Mario Torre <neugens.limasoftware@gmail.com>
date Tue, 26 Sep 2017 15:10:31 +0200
parents 8a51183ffad8
children d624d92d95ca
files plugins/jvm-overview/agent/src/main/java/com/redhat/thermostat/jvm/overview/agent/VmListenerBackend.java plugins/jvm-overview/agent/src/main/java/com/redhat/thermostat/jvm/overview/agent/VmMonitor.java plugins/jvm-overview/agent/src/main/java/com/redhat/thermostat/jvm/overview/agent/VmPollingBackend.java plugins/jvm-overview/agent/src/main/java/com/redhat/thermostat/jvm/overview/agent/internal/VMMonitorBackend.java plugins/jvm-overview/agent/src/main/java/com/redhat/thermostat/jvm/overview/agent/internal/VmMonitor.java plugins/jvm-overview/agent/src/main/java/com/redhat/thermostat/jvm/overview/agent/internal/VmStatusChangeNotifier.java plugins/jvm-overview/agent/src/main/java/com/redhat/thermostat/jvm/overview/agent/internal/model/VmMapperServiceImpl.java plugins/jvm-overview/agent/src/main/java/com/redhat/thermostat/jvm/overview/agent/model/HostsVMsLoader.java plugins/jvm-overview/agent/src/main/java/com/redhat/thermostat/jvm/overview/agent/model/VmMapperService.java plugins/jvm-overview/agent/src/test/java/com/redhat/thermostat/jvm/overview/agent/VmListenerBackendTest.java plugins/jvm-overview/agent/src/test/java/com/redhat/thermostat/jvm/overview/agent/VmMonitorTest.java plugins/jvm-overview/agent/src/test/java/com/redhat/thermostat/jvm/overview/agent/internal/VmMonitorTest.java plugins/jvm-overview/agent/src/test/java/com/redhat/thermostat/jvm/overview/agent/internal/VmStatusChangeNotifierTest.java plugins/jvm-overview/agent/src/test/java/com/redhat/thermostat/jvm/overview/agent/internal/model/VmMapperServiceImplTest.java
diffstat 14 files changed, 662 insertions(+), 494 deletions(-) [+]
line wrap: on
line diff
--- a/plugins/jvm-overview/agent/src/main/java/com/redhat/thermostat/jvm/overview/agent/VmListenerBackend.java	Tue Sep 26 15:10:28 2017 +0200
+++ b/plugins/jvm-overview/agent/src/main/java/com/redhat/thermostat/jvm/overview/agent/VmListenerBackend.java	Tue Sep 26 15:10:31 2017 +0200
@@ -42,6 +42,7 @@
 import com.redhat.thermostat.backend.BaseBackend;
 import com.redhat.thermostat.backend.BackendException;
 import com.redhat.thermostat.common.utils.LoggingUtils;
+import com.redhat.thermostat.jvm.overview.agent.internal.VmMonitor;
 import com.redhat.thermostat.storage.core.WriterID;
 
 /**
--- a/plugins/jvm-overview/agent/src/main/java/com/redhat/thermostat/jvm/overview/agent/VmMonitor.java	Tue Sep 26 15:10:28 2017 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,150 +0,0 @@
-/*
- * Copyright 2012-2017 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.jvm.overview.agent;
-
-import java.net.URISyntaxException;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-import com.redhat.thermostat.backend.BackendException;
-import com.redhat.thermostat.jvm.overview.agent.internal.VmListenerWrapper;
-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.common.portability.ProcessChecker;
-import com.redhat.thermostat.common.Pair;
-import com.redhat.thermostat.common.utils.LoggingUtils;
-
-public class VmMonitor {
-    
-    private final Logger logger = LoggingUtils.getLogger(VmMonitor.class);
-
-    private final ProcessChecker processChecker;
-    private MonitoredHost host;
-    private Map<Integer, Pair<MonitoredVm, VmListenerWrapper>> pidToData = new HashMap<>();
-    
-    public VmMonitor() throws BackendException {
-        this(new ProcessChecker());
-    }
-
-    public VmMonitor(ProcessChecker processChecker) throws BackendException {
-        this.processChecker = processChecker;
-        try {
-            HostIdentifier hostId = new HostIdentifier((String) null);
-            host = MonitoredHost.getMonitoredHost(hostId);
-        } catch (MonitorException me) {
-            throw new BackendException("Problems with connecting jvmstat to local machine", me);
-        } catch (URISyntaxException use) {
-            throw new BackendException("Failed to create host identifier", use);
-        }
-    }
-    
-    public void handleNewVm(VmUpdateListener listener, int pid) {
-        try {
-            MonitoredVm vm = host.getMonitoredVm(host.getHostIdentifier().resolve(new VmIdentifier(String.valueOf(pid))));
-            VmListenerWrapper wrapper = new VmListenerWrapper(listener, vm);
-            vm.addVmListener(wrapper);
-
-            pidToData.put(pid, new Pair<>(vm, wrapper));
-            logger.finer("Attached " + listener.getClass().getName() + " for VM: " + pid);
-        } catch (MonitorException e) {
-            logMsg(pid, e);
-        } catch (URISyntaxException e) {
-            logger.log(Level.WARNING, "unable to attach to vm " + pid, e);
-        }
-    }
-
-    private void logMsg(int pid, MonitorException e) {
-        Throwable cause = e.getCause();
-        if (cause != null && cause instanceof IllegalArgumentException && !processChecker.exists(pid)) {
-            logger.log(Level.FINEST, "Tried to attach to a process which no longer exists. Pid was " + pid, e);
-        } else {
-            logger.log(Level.WARNING, "unable to attach to vm" + pid, e);
-        }
-    }
-
-    public void handleStoppedVm(int pid) {
-        Pair<MonitoredVm, VmListenerWrapper> data = pidToData.remove(pid);
-        // we were not monitoring pid at all, so nothing to do
-        if (data == null) {
-            return;
-        }
-    
-        MonitoredVm vm = data.getFirst();
-        VmListenerWrapper listener = data.getSecond();
-        try {
-            vm.removeVmListener(listener);
-        } catch (MonitorException e) {
-            logger.log(Level.WARNING, "can't remove vm listener", e);
-        }
-        vm.detach();
-    }
-
-    public void removeVmListeners() {
-        for (Pair<MonitoredVm, VmListenerWrapper> data : pidToData.values()) {
-            MonitoredVm vm = data.getFirst();
-            VmListenerWrapper listener = data.getSecond();
-            try {
-                vm.removeVmListener(listener);
-            } catch (MonitorException e) {
-                logger.log(Level.WARNING, "can't remove vm listener", e);
-            }
-        }
-        pidToData.clear();
-    }
-    
-    /*
-     * For testing purposes only.
-     */
-    void setHost(MonitoredHost host) {
-        this.host = host;
-    }
-    
-    /*
-     * For testing purposes only.
-     */
-    Map<Integer, Pair<MonitoredVm, VmListenerWrapper>> getPidToDataMap() {
-        return pidToData;
-    }
-
-}
-
--- a/plugins/jvm-overview/agent/src/main/java/com/redhat/thermostat/jvm/overview/agent/VmPollingBackend.java	Tue Sep 26 15:10:28 2017 +0200
+++ b/plugins/jvm-overview/agent/src/main/java/com/redhat/thermostat/jvm/overview/agent/VmPollingBackend.java	Tue Sep 26 15:10:31 2017 +0200
@@ -50,6 +50,8 @@
 import com.redhat.thermostat.common.Pair;
 import com.redhat.thermostat.common.Version;
 import com.redhat.thermostat.common.utils.LoggingUtils;
+import com.redhat.thermostat.jvm.overview.agent.internal.model.VmMapperServiceImpl;
+import com.redhat.thermostat.jvm.overview.agent.model.VmMapperService;
 
 /**
  * Convenience {@link com.redhat.thermostat.backend.Backend} class for implementations that will take some
--- a/plugins/jvm-overview/agent/src/main/java/com/redhat/thermostat/jvm/overview/agent/internal/VMMonitorBackend.java	Tue Sep 26 15:10:28 2017 +0200
+++ b/plugins/jvm-overview/agent/src/main/java/com/redhat/thermostat/jvm/overview/agent/internal/VMMonitorBackend.java	Tue Sep 26 15:10:31 2017 +0200
@@ -43,6 +43,8 @@
 import com.redhat.thermostat.common.utils.LoggingUtils;
 import com.redhat.thermostat.jvm.overview.agent.VmBlacklist;
 import com.redhat.thermostat.jvm.overview.agent.internal.model.VmInfoDAO;
+import com.redhat.thermostat.jvm.overview.agent.internal.model.VmMapperServiceImpl;
+import com.redhat.thermostat.jvm.overview.agent.model.VmMapperService;
 import com.redhat.thermostat.storage.core.WriterID;
 import org.apache.felix.scr.annotations.Activate;
 import org.apache.felix.scr.annotations.Component;
@@ -50,6 +52,7 @@
 import org.apache.felix.scr.annotations.Reference;
 import org.apache.felix.scr.annotations.Service;
 import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceRegistration;
 import sun.jvmstat.monitor.HostIdentifier;
 import sun.jvmstat.monitor.MonitorException;
 import sun.jvmstat.monitor.MonitoredHost;
@@ -80,6 +83,8 @@
 
     private MonitoredHost host;
     private JvmStatHostListener hostListener;
+    private VmMapperServiceImpl vmMapperService;
+    private ServiceRegistration<VmMapperService> mapperRegistration;
 
     public VMMonitorBackend() {
         super("VM Basic Monitor Backend",
@@ -90,13 +95,18 @@
 
     @Activate
     private void _activate_(BundleContext context) {
-        notifier = new VmStatusChangeNotifier(context);
+
+        vmMapperService = new VmMapperServiceImpl();
+        mapperRegistration = context.registerService(VmMapperService.class, vmMapperService, null);
+
+        notifier = new VmStatusChangeNotifier(context, vmMapperService);
         notifier.start();
     }
 
     @Deactivate
     private void _deactivate_() {
         notifier.stop();
+        mapperRegistration.unregister();
     }
 
     @Override
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/jvm-overview/agent/src/main/java/com/redhat/thermostat/jvm/overview/agent/internal/VmMonitor.java	Tue Sep 26 15:10:31 2017 +0200
@@ -0,0 +1,150 @@
+/*
+ * Copyright 2012-2017 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.jvm.overview.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 com.redhat.thermostat.backend.BackendException;
+import com.redhat.thermostat.jvm.overview.agent.VmUpdateListener;
+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.common.portability.ProcessChecker;
+import com.redhat.thermostat.common.Pair;
+import com.redhat.thermostat.common.utils.LoggingUtils;
+
+public class VmMonitor {
+    
+    private final Logger logger = LoggingUtils.getLogger(VmMonitor.class);
+
+    private final ProcessChecker processChecker;
+    private MonitoredHost host;
+    private Map<Integer, Pair<MonitoredVm, VmListenerWrapper>> pidToData = new HashMap<>();
+    
+    public VmMonitor() throws BackendException {
+        this(new ProcessChecker());
+    }
+
+    public VmMonitor(ProcessChecker processChecker) throws BackendException {
+        this.processChecker = processChecker;
+        try {
+            HostIdentifier hostId = new HostIdentifier((String) null);
+            host = MonitoredHost.getMonitoredHost(hostId);
+        } catch (MonitorException me) {
+            throw new BackendException("Problems with connecting jvmstat to local machine", me);
+        } catch (URISyntaxException use) {
+            throw new BackendException("Failed to create host identifier", use);
+        }
+    }
+    
+    public void handleNewVm(VmUpdateListener listener, int pid) {
+        try {
+            MonitoredVm vm = host.getMonitoredVm(host.getHostIdentifier().resolve(new VmIdentifier(String.valueOf(pid))));
+            VmListenerWrapper wrapper = new VmListenerWrapper(listener, vm);
+            vm.addVmListener(wrapper);
+
+            pidToData.put(pid, new Pair<>(vm, wrapper));
+            logger.finer("Attached " + listener.getClass().getName() + " for VM: " + pid);
+        } catch (MonitorException e) {
+            logMsg(pid, e);
+        } catch (URISyntaxException e) {
+            logger.log(Level.WARNING, "unable to attach to vm " + pid, e);
+        }
+    }
+
+    private void logMsg(int pid, MonitorException e) {
+        Throwable cause = e.getCause();
+        if (cause != null && cause instanceof IllegalArgumentException && !processChecker.exists(pid)) {
+            logger.log(Level.FINEST, "Tried to attach to a process which no longer exists. Pid was " + pid, e);
+        } else {
+            logger.log(Level.WARNING, "unable to attach to vm" + pid, e);
+        }
+    }
+
+    public void handleStoppedVm(int pid) {
+        Pair<MonitoredVm, VmListenerWrapper> data = pidToData.remove(pid);
+        // we were not monitoring pid at all, so nothing to do
+        if (data == null) {
+            return;
+        }
+    
+        MonitoredVm vm = data.getFirst();
+        VmListenerWrapper listener = data.getSecond();
+        try {
+            vm.removeVmListener(listener);
+        } catch (MonitorException e) {
+            logger.log(Level.WARNING, "can't remove vm listener", e);
+        }
+        vm.detach();
+    }
+
+    public void removeVmListeners() {
+        for (Pair<MonitoredVm, VmListenerWrapper> data : pidToData.values()) {
+            MonitoredVm vm = data.getFirst();
+            VmListenerWrapper listener = data.getSecond();
+            try {
+                vm.removeVmListener(listener);
+            } catch (MonitorException e) {
+                logger.log(Level.WARNING, "can't remove vm listener", e);
+            }
+        }
+        pidToData.clear();
+    }
+    
+    /*
+     * For testing purposes only.
+     */
+    void setHost(MonitoredHost host) {
+        this.host = host;
+    }
+
+    /*
+     * For testing purposes only.
+     */
+    Map<Integer, Pair<MonitoredVm, VmListenerWrapper>> getPidToDataMap() {
+        return pidToData;
+    }
+
+}
+
--- a/plugins/jvm-overview/agent/src/main/java/com/redhat/thermostat/jvm/overview/agent/internal/VmStatusChangeNotifier.java	Tue Sep 26 15:10:28 2017 +0200
+++ b/plugins/jvm-overview/agent/src/main/java/com/redhat/thermostat/jvm/overview/agent/internal/VmStatusChangeNotifier.java	Tue Sep 26 15:10:31 2017 +0200
@@ -45,6 +45,7 @@
 
 import com.redhat.thermostat.jvm.overview.agent.VmStatusListener;
 import com.redhat.thermostat.jvm.overview.agent.VmStatusListener.Status;
+import com.redhat.thermostat.jvm.overview.agent.internal.model.VmMapperServiceImpl;
 import org.osgi.framework.BundleContext;
 import org.osgi.framework.ServiceReference;
 import org.osgi.util.tracker.ServiceTracker;
@@ -66,8 +67,12 @@
     private final Map<VmStatusListener, Set<Integer>> listeners = new ConcurrentHashMap<>();
 
     private final ServiceTracker tracker;
+    private VmMapperServiceImpl vmMapperService;
 
-    public VmStatusChangeNotifier(BundleContext bundleContext) {
+    public VmStatusChangeNotifier(BundleContext bundleContext, VmMapperServiceImpl vmMapperService) {
+
+        this.vmMapperService = vmMapperService;
+
         this.activePids = new HashMap<>();
 
         tracker = new ServiceTracker(bundleContext, VmStatusListener.class, null) {
@@ -128,6 +133,7 @@
 
             if (newStatus == Status.VM_STARTED) {
                 activePids.put(pid, vmId);
+                vmMapperService.cache(vmId, pid);
             } else {
                 activePids.remove(pid);
             }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/jvm-overview/agent/src/main/java/com/redhat/thermostat/jvm/overview/agent/internal/model/VmMapperServiceImpl.java	Tue Sep 26 15:10:31 2017 +0200
@@ -0,0 +1,71 @@
+/*
+ * Copyright 2012-2017 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.jvm.overview.agent.internal.model;
+
+import com.redhat.thermostat.jvm.overview.agent.model.VmId;
+import com.redhat.thermostat.jvm.overview.agent.model.VmMapperService;
+import com.redhat.thermostat.lang.schema.models.Pid;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+public class VmMapperServiceImpl implements VmMapperService {
+
+    private Map<String, Pid> cache;
+
+    public VmMapperServiceImpl() {
+        this(new ConcurrentHashMap<String, Pid>());
+    }
+
+    VmMapperServiceImpl(Map<String, Pid> cache) {
+        this.cache = cache;
+    }
+
+    @Override
+    public Pid getPid(VmId vmId) {
+        return getPid(vmId.get());
+    }
+
+    @Override
+    public Pid getPid(String vmId) {
+        return cache.get(vmId);
+    }
+
+    public void cache(String vmId, int pid) {
+        cache.put(vmId, new Pid(pid));
+    }
+}
--- a/plugins/jvm-overview/agent/src/main/java/com/redhat/thermostat/jvm/overview/agent/model/HostsVMsLoader.java	Tue Sep 26 15:10:28 2017 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,52 +0,0 @@
-/*
- * Copyright 2012-2017 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.jvm.overview.agent.model;
-
-import com.redhat.thermostat.storage.core.HostRef;
-
-import java.util.Collection;
-
-
-/**
- * Provides a way to load the current hosts and VMs.
- */
-public interface HostsVMsLoader {
-
-    Collection<HostRef> getHosts();
-    Collection<VmRef> getVMs(HostRef host);
-}
-
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/jvm-overview/agent/src/main/java/com/redhat/thermostat/jvm/overview/agent/model/VmMapperService.java	Tue Sep 26 15:10:31 2017 +0200
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2012-2017 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.jvm.overview.agent.model;
+
+import com.redhat.thermostat.annotations.Service;
+import com.redhat.thermostat.lang.schema.models.Pid;
+
+@Service
+public interface VmMapperService {
+    Pid getPid(VmId vmId);
+    Pid getPid(String vmId);
+}
--- a/plugins/jvm-overview/agent/src/test/java/com/redhat/thermostat/jvm/overview/agent/VmListenerBackendTest.java	Tue Sep 26 15:10:28 2017 +0200
+++ b/plugins/jvm-overview/agent/src/test/java/com/redhat/thermostat/jvm/overview/agent/VmListenerBackendTest.java	Tue Sep 26 15:10:31 2017 +0200
@@ -48,6 +48,7 @@
 import java.net.URISyntaxException;
 
 import com.redhat.thermostat.jvm.overview.agent.VmStatusListener.Status;
+import com.redhat.thermostat.jvm.overview.agent.internal.VmMonitor;
 import org.junit.Before;
 import org.junit.Test;
 
--- a/plugins/jvm-overview/agent/src/test/java/com/redhat/thermostat/jvm/overview/agent/VmMonitorTest.java	Tue Sep 26 15:10:28 2017 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,286 +0,0 @@
-/*
- * Copyright 2012-2017 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.jvm.overview.agent;
-
-import static org.junit.Assert.assertEquals;
-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 java.util.logging.Level;
-import java.util.logging.Logger;
-
-import com.redhat.thermostat.jvm.overview.agent.internal.TestLogHandler;
-import com.redhat.thermostat.jvm.overview.agent.internal.VmListenerWrapper;
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.mockito.ArgumentCaptor;
-import org.mockito.invocation.InvocationOnMock;
-import org.mockito.stubbing.Answer;
-
-import com.redhat.thermostat.common.portability.ProcessChecker;
-
-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;
-
-public class VmMonitorTest {
-
-    private static final String PROCESS_NOT_FOUND = "Process not found";
-    private static final int MONITOR_EXCEPTION_THROWING_PID = 999;
-    private VmMonitor monitor;
-    private HostIdentifier hostIdentifier;
-    private MonitoredHost host;
-    private MonitoredVm monitoredVm;
-    private ProcessChecker checker;
-    private TestLogHandler handler;
-    private Logger logger;
-    private Level savedLoggingLevel;
-
-    @After
-    public void tearDown() {
-        if (handler != null) {
-            logger.removeHandler(handler);
-            handler = null;
-        }
-        logger.setLevel(savedLoggingLevel);
-    }
-
-    @Before
-    public void setUp() throws Exception {
-        savedLoggingLevel = setupTestLoggerAndReturnOriginalLevel();
-
-        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);
-        
-        checker = mock(ProcessChecker.class);
-        when(checker.exists(isA(Integer.class))).thenReturn(false);
-
-        monitoredVm = mock(MonitoredVm.class);
-
-        monitor = new VmMonitor(checker);
-        monitor.setHost(host);
-    }
-    
-    private Level setupTestLoggerAndReturnOriginalLevel() {
-        logger = Logger.getLogger("com.redhat.thermostat");
-        Level originalLevel = logger.getLevel();
-        logger.setLevel(Level.FINEST);
-        handler = new TestLogHandler(MONITOR_EXCEPTION_THROWING_PID);
-        logger.addHandler(handler);
-        return originalLevel;
-    }
-
-    @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);
-
-        VmUpdateListener listener = mock(VmUpdateListener.class);
-        monitor.handleNewVm(listener, VM_PID);
-        
-        // Check listener registered
-        ArgumentCaptor<VmListenerWrapper> captor = ArgumentCaptor.forClass(VmListenerWrapper.class);
-        verify(monitoredVm).addVmListener(captor.capture());
-        VmListenerWrapper wrapper = captor.getValue();
-        assertEquals(listener, wrapper.getVmUpdateListener());
-        
-        // Check pid map
-        assertTrue(monitor.getPidToDataMap().containsKey(VM_PID));
-        assertEquals(wrapper, monitor.getPidToDataMap().get(VM_PID).getSecond());
-    }
-    
-    /*
-     * English locale "Process not found". It must not matter really for this
-     * test. See the next test which verifies this.
-     */
-    @Test
-    public void testNewVMWithProcessNotFoundDoesNotLogWarning() throws Exception {
-        String exceptionMsg = PROCESS_NOT_FOUND;
-        basicProcNotFoundTest(exceptionMsg);
-    }
-    
-    /*
-     * Random exception message as the exact message will be locale dependent.
-     */
-    @Test
-    public void testNewVMWithProcessNotFoundDoesNotLogWarning2() throws Exception {
-        String exceptionMsg = "foo but not bar";
-        basicProcNotFoundTest(exceptionMsg);
-    }
-
-    private void basicProcNotFoundTest(String exceptionMsg) throws Exception {
-        IllegalArgumentException iae = new IllegalArgumentException(exceptionMsg);
-        MonitorException procNotFound = new MonitorException(iae);
-        assertEquals(iae, procNotFound.getCause());
-        
-        VmIdentifier vmID = new VmIdentifier(String.valueOf(MONITOR_EXCEPTION_THROWING_PID));
-        when(host.getMonitoredVm(vmID)).thenThrow(procNotFound);
-        VmUpdateListener listener = mock(VmUpdateListener.class);
-
-        monitor.handleNewVm(listener, MONITOR_EXCEPTION_THROWING_PID);
-        assertFalse(handler.isUnableToAttachLoggedAsWarning());
-        assertTrue(handler.isUnableToAttachLoggedAsFinest());
-        assertFalse(handler.isUnableToAttachLoggedAsWarningUnrelated());
-    }
-
-    @Test
-    public void testNewVMUnrelatedCausedMonitorExceptionLogWarning() throws Exception {
-        MonitorException procNotFound = new MonitorException("unknown");
-        
-        VmIdentifier vmID = new VmIdentifier(String.valueOf(MONITOR_EXCEPTION_THROWING_PID));
-        when(host.getMonitoredVm(vmID)).thenThrow(procNotFound);
-        VmUpdateListener listener = mock(VmUpdateListener.class);
-        
-        monitor.handleNewVm(listener, MONITOR_EXCEPTION_THROWING_PID);
-        assertFalse(handler.isUnableToAttachLoggedAsWarning());
-        assertFalse(handler.isUnableToAttachLoggedAsFinest());
-        assertTrue(handler.isUnableToAttachLoggedAsWarningUnrelated());
-    }
-    
-    @Test
-    public void testStatVMGetMonitoredVmFails() throws MonitorException {
-        final int VM_PID = 1;
-        MonitorException monitorException = new MonitorException();
-        when(host.getMonitoredVm(isA(VmIdentifier.class))).thenThrow(monitorException);
-
-        VmUpdateListener listener = mock(VmUpdateListener.class);
-        monitor.handleNewVm(listener, VM_PID);
-
-        assertFalse(monitor.getPidToDataMap().containsKey(VM_PID));
-    }
-
-    @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);
-
-        VmUpdateListener listener = mock(VmUpdateListener.class);
-        
-        monitor.handleNewVm(listener, VM_PID);
-        monitor.handleStoppedVm(VM_PID);
-
-        // Check listener unregistered
-        ArgumentCaptor<VmListenerWrapper> captor = ArgumentCaptor.forClass(VmListenerWrapper.class);
-        verify(monitoredVm).removeVmListener(captor.capture());
-        VmListenerWrapper wrapper = captor.getValue();
-        assertEquals(listener, wrapper.getVmUpdateListener());
-        
-        assertFalse(monitor.getPidToDataMap().containsKey(VM_PID));
-    }
-
-    @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);
-
-        monitor.handleStoppedVm(VM_PID);
-
-        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(any(VmListener.class));
-
-        VmUpdateListener listener = mock(VmUpdateListener.class);
-        monitor.handleNewVm(listener, VM_PID);
-        monitor.handleStoppedVm(VM_PID);
-
-        verify(monitoredVm).detach();
-    }
-    
-    @Test
-    public void testRemoveAllListeners() throws URISyntaxException, MonitorException {
-        final int VM_PID1 = 1;
-        final int VM_PID2 = 2;
-        
-        VmIdentifier VM_ID1 = new VmIdentifier(String.valueOf(VM_PID1));
-        when(host.getMonitoredVm(VM_ID1)).thenReturn(monitoredVm);
-        
-        MonitoredVm monitoredVm2 = mock(MonitoredVm.class);
-        VmIdentifier VM_ID2 = new VmIdentifier(String.valueOf(VM_PID2));
-        when(host.getMonitoredVm(VM_ID2)).thenReturn(monitoredVm2);
-
-        VmUpdateListener listener1 = mock(VmUpdateListener.class);
-        VmUpdateListener listener2 = mock(VmUpdateListener.class);
-        monitor.handleNewVm(listener1, VM_PID1);
-        monitor.handleNewVm(listener2, VM_PID2);
-        
-        monitor.removeVmListeners();
-        
-        ArgumentCaptor<VmListenerWrapper> captor1 = ArgumentCaptor.forClass(VmListenerWrapper.class);
-        verify(monitoredVm).removeVmListener(captor1.capture());
-        VmListenerWrapper wrapper1 = captor1.getValue();
-        assertEquals(listener1, wrapper1.getVmUpdateListener());
-        
-        ArgumentCaptor<VmListenerWrapper> captor2 = ArgumentCaptor.forClass(VmListenerWrapper.class);
-        verify(monitoredVm2).removeVmListener(captor2.capture());
-        VmListenerWrapper wrapper2 = captor2.getValue();
-        assertEquals(listener2, wrapper2.getVmUpdateListener());
-        
-        assertEquals(0, monitor.getPidToDataMap().size());
-    }
-
-}
-
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/jvm-overview/agent/src/test/java/com/redhat/thermostat/jvm/overview/agent/internal/VmMonitorTest.java	Tue Sep 26 15:10:31 2017 +0200
@@ -0,0 +1,287 @@
+/*
+ * Copyright 2012-2017 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.jvm.overview.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.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 java.util.logging.Level;
+import java.util.logging.Logger;
+
+import com.redhat.thermostat.jvm.overview.agent.VmUpdateListener;
+import com.redhat.thermostat.jvm.overview.agent.internal.TestLogHandler;
+import com.redhat.thermostat.jvm.overview.agent.internal.VmListenerWrapper;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+
+import com.redhat.thermostat.common.portability.ProcessChecker;
+
+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;
+
+public class VmMonitorTest {
+
+    private static final String PROCESS_NOT_FOUND = "Process not found";
+    private static final int MONITOR_EXCEPTION_THROWING_PID = 999;
+    private VmMonitor monitor;
+    private HostIdentifier hostIdentifier;
+    private MonitoredHost host;
+    private MonitoredVm monitoredVm;
+    private ProcessChecker checker;
+    private TestLogHandler handler;
+    private Logger logger;
+    private Level savedLoggingLevel;
+
+    @After
+    public void tearDown() {
+        if (handler != null) {
+            logger.removeHandler(handler);
+            handler = null;
+        }
+        logger.setLevel(savedLoggingLevel);
+    }
+
+    @Before
+    public void setUp() throws Exception {
+        savedLoggingLevel = setupTestLoggerAndReturnOriginalLevel();
+
+        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);
+        
+        checker = mock(ProcessChecker.class);
+        when(checker.exists(isA(Integer.class))).thenReturn(false);
+
+        monitoredVm = mock(MonitoredVm.class);
+
+        monitor = new VmMonitor(checker);
+        monitor.setHost(host);
+    }
+    
+    private Level setupTestLoggerAndReturnOriginalLevel() {
+        logger = Logger.getLogger("com.redhat.thermostat");
+        Level originalLevel = logger.getLevel();
+        logger.setLevel(Level.FINEST);
+        handler = new TestLogHandler(MONITOR_EXCEPTION_THROWING_PID);
+        logger.addHandler(handler);
+        return originalLevel;
+    }
+
+    @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);
+
+        VmUpdateListener listener = mock(VmUpdateListener.class);
+        monitor.handleNewVm(listener, VM_PID);
+        
+        // Check listener registered
+        ArgumentCaptor<VmListenerWrapper> captor = ArgumentCaptor.forClass(VmListenerWrapper.class);
+        verify(monitoredVm).addVmListener(captor.capture());
+        VmListenerWrapper wrapper = captor.getValue();
+        assertEquals(listener, wrapper.getVmUpdateListener());
+        
+        // Check pid map
+        assertTrue(monitor.getPidToDataMap().containsKey(VM_PID));
+        assertEquals(wrapper, monitor.getPidToDataMap().get(VM_PID).getSecond());
+    }
+    
+    /*
+     * English locale "Process not found". It must not matter really for this
+     * test. See the next test which verifies this.
+     */
+    @Test
+    public void testNewVMWithProcessNotFoundDoesNotLogWarning() throws Exception {
+        String exceptionMsg = PROCESS_NOT_FOUND;
+        basicProcNotFoundTest(exceptionMsg);
+    }
+    
+    /*
+     * Random exception message as the exact message will be locale dependent.
+     */
+    @Test
+    public void testNewVMWithProcessNotFoundDoesNotLogWarning2() throws Exception {
+        String exceptionMsg = "foo but not bar";
+        basicProcNotFoundTest(exceptionMsg);
+    }
+
+    private void basicProcNotFoundTest(String exceptionMsg) throws Exception {
+        IllegalArgumentException iae = new IllegalArgumentException(exceptionMsg);
+        MonitorException procNotFound = new MonitorException(iae);
+        assertEquals(iae, procNotFound.getCause());
+        
+        VmIdentifier vmID = new VmIdentifier(String.valueOf(MONITOR_EXCEPTION_THROWING_PID));
+        when(host.getMonitoredVm(vmID)).thenThrow(procNotFound);
+        VmUpdateListener listener = mock(VmUpdateListener.class);
+
+        monitor.handleNewVm(listener, MONITOR_EXCEPTION_THROWING_PID);
+        assertFalse(handler.isUnableToAttachLoggedAsWarning());
+        assertTrue(handler.isUnableToAttachLoggedAsFinest());
+        assertFalse(handler.isUnableToAttachLoggedAsWarningUnrelated());
+    }
+
+    @Test
+    public void testNewVMUnrelatedCausedMonitorExceptionLogWarning() throws Exception {
+        MonitorException procNotFound = new MonitorException("unknown");
+        
+        VmIdentifier vmID = new VmIdentifier(String.valueOf(MONITOR_EXCEPTION_THROWING_PID));
+        when(host.getMonitoredVm(vmID)).thenThrow(procNotFound);
+        VmUpdateListener listener = mock(VmUpdateListener.class);
+        
+        monitor.handleNewVm(listener, MONITOR_EXCEPTION_THROWING_PID);
+        assertFalse(handler.isUnableToAttachLoggedAsWarning());
+        assertFalse(handler.isUnableToAttachLoggedAsFinest());
+        assertTrue(handler.isUnableToAttachLoggedAsWarningUnrelated());
+    }
+    
+    @Test
+    public void testStatVMGetMonitoredVmFails() throws MonitorException {
+        final int VM_PID = 1;
+        MonitorException monitorException = new MonitorException();
+        when(host.getMonitoredVm(isA(VmIdentifier.class))).thenThrow(monitorException);
+
+        VmUpdateListener listener = mock(VmUpdateListener.class);
+        monitor.handleNewVm(listener, VM_PID);
+
+        assertFalse(monitor.getPidToDataMap().containsKey(VM_PID));
+    }
+
+    @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);
+
+        VmUpdateListener listener = mock(VmUpdateListener.class);
+        
+        monitor.handleNewVm(listener, VM_PID);
+        monitor.handleStoppedVm(VM_PID);
+
+        // Check listener unregistered
+        ArgumentCaptor<VmListenerWrapper> captor = ArgumentCaptor.forClass(VmListenerWrapper.class);
+        verify(monitoredVm).removeVmListener(captor.capture());
+        VmListenerWrapper wrapper = captor.getValue();
+        assertEquals(listener, wrapper.getVmUpdateListener());
+        
+        assertFalse(monitor.getPidToDataMap().containsKey(VM_PID));
+    }
+
+    @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);
+
+        monitor.handleStoppedVm(VM_PID);
+
+        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(any(VmListener.class));
+
+        VmUpdateListener listener = mock(VmUpdateListener.class);
+        monitor.handleNewVm(listener, VM_PID);
+        monitor.handleStoppedVm(VM_PID);
+
+        verify(monitoredVm).detach();
+    }
+    
+    @Test
+    public void testRemoveAllListeners() throws URISyntaxException, MonitorException {
+        final int VM_PID1 = 1;
+        final int VM_PID2 = 2;
+        
+        VmIdentifier VM_ID1 = new VmIdentifier(String.valueOf(VM_PID1));
+        when(host.getMonitoredVm(VM_ID1)).thenReturn(monitoredVm);
+        
+        MonitoredVm monitoredVm2 = mock(MonitoredVm.class);
+        VmIdentifier VM_ID2 = new VmIdentifier(String.valueOf(VM_PID2));
+        when(host.getMonitoredVm(VM_ID2)).thenReturn(monitoredVm2);
+
+        VmUpdateListener listener1 = mock(VmUpdateListener.class);
+        VmUpdateListener listener2 = mock(VmUpdateListener.class);
+        monitor.handleNewVm(listener1, VM_PID1);
+        monitor.handleNewVm(listener2, VM_PID2);
+        
+        monitor.removeVmListeners();
+        
+        ArgumentCaptor<VmListenerWrapper> captor1 = ArgumentCaptor.forClass(VmListenerWrapper.class);
+        verify(monitoredVm).removeVmListener(captor1.capture());
+        VmListenerWrapper wrapper1 = captor1.getValue();
+        assertEquals(listener1, wrapper1.getVmUpdateListener());
+        
+        ArgumentCaptor<VmListenerWrapper> captor2 = ArgumentCaptor.forClass(VmListenerWrapper.class);
+        verify(monitoredVm2).removeVmListener(captor2.capture());
+        VmListenerWrapper wrapper2 = captor2.getValue();
+        assertEquals(listener2, wrapper2.getVmUpdateListener());
+        
+        assertEquals(0, monitor.getPidToDataMap().size());
+    }
+
+}
+
--- a/plugins/jvm-overview/agent/src/test/java/com/redhat/thermostat/jvm/overview/agent/internal/VmStatusChangeNotifierTest.java	Tue Sep 26 15:10:28 2017 +0200
+++ b/plugins/jvm-overview/agent/src/test/java/com/redhat/thermostat/jvm/overview/agent/internal/VmStatusChangeNotifierTest.java	Tue Sep 26 15:10:31 2017 +0200
@@ -44,6 +44,8 @@
 
 import com.redhat.thermostat.jvm.overview.agent.VmStatusListener;
 import com.redhat.thermostat.jvm.overview.agent.VmStatusListener.Status;
+import com.redhat.thermostat.jvm.overview.agent.internal.model.VmMapperServiceImpl;
+import org.junit.Before;
 import org.junit.Test;
 import org.osgi.framework.BundleContext;
 
@@ -51,13 +53,20 @@
 
 public class VmStatusChangeNotifierTest {
 
+    private VmMapperServiceImpl vmMapperService;
+
+    @Before
+    public void setUp() {
+        vmMapperService = mock(VmMapperServiceImpl.class);
+    }
+
     @Test
     public void verifyWorksWithoutAnyListeners() {
         final String VM_ID = "vmId";
         final int VM_PID = 2;
         StubBundleContext bundleContext = new StubBundleContext();
 
-        VmStatusChangeNotifier notifier = new VmStatusChangeNotifier(bundleContext);
+        VmStatusChangeNotifier notifier = new VmStatusChangeNotifier(bundleContext, vmMapperService);
         notifier.start();
         notifier.notifyVmStatusChange(Status.VM_STARTED, VM_ID, VM_PID);
 
@@ -73,11 +82,12 @@
         VmStatusListener listener = mock(VmStatusListener.class);
         bundleContext.registerService(VmStatusListener.class, listener, null);
 
-        VmStatusChangeNotifier notifier = new VmStatusChangeNotifier(bundleContext);
+        VmStatusChangeNotifier notifier = new VmStatusChangeNotifier(bundleContext, vmMapperService);
         notifier.start();
         notifier.notifyVmStatusChange(Status.VM_STARTED, VM_ID, VM_PID);
 
         verify(listener).vmStatusChanged(Status.VM_STARTED, VM_ID, VM_PID);
+        verify(vmMapperService).cache(VM_ID, VM_PID);
 
         notifier.notifyVmStatusChange(Status.VM_STOPPED, VM_ID, VM_PID);
 
@@ -90,7 +100,7 @@
         final int VM_PID = 2;
         StubBundleContext bundleContext = new StubBundleContext();
 
-        VmStatusChangeNotifier notifier = new VmStatusChangeNotifier(bundleContext);
+        VmStatusChangeNotifier notifier = new VmStatusChangeNotifier(bundleContext, vmMapperService);
         notifier.start();
         notifier.notifyVmStatusChange(Status.VM_STARTED, VM_ID, VM_PID);
 
@@ -109,7 +119,7 @@
     @Test
     public void canAddListenersWhileFiringEvent() throws InterruptedException {
         StubBundleContext bundleContext = new StubBundleContext();
-        VmStatusChangeNotifier notifier = new VmStatusChangeNotifier(bundleContext);
+        VmStatusChangeNotifier notifier = new VmStatusChangeNotifier(bundleContext, vmMapperService);
         
         // Add > 2 listeners. One of them registers another listener in vmStatusChanged()
         // Thus provoking ConcurrentModificationException.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/jvm-overview/agent/src/test/java/com/redhat/thermostat/jvm/overview/agent/internal/model/VmMapperServiceImplTest.java	Tue Sep 26 15:10:31 2017 +0200
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2012-2017 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.jvm.overview.agent.internal.model;
+
+import com.redhat.thermostat.jvm.overview.agent.model.VmId;
+import com.redhat.thermostat.lang.schema.models.Pid;
+import org.junit.Test;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+public class VmMapperServiceImplTest {
+    @Test
+    public void cacheNewVm() throws Exception {
+        Map<String, Pid> cache = new HashMap<>();
+        VmMapperServiceImpl service = new VmMapperServiceImpl(cache);
+        assertTrue(cache.isEmpty());
+
+        service.cache("42", 4242);
+        assertTrue(cache.containsKey("42"));
+        assertEquals(new Pid(4242), cache.get("42"));
+
+        assertEquals(new Pid(4242), service.getPid("42"));
+        assertEquals(new Pid(4242), service.getPid(new VmId("42")));
+    }
+
+    @Test
+    public void requestingNonExistingVmsReturnsSimplyNullNotExceptions() throws Exception {
+        Map<String, Pid> cache = new HashMap<>();
+        VmMapperServiceImpl service = new VmMapperServiceImpl(cache);
+
+        assertEquals(null, service.getPid("42"));
+        assertEquals(null, service.getPid(new VmId("42")));
+    }
+}