From 87a1dcd95770e74db5671d3e9131ad60e866b9c7 Mon Sep 17 00:00:00 2001 From: rschoene <rene.schoene@tu-dresden.de> Date: Mon, 2 May 2022 11:06:34 +0200 Subject: [PATCH] Updated initial version of Java handler. - also missing fix for CI (ragdoc-view should always run) --- .gitlab-ci.yml | 3 - .../src/main/jastadd/Handlers.jrag | 1 + .../jastadd/ragconnect/compiler/Compiler.java | 3 + .../src/main/resources/JavaHandler.mustache | 88 +++++++++++++++++++ .../src/main/resources/handler.mustache | 10 ++- .../main/resources/receiveDefinition.mustache | 12 +++ .../main/resources/sendDefinition.mustache | 22 +++++ ragconnect.tests/build.gradle | 29 +++++- .../src/test/01-input/java/README.md | 3 + .../src/test/01-input/java/Test.connect | 25 ++++++ .../src/test/01-input/java/Test.jadd | 30 +++++++ .../src/test/01-input/java/Test.relast | 9 ++ .../jastadd/ragconnect/tests/JavaTest.java | 43 +++++++++ 13 files changed, 270 insertions(+), 8 deletions(-) create mode 100644 ragconnect.base/src/main/resources/JavaHandler.mustache create mode 100644 ragconnect.tests/src/test/01-input/java/README.md create mode 100644 ragconnect.tests/src/test/01-input/java/Test.connect create mode 100644 ragconnect.tests/src/test/01-input/java/Test.jadd create mode 100644 ragconnect.tests/src/test/01-input/java/Test.relast create mode 100644 ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/JavaTest.java diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 87d2c9c..44a279a 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -94,9 +94,6 @@ ragdoc_view: - OUTPUT_DIR=$(pwd -P)/pages/docs/ragdoc - cd /ragdoc-view/src/ && rm -rf data && ln -s $DATA_DIR - /ragdoc-view/build-view.sh --output-path=$OUTPUT_DIR - only: - - dev - - master artifacts: paths: - "pages/docs/ragdoc" diff --git a/ragconnect.base/src/main/jastadd/Handlers.jrag b/ragconnect.base/src/main/jastadd/Handlers.jrag index 57c7ccd..e64a8c5 100644 --- a/ragconnect.base/src/main/jastadd/Handlers.jrag +++ b/ragconnect.base/src/main/jastadd/Handlers.jrag @@ -1,4 +1,5 @@ aspect RagConnectHandlers { + syn Handler RagConnect.javaHandler() = resolveHandlerByName("java"); syn Handler RagConnect.mqttHandler() = resolveHandlerByName("mqtt"); syn Handler RagConnect.restHandler() = resolveHandlerByName("rest"); diff --git a/ragconnect.base/src/main/java/org/jastadd/ragconnect/compiler/Compiler.java b/ragconnect.base/src/main/java/org/jastadd/ragconnect/compiler/Compiler.java index 8d4ab2c..e255b3b 100644 --- a/ragconnect.base/src/main/java/org/jastadd/ragconnect/compiler/Compiler.java +++ b/ragconnect.base/src/main/java/org/jastadd/ragconnect/compiler/Compiler.java @@ -35,6 +35,7 @@ public class Compiler extends AbstractCompiler { private static final String OPTION_LOGGING_TARGET_CONSOLE = "console"; private static final String OPTION_LOGGING_TARGET_SLF4J = "slf4j"; + private static final String OPTION_PROTOCOL_JAVA = "java"; private static final String OPTION_PROTOCOL_MQTT = "mqtt"; private static final String OPTION_PROTOCOL_REST = "rest"; @@ -179,6 +180,7 @@ public class Compiler extends AbstractCompiler { new ValueOption("protocols", "Protocols to enable") .acceptMultipleValues(true) .addDefaultValue(OPTION_PROTOCOL_MQTT, "Enable MQTT") + .addAcceptedValue(OPTION_PROTOCOL_JAVA, "Enable Java") .addAcceptedValue(OPTION_PROTOCOL_REST, "Enable REST") ); optionPrintYaml = addOption( @@ -331,6 +333,7 @@ public class Compiler extends AbstractCompiler { ragConnect.getConfiguration().setRootNode(rootNode); // Handler ::= <ClassName> <UniqueName> <InUse:boolean>; + ragConnect.addHandler(new Handler("JavaHandler", "java", optionProtocols.hasValue(OPTION_PROTOCOL_JAVA))); ragConnect.addHandler(new Handler("MqttServerHandler", "mqtt", optionProtocols.hasValue(OPTION_PROTOCOL_MQTT))); ragConnect.addHandler(new Handler("RestServerHandler", "rest", optionProtocols.hasValue(OPTION_PROTOCOL_REST))); } diff --git a/ragconnect.base/src/main/resources/JavaHandler.mustache b/ragconnect.base/src/main/resources/JavaHandler.mustache new file mode 100644 index 0000000..bc73c91 --- /dev/null +++ b/ragconnect.base/src/main/resources/JavaHandler.mustache @@ -0,0 +1,88 @@ +/** + * Singleton class providing routing functionality for byte[] based message calls. + */ +public class JavaHandler { + public static JavaHandler JAVA_HANDLER_INSTANCE = null; + private java.util.Map<String, java.util.List<Pair<String, java.util.function.BiConsumer<String, byte[]>>>> callbackList = new java.util.concurrent.ConcurrentHashMap<>(); + private String name; + + private JavaHandler() { + this("RagConnect"); + } + + public JavaHandler(String name) { + this.name = name; + } + + public synchronized static JavaHandler getInstance() { + if (JAVA_HANDLER_INSTANCE == null) { + JAVA_HANDLER_INSTANCE = new JavaHandler(); + } + return JAVA_HANDLER_INSTANCE; + } + + public String registerCallback(String topic, java.util.function.BiConsumer<String, byte[]> callback) { + {{logInfo}}("[JAVA_HANDLER] Registering new callback for {{log_}}.", topic); + + String callbackUUID = java.util.UUID.randomUUID().toString(); + + java.util.List<Pair<String, java.util.function.BiConsumer<String, byte[]>>> registeredCallbacks = getAllCallbacks().get(topic); + + if (registeredCallbacks == null) { + java.util.List<Pair<String, java.util.function.BiConsumer<String, byte[]>>> newCallbackList = java.util.Collections.synchronizedList(new java.util.ArrayList<>()); + newCallbackList.add(new Pair<>(callbackUUID, callback)); + callbackList.put(topic, newCallbackList); + } else { + registeredCallbacks.add(new Pair<>(callbackUUID, callback)); + } + + return callbackUUID; + } + + public boolean unregisterCallback(String path, String uuid) { + {{logInfo}}("[JAVA_HANDLER] Unregistering callback with uuid: {{log_}}", uuid + " on path: {{log_}}", path); + + java.util.List<Pair<String, java.util.function.BiConsumer<String, byte[]>>> callbacks = getAllCallbacks().get(path); + + int count = 0; + + if (callbacks != null) { + for (Pair<String, java.util.function.BiConsumer<String, byte[]>> callbackPair : callbacks) { + if (callbackPair._1.equals(uuid)) { + callbacks.remove(count); + return true; + } else { + count++; + } + } + } + return false; + } + + public void close() { + } + + public boolean push(String topic, byte[] data) { + {{logDebug}}("[JAVA_HANDLER] Pushing a message."); + String dataString = new String(data); + {{logDebug}}("[JAVA_HANDLER] Data: {{log_}}", dataString); + + java.util.List<Pair<String, java.util.function.BiConsumer<String, byte[]>>> callbacks = getAllCallbacks().get(topic); + + if (callbacks == null) { + {{logError}}("[JAVA_HANDLER] Could not publish message. No callback registered for topic {{log_}}", topic); + return false; + } + + for (Pair<String, java.util.function.BiConsumer<String, byte[]>> callbackPair : callbacks) { + {{logDebug}}("[JAVA_HANDLER] Calling callback: {{log_}}", callbackPair._1); + callbackPair._2.accept(topic, data); + } + + return true; + } + + public java.util.Map<String, java.util.List<Pair<String, java.util.function.BiConsumer<String, byte[]>>>> getAllCallbacks() { + return callbackList; + } +} diff --git a/ragconnect.base/src/main/resources/handler.mustache b/ragconnect.base/src/main/resources/handler.mustache index 4919426..2c4cf96 100644 --- a/ragconnect.base/src/main/resources/handler.mustache +++ b/ragconnect.base/src/main/resources/handler.mustache @@ -19,18 +19,24 @@ aspect RagConnectHandler { {{/configIncrementalOptionActive}} } +{{#javaHandler}} + {{#InUse}} + {{> JavaHandler}} + {{/InUse}} +{{/javaHandler}} + {{#mqttHandler}} {{#InUse}} public void {{rootNodeName}}.{{setupWaitUntilReadyMethodName}}(long time, java.util.concurrent.TimeUnit unit) { {{fieldName}}.setupWaitUntilReady(time, unit); } + {{> MqttHandler}} {{/InUse}} -{{> MqttHandler}} {{/mqttHandler}} {{#restHandler}} {{#InUse}} -{{> RestHandler}} + {{> RestHandler}} {{/InUse}} {{/restHandler}} diff --git a/ragconnect.base/src/main/resources/receiveDefinition.mustache b/ragconnect.base/src/main/resources/receiveDefinition.mustache index cb4dc8d..c4f070c 100644 --- a/ragconnect.base/src/main/resources/receiveDefinition.mustache +++ b/ragconnect.base/src/main/resources/receiveDefinition.mustache @@ -93,6 +93,13 @@ private boolean {{parentTypeName}}.{{internalConnectMethodName}}(String {{connec RagConnectToken connectToken = new RagConnectToken(uri, "{{entityName}}"); boolean success; switch (scheme) { + {{#javaHandler}} + {{#InUse}} + case "java": + success = {{attributeName}}().registerCallback(path, consumer, connectToken); + break; + {{/InUse}} + {{/javaHandler}} {{#mqttHandler}} {{#InUse}} case "mqtt": @@ -130,6 +137,11 @@ public boolean {{parentTypeName}}.{{disconnectMethodName}}(String {{connectParam } RagConnectDisconnectHandlerMethod disconnectingMethod; switch (scheme) { + {{#javaHandler}} + {{#InUse}} + case "java": return {{attributeName}}().unregisterCallback(uri.getPath(), connectTokens.get(this).get(uri).globalId); + {{/InUse}} + {{/javaHandler}} {{#mqttHandler}} {{#InUse}} case "mqtt": disconnectingMethod = {{attributeName}}()::disconnect; diff --git a/ragconnect.base/src/main/resources/sendDefinition.mustache b/ragconnect.base/src/main/resources/sendDefinition.mustache index 1e66fde..1485c17 100644 --- a/ragconnect.base/src/main/resources/sendDefinition.mustache +++ b/ragconnect.base/src/main/resources/sendDefinition.mustache @@ -5,6 +5,21 @@ public boolean {{parentTypeName}}.{{connectMethodName}}(String {{connectParamete RagConnectToken connectToken = new RagConnectToken(uri, "{{entityName}}"); boolean success; switch (scheme) { + {{#javaHandler}} + {{#InUse}} + case "java": + final JavaHandler handler = {{attributeName}}().getInstance(); + + {{senderName}}.add(() -> { + handler.push(path, {{lastValueGetterCall}}); + }{{#IndexBasedListAccess}}, index{{/IndexBasedListAccess}}, connectToken); + {{updateMethodName}}(); + if (writeCurrentValue) { + {{writeMethodName}}({{#IndexBasedListAccess}}index, {{/IndexBasedListAccess}}connectToken); + } + break; + {{/InUse}} + {{/javaHandler}} {{#mqttHandler}} {{#InUse}} case "mqtt": @@ -78,6 +93,13 @@ public boolean {{parentTypeName}}.{{disconnectMethodName}}(String {{connectParam {{/configIncrementalOptionActive}} RagConnectDisconnectHandlerMethod disconnectingMethod; switch (scheme) { + {{#javaHandler}} + {{#InUse}} + case "java": + disconnectingMethod = {{senderName}}::remove; + break; + {{/InUse}} + {{/javaHandler}} {{#mqttHandler}} {{#InUse}} case "mqtt": diff --git a/ragconnect.tests/build.gradle b/ragconnect.tests/build.gradle index 18dd839..d41a73c 100644 --- a/ragconnect.tests/build.gradle +++ b/ragconnect.tests/build.gradle @@ -649,8 +649,6 @@ task compileRelationIncremental(type: RagConnectTest) { inputFiles = [file('src/test/01-input/relation/Test.relast'), file('src/test/01-input/relation/Test.connect')] rootNode = 'Root' - logWrites = true - logIncremental = true extraOptions = defaultRagConnectOptionsAnd(['--experimental-jastadd-329']) } relast { @@ -666,9 +664,34 @@ task compileRelationIncremental(type: RagConnectTest) { } } +// --- Test: java-incremental --- +task compileJavaIncremental(type: RagConnectTest) { + ragconnect { + outputDir = file('src/test/02-after-ragconnect/javaInc') + inputFiles = [file('src/test/01-input/java/Test.relast'), + file('src/test/01-input/java/Test.connect')] + rootNode = 'Root' + logWrites = true + logIncremental = true + protocols = ['java'] + extraOptions = defaultRagConnectOptionsAnd(['--experimental-jastadd-329']) + } + relast { + useJastAddNames = true + grammarName = 'src/test/03-after-relast/javaInc/javaInc' + serializer = 'jackson' + } + jastadd { + jastAddList = 'JastAddList' + packageName = 'javaInc.ast' + inputFiles = [file('src/test/01-input/java/Test.jadd')] + extraOptions = JASTADD_INCREMENTAL_OPTIONS_TRACING_FULL + } +} + // --- Task order --- classes.dependsOn(':ragconnect.base:jar') -//compileAttributeIncremental.outputs.upToDateWhen { false } +compileJavaIncremental.outputs.upToDateWhen { false } // --- Misc --- static ArrayList<String> defaultRagConnectOptionsAnd(ArrayList<String> options = []) { diff --git a/ragconnect.tests/src/test/01-input/java/README.md b/ragconnect.tests/src/test/01-input/java/README.md new file mode 100644 index 0000000..272a851 --- /dev/null +++ b/ragconnect.tests/src/test/01-input/java/README.md @@ -0,0 +1,3 @@ +# Java + +Idea: Use receive and send definitions using the Java handler. diff --git a/ragconnect.tests/src/test/01-input/java/Test.connect b/ragconnect.tests/src/test/01-input/java/Test.connect new file mode 100644 index 0000000..dcb6a56 --- /dev/null +++ b/ragconnect.tests/src/test/01-input/java/Test.connect @@ -0,0 +1,25 @@ +send SenderRoot.SendToken ; +send SenderRoot.SendNode ; +send SenderRoot.SendManyNode ; +send SenderRoot.SendNTA ; + +AddSuffix maps A a to A {: + A result = new A(); + String changedValue = a.getValue() + "post"; + result.setValue(changedValue); + result.setInner(new Inner("inner" + a.getInner().getInnerValue())); + return result; +:} + +AddStringSuffix maps String s to String {: + return s + "post"; +:} + +AddPlusOne maps int i to int {: + return i + 1; +:} + +receive ReceiverRoot.SomeToken; +receive ReceiverRoot.SomeNode; +receive ReceiverRoot.SomeNodeWithMapping using AddSuffix; +receive ReceiverRoot.ManyNode; diff --git a/ragconnect.tests/src/test/01-input/java/Test.jadd b/ragconnect.tests/src/test/01-input/java/Test.jadd new file mode 100644 index 0000000..0bb643e --- /dev/null +++ b/ragconnect.tests/src/test/01-input/java/Test.jadd @@ -0,0 +1,30 @@ +aspect Computation { + syn String SenderRoot.basic() = getInput(); + syn String SenderRoot.simple() = getInput() + "Post"; + syn A SenderRoot.getSendNTA() { + A result = new A(); + result.setValue(getInput()); + Inner inner = new Inner(); + inner.setInnerValue("1"); + result.setInner(inner); + return result; + } +} +aspect MakeCodeCompile { + +} +aspect MakeCodeWork { + +} +aspect NameResolution { + // overriding customID guarantees to produce the same JSON representation for equal lists + // otherwise, the value for id is different each time + @Override + protected String A.customID() { + return getClass().getSimpleName() + getValue(); + } + @Override + protected String Inner.customID() { + return getClass().getSimpleName() + getInnerValue(); + } +} diff --git a/ragconnect.tests/src/test/01-input/java/Test.relast b/ragconnect.tests/src/test/01-input/java/Test.relast new file mode 100644 index 0000000..13c7ba3 --- /dev/null +++ b/ragconnect.tests/src/test/01-input/java/Test.relast @@ -0,0 +1,9 @@ +Root ::= SenderRoot* ReceiverRoot; +SenderRoot ::= <Input> <SendToken> SendNode:A SendManyNode:A* /SendNTA:A/ ; +ReceiverRoot ::= + <SomeToken> + SomeNode:A + SomeNodeWithMapping:A + ManyNode:A*; +A ::= <Value> Inner ; +Inner ::= <InnerValue> ; diff --git a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/JavaTest.java b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/JavaTest.java new file mode 100644 index 0000000..2d22b3c --- /dev/null +++ b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/JavaTest.java @@ -0,0 +1,43 @@ +package org.jastadd.ragconnect.tests; + +import javaInc.ast.Root; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Testing the Java handler. + * + * @author rschoene - Initial contribution + */ +@Tag("New") +public class JavaTest { + + protected Logger logger = LoggerFactory.getLogger(getClass()); + private Root model; + + void createModel() { + model = new Root(); + } + + @Test + public void testCommunicateSendInitialValue() { + createModel(); + } + + @Test + public void testCommunicateOnlyUpdatedValue() { + createModel(); + } + + @AfterEach + public void alwaysCloseConnections() { + logger.debug("Closing connections"); + if (model != null) { + model.ragconnectCloseConnections(); + } + } + +} -- GitLab