diff --git a/libs/jastadd2.jar b/libs/jastadd2.jar
deleted file mode 100644
index 6c69edae63a847e4274af7405c754b31b0518d3b..0000000000000000000000000000000000000000
Binary files a/libs/jastadd2.jar and /dev/null differ
diff --git a/pages/docs/extending.md b/pages/docs/extending.md
index 246b7a7527016298320fb89220988ccd6399fe49..61cabca224b7834f08cca7d96f3913c7cb9feb37 100644
--- a/pages/docs/extending.md
+++ b/pages/docs/extending.md
@@ -28,5 +28,58 @@ In `ragconnect.base/src/main/java/org/jastadd/ragconnect/compiler/Compiler.java`
 - 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`
 
-Furthermore, new test cases are appreciated. They can be added in the [ragconnect.rests repository](https://git-st.inf.tu-dresden.de/jastadd/ragconnect-tests)
+Furthermore, new test cases are appreciated, see [below](#writing-tests).
 
+## Writing Tests
+
+To add new tests, have a look at the module `ragconnect.tests`.
+It has three parts:
+1) In `src/test/01-input/*` are the [specifications](#specifications) that are going to be compiled (in principle using the steps described in [the guide to add RagConnect](adding)).
+2) In `src/test/java`, the jUnit 5 [test classes](#test-classes) are implemented. They mostly correspond 1-to-1 to a directory of the first part.
+3) In `build.gradle` the [instructions how to compile](#buildgradle) the specifications using the gradle plugin [PreprocessorPlugin][preprocessor-plugin] (`org.jastadd.preprocessor:testing`).
+
+### Specifications
+
+Every specification must have at least a `README.md` to describe the purpose of the test, a grammar `Test.relast`, and a RagConnect specification `Test.connect`.
+Usually an aspect file `Test.jadd` is included.
+
+### Test Classes
+
+Based on jUnit 5, the test classes testing some behaviour. If sending and/or receiving functionality is used, consider extending `AbstractMqttTest` in order to avoid duplicate code. In case of extending this class, please order the methods according to their lifecycle, i.e.:
+- createModel
+- setupReceiverAndConnect
+- communicateSendInitialValue
+- communicateOnlyUpdatedValue
+- closeConnections
+
+Within `AbstractMqttTest`, an `MqttHandler` named `publisher` is available to publish content.
+Some convenience methods are provided in `TestUtils`, e.g., the `DefaultMappings`, and `mqttUri` to prepend `"mqtt://"` and the correct host for the mqtt broker (`localhost` or a CI-specific host).
+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:
+
+```groovy
+task compileTreeAllowedTokens(type: RagConnectTest) {
+    ragconnect {
+        outputDir = file('src/test/02-after-ragconnect/treeAllowedTokens')
+        inputFiles = [file('src/test/01-input/treeAllowedTokens/Test.relast'),
+                      file('src/test/01-input/treeAllowedTokens/Test.connect'),
+                      file('src/test/01-input/treeAllowedTokens/TestDependencies.connect')]
+        rootNode = 'Root'
+    }
+    relast {
+        useJastAddNames = true
+        grammarName = 'src/test/03-after-relast/treeAllowedTokens/treeAllowedTokens'
+        serializer = 'jackson'
+    }
+    jastadd {
+        jastAddList = 'JastAddList'
+        packageName = 'treeAllowedTokens.ast'
+        inputFiles = [file('src/test/01-input/treeAllowedTokens/Test.jadd')]
+    }
+}
+```
+
+[preprocessor-plugin]: https://git-st.inf.tu-dresden.de/jastadd/testing
diff --git a/ragconnect.base/build.gradle b/ragconnect.base/build.gradle
index b3e89a6a7fe602f4bfa87611e3002e99b446f7c6..0a96c5864e96fd1746cd8b2f9e1fa66da21f46a7 100644
--- a/ragconnect.base/build.gradle
+++ b/ragconnect.base/build.gradle
@@ -31,7 +31,7 @@ dependencies {
     implementation group: 'org.apache.logging.log4j', name: 'log4j-core', version: "${log4j_version}"
     implementation group: 'org.apache.logging.log4j', name: 'log4j-jul', version: "${log4j_version}"
     implementation group: 'com.github.spullara.mustache.java', name: 'compiler', version: "${mustache_java_version}"
-    runtimeOnly group: 'org.jastadd', name: 'jastadd', version: '2.3.4'
+    runtimeOnly group: 'org.jastadd', name: 'jastadd', version: '2.3.5'
     api group: 'net.sf.beaver', name: 'beaver-rt', version: '0.9.11'
 }
 
diff --git a/ragconnect.base/src/main/jastadd/Analysis.jrag b/ragconnect.base/src/main/jastadd/Analysis.jrag
index f686d3af0009baa0382e8ec3ba2d548b797e85a6..f55044dddb1582d41689a138818ad2114d2d6fd7 100644
--- a/ragconnect.base/src/main/jastadd/Analysis.jrag
+++ b/ragconnect.base/src/main/jastadd/Analysis.jrag
@@ -86,4 +86,13 @@ aspect Analysis {
 
   // --- needProxyToken ---
   syn boolean TokenComponent.needProxyToken() = !getDependencySourceDefinitionList().isEmpty() || getTokenEndpointDefinitionList().stream().anyMatch(TokenEndpointDefinition::shouldSendValue);
+
+  // --- effectiveUsedAt ---
+  coll Set<EndpointDefinition> MappingDefinition.effectiveUsedAt()
+    [new java.util.HashSet<EndpointDefinition>()]
+    root RagConnect;
+  EndpointDefinition contributes this
+    to MappingDefinition.effectiveUsedAt()
+    for each effectiveMappings();
+
 }
diff --git a/ragconnect.base/src/main/jastadd/Errors.jrag b/ragconnect.base/src/main/jastadd/Errors.jrag
index c3ecc643a4edc8319da82f49438dc46f167a3618..1aa3666c2b19bf446667f1a763bfb4824aa44f25 100644
--- a/ragconnect.base/src/main/jastadd/Errors.jrag
+++ b/ragconnect.base/src/main/jastadd/Errors.jrag
@@ -32,10 +32,6 @@ aspect Errors {
       when isAlreadyDefined()
       to RagConnect.errors();
 
-    SendTypeEndpointDefinition contributes error("At least one mapping needs to be used")
-      when effectiveMappings().isEmpty()
-      to RagConnect.errors();
-
     DependencyDefinition contributes error("Dependency definition already defined for " + getSource().containingTypeDecl().getName() + " with name " + getID())
       when isAlreadyDefined()
       to RagConnect.errors();
@@ -43,10 +39,6 @@ aspect 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();
-
-    DependencyDefinition contributes error("There must be a send endpoint definition targeting " + getSource().parentTypeypeAndName() + " for dependency definition " + getID())
-      when targetEndpointDefinition() == null
-      to RagConnect.errors();
 }
 
 aspect ErrorHelpers {
@@ -58,7 +50,7 @@ aspect ErrorHelpers {
     }
     return false;
   }
-  syn String TokenComponent.parentTypeypeAndName() = containingTypeDecl().getName() + "." + getName();
+  syn String TokenComponent.parentTypeDeclAndName() = containingTypeDecl().getName() + "." + getName();
 }
 
 aspect ErrorMessage {
diff --git a/ragconnect.base/src/main/jastadd/NameResolution.jrag b/ragconnect.base/src/main/jastadd/NameResolution.jrag
index 253f1dabf8ed0f15e2483aafde9144d992d87cf2..6b9171df4e85464b6711aa80e76b34f952d6864b 100644
--- a/ragconnect.base/src/main/jastadd/NameResolution.jrag
+++ b/ragconnect.base/src/main/jastadd/NameResolution.jrag
@@ -27,4 +27,20 @@ aspect NameResolution {
     return null;
   }
 
+  refine RefResolverStubs eq ASTNode.globallyResolveComponentByToken(String id) {
+    // return a Component. id is of the form 'parent_type_name + "." + child_type_name'
+    int dotIndex = id.indexOf(".");
+    String parentTypeName = id.substring(0, dotIndex);
+    String childTypeName = id.substring(dotIndex + 1);
+    TypeDecl type = program().resolveTypeDecl(parentTypeName);
+    // iterate over components and find the matching typeComponent
+    for (Component comp : type.getComponentList()) {
+      if (comp.getName().equals(childTypeName)) {
+        return comp;
+      }
+    }
+    System.err.println("Could not resolve Component '" + id + "'.");
+    return null;
+  }
+
 }
diff --git a/ragconnect.base/src/main/jastadd/Navigation.jrag b/ragconnect.base/src/main/jastadd/Navigation.jrag
index c6432a14d9258795f1f402366ec5c418993d4aa1..5bddf79ea3c633d7fa5933dde4ab6b665000630d 100644
--- a/ragconnect.base/src/main/jastadd/Navigation.jrag
+++ b/ragconnect.base/src/main/jastadd/Navigation.jrag
@@ -68,12 +68,16 @@ aspect Navigation {
   eq SendTypeEndpointDefinition.asSendTypeEndpointDefinition() = this;
 
   // --- targetEndpointDefinition ---
-  syn SendTokenEndpointDefinition DependencyDefinition.targetEndpointDefinition() {
+  syn EndpointDefinition DependencyDefinition.targetEndpointDefinition() {
     // resolve definition in here, as we do not need resolveMethod in any other place (yet)
     for (EndpointDefinition endpointDefinition : ragconnect().getEndpointDefinitionList()) {
       if (endpointDefinition.isSendTokenEndpointDefinition() &&
           endpointDefinition.asSendTokenEndpointDefinition().getToken().equals(this.getTarget())) {
-        return endpointDefinition.asSendTokenEndpointDefinition();
+        return endpointDefinition;
+      }
+      if (endpointDefinition.isSendTypeEndpointDefinition() &&
+          endpointDefinition.asSendTypeEndpointDefinition().getType().equals(this.getTarget())) {
+        return endpointDefinition;
       }
     }
     return null;
diff --git a/ragconnect.base/src/main/jastadd/RagConnect.relast b/ragconnect.base/src/main/jastadd/RagConnect.relast
index bd3e5c0d964881a7d7d49a68da83f1b27b79d0a7..426b90ee22b0aeeb1a9d0cb7a5d77e52838bc0ff 100644
--- a/ragconnect.base/src/main/jastadd/RagConnect.relast
+++ b/ragconnect.base/src/main/jastadd/RagConnect.relast
@@ -18,7 +18,7 @@ SendTypeEndpointDefinition : TypeEndpointDefinition;
 
 DependencyDefinition ::= <ID>;
 rel DependencyDefinition.Source <-> TokenComponent.DependencySourceDefinition*;
-rel DependencyDefinition.Target -> TokenComponent;
+rel DependencyDefinition.Target -> Component;
 
 MappingDefinition ::= <ID> FromType:MappingDefinitionType <FromVariableName> ToType:MappingDefinitionType <Content> ;
 abstract MappingDefinitionType ::= ;
diff --git a/ragconnect.base/src/main/jastadd/intermediate/Generation.jadd b/ragconnect.base/src/main/jastadd/intermediate/Generation.jadd
index 00ef291fa015596647a6f15756ce6f4380929501..444f0214af4821f704a3307227c9f1b04b072e47 100644
--- a/ragconnect.base/src/main/jastadd/intermediate/Generation.jadd
+++ b/ragconnect.base/src/main/jastadd/intermediate/Generation.jadd
@@ -22,12 +22,15 @@ aspect AttributesForMustache {
   syn String MEndpointDefinition.firstInputVarName();
   syn String MEndpointDefinition.parentTypeName();
   syn String MEndpointDefinition.entityName();
+  syn String MEndpointDefinition.updateMethod();
+  syn String MEndpointDefinition.writeMethod();
 
   eq MEndpointDefinition.getInnerMappingDefinition(int i).isLast() = i == getNumInnerMappingDefinition() - 1;
   eq MEndpointDefinition.getInnerMappingDefinition(int i).inputVarName() = i == 0 ? firstInputVarName() : getInnerMappingDefinition(i - 1).outputVarName();
 
   syn String MEndpointDefinition.connectParameterName() = "uriString";
   syn String MEndpointDefinition.connectMethod() = "connect" + entityName();
+  syn boolean MEndpointDefinition.isTypeEndpointDefinition() = endpointDef().isTypeEndpointDefinition();
 
   syn String MEndpointDefinition.disconnectMethod() {
     // if both (send and receive) are defined for the token, ensure methods with different names
@@ -54,6 +57,7 @@ aspect AttributesForMustache {
   syn boolean MEndpointDefinition.alwaysApply() = endpointDef().getAlwaysApply();
   syn String MEndpointDefinition.tokenName() = token().getName();
   syn String MEndpointDefinition.typeName() = type().getName();
+  syn String MEndpointDefinition.typeDeclName() = type().getTypeDecl().getName();
   syn MInnerMappingDefinition MEndpointDefinition.lastDefinition() = getInnerMappingDefinition(getNumInnerMappingDefinition() - 1);
   syn String MEndpointDefinition.lastDefinitionToType() = lastDefinition().toType();
   syn String MEndpointDefinition.lastResult() = lastDefinition().outputVarName();
@@ -61,7 +65,7 @@ aspect AttributesForMustache {
     if (lastDefinition().mappingDef().getToType().isArray()) {
       return "java.util.Arrays.equals(" + preemptiveExpectedValue() + ", " + lastResult() + ")";
     }
-    if (token().isPrimitiveType() && lastDefinition().mappingDef().getToType().isPrimitiveType()) {
+    if (endpointDef().isTokenEndpointDefinition() && token().isPrimitiveType() && lastDefinition().mappingDef().getToType().isPrimitiveType()) {
       return preemptiveExpectedValue() + " == " + lastResult();
     }
     if (lastDefinition().mappingDef().getToType().isPrimitiveType() || lastDefinition().mappingDef().isDefaultMappingDefinition()) {
@@ -91,36 +95,40 @@ aspect AttributesForMustache {
   eq MTokenReceiveDefinition.preemptiveReturn() = "return;";
   eq MTokenReceiveDefinition.endpointDef() = getReceiveTokenEndpointDefinition();
   eq MTokenReceiveDefinition.firstInputVarName() = "message";
+  eq MTokenReceiveDefinition.updateMethod() = null;
+  eq MTokenReceiveDefinition.writeMethod() = null;
 
   // --- MTokenSendDefinition ---
   eq MTokenSendDefinition.preemptiveExpectedValue() = lastValue();
   eq MTokenSendDefinition.preemptiveReturn() = "return false;";
   eq MTokenSendDefinition.endpointDef() = getSendTokenEndpointDefinition();
   eq MTokenSendDefinition.firstInputVarName() = "get" + tokenName() + "()";
+  eq MTokenSendDefinition.updateMethod() = "_update_" + tokenName();
+  eq MTokenSendDefinition.writeMethod() = "_writeLastValue_" + tokenName();
 
   syn String MTokenSendDefinition.sender() = "_sender_" + tokenName();
   syn String MTokenSendDefinition.lastValue() = "_lastValue" + tokenName();
-  syn String MTokenSendDefinition.updateMethod() = "_update_" + tokenName();
-  syn String MTokenSendDefinition.writeMethod() = "_writeLastValue_" + tokenName();
   syn String MTokenSendDefinition.tokenResetMethod() = "get" + tokenName() + "_reset";
   syn boolean MTokenSendDefinition.shouldSendValue() = endpointDef().asTokenEndpointDefinition().shouldSendValue();
 
   // MTypeReceiveDefinition
-  eq MTypeReceiveDefinition.preemptiveExpectedValue() = null;
-  eq MTypeReceiveDefinition.preemptiveReturn() = null;
+  eq MTypeReceiveDefinition.preemptiveExpectedValue() = "get" + typeName() + "()";
+  eq MTypeReceiveDefinition.preemptiveReturn() = "return;";
   eq MTypeReceiveDefinition.endpointDef() = getReceiveTypeEndpointDefinition();
-  eq MTypeReceiveDefinition.firstInputVarName() = null;
+  eq MTypeReceiveDefinition.firstInputVarName() = "message";
+  eq MTypeReceiveDefinition.updateMethod() = null;
+  eq MTypeReceiveDefinition.writeMethod() = null;
 
   // MTypeSendDefinition
-  eq MTypeSendDefinition.preemptiveExpectedValue() = null;
-  eq MTypeSendDefinition.preemptiveReturn() = null;
+  eq MTypeSendDefinition.preemptiveExpectedValue() = lastValue();
+  eq MTypeSendDefinition.preemptiveReturn() = "return false;";
   eq MTypeSendDefinition.endpointDef() = getSendTypeEndpointDefinition();
-  eq MTypeSendDefinition.firstInputVarName() = null;
+  eq MTypeSendDefinition.firstInputVarName() = "get" + typeName() + "()";
+  eq MTypeSendDefinition.updateMethod() = "_update_" + typeName();
+  eq MTypeSendDefinition.writeMethod() = "_writeLastValue_" + typeName();
 
   syn String MTypeSendDefinition.sender() = "_sender_" + typeName();
   syn String MTypeSendDefinition.lastValue() = "_lastValue" + typeName();
-  syn String MTypeSendDefinition.updateMethod() = "_update_" + typeName();
-  syn String MTypeSendDefinition.writeMethod() = "_writeLastValue_" + typeName();
   syn String MTypeSendDefinition.tokenResetMethod() = "get" + typeName() + "_reset";
   syn boolean MTypeSendDefinition.shouldSendValue() = endpointDef().asTypeEndpointDefinition().shouldSendValue();
 
@@ -130,13 +138,14 @@ aspect AttributesForMustache {
   syn String MMappingDefinition.fromType() = getMappingDefinition().getFromType().prettyPrint();
   syn String MMappingDefinition.fromVariableName() = getMappingDefinition().getFromVariableName();
   syn String MMappingDefinition.content() = getMappingDefinition().getContent();
+  syn boolean MMappingDefinition.isUsed() = !getMappingDefinition().effectiveUsedAt().isEmpty();
 
   // --- MDependencyDefinition ---
   syn String MDependencyDefinition.targetParentTypeName() = getDependencyDefinition().getTarget().containingTypeDecl().getName();
   syn String MDependencyDefinition.dependencyMethod() = "add" + capitalize(getDependencyDefinition().getID());
   syn String MDependencyDefinition.sourceParentTypeName() = getDependencyDefinition().getSource().containingTypeDecl().getName();
   syn String MDependencyDefinition.internalRelationPrefix() = "_internal_" + getDependencyDefinition().getID();
-  syn nta MTokenSendDefinition MDependencyDefinition.targetEndpointDefinition() {
+  syn nta MEndpointDefinition MDependencyDefinition.targetEndpointDefinition() {
     return getDependencyDefinition().targetEndpointDefinition().toMustache();
   }
 
@@ -208,6 +217,7 @@ aspect AttributesForMustache {
     }
   }
 
+  public abstract MEndpointDefinition EndpointDefinition.toMustache();
   syn lazy MTokenReceiveDefinition ReceiveTokenEndpointDefinition.toMustache() {
     MTokenReceiveDefinition result = new MTokenReceiveDefinition();
     result.setReceiveTokenEndpointDefinition(this);
diff --git a/ragconnect.base/src/main/jastadd/intermediate/Mappings.jrag b/ragconnect.base/src/main/jastadd/intermediate/Mappings.jrag
index 3479f11728195a2480109343e05292ccb1c6a45e..93f74e51d8acbf140b56e99e279a337506c3a6c0 100644
--- a/ragconnect.base/src/main/jastadd/intermediate/Mappings.jrag
+++ b/ragconnect.base/src/main/jastadd/intermediate/Mappings.jrag
@@ -10,9 +10,9 @@ aspect DefaultMappings {
         new JavaMappingDefinitionType(new SimpleJavaTypeUse(typeName));
   }
 
-  private DefaultMappingDefinition RagConnect.baseDefaultMappingDefinition(String fromTypeName, String toTypeName, String content) {
+  private DefaultMappingDefinition RagConnect.createDefaultMappingDefinition(String prefix, String fromTypeName, String toTypeName, String content) {
     DefaultMappingDefinition result = new DefaultMappingDefinition();
-    result.setID("_Default" + baseDefaultMappingTypeNamePart(fromTypeName) + "To" + baseDefaultMappingTypeNamePart(toTypeName) + "Mapping");
+    result.setID(prefix + baseDefaultMappingTypeNamePart(fromTypeName) + "To" + baseDefaultMappingTypeNamePart(toTypeName) + "Mapping");
     result.setFromType(baseDefaultMappingTypeFromName(fromTypeName));
     result.setFromVariableName("input");
     result.setToType(baseDefaultMappingTypeFromName(toTypeName));
@@ -20,6 +20,14 @@ aspect DefaultMappings {
     return result;
   }
 
+  private DefaultMappingDefinition RagConnect.baseDefaultMappingDefinition(String fromTypeName, String toTypeName, String content) {
+    return createDefaultMappingDefinition("_Default", fromTypeName, toTypeName, content);
+  }
+
+  private DefaultMappingDefinition RagConnect.treeDefaultMappingDefinition(String fromTypeName, String toTypeName, String content) {
+    return createDefaultMappingDefinition("_TreeDefault", fromTypeName, toTypeName, content);
+  }
+
   syn nta DefaultMappingDefinition RagConnect.defaultBytesToBooleanMapping() = baseDefaultMappingDefinition(
       "byte[]", "boolean", "return input[0] == (byte) 1;");
   syn nta DefaultMappingDefinition RagConnect.defaultBytesToIntMapping() = baseDefaultMappingDefinition(
@@ -37,6 +45,28 @@ aspect DefaultMappings {
   syn nta DefaultMappingDefinition RagConnect.defaultBytesToStringMapping() = baseDefaultMappingDefinition(
       "byte[]", "String", "return new String(input);");
 
+  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;"
+    );
+  }
+  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();"
+    );
+  }
+
   syn nta DefaultMappingDefinition RagConnect.defaultBooleanToBytesMapping() = baseDefaultMappingDefinition(
         "boolean", "byte[]", "return java.nio.ByteBuffer.allocate(1).put((byte) (input ? 1 : 0)).array();");
   syn nta DefaultMappingDefinition RagConnect.defaultIntToBytesMapping() = baseDefaultMappingDefinition(
@@ -155,7 +185,13 @@ aspect Mappings {
       case "char":
       case "Character": return ragconnect().defaultBytesToCharMapping();
       case "String": return ragconnect().defaultBytesToStringMapping();
-      default: return null;
+      default:
+        try {
+            TypeDecl typeDecl = program().resolveTypeDecl(targetTypeName());
+            return ragconnect().defaultBytesToTreeMapping(typeDecl.getName());
+        } catch (Exception ignore) {}
+        System.err.println("Could not find suitable default mapping for " + targetTypeName() + " on " + this);
+        return null;
     }
   }
   // --- suitableSendDefaultMapping ---
@@ -176,7 +212,13 @@ aspect Mappings {
       case "char":
       case "Character": return ragconnect().defaultCharToBytesMapping();
       case "String": return ragconnect().defaultStringToBytesMapping();
-      default: return null;
+      default:
+        try {
+            TypeDecl typeDecl = program().resolveTypeDecl(targetTypeName());
+            return ragconnect().defaultTreeToBytesMapping(typeDecl.getName());
+        } catch (Exception ignore) {}
+        System.err.println("Could not find suitable default mapping for " + targetTypeName() + " on " + this);
+        return null;
     }
   }
 
@@ -188,7 +230,9 @@ aspect Mappings {
            getMappingList().get(0).getFromType().prettyPrint();
   }
   eq ReceiveTypeEndpointDefinition.targetTypeName() {
-    return getMappingList().get(0).getFromType().prettyPrint();
+    return getMappingList().isEmpty() ?
+           getType().getTypeDecl().getName() :
+           getMappingList().get(0).getFromType().prettyPrint();
   }
   eq SendTokenEndpointDefinition.targetTypeName() {
     return getMappingList().isEmpty() ?
@@ -196,7 +240,9 @@ aspect Mappings {
            getMappingList().get(getMappingList().size() - 1).getToType().prettyPrint();
   }
   eq SendTypeEndpointDefinition.targetTypeName() {
-    return getMappingList().get(getMappingList().size() - 1).getToType().prettyPrint();
+    return getMappingList().isEmpty() ?
+           getType().getTypeDecl().getName() :
+           getMappingList().get(getMappingList().size() - 1).getToType().prettyPrint();
   }
 
 //  eq ReceiveFromRestDefinition.suitableDefaultMapping() {
@@ -251,8 +297,9 @@ aspect Mappings {
   // --- allMappingDefinitions ---
   syn java.util.List<MappingDefinition> RagConnect.allMappingDefinitions() {
     java.util.List<MappingDefinition> result = new java.util.ArrayList<>();
+    // user-defined mappings
     getMappingDefinitionList().iterator().forEachRemaining(result::add);
-    // byte[] conversion
+    // byte[] <-> primitive conversion
     result.add(defaultBytesToBooleanMapping());
     result.add(defaultBytesToIntMapping());
     result.add(defaultBytesToShortMapping());
@@ -269,6 +316,11 @@ aspect Mappings {
     result.add(defaultDoubleToBytesMapping());
     result.add(defaultCharToBytesMapping());
     result.add(defaultStringToBytesMapping());
+    // byte[] <-> tree conversion
+    for (TypeDecl typeDecl : getProgram().typeDecls()) {
+      result.add(defaultBytesToTreeMapping(typeDecl.getName()));
+      result.add(defaultTreeToBytesMapping(typeDecl.getName()));
+    }
 //    // string conversion
 //    result.add(defaultStringToBooleanMapping());
 //    result.add(defaultStringToIntMapping());
diff --git a/ragconnect.base/src/main/jastadd/parser/RagConnect.parser b/ragconnect.base/src/main/jastadd/parser/RagConnect.parser
index 4094bfa23d62f87540939f2619274ade313f6db4..a9d4809331c0ea20e11b0ec2a04dfd1f2c76e95b 100644
--- a/ragconnect.base/src/main/jastadd/parser/RagConnect.parser
+++ b/ragconnect.base/src/main/jastadd/parser/RagConnect.parser
@@ -51,11 +51,11 @@ ArrayList string_list
 ;
 
 DependencyDefinition dependency_definition
-  = ID.target_type DOT ID.target_token CAN_DEPEND_ON ID.source_type DOT ID.source_token AS ID.id SCOL
+  = ID.target_type DOT ID.target_component CAN_DEPEND_ON ID.source_type DOT ID.source_token AS ID.id SCOL
     {:
       DependencyDefinition result = new DependencyDefinition();
       result.setSource(TokenComponent.createRef(source_type + "." + source_token));
-      result.setTarget(TokenComponent.createRef(target_type + "." + target_token));
+      result.setTarget(Component.createRef(target_type + "." + target_component));
       result.setID(id);
       return result;
     :}
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 95e08da36c6f2f3becf3f9f009aec443a3c4d5fa..88bfbdc1689fe0c8040efc952832c0bc9ac58b02 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
@@ -274,9 +274,10 @@ public class Compiler extends AbstractCompiler {
   }
 
   private void mergeRagConnectDefinitions(RagConnect ragConnect, RagConnect ragConnectToIntegrate) {
-    ragConnect.setEndpointDefinitionList(ragConnectToIntegrate.getEndpointDefinitionList());
-    ragConnect.setMappingDefinitionList(ragConnectToIntegrate.getMappingDefinitionList());
-    ragConnect.setDependencyDefinitionList(ragConnectToIntegrate.getDependencyDefinitionList());
+    ragConnectToIntegrate.getEndpointDefinitionList().forEach(ragConnect::addEndpointDefinition);
+    ragConnectToIntegrate.getMappingDefinitionList().forEach(ragConnect::addMappingDefinition);
+    ragConnectToIntegrate.getDependencyDefinitionList().forEach(ragConnect::addDependencyDefinition);
+    ragConnect.setFileName(ragConnect.getFileName().isEmpty() ? ragConnectToIntegrate.getFileName() : ragConnect.getFileName() + " + " + ragConnectToIntegrate.getFileName());
   }
 
 //  protected void printUsage() {
diff --git a/ragconnect.base/src/main/resources/mappingDefinition.mustache b/ragconnect.base/src/main/resources/mappingDefinition.mustache
index fe26e949b7a8e446bf97947e1632624741a83726..47a9381e2e017b8cc7b7264db6784f2bab1227d2 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 {
-  {{{content}}}{{!maybe print line by line to get better indentation}}
+  {{{content}}}
 }
diff --git a/ragconnect.base/src/main/resources/ragconnect.mustache b/ragconnect.base/src/main/resources/ragconnect.mustache
index 41cde06f025abc9663ce1acae3ee7549f5987956..2cc2423cc2008ea80f6e614cd0eabea471692cbe 100644
--- a/ragconnect.base/src/main/resources/ragconnect.mustache
+++ b/ragconnect.base/src/main/resources/ragconnect.mustache
@@ -18,7 +18,9 @@ aspect RagConnect {
   {{/TypeSendDefinitions}}
 
   {{#MappingDefinitions}}
+  {{#isUsed}}
   {{> mappingDefinition}}
+  {{/isUsed}}
   {{/MappingDefinitions}}
 
   {{#DependencyDefinitions}}
@@ -65,7 +67,9 @@ aspect RagConnectObserver {
       node.trace().setReceiver(this);
     }
     void add(ConnectToken connectToken, ASTNode node, String attributeString, Runnable attributeCall) {
-      System.out.println("** observer add " + node + " on " + attributeString);
+      {{#loggingEnabledForWrites}}
+      System.out.println("** observer add: " + node + " on " + attributeString);
+      {{/loggingEnabledForWrites}}
       observedNodes.add(new RagConnectObserverEntry(connectToken, node, attributeString, attributeCall));
     }
     void remove(ConnectToken connectToken) {
@@ -78,12 +82,16 @@ aspect RagConnectObserver {
       if (event != ASTState.Trace.Event.INC_FLUSH_ATTR) {
         return;
       }
-      System.out.println("** observer check INC_FLUSH_ATTR event");
+      {{#loggingEnabledForWrites}}
+      System.out.println("** observer check INC_FLUSH_ATTR event: " + node + " on " + attribute);
+      {{/loggingEnabledForWrites}}
       // iterate through list, if matching pair. could maybe be more efficient.
       for (RagConnectObserverEntry entry : observedNodes) {
         if (entry.node.equals(node) && entry.attributeString.equals(attribute)) {
           // hit. call the attribute/nta-token
-          System.out.println("** observer hit " + entry.node + " on " + entry.attributeString);
+          {{#loggingEnabledForWrites}}
+          System.out.println("** observer hit: " + entry.node + " on " + entry.attributeString);
+          {{/loggingEnabledForWrites}}
           entry.attributeCall.run();
         }
       }
diff --git a/ragconnect.base/src/main/resources/receiveDefinition.mustache b/ragconnect.base/src/main/resources/receiveDefinition.mustache
index 96333bd3497e9468ab99ebae1aaf6e63951fc7f0..10f519129c8dec8abe587a1bcb5876c4c0224745 100644
--- a/ragconnect.base/src/main/resources/receiveDefinition.mustache
+++ b/ragconnect.base/src/main/resources/receiveDefinition.mustache
@@ -3,9 +3,12 @@ public boolean {{parentTypeName}}.{{connectMethod}}(String {{connectParameterNam
   java.util.function.Consumer<byte[]> consumer = message -> {
     {{> mappingApplication}}
     {{#loggingEnabledForReads}}
-    System.out.println("[Receive] " + {{connectParameterName}} + " -> {{tokenName}} = " + {{lastResult}});
+    System.out.println("[Receive] " + {{connectParameterName}} + " -> {{entityName}} = " + {{lastResult}});
     {{/loggingEnabledForReads}}
-    set{{tokenName}}({{lastResult}});
+    {{#isTypeEndpointDefinition}}
+    {{lastResult}}.treeResolveAll();
+    {{/isTypeEndpointDefinition}}
+    set{{entityName}}({{lastResult}});
   };
   ConnectToken connectToken;
   switch (scheme) {
diff --git a/ragconnect.base/src/main/resources/sendDefinition.mustache b/ragconnect.base/src/main/resources/sendDefinition.mustache
index bee941ff75a105431db457c64ff24b769439a179..62cfb7d5016fe5ed6ac2abadab45e9fdc3a45228 100644
--- a/ragconnect.base/src/main/resources/sendDefinition.mustache
+++ b/ragconnect.base/src/main/resources/sendDefinition.mustache
@@ -16,7 +16,7 @@ public boolean {{parentTypeName}}.{{connectMethod}}(String {{connectParameterNam
       final String topic = {{mqttHandlerAttribute}}().extractTopic(uri);
       {{sender}} = () -> {
         {{#loggingEnabledForWrites}}
-        System.out.println("[Send] {{tokenName}} = " + get{{tokenName}}() + " -> " + {{connectParameterName}});
+        System.out.println("[Send] {{entityName}} = " + get{{entityName}}() + " -> " + {{connectParameterName}});
         {{/loggingEnabledForWrites}}
         handler.publish(topic, {{lastValue}});
       };
@@ -45,7 +45,7 @@ public boolean {{parentTypeName}}.{{connectMethod}}(String {{connectParameterNam
   connectTokens.computeIfAbsent(this, astNode -> new java.util.HashMap<java.net.URI, ConnectToken>())
                .put(uri, connectToken);
   {{#incrementalOptionActive}}
-  _ragConnectObserver().add(connectToken, this, "{{parentTypeName}}.get{{tokenName}}()", () -> {
+  _ragConnectObserver().add(connectToken, this, "get{{entityName}}", () -> {
     if (this.{{updateMethod}}()) {
       this.{{writeMethod}}();
     }
diff --git a/ragconnect.tests/build.gradle b/ragconnect.tests/build.gradle
index 28b71c074898d85d9546772476e56baae6a2e034..30fb2237ee167f74879bbb8903553d4eaaf1ceb1 100644
--- a/ragconnect.tests/build.gradle
+++ b/ragconnect.tests/build.gradle
@@ -36,13 +36,16 @@ repositories {
 dependencies {
     implementation project(':ragconnect.base')
 
-//    runtime group: 'org.jastadd', name: 'jastadd', version: '2.3.4'
-    runtime fileTree(include: ['jastadd2.jar'], dir: '../libs/')
+    runtime group: 'org.jastadd', name: 'jastadd', version: '2.3.5'
 
     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'
     testImplementation group: 'org.assertj', name: 'assertj-core', version: '3.12.1'
 
+    // jackson (for serialization of types)
+    implementation group: 'com.fasterxml.jackson.core', name: 'jackson-core', version: '2.12.1'
+    implementation group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: '2.12.1'
+
     // mqtt
     testImplementation group: 'org.fusesource.mqtt-client', name: 'mqtt-client', version: '1.15'
 
@@ -51,6 +54,7 @@ dependencies {
     testImplementation group: 'org.apache.logging.log4j', name: 'log4j-slf4j-impl', version: '2.11.2'
     testImplementation group: 'org.glassfish.jersey.core', name: 'jersey-client', version: '2.31'
     testImplementation group: 'org.glassfish.jersey.inject', name: 'jersey-hk2', version: '2.31'
+    testImplementation group: 'javax.activation', name: 'activation', version: '1.1.1'
 
     testImplementation group: 'net.sf.beaver', name: 'beaver-rt', version: '0.9.11'
     api group: 'com.google.protobuf', name: 'protobuf-java', version: '3.0.0'
@@ -309,3 +313,95 @@ task compileMapping(type: RagConnectTest) {
         inputFiles = [file('src/test/01-input/mapping/Test.jadd')]
     }
 }
+
+// --- Test: tree-manual ---
+task compileTreeManual(type: RagConnectTest, dependsOn: ':ragconnect.base:compileJava') {
+    ragconnect {
+        outputDir = file('src/test/02-after-ragconnect/tree')
+        inputFiles = [file('src/test/01-input/tree/Test.relast'),
+                      file('src/test/01-input/tree/Test.connect'),
+                      file('src/test/01-input/tree/TestDependencies.connect')]
+        rootNode = 'Root'
+    }
+    relast {
+        useJastAddNames = true
+        grammarName = 'src/test/03-after-relast/tree/tree'
+        serializer = 'jackson'
+    }
+    jastadd {
+        jastAddList = 'JastAddList'
+        packageName = 'tree.ast'
+        inputFiles = [file('src/test/01-input/tree/Test.jadd')]
+    }
+}
+
+// --- Test: tree-incremental ---
+task compileTreeIncremental(type: RagConnectTest) {
+    ragconnect {
+        outputDir = file('src/test/02-after-ragconnect/treeInc')
+        inputFiles = [file('src/test/01-input/tree/Test.relast'),
+                      file('src/test/01-input/tree/Test.connect')]
+        rootNode = 'Root'
+    }
+    relast {
+        useJastAddNames = true
+        grammarName = 'src/test/03-after-relast/treeInc/treeInc'
+        serializer = 'jackson'
+    }
+    jastadd {
+        jastAddList = 'JastAddList'
+        packageName = 'treeInc.ast'
+        inputFiles = [file('src/test/01-input/tree/Test.jadd')]
+        extraOptions = ['--tracing=cache,flush',
+                        '--incremental=param',
+                        '--cache=all',
+                        '--rewrite=cnta',
+                        '--flush=full']
+    }
+}
+
+// --- Test: tree-allowed-tokens ---
+task compileTreeAllowedTokens(type: RagConnectTest) {
+    ragconnect {
+        outputDir = file('src/test/02-after-ragconnect/treeAllowedTokens')
+        inputFiles = [file('src/test/01-input/treeAllowedTokens/Test.relast'),
+                      file('src/test/01-input/treeAllowedTokens/Test.connect'),
+                      file('src/test/01-input/treeAllowedTokens/TestDependencies.connect')]
+        rootNode = 'Root'
+    }
+    relast {
+        useJastAddNames = true
+        grammarName = 'src/test/03-after-relast/treeAllowedTokens/treeAllowedTokens'
+        serializer = 'jackson'
+    }
+    jastadd {
+        jastAddList = 'JastAddList'
+        packageName = 'treeAllowedTokens.ast'
+        inputFiles = [file('src/test/01-input/treeAllowedTokens/Test.jadd')]
+    }
+}
+
+// --- Test: tree-allowed-tokens-incremental ---
+task compileTreeAllowedTokensIncremental(type: RagConnectTest) {
+    ragconnect {
+        outputDir = file('src/test/02-after-ragconnect/treeAllowedTokensInc')
+        inputFiles = [file('src/test/01-input/treeAllowedTokens/Test.relast'),
+                      file('src/test/01-input/treeAllowedTokens/Test.connect')]
+        rootNode = 'Root'
+    }
+    relast {
+        useJastAddNames = true
+        grammarName = 'src/test/03-after-relast/treeAllowedTokensInc/treeAllowedTokensInc'
+        serializer = 'jackson'
+    }
+    jastadd {
+        jastAddList = 'JastAddList'
+        packageName = 'treeAllowedTokensInc.ast'
+        inputFiles = [file('src/test/01-input/treeAllowedTokens/Test.jadd')]
+        extraOptions = ['--tracing=cache,flush',
+                        '--incremental=param',
+                        '--cache=all',
+                        '--rewrite=cnta',
+                        '--flush=full']
+    }
+}
diff --git a/ragconnect.tests/src/test/01-input/tree/README.md b/ragconnect.tests/src/test/01-input/tree/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..060a0e39663f01d33bffb960c316c7de8da59f68
--- /dev/null
+++ b/ragconnect.tests/src/test/01-input/tree/README.md
@@ -0,0 +1,4 @@
+# Tree
+
+Idea: send and receive subtrees, test different relations within the subtree which was sent.
+Once without incremental evaluation (i.e., using manual dependencies), and the other time with incremental evaluation
diff --git a/ragconnect.tests/src/test/01-input/tree/Test.connect b/ragconnect.tests/src/test/01-input/tree/Test.connect
new file mode 100644
index 0000000000000000000000000000000000000000..857f6297f12249451b026051201ebfc004c2f6a6
--- /dev/null
+++ b/ragconnect.tests/src/test/01-input/tree/Test.connect
@@ -0,0 +1,2 @@
+send tree SenderRoot.Alfa ;
+receive tree ReceiverRoot.Alfa ;
diff --git a/ragconnect.tests/src/test/01-input/tree/Test.jadd b/ragconnect.tests/src/test/01-input/tree/Test.jadd
new file mode 100644
index 0000000000000000000000000000000000000000..c9e47ed7d9c0402992882e3d60a78f08044bf063
--- /dev/null
+++ b/ragconnect.tests/src/test/01-input/tree/Test.jadd
@@ -0,0 +1,81 @@
+aspect Computation {
+  syn Alfa SenderRoot.getAlfa() {
+    Alfa result = new Alfa();
+    for (int i = 0; i < 4; i++) {
+      result.addBravo(new Bravo().setID(i));
+      result.addCharlie(new Charlie().setID(i));
+      result.addDelta(new Delta().setID(i));
+    }
+    Bravo inputBravo = result.getBravo(getInput());
+    Charlie inputCharlie = result.getCharlie(getInput());
+    Delta inputDelta = result.getDelta(getInput());
+
+    // rel Alfa.MyBravo -> Bravo ;
+    result.setMyBravo(inputBravo);
+    // rel Alfa.OptionalBravo? -> Bravo ;
+    result.setOptionalBravo(inputBravo);
+    // rel Alfa.MultiBravo* -> Bravo ;
+    result.addMultiBravo(inputBravo);
+    result.addMultiBravo(result.getBravo(getInput() + 1));
+
+    // rel Charlie.MyAlfa? -> Alfa ;
+    inputCharlie.setMyAlfa(result);
+
+    // rel Alfa.SingleBi1Delta <-> Delta.SingleBack1Alfa? ;
+    result.setSingleBi1Delta(inputDelta);
+    // rel Alfa.MultiBi2Delta* <-> Delta.SingleBack2Alfa? ;
+    result.addMultiBi2Delta(inputDelta);
+    result.addMultiBi2Delta(result.getDelta(getInput() + 1));
+    // rel Alfa.MultiBi3Delta* <-> Delta.MultiBack3Alfa* ;
+    result.addMultiBi3Delta(inputDelta);
+    result.addMultiBi3Delta(result.getDelta(getInput() + 1));
+
+    // rel Alfa.Myself -> Alfa ;
+    result.setMyself(result);
+
+    // rel Bravo.MyCharlie -> Charlie ;
+    for (int i = 0; i < 4; i++) {
+      if (i == getInput()) {
+        result.getBravo(i).setMyCharlie(inputCharlie);
+      } else {
+        result.getBravo(i).setMyCharlie(result.getCharlie(0));
+      }
+    }
+    // rel Bravo.OptionalCharlie? -> Charlie ;
+    inputBravo.setOptionalCharlie(inputCharlie);
+    // rel Bravo.MultiCharlie* -> Charlie ;
+    inputBravo.addMultiCharlie(inputCharlie);
+    inputBravo.addMultiCharlie(result.getCharlie(getInput() + 1));
+
+    // rel Bravo.SingleBi1Delta? <-> Delta.SingleBack1Bravo? ;
+    inputBravo.setSingleBi1Delta(inputDelta);
+    // rel Bravo.MultiBi2Delta* <-> Delta.SingleBack2Bravo? ;
+    inputBravo.addMultiBi2Delta(inputDelta);
+    inputBravo.addMultiBi2Delta(result.getDelta(getInput() + 1));
+    // rel Bravo.MultiBi3Delta* <-> Delta.MultiBack3Bravo* ;
+    inputBravo.addMultiBi3Delta(inputDelta);
+    inputBravo.addMultiBi3Delta(result.getDelta(getInput() + 1));
+    result.getBravo(getInput() + 1).addMultiBi3Delta(inputDelta);
+    result.getBravo(getInput() + 1).addMultiBi3Delta(result.getDelta(getInput() + 1));
+
+    return result;
+  }
+
+  syn boolean ASTNode.isNameable() = false;
+  eq Nameable.isNameable() = true;
+}
+
+aspect Testing {
+  class ReceiverRoot implements org.jastadd.ragconnect.tests.AbstractTreeTest.TestWrapperReceiverRoot {}
+  class Alfa implements org.jastadd.ragconnect.tests.AbstractTreeTest.TestWrapperAlfa {}
+  class Bravo implements org.jastadd.ragconnect.tests.AbstractTreeTest.TestWrapperBravo {}
+  class Charlie implements org.jastadd.ragconnect.tests.AbstractTreeTest.TestWrapperCharlie {}
+  class Delta implements org.jastadd.ragconnect.tests.AbstractTreeTest.TestWrapperDelta {}
+}
+
+aspect NameResolution {
+  @Override
+  protected String Nameable.customID() {
+    return getClass().getSimpleName() + getID();
+  }
+}
diff --git a/ragconnect.tests/src/test/01-input/tree/Test.relast b/ragconnect.tests/src/test/01-input/tree/Test.relast
new file mode 100644
index 0000000000000000000000000000000000000000..f65444660383fa1f44d3b4519f01d16098164bf3
--- /dev/null
+++ b/ragconnect.tests/src/test/01-input/tree/Test.relast
@@ -0,0 +1,36 @@
+Root ::= SenderRoot* ReceiverRoot* ;
+
+Nameable ::= <ID:int> ;
+SenderRoot : Nameable ::= <Input:int> /Alfa/ ;
+
+ReceiverRoot : Nameable ::= Alfa ;
+Alfa : Nameable ::= <Value:String> Bravo* Charlie* Delta* ;
+
+Bravo : Nameable ;
+Charlie : Nameable ;
+Delta : Nameable ;
+
+rel Alfa.Myself -> Alfa ;
+
+// Alfa -> Bravo
+rel Alfa.MyBravo -> Bravo ;
+rel Alfa.OptionalBravo? -> Bravo ;
+rel Alfa.MultiBravo* -> Bravo ;
+
+// Charlie -> Alfa
+rel Charlie.MyAlfa? -> Alfa ;
+
+// Alfa <-> Delta
+rel Alfa.SingleBi1Delta <-> Delta.SingleBack1Alfa? ;
+rel Alfa.MultiBi2Delta* <-> Delta.SingleBack2Alfa? ;
+rel Alfa.MultiBi3Delta* <-> Delta.MultiBack3Alfa* ;
+
+// Bravo -> Charlie
+rel Bravo.MyCharlie -> Charlie ;
+rel Bravo.OptionalCharlie? -> Charlie ;
+rel Bravo.MultiCharlie* -> Charlie ;
+
+// Bravo <-> Delta
+rel Bravo.SingleBi1Delta? <-> Delta.SingleBack1Bravo? ;
+rel Bravo.MultiBi2Delta* <-> Delta.SingleBack2Bravo? ;
+rel Bravo.MultiBi3Delta* <-> Delta.MultiBack3Bravo* ;
diff --git a/ragconnect.tests/src/test/01-input/tree/TestDependencies.connect b/ragconnect.tests/src/test/01-input/tree/TestDependencies.connect
new file mode 100644
index 0000000000000000000000000000000000000000..aa5df696a54404332115d0069279a07f886f2e22
--- /dev/null
+++ b/ragconnect.tests/src/test/01-input/tree/TestDependencies.connect
@@ -0,0 +1 @@
+SenderRoot.Alfa canDependOn SenderRoot.Input as InputDependency ;
diff --git a/ragconnect.tests/src/test/01-input/treeAllowedTokens/README.md b/ragconnect.tests/src/test/01-input/treeAllowedTokens/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..c215115106a71daf358568368426b33421cfdd65
--- /dev/null
+++ b/ragconnect.tests/src/test/01-input/treeAllowedTokens/README.md
@@ -0,0 +1,4 @@
+# Tree Allowed Tokens
+
+Idea: send and receive subtrees, test different token types within the subtree which was sent.
+Once without incremental evaluation (i.e., using manual dependencies), and the other time with incremental evaluation
diff --git a/ragconnect.tests/src/test/01-input/treeAllowedTokens/Test.connect b/ragconnect.tests/src/test/01-input/treeAllowedTokens/Test.connect
new file mode 100644
index 0000000000000000000000000000000000000000..d872ce56c59097e678e6cff0b25ec422130b6129
--- /dev/null
+++ b/ragconnect.tests/src/test/01-input/treeAllowedTokens/Test.connect
@@ -0,0 +1,26 @@
+send tree SenderRoot.Alfa ;
+receive tree ReceiverRoot.Alfa ;
+
+receive SenderRoot.Input1WhenFlagIsTrue ;
+receive SenderRoot.Input1WhenFlagIsFalse ;
+receive SenderRoot.Input2 ;
+receive SenderRoot.Input3 ;
+
+send tree SenderRoot.AlfaPrimitive using Alfa2String ;
+receive tree ReceiverRoot.AlfaPrimitive using String2Alfa ;
+
+Alfa2String maps Alfa alfa to String {:
+  StringBuilder sb = new StringBuilder();
+  sb.append(alfa.getID()).append(":").append(alfa.getStringValue()).append(";");
+  return sb.toString();
+:}
+
+String2Alfa maps String s to Alfa {:
+  int colonIndex = s.indexOf(":");
+  int id = Integer.parseInt(s.substring(0, colonIndex));
+  String value = s.substring(colonIndex + 1, s.length() - 1);
+  Alfa result = new Alfa();
+  result.setID(id);
+  result.setStringValue(value);
+  return result;
+:}
diff --git a/ragconnect.tests/src/test/01-input/treeAllowedTokens/Test.jadd b/ragconnect.tests/src/test/01-input/treeAllowedTokens/Test.jadd
new file mode 100644
index 0000000000000000000000000000000000000000..e5f8750c50a076498fea4398899fbebb5999620e
--- /dev/null
+++ b/ragconnect.tests/src/test/01-input/treeAllowedTokens/Test.jadd
@@ -0,0 +1,48 @@
+aspect Computation {
+  syn Alfa SenderRoot.getAlfa() {
+    Alfa result = new Alfa();
+
+    // Inputs: <Flag:boolean> <Input1WhenFlagIsTrue:int> <Input1WhenFlagIsFalse:int> <Input2:String> <Input3:double>
+
+    result.setBooleanValue(getFlag()); // boolean
+    result.setIntValue(getFlag() ? getInput1WhenFlagIsTrue() : getInput1WhenFlagIsFalse()); // int
+    result.setShortValue((short) (getFlag() ? getInput1WhenFlagIsTrue() : getInput1WhenFlagIsFalse())); // short
+    result.setLongValue(getFlag() ? getInput1WhenFlagIsTrue() : getInput1WhenFlagIsFalse()); // long
+    result.setFloatValue((float) getInput3()); // float
+    result.setDoubleValue(getInput3()); // double
+    result.setCharValue(getInput2().charAt(0)); // char
+    result.setStringValue(getInput2()); // String
+    // needed format: 2011-12-03T10:15:30Z
+    result.setInstantValue(java.time.Instant.parse(getInput2())); // java.time.Instant
+    // setting days
+    result.setPeriodValue(java.time.Period.of(0, 0, getFlag() ? getInput1WhenFlagIsTrue() : getInput1WhenFlagIsFalse())); // java.time.Period
+    result.setEnumValue(getFlag() ? MyEnum.TRUE : MyEnum.FALSE); // MyEnum
+
+    return result;
+  }
+
+  syn Alfa SenderRoot.getAlfaPrimitive() {
+    Alfa result = new Alfa();
+    result.setStringValue(getInput2());
+    return result;
+  }
+
+  syn boolean ASTNode.isNameable() = false;
+  eq Nameable.isNameable() = true;
+}
+
+aspect Enum {
+  public enum MyEnum { FALSE, TRUE; }
+}
+aspect Testing {
+  class ReceiverRoot implements org.jastadd.ragconnect.tests.AbstractTreeAllowedTokensTest.TestWrapperReceiverRoot {}
+  class Alfa implements org.jastadd.ragconnect.tests.AbstractTreeAllowedTokensTest.TestWrapperAlfa {}
+}
+
+aspect NameResolution {
+  // this should most likely be generated
+  @Override
+  protected String Alfa.customID() {
+    return "0";
+  }
+}
diff --git a/ragconnect.tests/src/test/01-input/treeAllowedTokens/Test.relast b/ragconnect.tests/src/test/01-input/treeAllowedTokens/Test.relast
new file mode 100644
index 0000000000000000000000000000000000000000..591ebb22441a29b5a8561b0ea539367a44d9d2e8
--- /dev/null
+++ b/ragconnect.tests/src/test/01-input/treeAllowedTokens/Test.relast
@@ -0,0 +1,8 @@
+Root ::= SenderRoot* ReceiverRoot* ;
+
+Nameable ::= <ID:int> ;
+SenderRoot : Nameable ::= <Flag:boolean> <Input1WhenFlagIsTrue:int> <Input1WhenFlagIsFalse:int> <Input2:String> <Input3:double> /Alfa/ /AlfaPrimitive:Alfa/ ;
+
+ReceiverRoot : Nameable ::= Alfa AlfaPrimitive:Alfa ;
+
+Alfa : Nameable ::= <BooleanValue:boolean> <IntValue:int> <ShortValue:short> <LongValue:long> <FloatValue:float> <DoubleValue:double> <CharValue:char> <StringValue:String> <InstantValue:java.time.Instant> <PeriodValue:java.time.Period> <EnumValue:MyEnum> ;
diff --git a/ragconnect.tests/src/test/01-input/treeAllowedTokens/TestDependencies.connect b/ragconnect.tests/src/test/01-input/treeAllowedTokens/TestDependencies.connect
new file mode 100644
index 0000000000000000000000000000000000000000..afa4a832ca4bde7ca584c8b1d8f0fa69ee0955a8
--- /dev/null
+++ b/ragconnect.tests/src/test/01-input/treeAllowedTokens/TestDependencies.connect
@@ -0,0 +1,7 @@
+SenderRoot.Alfa canDependOn SenderRoot.Flag as FlagDependency ;
+SenderRoot.Alfa canDependOn SenderRoot.Input1WhenFlagIsTrue as Input1WhenFlagIsTrueDependency ;
+SenderRoot.Alfa canDependOn SenderRoot.Input1WhenFlagIsFalse as Input1WhenFlagIsFalseDependency ;
+SenderRoot.Alfa canDependOn SenderRoot.Input2 as Input2Dependency ;
+SenderRoot.Alfa canDependOn SenderRoot.Input3 as Input3Dependency ;
+
+SenderRoot.AlfaPrimitive canDependOn SenderRoot.Input2 as PrimitiveInput2Dependency ;
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 36baf2cf5ffda1fbb6225daf8f054a40c3a2c331..f2f5ddee691854ef40f8b3ede6820a38e0262c2b 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,10 +1,7 @@
 package org.jastadd.ragconnect.tests;
 
 import defaultOnlyRead.ast.MqttHandler;
-import org.junit.jupiter.api.AfterEach;
-import org.junit.jupiter.api.BeforeAll;
-import org.junit.jupiter.api.Tag;
-import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.*;
 
 import java.io.IOException;
 import java.util.concurrent.TimeUnit;
@@ -18,20 +15,22 @@ import java.util.concurrent.TimeUnit;
 public abstract class AbstractMqttTest {
 
   static boolean checkDone = false;
-  static Boolean checkResult;
+  static boolean checkResult;
+  static MqttHandler publisher;
 
   @BeforeAll
-  public static void checkMqttConnection() {
+  public static void createPublishAndOnceCheckMqttConnection() {
+    try {
+      publisher = new MqttHandler("Publisher")
+          .dontSendWelcomeMessage()
+          .setHost(TestUtils.getMqttHost());
+      checkResult = true;
+    } catch (IOException e) {
+      checkResult = false;
+    }
     if (!checkDone) {
       checkDone = true;
-      try {
-        checkResult = new MqttHandler()
-            .dontSendWelcomeMessage()
-            .setHost(TestUtils.getMqttHost())
-            .waitUntilReady(2, TimeUnit.SECONDS);
-      } catch (IOException e) {
-        checkResult = false;
-      }
+      checkResult &= publisher.waitUntilReady(2, TimeUnit.SECONDS);
     }
     if (!checkResult) {
       throw new IllegalStateException("Mqtt Broker not ready!");
@@ -52,6 +51,10 @@ public abstract class AbstractMqttTest {
     communicateSendInitialValue();
   }
 
+  /**
+   * Actual test code for communication when sending initial value.
+   * @throws InterruptedException because of TestUtils.waitForMqtt()
+   */
   protected abstract void communicateSendInitialValue() throws InterruptedException;
 
   @Tag("mqtt")
