changeset 1255:ae64ab3629ca

Host monitor API review-thread: http://icedtea.classpath.org/pipermail/thermostat/2013-August/007969.html reviewed-by: jerboaa
author Mario Torre <neugens.limasoftware@gmail.com>
date Wed, 18 Sep 2013 10:17:35 +0200
parents 789a3be5013e
children 9f8cb76cec1d
files common/core/src/main/java/com/redhat/thermostat/common/ActionNotifier.java storage/core/pom.xml storage/core/src/main/java/com/redhat/thermostat/storage/internal/Activator.java storage/core/src/main/java/com/redhat/thermostat/storage/monitor/HostMonitor.java storage/core/src/main/java/com/redhat/thermostat/storage/monitor/NetworkMonitor.java storage/core/src/main/java/com/redhat/thermostat/storage/monitor/internal/HostMonitorAction.java storage/core/src/main/java/com/redhat/thermostat/storage/monitor/internal/HostMonitorImpl.java storage/core/src/main/java/com/redhat/thermostat/storage/monitor/internal/MonitorAction.java storage/core/src/main/java/com/redhat/thermostat/storage/monitor/internal/NetworkMonitorAction.java storage/core/src/main/java/com/redhat/thermostat/storage/monitor/internal/NetworkMonitorImpl.java storage/core/src/test/java/com/redhat/thermostat/storage/internal/ActivatorTest.java storage/core/src/test/java/com/redhat/thermostat/storage/monitor/internal/HostMonitorActionTest.java storage/core/src/test/java/com/redhat/thermostat/storage/monitor/internal/HostMonitorImplTest.java storage/core/src/test/java/com/redhat/thermostat/storage/monitor/internal/NetworkMonitorActionTest.java storage/core/src/test/java/com/redhat/thermostat/storage/monitor/internal/NetworkMonitorImplTest.java
diffstat 15 files changed, 1153 insertions(+), 20 deletions(-) [+]
line wrap: on
line diff
--- a/common/core/src/main/java/com/redhat/thermostat/common/ActionNotifier.java	Wed Sep 18 10:16:49 2013 +0200
+++ b/common/core/src/main/java/com/redhat/thermostat/common/ActionNotifier.java	Wed Sep 18 10:17:35 2013 +0200
@@ -68,6 +68,10 @@
         fireAction(actionId, null);
     }
 
