From dd1bb751924343091c025e40712a6f277f12c198 Mon Sep 17 00:00:00 2001 From: Ziver Koc Date: Wed, 11 Jan 2017 17:31:00 +0100 Subject: [PATCH] initial implementation of triggers. issue 6 --- src/se/hal/HalServer.java | 9 +- src/se/hal/TriggerManager.java | 90 ++++++++++++++++++ src/se/hal/intf/HalAction.java | 9 ++ src/se/hal/intf/HalTrigger.java | 20 ++++ .../hal/plugin/netscan/NetScanController.java | 12 ++- src/se/hal/plugin/powerchallenge/plugin.json | 4 +- src/se/hal/struct/TriggerFlow.java | 55 +++++++++++ test/se/hal/TriggerManagerTest.java | 95 +++++++++++++++++++ 8 files changed, 283 insertions(+), 11 deletions(-) create mode 100755 src/se/hal/TriggerManager.java create mode 100755 src/se/hal/intf/HalAction.java create mode 100755 src/se/hal/intf/HalTrigger.java create mode 100755 src/se/hal/struct/TriggerFlow.java create mode 100755 test/se/hal/TriggerManagerTest.java diff --git a/src/se/hal/HalServer.java b/src/se/hal/HalServer.java index 8175004b..4182b91f 100755 --- a/src/se/hal/HalServer.java +++ b/src/se/hal/HalServer.java @@ -32,16 +32,17 @@ public class HalServer { // init logging LogUtil.readConfiguration("logging.properties"); + // init DB and other configurations + DBConnection db = HalContext.getDB(); + // init Managers PluginManager pluginManager = new PluginManager("./"); HalContext.initialize(); - ControllerManager.initialize(pluginManager); HalAlertManager.initialize(); + ControllerManager.initialize(pluginManager); + TriggerManager.initialize(pluginManager); - // init DB and other configurations - DBConnection db = HalContext.getDB(); - // Init sensors,events and controllers for(Sensor sensor : Sensor.getLocalSensors(db)){ ControllerManager.getInstance().register(sensor); diff --git a/src/se/hal/TriggerManager.java b/src/se/hal/TriggerManager.java new file mode 100755 index 00000000..61c4a454 --- /dev/null +++ b/src/se/hal/TriggerManager.java @@ -0,0 +1,90 @@ +package se.hal; + +import se.hal.intf.HalAction; +import se.hal.intf.HalTrigger; +import se.hal.struct.TriggerFlow; +import zutil.log.LogUtil; +import zutil.plugin.PluginManager; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.logging.Logger; + + +/** + * Handles all triggers registered on Hal + */ +public class TriggerManager { + private static final Logger logger = LogUtil.getLogger(); + private static TriggerManager instance; + + private ArrayList> availableTriggers = new ArrayList<>(); + private ArrayList> availableActions = new ArrayList<>(); + + private ArrayList triggerFlows = new ArrayList<>(); + + + + public void addAvailableTrigger(Class clazz) { + if ( ! availableTriggers.contains(clazz)) + availableTriggers.add(clazz); + } + public List> getAvailableTriggers() { + return availableTriggers; + } + + public void addAvailableAction(Class clazz) { + if ( ! availableActions.contains(clazz)) + availableActions.add(clazz); + } + public List> getAvailableActions() { + return availableActions; + } + + public void register(TriggerFlow flow){ + if ( ! triggerFlows.contains(flow)) + triggerFlows.add(flow); + } + + + + /** + * Main execution method. + * This method will go through all flows and evaluate them. If the + * evaluation of a trigger returns true then its execute method will be called. + */ + public synchronized void evaluateAndExecute() { + for (int i = 0; i < triggerFlows.size(); i++) { // avoid foreach as triggerFlow can change while we are running + TriggerFlow flow = triggerFlows.get(i); + if (flow.evaluate()) { + logger.fine("Flow "+ flow.getId() +" evaluated true, executing actions"); + flow.execute(); + flow.reset(); + } + } + } + + + + public static void initialize(PluginManager pluginManager){ + TriggerManager manager = new TriggerManager(); + + for (Iterator> it = pluginManager.getClassIterator(HalTrigger.class); + it.hasNext(); ){ + manager.addAvailableTrigger(it.next()); + } + + for (Iterator> it = pluginManager.getClassIterator(HalAction.class); + it.hasNext(); ){ + manager.addAvailableAction(it.next()); + } + + instance = manager; + } + + public static TriggerManager getInstance(){ + return instance; + } + +} diff --git a/src/se/hal/intf/HalAction.java b/src/se/hal/intf/HalAction.java new file mode 100755 index 00000000..6baf64c2 --- /dev/null +++ b/src/se/hal/intf/HalAction.java @@ -0,0 +1,9 @@ +package se.hal.intf; + +/** + * Defines a action that will be executed + */ +public interface HalAction { + + void execute(); +} diff --git a/src/se/hal/intf/HalTrigger.java b/src/se/hal/intf/HalTrigger.java new file mode 100755 index 00000000..d7a0e8d9 --- /dev/null +++ b/src/se/hal/intf/HalTrigger.java @@ -0,0 +1,20 @@ +package se.hal.intf; + +/** + * A interfaces that declares a trigger/condition that + * needs to be validated before an action can be run + */ +public interface HalTrigger { + + /** + * Evaluates if this trigger has passed. If the trigger is + * true then this method will return true until the {@link #reset()} + * method is called. + */ + boolean evaluate(); + + /** + * Reset the evaluation to false. + */ + void reset(); +} diff --git a/src/se/hal/plugin/netscan/NetScanController.java b/src/se/hal/plugin/netscan/NetScanController.java index cee4296f..222d0240 100755 --- a/src/se/hal/plugin/netscan/NetScanController.java +++ b/src/se/hal/plugin/netscan/NetScanController.java @@ -68,14 +68,16 @@ public class NetScanController implements HalEventController, HalAutoScannableCo public void run() { try(MultiCommandExecutor executor = new MultiCommandExecutor();){ for (Map.Entry entry : devices.entrySet()) { + LocalNetworkDevice device = entry.getKey(); + SwitchEventData data = entry.getValue(); if (listener != null) { - //logger.finest("Pinging IP "+entry.getKey().getHost()); - boolean online = InetScanner.isReachable(entry.getKey().getHost(), executor); - if (entry.getValue() == null || entry.getValue().isOn() != online) { + //logger.finest("Pinging IP "+ device.getHost()); + boolean online = InetScanner.isReachable(device.getHost(), executor); + if (data == null || data.isOn() != online) { entry.setValue( new SwitchEventData(online, System.currentTimeMillis())); - logger.fine("IP "+entry.getKey().getHost() +" state has changed to "+ entry.getValue()); - listener.reportReceived(entry.getKey(), entry.getValue()); + logger.fine("IP "+device.getHost() +" state has changed to "+ data); + listener.reportReceived(device, data); } } } diff --git a/src/se/hal/plugin/powerchallenge/plugin.json b/src/se/hal/plugin/powerchallenge/plugin.json index ea3cae8b..760ec2a4 100755 --- a/src/se/hal/plugin/powerchallenge/plugin.json +++ b/src/se/hal/plugin/powerchallenge/plugin.json @@ -5,7 +5,7 @@ {"se.hal.intf.HalHttpPage": "se.hal.page.PCOverviewHttpPage"}, {"se.hal.intf.HalHttpPage": "se.hal.page.PCHeatMapHttpPage"}, - {"se.hal.intf.HalDaemon": "se.hal.deamon.PCDataSynchronizationClient"}, - {"se.hal.intf.HalDaemon": "se.hal.deamon.PCDataSynchronizationDaemon"} + {"se.hal.intf.HalDaemon": "se.hal.daemon.PCDataSynchronizationClient"}, + {"se.hal.intf.HalDaemon": "se.hal.daemon.PCDataSynchronizationDaemon"} ] } \ No newline at end of file diff --git a/src/se/hal/struct/TriggerFlow.java b/src/se/hal/struct/TriggerFlow.java new file mode 100755 index 00000000..aab96a0e --- /dev/null +++ b/src/se/hal/struct/TriggerFlow.java @@ -0,0 +1,55 @@ +package se.hal.struct; + +import se.hal.intf.HalAction; +import se.hal.intf.HalTrigger; +import zutil.db.bean.DBBean; + +import java.util.ArrayList; + +/** + * A class that encapsulates triggers and their actions. + * TODO: Bad class name, should be renamed when we come up with a better one + */ +public class TriggerFlow extends DBBean { + private ArrayList triggers = new ArrayList<>(); + private ArrayList actions = new ArrayList<>(); + + + public void addTrigger(HalTrigger trigger) { + triggers.add(trigger); + } + public void addAction(HalAction action) { + actions.add(action); + } + + /** + * @return true if any one of the triggers evaluate to true, + * false if there are no triggers added. + * Note: this method will not execute any actions + */ + public boolean evaluate(){ + for(HalTrigger trigger : triggers){ + if (trigger.evaluate()) + return true; + } + return false; + } + + /** + * Executes the associated actions in this flow + */ + public void execute(){ + for(HalAction action : actions){ + action.execute(); + } + } + + /** + * Resets all trigger evaluations + */ + public void reset() { + for(HalTrigger trigger : triggers){ + trigger.reset(); + } + } +} diff --git a/test/se/hal/TriggerManagerTest.java b/test/se/hal/TriggerManagerTest.java new file mode 100755 index 00000000..3f2c854c --- /dev/null +++ b/test/se/hal/TriggerManagerTest.java @@ -0,0 +1,95 @@ +package se.hal; + +import org.junit.Test; +import se.hal.intf.HalAction; +import se.hal.intf.HalTrigger; +import se.hal.struct.TriggerFlow; + +import java.util.Collections; + +import static org.junit.Assert.*; + +/** + * + */ +public class TriggerManagerTest { + + private TriggerManager manager = new TriggerManager(); + + + @Test + public void registerAvailableTrigger(){ + assertEquals(Collections.EMPTY_LIST, manager.getAvailableTriggers()); + + manager.addAvailableTrigger(TestTrigger.class); + manager.addAvailableTrigger(TestTrigger.class); + assertEquals(1, manager.getAvailableTriggers().size()); + assertTrue(manager.getAvailableTriggers().contains(TestTrigger.class)); + } + + @Test + public void registerAvailableAction(){ + assertEquals(Collections.EMPTY_LIST, manager.getAvailableActions()); + + manager.addAvailableAction(TestAction.class); + manager.addAvailableAction(TestAction.class); + assertEquals(1, manager.getAvailableActions().size()); + assertTrue(manager.getAvailableActions().contains(TestAction.class)); + } + + + @Test + public void register(){ + registerAvailableTrigger(); + + TriggerFlow flow = new TriggerFlow(); + flow.addTrigger(new TestTrigger(true)); + TestAction action = new TestAction(); + flow.addAction(action); + manager.register(flow); + manager.evaluateAndExecute(); + assertEquals(1, action.nrOfExecutions); + } + + + @Test + public void evaluateAndExecute(){ + registerAvailableTrigger(); + + TriggerFlow flow = new TriggerFlow(); + TestTrigger trigger = new TestTrigger(true); + flow.addTrigger(trigger); + TestAction action = new TestAction(); + flow.addAction(action); + manager.register(flow); + + manager.evaluateAndExecute(); + assertEquals("Action executed nr of times", + 1, action.nrOfExecutions); + + manager.evaluateAndExecute(); + assertEquals("Action executed nr of times", + 1, action.nrOfExecutions); + + } + + ///////////////////////////////////////////////////////////////////////////// + + private static class TestTrigger implements HalTrigger { + boolean evaluation; + TestTrigger(boolean b){ evaluation = b; } + @Override + public boolean evaluate() { return evaluation; } + + @Override + public void reset() { evaluation = false; } + } + + + private class TestAction implements HalAction { + int nrOfExecutions; + @Override + public void execute() { nrOfExecutions++; } + } + +} \ No newline at end of file