From 4aaa2c158e3149fd6acaa4cd0d623894b27b9bbb Mon Sep 17 00:00:00 2001 From: Barachia Date: Fri, 10 Jul 2020 11:19:47 +0200 Subject: [PATCH] Updated Flipper to use GraalJS (ECMAScript 2020), added FlipperEnvironments, updated Dynamic example with FlipperEnvironment, added SLF4J logging to Flipper. --- pom.xml | 21 +- .../java/hmi/flipper2/FlipperException.java | 11 +- src/main/java/hmi/flipper2/Is.java | 4 +- src/main/java/hmi/flipper2/Main.java | 2 +- src/main/java/hmi/flipper2/Template.java | 6 +- .../java/hmi/flipper2/TemplateController.java | 14 +- src/main/java/hmi/flipper2/TemplateFile.java | 6 +- src/main/java/hmi/flipper2/TestScenarios.java | 6 +- .../java/hmi/flipper2/dataflow/DataFlow.java | 2 +- .../hmi/flipper2/debugger/CpuMemoryUsage.java | 4 +- .../flipper2/debugger/FlipperDebugger.java | 14 +- .../hmi/flipper2/effect/TemplateEffect.java | 6 +- .../environment/BaseFlipperEnvironment.java | 113 ++++++++ .../environment/FlipperEnvironmentBus.java | 260 ++++++++++++++++++ .../FlipperEnvironmentMessageJSON.java | 26 ++ .../environment/IFlipperEnvironment.java | 20 ++ .../java/hmi/flipper2/example/Dynamic.java | 51 +++- .../hmi/flipper2/example/PersonDbExample.java | 6 +- .../hmi/flipper2/javascript/JsEngine.java | 6 +- .../hmi/flipper2/javascript/JsExpression.java | 4 +- .../flipper2/launcher/FlipperLauncher.java | 4 +- .../launcher/FlipperLauncherThread.java | 4 + .../java/hmi/flipper2/postgres/Database.java | 6 +- .../java/hmi/flipper2/sax/SimpleHandler.java | 4 +- .../java/hmi/flipper2/value/IsJavaValue.java | 4 +- src/main/resources/example/Dynamic.xml | 133 +++++++++ src/main/resources/example/Environment.xml | 257 +++++++++++++++++ src/main/resources/example/Flipper2Count.xml | 2 +- src/main/resources/flipper.properties | 20 +- 29 files changed, 957 insertions(+), 59 deletions(-) create mode 100644 src/main/java/hmi/flipper2/environment/BaseFlipperEnvironment.java create mode 100644 src/main/java/hmi/flipper2/environment/FlipperEnvironmentBus.java create mode 100644 src/main/java/hmi/flipper2/environment/FlipperEnvironmentMessageJSON.java create mode 100644 src/main/java/hmi/flipper2/environment/IFlipperEnvironment.java create mode 100644 src/main/resources/example/Dynamic.xml create mode 100644 src/main/resources/example/Environment.xml diff --git a/pom.xml b/pom.xml index 64d3165..881733a 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ 4.0.0 hmi.flipper2.0 flipper2.0 - 0.2 + 1.0 jar ${project.groupId}:${project.artifactId} @@ -59,7 +59,7 @@ org.hamcrest - hamcrest-core + hamcrest 2.2 test @@ -73,16 +73,31 @@ javax.json 1.1.4 + + com.fasterxml.jackson.core + jackson-databind + 2.11.1 + org.postgresql postgresql - 42.2.5 + 42.2.14 org.slf4j slf4j-api 1.7.30 + + org.graalvm.js + js-scriptengine + 20.1.0 + + + org.graalvm.js + js + 20.1.0 + UTF-8 diff --git a/src/main/java/hmi/flipper2/FlipperException.java b/src/main/java/hmi/flipper2/FlipperException.java index b0b7a21..5531706 100644 --- a/src/main/java/hmi/flipper2/FlipperException.java +++ b/src/main/java/hmi/flipper2/FlipperException.java @@ -28,6 +28,8 @@ import org.xml.sax.SAXException; +import static hmi.flipper2.TemplateController.logger; + public class FlipperException extends Exception { /** @@ -65,6 +67,7 @@ public FlipperException(Exception ex) { public FlipperException(Exception ex, String extra) { _init(ex); this.extra = extra; + logger.error(this.extra); } public FlipperException(SAXException ex) { @@ -97,12 +100,12 @@ public void registerCurrentTemplate(String current_tf, String current_id, String public static void handle(FlipperException e) { if ( e.extra != null ) - System.err.println(e.extra); + logger.error(e.extra); if ( e.currentInfo != null ) - System.err.println(e.currentInfo); - System.err.println("!Caught Exception: "+e.text); + logger.error(e.currentInfo); + logger.error("!Caught Exception: "+e.text); if (e.stack != null ) - System.err.println("!Stack: \n"+e.stack); + logger.error("!Stack: \n"+e.stack); } } diff --git a/src/main/java/hmi/flipper2/Is.java b/src/main/java/hmi/flipper2/Is.java index f363bdc..e711f7a 100644 --- a/src/main/java/hmi/flipper2/Is.java +++ b/src/main/java/hmi/flipper2/Is.java @@ -27,6 +27,8 @@ import hmi.flipper2.javascript.JsExpression; import hmi.flipper2.postgres.Database; +import static hmi.flipper2.TemplateController.logger; + public class Is extends JsEngine { private Database db; @@ -75,7 +77,7 @@ public void rollback() throws FlipperException { for (Map.Entry entry : this.is_tf_table.entrySet()) { if (entry.getValue().is_updated) { entry.getValue().is_updated = false; - System.out.println("INCOMPLETE:Is:rollback: should re-read is after db rollback"); + logger.warn("INCOMPLETE:Is:rollback: should re-read is after db rollback"); } } } diff --git a/src/main/java/hmi/flipper2/Main.java b/src/main/java/hmi/flipper2/Main.java index fc107f7..3af8054 100644 --- a/src/main/java/hmi/flipper2/Main.java +++ b/src/main/java/hmi/flipper2/Main.java @@ -55,7 +55,7 @@ public static void main(String[] args) { int count = 0; boolean changed = true; while( changed && (count < maxcount) ) { - System.out.println("\nIS:\n---\n"+tc.getIs("is")+"\n"); + logger.debug("\nIS:\n---\n"+tc.getIs("is")+"\n"); changed = tc.checkTemplates(); count++; } diff --git a/src/main/java/hmi/flipper2/Template.java b/src/main/java/hmi/flipper2/Template.java index 3a1e3fd..4b0ec1e 100644 --- a/src/main/java/hmi/flipper2/Template.java +++ b/src/main/java/hmi/flipper2/Template.java @@ -46,6 +46,8 @@ import hmi.flipper2.value.JavaValueList; import hmi.flipper2.value.PersistentJavaValue; +import static hmi.flipper2.TemplateController.logger; + public class Template extends FlipperObject { public TemplateFile tf; @@ -299,7 +301,7 @@ public Set flowIn() { for(EffectList l : listOfEffectList) res.addAll(l.flowIn()); this.dfin = res; - System.out.println("TEMPLATE-IN["+id()+"]="+dfin); + logger.debug("TEMPLATE-IN["+id()+"]="+dfin); return res; } @@ -311,7 +313,7 @@ public Set flowOut() { for (EffectList l : listOfEffectList) res.addAll(l.flowOut()); this.dfout = res; - System.out.println("TEMPLATE-OUT["+id()+"]="+dfout); + logger.debug("TEMPLATE-OUT["+id()+"]="+dfout); return res; } diff --git a/src/main/java/hmi/flipper2/TemplateController.java b/src/main/java/hmi/flipper2/TemplateController.java index 221ea8f..7521644 100644 --- a/src/main/java/hmi/flipper2/TemplateController.java +++ b/src/main/java/hmi/flipper2/TemplateController.java @@ -36,6 +36,8 @@ import hmi.flipper2.effect.EffectList; import hmi.flipper2.postgres.Database; import hmi.flipper2.sax.SimpleSAXParser; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * This class is the main Flipper interface class. You can create/modify/destroy Flipper Templatecontollers using this class. @@ -47,6 +49,8 @@ */ public class TemplateController { + + public static Logger logger = LoggerFactory.getLogger(TemplateController.class.getName()); /** * This method creates a new TemplateController. @@ -103,8 +107,6 @@ public static TemplateController create(String name, String descr, Database db, * The name of the existing TemplateController in the Database. * @param db * The Database the TemplateController is stored. When db == null the method does nothing. - * @param jslibs - * String array of additional js libs to preload. * @exception FlipperException * On all errors. */ @@ -202,7 +204,7 @@ public void setDebugger(FlipperDebugger fd) { public void setDebugger(FlipperDebugger fd, boolean onoff) { if ( !Config.debugging && fd!=null ) - System.err.println("WARNING: Setting FlipperDebugger with Config.debugging=false"); + logger.error("WARNING: Setting FlipperDebugger with Config.debugging=false"); this.persistent_dbg = fd; switchDebugger(onoff); } @@ -324,9 +326,15 @@ public boolean checkTemplates(String templateFilter) throws FlipperException { this.dbg.start_CheckTemplates(this.name, null); } for (Template template : filterTemplates(this.base_templates, templateFilter) ) { + long startTime = System.nanoTime(); if ( Config.debugging && this.dbg != null ) this.dbg.start_CheckTemplate(template.id(), null); changed = checkPreconditions(template, Behaviour.IMMEDIATE_EFFECT) || changed; + long endTime = System.nanoTime(); + long duration = (endTime - startTime); //divide by 1000000 to get milliseconds. + if(duration/1000000>50){ + logger.debug("Duration Template, ID: " + template.id() + " and duration: " + duration/1000000); + } // while ( this.conditional_stack != null ) { // Template toCheck = this.conditional_stack; // this.conditional_stack = this.conditional_stack.pop(); diff --git a/src/main/java/hmi/flipper2/TemplateFile.java b/src/main/java/hmi/flipper2/TemplateFile.java index efb4311..8764be3 100644 --- a/src/main/java/hmi/flipper2/TemplateFile.java +++ b/src/main/java/hmi/flipper2/TemplateFile.java @@ -34,6 +34,8 @@ import hmi.flipper2.sax.SimpleElement; import hmi.flipper2.sax.SimpleSAXParser; +import static hmi.flipper2.TemplateController.logger; + public class TemplateFile extends FlipperObject { public int tfid = -1; @@ -184,7 +186,7 @@ public Set flowIn() { res.addAll(t.flowIn()); this.dfin = res; this.dfin_tf = is_var2templatefile(res); - System.out.println("TF-IN["+id()+"]="+dfin_tf); + logger.debug("TF-IN["+id()+"]="+dfin_tf); return res; } @@ -196,7 +198,7 @@ public Set flowOut() { res.addAll(t.flowOut()); this.dfout = res; this.dfout_tf = is_var2templatefile(res); - System.out.println("TF-OUT["+id()+"]="+dfout_tf); + logger.debug("TF-OUT["+id()+"]="+dfout_tf); return res; } } diff --git a/src/main/java/hmi/flipper2/TestScenarios.java b/src/main/java/hmi/flipper2/TestScenarios.java index 012e48e..da9a7b1 100644 --- a/src/main/java/hmi/flipper2/TestScenarios.java +++ b/src/main/java/hmi/flipper2/TestScenarios.java @@ -20,6 +20,8 @@ import hmi.flipper2.postgres.Database; +import static hmi.flipper2.TemplateController.logger; + public class TestScenarios { public static void main(String[] args) { @@ -38,9 +40,9 @@ public static void openCheckXCloseDestroy() { // for(int i=0; i<5; i++) { tc = new TemplateController(scenario, db); - // System.out.println("\nBEFORE IS:\n---\n"+tc.getIs("is")+"\n"); + logger.debug("\nBEFORE IS:\n---\n"+tc.getIs("is")+"\n"); tc.checkTemplates(); - System.out.println("\nIS:\n---\n"+tc.getIs("is")+"\n"); + logger.debug("\nIS:\n---\n"+tc.getIs("is")+"\n"); tc.close(); } // TemplateController.destroy(scenario, db); diff --git a/src/main/java/hmi/flipper2/dataflow/DataFlow.java b/src/main/java/hmi/flipper2/dataflow/DataFlow.java index d2395e4..f6e8638 100644 --- a/src/main/java/hmi/flipper2/dataflow/DataFlow.java +++ b/src/main/java/hmi/flipper2/dataflow/DataFlow.java @@ -74,7 +74,7 @@ else if ( r == EMPTY ) // public static void main(String[] args) { // Set res = extractRefs("is.a \"xis.\" en is.y.v._z is.x"); -// System.out.println("RES:" + res); +// logger.debug("RES:" + res); // } } diff --git a/src/main/java/hmi/flipper2/debugger/CpuMemoryUsage.java b/src/main/java/hmi/flipper2/debugger/CpuMemoryUsage.java index 258ad07..75d1bd5 100644 --- a/src/main/java/hmi/flipper2/debugger/CpuMemoryUsage.java +++ b/src/main/java/hmi/flipper2/debugger/CpuMemoryUsage.java @@ -28,6 +28,8 @@ import hmi.flipper2.TemplateController; +import static hmi.flipper2.TemplateController.logger; + public class CpuMemoryUsage { enum Channel { @@ -91,7 +93,7 @@ public double getProcessCpuLoad() { // returns a percentage value with 1 decimal point precision return ((int) (value * 1000) / 10.0); } catch (Exception e) { - System.out.println("EXCEPTION: " + e); + logger.error("EXCEPTION: " + e); return 0.0; } } diff --git a/src/main/java/hmi/flipper2/debugger/FlipperDebugger.java b/src/main/java/hmi/flipper2/debugger/FlipperDebugger.java index 08b8283..e4d33a1 100644 --- a/src/main/java/hmi/flipper2/debugger/FlipperDebugger.java +++ b/src/main/java/hmi/flipper2/debugger/FlipperDebugger.java @@ -20,6 +20,8 @@ import hmi.flipper2.TemplateController; +import static hmi.flipper2.TemplateController.logger; + public class FlipperDebugger { enum Kind { @@ -38,14 +40,14 @@ public void handle_state(FlipperState s) { boolean verbose = false; if ( verbose ) - System.out.println("#"+s.action.name() + "\t" + s.id + "\t"+pt(s.duration())); + logger.info("#"+s.action.name() + "\t" + s.id + "\t"+pt(s.duration())); switch (s.action) { case CheckTemplates: { - System.out.println("["); + logger.info("["); for (Action a : Action.values()) { - System.out.println("\t+ Total Time: "+ String.format("%1$14s", a.name()) + " = " + pt(this.totalTimes[a.ordinal()])); + logger.info("\t+ Total Time: "+ String.format("%1$14s", a.name()) + " = " + pt(this.totalTimes[a.ordinal()])); } - System.out.println("]"); + logger.info("]"); } break; case CheckTemplate: @@ -73,12 +75,12 @@ private void handle_startstop(Kind kind, Action action, String other_action, Str state.startTime = System.nanoTime(); state.startArg = arg; if ( verbose ) - System.out.println(kind.name() + "\t" + String.format("%1$14s", action.name()) + "\t" + id); + logger.info(kind.name() + "\t" + String.format("%1$14s", action.name()) + "\t" + id); } else { state.stopTime = System.nanoTime(); state.stopArg = arg; if ( verbose ) - System.out.println(kind.name() + "\t" + String.format("%1$14s", action.name()) + "\t" + id + "\t("+pt(state.duration())+")"); + logger.info(kind.name() + "\t" + String.format("%1$14s", action.name()) + "\t" + id + "\t("+pt(state.duration())+")"); this.totalTimes[action.ordinal()] += state.duration(); handle_state(state); state = state.pop(); diff --git a/src/main/java/hmi/flipper2/effect/TemplateEffect.java b/src/main/java/hmi/flipper2/effect/TemplateEffect.java index c57157d..574be90 100644 --- a/src/main/java/hmi/flipper2/effect/TemplateEffect.java +++ b/src/main/java/hmi/flipper2/effect/TemplateEffect.java @@ -24,6 +24,8 @@ import hmi.flipper2.Is; import hmi.flipper2.dataflow.DataFlow; +import static hmi.flipper2.TemplateController.logger; + public class TemplateEffect extends Effect { private String regexpr; @@ -34,8 +36,8 @@ public TemplateEffect(String id, String regexpr, String isregexpr) throws Flippe throw new FlipperException("TemplateEffect cannot have both regexpr and isregexpr"); this.regexpr = regexpr; this.isregexpr = isregexpr; - // System.out.println("regexpr="+this.regexpr); - // System.out.println("isregexpr="+this.isregexpr); + logger.debug("regexpr="+this.regexpr); + logger.debug("isregexpr="+this.isregexpr); } @Override diff --git a/src/main/java/hmi/flipper2/environment/BaseFlipperEnvironment.java b/src/main/java/hmi/flipper2/environment/BaseFlipperEnvironment.java new file mode 100644 index 0000000..261a99d --- /dev/null +++ b/src/main/java/hmi/flipper2/environment/BaseFlipperEnvironment.java @@ -0,0 +1,113 @@ +package hmi.flipper2.environment; + +import com.fasterxml.jackson.databind.JsonNode; +import org.slf4j.LoggerFactory; + +import java.sql.Connection; +import java.util.Iterator; +import java.util.Properties; +import java.util.UUID; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.LinkedBlockingQueue; + + +public abstract class BaseFlipperEnvironment implements IFlipperEnvironment { + + protected static org.slf4j.Logger logger = LoggerFactory.getLogger(BaseFlipperEnvironment.class.getName()); + + private String id; + protected BlockingQueue outQueue = null; + + public BaseFlipperEnvironment() { + outQueue = new LinkedBlockingQueue<>(256); + } + + @Override + public String getId() { + return id; + } + + @Override + public void setId(String id) { + this.id = id; + } + + @Override + public boolean hasMessage() { + return !outQueue.isEmpty(); + } + + @Override + public FlipperEnvironmentMessageJSON getMessage() throws InterruptedException { + FlipperEnvironmentMessageJSON message = outQueue.take(); + return message; + } + + protected FlipperEnvironmentMessageJSON buildResponse(FlipperEnvironmentMessageJSON responseTo, JsonNode params) { + FlipperEnvironmentMessageJSON msg = new FlipperEnvironmentMessageJSON(responseTo.cmd, responseTo.environment, responseTo.msgId, params); + return msg; + } + + protected void enqueueMessage(FlipperEnvironmentMessageJSON msg) { + if (outQueue.remainingCapacity() < 1) { + outQueue.poll(); + logger.warn("OutQueue is full, dropping oldest message."); + } + outQueue.add(msg); + } + + protected void enqueueMessage(JsonNode params, String cmd, String msgId) { + FlipperEnvironmentMessageJSON msg = new FlipperEnvironmentMessageJSON(cmd, this.getId(), msgId, params); + enqueueMessage(msg); + } + + protected String enqueueMessage(JsonNode params, String cmd) { + String msgId = "M"+ UUID.randomUUID(); + enqueueMessage(params, cmd, msgId); + return msgId; + } + + @Override + public abstract FlipperEnvironmentMessageJSON onMessage(FlipperEnvironmentMessageJSON fenvmsg) throws Exception; + + @Override + public abstract void setRequiredEnvironments(IFlipperEnvironment[] envs) throws Exception; + + @Override + public void init(JsonNode params) throws Exception { + throw new Exception("Not implemented init function in this environment."); + } + + @Override + public void init(JsonNode params, Connection connection) throws Exception { + this.init(params); + } + + // Return value of "loaderClass", and adds all other values in spec to pout + public static Properties getGMLProperties(JsonNode spec) { + Properties res = new Properties(); + JsonNode jnprops = null; + if (spec.has("properties")) { + jnprops = spec.get("properties"); + } else { + return null; + } + Iterator fieldNames = jnprops.fieldNames(); + while (fieldNames.hasNext()) { + String fieldName = fieldNames.next(); + res.put(fieldName, jnprops.get(fieldName).asText()); + } + return res; + } + + public static String getGMLClass(JsonNode spec) { + String loaderClass = ""; + if (spec.has("loaderClass")) { + loaderClass = spec.get("loaderClass").asText(); + } else { + return null; + } + return loaderClass; + } + +} \ No newline at end of file diff --git a/src/main/java/hmi/flipper2/environment/FlipperEnvironmentBus.java b/src/main/java/hmi/flipper2/environment/FlipperEnvironmentBus.java new file mode 100644 index 0000000..1fc0877 --- /dev/null +++ b/src/main/java/hmi/flipper2/environment/FlipperEnvironmentBus.java @@ -0,0 +1,260 @@ +package hmi.flipper2.environment; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.io.StringReader; +import java.lang.reflect.InvocationTargetException; +import java.sql.Connection; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.logging.Level; +import java.util.logging.Logger; + +/* Example environmentSpec: +{ + "environments": [ + { + "id":"bml", "loader":"eu.couch.hmi.environments.BMLEnvironment", "requiredloaders": [], + "params": { + "publishBmlFeedback": true, + "middleware": { + "loaderClass": "nl.utwente.hmi.middleware.activemq.ActiveMQMiddlewareLoader", + "properties": { + "iTopic": "COUCH/BML/FEEDBACK/ASAP", + "oTopic": "COUCH/BML/REQUEST/ASAP", + "amqBrokerURI": "tcp://localhost:61616" + } + } + } + }, { + "id":"dgep", "loader":"eu.couch.hmi.environments.DGEPEnvironment", "requiredloaders": [], + "params": { + "dgepCtrlMiddleware": { + "loaderClass": "nl.utwente.hmi.middleware.activemq.ActiveMQMiddlewareLoader", + "properties": { + "iTopic": "DGEP/response", + "oTopic": "DGEP/requests", + "amqBrokerURI": "tcp://localhost:61616" + } + }, + "dgepMovesMiddleware": { + "loaderClass": "nl.utwente.hmi.middleware.activemq.ActiveMQMiddlewareLoader", + "properties": { + "iTopic": "DGEP/dialogue_moves", + "oTopic": "DGEP/requests", + "amqBrokerURI": "tcp://localhost:61616" + } + } + } + }, { + "id":"flipperintents", "loader":"eu.couch.hmi.environments.FlipperIntentPlannerEnvironment", "requiredloaders": [ "bml" ], + "params": {} + }, { + "id":"ui", "loader":"eu.couch.hmi.environments.UIEnvironment", "requiredloaders": [ "dgep" ], + "params": { + "middleware": { + "loaderClass": "nl.utwente.hmi.middleware.activemq.ActiveMQMiddlewareLoader", + "properties": { + "iTopic": "COUCH/UI/REQUESTS", + "oTopic": "COUCH/UI/STATE", + "amqBrokerURI": "tcp://localhost:61616" + } + } + } + }, { + "id":"dialogueloader", "loader":"eu.couch.hmi.environments.DialogueLoaderEnvironment", "requiredloaders": [ "dgep", "flipperintents", "ui", "bml" ], + "params": {} + } + ] +} +*/ + +class EnvironmentSpec { + public EnvironmentLoaderSpec[] environments; +} + +class EnvironmentLoaderSpec { + public String id; + public String loader; + public String[] requiredloaders; + public JsonNode params; +} + +public class FlipperEnvironmentBus { + + private static org.slf4j.Logger logger = LoggerFactory.getLogger(FlipperEnvironmentBus.class.getName()); + + private Map environments; + private ObjectMapper om; + private Connection connection; + + public FlipperEnvironmentBus() { + environments = new HashMap<>(); + om = new ObjectMapper(); + om.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + } + + public FlipperEnvironmentBus(Connection connection){ + this(); + this.connection = connection; + } + + + public boolean init(String specString) { + try { + JsonNode jn = om.readTree(specString); + EnvironmentSpec spec = om.treeToValue(jn, EnvironmentSpec.class); + loadSpec(spec); + } catch (IOException | EnvironmentLoadException e) { + logger.error("Failed to init FlipperEnvironmentBus, invalid spec? ", e); + return false; + } + logger.info("Initialized with "+environments.size()+" environments."); + return true; + } + + public boolean hasMessages() { + for (IFlipperEnvironment env : environments.values()) { + if (env.hasMessage()) return true; + } + return false; + } + + public String getMessages() { + List res = new ArrayList(); + for (IFlipperEnvironment env : environments.values()) { + while (env.hasMessage()) { + try { + res.add(env.getMessage()); + } catch (Exception e) { + logger.warn("Failed to get message from environment: "+env.getId(), e); + } + } + } + + try { + return om.writeValueAsString(res.toArray(new FlipperEnvironmentMessageJSON[0])); + } catch (JsonProcessingException e) { + logger.warn("Failed to return queue messages from environments ", e); + } + return "[]"; + } + + public String sendMessages(String msgsJson) { + logger.debug("received messages:\n "+msgsJson); + List res = new ArrayList(); + try { + JsonNode jn = om.readTree(msgsJson); + FlipperEnvironmentMessageJSON[] msgs = om.treeToValue(jn, FlipperEnvironmentMessageJSON[].class); + for (FlipperEnvironmentMessageJSON msg : msgs) { + FlipperEnvironmentMessageJSON _res = sendMessage(msg); + if (_res != null) res.add(_res); + } + } catch (IOException e) { + logger.error("Failed to process messages "+msgsJson, e); + } + + try { + return om.writeValueAsString(res.toArray(new FlipperEnvironmentMessageJSON[0])); + } catch (Exception e) { + logger.warn("Failed to return responses from environments ", e); + } + + return "[]"; + } + + public FlipperEnvironmentMessageJSON sendMessage(FlipperEnvironmentMessageJSON msg) { + FlipperEnvironmentMessageJSON _result = null; + String targetEnv = ""; + if (environments.containsKey(msg.environment)) { + targetEnv = msg.environment; + try { + logger.debug("Forwarding message of type {} to environment {}.",msg.cmd,msg.environment); + _result = environments.get(targetEnv).onMessage(msg); + } catch (Exception e) { + logger.error("Environment "+targetEnv+" failed (error: {}) to handle message: {}", e, msg); + e.printStackTrace(); + } + } else { + logger.warn("Environment ID unknown: {}", msg.environment); + } + return _result; + } + + public String sendMessage(String msgJson) { + String result = "{}"; + try { + JsonNode msgNode = om.readTree(msgJson); + FlipperEnvironmentMessageJSON msg = om.treeToValue(msgNode,FlipperEnvironmentMessageJSON.class); + FlipperEnvironmentMessageJSON _result = sendMessage(msg); + if (_result != null) { + result = om.writeValueAsString(result); + } + } catch (IOException e) { + logger.error("Failed to process message request: ", e, msgJson); + result = "{}"; + } + + return result; + } + + public void loadSpec(EnvironmentSpec spec) throws EnvironmentLoadException { + for (EnvironmentLoaderSpec lspec : spec.environments) { + try { + if (environments.containsKey(lspec.id)) { + throw new EnvironmentLoadException("Duplicate environment id: "+lspec.id); + } + IFlipperEnvironment env = instantiateEnvironment(lspec.loader); + env.setId(lspec.id); + List requiredEnvs = new ArrayList(); + for (String envId : lspec.requiredloaders) { + if (!environments.containsKey(envId)) + throw new EnvironmentLoadException("Required Loader ID unknown: "+envId); + requiredEnvs.add(environments.get(envId)); + } + try { + env.setRequiredEnvironments(requiredEnvs.toArray(new IFlipperEnvironment[0])); + if(this.connection != null){ + env.init(lspec.params,this.connection); + } + else{ + env.init(lspec.params); + } + } catch (Exception e) { + System.out.println("E: " + e.getMessage()); + throw new EnvironmentLoadException("Failed to init environment "+lspec.id+": "+e.getMessage()); + } + logger.debug("Initialized environment: "+lspec.loader+" ("+env.getId()+")"); + environments.put(lspec.id, env); + } catch (NoSuchMethodException | IllegalArgumentException | InvocationTargetException ex) { + Logger.getLogger(FlipperEnvironmentBus.class.getName()).log(Level.SEVERE, null, ex); + } + } + } + + public IFlipperEnvironment instantiateEnvironment(final String className) throws EnvironmentLoadException, NoSuchMethodException, IllegalArgumentException, InvocationTargetException { + try{ + return IFlipperEnvironment.class.cast(Class.forName(className).getDeclaredConstructor().newInstance()); + } catch(InstantiationException + | IllegalAccessException + | ClassNotFoundException e) { + throw new EnvironmentLoadException("Failed to instantiate FlipperEnvironment with classname "+className+": "+e.getMessage()); + } + } + +} + +class EnvironmentLoadException extends Exception { + private static final long serialVersionUID = 8290847513890592695L; + + public EnvironmentLoadException(String errorMessage) { + super(errorMessage); + } +} \ No newline at end of file diff --git a/src/main/java/hmi/flipper2/environment/FlipperEnvironmentMessageJSON.java b/src/main/java/hmi/flipper2/environment/FlipperEnvironmentMessageJSON.java new file mode 100644 index 0000000..d11dd97 --- /dev/null +++ b/src/main/java/hmi/flipper2/environment/FlipperEnvironmentMessageJSON.java @@ -0,0 +1,26 @@ +package hmi.flipper2.environment; + +import com.fasterxml.jackson.databind.JsonNode; + +public class FlipperEnvironmentMessageJSON { + + public String cmd; + public String environment; + public String msgId; + + public JsonNode params; + + public FlipperEnvironmentMessageJSON(String cmd, String environment, String msgId, JsonNode params) { + this.cmd = cmd; + this.environment = environment; + this.msgId = msgId; + this.params = params; + } + + public FlipperEnvironmentMessageJSON() {} + + @Override + public String toString(){ + return this.cmd + " : " + this.environment + " : " + this.msgId + " : " + this.params.toString(); + } +} diff --git a/src/main/java/hmi/flipper2/environment/IFlipperEnvironment.java b/src/main/java/hmi/flipper2/environment/IFlipperEnvironment.java new file mode 100644 index 0000000..03c2e26 --- /dev/null +++ b/src/main/java/hmi/flipper2/environment/IFlipperEnvironment.java @@ -0,0 +1,20 @@ +package hmi.flipper2.environment; + +import com.fasterxml.jackson.databind.JsonNode; + +import java.sql.Connection; + +public interface IFlipperEnvironment { + + public void setRequiredEnvironments(IFlipperEnvironment[] envs) throws Exception; + public void init(JsonNode params) throws Exception; + public void init(JsonNode params, Connection connection) throws Exception; + public FlipperEnvironmentMessageJSON onMessage(FlipperEnvironmentMessageJSON fenvmsg) throws Exception; + + public boolean hasMessage(); + public FlipperEnvironmentMessageJSON getMessage() throws Exception; + + public String getId(); + public void setId(String id); +} + diff --git a/src/main/java/hmi/flipper2/example/Dynamic.java b/src/main/java/hmi/flipper2/example/Dynamic.java index 52fe485..751a915 100644 --- a/src/main/java/hmi/flipper2/example/Dynamic.java +++ b/src/main/java/hmi/flipper2/example/Dynamic.java @@ -18,61 +18,94 @@ ******************************************************************************/ package hmi.flipper2.example; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import hmi.flipper2.environment.BaseFlipperEnvironment; +import hmi.flipper2.environment.FlipperEnvironmentMessageJSON; +import hmi.flipper2.environment.IFlipperEnvironment; + import java.io.StringReader; import javax.json.Json; import javax.json.JsonObject; import javax.json.JsonReader; import javax.json.JsonValue; -public class Dynamic { +public class Dynamic extends BaseFlipperEnvironment { private final static boolean verbose = false; + private ObjectMapper mapper; + private Double base; public Dynamic() { if (verbose) - System.out.println("Dynamic() called"); + logger.info("Dynamic() called"); this.base = 0.0; } + @Override + public FlipperEnvironmentMessageJSON onMessage(FlipperEnvironmentMessageJSON fenvmsg) throws Exception { + switch(fenvmsg.cmd){ + case "Double": + Double one = fenvmsg.params.get("content").get("one").asDouble(); + Double two = fenvmsg.params.get("content").get("two").asDouble(); + JsonNode res = mapper.createObjectNode().put("f",this.fplus(one,two)); + enqueueMessage(res,"f",fenvmsg.msgId); + break; + default: + logger.error("No appropriate CMD was given: {}",fenvmsg.cmd); + } + return null; + } + + @Override + public void setRequiredEnvironments(IFlipperEnvironment[] envs) throws Exception { + + } + + @Override + public void init(JsonNode params) { + this.mapper = new ObjectMapper(); + } + public Dynamic(Double base) { if (verbose) - System.out.println("Dynamic(Double(" + base + ")) called"); + logger.info("Dynamic(Double(" + base + ")) called"); this.base = base; } public static Boolean alwaysTrue() { if (verbose) - System.out.println("Dynamic:method:alwaysTrue(" + "" + ") called."); + logger.info("Dynamic:method:alwaysTrue(" + "" + ") called."); return Boolean.TRUE; } public static void f(String s) { if (verbose) - System.out.println("Dynamic:static method:f(String(" + s + ")) called."); + logger.info("Dynamic:static method:f(String(" + s + ")) called."); } public static void f(Double d) { if (verbose) - System.out.println("Dynamic:static method:f(Double(" + d + ")) called."); + logger.info("Dynamic:static method:f(Double(" + d + ")) called."); } public Double fplus(Double dl, Double dr) { if (verbose) - System.out.println("Dynamic:static method:f(Double(" + dl + "," + dr + ")) called."); + logger.info("Dynamic:static method:f(Double(" + dl + "," + dr + ")) called."); return base + dl + dr; } public Double recur(Double dl, Dynamic dyn) { if (verbose) - System.out.println("Dynamic: method:recur(" + dl + ", obj.base=" + dyn.base + ")) called."); + logger.info("Dynamic: method:recur(" + dl + ", obj.base=" + dyn.base + ")) called."); return base + dl; } public String fjson(String json) { if (verbose) - System.out.println("Dynamic:static method:fjson(json=" + json + ") called."); + logger.info("Dynamic:static method:fjson(json=" + json + ") called."); JsonObject jso = string2json(json); JsonObject jso_counter = jso.getJsonObject("counter"); JsonValue jso_value = jso_counter.get("value"); diff --git a/src/main/java/hmi/flipper2/example/PersonDbExample.java b/src/main/java/hmi/flipper2/example/PersonDbExample.java index ee4e7da..cd5d0b9 100644 --- a/src/main/java/hmi/flipper2/example/PersonDbExample.java +++ b/src/main/java/hmi/flipper2/example/PersonDbExample.java @@ -36,6 +36,8 @@ import hmi.flipper2.FlipperException; +import static hmi.flipper2.TemplateController.logger; + public class PersonDbExample { private Connection connection; @@ -90,7 +92,7 @@ public String getPerson(Integer id) throws FlipperException { throw new FlipperException("Person with id \"" + id + "\" not found."); } catch (SQLException e) { - System.out.println("CAUGHT: " + e); + logger.error("CAUGHT: " + e); throw new FlipperException(e); } } @@ -99,7 +101,7 @@ public void greetingsToMa(String json) { JsonObject jperson = string2json(json); String fn = jperson.getString("firstname"); String ln = jperson.getString("lastname"); - System.out.println("!@! Person \"" + fn + " " + ln + "\" sends greetings to his Mother"); + logger.debug("!@! Person \"" + fn + " " + ln + "\" sends greetings to his Mother"); } /* diff --git a/src/main/java/hmi/flipper2/javascript/JsEngine.java b/src/main/java/hmi/flipper2/javascript/JsEngine.java index 4bb9789..157cde0 100644 --- a/src/main/java/hmi/flipper2/javascript/JsEngine.java +++ b/src/main/java/hmi/flipper2/javascript/JsEngine.java @@ -30,6 +30,8 @@ import hmi.flipper2.Config; import hmi.flipper2.FlipperException; +import static hmi.flipper2.TemplateController.logger; + public class JsEngine { private ScriptEngineManager mgr; @@ -43,7 +45,7 @@ public JsEngine(TemplateController tc) throws FlipperException { protected void js_init(TemplateController tc) throws FlipperException { this.mgr = new ScriptEngineManager(); - this.engine = mgr.getEngineByName("nashorn"); + this.engine = mgr.getEngineByName("graal.js"); this.invocable = (Invocable)engine; this.tc = tc; } @@ -62,7 +64,7 @@ public Object eval(String script) throws FlipperException { try { if ( Config.debugging && this.tc.dbg != null ) this.tc.dbg.start_JavascriptExec("js", script); - // System.out.println("XXXXXX->"+script); + logger.debug("XXXXXX->"+script); Object res = engine.eval(script); if ( Config.debugging && this.tc.dbg != null ) this.tc.dbg.stop_JavascriptExec("js", (res==null)?null:res.toString()); diff --git a/src/main/java/hmi/flipper2/javascript/JsExpression.java b/src/main/java/hmi/flipper2/javascript/JsExpression.java index 9350c08..9ef4db4 100644 --- a/src/main/java/hmi/flipper2/javascript/JsExpression.java +++ b/src/main/java/hmi/flipper2/javascript/JsExpression.java @@ -28,6 +28,8 @@ import hmi.flipper2.Config; import hmi.flipper2.FlipperException; +import static hmi.flipper2.TemplateController.logger; + public class JsExpression { private static long fcnt = 10000; @@ -42,7 +44,7 @@ public JsExpression(JsEngine jse, String args, String expr, String format) throw this.expr = expr; this.fid = "_f" + fcnt++; String fundef = "var " + this.fid + " = function(" + args + ") { " + String.format(format, expr) + "; };"; - // System.out.println("FUNDEF: "+fundef); + logger.debug("FUNDEF: "+fundef); jse.eval(fundef); } diff --git a/src/main/java/hmi/flipper2/launcher/FlipperLauncher.java b/src/main/java/hmi/flipper2/launcher/FlipperLauncher.java index aeabbd0..dd07963 100644 --- a/src/main/java/hmi/flipper2/launcher/FlipperLauncher.java +++ b/src/main/java/hmi/flipper2/launcher/FlipperLauncher.java @@ -39,7 +39,7 @@ public static void main(String[] args) { String flipperPropFile = "flipper.properties"; if(args.length % 2 != 0){ - System.err.println(help); + logger.error(help); System.exit(0); } @@ -47,7 +47,7 @@ public static void main(String[] args) { if(args[i].equals("-config")) { flipperPropFile = args[i+1]; } else { - System.err.println("Unknown commandline argument: \""+args[i]+" "+args[i+1]+"\".\n"+help); + logger.error("Unknown commandline argument: \""+args[i]+" "+args[i+1]+"\".\n"+help); System.exit(0); } } diff --git a/src/main/java/hmi/flipper2/launcher/FlipperLauncherThread.java b/src/main/java/hmi/flipper2/launcher/FlipperLauncherThread.java index 91c56b2..fb44397 100644 --- a/src/main/java/hmi/flipper2/launcher/FlipperLauncherThread.java +++ b/src/main/java/hmi/flipper2/launcher/FlipperLauncherThread.java @@ -98,6 +98,10 @@ private void init(Properties ps) { this.maxSteps = Integer.parseInt(ps.getProperty("maxSteps", "0")); this.stopIfUnchanged = Boolean.parseBoolean(ps.getProperty("stopIfUnchanged", "False")); this.showISViewer = Boolean.parseBoolean(ps.getProperty("isViewer", "False")); + if(Boolean.parseBoolean(ps.getProperty("nashorn-compat","false"))){ + logger.warn("Running Flipper in Nashorn Compatibility mode"); + System.setProperty("polyglot.js.nashorn-compat","true"); + } // Database this.host = ps.getProperty("host", ""); this.database = ps.getProperty("database", ""); diff --git a/src/main/java/hmi/flipper2/postgres/Database.java b/src/main/java/hmi/flipper2/postgres/Database.java index b739b95..83c0296 100644 --- a/src/main/java/hmi/flipper2/postgres/Database.java +++ b/src/main/java/hmi/flipper2/postgres/Database.java @@ -45,7 +45,7 @@ public class Database { public void commit() throws FlipperException { try { - // System.out.println("COMMIT!"); + logger.debug("COMMIT!"); conn.commit(); } catch (SQLException e) { throw new FlipperException(e); @@ -197,7 +197,7 @@ public void removeTemplateFile(TemplateController tc, TemplateFile tf) throws Fl public void updateTemplateFileIs(TemplateFile tf, String is_value) throws FlipperException { try { - // System.out.println("UPDATE TEMPLATE FILE (tf_id="+tf.tfid+"): "+ is_value); + logger.debug("UPDATE TEMPLATE FILE (tf_id="+tf.tfid+"): "+ is_value); String updateTableSQL = "UPDATE flipper_tf SET json_is = to_json(?::json), sync_is = sync_is+1, updated = ? WHERE tfid = ? RETURNING sync_is;"; PreparedStatement preparedStatement = conn.prepareStatement(updateTableSQL); preparedStatement.setString(1, is_value); @@ -219,7 +219,7 @@ public void updateTemplateFileIs(TemplateFile tf, String is_value) throws Flippe public List getTemplateFiles(TemplateController tc) throws FlipperException { List res = new ArrayList(); - // System.out.println("INCOMPLETE:Database:getTemplateFiles"); + logger.warn("INCOMPLETE:Database:getTemplateFiles"); try { String selectSQL = "SELECT tfid,path,xml,json_is#>>'{}' AS json_is,sync_is FROM flipper_tf WHERE cid = ?;"; PreparedStatement preparedStatement = conn.prepareStatement(selectSQL); diff --git a/src/main/java/hmi/flipper2/sax/SimpleHandler.java b/src/main/java/hmi/flipper2/sax/SimpleHandler.java index b079426..25dbbd4 100644 --- a/src/main/java/hmi/flipper2/sax/SimpleHandler.java +++ b/src/main/java/hmi/flipper2/sax/SimpleHandler.java @@ -25,6 +25,8 @@ import org.xml.sax.SAXParseException; import org.xml.sax.helpers.DefaultHandler; +import static hmi.flipper2.TemplateController.logger; + public class SimpleHandler extends DefaultHandler { private Vector stack = null; @@ -91,7 +93,7 @@ private String getParseExceptionInfo(SAXParseException spe) { } public void warning(SAXParseException spe) throws SAXException { - System.out.println("Warning: " + getParseExceptionInfo(spe)); + logger.warn("Warning: " + getParseExceptionInfo(spe)); } public void error(SAXParseException spe) throws SAXException { diff --git a/src/main/java/hmi/flipper2/value/IsJavaValue.java b/src/main/java/hmi/flipper2/value/IsJavaValue.java index 5455526..52e5046 100644 --- a/src/main/java/hmi/flipper2/value/IsJavaValue.java +++ b/src/main/java/hmi/flipper2/value/IsJavaValue.java @@ -27,6 +27,8 @@ import hmi.flipper2.dataflow.DataFlow; import hmi.flipper2.javascript.JsExpression; +import static hmi.flipper2.TemplateController.logger; + public class IsJavaValue extends JavaValue { private Is is; @@ -60,7 +62,7 @@ public Class objectClass() throws FlipperException { if (this.vt_type == ValueTransferType.TYPE_JSONSTRING) return String.class; else if (this.cl != null ) { - // System.out.println("DEFCLASS="+cl.getName()); + logger.debug("DEFCLASS="+cl.getName()); return this.cl; } else throw new RuntimeException("Should define class for is="+path+", is_type: Object. Dynamic calls implemented in future"); diff --git a/src/main/resources/example/Dynamic.xml b/src/main/resources/example/Dynamic.xml new file mode 100644 index 0000000..5f0b65f --- /dev/null +++ b/src/main/resources/example/Dynamic.xml @@ -0,0 +1,133 @@ + + + + { + "dynamic":{ + "initialized": false, + "requestQueue" : [], + "response" : {}, + "messages" : {} + }, + "counter" : 0 + } + + + + + 0){ + //Compare the message received to messages in the requestQueue + for(i = 0; i < is.components.dynamic.requestQueue.length; i++){ + if(is.components.dynamic.requestQueue[i].response.requestId === res.msgId){ + is.components.dynamic.messages = res.params; + //is.components.counter = is.components.dynamic.messages.f; + element = i; + } + } + } + // Remove the message from the queue, once it is processed. + is.components.dynamic.requestQueue.splice(element,1); + } + + // Whenever we want to send something from Flipper to Java, we push it to the requestQueue. + var updateDYN = function(message){ + is.components.dynamic.requestQueue.push({content : message}); + return message; + } + + // Once you have added the requests, you can use this method to send requests to the environment + var MakeRequests = function(type) { + for (var i=0; i < is.components.dynamic.requestQueue.length; i++) { + if (is.components.dynamic.requestQueue[i].response != null) continue; + var requestId = ENV.queueMessage("dynamic", type, is.components.dynamic.requestQueue[i]); + is.components.dynamic.requestQueue[i].response = { + requestId: requestId + }; + } + } + + //Register your listeners and request handlers here. + function Init() { + ENV.register("f", handleDYNResponse); + return true; + } + + return { + Init: Init, + MakeRequests: MakeRequests, + updateDYN: updateDYN + }; + + })(); + ]]> + + + + + + + + + + + + diff --git a/src/main/resources/example/Environment.xml b/src/main/resources/example/Environment.xml new file mode 100644 index 0000000..24ad994 --- /dev/null +++ b/src/main/resources/example/Environment.xml @@ -0,0 +1,257 @@ + + + + + { + "temp": { "newenvmsgs":[], "newenvresponses":[], "newenvoutmsgs": [] }, + "initialized":false, + "initnoerror":false, + "environmentSpec": { + "environments": [ + { + "id" : "dynamic", "loader":"hmi.flipper2.example.Dynamic", "requiredloaders":[] + } + ] + } + } + + + "+JSON.stringify(msgs[i])); + publishMsg(msgs[i].cmd, msgs[i]); + } + return []; + } + + function handleEnvironmentResponses(msgs) { + for (var i = 0; i < msgs.length; i++) { + //print("res > "+JSON.stringify(msgs[i])); + publishMsg(msgs[i].cmd, msgs[i]); + } + return []; + } + + function haveMessagesQueued() { + return messageQueue.length > 0; + } + + function queueMessage(environment, cmd, params) { + var msgId = Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15); + messageQueue.push({ + environment: environment, + cmd: cmd, + msgId: msgId, + params: params + }); + return msgId; + } + + function queueResponse(respondTo, params) { + messageQueue.push({ + environment: respondTo.environment, + cmd: respondTo.cmd, + msgId: respondTo.msgId, + params: params + }); + return respondTo.msgId; + } + + function getEntireMessageQueue() { + var res = messageQueue; + messageQueue = []; + return res; + } + + return { + register: register, + handleEnvironmentMessages: handleEnvironmentMessages, + queueMessage: queueMessage, + handleEnvironmentResponses: handleEnvironmentResponses, + haveMessagesQueued: haveMessagesQueued, + getEntireMessageQueue: getEntireMessageQueue, + queueResponse: queueResponse, + isInitialized: function() { + return is.env.initialized && is.env.initnoerror; + } + }; +})(); +]]> + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/resources/example/Flipper2Count.xml b/src/main/resources/example/Flipper2Count.xml index 3eff73b..82fe105 100644 --- a/src/main/resources/example/Flipper2Count.xml +++ b/src/main/resources/example/Flipper2Count.xml @@ -2,7 +2,7 @@ { - "value" : 1 + "value" : 1.0 } diff --git a/src/main/resources/flipper.properties b/src/main/resources/flipper.properties index cdcc16e..b62476f 100644 --- a/src/main/resources/flipper.properties +++ b/src/main/resources/flipper.properties @@ -1,21 +1,23 @@ # name for TemplateController -name: flipperis +name=flipperis # comma seperated list of external lists -jslibs: jslibs/underscore-min.js +jslibs=jslibs/underscore-min.js # comma seperated list of template paths -templates: example/Underscore.xml +templates=example/Flipper2Count.xml, example/Environment.xml, example/Dynamic.xml #evaluate template every 1000/evalFrequency milliseconds -evalFrequency: 1 +evalFrequency=1 #n=0:don't stop, n=1+: stop after n times checking templates -maxSteps: 0 +maxSteps=0 #True|False -stopIfUnchanged: False +stopIfUnchanged=False #ALL,NONE,ONLY_CHANGED -autoReloadTemplates: ONLY_CHANGED +autoReloadTemplates=ONLY_CHANGED #Data flow analysis -analyze: False +analyze=True #Show GUI -isViewer: False +isViewer=False +#Nashorn compatible javascript default: false +nashorn-compat=False #host: 127.0.0.1 #database: flipperis