From a14db74f22d8e8e5eaa765928a3c01251c5324a3 Mon Sep 17 00:00:00 2001
From: rschoene <rene.schoene@tu-dresden.de>
Date: Tue, 30 Nov 2021 15:45:11 +0100
Subject: [PATCH] working on concise grammar

- auto-format aspects
- some refactorings for #39
- unified handler setup
- cleanup
- update extending.md for new process (fewer steps now)
---
 pages/docs/extending.md                       |  30 +--
 .../src/main/jastadd/Analysis.jrag            |  77 ++-----
 .../src/main/jastadd/Configuration.jadd       |  18 +-
 ragconnect.base/src/main/jastadd/Errors.jrag  |  60 ++---
 .../src/main/jastadd/Handlers.jrag            |  14 ++
 .../src/main/jastadd/NameResolution.jrag      |  40 +++-
 .../src/main/jastadd/Navigation.jrag          |   4 +-
 .../src/main/jastadd/RagConnect.relast        |  39 ++--
 .../src/main/jastadd/RagConnect_old.relast    |  35 ---
 .../main/jastadd/intermediate/Generation.jadd |  25 +--
 .../main/jastadd/intermediate/Mappings.jrag   | 212 ++++++++++--------
 .../src/main/jastadd/scanner/Keywords.flex    |   2 -
 .../jastadd/ragconnect/compiler/Compiler.java |  97 ++++----
 .../src/main/resources/MqttHandler.jadd       |  17 +-
 .../src/main/resources/handler.mustache       |  11 +-
 .../src/main/resources/mqtt.mustache          |   5 -
 .../src/main/resources/ragconnect.mustache    |   1 -
 .../main/resources/receiveDefinition.mustache |  32 ++-
 .../main/resources/sendDefinition.mustache    |  32 ++-
 19 files changed, 388 insertions(+), 363 deletions(-)
 create mode 100644 ragconnect.base/src/main/jastadd/Handlers.jrag
 delete mode 100644 ragconnect.base/src/main/jastadd/RagConnect_old.relast
 delete mode 100644 ragconnect.base/src/main/resources/mqtt.mustache

diff --git a/pages/docs/extending.md b/pages/docs/extending.md
index 61cabca..14fa8c8 100644
--- a/pages/docs/extending.md
+++ b/pages/docs/extending.md
@@ -1,32 +1,24 @@
 # Extending `RagConnect`
 
-To add a new communication protocol, the following locations have to be changed (replace `ABC` and `abc` with the name of the protocol):
+To add a new communication protocol, the following locations have to be changed (replace `ABC` and `abc` with the name of the protocol).
 
-Within `ragconnect.base/src/main/resources`:
+### Within `ragconnect.base/src/main/resources`
 
 {% raw %}
-- Add a new handler `ABCHandler`, if appropriate, similar to the existing handlers
-    - If further methods are needed for handler initialization, add a new template `abc.mustache` containing those procedures. Add `{{#usesABC}}{{> abc}}{{/usesABC}}` at the top of `ragconnect.mustache` to use this template
-- In `receiveDefinition.mustache` and `sendDefinition.mustache`: add a new case in the switch statement defining the logic to happen for both definitions. If the new protocol is close to a PUSH semantic, follow `mqtt`. If it is closer to PULL semantic, follow `rest`.
+- Add a new handler `ABCHandler.jadd`, similar to the existing handlers. 
+- In `handler.mustache`, add further methods if needed for handler usage in the application code (similar to `{{rootNodeName}}.{{SetupWaitUntilReadyMethodName}}` for `mqtt`)
+- In `receiveDefinition.mustache` and `sendDefinition.mustache`: add a new case in the switch statements defining the logic to happen upon connect and disconnect for both definitions. If the new protocol is close to a PUSH semantic, follow `mqtt`. If it is closer to PULL semantic, follow `rest`.
 {% endraw %}
 
-Within `ragconnect.base/src/main/jastadd`:
+### Within `ragconnect.base/src/main/jastadd`
 
-- In `backend/Configuration`:
-    - Add a new static boolean flag `usesABC` to indicate whether the protocol is used
-- In `backend/Generation`:
-    - Add new attributes for type `MRagConnect` for handler attribute and handler field, if needed
-    - Add attributes for newly introduced references in changed mustache templates, if any
-    - Add a newly constructed handler within the definition of `RagConnect.toMustache` with the needed fields (class name, construction snippet, handler attribute, handler field, the boolean flag you just added to Configuration)
-- In `backend/MustacheNodesToYAML`:
-    - Add key-value-pair for `usesABC` (and handler, if any)
-    - Add key-value-pairs for newly introduced referemces in changed mustache templates, if any
+In `Handlers.jrag`: Add a new attribute `RagConnect.abcHandler()` returning the resolved handler
 
-In `ragconnect.base/src/main/java/org/jastadd/ragconnect/compiler/Compiler.java`:
+### Within `ragconnect.base/src/main/java/org/jastadd/ragconnect/compiler`
 
+In `Compiler.java`:
 - Add a new choice for `--protocols` similar to the existing ones
