diff --git a/libs/jastadd2.jar b/libs/jastadd2.jar index d615b895453d660f0e7397fffad58a05029169fd..3c952d30318ddc23d38d7ccb277ee8431fe6b000 100644 Binary files a/libs/jastadd2.jar and b/libs/jastadd2.jar differ diff --git a/ragconnect.base/build.gradle b/ragconnect.base/build.gradle index f039c8bc129e8f2ca58ed389c1cc5bf5cd8e92e9..7275d3ba1afc3347e047f840ad3188e3b6048c6c 100644 --- a/ragconnect.base/build.gradle +++ b/ragconnect.base/build.gradle @@ -22,7 +22,10 @@ mainClassName = 'org.jastadd.ragconnect.compiler.Compiler' repositories { mavenCentral() - jcenter() + maven { + name "gitlab-maven" + url "https://git-st.inf.tu-dresden.de/api/v4/groups/jastadd/-/packages/maven" + } } tasks.compileJava { options.release.set(11) @@ -31,8 +34,8 @@ tasks.compileJava { dependencies { implementation project(':relast-preprocessor') implementation group: 'com.github.spullara.mustache.java', name: 'compiler', version: "${mustache_java_version}" -// runtimeOnly group: 'org.jastadd', name: 'jastadd', version: '2.3.5' - runtimeOnly fileTree(include: ['jastadd2.jar'], dir: '../libs') + runtimeOnly group: 'org.jastadd', name: 'jastadd2', version: '2.3.5-dresden' +// runtimeOnly fileTree(include: ['jastadd2.jar'], dir: '../libs') api group: 'net.sf.beaver', name: 'beaver-rt', version: '0.9.11' } diff --git a/ragconnect.base/src/main/jastadd/Intermediate.jadd b/ragconnect.base/src/main/jastadd/Intermediate.jadd index c75199d0f2621057fc7960327f0f6519cec26c3b..1df267fade1abf9ba068b6a8af4a42688f79125f 100644 --- a/ragconnect.base/src/main/jastadd/Intermediate.jadd +++ b/ragconnect.base/src/main/jastadd/Intermediate.jadd @@ -4,6 +4,10 @@ Design considerations - no complete intermediate structure, but instead single nodes where applicable/needed */ aspect NewStuff { + // unsorted + syn String RagConnect.observerInstanceFieldName() = internalRagConnectPrefix() + "ObserverInstance"; + syn String RagConnect.observerInstanceSingletonMethodName() = internalRagConnectPrefix() + "Observer"; + syn String RagConnect.observerInstanceResetMethodName() = internalRagConnectPrefix() + "resetObserver"; // send.mustache syn boolean EndpointDefinition.needForwardingNTA() = getEndpointTarget().needForwardingNTA(); syn String EndpointDefinition.forwardingNTA_Name() = getEndpointTarget().forwardingNTA_Name(); diff --git a/ragconnect.base/src/main/resources/handler.mustache b/ragconnect.base/src/main/resources/handler.mustache index 50c4609d02c64eacf641ad6dca5516da0e878b58..d765f5384f428dd67ff084f139321e5018d3a364 100644 --- a/ragconnect.base/src/main/resources/handler.mustache +++ b/ragconnect.base/src/main/resources/handler.mustache @@ -13,6 +13,8 @@ aspect RagConnectHandler { {{#Handlers}} {{#InUse}}{{fieldName}}.close();{{/InUse}} {{/Handlers}} + trace().setReceiver({{observerInstanceSingletonMethodName}}().oldReceiver); + {{observerInstanceResetMethodName}}(); } {{#mqttHandler}} @@ -73,20 +75,33 @@ aspect RagConnectHandler { } boolean remove(RagConnectToken token) { - if (tokenToSender == null) { - System.err.println("Removing sender before first addition for " + token.entityName + " at " + token.uri); + String errorMessage = internal_remove(token); + if (errorMessage == null) { + return true; + } else { + System.err.println(errorMessage); return false; } + } + + /** + * (internal) Removes the token, returning an error message if there is one. + * @param token the token to be removed + * @return an error message (upon error), or null (upon success) + */ + String internal_remove(RagConnectToken token) { + if (tokenToSender == null) { + return "Removing sender before first addition for " + token.entityName + " at " + token.uri; + } Runnable sender = tokenToSender.remove(token); if (sender == null) { - System.err.println("Could not find connected sender for " + token.entityName + " at " + token.uri); - return false; + return "Could not find connected sender for " + token.entityName + " at " + token.uri; } boolean success = senders.remove(sender); if (senders.isEmpty()) { lastValue = null; } - return success; + return success ? null : "Could not remove sender for " + token.entityName + " at " + token.uri; } void run() { @@ -111,9 +126,22 @@ aspect RagConnectHandler { boolean remove(RagConnectToken token) { // publishers.forEach((index, publisher) -> publisher.remove(token)); - return publishers.values().stream() - .map(publisher -> publisher.remove(token)) - .reduce(true, (result, success) -> result && success); + // remove token from each publisher, at least one has to successfully remove the token to make this call a success + boolean result = false; + java.util.List<String> errorMessages = new java.util.ArrayList<>(); + for (RagConnectPublisher publisher : publishers.values()) { + String errorMessage = publisher.internal_remove(token); + if (errorMessage == null) { + result = true; + } else { + errorMessages.add(errorMessage); + } + } + if (!result) { + // only print error message, if all publishers failed to remove the token + errorMessages.stream().forEachOrdered(System.err::println); + } + return result; } void run(int index) { diff --git a/ragconnect.base/src/main/resources/ragconnect.mustache b/ragconnect.base/src/main/resources/ragconnect.mustache index 6917f46e42437b58cdcd168c31c0a06c647e99f9..5539f419f41ece5bd6a55448f00a343698a3ecc1 100644 --- a/ragconnect.base/src/main/resources/ragconnect.mustache +++ b/ragconnect.base/src/main/resources/ragconnect.mustache @@ -57,12 +57,17 @@ aspect RagConnectObserver { final RagConnectToken connectToken; final ASTNode node; final String attributeString; + final boolean compareParams; + final Object params; final Runnable attributeCall; - RagConnectObserverEntry(RagConnectToken connectToken, ASTNode node, String attributeString, Runnable attributeCall) { + RagConnectObserverEntry(RagConnectToken connectToken, ASTNode node, String attributeString, + boolean compareParams, Object params, Runnable attributeCall) { this.connectToken = connectToken; this.node = node; this.attributeString = attributeString; + this.compareParams = compareParams; + this.params = params; this.attributeCall = attributeCall; } } @@ -96,11 +101,21 @@ aspect RagConnectObserver { } void add(RagConnectToken connectToken, ASTNode node, String attributeString, Runnable attributeCall) { + internal_add(connectToken, node, attributeString, false, null, attributeCall); + } + void add(RagConnectToken connectToken, ASTNode node, String attributeString, Object params, Runnable attributeCall) { + internal_add(connectToken, node, attributeString, true, params, attributeCall); + } + + private void internal_add(RagConnectToken connectToken, ASTNode node, String attributeString, + boolean compareParams, Object params, Runnable attributeCall) { {{#configLoggingEnabledForIncremental}} - System.out.println("** observer add: " + node + " on " + attributeString); + System.out.println("** observer add: " + node + " on " + attributeString + (compareParams ? " (parameterized)" : "")); {{/configLoggingEnabledForIncremental}} - observedNodes.add(new RagConnectObserverEntry(connectToken, node, attributeString, attributeCall)); + observedNodes.add(new RagConnectObserverEntry(connectToken, node, attributeString, + compareParams, params, attributeCall)); } + void remove(RagConnectToken connectToken) { observedNodes.removeIf(entry -> entry.connectToken.equals(connectToken)); } @@ -127,7 +142,7 @@ aspect RagConnectObserver { entryQueue.clear(); startEntry = null; {{#configLoggingEnabledForIncremental}} - System.out.println("** observer process (" + entriesToProcess.length + "): " + node + " on " + attribute); + System.out.println("** observer process (entries: " + entriesToProcess.length + "): " + node + " on " + attribute); {{/configLoggingEnabledForIncremental}} for (RagConnectObserverEntry entry : entriesToProcess) { entry.attributeCall.run(); @@ -146,7 +161,7 @@ aspect RagConnectObserver { {{/configLoggingEnabledForIncremental}} // iterate through list, if matching pair. could maybe be more efficient. for (RagConnectObserverEntry entry : observedNodes) { - if (entry.node.equals(node) && entry.attributeString.equals(attribute)) { + if (entry.node.equals(node) && entry.attributeString.equals(attribute) && (!entry.compareParams || java.util.Objects.equals(entry.params, params))) { // hit. call the attribute/nta-token {{#configLoggingEnabledForIncremental}} System.out.println("** observer hit: " + entry.node + " on " + entry.attributeString); @@ -162,13 +177,16 @@ aspect RagConnectObserver { } } - private static RagConnectObserver ASTNode.{{internalRagConnectPrefix}}ObserverInstance; - RagConnectObserver ASTNode.{{internalRagConnectPrefix}}Observer() { - if ({{internalRagConnectPrefix}}ObserverInstance == null) { + private static RagConnectObserver ASTNode.{{observerInstanceFieldName}}; + RagConnectObserver ASTNode.{{observerInstanceSingletonMethodName}}() { + if ({{observerInstanceFieldName}} == null) { // does not matter, which node is used to create the observer as ASTState/tracing is also static - {{internalRagConnectPrefix}}ObserverInstance = new RagConnectObserver(this); + {{observerInstanceFieldName}} = new RagConnectObserver(this); } - return {{internalRagConnectPrefix}}ObserverInstance; + return {{observerInstanceFieldName}}; + } + void ASTNode.{{observerInstanceResetMethodName}}() { + {{observerInstanceFieldName}} = null; } } {{/configIncrementalOptionActive}} diff --git a/ragconnect.base/src/main/resources/sendDefinition.mustache b/ragconnect.base/src/main/resources/sendDefinition.mustache index d48d147571e67f3233da7285310df85d49794942..464b9e3adfb27bd4ef01ea41eb4013a778238f0c 100644 --- a/ragconnect.base/src/main/resources/sendDefinition.mustache +++ b/ragconnect.base/src/main/resources/sendDefinition.mustache @@ -42,11 +42,17 @@ public boolean {{parentTypeName}}.{{connectMethodName}}(String {{connectParamete connectTokenMap.add(this, false, connectToken); {{#configIncrementalOptionActive}} {{!todo maybe getterMethodName needs to be change for indexed send}} - {{internalRagConnectPrefix}}Observer().add(connectToken, this, "{{getterMethodName}}", () -> { - if (this.{{updateMethodName}}({{#IndexBasedListAccess}}index{{/IndexBasedListAccess}})) { - this.{{writeMethodName}}({{#IndexBasedListAccess}}index{{/IndexBasedListAccess}}); + {{observerInstanceSingletonMethodName}}().add( + connectToken, + this, + "{{getterMethodName}}{{#IndexBasedListAccess}}_int{{/IndexBasedListAccess}}", + {{#IndexBasedListAccess}}index,{{/IndexBasedListAccess}} + () -> { + if (this.{{updateMethodName}}({{#IndexBasedListAccess}}index{{/IndexBasedListAccess}})) { + this.{{writeMethodName}}({{#IndexBasedListAccess}}index{{/IndexBasedListAccess}}); + } } - }); + ); {{/configIncrementalOptionActive}} } return success; @@ -61,7 +67,7 @@ public boolean {{parentTypeName}}.{{disconnectMethodName}}(String {{connectParam return false; } {{#configIncrementalOptionActive}} - connectTokens.forEach(token -> {{internalRagConnectPrefix}}Observer().remove(token)); + connectTokens.forEach(token -> {{observerInstanceSingletonMethodName}}().remove(token)); {{/configIncrementalOptionActive}} RagConnectDisconnectHandlerMethod disconnectingMethod; switch (scheme) { diff --git a/ragconnect.tests/build.gradle b/ragconnect.tests/build.gradle index 1d769f36ec96c785ce8eb0cef9df27e7aafaacc8..5a3066474cb8d69575896e328adf586bd9953c16 100644 --- a/ragconnect.tests/build.gradle +++ b/ragconnect.tests/build.gradle @@ -31,6 +31,10 @@ group = 'de.tudresden.inf.st' repositories { mavenCentral() + maven { + name "gitlab-maven" + url "https://git-st.inf.tu-dresden.de/api/v4/groups/jastadd/-/packages/maven" + } } tasks.compileTestJava { options.release.set(11) @@ -39,8 +43,8 @@ tasks.compileTestJava { dependencies { implementation project(':ragconnect.base') - runtimeOnly group: 'org.jastadd', name: 'jastadd', version: '2.3.5-dresden' -// runtimeOnly fileTree(include: ['jastadd2.jar'], dir: '../libs') +// runtimeOnly group: 'org.jastadd', name: 'jastadd', version: '2.3.5-dresden' + runtimeOnly fileTree(include: ['jastadd2.jar'], dir: '../libs') testImplementation group: 'org.junit.jupiter', name: 'junit-jupiter-api', version: '5.4.0' testRuntimeOnly group: 'org.junit.jupiter', name: 'junit-jupiter-engine', version: '5.4.0' diff --git a/ragconnect.tests/src/test/01-input/indexedSend/Test.jadd b/ragconnect.tests/src/test/01-input/indexedSend/Test.jadd index f27b197bc82583b357d24a44e0c3b0025c8a21eb..7ed12deefdb5fa2e3ffa3ce1561fc12c69bdb658 100644 --- a/ragconnect.tests/src/test/01-input/indexedSend/Test.jadd +++ b/ragconnect.tests/src/test/01-input/indexedSend/Test.jadd @@ -12,3 +12,12 @@ aspect MakeCodeWork { return tree; } } + +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(); + } +} diff --git a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/AbstractMqttTest.java b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/AbstractMqttTest.java index db48fdf9a525e242bed1c39f7adf54260668a08a..81071c7381e0768c28390f20dc3e255294f64e01 100644 --- a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/AbstractMqttTest.java +++ b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/AbstractMqttTest.java @@ -1,6 +1,8 @@ package org.jastadd.ragconnect.tests; import defaultOnlyRead.ast.MqttHandler; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.junit.jupiter.api.*; import java.io.IOException; @@ -16,9 +18,10 @@ public abstract class AbstractMqttTest { private static boolean checkDone = false; protected static MqttHandler publisher; + protected Logger logger = LogManager.getLogger(getClass()); @BeforeAll - public static void createPublishAndOnceCheckMqttConnection() { + public static void createPublisherAndCheckMqttConnectionOnce() { boolean checkResult; try { publisher = new MqttHandler("Publisher") @@ -45,9 +48,11 @@ public abstract class AbstractMqttTest { @Tag("mqtt") @Test public final void testCommunicateSendInitialValue() throws IOException, InterruptedException { + logger.debug("Start testCommunicateSendInitialValue"); createModel(); setupReceiverAndConnect(true); + logger.debug("Calling communicateSendInitialValue"); communicateSendInitialValue(); } @@ -60,9 +65,11 @@ public abstract class AbstractMqttTest { @Tag("mqtt") @Test public final void testCommunicateOnlyUpdatedValue() throws IOException, InterruptedException { + logger.debug("Start testCommunicateOnlyUpdatedValue"); createModel(); setupReceiverAndConnect(false); + logger.debug("Calling communicateOnlyUpdatedValue"); communicateOnlyUpdatedValue(); } @@ -80,10 +87,12 @@ public abstract class AbstractMqttTest { /** * Begin with this snippet * <pre> - * model.ragconnectSetupMqttWaitUntilReady(2, TimeUnit.SECONDS); + * {@code + * model.ragconnectSetupMqttWaitUntilReady(2, TimeUnit.SECONDS); * - * handler = new MqttHandler().dontSendWelcomeMessage().setHost(TestUtils.getMqttHost()); - * assertTrue(handler.waitUntilReady(2, TimeUnit.SECONDS)); + * handler = new MqttHandler().dontSendWelcomeMessage().setHost(TestUtils.getMqttHost()); + * assertTrue(handler.waitUntilReady(2, TimeUnit.SECONDS)); + * } * </pre> * * And then add dependencies, initialise receiver, add connections to those receivers, @@ -94,18 +103,21 @@ public abstract class AbstractMqttTest { @AfterEach public void alwaysCloseConnections() { + logger.debug("Closing connections"); closeConnections(); } /** * Write the following snippet (using your correct handler and model): * <pre> + * {@code * if (handler != null) { * handler.close(); * } * if (model != null) { * model.ragconnectCloseConnections(); * } + * } * </pre> */ protected abstract void closeConnections(); diff --git a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/IndexedSendTest.java b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/IndexedSendTest.java index 19d4b5c33b9b2367353179ecf898756423a5eca9..d0847805a6c18e6cde3be97165962d69b8c60ff9 100644 --- a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/IndexedSendTest.java +++ b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/IndexedSendTest.java @@ -15,7 +15,8 @@ import static org.assertj.core.groups.Tuple.tuple; import static org.awaitility.Awaitility.await; import static org.jastadd.ragconnect.tests.TestUtils.mqttUri; import static org.jastadd.ragconnect.tests.TestUtils.waitForMqtt; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; /** * Test case "forwarding". @@ -38,6 +39,7 @@ public class IndexedSendTest extends AbstractMqttTest { /** Use initially created members as values in {@link #check} todo link correct method */ private static final String INITIAL_VALUE = "initial" + rand.nextInt(100); + private static final Logger logger = LogManager.getLogger(IndexedSendTest.class); private MqttHandler handler; private ReceiverData data; @@ -101,7 +103,6 @@ public class IndexedSendTest extends AbstractMqttTest { @Override protected void communicateSendInitialValue() throws IOException, InterruptedException { - // TODO check // Sink.ManyA <-- Root.MultipleA // Sink.ManyAWithSuffix <-- Root.MultipleAWithSuffix checkNoWait(4, tuple("am0", "am1"), tuple("am0post", "am1post")); @@ -140,7 +141,6 @@ public class IndexedSendTest extends AbstractMqttTest { // TODO check assertEquals(listA0.getValue(), senderRoot._ragconnect_MultipleA(0).getValue()); - System.out.println("before changing value"); listA0.setValue("changedValue"); assertEquals(listA0.getValue(), senderRoot._ragconnect_MultipleA(0).getValue()); check(1, tuple("changedValue"), tuple()); @@ -244,16 +244,15 @@ public class IndexedSendTest extends AbstractMqttTest { private static class ReceiverData { int numberOfValues = 0; } -} - -class PrintingReceiver implements ASTState.Trace.Receiver { + static class PrintingReceiver implements ASTState.Trace.Receiver { - static Logger logger = LogManager.getLogger(PrintingReceiver.class); + static Logger logger = LogManager.getLogger(PrintingReceiver.class); - @Override - public void accept(ASTState.Trace.Event event, ASTNode node, String attribute, Object params, Object value) { - logger.info("event: {}, node: {}, attribute: {}, params: {}, value: {}", - event, node, attribute, params, value); + @Override + public void accept(ASTState.Trace.Event event, @SuppressWarnings("rawtypes") ASTNode node, String attribute, Object params, Object value) { + logger.info("event: {}, node: {}, attribute: {}, params: {}, value: {}", + event, node, attribute, params, value); + } } } diff --git a/ragconnect.tests/src/test/resources/log4j2.xml b/ragconnect.tests/src/test/resources/log4j2.xml index f5ba31da2ac541c6bca724ef1a4fb5cef2cbdb71..653d6c357cef6a51c43a2fe9e54d4a3e86148abf 100644 --- a/ragconnect.tests/src/test/resources/log4j2.xml +++ b/ragconnect.tests/src/test/resources/log4j2.xml @@ -2,7 +2,7 @@ <Configuration status="INFO"> <Appenders> <Console name="Console" target="SYSTEM_OUT"> - <PatternLayout pattern="%d{HH:mm:ss.SSS} %-5level [%t] %logger{20} - %msg%n"/> + <PatternLayout pattern="%highlight{%d{HH:mm:ss.SSS} %-5level [%t] %logger{20} - %msg%n}" disableAnsi="false"/> </Console> </Appenders> <Loggers>