+    public int listenersCount() {
+        return listeners.size();
+    }
+    
     public void fireAction(T actionId, Object payload) {
         ActionEvent<T> action = new ActionEvent<>(source, actionId);
         action.setPayload(payload);
--- a/storage/core/pom.xml	Wed Sep 18 10:16:49 2013 +0200
+++ b/storage/core/pom.xml	Wed Sep 18 10:17:35 2013 +0200
@@ -69,6 +69,7 @@
               com.redhat.thermostat.storage.model,
               com.redhat.thermostat.storage.dao,
               com.redhat.thermostat.storage.query,
+              com.redhat.thermostat.storage.monitor,
             </Export-Package>
             <!-- TODO: For the thread tab (i.e. thread plug-in) the web server
                  bundle requires model classes provided by said bundle. Since
@@ -84,6 +85,7 @@
               com.redhat.thermostat.storage.internal,
               com.redhat.thermostat.storage.internal.test,
               com.redhat.thermostat.storage.internal.statement,
+              com.redhat.thermostat.storage.monitor.internal,
             </Private-Package>
             <!-- Do not autogenerate uses clauses in Manifests -->
             <_nouses>true</_nouses>
--- a/storage/core/src/main/java/com/redhat/thermostat/storage/internal/Activator.java	Wed Sep 18 10:16:49 2013 +0200
+++ b/storage/core/src/main/java/com/redhat/thermostat/storage/internal/Activator.java	Wed Sep 18 10:17:35 2013 +0200
@@ -38,14 +38,16 @@
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Map;
 import java.util.UUID;
 
 import org.osgi.framework.BundleActivator;
 import org.osgi.framework.BundleContext;
-import org.osgi.framework.ServiceReference;
 import org.osgi.framework.ServiceRegistration;
-import org.osgi.util.tracker.ServiceTracker;
 
+import com.redhat.thermostat.common.ApplicationService;
+import com.redhat.thermostat.common.MultipleServiceTracker;
+import com.redhat.thermostat.common.TimerFactory;
 import com.redhat.thermostat.storage.core.Storage;
 import com.redhat.thermostat.storage.core.WriterID;
 import com.redhat.thermostat.storage.dao.AgentInfoDAO;
@@ -58,12 +60,16 @@
 import com.redhat.thermostat.storage.internal.dao.HostInfoDAOImpl;
 import com.redhat.thermostat.storage.internal.dao.NetworkInterfaceInfoDAOImpl;
 import com.redhat.thermostat.storage.internal.dao.VmInfoDAOImpl;
+import com.redhat.thermostat.storage.monitor.HostMonitor;
+import com.redhat.thermostat.storage.monitor.NetworkMonitor;
+import com.redhat.thermostat.storage.monitor.internal.HostMonitorImpl;
+import com.redhat.thermostat.storage.monitor.internal.NetworkMonitorImpl;
 
 public class Activator implements BundleActivator {
     
     private static final String WRITER_UUID = UUID.randomUUID().toString();
     
-    ServiceTracker<Storage, Storage> tracker;
+    MultipleServiceTracker tracker;
     List<ServiceRegistration<?>> regs;
     
     public Activator() {
@@ -71,44 +77,63 @@
     }
 
     @Override
-    public void start(BundleContext context) throws Exception {
+    public void start(final BundleContext context) throws Exception {
+        Class<?>[] deps = new Class<?>[] {
+                Storage.class,
+                ApplicationService.class,
+        };
+
         // WriterID has to be registered unconditionally (at least not as part
         // of the Storage.class tracker, since that is only registered once
         // storage is connected).
         final WriterID writerID = new WriterIDImpl(WRITER_UUID);
-        ServiceRegistration<?> reg = context.registerService(WriterID.class, writerID, null);
+        final ServiceRegistration<?> reg = context.registerService(WriterID.class, writerID, null);
         regs.add(reg);
         
-        tracker = new ServiceTracker<Storage, Storage>(context, Storage.class, null) {
+        tracker = new MultipleServiceTracker(context, deps, new MultipleServiceTracker.Action() {
+
             @Override
-            public Storage addingService(ServiceReference<Storage> reference) {
-                Storage storage = (Storage) super.addingService(reference);
+            public void dependenciesAvailable(Map<String, Object> services) {
+                
+                Storage storage = (Storage) services.get(Storage.class.getName());          
                 AgentInfoDAO agentInfoDao = new AgentInfoDAOImpl(storage);
+                
                 ServiceRegistration<?> reg = context.registerService(AgentInfoDAO.class.getName(), agentInfoDao, null);
                 regs.add(reg);
+                
                 BackendInfoDAO backendInfoDao = new BackendInfoDAOImpl(storage);
                 reg = context.registerService(BackendInfoDAO.class.getName(), backendInfoDao, null);
                 regs.add(reg);
+                
                 HostInfoDAO hostInfoDao = new HostInfoDAOImpl(storage, agentInfoDao);
                 reg = context.registerService(HostInfoDAO.class.getName(), hostInfoDao, null);
                 regs.add(reg);
+                
                 NetworkInterfaceInfoDAO networkInfoDao = new NetworkInterfaceInfoDAOImpl(storage);
                 reg = context.registerService(NetworkInterfaceInfoDAO.class.getName(), networkInfoDao, null);
                 regs.add(reg);
+                
                 VmInfoDAO vmInfoDao = new VmInfoDAOImpl(storage);
                 reg = context.registerService(VmInfoDAO.class.getName(), vmInfoDao, null);
                 regs.add(reg);
-                return storage;
+            
+                ApplicationService appService = (ApplicationService) services.get(ApplicationService.class.getName());
+                TimerFactory timers = appService.getTimerFactory();
+                NetworkMonitor networkMonitor = new NetworkMonitorImpl(timers, hostInfoDao);
+                reg = context.registerService(NetworkMonitor.class.getName(), networkMonitor, null);
+                regs.add(reg);
+                                
+                HostMonitor hostMonitor = new HostMonitorImpl(timers, vmInfoDao);
+                reg = context.registerService(HostMonitor.class.getName(), hostMonitor, null);
+                regs.add(reg);
             }
-            
+        
             @Override
-            public void removedService(ServiceReference<Storage> reference,
-                    Storage service) {
+            public void dependenciesUnavailable() {
                 unregisterServices();
-                super.removedService(reference, service);
             }
-        };
-        
+        });
+
         tracker.open();
     }
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/storage/core/src/main/java/com/redhat/thermostat/storage/monitor/HostMonitor.java	Wed Sep 18 10:17:35 2013 +0200
@@ -0,0 +1,62 @@
+/*
+ * 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.storage.monitor;
+
+import com.redhat.thermostat.common.ActionListener;
+import com.redhat.thermostat.storage.core.HostRef;
+import com.redhat.thermostat.storage.core.VmRef;
+
+/**
+ * Monitors selected {@link HostRef} for {@link VmRef} lifecycle changes.
+ */
+public interface HostMonitor {
+    
+    public enum Action {
+        VM_ADDED,
+        VM_REMOVED,
+    }
+    
+    /**
+     * Adds this listener to the given {@link HostRef}
+     */
+    void addHostChangeListener(HostRef host, ActionListener<Action> listener);
+    
+    /**
+     * Removes the listener to the given {@link HostRef}
+     */
+    void removeHostChangeListener(HostRef host, ActionListener<Action> listener);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/storage/core/src/main/java/com/redhat/thermostat/storage/monitor/NetworkMonitor.java	Wed Sep 18 10:17:35 2013 +0200
@@ -0,0 +1,54 @@
+/*
+ * 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.storage.monitor;
+
+import com.redhat.thermostat.common.ActionListener;
+import com.redhat.thermostat.storage.core.HostRef;
+
+/**
+ * Monitors the network for addition, removals of new {@link HostRef}.
+ */
+public interface NetworkMonitor {
+
+    public enum Action {
+        HOST_ADDED,
+        HOST_REMOVED,
+    }
+     
+    void addNetworkChangeListener(ActionListener<Action> listener);
+    void removeNetworkChangeListener(ActionListener<Action> listener);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/storage/core/src/main/java/com/redhat/thermostat/storage/monitor/internal/HostMonitorAction.java	Wed Sep 18 10:17:35 2013 +0200
@@ -0,0 +1,85 @@
+/*
+ * 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.storage.monitor.internal;
+
+import java.util.ArrayList;
+import java.util.Collection;
+
+import com.redhat.thermostat.common.ActionNotifier;
+import com.redhat.thermostat.storage.core.HostRef;
+import com.redhat.thermostat.storage.core.VmRef;
+import com.redhat.thermostat.storage.dao.VmInfoDAO;
+import com.redhat.thermostat.storage.model.VmInfo;
+import com.redhat.thermostat.storage.monitor.HostMonitor;
+import com.redhat.thermostat.storage.monitor.HostMonitor.Action;
+
+class HostMonitorAction extends MonitorAction<VmRef, HostMonitor.Action> {
+
+    private VmInfoDAO vmsDao;
+    private HostRef host;
+        
+    public HostMonitorAction(ActionNotifier<Action> notifier, VmInfoDAO vmsDao,
+                             HostRef host)
+    {
+        super(notifier);
+        this.host = host;
+        this.vmsDao = vmsDao;
+    }
+
+    @Override
+    protected Action getAddAction() {
+        return HostMonitor.Action.VM_ADDED;
+    }
+
+    @Override
+    protected Action getRemoveAction() {
+        return HostMonitor.Action.VM_REMOVED;
+    }
+
+    @Override
+    protected Collection<VmRef> getNewReferences() {
+        Collection<VmRef> vms = vmsDao.getVMs(host);
+        Collection<VmRef> livingVMS = new ArrayList<>();
+        for (VmRef vm : vms) {
+            VmInfo vmInfo = vmsDao.getVmInfo(vm);
+            if (vmInfo.isAlive()) {
+                livingVMS.add(vm);
+            }
+        }
+        return livingVMS;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/storage/core/src/main/java/com/redhat/thermostat/storage/monitor/internal/HostMonitorImpl.java	Wed Sep 18 10:17:35 2013 +0200
@@ -0,0 +1,109 @@
+/*
+ * 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.storage.monitor.internal;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.TimeUnit;
+
+import com.redhat.thermostat.common.ActionListener;
+import com.redhat.thermostat.common.ActionNotifier;
+import com.redhat.thermostat.common.Pair;
+import com.redhat.thermostat.common.Timer;
+import com.redhat.thermostat.common.TimerFactory;
+import com.redhat.thermostat.storage.core.HostRef;
+import com.redhat.thermostat.storage.dao.VmInfoDAO;
+import com.redhat.thermostat.storage.monitor.HostMonitor;
+
+public class HostMonitorImpl implements HostMonitor {
+    
+    static final long DELAY = 200;
+    
+    private VmInfoDAO vmDao;
+    private TimerFactory timerFactory;
+    
+    private Map<HostRef, Pair<Timer, ActionNotifier<HostMonitor.Action>>> listeners;
+    
+    public HostMonitorImpl(TimerFactory timerFactory, VmInfoDAO vmDao) {
+        this.vmDao = vmDao;
+        this.timerFactory = timerFactory;
+        listeners = new ConcurrentHashMap<>();
+    }
+    
+    Map<HostRef, Pair<Timer, ActionNotifier<HostMonitor.Action>>> getListeners() {
+        return listeners;
+    }
+    
+    @Override
+    public void addHostChangeListener(HostRef host,
+                                      ActionListener<Action> listener)
+    {
+        Pair<Timer, ActionNotifier<HostMonitor.Action>> payload =
+                listeners.get(host);
+        if (payload == null) {
+            ActionNotifier<Action> notifier = new ActionNotifier<>(this);
+            Timer timer = timerFactory.createTimer();
+
+            timer.setTimeUnit(TimeUnit.MILLISECONDS);
+            timer.setDelay(DELAY);
+            timer.setSchedulingType(Timer.SchedulingType.FIXED_RATE);
+            timer.setAction(new HostMonitorAction(notifier, vmDao, host));
+            timer.start();
+            
+            payload = new Pair<>(timer, notifier);
+            listeners.put(host, payload);
+        }
+        
+        payload.getSecond().addActionListener(listener);
+    }
+
+    @Override
+    public void removeHostChangeListener(HostRef host,
+                                         ActionListener<Action> listener)
+    {
+        Pair<Timer, ActionNotifier<HostMonitor.Action>> payload =
+                listeners.get(host);
+        if (payload != null) {
+            ActionNotifier<HostMonitor.Action> notifier = payload.getSecond();
+            notifier.removeActionListener(listener);
+            if (notifier.listenersCount() == 0) {
+                payload.getFirst().stop();
+                listeners.remove(host);
+            }
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/storage/core/src/main/java/com/redhat/thermostat/storage/monitor/internal/MonitorAction.java	Wed Sep 18 10:17:35 2013 +0200
@@ -0,0 +1,95 @@
+/*
+ * 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.storage.monitor.internal;
+
+import java.util.ArrayList;
+import java.util.Collection;
+
+import com.redhat.thermostat.common.ActionNotifier;
+import com.redhat.thermostat.storage.core.Ref;
+
+abstract class MonitorAction<R extends Ref, A extends Enum<?>> implements Runnable {
+
+    private static final String LOCK = new String("MonitorAction_LOCK");
+
+    private ActionNotifier<A> notifier;
+    Collection<R> references;
+    
+    public MonitorAction(ActionNotifier<A> notifier) {
+        references = new ArrayList<>();
+        this.notifier = notifier;
+    }
+    
+    @Override
+    public void run() {
+        Collection<R> newReferences = getNewReferences();
+        Collection<R> _refs = null;
+        
+        synchronized (LOCK) {
+            _refs = new ArrayList<>(references);
+            references = new ArrayList<>(newReferences);
+        }
+        
+        handleRemovedHosts(_refs, newReferences);
+        handleAddedReferences(_refs, newReferences);
+    }
+    
+    private void handleAddedReferences(Collection<R> currentReference,
+                                       Collection<R> newReference)
+    {
+        Collection<R> copy = new ArrayList<>(newReference);
+        copy.removeAll(currentReference);
+        for (R reference : copy) {
+            notifier.fireAction(getAddAction(), reference);
+        }
+    }
+
+    private void handleRemovedHosts(Collection<R> currentReference,
+                                    Collection<R> newReference)
+    {
+        Collection<R> copy = new ArrayList<>(currentReference);
+        copy.removeAll(newReference);
+        for (R reference : copy) {
+            notifier.fireAction(getRemoveAction(), reference);
+        }
+    }
+    
+    protected abstract A getAddAction();
+    protected abstract A getRemoveAction();
+
+    protected abstract Collection<R> getNewReferences();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/storage/core/src/main/java/com/redhat/thermostat/storage/monitor/internal/NetworkMonitorAction.java	Wed Sep 18 10:17:35 2013 +0200
@@ -0,0 +1,74 @@
+/*
+ * 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.storage.monitor.internal;
+
+import java.util.Collection;
+
+import com.redhat.thermostat.common.ActionNotifier;
+
+import com.redhat.thermostat.storage.core.HostRef;
+import com.redhat.thermostat.storage.dao.HostInfoDAO;
+
+import com.redhat.thermostat.storage.monitor.NetworkMonitor;
+import com.redhat.thermostat.storage.monitor.NetworkMonitor.Action;
+
+class NetworkMonitorAction extends MonitorAction<HostRef, NetworkMonitor.Action> {
+    
+    private HostInfoDAO hostDAO;
+    
+    public NetworkMonitorAction(ActionNotifier<NetworkMonitor.Action> notifier,
+                                HostInfoDAO hostDAO)
+    {
+        super(notifier);
+        this.hostDAO = hostDAO;
+    }
+
+    @Override
+    protected Action getAddAction() {
+        return NetworkMonitor.Action.HOST_ADDED;
+    }
+
+    @Override
+    protected Action getRemoveAction() {
+        return NetworkMonitor.Action.HOST_REMOVED;
+    }
+
+    @Override
+    protected Collection<HostRef> getNewReferences() {
+        return hostDAO.getAliveHosts();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/storage/core/src/main/java/com/redhat/thermostat/storage/monitor/internal/NetworkMonitorImpl.java	Wed Sep 18 10:17:35 2013 +0200
@@ -0,0 +1,82 @@
+/*
+ * 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.storage.monitor.internal;
+
+import java.util.concurrent.TimeUnit;
+
+import com.redhat.thermostat.common.ActionListener;
+import com.redhat.thermostat.common.ActionNotifier;
+import com.redhat.thermostat.common.Timer;
+import com.redhat.thermostat.common.TimerFactory;
+import com.redhat.thermostat.storage.dao.HostInfoDAO;
+import com.redhat.thermostat.storage.monitor.NetworkMonitor;
+
+public class NetworkMonitorImpl implements NetworkMonitor {
+    
+    public static final long DELAY = 1;
+
+    protected final ActionNotifier<NetworkMonitor.Action> notifier;
+    
+    private Timer timer;
+    
+    public NetworkMonitorImpl(TimerFactory timerFactory, HostInfoDAO hostDAO) {
+        
+        notifier = new ActionNotifier<>(this);
+        
+        timer = timerFactory.createTimer();
+        timer.setTimeUnit(TimeUnit.SECONDS);
+        timer.setDelay(DELAY);
+        timer.setSchedulingType(Timer.SchedulingType.FIXED_RATE);
+        timer.setAction(new NetworkMonitorAction(notifier, hostDAO));
+    }
+    
+    @Override
+    public void addNetworkChangeListener(ActionListener<Action> listener) {
+        notifier.addActionListener(listener);
+        if (notifier.listenersCount() == 1) {
+            timer.start();
+        }
+    }
+    
+    @Override
+    public void removeNetworkChangeListener(ActionListener<Action> listener) {
+        notifier.removeActionListener(listener);
+        if (notifier.listenersCount() == 0) {
+            timer.stop(); 
+        }
+    }
+}
--- a/storage/core/src/test/java/com/redhat/thermostat/storage/internal/ActivatorTest.java	Wed Sep 18 10:16:49 2013 +0200
+++ b/storage/core/src/test/java/com/redhat/thermostat/storage/internal/ActivatorTest.java	Wed Sep 18 10:17:35 2013 +0200
@@ -40,9 +40,13 @@
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
 
 import org.junit.Test;
 
+import com.redhat.thermostat.common.ApplicationService;
+import com.redhat.thermostat.common.Timer;
+import com.redhat.thermostat.common.TimerFactory;
 import com.redhat.thermostat.storage.core.Storage;
 import com.redhat.thermostat.storage.core.WriterID;
 import com.redhat.thermostat.storage.dao.AgentInfoDAO;
@@ -55,6 +59,10 @@
 import com.redhat.thermostat.storage.internal.dao.HostInfoDAOImpl;
 import com.redhat.thermostat.storage.internal.dao.NetworkInterfaceInfoDAOImpl;
 import com.redhat.thermostat.storage.internal.dao.VmInfoDAOImpl;
+import com.redhat.thermostat.storage.monitor.HostMonitor;
+import com.redhat.thermostat.storage.monitor.NetworkMonitor;
+import com.redhat.thermostat.storage.monitor.internal.HostMonitorImpl;
+import com.redhat.thermostat.storage.monitor.internal.NetworkMonitorImpl;
 import com.redhat.thermostat.testutils.StubBundleContext;
 
 public class ActivatorTest {
@@ -69,7 +77,7 @@
 
         // WriterID should get registered unconditionally
         assertEquals("At least WriterID service must be registered", 1, context.getAllServices().size());
-        assertEquals(1, context.getServiceListeners().size());
+        assertEquals(2, context.getServiceListeners().size());
 
         activator.stop(context);
         assertEquals(0, context.getAllServices().size());
@@ -80,7 +88,15 @@
     public void verifyActivatorRegistersServices() throws Exception {
         StubBundleContext context = new StubBundleContext();
         Storage storage = mock(Storage.class);
+        
+        ApplicationService appService = mock(ApplicationService.class);
+        TimerFactory timerFactory = mock(TimerFactory.class);
+        when(appService.getTimerFactory()).thenReturn(timerFactory);
+                
+        Timer timer = mock(Timer.class);
+        when(timerFactory.createTimer()).thenReturn(timer);
 
+        context.registerService(ApplicationService.class, appService, null);
         context.registerService(Storage.class, storage, null);
 
         Activator activator = new Activator();
@@ -98,7 +114,7 @@
 
         assertEquals(0, context.getServiceListeners().size());
         
-        assertEquals(1, context.getAllServices().size());
+        assertEquals(2, context.getAllServices().size());
     }
 
     @Test
@@ -106,7 +122,15 @@
         StubBundleContext context = new StubBundleContext();
         Storage storage = mock(Storage.class);
 
+        ApplicationService appService = mock(ApplicationService.class);
+        TimerFactory timerFactory = mock(TimerFactory.class);
+        when(appService.getTimerFactory()).thenReturn(timerFactory);
+                
+        Timer timer = mock(Timer.class);
+        when(timerFactory.createTimer()).thenReturn(timer);
+
         context.registerService(Storage.class, storage, null);
+        context.registerService(ApplicationService.class, appService, null);
 
         Activator activator = new Activator();
 
@@ -122,20 +146,30 @@
         assertFalse(context.isServiceRegistered(WriterID.class.getName(), WriterIDImpl.class));
         
         assertEquals(0, context.getServiceListeners().size());
-        assertEquals(1, context.getAllServices().size());
+        assertEquals(2, context.getAllServices().size());
     }
     
     @Test
     public void verifyActivatorRegistersServicesMultipleTimes() throws Exception {
         StubBundleContext context = new StubBundleContext();
         Storage storage = mock(Storage.class);
+                
+        ApplicationService appService = mock(ApplicationService.class);
+        TimerFactory timerFactory = mock(TimerFactory.class);
+        when(appService.getTimerFactory()).thenReturn(timerFactory);        
+        Timer timer = mock(Timer.class);
+        when(timerFactory.createTimer()).thenReturn(timer);
 
         context.registerService(Storage.class, storage, null);
+        context.registerService(ApplicationService.class, appService, null);
 
         Activator activator = new Activator();
 
         activator.start(context);
 
+        assertTrue(context.isServiceRegistered(NetworkMonitor.class.getName(), NetworkMonitorImpl.class));
+        assertTrue(context.isServiceRegistered(HostMonitor.class.getName(), HostMonitorImpl.class));
+
         assertTrue(context.isServiceRegistered(HostInfoDAO.class.getName(), HostInfoDAOImpl.class));
         assertTrue(context.isServiceRegistered(NetworkInterfaceInfoDAO.class.getName(), NetworkInterfaceInfoDAOImpl.class));
         assertTrue(context.isServiceRegistered(VmInfoDAO.class.getName(), VmInfoDAOImpl.class));
@@ -146,10 +180,13 @@
         activator.stop(context);
         
         assertEquals(0, context.getServiceListeners().size());
-        assertEquals(1, context.getAllServices().size());
+        assertEquals(2, context.getAllServices().size());
         
         activator.start(context);
 
+        assertTrue(context.isServiceRegistered(NetworkMonitor.class.getName(), NetworkMonitorImpl.class));
+        assertTrue(context.isServiceRegistered(HostMonitor.class.getName(), HostMonitorImpl.class));
+        
         assertTrue(context.isServiceRegistered(HostInfoDAO.class.getName(), HostInfoDAOImpl.class));
         assertTrue(context.isServiceRegistered(NetworkInterfaceInfoDAO.class.getName(), NetworkInterfaceInfoDAOImpl.class));
         assertTrue(context.isServiceRegistered(VmInfoDAO.class.getName(), VmInfoDAOImpl.class));
@@ -160,7 +197,8 @@
         activator.stop(context);
 
         assertEquals(0, context.getServiceListeners().size());
-        assertEquals(1, context.getAllServices().size());
+        assertEquals(2, context.getAllServices().size());
+        
     }
 }
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/storage/core/src/test/java/com/redhat/thermostat/storage/monitor/internal/HostMonitorActionTest.java	Wed Sep 18 10:17:35 2013 +0200
@@ -0,0 +1,151 @@
+/*
+ * 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.storage.monitor.internal;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.times;
+
+import java.util.ArrayList;
+import java.util.Collection;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import com.redhat.thermostat.common.ActionNotifier;
+import com.redhat.thermostat.storage.core.HostRef;
+import com.redhat.thermostat.storage.core.VmRef;
+import com.redhat.thermostat.storage.dao.VmInfoDAO;
+import com.redhat.thermostat.storage.model.VmInfo;
+import com.redhat.thermostat.storage.monitor.HostMonitor;
+import com.redhat.thermostat.storage.monitor.HostMonitor.Action;
+
+public class HostMonitorActionTest {
+
+    private VmInfoDAO vmsDAO;
+    
+    private ActionNotifier<HostMonitor.Action> notifier;
+    
+    @SuppressWarnings("unchecked")
+    @Before
+    public void setup() {
+        vmsDAO = mock(VmInfoDAO.class);
+        notifier = mock(ActionNotifier.class);
+    }
+    
+    @Test
+    public void testAddRemoveVMS() {
+
+        HostRef host = new HostRef("01", "01");
+        
+        Collection<VmRef> currentVMs = new ArrayList<>();
+        VmRef a = new VmRef(host, "0", 0, "a");
+        VmRef b = new VmRef(host, "1", 1, "b");
+        VmRef c = new VmRef(host, "2", 2, "c");
+        VmRef d = new VmRef(host, "3", 3, "d");
+        VmRef e = new VmRef(host, "4", 3, "e");
+
+        VmInfo info_a = mock(VmInfo.class);
+        when(info_a.isAlive()).thenReturn(true);
+        
+        VmInfo info_b = mock(VmInfo.class);
+        when(info_b.isAlive()).thenReturn(true);
+        
+        VmInfo info_c = mock(VmInfo.class);
+        when(info_c.isAlive()).thenReturn(true);
+        
+        VmInfo info_d = mock(VmInfo.class);
+        when(info_d.isAlive()).thenReturn(true);
+        
+        VmInfo info_e = mock(VmInfo.class);
+        when(info_e.isAlive()).thenReturn(false);
+        
+        when(vmsDAO.getVmInfo(a)).thenReturn(info_a);
+        when(vmsDAO.getVmInfo(b)).thenReturn(info_b);
+        when(vmsDAO.getVmInfo(c)).thenReturn(info_c);
+        when(vmsDAO.getVmInfo(d)).thenReturn(info_d);
+        when(vmsDAO.getVmInfo(e)).thenReturn(info_e);
+        
+        currentVMs.add(a);
+        currentVMs.add(b);
+        currentVMs.add(c);
+        currentVMs.add(d);
+        currentVMs.add(e);
+
+        when(vmsDAO.getVMs(host)).thenReturn(currentVMs);
+        
+
+        // the first result is to be notified of all those vms
+        HostMonitorAction action = new HostMonitorAction(notifier, vmsDAO, host);
+        action.run();
+
+        verify(notifier).fireAction(Action.VM_ADDED, a);
+        verify(notifier).fireAction(Action.VM_ADDED, b);
+        verify(notifier).fireAction(Action.VM_ADDED, c);
+        verify(notifier).fireAction(Action.VM_ADDED, d);
+
+        verify(notifier,times(0)).fireAction(Action.VM_ADDED, e);
+        verify(notifier, times(0)).fireAction(Action.VM_REMOVED, eq(any(VmRef.class)));
+
+        // now remove one vm from each side
+        currentVMs.remove(b);
+        currentVMs.remove(c);
+
+        action.run();
+
+        verify(notifier).fireAction(Action.VM_REMOVED, b);
+        verify(notifier).fireAction(Action.VM_REMOVED, c);
+        
+        verify(notifier, times(0)).fireAction(Action.VM_ADDED, eq(any(VmRef.class)));
+        
+        when(info_a.isAlive()).thenReturn(false);
+        
+        // not that a process can ever become alive again :)
+        when(info_e.isAlive()).thenReturn(true);
+
+        action.run();
+
+        verify(notifier,times(1)).fireAction(Action.VM_ADDED, e);
+        verify(notifier,times(1)).fireAction(Action.VM_REMOVED, a);
+        
+        verify(notifier, times(0)).fireAction(Action.VM_ADDED, eq(any(VmRef.class)));
+        verify(notifier, times(0)).fireAction(Action.VM_REMOVED, eq(any(VmRef.class)));
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/storage/core/src/test/java/com/redhat/thermostat/storage/monitor/internal/HostMonitorImplTest.java	Wed Sep 18 10:17:35 2013 +0200
@@ -0,0 +1,127 @@
+/*
+ * 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.storage.monitor.internal;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.times;
+
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertEquals;
+
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import com.redhat.thermostat.common.ActionListener;
+import com.redhat.thermostat.common.ActionNotifier;
+import com.redhat.thermostat.common.Pair;
+import com.redhat.thermostat.common.Timer;
+import com.redhat.thermostat.common.TimerFactory;
+import com.redhat.thermostat.storage.core.HostRef;
+import com.redhat.thermostat.storage.dao.VmInfoDAO;
+import com.redhat.thermostat.storage.monitor.HostMonitor;
+
+public class HostMonitorImplTest {
+
+    private VmInfoDAO vmDao;
+    private TimerFactory timerFactory;
+    
+    @Before
+    public void setup() {
+        vmDao = mock(VmInfoDAO.class);
+        timerFactory = mock(TimerFactory.class);
+    }
+    
+    @SuppressWarnings("unchecked")
+    @Test
+    public void test() {
+        ActionListener<HostMonitor.Action> listener1 = mock(ActionListener.class);
+        ActionListener<HostMonitor.Action> listener2 = mock(ActionListener.class);
+
+        Timer timer1 = mock(Timer.class);
+        when(timerFactory.createTimer()).thenReturn(timer1);
+        
+        HostRef host1 = new HostRef("0", "0");
+
+        HostMonitor monitor = new HostMonitorImpl(timerFactory, vmDao);
+        Map<HostRef, Pair<Timer, ActionNotifier<HostMonitor.Action>>> listeners =
+                ((HostMonitorImpl) monitor).getListeners();
+        assertTrue(listeners.isEmpty());
+        
+        monitor.addHostChangeListener(host1, listener1);
+        
+        assertEquals(1, listeners.size());
+
+        verify(timer1, times(1)).setTimeUnit(TimeUnit.MILLISECONDS);
+        verify(timer1, times(1)).setDelay(HostMonitorImpl.DELAY);
+        verify(timer1, times(1)).setSchedulingType(Timer.SchedulingType.FIXED_RATE);
+        verify(timer1, times(1)).start();
+
+        verify(timer1).setAction(any(HostMonitorAction.class));
+        
+        monitor.addHostChangeListener(host1, listener2);
+        
+        assertEquals(1, listeners.size());
+
+        verify(timer1, times(1)).setTimeUnit(TimeUnit.MILLISECONDS);
+        verify(timer1, times(1)).setDelay(HostMonitorImpl.DELAY);
+        verify(timer1, times(1)).start();
+        verify(timer1, times(1)).setSchedulingType(Timer.SchedulingType.FIXED_RATE);
+        
+        verify(timer1).setAction(any(HostMonitorAction.class));
+        
+        monitor.removeHostChangeListener(host1, listener1);
+        verify(timer1, times(0)).stop();
+        
+        assertEquals(1, listeners.size());
+        monitor.removeHostChangeListener(host1, listener2);
+
+        assertTrue(listeners.isEmpty());
+        verify(timer1, times(1)).stop();
+        
+        HostRef host2 = new HostRef("1", "1");
+
+        monitor.addHostChangeListener(host1, listener1);
+        monitor.addHostChangeListener(host2, listener2);
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/storage/core/src/test/java/com/redhat/thermostat/storage/monitor/internal/NetworkMonitorActionTest.java	Wed Sep 18 10:17:35 2013 +0200
@@ -0,0 +1,125 @@
+/*
+ * 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.storage.monitor.internal;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import java.util.ArrayList;
+import java.util.Collection;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import com.redhat.thermostat.common.ActionNotifier;
+import com.redhat.thermostat.storage.core.HostRef;
+import com.redhat.thermostat.storage.dao.HostInfoDAO;
+import com.redhat.thermostat.storage.monitor.NetworkMonitor;
+import com.redhat.thermostat.storage.monitor.NetworkMonitor.Action;
+
+public class NetworkMonitorActionTest {
+
+    private HostInfoDAO hostDAO;
+    
+    private ActionNotifier<NetworkMonitor.Action> notifier;
+    
+    @SuppressWarnings("unchecked")
+    @Before
+    public void setup() {
+        hostDAO = mock(HostInfoDAO.class);
+        notifier = mock(ActionNotifier.class);
+    }
+    
+    @Test
+    public void testAddRemoveHost() {
+        
+        Collection<HostRef> currentHosts = new ArrayList<>();
+        HostRef a = new HostRef("0", "a");
+        HostRef b = new HostRef("1", "b");
+        HostRef c = new HostRef("2", "c");
+        HostRef d = new HostRef("3", "d");
+        
+        currentHosts.add(a);
+        currentHosts.add(b);
+        currentHosts.add(c);
+        currentHosts.add(d);
+        
+        when(hostDAO.getAliveHosts()).thenReturn(currentHosts);
+        
+        // the first result is to be notified of all those hosts
+        NetworkMonitorAction action = new NetworkMonitorAction(notifier, hostDAO);
+        action.run();
+        
+        verify(notifier).fireAction(Action.HOST_ADDED, a);
+        verify(notifier).fireAction(Action.HOST_ADDED, b);
+        verify(notifier).fireAction(Action.HOST_ADDED, c);
+        verify(notifier).fireAction(Action.HOST_ADDED, d);
+        
+        verify(notifier, times(0)).fireAction(Action.HOST_REMOVED, eq(any(HostRef.class)));
+
+        // now remove a from the series, add e
+        HostRef e = new HostRef("4", "e");
+        currentHosts.add(e);
+        currentHosts.remove(a);
+        
+        action.run();
+        
+        verify(notifier).fireAction(Action.HOST_REMOVED, a);
+        verify(notifier).fireAction(Action.HOST_ADDED, e);
+        
+        // now add f from the series, no host removal
+        HostRef f = new HostRef("5", "f");
+        currentHosts.add(f);
+
+        action.run();
+        
+        verify(notifier).fireAction(Action.HOST_ADDED, f);
+        verify(notifier, times(0)).fireAction(Action.HOST_REMOVED, eq(any(HostRef.class)));
+        
+        // now only remove f from the series, no other changes
+        currentHosts.remove(f);
+        
+        action.run();
+
+        verify(notifier).fireAction(Action.HOST_REMOVED, f);
+        verify(notifier, times(0)).fireAction(Action.HOST_ADDED, eq(any(HostRef.class)));
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/storage/core/src/test/java/com/redhat/thermostat/storage/monitor/internal/NetworkMonitorImplTest.java	Wed Sep 18 10:17:35 2013 +0200
@@ -0,0 +1,100 @@
+/*
+ * 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.storage.monitor.internal;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import static org.mockito.Mockito.times;
+
+import java.util.concurrent.TimeUnit;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import com.redhat.thermostat.common.ActionListener;
+import com.redhat.thermostat.common.Timer;
+import com.redhat.thermostat.common.TimerFactory;
+import com.redhat.thermostat.storage.dao.HostInfoDAO;
+import com.redhat.thermostat.storage.monitor.NetworkMonitor;
+import com.redhat.thermostat.storage.monitor.NetworkMonitor.Action;
+
+public class NetworkMonitorImplTest {
+
+    private HostInfoDAO hostDao;
+    private TimerFactory timerFactory;
+    
+    @Before
+    public void setup() {
+        hostDao = mock(HostInfoDAO.class);
+        timerFactory = mock(TimerFactory.class);
+    }
+    
+    @SuppressWarnings("unchecked")
+    @Test
+    public void test() {
+        
+        ActionListener<Action> listener1 = mock(ActionListener.class);
+        ActionListener<Action> listener2 = mock(ActionListener.class);
+
+        Timer timer = mock(Timer.class);
+        when(timerFactory.createTimer()).thenReturn(timer);
+        
+        NetworkMonitor monitor = new NetworkMonitorImpl(timerFactory, hostDao);
+        monitor.addNetworkChangeListener(listener1);
+        
+        verify(timer).setTimeUnit(TimeUnit.SECONDS);
+        verify(timer).setDelay(NetworkMonitorImpl.DELAY);
+        verify(timer).setSchedulingType(Timer.SchedulingType.FIXED_RATE);
+                
+        verify(timer).start();
+        verify(timer, times(0)).stop();
+
+        monitor.addNetworkChangeListener(listener2);
+
+        verify(timer, times(1)).start();
+        verify(timer, times(0)).stop();
+
+        monitor.removeNetworkChangeListener(listener1);
+        verify(timer, times(0)).stop();
+        verify(timer, times(1)).start();
+
+        monitor.removeNetworkChangeListener(listener2);
+        verify(timer).stop();
+    }
+
+}