Refactored storing logic of triggers and actions. issue 6

This commit is contained in:
Ziver Koc 2017-01-30 17:16:56 +01:00
parent 8d9354d845
commit a1cb12f6ce
12 changed files with 227 additions and 201 deletions

View file

@ -94,7 +94,7 @@
<target name="build-dependencies"> <target name="build-dependencies">
<mkdir dir="${buildDir}" /> <mkdir dir="${buildDir}" />
<get src="http://ci.koc.se/jenkins/job/Zutil/119/artifact/build/release/Zutil.jar" <get src="http://ci.koc.se/jenkins/job/Zutil/131/artifact/build/release/Zutil.jar"
dest="${libDir}" verbose="true" usetimestamp="true"/> dest="${libDir}" verbose="true" usetimestamp="true"/>
</target> </target>

View file

@ -98,6 +98,53 @@
<!-------------------------------- MODALS --------------------------------> <!-------------------------------- MODALS -------------------------------->
<script>
$(function(){
initDynamicModalForm("trigger");
initDynamicModalForm("action");
});
var dynamicConf = {};
function initDynamicModalForm(name){
// read in all configurations into global variable (to skip naming issues)
dynamicConf[name] = [];
$("#"+name+"-data-conf-template div").each(function(){
dynamicConf[name][$(this).attr("id")] = $(this).html();
});
// Update dynamic inputs
$("#"+name+"Modal select[name=type]").change(function(){
$("#"+name+"Modal #"+name+"-data-conf").html(dynamicConf[name][$(this).val()]);
});
// click event
$("#"+name+"Modal").on('show.bs.modal', function (event) {
var button = $(event.relatedTarget);
var modal = $(this);
modal.find("input[type=text]").val(""); // Reset all inputs
if(button.data("id") >= 0){ // edit
modal.find("input[name=action]").val("modify_"+name);
}
else{ // create
modal.find("input[name=action]").val("create_"+name);
modal.find("input[name=id]").val(-1);
}
// set dynamic form data
modal.find("select[name=type]").val(button.data("type"));
modal.find("select[name=type]").change(); // Update dynamic inputs
$.each(button.attr(), function(fieldName, value) {
if(fieldName.startsWith("data-")) {
fieldName = fieldName.substring(5);
console.log(fieldName, value);
// case insensitive search
modal.find("input").filter(function() {
return this.name.toLowerCase() == fieldName;
}).val(value);
}
});
});
}
</script>
<div class="modal fade" id="triggerModal" tabindex="-1"> <div class="modal fade" id="triggerModal" tabindex="-1">
<div class="modal-dialog"> <div class="modal-dialog">
@ -119,6 +166,10 @@
</select> </select>
</div> </div>
<hr>
<div id="trigger-data-conf">
<!-- Dynamic form -->
</div>
</div> </div>
<div class="modal-footer"> <div class="modal-footer">
<button type="reset" class="btn btn-default" data-dismiss="modal">Cancel</button> <button type="reset" class="btn btn-default" data-dismiss="modal">Cancel</button>
@ -150,6 +201,10 @@
</select> </select>
</div> </div>
<hr>
<div id="action-data-conf">
<!-- Dynamic form -->
</div>
</div> </div>
<div class="modal-footer"> <div class="modal-footer">
<button type="reset" class="btn btn-default" data-dismiss="modal">Cancel</button> <button type="reset" class="btn btn-default" data-dismiss="modal">Cancel</button>
@ -158,4 +213,45 @@
</form> </form>
</div> </div>
</div> </div>
</div>
<div id="trigger-data-conf-template" class="hidden">
{{#triggerConf}}
<div id="{{.clazz.getName()}}">
{{#.params}}
<div class="form-group">
<label class="control-label">{{.getNiceName()}}:</label>
{{#.isTypeString()}}<input type="text" class="form-control" name="{{.getName()}}">{{/#.isTypeString()}}
{{#.isTypeInt()}}<input type="number" class="form-control" name="{{.getName()}}">{{/#.isTypeInt()}}
{{#.isTypeBoolean()}}<input type="checkbox" name="{{.getName()}}">{{/#.isTypeBoolean()}}
{{#.isTypeEnum()}}
<select class="form-control" name="{{.getName()}}">
{{#.getPossibleValues()}}<option>{{.}}</option>{{/.getPossibleValues()}}
</select>
{{/#.isTypeEnum()}}
</div>
{{/.params}}
</div>
{{/triggerConf}}
</div>
<div id="sensor-data-conf-template" class="hidden">
{{#actionConf}}
<div id="{{.clazz.getName()}}">
{{#.params}}
<div class="form-group">
<label class="control-label">{{.getNiceName()}}:</label>
{{#.isTypeString()}}<input type="text" class="form-control" name="{{.getName()}}">{{/#.isTypeString()}}
{{#.isTypeInt()}}<input type="number" class="form-control" name="{{.getName()}}">{{/#.isTypeInt()}}
{{#.isTypeBoolean()}}<input type="checkbox" name="{{.getName()}}">{{/#.isTypeBoolean()}}
{{#.isTypeEnum()}}
<select class="form-control" name="{{.getName()}}">
{{#.getPossibleValues()}}<option>{{.}}</option>{{/.getPossibleValues()}}
</select>
{{/#.isTypeEnum()}}
</div>
{{/.params}}
</div>
{{/actionConf}}
</div> </div>

View file

@ -1,49 +1,14 @@
package se.hal.intf; package se.hal.intf;
import se.hal.struct.TriggerFlow;
import se.hal.struct.dso.ActionDSO;
import zutil.db.DBConnection;
import zutil.db.bean.DBBean;
import java.sql.SQLException;
import java.util.List;
/** /**
* Defines a action that will be executed * Defines a action that will be executed
*/ */
public abstract class HalAction{ public interface HalAction{
private ActionDSO dso;
public static HalAction getAction(DBConnection db, long id) throws SQLException {
ActionDSO dso = DBBean.load(db, ActionDSO.class, id);
dso.getObject().dso = dso;
return dso.getObject();
}
public static List<HalAction> getActions(DBConnection db, TriggerFlow flow) {
// TODO:
return null;
}
public Long getId(){
return (dso!=null ? dso.getId() : null);
}
public void save(DBConnection db) throws SQLException {
if (dso == null)
dso = new ActionDSO();
dso.setObject(this);
dso.save(db);
}
public void delete(DBConnection db) throws SQLException {
dso.delete(db);
}
/** /**
* Executes this specific action * Executes this specific action
*/ */
public abstract void execute(); void execute();
} }

View file

@ -1,58 +1,22 @@
package se.hal.intf; package se.hal.intf;
import se.hal.struct.TriggerFlow;
import se.hal.struct.dso.TriggerDSO;
import zutil.db.DBConnection;
import zutil.db.bean.DBBean;
import java.sql.SQLException;
import java.util.List;
/** /**
* A class that declares a trigger/condition that * A interface that declares a trigger/condition that
* needs to be validated before an action can be run * needs to be validated before an action can be run
*/ */
public abstract class HalTrigger{ public interface HalTrigger{
private TriggerDSO dso;
public static HalTrigger getTrigger(DBConnection db, long id) throws SQLException {
TriggerDSO dso = DBBean.load(db, TriggerDSO.class, id);
dso.getObject().dso = dso;
return dso.getObject();
}
public static List<HalTrigger> getTriggers(DBConnection db, TriggerFlow flow) {
return null;
}
public Long getId(){
return (dso!=null ? dso.getId() : null);
}
public void save(DBConnection db) throws SQLException {
if (dso == null)
dso = new TriggerDSO();
dso.setObject(this);
dso.save(db);
}
public void delete(DBConnection db) throws SQLException {
dso.delete(db);
}
/** /**
* Evaluates if this trigger has passed. If the trigger is * Evaluates if this trigger has passed. If the trigger is
* true then this method will return true until the {@link #reset()} * true then this method will return true until the {@link #reset()}
* method is called. * method is called.
*/ */
public abstract boolean evaluate(); boolean evaluate();
/** /**
* Reset the evaluation to false. * Reset the evaluation to false.
*/ */
public abstract void reset(); void reset();
} }

View file

@ -5,7 +5,9 @@ import se.hal.TriggerManager;
import se.hal.intf.HalAction; import se.hal.intf.HalAction;
import se.hal.intf.HalHttpPage; import se.hal.intf.HalHttpPage;
import se.hal.intf.HalTrigger; import se.hal.intf.HalTrigger;
import se.hal.struct.Action;
import se.hal.struct.ClassConfigurationData; import se.hal.struct.ClassConfigurationData;
import se.hal.struct.Trigger;
import se.hal.struct.TriggerFlow; import se.hal.struct.TriggerFlow;
import zutil.db.DBConnection; import zutil.db.DBConnection;
import zutil.io.file.FileUtil; import zutil.io.file.FileUtil;
@ -46,12 +48,12 @@ public class TriggerHttpPage extends HalHttpPage {
TriggerFlow flow = null; TriggerFlow flow = null;
if (request.containsKey("flow_id")) if (request.containsKey("flow_id"))
flow = TriggerFlow.getTriggerFlow(db, Integer.parseInt(request.get("flow_id"))); flow = TriggerFlow.getTriggerFlow(db, Integer.parseInt(request.get("flow_id")));
HalTrigger trigger = null; Trigger trigger = null;
if (request.containsKey("trigger_id")) if (request.containsKey("trigger_id"))
trigger = HalTrigger.getTrigger(db, Integer.parseInt(request.get("trigger_id"))); trigger = Trigger.getTrigger(db, Integer.parseInt(request.get("trigger_id")));
HalAction action = null; Action action = null;
if (request.containsKey("action_id")) if (request.containsKey("action_id"))
action = HalAction.getAction(db, Integer.parseInt(request.get("action_id"))); action = Action.getAction(db, Integer.parseInt(request.get("action_id")));
switch(request.get("action")) { switch(request.get("action")) {
@ -94,8 +96,8 @@ public class TriggerHttpPage extends HalHttpPage {
Templator tmpl = new Templator(FileUtil.find(TEMPLATE)); Templator tmpl = new Templator(FileUtil.find(TEMPLATE));
tmpl.set("triggerConfigurations", triggerConfigurators); tmpl.set("triggerConf", triggerConfigurators);
tmpl.set("actionConfigurations", actionConfigurators); tmpl.set("actionConf", actionConfigurators);
tmpl.set("availableTriggers", TriggerManager.getInstance().getAvailableTriggers()); tmpl.set("availableTriggers", TriggerManager.getInstance().getAvailableTriggers());
tmpl.set("availableActions", TriggerManager.getInstance().getAvailableActions()); tmpl.set("availableActions", TriggerManager.getInstance().getAvailableActions());
tmpl.set("flows", TriggerFlow.getTriggerFlows(db)); tmpl.set("flows", TriggerFlow.getTriggerFlows(db));

43
src/se/hal/struct/Action.java Executable file
View file

@ -0,0 +1,43 @@
package se.hal.struct;
import se.hal.intf.HalAction;
import se.hal.intf.HalTrigger;
import zutil.db.DBConnection;
import zutil.db.bean.DBBean;
import zutil.db.bean.DBBeanObjectDSO;
import java.sql.SQLException;
import java.util.List;
/**
* Defines a action that will be executed
*/
@DBBean.DBTable(value = "action", superBean = true)
public class Action extends DBBeanObjectDSO<HalAction>{
public static Action getAction(DBConnection db, long id) throws SQLException {
return DBBean.load(db, Action.class, id);
}
public static List<Action> getActions(DBConnection db, TriggerFlow flow) {
// TODO:
return null;
}
public Action() { }
public Action(HalAction action) {
this.setObject(action);
}
/**
* Executes this specific action
*/
public void execute(){
if (getObject() != null)
getObject().execute();
}
}

54
src/se/hal/struct/Trigger.java Executable file
View file

@ -0,0 +1,54 @@
package se.hal.struct;
import se.hal.intf.HalTrigger;
import zutil.db.DBConnection;
import zutil.db.bean.DBBean;
import zutil.db.bean.DBBeanObjectDSO;
import java.sql.SQLException;
import java.util.List;
/**
* A class that declares a trigger/condition that
* needs to be validated before an action can be run
*/
@DBBean.DBTable(value = "trigger", superBean = true)
public class Trigger extends DBBeanObjectDSO<HalTrigger>{
public static Trigger getTrigger(DBConnection db, long id) throws SQLException {
return DBBean.load(db, Trigger.class, id);
}
public static List<Trigger> getTriggers(DBConnection db, TriggerFlow flow) {
// Todo:
return null;
}
public Trigger() { }
public Trigger(HalTrigger trigger) {
this.setObject(trigger);
}
/**
* Evaluates if this trigger has passed. If the trigger is
* true then this method will return true until the {@link #reset()}
* method is called.
*/
public boolean evaluate(){
if (getObject() != null)
return getObject().evaluate();
return false;
}
/**
* Reset the evaluation to false.
*/
public void reset(){
if (getObject() != null)
getObject().reset();
}
}

View file

@ -26,8 +26,8 @@ import java.util.logging.Logger;
public class TriggerFlow extends DBBean { public class TriggerFlow extends DBBean {
private static final Logger logger = LogUtil.getLogger(); private static final Logger logger = LogUtil.getLogger();
private transient List<HalTrigger> triggerList = new ArrayList<>(); private transient List<Trigger> triggerList = new ArrayList<>();
private transient List<HalAction> actionList = new ArrayList<>(); private transient List<Action> actionList = new ArrayList<>();
@ -46,24 +46,24 @@ public class TriggerFlow extends DBBean {
DBConnection db = HalContext.getDB(); DBConnection db = HalContext.getDB();
triggerList.clear(); triggerList.clear();
triggerList = HalTrigger.getTriggers(db, this); triggerList = Trigger.getTriggers(db, this);
actionList.clear(); actionList.clear();
actionList = HalAction.getActions(db, this); actionList = Action.getActions(db, this);
} }
public void addTrigger(HalTrigger trigger) { public void addTrigger(Trigger trigger) {
triggerList.add(trigger); triggerList.add(trigger);
} }
public void removeTrigger(HalTrigger trigger) { public void removeTrigger(Trigger trigger) {
triggerList.remove(trigger); triggerList.remove(trigger);
} }
public void addAction(HalAction action) { public void addAction(Action action) {
actionList.add(action); actionList.add(action);
} }
public void removeAction(HalAction action) { public void removeAction(Action action) {
actionList.remove(action); actionList.remove(action);
} }
@ -73,7 +73,7 @@ public class TriggerFlow extends DBBean {
* Note: this method will not execute any actionList * Note: this method will not execute any actionList
*/ */
public boolean evaluate(){ public boolean evaluate(){
for(HalTrigger trigger : triggerList){ for(Trigger trigger : triggerList){
if (trigger.evaluate()) if (trigger.evaluate())
return true; return true;
} }
@ -84,7 +84,7 @@ public class TriggerFlow extends DBBean {
* Executes the associated actionList in this flow * Executes the associated actionList in this flow
*/ */
public void execute(){ public void execute(){
for(HalAction action : actionList){ for(Action action : actionList){
action.execute(); action.execute();
} }
} }
@ -93,7 +93,7 @@ public class TriggerFlow extends DBBean {
* Resets all trigger evaluations * Resets all trigger evaluations
*/ */
public void reset() { public void reset() {
for(HalTrigger trigger : triggerList){ for(Trigger trigger : triggerList){
trigger.reset(); trigger.reset();
} }
} }

View file

@ -1,14 +0,0 @@
package se.hal.struct.dso;
import se.hal.intf.HalAction;
import zutil.db.bean.DBBean;
/**
* A intermediate class for loading HalAction objects from DB
*/
@DBBean.DBTable(value = "action", superBean = true)
public class ActionDSO extends ObjectDSO<HalAction>{
}

View file

@ -1,72 +0,0 @@
package se.hal.struct.dso;
import se.hal.intf.HalTrigger;
import zutil.db.DBConnection;
import zutil.db.bean.DBBean;
import zutil.log.LogUtil;
import zutil.parser.json.JSONParser;
import zutil.parser.json.JSONWriter;
import zutil.ui.Configurator;
import java.sql.SQLException;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* A intermediate class for loading HalTrigger objects from DB
*/
public abstract class ObjectDSO<T> extends DBBean{
private static final Logger logger = LogUtil.getLogger();
// DB parameters
private String type;
private String config;
// Local parameters
private transient T cachedObj;
@Override
protected void postUpdateAction() {
if (type != null && !type.isEmpty()) {
if (cachedObj == null) {
try {
Class clazz = Class.forName(type);
cachedObj = (T) clazz.newInstance();
} catch (Exception e) {
logger.log(Level.SEVERE, "Unable instantiate class: " + type, e);
}
}
if (config != null && !config.isEmpty()) {
Configurator<T> configurator = new Configurator<>(cachedObj);
configurator.setValues(JSONParser.read(config));
configurator.applyConfiguration();
}
}
}
@Override
public void save(DBConnection db) throws SQLException {
if (cachedObj == null)
this.config = null;
else {
Configurator<T> configurator = new Configurator<>(cachedObj);
this.config = JSONWriter.toString(configurator.getValuesAsNode());
}
super.save(db);
}
public T getObject(){
return cachedObj;
}
public void setObject(T obj){
this.cachedObj = obj;
}
}

View file

@ -1,14 +0,0 @@
package se.hal.struct.dso;
import se.hal.intf.HalTrigger;
import zutil.db.bean.DBBean;
/**
* A intermediate class for loading HalTrigger objects from DB
*/
@DBBean.DBTable(value = "trigger", superBean = true)
public class TriggerDSO extends ObjectDSO<HalTrigger>{
}

View file

@ -3,6 +3,8 @@ package se.hal;
import org.junit.Test; import org.junit.Test;
import se.hal.intf.HalAction; import se.hal.intf.HalAction;
import se.hal.intf.HalTrigger; import se.hal.intf.HalTrigger;
import se.hal.struct.Action;
import se.hal.struct.Trigger;
import se.hal.struct.TriggerFlow; import se.hal.struct.TriggerFlow;
import java.util.Collections; import java.util.Collections;
@ -43,9 +45,9 @@ public class TriggerManagerTest {
registerAvailableTrigger(); registerAvailableTrigger();
TriggerFlow flow = new TriggerFlow(); TriggerFlow flow = new TriggerFlow();
flow.addTrigger(new TestTrigger(true)); flow.addTrigger(new Trigger(new TestTrigger(true)));
TestAction action = new TestAction(); TestAction action = new TestAction();
flow.addAction(action); flow.addAction(new Action(action));
manager.register(flow); manager.register(flow);
manager.evaluateAndExecute(); manager.evaluateAndExecute();
assertEquals(1, action.nrOfExecutions); assertEquals(1, action.nrOfExecutions);
@ -58,9 +60,9 @@ public class TriggerManagerTest {
TriggerFlow flow = new TriggerFlow(); TriggerFlow flow = new TriggerFlow();
TestTrigger trigger = new TestTrigger(true); TestTrigger trigger = new TestTrigger(true);
flow.addTrigger(trigger); flow.addTrigger(new Trigger(trigger));
TestAction action = new TestAction(); TestAction action = new TestAction();
flow.addAction(action); flow.addAction(new Action(action));
manager.register(flow); manager.register(flow);
manager.evaluateAndExecute(); manager.evaluateAndExecute();
@ -75,7 +77,7 @@ public class TriggerManagerTest {
///////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////
private static class TestTrigger extends HalTrigger { private static class TestTrigger implements HalTrigger {
boolean evaluation; boolean evaluation;
TestTrigger(boolean b){ evaluation = b; } TestTrigger(boolean b){ evaluation = b; }
@Override @Override
@ -86,7 +88,7 @@ public class TriggerManagerTest {
} }
private class TestAction extends HalAction { private class TestAction implements HalAction {
int nrOfExecutions; int nrOfExecutions;
@Override @Override
public void execute() { nrOfExecutions++; } public void execute() { nrOfExecutions++; }