-- Set the flag `usesABC` if the choice is given.
-- Add code to add the handler to the list `handlers` if the choice is given, i.e., if `ASTNode.usesABC`
+- Add a newly constructed handler in `setConfiguration`  with the needed fields (definition file name within `resources` directory, commonly `ABCHandler.jadd`; class name of the handler; unique name for the protocol; whether the handler is used, i.e., if it was given in `--protocols`)
 
 Furthermore, new test cases are appreciated, see [below](#writing-tests).
 
@@ -58,7 +50,7 @@ All tests are required to run both locally, and within the CI.
 
 ### build.gradle
 
-Use the [PreprocessorPlugin][preprocessor-plugin], the build process can be written concisely in three parts per task:
+Using the [PreprocessorPlugin][preprocessor-plugin], the build process can be written concisely in three parts per task:
 
 ```groovy
 task compileTreeAllowedTokens(type: RagConnectTest) {
diff --git a/ragconnect.base/src/main/jastadd/Analysis.jrag b/ragconnect.base/src/main/jastadd/Analysis.jrag
index 88d2ffa..c968752 100644
--- a/ragconnect.base/src/main/jastadd/Analysis.jrag
+++ b/ragconnect.base/src/main/jastadd/Analysis.jrag
@@ -1,55 +1,16 @@
 aspect Analysis {
-  // --- lookupTokenEndpointDefinition ---
-  inh java.util.List<EndpointDefinition> EndpointDefinition.lookupTokenEndpointDefinitions(TokenComponent token);
-  inh java.util.List<EndpointDefinition> EndpointTarget.lookupTokenEndpointDefinitions(TokenComponent token);
-  eq RagConnect.getConnectSpecificationFile().lookupTokenEndpointDefinitions(TokenComponent token) = lookupTokenEndpointDefinitions(token);
-  syn java.util.List<EndpointDefinition> RagConnect.lookupTokenEndpointDefinitions(TokenComponent token) {
-    java.util.List<EndpointDefinition> result = new java.util.ArrayList<>();
-    for (EndpointTarget target : allEndpointTargetList()) {
-      if (target.isTokenEndpointTarget() && target.asTokenEndpointTarget().getToken().equals(token)) {
-        result.add(target.containingEndpointDefinition());
-      }
-    }
-    return result;
-  }
-
-  // --- lookupTypeEndpointDefinition ---
-  inh java.util.List<EndpointDefinition> EndpointDefinition.lookupTypeEndpointDefinitions(TypeComponent type);
-  inh java.util.List<EndpointDefinition> EndpointTarget.lookupTypeEndpointDefinitions(TypeComponent type);
-  eq RagConnect.getConnectSpecificationFile().lookupTypeEndpointDefinitions(TypeComponent type) = lookupTypeEndpointDefinitions(type);
-  syn java.util.List<EndpointDefinition> RagConnect.lookupTypeEndpointDefinitions(TypeComponent type) {
-    java.util.List<EndpointDefinition> result = new java.util.ArrayList<>();
-    for (EndpointTarget target : allEndpointTargetList()) {
-      if (target.isTypeEndpointTarget() && target.asTypeEndpointTarget().getType().equals(type)) {
-        result.add(target.containingEndpointDefinition());
-      }
-    }
-    return result;
-  }
-
-  // --- lookupDependencyDefinition ---
-  inh DependencyDefinition DependencyDefinition.lookupDependencyDefinition(TypeDecl source, String id);
-  eq RagConnect.getConnectSpecificationFile().lookupDependencyDefinition(TypeDecl source, String id) {
-    for (DependencyDefinition def : allDependencyDefinitionList()) {
-      if (def.getID().equals(id) && def.getSource().containingTypeDecl().equals(source)) {
-        return def;
-      }
-    }
-    return null;
-  }
-
   // --- isAlreadyDefined ---
   syn boolean EndpointDefinition.isAlreadyDefined() = getEndpointTarget().isAlreadyDefined();
   syn boolean EndpointTarget.isAlreadyDefined();
   eq TokenEndpointTarget.isAlreadyDefined() {
     return lookupTokenEndpointDefinitions(getToken()).stream()
-      .filter(containingEndpointDefinition()::matchesType)
-      .count() > 1;
+        .filter(containingEndpointDefinition()::matchesType)
+        .count() > 1;
   }
   eq TypeEndpointTarget.isAlreadyDefined() {
     return lookupTypeEndpointDefinitions(getType()).stream()
-      .filter(containingEndpointDefinition()::matchesType)
-      .count() > 1;
+        .filter(containingEndpointDefinition()::matchesType)
+        .count() > 1;
   }
   syn boolean DependencyDefinition.isAlreadyDefined() = lookupDependencyDefinition(getSource().containingTypeDecl(), getID()) != this;
 
@@ -60,7 +21,9 @@ aspect Analysis {
   syn boolean MappingDefinitionType.assignableTo(JavaTypeUse target);
   eq JavaMappingDefinitionType.assignableTo(JavaTypeUse target) = getType().assignableTo(target);
   eq JavaArrayMappingDefinitionType.assignableTo(JavaTypeUse target) {
-    if (!target.getName().endsWith("[]")) { return false; }
+    if (!target.getName().endsWith("[]")) {
+      return false;
+    }
     return getType().assignableTo(new SimpleJavaTypeUse(target.getName().replace("[]", "")));
   }
   syn boolean JavaTypeUse.assignableTo(JavaTypeUse target) {
@@ -68,22 +31,30 @@ aspect Analysis {
     return target.primitivePrettyPrint().equals(this.primitivePrettyPrint());
   }
   syn String JavaTypeUse.primitivePrettyPrint() {
-    switch(getName()) {
+    switch (getName()) {
       case "boolean":
-      case "Boolean": return "boolean";
+      case "Boolean":
+        return "boolean";
       case "int":
-      case "Integer": return "int";
+      case "Integer":
+        return "int";
       case "short":
-      case "Short": return "short";
+      case "Short":
+        return "short";
       case "long":
-      case "Long": return "long";
+      case "Long":
+        return "long";
       case "float":
-      case "Float": return "float";
+      case "Float":
+        return "float";
       case "double":
-      case "Double": return "double";
+      case "Double":
+        return "double";
       case "char":
-      case "Character": return "char";
-      default: return getName();
+      case "Character":
+        return "char";
+      default:
+        return getName();
     }
   }
 
diff --git a/ragconnect.base/src/main/jastadd/Configuration.jadd b/ragconnect.base/src/main/jastadd/Configuration.jadd
index 8fd0e64..72a3e9d 100644
--- a/ragconnect.base/src/main/jastadd/Configuration.jadd
+++ b/ragconnect.base/src/main/jastadd/Configuration.jadd
@@ -1,11 +1,9 @@
-aspect Configuration {
-  public static boolean ASTNode.loggingEnabledForReads = false;
-  public static boolean ASTNode.loggingEnabledForWrites = false;
-  public static boolean ASTNode.loggingEnabledForIncremental = false;
-  public static TypeDecl ASTNode.rootNode;
-  public static String ASTNode.JastAddList = "List";
-  public static boolean ASTNode.usesMqtt;
-  public static boolean ASTNode.usesRest;
-  public static boolean ASTNode.incrementalOptionActive;
-  public static boolean ASTNode.experimentalJastAdd329;
+aspect ConfigurationShortcuts {
+  syn boolean RagConnect.loggingEnabledForReads() = getConfiguration().getLoggingEnabledForReads();
+  syn boolean RagConnect.loggingEnabledForWrites() = getConfiguration().getLoggingEnabledForWrites();
+  syn boolean RagConnect.loggingEnabledForIncremental() = getConfiguration().getLoggingEnabledForIncremental();
+  syn TypeDecl RagConnect.rootNode() = getConfiguration().getRootNode();
+  syn String RagConnect.JastAddList() = getConfiguration().getJastAddList();
+  syn boolean RagConnect.incrementalOptionActive() = getConfiguration().getIncrementalOptionActive();
+  syn boolean RagConnect.experimentalJastAdd329() = getConfiguration().getExperimentalJastAdd329();
 }
diff --git a/ragconnect.base/src/main/jastadd/Errors.jrag b/ragconnect.base/src/main/jastadd/Errors.jrag
index 610f994..61f9665 100644
--- a/ragconnect.base/src/main/jastadd/Errors.jrag
+++ b/ragconnect.base/src/main/jastadd/Errors.jrag
@@ -3,36 +3,37 @@ aspect Errors {
     [new TreeSet<ErrorMessage>()]
     root RagConnect;
 
-    EndpointDefinition contributes error("Endpoint definition already defined for " + parentTypeName() + "." + entityName())
-      when isAlreadyDefined()
-      to RagConnect.errors();
+  EndpointDefinition contributes error("Endpoint definition already defined for " + parentTypeName() + "." + entityName())
+    when isAlreadyDefined()
+    to RagConnect.errors();
 
-    EndpointDefinition contributes error("Receiving target token must not be an NTA token!")
-      when !getSend() && getEndpointTarget().isTokenEndpointTarget() && token().getNTA()
-      to RagConnect.errors();
+  EndpointDefinition contributes error("Receiving target token must not be an NTA token!")
+    when !getSend() && getEndpointTarget().isTokenEndpointTarget() && token().getNTA()
+    to RagConnect.errors();
 
-    // if first mapping is null, then suitableDefaultMapping() == null
-    EndpointDefinition contributes error("No suitable default mapping found for type " +
+  // if first mapping is null, then suitableDefaultMapping() == null
+  EndpointDefinition contributes error("No suitable default mapping found for type " +
       ((getMappingList().isEmpty())
-        ? token().effectiveJavaTypeUse().prettyPrint()
-        : getMappingList().get(0).getFromType().prettyPrint()))
-      when !getSend() && getEndpointTarget().isTokenEndpointTarget() && effectiveMappings().get(0) == null
-      to RagConnect.errors();
-
-    // TODO copy for type endpoint target
-    EndpointDefinition contributes error("to-type of last mapping (" + effectiveMappings().get(effectiveMappings().size() - 1).getToType().prettyPrint() + ") not assignable to type of the token (" + token().effectiveJavaTypeUse().prettyPrint() + ")!")
-      when !getSend() && getEndpointTarget().isTokenEndpointTarget() &&
-           !effectiveMappings().get(effectiveMappings().size() - 1).getToType().assignableTo(
-            token().effectiveJavaTypeUse())
-      to RagConnect.errors();
-
-    DependencyDefinition contributes error("Dependency definition already defined for " + getSource().containingTypeDecl().getName() + " with name " + getID())
-      when isAlreadyDefined()
-      to RagConnect.errors();
-
-    DependencyDefinition contributes error("The name of a dependency definition must not be equal to a list-node on the source")
-      when isAlreadyDefinedAsList()
-      to RagConnect.errors();
+          ? token().effectiveJavaTypeUse().prettyPrint()
+          : getMappingList().get(0).getFromType().prettyPrint()))
+    when !getSend() && getEndpointTarget().isTokenEndpointTarget() && effectiveMappings().get(0) == null
+    to RagConnect.errors();
+
+  EndpointDefinition contributes error("to-type of last mapping (" +
+      effectiveMappings().get(effectiveMappings().size() - 1).getToType().prettyPrint() +
+      ") not assignable to type of the token (" + token().effectiveJavaTypeUse().prettyPrint() + ")!")
+    when !getSend() && getEndpointTarget().isTokenEndpointTarget() &&
+      !effectiveMappings().get(effectiveMappings().size() - 1).getToType().assignableTo(
+          token().effectiveJavaTypeUse())
+    to RagConnect.errors();
+
+  DependencyDefinition contributes error("Dependency definition already defined for " + getSource().containingTypeDecl().getName() + " with name " + getID())
+    when isAlreadyDefined()
+    to RagConnect.errors();
+
+  DependencyDefinition contributes error("The name of a dependency definition must not be equal to a list-node on the source")
+    when isAlreadyDefinedAsList()
+    to RagConnect.errors();
 }
 
 aspect ErrorHelpers {
@@ -65,12 +66,15 @@ aspect ErrorMessage {
     public ASTNode getNode() {
       return node;
     }
+
     public int getLine() {
       return line;
     }
+
     public int getCol() {
       return col;
     }
+
     public String getMessage() {
       return message;
     }
@@ -91,7 +95,7 @@ aspect ErrorMessage {
         return n;
       }
 
-      n = col-err.col;
+      n = col - err.col;
       if (n != 0) {
         return n;
       }
diff --git a/ragconnect.base/src/main/jastadd/Handlers.jrag b/ragconnect.base/src/main/jastadd/Handlers.jrag
new file mode 100644
index 0000000..57c7ccd
--- /dev/null
+++ b/ragconnect.base/src/main/jastadd/Handlers.jrag
@@ -0,0 +1,14 @@
+aspect RagConnectHandlers {
+  syn Handler RagConnect.mqttHandler() = resolveHandlerByName("mqtt");
+  syn Handler RagConnect.restHandler() = resolveHandlerByName("rest");
+
+  private Handler RagConnect.resolveHandlerByName(String uniqueName) {
+    for (Handler handler : getHandlerList()) {
+      if (uniqueName.equals(handler.getUniqueName())) {
+        return handler;
+      }
+    }
+    System.err.println("Could not find handler with name '" + uniqueName + "'");
+    return null;
+  }
+}
diff --git a/ragconnect.base/src/main/jastadd/NameResolution.jrag b/ragconnect.base/src/main/jastadd/NameResolution.jrag
index 47ae57a..9b37ee7 100644
--- a/ragconnect.base/src/main/jastadd/NameResolution.jrag
+++ b/ragconnect.base/src/main/jastadd/NameResolution.jrag
@@ -1,4 +1,42 @@
 aspect RagConnectNameResolution {
+  // --- lookupTokenEndpointDefinition ---
+  inh java.util.List<EndpointDefinition> EndpointDefinition.lookupTokenEndpointDefinitions(TokenComponent token);
+  inh java.util.List<EndpointDefinition> EndpointTarget.lookupTokenEndpointDefinitions(TokenComponent token);
+  eq RagConnect.getConnectSpecificationFile().lookupTokenEndpointDefinitions(TokenComponent token) = lookupTokenEndpointDefinitions(token);
+  syn java.util.List<EndpointDefinition> RagConnect.lookupTokenEndpointDefinitions(TokenComponent token) {
+    java.util.List<EndpointDefinition> result = new java.util.ArrayList<>();
+    for (EndpointTarget target : allEndpointTargetList()) {
+      if (target.isTokenEndpointTarget() && target.asTokenEndpointTarget().getToken().equals(token)) {
+        result.add(target.containingEndpointDefinition());
+      }
+    }
+    return result;
+  }
+
+  // --- lookupTypeEndpointDefinition ---
+  inh java.util.List<EndpointDefinition> EndpointDefinition.lookupTypeEndpointDefinitions(TypeComponent type);
+  inh java.util.List<EndpointDefinition> EndpointTarget.lookupTypeEndpointDefinitions(TypeComponent type);
+  eq RagConnect.getConnectSpecificationFile().lookupTypeEndpointDefinitions(TypeComponent type) = lookupTypeEndpointDefinitions(type);
+  syn java.util.List<EndpointDefinition> RagConnect.lookupTypeEndpointDefinitions(TypeComponent type) {
+    java.util.List<EndpointDefinition> result = new java.util.ArrayList<>();
+    for (EndpointTarget target : allEndpointTargetList()) {
+      if (target.isTypeEndpointTarget() && target.asTypeEndpointTarget().getType().equals(type)) {
+        result.add(target.containingEndpointDefinition());
+      }
+    }
+    return result;
+  }
+
+  // --- lookupDependencyDefinition ---
+  inh DependencyDefinition DependencyDefinition.lookupDependencyDefinition(TypeDecl source, String id);
+  eq RagConnect.getConnectSpecificationFile().lookupDependencyDefinition(TypeDecl source, String id) {
+    for (DependencyDefinition def : allDependencyDefinitionList()) {
+      if (def.getID().equals(id) && def.getSource().containingTypeDecl().equals(source)) {
+        return def;
+      }
+    }
+    return null;
+  }
 
   // rel EndpointDefinition.Mapping* -> MappingDefinition
   refine RefResolverStubs eq EndpointDefinition.resolveMappingByToken(String id, int position) {
@@ -64,7 +102,7 @@ aspect RagConnectNameResolution {
   }
 
   // rel ___ -> TokenComponent (from relast-preprocessor)
-  // refine here to have an attribute without writing on stderr if not found
+  // refine from relast PP here to have an attribute that does not write on stderr if no TokenComponent was found
   refine NameResolution eq ASTNode.globallyResolveTokenComponentByToken(String id) {
     TokenComponent result = tryGloballyResolveTokenComponentByToken(id);
     if (result == null) {
diff --git a/ragconnect.base/src/main/jastadd/Navigation.jrag b/ragconnect.base/src/main/jastadd/Navigation.jrag
index d09444c..369ba4f 100644
--- a/ragconnect.base/src/main/jastadd/Navigation.jrag
+++ b/ragconnect.base/src/main/jastadd/Navigation.jrag
@@ -112,7 +112,9 @@ aspect RagConnectNavigation {
   syn EndpointDefinition DependencyDefinition.targetEndpointDefinition() {
     // resolve definition in here, as we do not need resolveMethod in any other place (yet)
     for (EndpointDefinition endpointDefinition : ragconnect().allEndpointDefinitionList()) {
-      if (!endpointDefinition.getSend()) { continue; }
+      if (!endpointDefinition.getSend()) {
+        continue;
+      }
       EndpointTarget endpointTarget = endpointDefinition.getEndpointTarget();
       if (endpointTarget.isTokenEndpointTarget() &&
           endpointTarget.asTokenEndpointTarget().getToken().equals(this.getTarget())) {
diff --git a/ragconnect.base/src/main/jastadd/RagConnect.relast b/ragconnect.base/src/main/jastadd/RagConnect.relast
index c0037c7..30e6d5a 100644
--- a/ragconnect.base/src/main/jastadd/RagConnect.relast
+++ b/ragconnect.base/src/main/jastadd/RagConnect.relast
@@ -1,17 +1,17 @@
-RagConnect ::= ConnectSpecificationFile* Handler* Program ;
+RagConnect ::= ConnectSpecificationFile* Program Handler* Configuration;
 
-abstract ConnectSpecification ::= EndpointDefinition* DependencyDefinition* MappingDefinition* ;
-ConnectSpecificationFile : ConnectSpecification ::= <FileName> ;
+abstract ConnectSpecification ::= EndpointDefinition* DependencyDefinition* MappingDefinition*;
+ConnectSpecificationFile : ConnectSpecification ::= <FileName>;
 
-EndpointDefinition ::= <AlwaysApply:boolean> <IndexBasedListAccess:boolean> <WithAdd:boolean> <Send:boolean> EndpointTarget ;
+EndpointDefinition ::= <AlwaysApply:boolean> <IndexBasedListAccess:boolean> <WithAdd:boolean> <Send:boolean> EndpointTarget;
 rel EndpointDefinition.Mapping* <-> MappingDefinition.UsedAt*;
 
-abstract EndpointTarget ;
-TokenEndpointTarget : EndpointTarget ;
-rel TokenEndpointTarget.Token <-> TokenComponent.TokenEndpointTarget* ;
-TypeEndpointTarget : EndpointTarget ;
-rel TypeEndpointTarget.Type <-> TypeComponent.TypeEndpointTarget* ;
-UntypedEndpointTarget : EndpointTarget ::= <TokenOrType> ;  // only used by parser
+abstract EndpointTarget;
+TokenEndpointTarget : EndpointTarget;
+rel TokenEndpointTarget.Token <-> TokenComponent.TokenEndpointTarget*;
+TypeEndpointTarget : EndpointTarget;
+rel TypeEndpointTarget.Type <-> TypeComponent.TypeEndpointTarget*;
+UntypedEndpointTarget : EndpointTarget ::= <TokenOrType>;  // only used by parser
 // to be integrated:
 //AttributeEndpointTarget : EndpointTarget ::= <Name> ;
 //RelationEndpointTarget : EndpointTarget ;
@@ -21,10 +21,19 @@ DependencyDefinition ::= <ID>;
 rel DependencyDefinition.Source <-> TokenComponent.DependencySourceDefinition*;
 rel DependencyDefinition.Target -> Component;
 
-MappingDefinition ::= <ID> FromType:MappingDefinitionType <FromVariableName> ToType:MappingDefinitionType <Content> ;
+MappingDefinition ::= <ID> FromType:MappingDefinitionType <FromVariableName> ToType:MappingDefinitionType <Content>;
 abstract MappingDefinitionType ::= ;
-JavaMappingDefinitionType : MappingDefinitionType ::= Type:JavaTypeUse ;
-JavaArrayMappingDefinitionType : MappingDefinitionType ::= Type:JavaTypeUse ;
-DefaultMappingDefinition : MappingDefinition ;
+JavaMappingDefinitionType : MappingDefinitionType ::= Type:JavaTypeUse;
+JavaArrayMappingDefinitionType : MappingDefinitionType ::= Type:JavaTypeUse;
+DefaultMappingDefinition : MappingDefinition;
 
-Handler ::= <ClassName> <Construction> <AttributeName> <FieldName> <InUse:boolean>;
+Handler ::= <DefinitionFileName> <ClassName> <UniqueName> <InUse:boolean>;
+
+Configuration ::=
+<LoggingEnabledForReads:boolean>
+<LoggingEnabledForWrites:boolean>
+<LoggingEnabledForIncremental:boolean>
+<JastAddList:String>
+<IncrementalOptionActive:boolean>
+<ExperimentalJastAdd329:boolean>;
+rel Configuration.RootNode -> TypeDecl ;
diff --git a/ragconnect.base/src/main/jastadd/RagConnect_old.relast b/ragconnect.base/src/main/jastadd/RagConnect_old.relast
deleted file mode 100644
index 4301c70..0000000
--- a/ragconnect.base/src/main/jastadd/RagConnect_old.relast
+++ /dev/null
@@ -1,35 +0,0 @@
-RagConnect ::= ConnectSpecificationFile* Program ;
-
-abstract ConnectSpecification ::= EndpointDefinition* DependencyDefinition* MappingDefinition* ;
-ConnectSpecificationFile : ConnectSpecification ::= <FileName> ;
-
-abstract EndpointDefinition ::= <AlwaysApply:boolean> ;
-
-rel EndpointDefinition.Mapping* <-> MappingDefinition.UsedAt*;
-
-abstract TokenEndpointDefinition : EndpointDefinition;
-rel TokenEndpointDefinition.Token <-> TokenComponent.TokenEndpointDefinition*;
-
-ReceiveTokenEndpointDefinition : TokenEndpointDefinition;
-SendTokenEndpointDefinition : TokenEndpointDefinition;
-
-abstract TypeEndpointDefinition : EndpointDefinition ::= <IndexBasedListAccess:boolean> ;
-rel TypeEndpointDefinition.Type <-> TypeComponent.TypeEndpointDefinition*;
-
-ReceiveTypeEndpointDefinition : TypeEndpointDefinition ::= <WithAdd:boolean>;
-SendTypeEndpointDefinition : TypeEndpointDefinition;
-
-DependencyDefinition ::= <ID>;
-rel DependencyDefinition.Source <-> TokenComponent.DependencySourceDefinition*;
-rel DependencyDefinition.Target -> Component;
-
-MappingDefinition ::= <ID> FromType:MappingDefinitionType <FromVariableName> ToType:MappingDefinitionType <Content> ;
-abstract MappingDefinitionType ::= ;
-JavaMappingDefinitionType : MappingDefinitionType ::= Type:JavaTypeUse ;
-JavaArrayMappingDefinitionType : MappingDefinitionType ::= Type:JavaTypeUse ;
-DefaultMappingDefinition : MappingDefinition ;
-
-// only used by parser
-abstract UntypedEndpointDefinition : EndpointDefinition ::= <TokenOrType> <Indexed:boolean> <WithAdd:boolean> ;
-ReceiveUntypedEndpointDefinition : UntypedEndpointDefinition;
-SendUntypedEndpointDefinition : UntypedEndpointDefinition;
diff --git a/ragconnect.base/src/main/jastadd/intermediate/Generation.jadd b/ragconnect.base/src/main/jastadd/intermediate/Generation.jadd
index d52f627..2e0c4a9 100644
--- a/ragconnect.base/src/main/jastadd/intermediate/Generation.jadd
+++ b/ragconnect.base/src/main/jastadd/intermediate/Generation.jadd
@@ -6,7 +6,7 @@ Design considerations
 aspect AttributesForMustache {
   syn List<TypeComponent> RagConnect.rootTypeComponents() {
     List<TypeComponent> result = new ArrayList<>();
-    for (Component child : rootNode.getComponentList()) {
+    for (Component child : rootNode().getComponentList()) {
       if (child.isTypeComponent()) {
         result.add(child.asTypeComponent());
       }
@@ -16,12 +16,11 @@ aspect AttributesForMustache {
   syn boolean RagConnect.hasRootTypeComponents() = !rootTypeComponents().isEmpty();
 
   syn String RagConnect.closeMethodName() = "ragconnectCloseConnections";
-  syn String RagConnect.mqttHandlerAttribute() = "_mqttHandler";
-  syn String RagConnect.mqttHandlerField() = "_mqttHandler";
-  syn String RagConnect.mqttSetupWaitUntilReadyMethodName() = "ragconnectSetupMqttWaitUntilReady";
 
-  syn String RagConnect.restHandlerAttribute() = "_restHandler";
-  syn String RagConnect.restHandlerField() = "_restHandler";
+  syn String Handler.AttributeName() = "_" + getUniqueName() + "Handler";
+  syn String Handler.FieldName() = "_" + getUniqueName() + "Handler";
+  syn String Handler.SetupWaitUntilReadyMethodName() = "ragconnectSetup" + capitalize(getUniqueName()) + "WaitUntilReady";
+  syn String Handler.ConstructionSnippet() = "new " + getClassName() + "(\"Handler for " + ragconnect().rootNodeName() + ".\" + this.hashCode())";
 
   syn boolean RagConnect.hasTreeListEndpoints() {
     for (EndpointDefinition endpointDef : allEndpointDefinitionList()) {
@@ -249,7 +248,7 @@ aspect AttributesForMustache {
 
 aspect AspectGeneration {
   // --- rootNodeName ---
-  syn String ASTNode.rootNodeName() = rootNode.getName();
+  syn String RagConnect.rootNodeName() = getConfiguration().getRootNode().getName();
 }
 
 aspect GrammarGeneration {
@@ -273,13 +272,13 @@ aspect GrammarGeneration {
     return result;
   }
 
-//  coll java.util.Map<TypeDecl, TokenComponent> RagConnect.additionalTokens() [new java.util.HashMap<>()] with put root RagConnect;
+  //  coll java.util.Map<TypeDecl, TokenComponent> RagConnect.additionalTokens() [new java.util.HashMap<>()] with put root RagConnect;
 
-//  TypeEndpointDefinition contributes getTokenToCreate()
-//    when typeIsList() && !getUseList()
-//    to RagConnect.additionalTokens()
-////    for ragconnect()
-//    ;
+  //  TypeEndpointDefinition contributes getTokenToCreate()
+  //    when typeIsList() && !getUseList()
+  //    to RagConnect.additionalTokens()
+  ////    for ragconnect()
+  //    ;
 
   syn java.util.Map<TypeDecl, TokenComponent> RagConnect.additionalTokens() {
     java.util.Map<TypeDecl, TokenComponent> result = new java.util.HashMap<>();
diff --git a/ragconnect.base/src/main/jastadd/intermediate/Mappings.jrag b/ragconnect.base/src/main/jastadd/intermediate/Mappings.jrag
index 82ca5a5..e41821f 100644
--- a/ragconnect.base/src/main/jastadd/intermediate/Mappings.jrag
+++ b/ragconnect.base/src/main/jastadd/intermediate/Mappings.jrag
@@ -47,50 +47,50 @@ aspect DefaultMappings {
 
   syn nta DefaultMappingDefinition RagConnect.defaultBytesToTreeMapping(String typeName) {
     return treeDefaultMappingDefinition("byte[]", typeName,
-      "String content = new String(input);\n" +
-      "com.fasterxml.jackson.databind.ObjectMapper mapper = new com.fasterxml.jackson.databind.ObjectMapper();\n" +
-      "com.fasterxml.jackson.core.JsonFactory factory = new com.fasterxml.jackson.core.JsonFactory();\n" +
-      "com.fasterxml.jackson.core.JsonParser parser = factory.createParser(content);\n" +
-      typeName + " result = " + typeName + ".deserialize((com.fasterxml.jackson.databind.JsonNode)mapper.readTree(parser));\n" +
-      "parser.close();\n" +
-      "return result;"
+        "String content = new String(input);\n" +
+            "com.fasterxml.jackson.databind.ObjectMapper mapper = new com.fasterxml.jackson.databind.ObjectMapper();\n" +
+            "com.fasterxml.jackson.core.JsonFactory factory = new com.fasterxml.jackson.core.JsonFactory();\n" +
+            "com.fasterxml.jackson.core.JsonParser parser = factory.createParser(content);\n" +
+            typeName + " result = " + typeName + ".deserialize((com.fasterxml.jackson.databind.JsonNode)mapper.readTree(parser));\n" +
+            "parser.close();\n" +
+            "return result;"
     );
   }
   syn nta DefaultMappingDefinition RagConnect.defaultTreeToBytesMapping(String typeName) {
     return treeDefaultMappingDefinition(typeName, "byte[]",
-      "java.io.ByteArrayOutputStream outputStream = new java.io.ByteArrayOutputStream();\n" +
-      "com.fasterxml.jackson.core.JsonFactory factory = new com.fasterxml.jackson.core.JsonFactory();\n" +
-      "com.fasterxml.jackson.core.JsonGenerator generator = factory.createGenerator(outputStream, com.fasterxml.jackson.core.JsonEncoding.UTF8);\n"+
-      "input.serialize(generator);\n" +
-      "generator.flush();\n" +
-      "return outputStream.toString().getBytes();"
+        "java.io.ByteArrayOutputStream outputStream = new java.io.ByteArrayOutputStream();\n" +
+            "com.fasterxml.jackson.core.JsonFactory factory = new com.fasterxml.jackson.core.JsonFactory();\n" +
+            "com.fasterxml.jackson.core.JsonGenerator generator = factory.createGenerator(outputStream, com.fasterxml.jackson.core.JsonEncoding.UTF8);\n" +
+            "input.serialize(generator);\n" +
+            "generator.flush();\n" +
+            "return outputStream.toString().getBytes();"
     );
   }
 
   syn nta DefaultMappingDefinition RagConnect.defaultBytesToListTreeMapping(String typeName) {
-    return treeDefaultMappingDefinition("byte[]", JastAddList + "<" + typeName + ">",
-      "String content = new String(input);\n" +
-      "com.fasterxml.jackson.databind.ObjectMapper mapper = new com.fasterxml.jackson.databind.ObjectMapper();\n" +
-      "com.fasterxml.jackson.core.JsonFactory factory = new com.fasterxml.jackson.core.JsonFactory();\n" +
-      "com.fasterxml.jackson.core.JsonParser parser = factory.createParser(content);\n" +
-      JastAddList + "<" + typeName + ">" + " result = " + typeName + ".deserializeList((com.fasterxml.jackson.databind.node.ArrayNode)mapper.readTree(parser));\n" +
-      "parser.close();\n" +
-      "return result;"
+    return treeDefaultMappingDefinition("byte[]", JastAddList() + "<" + typeName + ">",
+        "String content = new String(input);\n" +
+            "com.fasterxml.jackson.databind.ObjectMapper mapper = new com.fasterxml.jackson.databind.ObjectMapper();\n" +
+            "com.fasterxml.jackson.core.JsonFactory factory = new com.fasterxml.jackson.core.JsonFactory();\n" +
+            "com.fasterxml.jackson.core.JsonParser parser = factory.createParser(content);\n" +
+            JastAddList() + "<" + typeName + ">" + " result = " + typeName + ".deserializeList((com.fasterxml.jackson.databind.node.ArrayNode)mapper.readTree(parser));\n" +
+            "parser.close();\n" +
+            "return result;"
     );
   }
   syn nta DefaultMappingDefinition RagConnect.defaultListTreeToBytesMapping() {
-    return treeDefaultMappingDefinition(JastAddList, "byte[]",
-      "java.io.ByteArrayOutputStream outputStream = new java.io.ByteArrayOutputStream();\n" +
-      "com.fasterxml.jackson.core.JsonFactory factory = new com.fasterxml.jackson.core.JsonFactory();\n" +
-      "com.fasterxml.jackson.core.JsonGenerator generator = factory.createGenerator(outputStream, com.fasterxml.jackson.core.JsonEncoding.UTF8);\n"+
-      "input.serialize(generator);\n" +
-      "generator.flush();\n" +
-      "return outputStream.toString().getBytes();"
+    return treeDefaultMappingDefinition(JastAddList(), "byte[]",
+        "java.io.ByteArrayOutputStream outputStream = new java.io.ByteArrayOutputStream();\n" +
+            "com.fasterxml.jackson.core.JsonFactory factory = new com.fasterxml.jackson.core.JsonFactory();\n" +
+            "com.fasterxml.jackson.core.JsonGenerator generator = factory.createGenerator(outputStream, com.fasterxml.jackson.core.JsonEncoding.UTF8);\n" +
+            "input.serialize(generator);\n" +
+            "generator.flush();\n" +
+            "return outputStream.toString().getBytes();"
     );
   }
 
   syn nta DefaultMappingDefinition RagConnect.defaultBooleanToBytesMapping() = baseDefaultMappingDefinition(
-        "boolean", "byte[]", "return java.nio.ByteBuffer.allocate(1).put((byte) (input ? 1 : 0)).array();");
+      "boolean", "byte[]", "return java.nio.ByteBuffer.allocate(1).put((byte) (input ? 1 : 0)).array();");
   syn nta DefaultMappingDefinition RagConnect.defaultIntToBytesMapping() = baseDefaultMappingDefinition(
       "int", "byte[]", "return java.nio.ByteBuffer.allocate(Integer.BYTES).putInt(input).array();");
   syn nta DefaultMappingDefinition RagConnect.defaultShortToBytesMapping() = baseDefaultMappingDefinition(
@@ -168,7 +168,7 @@ aspect Mappings {
   syn boolean TokenComponent.isPrimitiveType() = effectiveJavaTypeUse().isPrimitiveType();
   syn boolean JavaTypeUse.isPrimitiveType() = false;
   eq SimpleJavaTypeUse.isPrimitiveType() {
-    switch(getName()) {
+    switch (getName()) {
       case "boolean":
       case "int":
       case "short":
@@ -176,8 +176,10 @@ aspect Mappings {
       case "float":
       case "double":
       case "char":
-      case "byte": return true;
-      default: return false;
+      case "byte":
+        return true;
+      default:
+        return false;
     }
   }
   syn boolean MappingDefinitionType.isPrimitiveType() = false;
@@ -193,30 +195,40 @@ aspect Mappings {
       try {
         TypeDecl typeDecl = program().resolveTypeDecl(targetTypeName());
         return typeIsList() && !getIndexBasedListAccess() ? ragconnect().defaultBytesToListTreeMapping(typeDecl.getName()) : ragconnect().defaultBytesToTreeMapping(typeDecl.getName());
-      } catch (Exception ignore) {}
+      } catch (Exception ignore) {
+      }
     }
     switch (targetTypeName()) {
       case "boolean":
-      case "Boolean": return ragconnect().defaultBytesToBooleanMapping();
+      case "Boolean":
+        return ragconnect().defaultBytesToBooleanMapping();
       case "int":
-      case "Integer": return ragconnect().defaultBytesToIntMapping();
+      case "Integer":
+        return ragconnect().defaultBytesToIntMapping();
       case "short":
-      case "Short": return ragconnect().defaultBytesToShortMapping();
+      case "Short":
+        return ragconnect().defaultBytesToShortMapping();
       case "long":
-      case "Long": return ragconnect().defaultBytesToLongMapping();
+      case "Long":
+        return ragconnect().defaultBytesToLongMapping();
       case "float":
-      case "Float": return ragconnect().defaultBytesToFloatMapping();
+      case "Float":
+        return ragconnect().defaultBytesToFloatMapping();
       case "double":
-      case "Double": return ragconnect().defaultBytesToDoubleMapping();
+      case "Double":
+        return ragconnect().defaultBytesToDoubleMapping();
       case "char":
-      case "Character": return ragconnect().defaultBytesToCharMapping();
-      case "String": return ragconnect().defaultBytesToStringMapping();
+      case "Character":
+        return ragconnect().defaultBytesToCharMapping();
+      case "String":
+        return ragconnect().defaultBytesToStringMapping();
       default:
         try {
           TypeDecl typeDecl = program().resolveTypeDecl(targetTypeName());
           // TODO: also support list-types, if list is first type
           return ragconnect().defaultBytesToTreeMapping(typeDecl.getName());
-        } catch (Exception ignore) {}
+        } catch (Exception ignore) {
+        }
         System.err.println("Could not find suitable default mapping for " + targetTypeName() + " on " + this);
         return null;
     }
@@ -228,30 +240,40 @@ aspect Mappings {
       try {
         TypeDecl typeDecl = program().resolveTypeDecl(targetTypeName());
         return typeIsList() && !getIndexBasedListAccess() ? ragconnect().defaultListTreeToBytesMapping() : ragconnect().defaultTreeToBytesMapping(typeDecl.getName());
-      } catch (Exception ignore) {}
+      } catch (Exception ignore) {
+      }
     }
     switch (targetTypeName()) {
       case "boolean":
-      case "Boolean": return ragconnect().defaultBooleanToBytesMapping();
+      case "Boolean":
+        return ragconnect().defaultBooleanToBytesMapping();
       case "int":
-      case "Integer": return ragconnect().defaultIntToBytesMapping();
+      case "Integer":
+        return ragconnect().defaultIntToBytesMapping();
       case "short":
-      case "Short": return ragconnect().defaultShortToBytesMapping();
+      case "Short":
+        return ragconnect().defaultShortToBytesMapping();
       case "long":
-      case "Long": return ragconnect().defaultLongToBytesMapping();
+      case "Long":
+        return ragconnect().defaultLongToBytesMapping();
       case "float":
-      case "Float": return ragconnect().defaultFloatToBytesMapping();
+      case "Float":
+        return ragconnect().defaultFloatToBytesMapping();
       case "double":
-      case "Double": return ragconnect().defaultDoubleToBytesMapping();
+      case "Double":
+        return ragconnect().defaultDoubleToBytesMapping();
       case "char":
-      case "Character": return ragconnect().defaultCharToBytesMapping();
-      case "String": return ragconnect().defaultStringToBytesMapping();
+      case "Character":
+        return ragconnect().defaultCharToBytesMapping();
+      case "String":
+        return ragconnect().defaultStringToBytesMapping();
       default:
         try {
           TypeDecl typeDecl = program().resolveTypeDecl(targetTypeName());
           // TODO: also support list-types, if list is last type
           return ragconnect().defaultTreeToBytesMapping(typeDecl.getName());
-        } catch (Exception ignore) {}
+        } catch (Exception ignore) {
+        }
         System.err.println("Could not find suitable default mapping for " + targetTypeName() + " on " + this);
         return null;
     }
@@ -273,54 +295,54 @@ aspect Mappings {
   eq TokenEndpointTarget.targetTypeName() = getToken().effectiveJavaTypeUse().getName();
   eq TypeEndpointTarget.targetTypeName() = getType().getTypeDecl().getName();
 
-//  eq ReceiveFromRestDefinition.suitableDefaultMapping() {
-//    String typeName = getMappingList().isEmpty() ?
-//        getToken().getJavaTypeUse().getName() :
-//        getMappingList().get(0).getFromType().prettyPrint();
-//    switch(typeName) {
-//      case "int":
-//      case "Integer": return ragconnect().defaultStringToIntMapping();
-//      case "short":
-//      case "Short": return ragconnect().defaultStringToShortMapping();
-//      case "long":
-//      case "Long": return ragconnect().defaultStringToLongMapping();
-//      case "float":
-//      case "Float": return ragconnect().defaultStringToFloatMapping();
-//      case "double":
-//      case "Double": return ragconnect().defaultStringToDoubleMapping();
-//      case "char":
-//      case "Character": return ragconnect().defaultStringToCharMapping();
-//      default: return null;
-//    }
-//  }
-//  eq SendToRestDefinition.suitableDefaultMapping() {
-//    String typeName = getMappingList().isEmpty() ?
-//        getToken().getJavaTypeUse().getName() :
-//        getMappingList().get(getMappingList().size() - 1).getFromType().prettyPrint();
-//    switch(typeName) {
-//      case "int":
-//      case "Integer": return ragconnect().defaultIntToStringMapping();
-//      case "short":
-//      case "Short": return ragconnect().defaultShortToStringMapping();
-//      case "long":
-//      case "Long": return ragconnect().defaultLongToStringMapping();
-//      case "float":
-//      case "Float": return ragconnect().defaultFloatToStringMapping();
-//      case "double":
-//      case "Double": return ragconnect().defaultDoubleToStringMapping();
-//      case "char":
-//      case "Character": return ragconnect().defaultCharToStringMapping();
-//      default: return null;
-//    }
-//  }
+  //  eq ReceiveFromRestDefinition.suitableDefaultMapping() {
+  //    String typeName = getMappingList().isEmpty() ?
+  //        getToken().getJavaTypeUse().getName() :
+  //        getMappingList().get(0).getFromType().prettyPrint();
+  //    switch(typeName) {
+  //      case "int":
+  //      case "Integer": return ragconnect().defaultStringToIntMapping();
+  //      case "short":
+  //      case "Short": return ragconnect().defaultStringToShortMapping();
+  //      case "long":
+  //      case "Long": return ragconnect().defaultStringToLongMapping();
+  //      case "float":
+  //      case "Float": return ragconnect().defaultStringToFloatMapping();
+  //      case "double":
+  //      case "Double": return ragconnect().defaultStringToDoubleMapping();
+  //      case "char":
+  //      case "Character": return ragconnect().defaultStringToCharMapping();
+  //      default: return null;
+  //    }
+  //  }
+  //  eq SendToRestDefinition.suitableDefaultMapping() {
+  //    String typeName = getMappingList().isEmpty() ?
+  //        getToken().getJavaTypeUse().getName() :
+  //        getMappingList().get(getMappingList().size() - 1).getFromType().prettyPrint();
+  //    switch(typeName) {
+  //      case "int":
+  //      case "Integer": return ragconnect().defaultIntToStringMapping();
+  //      case "short":
+  //      case "Short": return ragconnect().defaultShortToStringMapping();
+  //      case "long":
+  //      case "Long": return ragconnect().defaultLongToStringMapping();
+  //      case "float":
+  //      case "Float": return ragconnect().defaultFloatToStringMapping();
+  //      case "double":
+  //      case "Double": return ragconnect().defaultDoubleToStringMapping();
+  //      case "char":
+  //      case "Character": return ragconnect().defaultCharToStringMapping();
+  //      default: return null;
+  //    }
+  //  }
 
   // --- isByteArray ---
   syn boolean MappingDefinitionType.isByteArray() = false;
   eq JavaArrayMappingDefinitionType.isByteArray() = getType().getName().equals("byte");
 
-//  // --- isString ---
-//  syn boolean MappingDefinitionType.isString() = false;
-//  eq JavaMappingDefinitionType.isString() = getType().getName().equals("String");
+  //  // --- isString ---
+  //  syn boolean MappingDefinitionType.isString() = false;
+  //  eq JavaMappingDefinitionType.isString() = getType().getName().equals("String");
 
   // --- allMappingDefinitions ---
   syn java.util.List<MappingDefinition> RagConnect.allMappingDefinitions() {
diff --git a/ragconnect.base/src/main/jastadd/scanner/Keywords.flex b/ragconnect.base/src/main/jastadd/scanner/Keywords.flex
index 11f73da..8a1eaec 100644
--- a/ragconnect.base/src/main/jastadd/scanner/Keywords.flex
+++ b/ragconnect.base/src/main/jastadd/scanner/Keywords.flex
@@ -5,8 +5,6 @@
 "maps"       { return sym(Terminals.MAPS); }
 "to"         { return sym(Terminals.TO); }
 "as"         { return sym(Terminals.AS); }
-//"tree"       { return sym(Terminals.TREE); }
-//"list"       { return sym(Terminals.LIST); }
 "with"       { return sym(Terminals.WITH); }
 "indexed"    { return sym(Terminals.INDEXED); }
 "add"        { return sym(Terminals.ADD); }
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 1f57a7a..abddccd 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
@@ -20,7 +20,7 @@ import java.util.logging.Logger;
 
 public class Compiler extends AbstractCompiler {
 
-//  private ValueOption optionOutputDir;
+  //  private ValueOption optionOutputDir;
   private ValueOption optionRootNode;
   private ValueOption optionProtocols;
   private BooleanOption optionPrintYaml;
@@ -68,6 +68,7 @@ public class Compiler extends AbstractCompiler {
     }
 
     RagConnect ragConnect = parseProgram(getConfiguration().getFiles());
+    setConfiguration(ragConnect);
 
     if (!ragConnect.errors().isEmpty()) {
       StringBuilder sb = new StringBuilder("Errors:\n");
@@ -79,24 +80,17 @@ public class Compiler extends AbstractCompiler {
     }
 
     if (optionPrintYaml.value()) {
-      System.err.println("Currently unsupported option!");
-//      ASTNode.rootNode = ragConnect.getProgram().resolveTypeDecl(optionRootNode.value());
-//      String yamlContent = ragConnect.toMustache().toYAML().prettyPrint();
-//      System.out.println(yamlContent);
-//      writeToFile(getConfiguration().outputDir().toPath().resolve("RagConnect.yml"), yamlContent);
+      System.err.println("'--printYaml' currently unsupported option!");
       return 1;
     }
 
     LOGGER.fine("Writing output files");
-    final List<String> handlers = new ArrayList<>();
-    if (ASTNode.usesMqtt) {
-      handlers.add("MqttHandler.jadd");
-    }
-    if (ASTNode.usesRest) {
-      handlers.add("RestHandler.jadd");
-    }
     // copy handlers into outputDir
-    for (String handlerFileName : handlers) {
+    for (Handler handler : ragConnect.getHandlerList()) {
+      if (!handler.getInUse()) {
+        continue;
+      }
+      String handlerFileName = handler.getDefinitionFileName();
       try {
         InputStream inputStream = Compiler.class.getClassLoader().getResourceAsStream(handlerFileName);
         if (inputStream == null) {
@@ -114,7 +108,7 @@ public class Compiler extends AbstractCompiler {
       writeToFile(outputFile, grammarFile.generateAbstractGrammar());
     }
     writeToFile(getConfiguration().outputDir().toPath().resolve("RagConnect.jadd"),
-        generateAspect(ragConnect, optionRootNode.value()));
+        generateAspect(ragConnect));
     return 0;
   }
 
@@ -130,14 +124,14 @@ public class Compiler extends AbstractCompiler {
 
   /**
    * Reads the version string.
-   *
+   * <p>
    * The version string is read from the property file
    * src/main/resources/Version.properties. This
    * file should be generated during the build process. If it is missing
    * then there is some problem in the build script.
    *
-   * @author Jesper Öqvist <jesper.oqvist@cs.lth.se>
    * @return the read version string, or <code>version ?</code>
+   * @author Jesper Öqvist <jesper.oqvist@cs.lth.se>
    */
   private String readVersion() {
     try {
@@ -160,26 +154,26 @@ public class Compiler extends AbstractCompiler {
     super.initOptions();
     optionRootNode = addOption(
         new ValueOption("rootNode", "root node in the base grammar.")
-        .acceptAnyValue()
-        .needsValue(true));
+            .acceptAnyValue()
+            .needsValue(true));
     optionProtocols = addOption(
         new ValueOption("protocols", "Protocols to enable")
-        .acceptMultipleValues(true)
-        .addDefaultValue(OPTION_PROTOCOL_MQTT, "Enable MQTT")
-        .addAcceptedValue(OPTION_PROTOCOL_REST, "Enable REST")
+            .acceptMultipleValues(true)
+            .addDefaultValue(OPTION_PROTOCOL_MQTT, "Enable MQTT")
+            .addAcceptedValue(OPTION_PROTOCOL_REST, "Enable REST")
     );
     optionPrintYaml = addOption(
-        new BooleanOption("printYaml", "Print out YAML instead of generating files")
-        .defaultValue(false));
+        new BooleanOption("printYaml", "Print out YAML instead of generating files (currently unsupported)")
+            .defaultValue(false));
     optionVerbose = addOption(
         new BooleanOption("verbose", "Print more messages while compiling.")
-        .defaultValue(false));
+            .defaultValue(false));
     optionLogReads = addOption(
         new BooleanOption("logReads", "Enable logging for every read.")
-        .defaultValue(false));
+            .defaultValue(false));
     optionLogWrites = addOption(
         new BooleanOption("logWrites", "Enable logging for every write.")
-        .defaultValue(false));
+            .defaultValue(false));
     optionLogIncremental = addOption(
         new BooleanOption("logIncremental", "Enable logging for observer in incremental dependency tracking.")
             .defaultValue(false));
@@ -235,20 +229,7 @@ public class Compiler extends AbstractCompiler {
 
     ragConnect.additionalRelations().forEach(ragConnectGrammarPart::addDeclaration);
     ragConnect.additionalTokens().forEach(TypeDecl::addComponent);
-    ASTNode.loggingEnabledForReads = optionLogReads.value();
-    ASTNode.loggingEnabledForWrites = optionLogWrites.value();
-    ASTNode.loggingEnabledForIncremental = optionLogIncremental.value();
-    ASTNode.experimentalJastAdd329 = optionExperimentalJastAdd329.value();
 
-    // reuse "--incremental" option of JastAdd
-    ASTNode.incrementalOptionActive = getConfiguration().incremental() && getConfiguration().traceFlush();
-    LOGGER.fine(() -> "ASTNode.incrementalOptionActive = " + ASTNode.incrementalOptionActive);
-
-    // reuse "--List" option of JastAdd
-    ASTNode.JastAddList = getConfiguration().listType();
-
-    ASTNode.usesMqtt = optionProtocols.hasValue(OPTION_PROTOCOL_MQTT);
-    ASTNode.usesRest = optionProtocols.hasValue(OPTION_PROTOCOL_REST);
     return ragConnect;
   }
 
@@ -281,6 +262,7 @@ public class Compiler extends AbstractCompiler {
 
   /**
    * Extracts the basename of the given file, with file extension
+   *
    * @param filename the given filename
    * @return the basename
    */
@@ -288,20 +270,29 @@ public class Compiler extends AbstractCompiler {
     return new File(filename).getName();
   }
 
-//  protected void printUsage() {
-//    System.out.println("Usage: java -jar ragconnect.jar [--option1] [--option2=value] ...  <filename1> <filename2> ... ");
-//    System.out.println("Options:");
-//    System.out.print(commandLine.printOptionHelp());
-//  }
+  private void setConfiguration(RagConnect ragConnect) {
+    ragConnect.setConfiguration(new Configuration());
+    ragConnect.getConfiguration().setLoggingEnabledForReads(optionLogReads.value());
+    ragConnect.getConfiguration().setLoggingEnabledForWrites(optionLogWrites.value());
+    ragConnect.getConfiguration().setLoggingEnabledForIncremental(optionLogIncremental.value());
+    ragConnect.getConfiguration().setExperimentalJastAdd329(optionExperimentalJastAdd329.value());
+
+    // reuse "--incremental" and "--trace=flush" options of JastAdd
+    boolean incrementalOptionActive = this.getConfiguration().incremental() && this.getConfiguration().traceFlush();
+    ragConnect.getConfiguration().setIncrementalOptionActive(incrementalOptionActive);
+    LOGGER.fine(() -> "ragConnect.getConfiguration().IncrementalOptionActive = " + incrementalOptionActive);
 
+    // reuse "--List" option of JastAdd
+    ragConnect.getConfiguration().setJastAddList(this.getConfiguration().listType());
+
+    ragConnect.getConfiguration().setRootNode(ragConnect.getProgram().resolveTypeDecl(optionRootNode.value()));
+
+    // Handler ::= <ClassName> <UniqueName> <InUse:boolean>;
+    ragConnect.addHandler(new Handler("MqttHandler.jadd", "MqttServerHandler", "mqtt", optionProtocols.hasValue(OPTION_PROTOCOL_MQTT)));
+    ragConnect.addHandler(new Handler("RestHandler.jadd", "RestServerHandler", "rest", optionProtocols.hasValue(OPTION_PROTOCOL_REST)));
+  }
 
-  public String generateAspect(RagConnect ragConnectSpec, String rootNodeName) {
-    ASTNode.rootNode = ragConnectSpec.getProgram().resolveTypeDecl(rootNodeName);
-    // Handler ::= <ClassName> <Construction> <AttributeName> <FieldName> <InUse:boolean>;
-    ragConnectSpec.addHandler(new Handler("MqttServerHandler", "new MqttServerHandler(\"RagConnectMQTT\")",
-        ragConnectSpec.mqttHandlerAttribute(), ragConnectSpec.mqttHandlerField(), ASTNode.usesMqtt));
-    ragConnectSpec.addHandler(new Handler("RestServerHandler", "new RestServerHandler(\"RagConnectREST\")",
-        ragConnectSpec.restHandlerAttribute(), ragConnectSpec.restHandlerField(), ASTNode.usesRest));
+  public String generateAspect(RagConnect ragConnect) {
     StringBuilder sb = new StringBuilder();
     com.github.mustachejava.reflect.ReflectionObjectHandler roh = new com.github.mustachejava.reflect.ReflectionObjectHandler() {
       @Override
@@ -321,7 +312,7 @@ public class Compiler extends AbstractCompiler {
     com.github.mustachejava.DefaultMustacheFactory mf = new com.github.mustachejava.DefaultMustacheFactory();
     mf.setObjectHandler(roh);
     com.github.mustachejava.Mustache m = mf.compile("ragconnect.mustache");
-    m.execute(new java.io.PrintWriter(new org.jastadd.ragconnect.compiler.AppendableWriter(sb)), ragConnectSpec);
+    m.execute(new java.io.PrintWriter(new org.jastadd.ragconnect.compiler.AppendableWriter(sb)), ragConnect);
     return sb.toString();
   }
 }
diff --git a/ragconnect.base/src/main/resources/MqttHandler.jadd b/ragconnect.base/src/main/resources/MqttHandler.jadd
index d859d38..0651d5f 100644
--- a/ragconnect.base/src/main/resources/MqttHandler.jadd
+++ b/ragconnect.base/src/main/resources/MqttHandler.jadd
@@ -316,11 +316,8 @@ public class MqttHandler {
       needSubscribe = pairToAddTo.callbacks.isEmpty();
       pairToAddTo.callbacks.add(callback);
     } else { // normal topic
-      java.util.List<java.util.function.BiConsumer<String, byte[]>> callbacksForTopic = normalCallbacks.get(topic);
-      if (callbacksForTopic == null) {
-        callbacksForTopic = new java.util.ArrayList<>();
-        normalCallbacks.put(topic, callbacksForTopic);
-      }
+      java.util.List<java.util.function.BiConsumer<String, byte[]>> callbacksForTopic = normalCallbacks.
+          computeIfAbsent(topic, t -> new java.util.ArrayList<>());
       needSubscribe = callbacksForTopic.isEmpty();
       callbacksForTopic.add(callback);
     }
@@ -346,7 +343,10 @@ public class MqttHandler {
         });
       });
       try {
-        operationFinished.await(2, java.util.concurrent.TimeUnit.SECONDS);
+        boolean finishedInTime = operationFinished.await(2, java.util.concurrent.TimeUnit.SECONDS);
+        if (!finishedInTime) {
+          return false;
+        }
         return success.get();
       } catch (InterruptedException e) {
         return false;
@@ -428,7 +428,10 @@ public class MqttHandler {
         });
       });
       try {
-        operationFinished.await(2, java.util.concurrent.TimeUnit.SECONDS);
+        boolean finishedInTime = operationFinished.await(2, java.util.concurrent.TimeUnit.SECONDS);
+        if (!finishedInTime) {
+          return false;
+        }
       } catch (InterruptedException e) {
         logger.catching(e);
         success.set(false);
diff --git a/ragconnect.base/src/main/resources/handler.mustache b/ragconnect.base/src/main/resources/handler.mustache
index 874eba4..32d7986 100644
--- a/ragconnect.base/src/main/resources/handler.mustache
+++ b/ragconnect.base/src/main/resources/handler.mustache
@@ -1,7 +1,7 @@
 aspect RagConnectHandler {
 {{#Handlers}}
   {{#InUse}}
-  private {{ClassName}} {{rootNodeName}}.{{FieldName}} = {{{Construction}}};
+  private {{ClassName}} {{rootNodeName}}.{{FieldName}} = {{{ConstructionSnippet}}};
   {{#hasRootTypeComponents}}inh {{ClassName}} ASTNode.{{AttributeName}}();{{/hasRootTypeComponents}}
   {{#rootTypeComponents}}
   eq {{rootNodeName}}.get{{name}}().{{AttributeName}}() = {{FieldName}};
@@ -14,6 +14,15 @@ aspect RagConnectHandler {
     {{#InUse}}{{FieldName}}.close();{{/InUse}}
     {{/Handlers}}
   }
+
+{{#mqttHandler}}
+  {{#InUse}}
+  public void {{rootNodeName}}.{{SetupWaitUntilReadyMethodName}}(long time, java.util.concurrent.TimeUnit unit) {
+    {{FieldName}}.setupWaitUntilReady(time, unit);
+  }
+  {{/InUse}}
+{{/mqttHandler}}
+
   class RagConnectToken {
     static java.util.concurrent.atomic.AtomicLong counter = new java.util.concurrent.atomic.AtomicLong(0);
     final long id;
diff --git a/ragconnect.base/src/main/resources/mqtt.mustache b/ragconnect.base/src/main/resources/mqtt.mustache
deleted file mode 100644
index 5561c2d..0000000
--- a/ragconnect.base/src/main/resources/mqtt.mustache
+++ /dev/null
@@ -1,5 +0,0 @@
-aspect MQTT {
-  public void {{rootNodeName}}.{{mqttSetupWaitUntilReadyMethodName}}(long time, java.util.concurrent.TimeUnit unit) {
-    {{mqttHandlerField}}.setupWaitUntilReady(time, unit);
-  }
-}
diff --git a/ragconnect.base/src/main/resources/ragconnect.mustache b/ragconnect.base/src/main/resources/ragconnect.mustache
index f53cb2f..b1ee1b2 100644
--- a/ragconnect.base/src/main/resources/ragconnect.mustache
+++ b/ragconnect.base/src/main/resources/ragconnect.mustache
@@ -1,4 +1,3 @@
-{{#usesMqtt}}{{> mqtt}}{{/usesMqtt}}
 {{> handler}}
 aspect RagConnect {
   {{#allEndpointDefinitionList}}
diff --git a/ragconnect.base/src/main/resources/receiveDefinition.mustache b/ragconnect.base/src/main/resources/receiveDefinition.mustache
index 379633d..e457306 100644
--- a/ragconnect.base/src/main/resources/receiveDefinition.mustache
+++ b/ragconnect.base/src/main/resources/receiveDefinition.mustache
@@ -92,19 +92,23 @@ private boolean {{parentTypeName}}.{{internalConnectMethodName}}(String {{connec
   RagConnectToken connectToken = new RagConnectToken(uri, "{{entityName}}");
   boolean success;
   switch (scheme) {
-  {{#usesMqtt}}
+  {{#mqttHandler}}
+  {{#InUse}}
     case "mqtt":
-      success = {{mqttHandlerAttribute}}().newConnection(connectToken, consumer);
+      success = {{AttributeName}}().newConnection(connectToken, consumer);
       break;
-  {{/usesMqtt}}
-  {{#usesRest}}
+  {{/InUse}}
+  {{/mqttHandler}}
+  {{#restHandler}}
+  {{#InUse}}
     case "rest":
-      success = {{restHandlerAttribute}}().newPUTConnection(connectToken, input -> {
+      success = {{AttributeName}}().newPUTConnection(connectToken, input -> {
         // TODO wildcard-topic not supported yet
         consumer.accept("", input.getBytes());
       });
       break;
-  {{/usesRest}}
+  {{/InUse}}
+  {{/restHandler}}
     default:
       System.err.println("Unknown protocol '" + scheme + "'.");
       success = false;
@@ -124,14 +128,18 @@ public boolean {{parentTypeName}}.{{disconnectMethodName}}(String {{connectParam
   }
   RagConnectDisconnectHandlerMethod disconnectingMethod;
   switch (scheme) {
-  {{#usesMqtt}}
-    case "mqtt": disconnectingMethod = {{mqttHandlerAttribute}}()::disconnect;
+  {{#mqttHandler}}
+  {{#InUse}}
+    case "mqtt": disconnectingMethod = {{AttributeName}}()::disconnect;
     break;
-  {{/usesMqtt}}
-  {{#usesRest}}
-    case "rest": disconnectingMethod = {{restHandlerAttribute}}()::disconnect;
+  {{/InUse}}
+  {{/mqttHandler}}
+  {{#restHandler}}
+  {{#InUse}}
+    case "rest": disconnectingMethod = {{AttributeName}}()::disconnect;
     break;
-  {{/usesRest}}
+  {{/InUse}}
+  {{/restHandler}}
     default:
       System.err.println("Unknown protocol '" + scheme + "' in '" + {{connectParameterName}} + "' for disconnecting {{parentTypeName}}.{{entityName}}");
       return false;
diff --git a/ragconnect.base/src/main/resources/sendDefinition.mustache b/ragconnect.base/src/main/resources/sendDefinition.mustache
index a07265f..bc7bc5a 100644
--- a/ragconnect.base/src/main/resources/sendDefinition.mustache
+++ b/ragconnect.base/src/main/resources/sendDefinition.mustache
@@ -5,10 +5,11 @@ public boolean {{parentTypeName}}.{{connectMethodName}}(String {{connectParamete
   RagConnectToken connectToken = new RagConnectToken(uri, "{{entityName}}");
   boolean success;
   switch (scheme) {
-  {{#usesMqtt}}
+  {{#mqttHandler}}
+  {{#InUse}}
     case "mqtt":
-      final MqttHandler handler = {{mqttHandlerAttribute}}().resolveHandler(uri);
-      final String topic = {{mqttHandlerAttribute}}().extractTopic(uri);
+      final MqttHandler handler = {{AttributeName}}().resolveHandler(uri);
+      final String topic = {{AttributeName}}().extractTopic(uri);
       {{senderName}}.add(() -> {
         {{#loggingEnabledForWrites}}
         System.out.println("[Send] {{entityName}} = " + {{getterMethodName}}() + " -> " + {{connectParameterName}});
@@ -21,15 +22,18 @@ public boolean {{parentTypeName}}.{{connectMethodName}}(String {{connectParamete
       }
       success = true;
       break;
-  {{/usesMqtt}}
-  {{#usesRest}}
+  {{/InUse}}
+  {{/mqttHandler}}
+  {{#restHandler}}
+  {{#InUse}}
     case "rest":
-      success = {{restHandlerAttribute}}().newGETConnection(connectToken, () -> {
+      success = {{AttributeName}}().newGETConnection(connectToken, () -> {
         {{updateMethodName}}();
         return new String({{lastValue}});
       });
       break;
-  {{/usesRest}}
+  {{/InUse}}
+  {{/restHandler}}
     default:
       System.err.println("Unknown protocol '" + scheme + "'.");
       success = false;
@@ -59,16 +63,20 @@ public boolean {{parentTypeName}}.{{disconnectMethodName}}(String {{connectParam
   {{/incrementalOptionActive}}
   RagConnectDisconnectHandlerMethod disconnectingMethod;
   switch (scheme) {
-  {{#usesMqtt}}
+  {{#mqttHandler}}
+  {{#InUse}}
     case "mqtt":
       disconnectingMethod = {{senderName}}::remove;
       break;
-  {{/usesMqtt}}
-  {{#usesRest}}
+  {{/InUse}}
+  {{/mqttHandler}}
+  {{#restHandler}}
+  {{#InUse}}
     case "rest":
-      disconnectingMethod = {{restHandlerAttribute}}()::disconnect;
+      disconnectingMethod = {{AttributeName}}()::disconnect;
       break;
-  {{/usesRest}}
+  {{/InUse}}
+  {{/restHandler}}
     default:
       System.err.println("Unknown protocol '" + scheme + "' in '" + {{connectParameterName}} + "' for disconnecting {{parentTypeName}}.{{entityName}}");
       return false;
-- 
GitLab