@@ -63,9 +66,15 @@ public abstract class AbstractMqttTest {
     communicateOnlyUpdatedValue();
   }
 
+  /**
+   * Actual test code for communication without sending any value upon connecting.
+   * @throws InterruptedException because of TestUtils.waitForMqtt()
+   */
   protected abstract void communicateOnlyUpdatedValue() throws InterruptedException;
 
-
+  /**
+   * Create the model, and set required default values.
+   */
   protected abstract void createModel();
 
   /**
@@ -79,7 +88,7 @@ public abstract class AbstractMqttTest {
    *
    * And then add dependencies, initialise receiver, add connections to those receivers,
    * and finally call generated connect* methods on model elements.
-   * @param writeCurrentValue
+   * @param writeCurrentValue if the initial/current value shall be sent upon connecting
    */
   protected abstract void setupReceiverAndConnect(boolean writeCurrentValue) throws IOException;
 
@@ -101,4 +110,11 @@ public abstract class AbstractMqttTest {
    */
   protected abstract void closeConnections();
 
+  @AfterAll
+  public static void closePublisher() {
+    if (publisher != null) {
+      publisher.close();
+    }
+  }
+
 }
diff --git a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/AbstractTreeAllowedTokensTest.java b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/AbstractTreeAllowedTokensTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..e87a298fe92e0019fa7da2012d6630590d7a348e
--- /dev/null
+++ b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/AbstractTreeAllowedTokensTest.java
@@ -0,0 +1,216 @@
+package org.jastadd.ragconnect.tests;
+
+import java.io.IOException;
+import java.time.Instant;
+import java.time.Period;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+/**
+ * Base class for test cases "tree allowed tokens manual" and "tree allowed tokens incremental".
+ *
+ * @author rschoene - Initial contribution
+ */
+public abstract class AbstractTreeAllowedTokensTest extends AbstractMqttTest {
+
+  protected static final String TOPIC_INPUT1TRUE = "input1/true";
+  protected static final String TOPIC_INPUT1FALSE = "input1/false";
+  protected static final String TOPIC_INPUT2 = "input2";
+  protected static final String TOPIC_INPUT3 = "input3";
+  protected static final String TOPIC_ALFA = "alfa";
+  protected static final String TOPIC_ALFA_PRIMITIVE = "primitive";
+
+  protected static final String INSTANT_A = "1999-12-03T10:15:30Z";
+  protected static final String INSTANT_B = "2011-12-03T10:15:30Z";
+  protected static final String INSTANT_C = "2012-12-03T10:15:30Z";
+
+  protected TestWrapperReceiverRoot receiverRoot;
+
+  public interface TestWrapperReceiverRoot {
+    TestWrapperAlfa getAlfa();
+    TestWrapperAlfa getAlfaPrimitive();
+    boolean connectAlfa(String mqttUri) throws IOException;
+    boolean connectAlfaPrimitive(String mqttUri) throws IOException;
+  }
+  public interface TestWrapperAlfa {
+    boolean getBooleanValue();
+    int getIntValue();
+    short getShortValue();
+    long getLongValue();
+    float getFloatValue();
+    double getDoubleValue();
+    String getStringValue();
+    char getCharValue();
+    Instant getInstantValue();
+    Period getPeriodValue();
+  }
+
+  @Override
+  protected void communicateSendInitialValue() throws InterruptedException {
+    checkTree(1, false, 0, INSTANT_A, 0);
+    checkPrimitiveTree(1, INSTANT_A);
+
+    // flag: false. sendInput1WhenFalse(2)
+    sendInput1WhenFalse(2);
+    checkTree(2, false, 2, INSTANT_A, 0);
+    checkPrimitiveTree(1, INSTANT_A);
+
+    // flag: false. setFlag(false) -> no change
+    setFlag(false);
+    checkTree(2, false, 2, INSTANT_A, 0);
+    checkPrimitiveTree(1, INSTANT_A);
+
+    // flag: false. setFlag(true)
+    setFlag(true);
+    checkTree(3, true, 0, INSTANT_A, 0);
+    checkPrimitiveTree(1, INSTANT_A);
+
+    // flag: true. sendInput1WhenFalse(3) -> no change
+    sendInput1WhenFalse(3);
+    checkTree(3, true, 0, INSTANT_A, 0);
+    checkPrimitiveTree(1, INSTANT_A);
+
+    // flag: true. sendInput1WhenTrue(4)
+    sendInput1WhenTrue(4);
+    checkTree(4, true, 4, INSTANT_A, 0);
+    checkPrimitiveTree(1, INSTANT_A);
+
+    // sendInput2(INSTANT_B)
+    sendInput2(INSTANT_B);
+    checkTree(5, true, 4, INSTANT_B, 0);
+    checkPrimitiveTree(2, INSTANT_B);
+
+    // sendInput2(INSTANT_B) -> no change
+    sendInput2(INSTANT_B);
+    checkTree(5, true, 4, INSTANT_B, 0);
+    checkPrimitiveTree(2, INSTANT_B);
+
+    // sendInput3(5.1)
+    sendInput3(5.1);
+    checkTree(6, true, 4, INSTANT_B, 5.1);
+    checkPrimitiveTree(2, INSTANT_B);
+
+    // sendInput3(5.1) -> no change
+    sendInput3(5.1);
+    checkTree(6, true, 4, INSTANT_B, 5.1);
+    checkPrimitiveTree(2, INSTANT_B);
+  }
+
+  @Override
+  protected void communicateOnlyUpdatedValue() throws InterruptedException {
+    checkTree(0, false, null, null, 0);
+    checkPrimitiveTree(0, null);
+
+    // flag: false. sendInput1WhenFalse(12)
+    sendInput1WhenFalse(12);
+    checkTree(1, false, 12, INSTANT_A, 0);
+    checkPrimitiveTree(0, null);
+
+    // flag: false. setFlag(false) -> no change
+    setFlag(false);
+    checkTree(1, false, 12, INSTANT_A, 0);
+    checkPrimitiveTree(0, null);
+
+    // flag: false. setFlag(true)
+    setFlag(true);
+    checkTree(2, true, 0, INSTANT_A, 0);
+    checkPrimitiveTree(0, null);
+
+    // flag: true. sendInput1WhenFalse(13) -> no change
+    sendInput1WhenFalse(13);
+    checkTree(2, true, 0, INSTANT_A, 0);
+    checkPrimitiveTree(0, null);
+
+    // flag: true. sendInput1WhenTrue(14)
+    sendInput1WhenTrue(14);
+    checkTree(3, true, 14, INSTANT_A, 0);
+    checkPrimitiveTree(0, null);
+
+    // sendInput2(INSTANT_C)
+    sendInput2(INSTANT_C);
+    checkTree(4, true, 14, INSTANT_C, 0);
+    checkPrimitiveTree(1, INSTANT_C);
+
+    // sendInput2(INSTANT_C) -> no change
+    sendInput2(INSTANT_C);
+    checkTree(4, true, 14, INSTANT_C, 0);
+    checkPrimitiveTree(1, INSTANT_C);
+
+    // sendInput3(15.1)
+    sendInput3(15.1);
+    checkTree(5, true, 14, INSTANT_C, 15.1);
+    checkPrimitiveTree(1, INSTANT_C);
+
+    // sendInput3(15.1) -> no change
+    sendInput3(15.1);
+    checkTree(5, true, 14, INSTANT_C, 15.1);
+    checkPrimitiveTree(1, INSTANT_C);
+  }
+
+  protected void sendInput1WhenFalse(int value) {
+    publisher.publish(TOPIC_INPUT1FALSE, TestUtils.DefaultMappings.IntToBytes(value));
+  }
+
+  protected void sendInput1WhenTrue(int value) {
+    publisher.publish(TOPIC_INPUT1TRUE, TestUtils.DefaultMappings.IntToBytes(value));
+  }
+
+  protected void sendInput2(String value) {
+    publisher.publish(TOPIC_INPUT2, TestUtils.DefaultMappings.StringToBytes(value));
+  }
+
+  protected void sendInput3(double value) {
+    publisher.publish(TOPIC_INPUT3, TestUtils.DefaultMappings.DoubleToBytes(value));
+  }
+
+  protected abstract void setFlag(boolean value);
+
+  protected void checkTree(int expectedCount, boolean expectedBooleanValue, Integer expectedIntValue, String expectedStringValue, double expectedDoubleValue) throws InterruptedException {
+    TestUtils.waitForMqtt();
+
+    assertEquals(expectedCount, data.numberOfTrees);
+    if (expectedStringValue == null) {
+      assertNull(receiverRoot.getAlfa());
+    } else {
+      assertNotNull(receiverRoot.getAlfa());
+      TestWrapperAlfa alfa = receiverRoot.getAlfa();
+
+      assertEquals(expectedBooleanValue, alfa.getBooleanValue());
+      assertEquals(expectedIntValue, alfa.getIntValue());
+      assertEquals(expectedIntValue.shortValue(), alfa.getShortValue());
+      assertEquals(expectedIntValue.longValue(), alfa.getLongValue());
+
+      assertEquals(expectedDoubleValue, alfa.getFloatValue(), TestUtils.DELTA);
+      assertEquals(expectedDoubleValue, alfa.getDoubleValue(), TestUtils.DELTA);
+
+      assertEquals(expectedStringValue, alfa.getStringValue());
+      assertEquals(expectedStringValue.charAt(0), alfa.getCharValue());
+      assertEquals(Instant.parse(expectedStringValue), alfa.getInstantValue());
+      assertEquals(Period.of(0, 0, expectedIntValue), alfa.getPeriodValue());
+
+      checkMyEnum(alfa, expectedBooleanValue);
+    }
+  }
+
+  protected void checkPrimitiveTree(int expectedCount, String expectedStringValue) {
+    assertEquals(expectedCount, data.numberOfPrimitiveTrees);
+    if (expectedStringValue == null) {
+      assertNull(receiverRoot.getAlfaPrimitive());
+    } else {
+      assertNotNull(receiverRoot.getAlfaPrimitive());
+
+      TestWrapperAlfa alfaPrimitive = receiverRoot.getAlfaPrimitive();
+      assertEquals(expectedStringValue, alfaPrimitive.getStringValue());
+    }
+  }
+
+  protected abstract void checkMyEnum(TestWrapperAlfa alfa, boolean expectedBooleanValue);
+
+  protected ReceiverData data;
+
+  protected static class ReceiverData {
+    int numberOfTrees = 0;
+    int numberOfPrimitiveTrees = 0;
+  }
+
+}
diff --git a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/AbstractTreeTest.java b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/AbstractTreeTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..069f622c84cf95508575143b3b9d23288f42bc4a
--- /dev/null
+++ b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/AbstractTreeTest.java
@@ -0,0 +1,206 @@
+package org.jastadd.ragconnect.tests;
+
+import java.io.IOException;
+import java.util.List;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.junit.jupiter.api.Assertions.*;
+
+/**
+ * Base class for test cases "tree manual" and "tree incremental".
+ *
+ * @author rschoene - Initial contribution
+ */
+public abstract class AbstractTreeTest extends AbstractMqttTest {
+  protected static final String TOPIC_ALFA = "alfa";
+  protected ReceiverData data;
+  protected TestWrapperReceiverRoot receiverRoot;
+
+  public interface TestWrapperReceiverRoot {
+    TestWrapperAlfa getAlfa();
+    boolean connectAlfa(String mqttUri) throws IOException;
+  }
+  public interface TestWrapperAlfa {
+    TestWrapperBravo getBravo(int i);
+    int getNumBravo();
+    int getNumCharlie();
+    int getNumDelta();
+
+    TestWrapperAlfa getMyself();
+    TestWrapperBravo getMyBravo();
+    boolean hasOptionalBravo();
+    TestWrapperBravo getOptionalBravo();
+    TestWrapperCharlie getCharlie(int i);
+    TestWrapperDelta getDelta(int i);
+    <T extends TestWrapperBravo> List<T> getMultiBravoList();
+  }
+  public interface TestWrapperBravo {
+
+    TestWrapperCharlie getMyCharlie();
+    boolean hasOptionalCharlie();
+    TestWrapperCharlie getOptionalCharlie();
+    <T extends TestWrapperCharlie> List<T> getMultiCharlieList();
+    boolean hasSingleBi1Delta();
+    TestWrapperDelta getSingleBi1Delta();
+    <T extends TestWrapperDelta> List<T> getMultiBi2DeltaList();
+    <T extends TestWrapperDelta> List<T> getMultiBi3DeltaList();
+  }
+  public interface TestWrapperCharlie {
+    boolean hasMyAlfa();
+    TestWrapperAlfa getMyAlfa();
+  }
+  public interface TestWrapperDelta {
+    boolean hasSingleBack1Alfa();
+    TestWrapperAlfa getSingleBack1Alfa();
+    boolean hasSingleBack2Alfa();
+    TestWrapperAlfa getSingleBack2Alfa();
+    <T extends TestWrapperAlfa> List<T> getMultiBack3AlfaList();
+    boolean hasSingleBack1Bravo();
+    TestWrapperBravo getSingleBack1Bravo();
+    TestWrapperBravo getSingleBack2Bravo();
+    boolean hasSingleBack2Bravo();
+    <T extends TestWrapperBravo> List<T> getMultiBack3BravoList();
+  }
+
+  @Override
+  protected void communicateSendInitialValue() throws InterruptedException {
+    checkTree(1, 0);
+
+    setInput(1);
+    checkTree(2, 1);
+
+    setInput(1);
+    checkTree(2, 1);
+
+    setInput(2);
+    checkTree(3, 2);
+  }
+
+  @Override
+  protected void communicateOnlyUpdatedValue() throws InterruptedException {
+    checkTree(0, null);
+
+    setInput(1);
+    checkTree(1, 1);
+
+    setInput(1);
+    checkTree(1, 1);
+
+    setInput(2);
+    checkTree(2, 2);
+  }
+
+  protected abstract void setInput(int input);
+
+  protected void checkTree(int expectedCount, Integer expectedInput) throws InterruptedException {
+    TestUtils.waitForMqtt();
+
+    assertEquals(expectedCount, data.numberOfTrees);
+    if (expectedInput == null) {
+      assertNull(receiverRoot.getAlfa());
+    } else {
+      assertNotNull(receiverRoot.getAlfa());
+      TestWrapperAlfa alfa = receiverRoot.getAlfa();
+      assertEquals(4, alfa.getNumBravo());
+      assertEquals(4, alfa.getNumCharlie());
+      assertEquals(4, alfa.getNumDelta());
+
+      TestWrapperBravo inputBravo = alfa.getBravo(expectedInput);
+      TestWrapperCharlie inputCharlie = alfa.getCharlie(expectedInput);
+      TestWrapperDelta inputDelta = alfa.getDelta(expectedInput);
+
+      // Alfa -> Alfa
+      assertEquals(alfa, alfa.getMyself());
+
+      // Alfa -> Bravo
+      assertEquals(inputBravo, alfa.getMyBravo());
+      assertTrue(alfa.hasOptionalBravo());
+      assertEquals(inputBravo, alfa.getOptionalBravo());
+      assertThat(alfa.getMultiBravoList()).containsExactly(
+          inputBravo, alfa.getBravo(expectedInput + 1));
+
+      // Charlie -> Alfa
+      for (int i = 0; i < 4; i++) {
+        TestWrapperCharlie Charlie = alfa.getCharlie(i);
+        if (i == expectedInput) {
+          assertTrue(Charlie.hasMyAlfa());
+          assertEquals(alfa, Charlie.getMyAlfa());
+        } else {
+          assertFalse(Charlie.hasMyAlfa());
+        }
+      }
+
+      // Alfa <-> Delta
+      for (int i = 0; i < 4; i++) {
+        TestWrapperDelta Delta = alfa.getDelta(i);
+        if (i == expectedInput) {
+          assertTrue(Delta.hasSingleBack1Alfa());
+          assertEquals(alfa, Delta.getSingleBack1Alfa());
+        } else {
+          assertFalse(Delta.hasSingleBack1Alfa());
+        }
+        if (i == expectedInput || i == expectedInput + 1) {
+          assertTrue(Delta.hasSingleBack2Alfa());
+          assertEquals(alfa, Delta.getSingleBack2Alfa());
+          assertThat(Delta.getMultiBack3AlfaList()).containsExactly(alfa);
+        } else {
+          assertFalse(Delta.hasSingleBack2Alfa());
+          assertThat(Delta.getMultiBack3AlfaList()).isEmpty();
+        }
+      }
+
+      // Bravo -> Charlie
+      for (int i = 0; i < 4; i++) {
+        TestWrapperBravo Bravo = alfa.getBravo(i);
+
+        if (i == expectedInput) {
+          assertEquals(inputCharlie, Bravo.getMyCharlie());
+          assertTrue(Bravo.hasOptionalCharlie());
+          assertEquals(inputCharlie, Bravo.getOptionalCharlie());
+          assertThat(Bravo.getMultiCharlieList()).containsExactly(
+              inputCharlie, alfa.getCharlie(expectedInput + 1));
+        } else {
+          assertEquals(alfa.getCharlie(0), Bravo.getMyCharlie());
+          assertFalse(Bravo.hasOptionalCharlie());
+          assertThat(Bravo.getMultiCharlieList()).isEmpty();
+        }
+      }
+
+      // Bravo <-> Delta
+      for (int i = 0; i < 4; i++) {
+        TestWrapperBravo Bravo = alfa.getBravo(i);
+        TestWrapperDelta Delta = alfa.getDelta(i);
+
+        if (i == expectedInput) {
+          assertTrue(Bravo.hasSingleBi1Delta());
+          assertTrue(Delta.hasSingleBack1Bravo());
+          assertEquals(inputDelta, Bravo.getSingleBi1Delta());
+          assertEquals(inputBravo, Delta.getSingleBack1Bravo());
+          assertThat(Bravo.getMultiBi2DeltaList()).containsExactly(
+              inputDelta, alfa.getDelta(expectedInput + 1));
+        } else {
+          assertFalse(Bravo.hasSingleBi1Delta());
+          assertFalse(Delta.hasSingleBack1Bravo());
+          assertThat(Bravo.getMultiBi2DeltaList()).isEmpty();
+        }
+        if (i == expectedInput || i == expectedInput + 1) {
+          assertThat(Bravo.getMultiBi3DeltaList()).containsExactly(
+              inputDelta, alfa.getDelta(expectedInput + 1));
+          assertTrue(Delta.hasSingleBack2Bravo());
+          assertEquals(inputBravo, Delta.getSingleBack2Bravo());
+          assertThat(Delta.getMultiBack3BravoList()).containsExactly(
+              inputBravo, alfa.getBravo(expectedInput + 1));
+        } else {
+          assertThat(Bravo.getMultiBi3DeltaList()).isEmpty();
+          assertFalse(Delta.hasSingleBack2Bravo());
+          assertThat(Delta.getMultiBack3BravoList()).isEmpty();
+        }
+      }
+    }
+  }
+
+  protected static class ReceiverData {
+    int numberOfTrees = 0;
+  }
+
+}
diff --git a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/DefaultOnlyReadTest.java b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/DefaultOnlyReadTest.java
index 844f35d61b64364aa271a82fe770401f7ab8ba5f..cf5e02f956ae55258f08458bcb88130e1afca4b0 100644
--- a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/DefaultOnlyReadTest.java
+++ b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/DefaultOnlyReadTest.java
@@ -4,11 +4,13 @@ import defaultOnlyRead.ast.A;
 import defaultOnlyRead.ast.BoxedTypes;
 import defaultOnlyRead.ast.MqttHandler;
 import defaultOnlyRead.ast.NativeTypes;
