diff --git a/ragconnect.base/src/main/jastadd/Intermediate.jadd b/ragconnect.base/src/main/jastadd/Intermediate.jadd index 69ff5d8932cd33a97e44b2fa3562b13cd52617f2..f7a12a6635641baf5adfb1d2b36fc02bd3f832af 100644 --- a/ragconnect.base/src/main/jastadd/Intermediate.jadd +++ b/ragconnect.base/src/main/jastadd/Intermediate.jadd @@ -15,6 +15,10 @@ aspect SharedMustache { syn boolean RagConnect.configExperimentalJastAdd329() = getConfiguration().getExperimentalJastAdd329(); + syn boolean RagConnect.configEvaluationCounter() = getConfiguration().getEvaluationCounter(); + + syn String RagConnect.evaluationCounterVariable() = internalRagConnectPrefix() + "evaluationCounter"; + syn String RagConnect.internalRagConnectPrefix() = "_ragconnect_"; syn String RagConnect.logDebug() = logStatement("debug"); @@ -99,6 +103,10 @@ aspect MustacheHandler { // === RagConnect === syn String RagConnect.closeMethodName() = "ragconnectCloseConnections"; + syn String RagConnect.evaluationCounterInnerClass() = internalRagConnectPrefix() + "Counter"; + + syn String RagConnect.evaluationCounterSummaryMethodName() = "ragconnectEvaluationCounterSummary"; + syn boolean RagConnect.hasRootTypeComponents() = !rootTypeComponents().isEmpty(); syn List<TypeComponent> RagConnect.rootTypeComponents() { diff --git a/ragconnect.base/src/main/jastadd/Mappings.jrag b/ragconnect.base/src/main/jastadd/Mappings.jrag index 9fd55aa4cad7eb22f75913f7942b547ca91ddec2..e92fc4fa27569ee353b0c16798e2ce3b7eb07a29 100644 --- a/ragconnect.base/src/main/jastadd/Mappings.jrag +++ b/ragconnect.base/src/main/jastadd/Mappings.jrag @@ -243,7 +243,7 @@ aspect Mappings { return getEndpointTarget().isTypeEndpointTarget() && typeIsList() && !getIndexBasedListAccess() ? ragconnect().defaultBytesToListMapping(typeDecl.getName()) : ragconnect().defaultBytesToTreeMapping(typeDecl.getName()); } catch (Exception ignore) { } - System.err.println("Could not find suitable default mapping for " + targetTypeName() + " on " + this); + System.err.println("Could not find suitable default receive mapping for " + targetTypeName() + " on " + this); return null; } } @@ -287,7 +287,7 @@ aspect Mappings { } catch (Exception ignore) { // exception should be logged to debug/fine } - System.err.println("Could not find suitable default mapping for " + targetTypeName() + " on " + this); + System.err.println("Could not find suitable default send mapping for " + targetTypeName() + " on " + this); return null; } } diff --git a/ragconnect.base/src/main/jastadd/RagConnect.relast b/ragconnect.base/src/main/jastadd/RagConnect.relast index cf12d73202b144240ed929ba499d99a8e797aa89..df50e3dab1dde22e38bca0588396de647b49c8cb 100644 --- a/ragconnect.base/src/main/jastadd/RagConnect.relast +++ b/ragconnect.base/src/main/jastadd/RagConnect.relast @@ -40,5 +40,6 @@ Configuration ::= <JastAddOpt:String> <IncrementalOptionActive:boolean> <CacheAllOptionActive:boolean> +<EvaluationCounter:boolean> <ExperimentalJastAdd329:boolean>; rel Configuration.RootNode -> TypeDecl ; 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 e255b3b49c8a308661cf248a3f00e8ca41b96869..772cebba17f57474352176a38a06a1b19b039973 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 @@ -31,6 +31,7 @@ public class Compiler extends AbstractCompiler { private BooleanOption optionLogIncremental; private ValueOption optionLogTarget; private BooleanOption optionExperimentalJastAdd329; + private BooleanOption optionEvaluationCounter; private static final String OPTION_LOGGING_TARGET_CONSOLE = "console"; private static final String OPTION_LOGGING_TARGET_SLF4J = "slf4j"; @@ -206,6 +207,9 @@ public class Compiler extends AbstractCompiler { optionExperimentalJastAdd329 = addOption( new BooleanOption("experimental-jastadd-329", "Use trace events INC_FLUSH_START and INC_FLUSH_END (JastAdd issue #329).") .defaultValue(false)); + optionEvaluationCounter = addOption( + new BooleanOption("evaluationCounter", "Enable counters for evaluation.") + .defaultValue(false)); } private RagConnect parseProgram(Collection<String> files) throws CompilerException { @@ -301,13 +305,14 @@ public class Compiler extends AbstractCompiler { * Set all configuration values. * @param ragConnect the RagConnect instance to set configuration values */ - private void setConfiguration(RagConnect ragConnect) { + private void setConfiguration(RagConnect ragConnect) throws CompilerException { ragConnect.setConfiguration(new Configuration()); ragConnect.getConfiguration().setLoggingEnabledForReads(optionLogReads.value()); ragConnect.getConfiguration().setLoggingEnabledForWrites(optionLogWrites.value()); ragConnect.getConfiguration().setLoggingEnabledForIncremental(optionLogIncremental.value()); ragConnect.getConfiguration().setLoggingTarget(optionLogTarget.value()); ragConnect.getConfiguration().setExperimentalJastAdd329(optionExperimentalJastAdd329.value()); + ragConnect.getConfiguration().setEvaluationCounter(optionEvaluationCounter.value()); // reuse "--incremental" and "--trace=flush" options of JastAdd boolean incrementalOptionActive = this.getConfiguration().incremental() && this.getConfiguration().traceFlush(); @@ -328,7 +333,7 @@ public class Compiler extends AbstractCompiler { rootNode = ragConnect.getProgram().resolveTypeDecl(optionRootNode.value()); } catch (RuntimeException re) { // root node was not found - throw new RuntimeException("Could not resolve root node '" + optionRootNode.value() + "'!", re); + throw new CompilerException("Could not resolve root node '" + optionRootNode.value() + "'!", re); } ragConnect.getConfiguration().setRootNode(rootNode); diff --git a/ragconnect.base/src/main/resources/handleUri.mustache b/ragconnect.base/src/main/resources/handleUri.mustache index 2312cd6983822c0e0b2987459047b7ca2a277fc2..efb4aacfbf1e003dcb540ba77bdaca68a8aed6cd 100644 --- a/ragconnect.base/src/main/resources/handleUri.mustache +++ b/ragconnect.base/src/main/resources/handleUri.mustache @@ -10,14 +10,14 @@ try { return false; } if (scheme == null || scheme.isBlank()) { - {{logError}}("Missing or empty scheme in " + uri); + {{logError}}("Missing or empty scheme in {{log_}}", uri); return false; } if (host == null || host.isBlank()) { - {{logError}}("Missing or empty host in " + uri); + {{logError}}("Missing or empty host in {{log_}}", uri); return false; } if (path == null || path.isBlank()) { - {{logError}}("Missing or empty path in " + uri); + {{logError}}("Missing or empty path in {{log_}}", uri); return false; } diff --git a/ragconnect.base/src/main/resources/mappingApplication.mustache b/ragconnect.base/src/main/resources/mappingApplication.mustache index 482d54783782ada82cacd9ce75b81d30c1e21e68..efd17af4e8947232f44b93c3749d86b1e637dd46 100644 --- a/ragconnect.base/src/main/resources/mappingApplication.mustache +++ b/ragconnect.base/src/main/resources/mappingApplication.mustache @@ -1,6 +1,12 @@ +{{#configEvaluationCounter}} + {{evaluationCounterVariable}}.incrementCall("{{parentTypeName}}", "{{entityName}}"); +{{/configEvaluationCounter}} {{#Send}} {{^PrimitiveType}} if ({{firstInputVarName}} == null) { + {{#configEvaluationCounter}} + {{evaluationCounterVariable}}.incrementFirstNull("{{parentTypeName}}", "{{entityName}}"); + {{/configEvaluationCounter}} {{preemptiveReturn}} } {{/PrimitiveType}} @@ -12,13 +18,22 @@ try { {{/innerMappingDefinitions}} } catch (RagConnectRejectMappingException e) { // do not print message in case of rejection + {{#configEvaluationCounter}} + {{evaluationCounterVariable}}.incrementReject("{{parentTypeName}}", "{{entityName}}"); + {{/configEvaluationCounter}} {{preemptiveReturn}} } catch (Exception e) { e.printStackTrace(); + {{#configEvaluationCounter}} + {{evaluationCounterVariable}}.incrementException("{{parentTypeName}}", "{{entityName}}"); + {{/configEvaluationCounter}} {{preemptiveReturn}} } {{^AlwaysApply}} if ({{{condition}}}) { + {{#configEvaluationCounter}} + {{evaluationCounterVariable}}.incrementSkip("{{parentTypeName}}", "{{entityName}}"); + {{/configEvaluationCounter}} {{preemptiveReturn}} } {{/AlwaysApply}} diff --git a/ragconnect.base/src/main/resources/mappingDefinition.mustache b/ragconnect.base/src/main/resources/mappingDefinition.mustache index 5be93e4bdc91ca2eaa54f364eb8e5f7c069da2a5..b6a035eb41b9d3f877b731293203de399399e6c0 100644 --- a/ragconnect.base/src/main/resources/mappingDefinition.mustache +++ b/ragconnect.base/src/main/resources/mappingDefinition.mustache @@ -1,3 +1,3 @@ -protected static {{{toType}}} ASTNode.{{methodName}}({{{fromType}}} {{FromVariableName}}) throws Exception { +protected {{{toType}}} ASTNode.{{methodName}}({{{fromType}}} {{FromVariableName}}) throws Exception { {{{Content}}} } diff --git a/ragconnect.base/src/main/resources/ragconnect.mustache b/ragconnect.base/src/main/resources/ragconnect.mustache index ac7b906067f5f9ecd4a2bf0c51a368797f55dfbc..7e054a4927b527e6d32a30e32a6bffa71d86b97f 100644 --- a/ragconnect.base/src/main/resources/ragconnect.mustache +++ b/ragconnect.base/src/main/resources/ragconnect.mustache @@ -285,3 +285,98 @@ aspect RagConnectObserver { } } {{/configIncrementalOptionActive}} + +aspect EvaluationCounter { + public String ASTNode.{{evaluationCounterSummaryMethodName}}() { + {{#configEvaluationCounter}} + return {{evaluationCounterVariable}}.summary(); + {{/configEvaluationCounter}} + {{^configEvaluationCounter}} + String message = "Option --evaluationCounter was not set. No Summary available"; + {{logWarn}}(message); + return message; + {{/configEvaluationCounter}} + } +{{#configEvaluationCounter}} + static EvaluationCounter ASTNode.{{evaluationCounterVariable}} = new EvaluationCounter(); + + public class EvaluationCounter { + private java.util.Map<String, java.util.Map<String, {{evaluationCounterInnerClass}}>> counters = new java.util.HashMap<>(); + private final java.util.function.Function<? super String, ? extends java.util.Map<String, {{evaluationCounterInnerClass}}>> parentAbsent = key -> { + return new java.util.HashMap<>(); + }; + private final java.util.function.Function<? super String, ? extends {{evaluationCounterInnerClass}}> entityAbsent = key -> { + return new {{evaluationCounterInnerClass}}(); + }; + + public void incrementReceive(String parentTypeName, String entityName) { + getCounter(parentTypeName, entityName).receive += 1; + } + + public void incrementSend(String parentTypeName, String entityName) { + getCounter(parentTypeName, entityName).send += 1; + } + + public void incrementCall(String parentTypeName, String entityName) { + getCounter(parentTypeName, entityName).call += 1; + } + + public void incrementFirstNull(String parentTypeName, String entityName) { + getCounter(parentTypeName, entityName).firstNull += 1; + } + + public void incrementSkip(String parentTypeName, String entityName) { + getCounter(parentTypeName, entityName).skip += 1; + } + + public void incrementException(String parentTypeName, String entityName) { + getCounter(parentTypeName, entityName).exception += 1; + } + + public void incrementReject(String parentTypeName, String entityName) { + getCounter(parentTypeName, entityName).reject += 1; + } + + public String summary() { + StringBuilder sb = new StringBuilder(); + // header + sb.append("parentTypeName,entityName,receive,send,call,firstNull,skip,exception,reject").append("\n"); + // values + java.util.Set<String> sortedParentTypes = new java.util.TreeSet<>(counters.keySet()); + for (String parentType : sortedParentTypes) { + java.util.Set<String> sortedEntityNames = new java.util.TreeSet<>(counters.get(parentType).keySet()); + for (String entityName : sortedEntityNames) { + {{evaluationCounterInnerClass}} count = getCounter(parentType, entityName); + java.util.StringJoiner sj = new java.util.StringJoiner(",", "", "\n"); + sj.add(parentType) + .add(entityName) + .add(Integer.toString(count.receive)) + .add(Integer.toString(count.send)) + .add(Integer.toString(count.call)) + .add(Integer.toString(count.firstNull)) + .add(Integer.toString(count.skip)) + .add(Integer.toString(count.exception)) + .add(Integer.toString(count.reject)) + ; + sb.append(sj); + } + } + return sb.toString(); + } + + private {{evaluationCounterInnerClass}} getCounter(String parentTypeName, String entityName) { + return counters.computeIfAbsent(parentTypeName, parentAbsent).computeIfAbsent(entityName, entityAbsent); + } + } + + class {{evaluationCounterInnerClass}} { + int receive = 0; + int send = 0; + int call = 0; + int firstNull = 0; + int skip = 0; + int exception = 0; + int reject = 0; + } +{{/configEvaluationCounter}} +} diff --git a/ragconnect.base/src/main/resources/receiveDefinition.mustache b/ragconnect.base/src/main/resources/receiveDefinition.mustache index 28a9bb5feae6e8305744ab3bfdd224f7daecc31b..81b0ecbbd96aa40155ec8f396549a4af2160b64e 100644 --- a/ragconnect.base/src/main/resources/receiveDefinition.mustache +++ b/ragconnect.base/src/main/resources/receiveDefinition.mustache @@ -25,6 +25,9 @@ private int {{parentTypeName}}.{{resolveInListMethodName}}(String topic) { */ public boolean {{parentTypeName}}.{{connectMethodName}}(String {{connectParameterName}}{{#typeIsList}}{{#IndexBasedListAccess}}{{^WithAdd}}, int index{{/WithAdd}}{{/IndexBasedListAccess}}{{/typeIsList}}) throws java.io.IOException { java.util.function.BiConsumer<String, byte[]> consumer = (topic, message) -> { + {{#configEvaluationCounter}} + {{evaluationCounterVariable}}.incrementReceive("{{parentTypeName}}", "{{entityName}}"); + {{/configEvaluationCounter}} {{> mappingApplication}} {{#configLoggingEnabledForReads}} {{logDebug}}("[Receive] {{log_}} -> {{entityName}} = {{log_}}", {{connectParameterName}}, {{lastResult}}); @@ -70,6 +73,9 @@ public boolean {{parentTypeName}}.{{connectMethodName}}(String {{connectParamete */ public boolean {{parentTypeName}}.{{connectMethodName}}(String {{connectParameterName}}) throws java.io.IOException { java.util.function.BiConsumer<String, byte[]> consumer = (topic, message) -> { + {{#configEvaluationCounter}} + {{evaluationCounterVariable}}.incrementReceive("{{parentTypeName}}", "{{entityName}}"); + {{/configEvaluationCounter}} int index = {{resolveInListMethodName}}(topic); {{> mappingApplication}} {{#configLoggingEnabledForReads}} diff --git a/ragconnect.base/src/main/resources/sendDefinition.mustache b/ragconnect.base/src/main/resources/sendDefinition.mustache index 082997361ba3ec4c08a28f31eee827677454115d..abcbdffe2d4f7aaff5dfce8700cb11e72ec8d83d 100644 --- a/ragconnect.base/src/main/resources/sendDefinition.mustache +++ b/ragconnect.base/src/main/resources/sendDefinition.mustache @@ -134,14 +134,25 @@ protected boolean {{parentTypeName}}.{{updateMethodName}}({{#IndexBasedListAcces {{> mappingApplication}} {{lastValueSetter}}({{#IndexBasedListAccess}}index, {{/IndexBasedListAccess}}{{lastResult}}); // normally we would return true here. unless no connect method was called so far to initialize {{senderName}} yet + {{#configEvaluationCounter}} + if ({{senderName}} == null) { + {{evaluationCounterVariable}}.incrementSkip("{{parentTypeName}}", "{{entityName}}"); + } + {{/configEvaluationCounter}} return {{senderName}} != null; } protected void {{parentTypeName}}.{{writeMethodName}}({{#IndexBasedListAccess}}int index{{/IndexBasedListAccess}}) { + {{#configEvaluationCounter}} + {{evaluationCounterVariable}}.incrementSend("{{parentTypeName}}", "{{entityName}}"); + {{/configEvaluationCounter}} {{senderName}}.run({{#IndexBasedListAccess}}index{{/IndexBasedListAccess}}); } protected void {{parentTypeName}}.{{writeMethodName}}({{#IndexBasedListAccess}}int index, {{/IndexBasedListAccess}}RagConnectToken token) { + {{#configEvaluationCounter}} + {{evaluationCounterVariable}}.incrementSend("{{parentTypeName}}", "{{entityName}}"); + {{/configEvaluationCounter}} {{senderName}}.run({{#IndexBasedListAccess}}index, {{/IndexBasedListAccess}}token); } diff --git a/ragconnect.tests/build.gradle b/ragconnect.tests/build.gradle index 719b55dfb92eb09ebfae7fce2aa51b7be543487e..42c2cf660f7893a7990792b8e8c89f12d96533a1 100644 --- a/ragconnect.tests/build.gradle +++ b/ragconnect.tests/build.gradle @@ -651,7 +651,7 @@ task compileRelationIncremental(type: RagConnectTest) { inputFiles = [file('src/test/01-input/relation/Test.relast'), file('src/test/01-input/relation/Test.connect')] rootNode = 'Root' - extraOptions = defaultRagConnectOptionsAnd(['--experimental-jastadd-329']) + extraOptions = defaultRagConnectOptionsAnd(['--experimental-jastadd-329', '--evaluationCounter']) } relast { useJastAddNames = true @@ -677,7 +677,7 @@ task compileJavaIncremental(type: RagConnectTest) { logWrites = true logIncremental = true protocols = ['java'] - extraOptions = defaultRagConnectOptionsAnd(['--experimental-jastadd-329']) + extraOptions = defaultRagConnectOptionsAnd(['--experimental-jastadd-329', '--evaluationCounter']) } relast { useJastAddNames = true 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 index 5d6e49248e8e025c3f4ab383637dedb335fd8e7a..fa4319cccf9844dabbb8068b365661a36e7cbd11 100644 --- a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/JavaTest.java +++ b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/JavaTest.java @@ -106,6 +106,8 @@ public class JavaTest { handler.push(TOPIC_RECEIVE_TOKEN, ExposingASTNode.INSTANCE.stringToBytes("7")); checker.put(TOPIC_RECEIVE_TOKEN, "7").check(); + + System.out.println(model.ragconnectEvaluationCounterSummary()); } @Test diff --git a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/relation/RelationTest.java b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/relation/RelationTest.java index d9c4979c0d6c206c642ca8aa9dbcf780e3fbf370..a245387637d4a61f0e6ff38ab1389e340f4a7093 100644 --- a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/relation/RelationTest.java +++ b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/relation/RelationTest.java @@ -616,6 +616,7 @@ public class RelationTest extends AbstractMqttTest { biB(3).getInner().setInnerValue("inner-bi-b3"); checker.check(); + System.out.println(model.ragconnectEvaluationCounterSummary()); } private void assertNullOrA(String expectedValue, A actual, String alias) {