+import org.junit.jupiter.api.Test;
 
 import java.io.IOException;
+import java.nio.file.Paths;
 import java.util.concurrent.TimeUnit;
 
-import static org.jastadd.ragconnect.tests.TestUtils.mqttUri;
+import static org.jastadd.ragconnect.tests.TestUtils.*;
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 
@@ -41,7 +43,13 @@ public class DefaultOnlyReadTest extends AbstractMqttTest {
   private NativeTypes floats;
   private NativeTypes chars;
   private BoxedTypes allBoxed;
-  private MqttHandler sender;
+
+  @Test
+  public void checkNotJacksonReference() {
+    testJaddContainReferenceToJackson(
+        Paths.get("src", "test",
+            "02-after-ragconnect", "defaultOnlyRead", "RagConnect.jadd"), false);
+  }
 
   @Override
   protected void createModel() {
@@ -93,9 +101,6 @@ public class DefaultOnlyReadTest extends AbstractMqttTest {
     allBoxed.connectFloatValueTransformed(mqttUri(TOPIC_BOXED_FLOAT));
     allBoxed.connectDoubleValueTransformed(mqttUri(TOPIC_BOXED_DOUBLE));
     allBoxed.connectCharValueTransformed(mqttUri(TOPIC_BOXED_CHARACTER));
-
-    sender = new MqttHandler().dontSendWelcomeMessage().setHost(TestUtils.getMqttHost());
-    assertTrue(sender.waitUntilReady(2, TimeUnit.SECONDS));
   }
 
   @Override
@@ -114,22 +119,22 @@ public class DefaultOnlyReadTest extends AbstractMqttTest {
     final char expectedCharValue = 'c';
     final String expectedStringValue = "6.3";
 
-    sender.publish(TOPIC_NATIVE_BOOLEAN, TestUtils.DefaultMappings.BoolToBytes(expectedBooleanValue));
-    sender.publish(TOPIC_NATIVE_INT, TestUtils.DefaultMappings.IntToBytes(expectedIntValue));
-    sender.publish(TOPIC_NATIVE_SHORT, TestUtils.DefaultMappings.ShortToBytes(expectedShortValue));
-    sender.publish(TOPIC_NATIVE_LONG, TestUtils.DefaultMappings.LongToBytes(expectedLongValue));
-    sender.publish(TOPIC_NATIVE_FLOAT, TestUtils.DefaultMappings.FloatToBytes(expectedFloatValue));
-    sender.publish(TOPIC_NATIVE_DOUBLE, TestUtils.DefaultMappings.DoubleToBytes(expectedDoubleValue));
-    sender.publish(TOPIC_NATIVE_CHAR, TestUtils.DefaultMappings.CharToBytes(expectedCharValue));
-    sender.publish(TOPIC_NATIVE_STRING, TestUtils.DefaultMappings.StringToBytes(expectedStringValue));
-
-    sender.publish(TOPIC_BOXED_BOOLEAN, TestUtils.DefaultMappings.BoolToBytes(expectedBooleanValue));
-    sender.publish(TOPIC_BOXED_INTEGER, TestUtils.DefaultMappings.IntToBytes(expectedIntValue));
-    sender.publish(TOPIC_BOXED_SHORT, TestUtils.DefaultMappings.ShortToBytes(expectedShortValue));
-    sender.publish(TOPIC_BOXED_LONG, TestUtils.DefaultMappings.LongToBytes(expectedLongValue));
-    sender.publish(TOPIC_BOXED_FLOAT, TestUtils.DefaultMappings.FloatToBytes(expectedFloatValue));
-    sender.publish(TOPIC_BOXED_DOUBLE, TestUtils.DefaultMappings.DoubleToBytes(expectedDoubleValue));
-    sender.publish(TOPIC_BOXED_CHARACTER, TestUtils.DefaultMappings.CharToBytes(expectedCharValue));
+    publisher.publish(TOPIC_NATIVE_BOOLEAN, TestUtils.DefaultMappings.BoolToBytes(expectedBooleanValue));
+    publisher.publish(TOPIC_NATIVE_INT, TestUtils.DefaultMappings.IntToBytes(expectedIntValue));
+    publisher.publish(TOPIC_NATIVE_SHORT, TestUtils.DefaultMappings.ShortToBytes(expectedShortValue));
+    publisher.publish(TOPIC_NATIVE_LONG, TestUtils.DefaultMappings.LongToBytes(expectedLongValue));
+    publisher.publish(TOPIC_NATIVE_FLOAT, TestUtils.DefaultMappings.FloatToBytes(expectedFloatValue));
+    publisher.publish(TOPIC_NATIVE_DOUBLE, TestUtils.DefaultMappings.DoubleToBytes(expectedDoubleValue));
+    publisher.publish(TOPIC_NATIVE_CHAR, TestUtils.DefaultMappings.CharToBytes(expectedCharValue));
+    publisher.publish(TOPIC_NATIVE_STRING, TestUtils.DefaultMappings.StringToBytes(expectedStringValue));
+
+    publisher.publish(TOPIC_BOXED_BOOLEAN, TestUtils.DefaultMappings.BoolToBytes(expectedBooleanValue));
+    publisher.publish(TOPIC_BOXED_INTEGER, TestUtils.DefaultMappings.IntToBytes(expectedIntValue));
+    publisher.publish(TOPIC_BOXED_SHORT, TestUtils.DefaultMappings.ShortToBytes(expectedShortValue));
+    publisher.publish(TOPIC_BOXED_LONG, TestUtils.DefaultMappings.LongToBytes(expectedLongValue));
+    publisher.publish(TOPIC_BOXED_FLOAT, TestUtils.DefaultMappings.FloatToBytes(expectedFloatValue));
+    publisher.publish(TOPIC_BOXED_DOUBLE, TestUtils.DefaultMappings.DoubleToBytes(expectedDoubleValue));
+    publisher.publish(TOPIC_BOXED_CHARACTER, TestUtils.DefaultMappings.CharToBytes(expectedCharValue));
 
     TestUtils.waitForMqtt();
 
@@ -170,9 +175,6 @@ public class DefaultOnlyReadTest extends AbstractMqttTest {
 
   @Override
   public void closeConnections() {
-    if (sender != null) {
-      sender.close();
-    }
     if (model != null) {
       model.ragconnectCloseConnections();
     }
diff --git a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/DefaultOnlyWriteTest.java b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/DefaultOnlyWriteTest.java
index a5d20afb5a2b98ee5e9aef0fac0102ecced2b15a..c18b185503053ec03428f620ea1aaa5df2505354 100644
--- a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/DefaultOnlyWriteTest.java
+++ b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/DefaultOnlyWriteTest.java
@@ -4,11 +4,13 @@ import defaultOnlyWrite.ast.A;
 import defaultOnlyWrite.ast.BoxedTypesSyn;
 import defaultOnlyWrite.ast.MqttHandler;
 import defaultOnlyWrite.ast.NativeTypesSyn;
+import org.junit.jupiter.api.Test;
 
 import java.io.IOException;
+import java.nio.file.Paths;
 import java.util.concurrent.TimeUnit;
 
-import static org.jastadd.ragconnect.tests.TestUtils.mqttUri;
+import static org.jastadd.ragconnect.tests.TestUtils.*;
 import static org.junit.jupiter.api.Assertions.*;
 
 /**
@@ -63,6 +65,13 @@ public class DefaultOnlyWriteTest extends AbstractMqttTest {
   private ReceiverData dataNormal;
   private ReceiverData dataTransformed;
 
+  @Test
+  public void checkNotJacksonReference() {
+    testJaddContainReferenceToJackson(
+        Paths.get("src", "test",
+            "02-after-ragconnect", "defaultOnlyWrite", "RagConnect.jadd"), false);
+  }
+
   @Override
   protected void createModel() {
     model = new A();
diff --git a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/ExampleTest.java b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/ExampleTest.java
index c3fae394f191920fdfdbe9be818a0393516faa9d..17bc074545fd2815c750d957dd1038586c5ff018 100644
--- a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/ExampleTest.java
+++ b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/ExampleTest.java
@@ -251,13 +251,13 @@ public class ExampleTest extends AbstractMqttTest {
     createModel();
     setupReceiverAndConnect(false);
 
-    handler.publish(TOPIC_JOINT1, "not-a-pandaLinkState".getBytes());
+    publisher.publish(TOPIC_JOINT1, "not-a-pandaLinkState".getBytes());
     assertEquals(0, data.numberOfConfigs);
     assertTrue(data.failedLastConversion);
   }
 
   private void sendData(String topic, float x, float y, float z) {
-    handler.publish(topic, RobotState.newBuilder()
+    publisher.publish(topic, RobotState.newBuilder()
         .setPosition(RobotState.Position.newBuilder().setX(x).setY(y).setZ(z).build())
         .build()
         .toByteArray()
diff --git a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/IncrementalDependencyTest.java b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/IncrementalDependencyTest.java
index e7d6555ef302f59f8ada8d77a9e6a33c262910cc..43fe8b349e2f10568238042276faf2d43c74271d 100644
--- a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/IncrementalDependencyTest.java
+++ b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/IncrementalDependencyTest.java
@@ -3,7 +3,6 @@ package org.jastadd.ragconnect.tests;
 import incremental.ast.A;
 import incremental.ast.B;
 import incremental.ast.MqttHandler;
-import org.junit.jupiter.api.Tag;
 
 import java.io.IOException;
 import java.util.concurrent.TimeUnit;
@@ -144,7 +143,7 @@ public class IncrementalDependencyTest extends AbstractMqttTest {
   }
 
   private void sendData(String input) throws InterruptedException {
-    handler.publish(TOPIC_IN, input.getBytes());
+    publisher.publish(TOPIC_IN, input.getBytes());
     TestUtils.waitForMqtt();
   }
 
diff --git a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/MappingTest.java b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/MappingTest.java
index 2c7cd6ccf9d0a2ac3ce353fcc025881938194191..7f83e3387688f578e47f6ac67c14e5f7fb26a688 100644
--- a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/MappingTest.java
+++ b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/MappingTest.java
@@ -168,7 +168,7 @@ public class MappingTest extends AbstractMqttTest {
 
   private void sendAndSetData(String driver, String input) {
     natives.setDriver(driver);
-    handler.publish(TOPIC_INPUT, input.getBytes());
+    publisher.publish(TOPIC_INPUT, input.getBytes());
   }
 
   private void checkSendData(int expectedNumberOfValues, int expectedInt, short expectedShort, long expectedLong, float expectedFloat, double expectedDouble, char expectedChar, int expectedNumberOfBooleanValues, boolean expectedBoolean) throws InterruptedException {
diff --git a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/Read1Write2Test.java b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/Read1Write2Test.java
index 57db1a70581249d94023a3865264085d4cfa1334..889ce0531a6b61196856f40c5045920517b8dfd3 100644
--- a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/Read1Write2Test.java
+++ b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/Read1Write2Test.java
@@ -174,8 +174,8 @@ public class Read1Write2Test extends AbstractMqttTest {
   }
 
   private void sendData(String inputSame, String inputDifferent) {
-    handler.publish(TOPIC_SAME_READ, inputSame.getBytes());
-    handler.publish(TOPIC_DIFFERENT_READ, inputDifferent.getBytes());
+    publisher.publish(TOPIC_SAME_READ, inputSame.getBytes());
+    publisher.publish(TOPIC_DIFFERENT_READ, inputDifferent.getBytes());
   }
 
   private void setDataOnlySame(String inputSame) {
diff --git a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/Read2Write1Test.java b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/Read2Write1Test.java
index 048528bd895d45d4a296907cb4580b72f07f5b02..f0f634c8e031bcadac54b09dd644d34c1f2a8dff 100644
--- a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/Read2Write1Test.java
+++ b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/Read2Write1Test.java
@@ -167,14 +167,14 @@ public class Read2Write1Test extends AbstractMqttTest {
 
   private void sendData(boolean useSameInput1, String inputSame,
                         boolean useDifferentInput1, String inputDifferent) {
-    handler.publish(useSameInput1 ? TOPIC_SAME_READ1 : TOPIC_SAME_READ2,
+    publisher.publish(useSameInput1 ? TOPIC_SAME_READ1 : TOPIC_SAME_READ2,
         inputSame.getBytes());
-    handler.publish(useDifferentInput1 ? TOPIC_DIFFERENT_READ1 : TOPIC_DIFFERENT_READ2,
+    publisher.publish(useDifferentInput1 ? TOPIC_DIFFERENT_READ1 : TOPIC_DIFFERENT_READ2,
         inputDifferent.getBytes());
   }
 
   private void setDataOnlySame(String inputSame) {
-    handler.publish(TOPIC_SAME_READ1, inputSame.getBytes());
+    publisher.publish(TOPIC_SAME_READ1, inputSame.getBytes());
   }
 
   private void checkData(int numberOfSameValues, Integer lastSameIntValue,
diff --git a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/TestUtils.java b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/TestUtils.java
index 5958b395d7920e70e71bfa6a4f49866dfce4c087..776e7a01b5f2c683c09cb2dfd3b3e496f41110ad 100644
--- a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/TestUtils.java
+++ b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/TestUtils.java
@@ -1,14 +1,15 @@
 package org.jastadd.ragconnect.tests;
 
-import defaultOnlyRead.ast.ASTNode;
-
 import java.io.File;
 import java.io.IOException;
 import java.nio.charset.Charset;
 import java.nio.file.Files;
+import java.nio.file.Path;
 import java.nio.file.Paths;
 import java.util.concurrent.TimeUnit;
 
+import static org.junit.jupiter.api.Assertions.fail;
+
 /**
  * Utility methods for tests.
  *
@@ -63,6 +64,21 @@ public class TestUtils {
     return process.exitValue();
   }
 
+  public static void testJaddContainReferenceToJackson(Path path, boolean shouldContain) {
+    try {
+      String content = Files.readString(path);
+      boolean actualContain = content.contains("com.fasterxml.jackson.databind.ObjectMapper");
+      if (actualContain && !shouldContain) {
+        fail(path + " should not depend on jackson library, but does");
+      }
+      if (!actualContain && shouldContain) {
+        fail(path + " does not depend on jackson library");
+      }
+    } catch (IOException e) {
+      fail(e);
+    }
+  }
+
   public static String readFile(String path, Charset encoding)
       throws IOException {
     byte[] encoded = Files.readAllBytes(Paths.get(path));
@@ -74,10 +90,64 @@ public class TestUtils {
   }
 
   @SuppressWarnings({"unused", "rawtypes"})
-  static class DefaultMappings extends ASTNode {
+  static class DefaultMappings {
+    static class ReadNode extends defaultOnlyRead.ast.ASTNode {
+      public static boolean _apply__DefaultBytesToBooleanMapping(byte[] input) throws Exception {
+        return defaultOnlyRead.ast.ASTNode._apply__DefaultBytesToBooleanMapping(input);
+      }
+      public static int _apply__DefaultBytesToIntMapping(byte[] input) throws Exception {
+        return defaultOnlyRead.ast.ASTNode._apply__DefaultBytesToIntMapping(input);
+      }
+      public static short _apply__DefaultBytesToShortMapping(byte[] input) throws Exception {
+        return defaultOnlyRead.ast.ASTNode._apply__DefaultBytesToShortMapping(input);
+      }
+      public static long _apply__DefaultBytesToLongMapping(byte[] input) throws Exception {
+        return defaultOnlyRead.ast.ASTNode._apply__DefaultBytesToLongMapping(input);
+      }
+      public static float _apply__DefaultBytesToFloatMapping(byte[] input) throws Exception {
+        return defaultOnlyRead.ast.ASTNode._apply__DefaultBytesToFloatMapping(input);
+      }
+      public static double _apply__DefaultBytesToDoubleMapping(byte[] input) throws Exception {
+        return defaultOnlyRead.ast.ASTNode._apply__DefaultBytesToDoubleMapping(input);
+      }
+      public static char _apply__DefaultBytesToCharMapping(byte[] input) throws Exception {
+        return defaultOnlyRead.ast.ASTNode._apply__DefaultBytesToCharMapping(input);
+      }
+      public static String _apply__DefaultBytesToStringMapping(byte[] input) throws Exception {
+        return defaultOnlyRead.ast.ASTNode._apply__DefaultBytesToStringMapping(input);
+      }
+    }
+
+    static class WriteNode extends defaultOnlyWrite.ast.ASTNode {
+      public static byte[] _apply__DefaultBooleanToBytesMapping(boolean input) throws Exception {
+        return defaultOnlyWrite.ast.ASTNode._apply__DefaultBooleanToBytesMapping(input);
+      }
+      public static byte[] _apply__DefaultIntToBytesMapping(int input) throws Exception {
+        return defaultOnlyWrite.ast.ASTNode._apply__DefaultIntToBytesMapping(input);
+      }
+      public static byte[] _apply__DefaultShortToBytesMapping(short input) throws Exception {
+        return defaultOnlyWrite.ast.ASTNode._apply__DefaultShortToBytesMapping(input);
+      }
+      public static byte[] _apply__DefaultLongToBytesMapping(long input) throws Exception {
+        return defaultOnlyWrite.ast.ASTNode._apply__DefaultLongToBytesMapping(input);
+      }
+      public static byte[] _apply__DefaultFloatToBytesMapping(float input) throws Exception {
+        return defaultOnlyWrite.ast.ASTNode._apply__DefaultFloatToBytesMapping(input);
+      }
+      public static byte[] _apply__DefaultDoubleToBytesMapping(double input) throws Exception {
+        return defaultOnlyWrite.ast.ASTNode._apply__DefaultDoubleToBytesMapping(input);
+      }
+      public static byte[] _apply__DefaultCharToBytesMapping(char input) throws Exception {
+        return defaultOnlyWrite.ast.ASTNode._apply__DefaultCharToBytesMapping(input);
+      }
+      public static byte[] _apply__DefaultStringToBytesMapping(String input) throws Exception {
+        return defaultOnlyWrite.ast.ASTNode._apply__DefaultStringToBytesMapping(input);
+      }
+    }
+
     public static boolean BytesToBool(byte[] input) {
       try {
-        return _apply__DefaultBytesToBooleanMapping(input);
+        return ReadNode._apply__DefaultBytesToBooleanMapping(input);
       } catch (Exception e) {
         e.printStackTrace();
         return false;
@@ -85,7 +155,7 @@ public class TestUtils {
     }
     public static int BytesToInt(byte[] input) {
       try {
-        return _apply__DefaultBytesToIntMapping(input);
+        return ReadNode._apply__DefaultBytesToIntMapping(input);
       } catch (Exception e) {
         e.printStackTrace();
         return 0;
@@ -93,7 +163,7 @@ public class TestUtils {
     }
     public static short BytesToShort(byte[] input) {
       try {
-        return _apply__DefaultBytesToShortMapping(input);
+        return ReadNode._apply__DefaultBytesToShortMapping(input);
       } catch (Exception e) {
         e.printStackTrace();
         return 0;
@@ -101,7 +171,7 @@ public class TestUtils {
     }
     public static long BytesToLong(byte[] input) {
       try {
-        return _apply__DefaultBytesToLongMapping(input);
+        return ReadNode._apply__DefaultBytesToLongMapping(input);
       } catch (Exception e) {
         e.printStackTrace();
         return 0;
@@ -109,7 +179,7 @@ public class TestUtils {
     }
     public static float BytesToFloat(byte[] input) {
       try {
-        return _apply__DefaultBytesToFloatMapping(input);
+        return ReadNode._apply__DefaultBytesToFloatMapping(input);
       } catch (Exception e) {
         e.printStackTrace();
         return 0;
@@ -117,7 +187,7 @@ public class TestUtils {
     }
     public static double BytesToDouble(byte[] input) {
       try {
-        return _apply__DefaultBytesToDoubleMapping(input);
+        return ReadNode._apply__DefaultBytesToDoubleMapping(input);
       } catch (Exception e) {
         e.printStackTrace();
         return 0;
@@ -125,7 +195,7 @@ public class TestUtils {
     }
     public static char BytesToChar(byte[] input) {
       try {
-        return _apply__DefaultBytesToCharMapping(input);
+        return ReadNode._apply__DefaultBytesToCharMapping(input);
       } catch (Exception e) {
         e.printStackTrace();
         return 0;
@@ -133,7 +203,7 @@ public class TestUtils {
     }
     public static String BytesToString(byte[] input) {
       try {
-        return _apply__DefaultBytesToStringMapping(input);
+        return ReadNode._apply__DefaultBytesToStringMapping(input);
       } catch (Exception e) {
         e.printStackTrace();
         return null;
@@ -141,7 +211,7 @@ public class TestUtils {
     }
     public static byte[] BoolToBytes(boolean input) {
       try {
-        return _apply__DefaultBooleanToBytesMapping(input);
+        return WriteNode._apply__DefaultBooleanToBytesMapping(input);
       } catch (Exception e) {
         e.printStackTrace();
         return null;
@@ -149,7 +219,7 @@ public class TestUtils {
     }
     public static byte[] IntToBytes(int input) {
       try {
-        return _apply__DefaultIntToBytesMapping(input);
+        return WriteNode._apply__DefaultIntToBytesMapping(input);
       } catch (Exception e) {
         e.printStackTrace();
         return null;
@@ -157,7 +227,7 @@ public class TestUtils {
     }
     public static byte[] ShortToBytes(short input) {
       try {
-        return _apply__DefaultShortToBytesMapping(input);
+        return WriteNode._apply__DefaultShortToBytesMapping(input);
       } catch (Exception e) {
         e.printStackTrace();
         return null;
@@ -165,7 +235,7 @@ public class TestUtils {
     }
     public static byte[] LongToBytes(long input) {
       try {
-        return _apply__DefaultLongToBytesMapping(input);
+        return WriteNode._apply__DefaultLongToBytesMapping(input);
       } catch (Exception e) {
         e.printStackTrace();
         return null;
@@ -173,7 +243,7 @@ public class TestUtils {
     }
     public static byte[] FloatToBytes(float input) {
       try {
-        return _apply__DefaultFloatToBytesMapping(input);
+        return WriteNode._apply__DefaultFloatToBytesMapping(input);
       } catch (Exception e) {
         e.printStackTrace();
         return null;
@@ -181,7 +251,7 @@ public class TestUtils {
     }
     public static byte[] DoubleToBytes(double input) {
       try {
-        return _apply__DefaultDoubleToBytesMapping(input);
+        return WriteNode._apply__DefaultDoubleToBytesMapping(input);
       } catch (Exception e) {
         e.printStackTrace();
         return null;
@@ -189,7 +259,7 @@ public class TestUtils {
     }
     public static byte[] CharToBytes(char input) {
       try {
-        return _apply__DefaultCharToBytesMapping(input);
+        return WriteNode._apply__DefaultCharToBytesMapping(input);
       } catch (Exception e) {
         e.printStackTrace();
         return null;
@@ -197,7 +267,7 @@ public class TestUtils {
     }
     public static byte[] StringToBytes(String input) {
       try {
-        return _apply__DefaultStringToBytesMapping(input);
+        return WriteNode._apply__DefaultStringToBytesMapping(input);
       } catch (Exception e) {
         e.printStackTrace();
         return null;
diff --git a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/TokenValueSendTest.java b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/TokenValueSendTest.java
index 8c8c461df4bfdc8e2d21b9520815fb20807ae240..b22a2fb6ac2d4fcfe5c78332f64a5ec6025613d9 100644
--- a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/TokenValueSendTest.java
+++ b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/TokenValueSendTest.java
@@ -226,8 +226,8 @@ public class TokenValueSendTest extends AbstractMqttTest {
   }
 
   private void sendData(String inputTwo, String inputThree) {
-    handler.publish(TOPIC_RECEIVE_TWO, inputTwo.getBytes());
-    handler.publish(TOPIC_RECEIVE_THREE_VALUE, inputThree.getBytes());
+    publisher.publish(TOPIC_RECEIVE_TWO, inputTwo.getBytes());
+    publisher.publish(TOPIC_RECEIVE_THREE_VALUE, inputThree.getBytes());
   }
 
   private void setData(String inputOne, String inputTwo, String inputThree) {
diff --git a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/TreeAllowedTokensIncrementalTest.java b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/TreeAllowedTokensIncrementalTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..5dd1b1504701b0c5499e7277b8287e1b66a9e5a5
--- /dev/null
+++ b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/TreeAllowedTokensIncrementalTest.java
@@ -0,0 +1,89 @@
+package org.jastadd.ragconnect.tests;
+
+import org.junit.jupiter.api.Tag;
+import org.junit.jupiter.api.Test;
+import treeAllowedTokensInc.ast.*;
+
+import java.io.IOException;
+import java.nio.file.Paths;
+import java.util.concurrent.TimeUnit;
+
+import static org.jastadd.ragconnect.tests.TestUtils.mqttUri;
+import static org.jastadd.ragconnect.tests.TestUtils.testJaddContainReferenceToJackson;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+/**
+ * Test case "tree allowed tokens incremental"
+ *
+ * @author rschoene - Initial contribution
+ */
+@Tag("SpecificTest")
+public class TreeAllowedTokensIncrementalTest extends AbstractTreeAllowedTokensTest {
+
+  private Root model;
+  private SenderRoot senderRoot;
+  private MqttHandler handler;
+
+  @Test
+  public void checkJacksonReference() {
+    testJaddContainReferenceToJackson(
+        Paths.get("src", "test",
+            "02-after-ragconnect", "treeAllowedTokens", "RagConnect.jadd"), true);
+  }
+
+  @Override
+  protected void createModel() {
+    model = new Root();
+    senderRoot = new SenderRoot();
+    senderRoot.setFlag(false);
+    senderRoot.setInput2(INSTANT_A);
+    model.addSenderRoot(senderRoot);
+
+    receiverRoot = new ReceiverRoot();
+    model.addReceiverRoot((ReceiverRoot) receiverRoot);
+  }
+
+  @Override
+  protected void setupReceiverAndConnect(boolean writeCurrentValue) throws IOException {
+    model.ragconnectSetupMqttWaitUntilReady(2, TimeUnit.SECONDS);
+
+    handler = new MqttHandler().dontSendWelcomeMessage().setHost(TestUtils.getMqttHost());
+    assertTrue(handler.waitUntilReady(2, TimeUnit.SECONDS));
+
+    // no dependencies
+
+    data = new ReceiverData();
+    handler.newConnection(TOPIC_ALFA, bytes -> data.numberOfTrees += 1);
+    handler.newConnection(TOPIC_ALFA_PRIMITIVE, bytes -> data.numberOfPrimitiveTrees += 1);
+
+    // connect. important: first receiver, then sender. to not miss initial value.
+    senderRoot.connectInput1WhenFlagIsFalse(mqttUri(TOPIC_INPUT1FALSE));
+    senderRoot.connectInput1WhenFlagIsTrue(mqttUri(TOPIC_INPUT1TRUE));
+    senderRoot.connectInput2(mqttUri(TOPIC_INPUT2));
+    senderRoot.connectInput3(mqttUri(TOPIC_INPUT3));
+    receiverRoot.connectAlfa(mqttUri(TOPIC_ALFA));
+    receiverRoot.connectAlfaPrimitive(mqttUri(TOPIC_ALFA_PRIMITIVE));
+    senderRoot.connectAlfa(mqttUri(TOPIC_ALFA), writeCurrentValue);
+    senderRoot.connectAlfaPrimitive(mqttUri(TOPIC_ALFA_PRIMITIVE), writeCurrentValue);
+  }
+
+  @Override
+  protected void closeConnections() {
+    if (handler != null) {
+      handler.close();
+    }
+    if (model != null) {
+      model.ragconnectCloseConnections();
+    }
+  }
+
+  protected void setFlag(boolean value) {
+    senderRoot.setFlag(value);
+  }
+
+  @Override
+  protected void checkMyEnum(TestWrapperAlfa alfa, boolean expectedBooleanValue) {
+    assertEquals(expectedBooleanValue ? MyEnum.TRUE : MyEnum.FALSE, ((Alfa) alfa).getEnumValue());
+  }
+}
diff --git a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/TreeAllowedTokensManualTest.java b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/TreeAllowedTokensManualTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..c3b91a04f45a47250f2cdf557575c5c48ac09c05
--- /dev/null
+++ b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/TreeAllowedTokensManualTest.java
@@ -0,0 +1,94 @@
+package org.jastadd.ragconnect.tests;
+
+import org.junit.jupiter.api.Test;
+import treeAllowedTokens.ast.*;
+
+import java.io.IOException;
+import java.nio.file.Paths;
+import java.time.Instant;
+import java.time.Period;
+import java.util.concurrent.TimeUnit;
+
+import static org.jastadd.ragconnect.tests.TestUtils.mqttUri;
+import static org.jastadd.ragconnect.tests.TestUtils.testJaddContainReferenceToJackson;
+import static org.junit.jupiter.api.Assertions.*;
+
+/**
+ * Test case "tree allowed tokens manual"
+ *
+ * @author rschoene - Initial contribution
+ */
+public class TreeAllowedTokensManualTest extends AbstractTreeAllowedTokensTest {
+
+  private Root model;
+  private SenderRoot senderRoot;
+  private MqttHandler handler;
+
+  @Test
+  public void checkJacksonReference() {
+    testJaddContainReferenceToJackson(
+        Paths.get("src", "test",
+            "02-after-ragconnect", "treeAllowedTokens", "RagConnect.jadd"), true);
+  }
+
+  @Override
+  protected void createModel() {
+    model = new Root();
+    senderRoot = new SenderRoot();
+    senderRoot.setFlag(false);
+    senderRoot.setInput2(INSTANT_A);
+    model.addSenderRoot(senderRoot);
+
+    receiverRoot = new ReceiverRoot();
+    model.addReceiverRoot((ReceiverRoot) receiverRoot);
+  }
+
+  @Override
+  protected void setupReceiverAndConnect(boolean writeCurrentValue) throws IOException {
+    model.ragconnectSetupMqttWaitUntilReady(2, TimeUnit.SECONDS);
+
+    handler = new MqttHandler().dontSendWelcomeMessage().setHost(TestUtils.getMqttHost());
+    assertTrue(handler.waitUntilReady(2, TimeUnit.SECONDS));
+
+    // add dependencies
+    senderRoot.addFlagDependency(senderRoot);
+    senderRoot.addInput1WhenFlagIsFalseDependency(senderRoot);
+    senderRoot.addInput1WhenFlagIsTrueDependency(senderRoot);
+    senderRoot.addInput2Dependency(senderRoot);
+    senderRoot.addInput3Dependency(senderRoot);
+    senderRoot.addPrimitiveInput2Dependency(senderRoot);
+
+    data = new ReceiverData();
+    handler.newConnection(TOPIC_ALFA, bytes -> data.numberOfTrees += 1);
+    handler.newConnection(TOPIC_ALFA_PRIMITIVE, bytes -> data.numberOfPrimitiveTrees += 1);
+
+    // connect. important: first receiver, then sender. to not miss initial value.
+    senderRoot.connectInput1WhenFlagIsFalse(mqttUri(TOPIC_INPUT1FALSE));
+    senderRoot.connectInput1WhenFlagIsTrue(mqttUri(TOPIC_INPUT1TRUE));
+    senderRoot.connectInput2(mqttUri(TOPIC_INPUT2));
+    senderRoot.connectInput3(mqttUri(TOPIC_INPUT3));
+    receiverRoot.connectAlfa(mqttUri(TOPIC_ALFA));
+    receiverRoot.connectAlfaPrimitive(mqttUri(TOPIC_ALFA_PRIMITIVE));
+    senderRoot.connectAlfa(mqttUri(TOPIC_ALFA), writeCurrentValue);
+    senderRoot.connectAlfaPrimitive(mqttUri(TOPIC_ALFA_PRIMITIVE), writeCurrentValue);
+  }
+
+  @Override
+  protected void closeConnections() {
+    if (handler != null) {
+      handler.close();
+    }
+    if (model != null) {
+      model.ragconnectCloseConnections();
+    }
+  }
+
+  protected void setFlag(boolean value) {
+    senderRoot.setFlag(value);
+  }
+
+  @Override
+  protected void checkMyEnum(TestWrapperAlfa alfa, boolean expectedBooleanValue) {
+    assertEquals(expectedBooleanValue ? MyEnum.TRUE : MyEnum.FALSE, ((Alfa) alfa).getEnumValue());
+  }
+}
diff --git a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/TreeIncrementalTest.java b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/TreeIncrementalTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..e0d7c817e372395adf98565b5a44347335a2eb45
--- /dev/null
+++ b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/TreeIncrementalTest.java
@@ -0,0 +1,73 @@
+package org.jastadd.ragconnect.tests;
+
+import org.junit.jupiter.api.Test;
+import treeInc.ast.*;
+
+import java.io.IOException;
+import java.nio.file.Paths;
+import java.util.concurrent.TimeUnit;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.jastadd.ragconnect.tests.TestUtils.mqttUri;
+import static org.jastadd.ragconnect.tests.TestUtils.testJaddContainReferenceToJackson;
+import static org.junit.jupiter.api.Assertions.*;
+
+/**
+ * Test case "tree incremental"
+ *
+ * @author rschoene - Initial contribution
+ */
+public class TreeIncrementalTest extends AbstractTreeTest {
+
+  private Root model;
+  private SenderRoot senderRoot;
+  private MqttHandler handler;
+
+  @Test
+  public void checkJacksonReference() {
+    testJaddContainReferenceToJackson(
+        Paths.get("src", "test",
+            "02-after-ragconnect", "treeInc", "RagConnect.jadd"), true);
+  }
+
+  @Override
+  protected void createModel() {
+    model = new Root();
+    senderRoot = new SenderRoot();
+    model.addSenderRoot(senderRoot);
+
+    receiverRoot = new ReceiverRoot();
+    model.addReceiverRoot((ReceiverRoot) receiverRoot);
+  }
+
+  @Override
+  protected void setupReceiverAndConnect(boolean writeCurrentValue) throws IOException {
+    model.ragconnectSetupMqttWaitUntilReady(2, TimeUnit.SECONDS);
+
+    handler = new MqttHandler().dontSendWelcomeMessage().setHost(TestUtils.getMqttHost());
+    assertTrue(handler.waitUntilReady(2, TimeUnit.SECONDS));
+
+    // no dependencies
+
+    data = new ReceiverData();
+    handler.newConnection(TOPIC_ALFA, bytes -> data.numberOfTrees += 1);
+
+    // connect. important: first receiver, then sender. to not miss initial value.
+    receiverRoot.connectAlfa(mqttUri(TOPIC_ALFA));
+    senderRoot.connectAlfa(mqttUri(TOPIC_ALFA), writeCurrentValue);
+  }
+
+  @Override
+  protected void closeConnections() {
+    if (handler != null) {
+      handler.close();
+    }
+    if (model != null) {
+      model.ragconnectCloseConnections();
+    }
+  }
+
+  protected void setInput(int input) {
+    senderRoot.setInput(input);
+  }
+}
diff --git a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/TreeManualTest.java b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/TreeManualTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..060317dfd749f0815af9fe42d1cb9553fe9034ae
--- /dev/null
+++ b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/TreeManualTest.java
@@ -0,0 +1,76 @@
+package org.jastadd.ragconnect.tests;
+
+import org.junit.jupiter.api.Test;
+import tree.ast.MqttHandler;
+import tree.ast.ReceiverRoot;
+import tree.ast.Root;
+import tree.ast.SenderRoot;
+
+import java.io.IOException;
+import java.nio.file.Paths;
+import java.util.concurrent.TimeUnit;
+
+import static org.jastadd.ragconnect.tests.TestUtils.mqttUri;
+import static org.jastadd.ragconnect.tests.TestUtils.testJaddContainReferenceToJackson;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+/**
+ * Test case "tree manual"
+ *
+ * @author rschoene - Initial contribution
+ */
+public class TreeManualTest extends AbstractTreeTest {
+
+  private Root model;
+  private SenderRoot senderRoot;
+  private MqttHandler handler;
+
+  @Test
+  public void checkJacksonReference() {
+    testJaddContainReferenceToJackson(
+        Paths.get("src", "test",
+            "02-after-ragconnect", "tree", "RagConnect.jadd"), true);
+  }
+
+  @Override
+  protected void createModel() {
+    model = new Root();
+    senderRoot = new SenderRoot();
+    model.addSenderRoot(senderRoot);
+
+    receiverRoot = new ReceiverRoot();
+    model.addReceiverRoot((ReceiverRoot) receiverRoot);
+  }
+
+  @Override
+  protected void setupReceiverAndConnect(boolean writeCurrentValue) throws IOException {
+    model.ragconnectSetupMqttWaitUntilReady(2, TimeUnit.SECONDS);
+
+    handler = new MqttHandler().dontSendWelcomeMessage().setHost(TestUtils.getMqttHost());
+    assertTrue(handler.waitUntilReady(2, TimeUnit.SECONDS));
+
+    // add dependencies
+    senderRoot.addInputDependency(senderRoot);
+
+    data = new ReceiverData();
+    handler.newConnection(TOPIC_ALFA, bytes -> data.numberOfTrees += 1);
+
+    // connect. important: first receiver, then sender. to not miss initial value.
+    receiverRoot.connectAlfa(mqttUri(TOPIC_ALFA));
+    senderRoot.connectAlfa(mqttUri(TOPIC_ALFA), writeCurrentValue);
+  }
+
+  @Override
+  protected void closeConnections() {
+    if (handler != null) {
+      handler.close();
+    }
+    if (model != null) {
+      model.ragconnectCloseConnections();
+    }
+  }
+
+  protected void setInput(int input) {
+    senderRoot.setInput(input);
+  }
+}
diff --git a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/ViaTest.java b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/ViaTest.java
index 766208f725c6a73c6dd391a20cd0b105ac894a77..5433514398ea8ec3037f95fbc1c63bbb6a1cf2be 100644
--- a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/ViaTest.java
+++ b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/ViaTest.java
@@ -57,16 +57,68 @@ public class ViaTest extends AbstractMqttTest {
   private WebTarget senderBoth2Rest;
 
   @Override
-  public void closeConnections() {
-    if (handler != null) {
-      handler.close();
-    }
-    if (model != null) {
-      model.ragconnectCloseConnections();
-    }
+  protected void createModel() {
+    // Setting value for Input without dependencies does not trigger any updates
+    model = new A();
+    model.setMqtt2MqttInput("100");
+    model.setRest2RestInput("200");
+    model.setMqtt2RestInput("300");
+    model.setRest2MqttInput("400");
+    model.setBoth2BothInput("500");
   }
 
+  @Override
+  protected void setupReceiverAndConnect(boolean writeCurrentValue) throws IOException {
+    model.ragconnectSetupMqttWaitUntilReady(2, TimeUnit.SECONDS);
+
+    handler = new MqttHandler().dontSendWelcomeMessage().setHost(TestUtils.getMqttHost());
+    assertTrue(handler.waitUntilReady(2, TimeUnit.SECONDS));
+
+    model.addDependencyMqtt2Mqtt(model);
+    model.addDependencyRest2Rest(model);
+    model.addDependencyMqtt2Rest(model);
+    model.addDependencyRest2Mqtt(model);
+    model.addDependencyBoth2Mqtt(model);
+    model.addDependencyBoth2Rest(model);
+
+    dataMqtt2Mqtt = new ReceiverData();
+    dataRest2Mqtt = new ReceiverData();
+    dataBoth2Mqtt = new ReceiverData();
+
+    handler.newConnection(TOPIC_MQTT_2_MQTT_SEND, bytes -> {
+      dataMqtt2Mqtt.numberOfStringValues += 1;
+      dataMqtt2Mqtt.lastStringValue = TestUtils.DefaultMappings.BytesToString(bytes);
+    });
+    handler.newConnection(TOPIC_REST_2_MQTT_SEND, bytes -> {
+      dataRest2Mqtt.numberOfStringValues += 1;
+      dataRest2Mqtt.lastStringValue = TestUtils.DefaultMappings.BytesToString(bytes);
+    });
+    handler.newConnection(TOPIC_BOTH_2_MQTT_SEND, bytes -> {
+      dataBoth2Mqtt.numberOfStringValues += 1;
+      dataBoth2Mqtt.lastStringValue = TestUtils.DefaultMappings.BytesToString(bytes);
+    });
+
+    Client client = ClientBuilder.newClient();
+    dataRest2Rest = client.target(REST_SERVER_BASE_URL + PATH_REST_2_REST_SEND);
+    dataMqtt2Rest = client.target(REST_SERVER_BASE_URL + PATH_MQTT_2_REST_SEND);
+    dataBoth2Rest = client.target(REST_SERVER_BASE_URL + PATH_BOTH_2_REST_SEND);
+    senderRest2Rest = client.target(REST_SERVER_BASE_URL + PATH_REST_2_REST_RECEIVE);
+    senderRest2Mqtt = client.target(REST_SERVER_BASE_URL + PATH_REST_2_MQTT_RECEIVE);
+    senderBoth2Rest = client.target(REST_SERVER_BASE_URL + PATH_BOTH_REST_RECEIVE);
 
+    model.connectMqtt2MqttInput(mqttUri(TOPIC_MQTT_2_MQTT_RECEIVE));
+    model.connectMqtt2MqttOutput(mqttUri(TOPIC_MQTT_2_MQTT_SEND), writeCurrentValue);
+    model.connectMqtt2RestInput(mqttUri(TOPIC_MQTT_2_REST_RECEIVE));
+    model.connectMqtt2RestOutput(restUri(PATH_MQTT_2_REST_SEND, REST_PORT), writeCurrentValue);
+    model.connectRest2MqttInput(restUri(PATH_REST_2_MQTT_RECEIVE, REST_PORT));
+    model.connectRest2MqttOutput(mqttUri(TOPIC_REST_2_MQTT_SEND), writeCurrentValue);
+    model.connectRest2RestInput(restUri(PATH_REST_2_REST_RECEIVE, REST_PORT));
+    model.connectRest2RestOutput(restUri(PATH_REST_2_REST_SEND, REST_PORT), writeCurrentValue);
+    model.connectBoth2BothInput(mqttUri(TOPIC_BOTH_MQTT_RECEIVE));
+    model.connectBoth2BothInput(restUri(PATH_BOTH_REST_RECEIVE, REST_PORT));
+    model.connectBoth2MqttOutput(mqttUri(TOPIC_BOTH_2_MQTT_SEND), writeCurrentValue);
+    model.connectBoth2RestOutput(restUri(PATH_BOTH_2_REST_SEND, REST_PORT), writeCurrentValue);
+  }
 
   @Override
   protected void communicateSendInitialValue() throws InterruptedException {
@@ -212,16 +264,26 @@ public class ViaTest extends AbstractMqttTest {
         "512-B2R-ToRest");
   }
 
+  @Override
+  public void closeConnections() {
+    if (handler != null) {
+      handler.close();
+    }
+    if (model != null) {
+      model.ragconnectCloseConnections();
+    }
+  }
+
   private void sendData(String inputMqtt2Mqtt, String inputRest2Rest, String inputMqtt2Rest, String inputRest2Mqtt) {
-    handler.publish(TOPIC_MQTT_2_MQTT_RECEIVE, inputMqtt2Mqtt.getBytes());
+    publisher.publish(TOPIC_MQTT_2_MQTT_RECEIVE, inputMqtt2Mqtt.getBytes());
     senderRest2Rest.request().put(Entity.entity(inputRest2Rest, MediaType.TEXT_PLAIN_TYPE));
-    handler.publish(TOPIC_MQTT_2_REST_RECEIVE, inputMqtt2Rest.getBytes());
+    publisher.publish(TOPIC_MQTT_2_REST_RECEIVE, inputMqtt2Rest.getBytes());
     senderRest2Mqtt.request().put(Entity.entity(inputRest2Mqtt, MediaType.TEXT_PLAIN_TYPE));
   }
 
   private void sendDataForBoth(String input, boolean useMqtt) {
     if (useMqtt) {
-      handler.publish(TOPIC_BOTH_MQTT_RECEIVE, input.getBytes());
+      publisher.publish(TOPIC_BOTH_MQTT_RECEIVE, input.getBytes());
     } else {
       senderBoth2Rest.request().put(Entity.entity(input, MediaType.TEXT_PLAIN_TYPE));
     }
@@ -248,70 +310,6 @@ public class ViaTest extends AbstractMqttTest {
     return dataBoth2Rest.request().get().readEntity(String.class);
   }
 
-  @Override
-  protected void createModel() {
-    // Setting value for Input without dependencies does not trigger any updates
-    model = new A();
-    model.setMqtt2MqttInput("100");
-    model.setRest2RestInput("200");
-    model.setMqtt2RestInput("300");
-    model.setRest2MqttInput("400");
-    model.setBoth2BothInput("500");
-  }
-
-  @Override
-  protected void setupReceiverAndConnect(boolean writeCurrentValue) throws IOException {
-    model.ragconnectSetupMqttWaitUntilReady(2, TimeUnit.SECONDS);
-
-    handler = new MqttHandler().dontSendWelcomeMessage().setHost(TestUtils.getMqttHost());
-    assertTrue(handler.waitUntilReady(2, TimeUnit.SECONDS));
-
-    model.addDependencyMqtt2Mqtt(model);
-    model.addDependencyRest2Rest(model);
-    model.addDependencyMqtt2Rest(model);
-    model.addDependencyRest2Mqtt(model);
-    model.addDependencyBoth2Mqtt(model);
-    model.addDependencyBoth2Rest(model);
-
-    dataMqtt2Mqtt = new ReceiverData();
-    dataRest2Mqtt = new ReceiverData();
-    dataBoth2Mqtt = new ReceiverData();
-
-    handler.newConnection(TOPIC_MQTT_2_MQTT_SEND, bytes -> {
-      dataMqtt2Mqtt.numberOfStringValues += 1;
-      dataMqtt2Mqtt.lastStringValue = TestUtils.DefaultMappings.BytesToString(bytes);
-    });
-    handler.newConnection(TOPIC_REST_2_MQTT_SEND, bytes -> {
-      dataRest2Mqtt.numberOfStringValues += 1;
-      dataRest2Mqtt.lastStringValue = TestUtils.DefaultMappings.BytesToString(bytes);
-    });
-    handler.newConnection(TOPIC_BOTH_2_MQTT_SEND, bytes -> {
-      dataBoth2Mqtt.numberOfStringValues += 1;
-      dataBoth2Mqtt.lastStringValue = TestUtils.DefaultMappings.BytesToString(bytes);
-    });
-
-    Client client = ClientBuilder.newClient();
-    dataRest2Rest = client.target(REST_SERVER_BASE_URL + PATH_REST_2_REST_SEND);
-    dataMqtt2Rest = client.target(REST_SERVER_BASE_URL + PATH_MQTT_2_REST_SEND);
-    dataBoth2Rest = client.target(REST_SERVER_BASE_URL + PATH_BOTH_2_REST_SEND);
-    senderRest2Rest = client.target(REST_SERVER_BASE_URL + PATH_REST_2_REST_RECEIVE);
-    senderRest2Mqtt = client.target(REST_SERVER_BASE_URL + PATH_REST_2_MQTT_RECEIVE);
-    senderBoth2Rest = client.target(REST_SERVER_BASE_URL + PATH_BOTH_REST_RECEIVE);
-
-    model.connectMqtt2MqttInput(mqttUri(TOPIC_MQTT_2_MQTT_RECEIVE));
-    model.connectMqtt2MqttOutput(mqttUri(TOPIC_MQTT_2_MQTT_SEND), writeCurrentValue);
-    model.connectMqtt2RestInput(mqttUri(TOPIC_MQTT_2_REST_RECEIVE));
-    model.connectMqtt2RestOutput(restUri(PATH_MQTT_2_REST_SEND, REST_PORT), writeCurrentValue);
-    model.connectRest2MqttInput(restUri(PATH_REST_2_MQTT_RECEIVE, REST_PORT));
-    model.connectRest2MqttOutput(mqttUri(TOPIC_REST_2_MQTT_SEND), writeCurrentValue);
-    model.connectRest2RestInput(restUri(PATH_REST_2_REST_RECEIVE, REST_PORT));
-    model.connectRest2RestOutput(restUri(PATH_REST_2_REST_SEND, REST_PORT), writeCurrentValue);
-    model.connectBoth2BothInput(mqttUri(TOPIC_BOTH_MQTT_RECEIVE));
-    model.connectBoth2BothInput(restUri(PATH_BOTH_REST_RECEIVE, REST_PORT));
-    model.connectBoth2MqttOutput(mqttUri(TOPIC_BOTH_2_MQTT_SEND), writeCurrentValue);
-    model.connectBoth2RestOutput(restUri(PATH_BOTH_2_REST_SEND, REST_PORT), writeCurrentValue);
-  }
-
   private static class ReceiverData {
     String lastStringValue;
     int numberOfStringValues = 0;