diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 401f721305affd0b14d18923027c42dd32c6edfb..a33e8f0fcddb2760ec9a9e83e337ccd5035da873 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -100,12 +100,25 @@ ragdoc_view:
     paths:
       - "pages/docs/ragdoc"
 
+build_cloc:
+  image: "alpine:latest"
+  needs:
+    - build
+  script:
+    - apk add cloc
+    - cd pages/cloc
+    - sh run-cloc.sh
+  artifacts:
+    paths:
+      - pages/docs/cloc.md
+
 .pages-template:
   image: python:3.10.0-bullseye
   stage: publish
   needs:
     - ragdoc_view
     - test
+    - build_cloc
   before_script:
     - pip install -r pages/requirements.txt
   script:
@@ -117,7 +130,7 @@ pages-dry-run:
     - dev
     - master
 
-pages-master:
+pages:
   extends: .pages-template
   artifacts:
     paths:
diff --git a/build.gradle b/build.gradle
index 716e4573edac6a32f4fe667ce5fa8140e2ee8858..6c38c4c59743a075e698735d42ca14200772cb58 100644
--- a/build.gradle
+++ b/build.gradle
@@ -4,3 +4,11 @@ plugins {
 }
 
 java.toolchain.languageVersion.set(JavaLanguageVersion.of(11))
+
+if (JavaVersion.current().isJava8Compatible()) {
+    allprojects {
+        tasks.withType(Javadoc) {
+            options.addStringOption('Xdoclint:none', '-quiet')
+        }
+    }
+}
diff --git a/gradle.properties b/gradle.properties
index 9c88607945e343af388683de801766b36845c4de..0bce44fa414299045b03a6dadeb0263340d9c523 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -1,3 +1,5 @@
 log4j_version = 2.17.2
 mustache_java_version = 0.9.10
 preprocessor_version = 0.1.0-41
+relast_version = 0.3.0-137
+dumpAst_version = 1.2.0
diff --git a/pages/.gitignore b/pages/.gitignore
index 14338f39f89e2a45e3473a6597e640aa9458f9a8..b731620fdf8be028d30088f0df75e32b5068744c 100644
--- a/pages/.gitignore
+++ b/pages/.gitignore
@@ -1,2 +1,3 @@
 /docs/ragdoc/
+/docs/cloc.md
 __pycache__
diff --git a/pages/cloc/.clocignore b/pages/cloc/.clocignore
new file mode 100644
index 0000000000000000000000000000000000000000..6be9eae9397330f2ed35619202932df433ce8bda
--- /dev/null
+++ b/pages/cloc/.clocignore
@@ -0,0 +1 @@
+run-cloc.sh
diff --git a/pages/cloc/.gitignore b/pages/cloc/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..2591dbfb5069d5010b8f0b40b18bf1a0a85cdb46
--- /dev/null
+++ b/pages/cloc/.gitignore
@@ -0,0 +1,10 @@
+files.csv
+my_definitions.txt
+ragconnect.base.lang
+ragconnect.file
+ragconnect.lang
+ragconnect.base-*.md
+ragconnect.base-*.txt
+ragconnect.tests-*.md
+ragconnect.tests-*.txt
+tmp.json
diff --git a/pages/cloc/cloc-def-connect.txt b/pages/cloc/cloc-def-connect.txt
new file mode 100644
index 0000000000000000000000000000000000000000..a20ca00ecfd5c2377ea06a87ebda2c4731ac30a2
--- /dev/null
+++ b/pages/cloc/cloc-def-connect.txt
@@ -0,0 +1,5 @@
+Connect
+    filter call_regexp_common Java
+    extension connect
+    extension ragconnect
+    3rd_gen_scale 10
diff --git a/pages/cloc/cloc-def-flex.txt b/pages/cloc/cloc-def-flex.txt
new file mode 100644
index 0000000000000000000000000000000000000000..4ac6dbb8da93e05d5b848dd5f8e8ed393c8b03c3
--- /dev/null
+++ b/pages/cloc/cloc-def-flex.txt
@@ -0,0 +1,4 @@
+Flex
+    filter remove_matches ^\s*//
+    extension flex
+    3rd_gen_scale 1.5
diff --git a/pages/cloc/cloc-def-grammar.txt b/pages/cloc/cloc-def-grammar.txt
new file mode 100644
index 0000000000000000000000000000000000000000..0afc6b8ad9bb80c5a34d155d2ea35cd856e9d924
--- /dev/null
+++ b/pages/cloc/cloc-def-grammar.txt
@@ -0,0 +1,5 @@
+Grammar
+    filter call_regexp_common Java
+    extension ast
+    extension relast
+    3rd_gen_scale 30
diff --git a/pages/cloc/cloc-def-jrag-jadd.txt b/pages/cloc/cloc-def-jrag-jadd.txt
new file mode 100644
index 0000000000000000000000000000000000000000..b5e3435ab068a9a83653808aa4e22aeb30c604fa
--- /dev/null
+++ b/pages/cloc/cloc-def-jrag-jadd.txt
@@ -0,0 +1,5 @@
+Attributes
+    filter call_regexp_common Java
+    extension jrag
+    extension jadd
+    3rd_gen_scale 1.5
diff --git a/pages/cloc/cloc-def-parser.txt b/pages/cloc/cloc-def-parser.txt
new file mode 100644
index 0000000000000000000000000000000000000000..c3df6758e0c6ca76f3af52e0ebb7a455c73b25a5
--- /dev/null
+++ b/pages/cloc/cloc-def-parser.txt
@@ -0,0 +1,4 @@
+Parser
+    filter remove_matches ^\s*//
+    extension parser
+    3rd_gen_scale 1.5
diff --git a/pages/cloc/run-cloc.sh b/pages/cloc/run-cloc.sh
new file mode 100755
index 0000000000000000000000000000000000000000..79308c9070e19ea9b6e77f26a9a343c9cca7117c
--- /dev/null
+++ b/pages/cloc/run-cloc.sh
@@ -0,0 +1,77 @@
+#!/bin/bash
+if [ "$1" == "-h" ] || [ "$1" == "--help" ]; then
+  echo "-s -> skip running cloc commands"
+  echo "-a -> produce files.csv with code count for single src files"
+  exit
+fi
+if [ "$1" != "-s" ]; then
+  #  --force-lang=Java,jrag --force-lang=Java,jadd
+  DEF_FILE=my_definitions.txt
+  echo "Export language definitions"
+  cloc --quiet --write-lang-def="$DEF_FILE"
+  for f in cloc-def-*.txt;
+  do
+  	cat $f >> "$DEF_FILE"
+  done
+  REPO_ROOT="../.."
+  CLOC_CMD="cloc --exclude-lang=JSON --read-lang-def=my_definitions.txt --exclude-list-file=.clocignore --quiet --hide-rate"
+  #  --ignored=bad-files.txt
+  cloc_double() {
+    f=$1
+    shift
+    echo "Creating $f.txt"
+    $CLOC_CMD --report-file="$f.txt" $@
+    echo "Creating $f.md"
+    $CLOC_CMD --md --report-file=tmp.md $@
+    tail -n+3 tmp.md > "$f.md"
+    rm tmp.md
+  }
+  make_page() {
+    echo "# Evaluation Metrics: Lines of Code"
+    echo
+    echo "## Manually written generator code"
+    echo
+    cat ragconnect.base-src-result.md
+    echo
+    echo "## Generated generator code"
+    cat ragconnect.base-gen-result.md
+    echo
+    echo "## Manually written test code"
+    cat ragconnect.tests-src-result.md
+    echo
+    echo "## Generated test code"
+    cat ragconnect.tests-gen-result.md
+  }
+fi
+if [ "$1" != "-s" ] && [ "$1" != "-a" ]; then
+  echo "Running cloc with new definitions"
+  cloc_double "ragconnect.base-src-result" --found=ragconnect.base-src-found.txt ${REPO_ROOT}/ragconnect.base/src/main/
+  cloc_double "ragconnect.base-gen-result" --found=ragconnect.base-gen-found.txt ${REPO_ROOT}/ragconnect.base/src/gen/jastadd ${REPO_ROOT}/ragconnect.base/src/gen/java
+  cloc_double "ragconnect.tests-src-result" --found=ragconnect.tests-src-found.txt ${REPO_ROOT}/ragconnect.tests/src/test/01-input/ ${REPO_ROOT}/ragconnect.tests/src/test/java/
+  cloc_double "ragconnect.tests-gen-result" --found=ragconnect.tests-gen-found.txt ${REPO_ROOT}/ragconnect.tests/src/test/02-after-ragconnect/ ${REPO_ROOT}/ragconnect.tests/src/test/java-gen
+  $CLOC_CMD --sum-reports --report_file=ragconnect ragconnect.base-src-result.txt ragconnect.base-gen-result.txt ragconnect.tests-src-result.txt ragconnect.tests-gen-result.txt
+  echo "Creating ../docs/cloc.md"
+  make_page > ../docs/cloc.md
+fi
+if [ "$1" == "-a" ]; then
+  echo "filename,code" > files.csv
+  for f in $(find ${REPO_ROOT}/ragconnect.base/src/main/ ${REPO_ROOT}/ragconnect.base/src/gen/jastadd-sources/ -type f); do
+    printf '.'
+    echo $f,$($CLOC_CMD --json $f | jq '.SUM.code') >> files.csv
+  done
+  echo
+  exit
+fi
+
+
+# cat ragconnect.base-src-result.txt
+# cat ragconnect.base.file
+# cat ragconnect.tests-result.txt
+
+echo "LOC stats:"
+echo "Language                     files          blank        comment           code"
+( for t in *-result.txt ; do echo -e "==> $t <=="; grep -v -e '---' -e 'SUM' -e 'Language' -e 'github' $t; done)
+
+echo 
+echo "Summary:"
+grep -v -e '---' -e 'SUM' -e 'Language' -e 'github' ragconnect.file
diff --git a/pages/docs/changelog.md b/pages/docs/changelog.md
index 1f41f95d1e11cb361d9d4e4b3675d20e09e575fe..450dbc1ac84cb0b038abbdc1d081fba315f0da76 100644
--- a/pages/docs/changelog.md
+++ b/pages/docs/changelog.md
@@ -4,9 +4,9 @@
 
 ### Changes
 
-- Allow connection endpoints for relations ([#37](https://git-st.inf.tu-dresden.de/jastadd/ragconnect/-/issues/37)) and attributes ([#38](https://git-st.inf.tu-dresden.de/jastadd/ragconnect/-/issues/38))
-- Allow send connection endpoints non-NTA nonterminals ([#36](https://git-st.inf.tu-dresden.de/jastadd/ragconnect/-/issues/36))
-- Allow context-free context endpoints ([#34](https://git-st.inf.tu-dresden.de/jastadd/ragconnect/-/issues/34))
+- Allow connection ports for relations ([#37](https://git-st.inf.tu-dresden.de/jastadd/ragconnect/-/issues/37)) and attributes ([#38](https://git-st.inf.tu-dresden.de/jastadd/ragconnect/-/issues/38))
+- Allow send connection ports non-NTA nonterminals ([#36](https://git-st.inf.tu-dresden.de/jastadd/ragconnect/-/issues/36))
+- Allow context-free context ports ([#34](https://git-st.inf.tu-dresden.de/jastadd/ragconnect/-/issues/34))
 - Experimental support for Java handler ([#52](https://git-st.inf.tu-dresden.de/jastadd/ragconnect/-/issues/52))
 - Make specification language more concise ([#33](https://git-st.inf.tu-dresden.de/jastadd/ragconnect/-/issues/33))
 
@@ -17,24 +17,24 @@
 
 ## 0.3.2
 
-- Allow connection endpoints for list nonterminals ([#21](https://git-st.inf.tu-dresden.de/jastadd/ragconnect/-/issues/21))
+- Allow connection ports for list nonterminals ([#21](https://git-st.inf.tu-dresden.de/jastadd/ragconnect/-/issues/21))
 - Ensure correct connect and disconnect functionality ([#31](https://git-st.inf.tu-dresden.de/jastadd/ragconnect/-/issues/31))
 - Enhance documentation ([#13](https://git-st.inf.tu-dresden.de/jastadd/ragconnect/-/issues/13), [#20](https://git-st.inf.tu-dresden.de/jastadd/ragconnect/-/issues/20), [#41](https://git-st.inf.tu-dresden.de/jastadd/ragconnect/-/issues/41))
 
 ## 0.3.1
 
 - Full support for incremental dependency tracking
-- Full support for subtree endpoint definitions ([#9](https://git-st.inf.tu-dresden.de/jastadd/ragconnect/-/issues/9))
+- Full support for subtree port definitions ([#9](https://git-st.inf.tu-dresden.de/jastadd/ragconnect/-/issues/9))
 - Internal: Use updated gradle plugin for tests ([#18](https://git-st.inf.tu-dresden.de/jastadd/ragconnect/-/issues/18))
-- Bugfix [#22](https://git-st.inf.tu-dresden.de/jastadd/ragconnect/-/issues/22): Correct handling of malformed URIs passed when connecting an endpoint
-- Bugfix [#23](https://git-st.inf.tu-dresden.de/jastadd/ragconnect/-/issues/23): Correct handling of OptComponents as endpoints
+- Bugfix [#22](https://git-st.inf.tu-dresden.de/jastadd/ragconnect/-/issues/22): Correct handling of malformed URIs passed when connecting an port
+- Bugfix [#23](https://git-st.inf.tu-dresden.de/jastadd/ragconnect/-/issues/23): Correct handling of OptComponents as ports
 - Bugfix [#27](https://git-st.inf.tu-dresden.de/jastadd/ragconnect/-/issues/27): Correctly handle whitespaces in grammars
 
 ## 0.3.0
 
 - Added [API documentation](ragdoc/index.html) to documentation
-- Add methods to `disconnect` an endpoint
-- Internal: PoC for incremental dependency tracking and subtree endpoint definitions ([#14](https://git-st.inf.tu-dresden.de/jastadd/ragconnect/-/issues/14))
+- Add methods to `disconnect` a port
+- Internal: PoC for incremental dependency tracking and subtree port definitions ([#14](https://git-st.inf.tu-dresden.de/jastadd/ragconnect/-/issues/14))
 - Bugfix [#17](https://git-st.inf.tu-dresden.de/jastadd/ragconnect/-/issues/17): Added missing support for `boolean`
 
 ## 0.2.2
diff --git a/pages/docs/compiler.md b/pages/docs/compiler.md
index 3638757c78a1624bff4b94407d006a2636751884..e89ad8fc8169e084c97736d6447c3466f1e2bc27 100644
--- a/pages/docs/compiler.md
+++ b/pages/docs/compiler.md
@@ -90,9 +90,9 @@ However, depending on the selected protocols and/or used features, additional de
     - JastAdd version has to support `INC_FLUSH_START` and `INC_FLUSH_END` (i.e., has [issue #329][jastadd-issue-329] resolved)
     - [Feature description](using.md#dependency-tracking-automatically-derived)
 
-### Tree/List Endpoints
+### Tree/List Ports
 
-- Condition: When using endpoints along with default mappings for subtrees
+- Condition: When using ports along with default mappings for subtrees
 - Required runtime dependencies:
     - `group: 'com.fasterxml.jackson.core', name: 'jackson-core', version: '2.12.1'`
     - `group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: '2.12.1'`
diff --git a/pages/docs/dsl.md b/pages/docs/dsl.md
index 3451c43f32ba18d6af90d8f201fd0aa366d18817..6776c0449cce0a903cab11c8f9fa061fef04045e 100644
--- a/pages/docs/dsl.md
+++ b/pages/docs/dsl.md
@@ -3,14 +3,14 @@
 
 # The RagConnect Specification Language
 
-To declare endpoints and mappings, a domain-specific language ([DSL](https://en.wikipedia.org/wiki/Domain-specific_language)) is used.
+To declare ports and mappings, a domain-specific language ([DSL](https://en.wikipedia.org/wiki/Domain-specific_language)) is used.
 
-## Endpoints
+## Ports
 
-An endpoint marks an element of an AST as sending or receiving element.
-The kind of the element determines, whether an endpoint for it can be receiving, sending, or both at the same time.
+A port marks an element of an AST as sending or receiving element.
+The kind of the element determines, whether an port for it can be receiving, sending, or both at the same time.
 
-To declare a new endpoints, use the following syntax:
+To declare a new ports, use the following syntax:
 
 ```
 ("send"|"receive") ["indexed"] ["with add"] <Non-Terminal>[.<Target>["(<AttributeType>)"]] ["using" <Mapping-Name> (, <Mapping-Name>)*] ";"
@@ -18,34 +18,34 @@ To declare a new endpoints, use the following syntax:
 
 A breakdown of the parts of that syntax:
 
-- The first word (`send` or `receive`) defines the kind of endpoint - sending or receiving, respectively.
-- The optional `indexed` applies only for list children and lets the endpoint act on elements of that list.
-  This only works for receiving endpoints, and is further changed by `with add`.
+- The first word (`send` or `receive`) defines the kind of port - sending or receiving, respectively.
+- The optional `indexed` applies only for list children and lets the port act on elements of that list.
+  This only works for receiving ports, and is further changed by `with add`.
     - A lonely `indexed` assigns each incoming "topic" to an index in a list.
-      This can be useful if multiple instances of this endpoint are connected, or the communication protocol supports wildcard topics.
+      This can be useful if multiple instances of this port are connected, or the communication protocol supports wildcard topics.
       For the former case, the connect method with an explicit index can be used, whereas the "normal" connect method without the index acts as a method for "wildcard-connect".
     - Combining `indexed with add`, incoming data is required to be an element of the list, and will be appended to the list.
-- The second optional keyword `with add` can also be used only for receiving endpoints targeting a list children.
+- The second optional keyword `with add` can also be used only for receiving ports targeting a list children.
   As described above, it can be combined with `indexed`.
   If used on its own, the incoming data is interpreted as a complete list and its elements will be appended to the current list.
 - The `<Non-Terminal>[.<Target>["(<AttributeType>)"]]` notation describes the actual affected node.
-    - If the target is omitted, all nodes of that non-terminal type can be connected, irrespective of their context. This is a context-free endpoint definition.
+    - If the target is omitted, all nodes of that non-terminal type can be connected, irrespective of their context. This is a context-free port definition.
     - The target can be any child on the right-hand side of a production rule, a role of a relation, or an attribute.
       The brackets `(<AttributeType>)` after the target must be used in case of an attribute, and only then.
       Here, the return type of the attribute has to be specified, as aspect files are not parsed by RagConnect.
       Hence, RagConnect can not and will not verify the existence of the attribute, and the possible non-existence of an attribute will be found by the Java compiler.
-- Optionally, an endpoint can use one or more [mappings](#mappings).
+- Optionally, a port can use one or more [mappings](#mappings).
   They will be applied before sending, or after receiving a message.
   Mappings will always be applied in the order they are listed after `using`.
 
-### Context-Free Endpoints
+### Context-Free Ports
 
-An endpoint with only a non-terminal and without a target is called context-free endpoint.
-Specifying such an endpoint has several consequences:
+A port with only a non-terminal and without a target is called context-free port.
+Specifying such a port has several consequences:
 
-- The given non-terminal can be connected to in all contexts it occurs as if there were endpoints for all those contexts.
+- The given non-terminal can be connected to in all contexts it occurs as if there were ports for all those contexts.
 - There is a special method available on the given non-terminal to connect itself, which selects the correct connect-method depending on its context.
-- Context-sensitive endpoints for this non-terminal can still be specified to modify mappings in this context. If the context is a list, the endpoint must use `indexed` and cannot use `with add`.
+- Context-sensitive ports for this non-terminal can still be specified to modify mappings in this context. If the context is a list, the port must use `indexed` and cannot use `with add`.
 
 **Example**:
 
@@ -56,7 +56,7 @@ A ::= <Value> ;
 
 // connect
 receive A;
-receive Root.SingleA using MyMapping; // specialized endpoint
+receive Root.SingleA using MyMapping; // specialized port
 ```
 
 Implied, additional connect specifications:
@@ -79,8 +79,8 @@ root.connectOptA("<some-uri-to-connect>");
 
 ## Mappings
 
-A mapping is a side effect-free function with one argument (the value that will be transformed) and one result (the transformed value), that will be applied on a value to be sent for a sending endpoint, a received value for a receiving endpoint, or the result of another mapping.
-Mappings can be shared between endpoints.
+A mapping is a side effect-free function with one argument (the value that will be transformed) and one result (the transformed value), that will be applied on a value to be sent for a sending port, a received value for a receiving port, or the result of another mapping.
+Mappings can be shared between ports.
 
 To declare a mapping, use the following syntax:
 
@@ -93,16 +93,16 @@ To declare a mapping, use the following syntax:
 A breakdown of the parts of that syntax:
 
 - The `<Mapping-Name>` identifies the mapping.
-- The `<From-Type` is the type of the input. The type of the first mapping of a receiving endpoint must be `byte[]`.
+- The `<From-Type` is the type of the input. The type of the first mapping of a receiving port must be `byte[]`.
 - To refer to the input, `<Input-Variable-Name>` defines the name of it.
-- The `<To-Type>` is the type of the result. The type of the last mapping of a sending endpoint must be `byte[]`.
+- The `<To-Type>` is the type of the result. The type of the last mapping of a sending port must be `byte[]`.
 - Finally, the `<Java-Block>` is the actual definition of the mapping using normal Java syntax.
   The previously defined input variable can be used via its name here.
   This block can contain multiple statements, but must end with a `return` statement.
   The validity of this block is not verified by RagConnect itself, but later in the compilation process by the Java compiler.
 
 Note: There are default mappings provided for all primitive Java types (using their "normal" byte representation), and for all non-terminal types (using their JSON representation converted from/to bytes).
-Those default mappings apply to both sending and receiving endpoints, and match their counterparts, e.g., the mapping from `int` to `byte[]` uses the same byte representation as the mapping back from `byte[]` to `int`.
+Those default mappings apply to both sending and receiving ports, and match their counterparts, e.g., the mapping from `int` to `byte[]` uses the same byte representation as the mapping back from `byte[]` to `int`.
 Default mappings are always inserted if either no mapping is present, or if the type of the first/last mapping is not `byte[]` as stated above.
 Their main intent is to allow quick prototyping without constraining a more complex use case.
 
@@ -112,7 +112,7 @@ Their main intent is to allow quick prototyping without constraining a more comp
     Deprecated since `1.0.0`
 
 A dependency definition describes a possible dependency on type-level from a token to an attribute.
-Whenever the token changes, the attribute is eagerly re-computed and endpoints attached to it are triggered.
+Whenever the token changes, the attribute is eagerly re-computed and ports attached to it are triggered.
 
 Such a dependency must be added on instance-level for every token that could have an influence to the attribute.
 An alternative for those explicit dependency definitions is [incremental dependency tracking](using.md#dependency-tracking-automatically-derived).
diff --git a/pages/docs/img/poster.pdf b/pages/docs/img/2020_mpm4cps/poster.pdf
similarity index 100%
rename from pages/docs/img/poster.pdf
rename to pages/docs/img/2020_mpm4cps/poster.pdf
diff --git a/pages/docs/img/robo3d.png b/pages/docs/img/2020_mpm4cps/robo3d.png
similarity index 100%
rename from pages/docs/img/robo3d.png
rename to pages/docs/img/2020_mpm4cps/robo3d.png
diff --git a/pages/docs/img/mpm4cps-slides.pdf b/pages/docs/img/2020_mpm4cps/slides.pdf
similarity index 100%
rename from pages/docs/img/mpm4cps-slides.pdf
rename to pages/docs/img/2020_mpm4cps/slides.pdf
diff --git a/pages/docs/img/2022_models/photo.jpg b/pages/docs/img/2022_models/photo.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..55a8d11e0e1fe10a16d5f4849c8af727b25f89cd
Binary files /dev/null and b/pages/docs/img/2022_models/photo.jpg differ
diff --git a/pages/docs/img/ragconnect-process.png b/pages/docs/img/ragconnect-process.png
new file mode 100644
index 0000000000000000000000000000000000000000..7f7ee7ec8d6d2e6b001ca9bc16c9cac8a928d975
Binary files /dev/null and b/pages/docs/img/ragconnect-process.png differ
diff --git a/pages/docs/img/ros2rag-process.png b/pages/docs/img/ros2rag-process.png
deleted file mode 100644
index f44fad87ba0d5205dc6638be24fb97fe8ce86bbd..0000000000000000000000000000000000000000
Binary files a/pages/docs/img/ros2rag-process.png and /dev/null differ
diff --git a/pages/docs/inner-workings.md b/pages/docs/inner-workings.md
index 1cb22a597804d2a67a6f05c0181f4b7c359644ee..c3f88279d126ec6f4f878621c6eb0f0650061225 100644
--- a/pages/docs/inner-workings.md
+++ b/pages/docs/inner-workings.md
@@ -2,17 +2,17 @@
 
 Please see [API documentation](ragdoc/index.html) for more details.
 
-![ros2rag-process](img/ros2rag-process.png)
+![ragconnect-process](img/ragconnect-process.png)
 
 `RagConnect` uses the [relast-preprocessor](https://git-st.inf.tu-dresden.de/jastadd/relast-preprocessor) to parse `.relast` grammar files.
 This results in an ASTNode of type `Program`.
-It further uses a dedicated parser for `.connect` files containing endpoint-, mapping-, and dependency-definitions.
+It further uses a dedicated parser for `.connect` files containing port-, mapping-, and dependency-definitions.
 This results in an ASTNode of type `RagConnect`.
-The goal is to generate an aspect file containing setters and getters of tokens referred to by endpoint-definitions
+The goal is to generate an aspect file containing setters and getters of tokens referred to by port-definitions
 We use [mustache](https://mustache.github.io/) (currently its [Java version](https://github.com/spullara/mustache.java)) making use of partials to separate concerns.
 The `.mustache` files are located in `ragconnect.base/src/main/resources`.
 The generation process uses intermediates NTAs (whose types are defined in `Intermediate.relast`) to ease the definition of two main generation "problems".
-Those problems are differentiation on both kinds of an endpoint (send/receive and type/token/list), and attributes depending on position in n-ary relations.
+Those problems are differentiation on both kinds of a port (send/receive and type/token/list), and attributes depending on position in n-ary relations.
 
 There are aspect files for `Navigation` (mainly isX/asX attributes), `Analysis` (static analysis attributes), `Printing`, `Mappings` (default mappings).
 One of the main aspects is `Intermediate` containing all attributes consumed by `mustache` and other attributes the former depend on.
@@ -26,7 +26,7 @@ In the following, details for special implementation topics are discussed.
 
 ## Forwarding
 
-When a nonterminal is used in a send endpoints, it needs an implicit forwarding attribute to work, because only _computed elements_ can be sent.
+When a nonterminal is used in a send ports, it needs an implicit forwarding attribute to work, because only _computed elements_ can be sent.
 Since the nonterminal itself should be sent, the generated attribute simply returns this nonterminal.
 
 However, changing any token within the whole subtree or changing the structure of the subtree must trigger a new message, upon computation of the forwarding attribute, all tokens are "touched" (their getter is called).
@@ -42,20 +42,31 @@ To help with finding errors/bugs when tests fail, there are several things to fi
 
 - **Look closely**. Analyze the error message closely, and possible any previous error message(s) that could have caused the test to fail.
 - **Focus on single error**
-  - To only inspect one test, mark them with `@Tag("New")` and use the gradle task "newTests".
-  - Use `Assumptions.assumeTrue(false);` to abort unneeded test cases early.
-  - When editing RagConnect itself and force recreating source for the affected test, e.g., `compileForwardingIncremental.outputs.upToDateWhen { false }`
-  - _Remember to undo all changes, once the bug is fixed._
-- **Activate logs**. Add the following to the `ragconnect` specification of the compile-task of the affected test:
-  ```
-  logReads = true
-  logWrites = true
-  logIncremental = true
-  ```
-  _Remember to remove those lines, once the bug is fixed._
-- **Trace incremental events**. Add the following right after create the root node (named `model` here):
-  ```java
-  model.trace().setReceiver(TestUtils::logEvent);
-  ```
-  This will output every event fired by the incremental evaluation engine. _Remember to remove this line, once the bug is fixed._
+    - To only inspect one test, mark them with `@Tag("New")` and use the gradle task "newTests".
+    - Use `Assumptions.assumeTrue(false);` to abort unneeded test cases early.
+    - When editing RagConnect itself and force recreating source for the affected test, e.g., `compileForwardingIncremental.outputs.upToDateWhen { false }`
+    - _Remember to undo all changes, once the bug is fixed._
+- **Activate logs**. Activate logging in the `ragconnect` specification of the compile-task of the affected test:
+    - _Remember to remove those lines, once the bug is fixed._
+
+```groovy
+task compile(type: RagConnectTest) {
+    ragconnect {
+        // ... other parameters ...
+        logReads = true
+        logWrites = true
+        logIncremental = true
+    }
+    // ... other tools ...
+}
+```
+
+- **Trace incremental events**. Add a receiver right after creation of the root node (named `model` here)
+    - This will output every event fired by the incremental evaluation engine.
+    - _Remember to remove this line, once the bug is fixed._
+
+```java
+model.trace().setReceiver(TestUtils::logEvent);
+```
+
 - **Add log statements**. As there will be quite some log output, add some identifying log statement (i.e., using `logger.fatal("---")`) right before the suspicious statement to inspect only the relevant log message after that.
diff --git a/pages/docs/use_cases.md b/pages/docs/use_cases.md
index 6e4524c7801f92d41c41371db585046ec6a83136..56c5af23a33a6aded02584dd4d1b37828dfdac4d 100644
--- a/pages/docs/use_cases.md
+++ b/pages/docs/use_cases.md
@@ -1,13 +1,13 @@
 # Use cases with `RagConnect`
 
-## MPM4CPS Paper - Codename 'Ros2Rag'
+## MPM4CPS Paper (2020) - Codename 'Ros2Rag'
 
 In the publication [*"Connecting conceptual models using Relational Reference Attribute Grammars"*](https://doi.org/10.1145/3417990.3421437), a use case involving a simulated robot arm and two different models connected to it was shown.
 One model was used to ensure a low speed of the robot when within a safety zone (purple boxes in the picture below), and the other model executes a workflow to control the robot.
 
-![Screenshot of Gazebo](img/robo3d.png)
+![Screenshot of Gazebo](img/2020_mpm4cps/robo3d.png)
 
-This paper was presented on October, 16h during the [MPM4CPS workshop](https://msdl.uantwerpen.be/conferences/MPM4CPS/2020/) within the [MODELS 2020 conference](https://conf.researchr.org/home/models-2020). For more information, see the [presented slides](img/mpm4cps-slides.pdf), [a recording of the session](https://youtu.be/Hgc1qFfmr44?t=1220) or the accompanied [poster](img/poster.pdf).
+This paper was presented on October, 16th 2020 during the [MPM4CPS workshop](https://msdl.uantwerpen.be/conferences/MPM4CPS/2020/) within the [MODELS 2020 conference](https://conf.researchr.org/home/models-2020). For more information, see the [presented slides](img/2020_mpm4cps/slides.pdf), [a recording of the session](https://youtu.be/Hgc1qFfmr44?t=1220) or the accompanied [poster](img/2020_mpm4cps/poster.pdf).
 
 The repository with the used source code can be found at: <https://git-st.inf.tu-dresden.de/ceti/ros/mpm4cps2020>
 The usage is dockerized, so starting the application only involves the commands listed below.
@@ -28,3 +28,22 @@ docker-compose up rag_app
 # Terminal 3: Goal-Model
 docker-compose up rag_goal
 ```
+
+## ACSOS Paper (2022) - Codename 'Motion Grammar Demo'
+
+In the paper [*"Specifying Reactive Robotic Applications
+With Reference Attribute Motion Grammars"*](http://mg.relational-rags.eu), motion grammars (an older approach by [Dantham and Stilman](https://doi.org/10.1109/TRO.2013.2239553)) were implemented using Relational RAGs.
+This paper will be presented on September, 21st 2022 during the [Posters and Demos session](https://2022.acsos.org/track/acsos-2022-posters-and-demos) of the [ACSOS 2022 conference](https://2022.acsos.org/).
+For more information, see <http://mg.relational-rags.eu/>.
+
+## MODELS Paper (2022) - Codename 'Ros3Rag'
+
+In the paper [*"Incremental Causal Connection for Self-Adaptive Systems Based on Relational Reference Attribute Grammars"*](https://doi.org/10.1145/3550355.3552460), a [previous use case](#mpm4cps-paper-codename-ros2rag) was extended to Collaborative, Teaching-Based Robotic Cells.
+
+![Screenshot](img/2022_models/photo.jpg)
+
+This paper will be presented in the technical track of the [MODELS 2022 conference](https://conf.researchr.org/home/models-2022) (Oct 23 - Oct 29).
+There is an [artifact hosted at Zenodo](https://doi.org/10.5281/zenodo.7009758) containing all source code and the executable case study.
+Furthermore, the presentation slides and a poster will be available soon.
+
+The repository with the used source code can be found at: <https://git-st.inf.tu-dresden.de/ceti/ros/models2022>
diff --git a/pages/docs/using.md b/pages/docs/using.md
index 3db759d61abfb9995e31a661c2573fec86540677..e9705465ab851344f0abda19acd75e1cad334cb4 100644
--- a/pages/docs/using.md
+++ b/pages/docs/using.md
@@ -17,7 +17,7 @@ B ::= <OutputOnB:String> ;
 To declare receiving and sending tokens, a dedicated DSL is used:
 
 ```java
-// endpoint definitions
+// port definitions
 receive A.Input ;
 send A.OutputOnA ;
 send B.OutputOnB using Transformation ;
@@ -101,11 +101,11 @@ The first parameter of those connect-methods is always a URI-like String, to ide
 In case of MQTT, the server is the host running an MQTT broker, and the path is equal to the topic to publish or subscribe to.
 Please note, that the first leading slash (`/`) is removed for MQTT topics, e.g., for `A.Input` the topic is actually `topic/for/input`.
 
-For sending endpoints, there is a second boolean parameter to specify whether the current value shall be sent immediately after connecting.
+For sending ports, there is a second boolean parameter to specify whether the current value shall be sent immediately after connecting.
 
 ## Remarks for using manual dependency tracking
 
-When constructing the AST and connecting it, one should always set dependencies before connecting, especially if updates already arriving for receiving endpoints.
+When constructing the AST and connecting it, one should always set dependencies before connecting, especially if updates already arriving for receiving ports.
 Otherwise, updates might not be propagated after setting dependencies, if values are equal after applying transformations of mapping definitions.
 
 As an example, when using the following grammar and definitions for RagConnect ...
@@ -129,7 +129,7 @@ Round maps float f to int {:
 
 # An advanced example
 
-Non-terminal children can also be selected as endpoints (not only tokens).
+Non-terminal children can also be selected as ports (not only tokens).
 
 ## Normal Non-Terminal Children
 
@@ -156,7 +156,7 @@ receive ReceiverRoot.A ;
 
 To process non-terminals, default mappings are provided for every non-terminal type of the used grammar.
 They use the JSON serialization offered by the RelAST compiler, i.e., interpret the message as a `String`, deserialize the content reading the message as JSON, or vice versa.
-Additional dependencies are required to use this feature, as detailed in [the compiler section](compiler.md#treelist-endpoints).
+Additional dependencies are required to use this feature, as detailed in [the compiler section](compiler.md#treelist-ports).
 
 ## Receiving List Children
 
@@ -173,7 +173,7 @@ Several options are possible (please also refer to the specification of the [con
 
 ### (empty)
 
-A message for a list endpoint can be interpreted as a complete list (a sequence of nodes of type `A`) by not specifying any special keyword:
+A message for a list port can be interpreted as a complete list (a sequence of nodes of type `A`) by not specifying any special keyword:
 
 ```
 receive ReceiverRoot.A ;
@@ -190,7 +190,7 @@ receive with add ReceiverRoot.Alfa ;
 
 ### indexed
 
-A message for a list endpoint can also be interpreted as an element of this list.
+A message for a list port can also be interpreted as an element of this list.
 
 ```
 receive tree ReceiverRoot.A ;
@@ -245,10 +245,10 @@ assertEquals(receiverRoot.getAList(), list("1", "other"));
 assertEquals(receiverRoot.getAList(), list("1", "other", "new"));
 ```
 
-## Using attributes as endpoint targets
+## Using attributes as port targets
 
-As described in the [DSL specification](dsl.md), attributes can be used as endpoint targets.
-They can only be used in send endpoints, and the return type of the attribute must be specified in the connect specification (because aspect files are not handled completely yet).
+As described in the [DSL specification](dsl.md), attributes can be used as port targets.
+They can only be used in send ports, and the return type of the attribute must be specified in the connect specification (because aspect files are not handled completely yet).
 
 Currently, synthesized, inherited, collection, and circular attributes are supported.
 Nonterminal attributes are best used with the "legacy" notation `/Context:Type/` within the grammar.
diff --git a/pages/mkdocs.yml b/pages/mkdocs.yml
index 4c2d24f6df4052f0ddeb6eb79c6f077b8338a0af..7c389957da15ec854fc86fe62d6d84bfcad3873d 100644
--- a/pages/mkdocs.yml
+++ b/pages/mkdocs.yml
@@ -9,6 +9,7 @@ nav:
   - "RagConnect Specification Language": dsl.md
   - "Compiler options": compiler.md
   - "Inner workings": inner-workings.md
+  - "Evaluation Metrics: Lines of Code": cloc.md
   - "Extending RagConnect": extending.md
   - "Changelog": changelog.md
   - "API documentation": ragdoc/index.html
diff --git a/ragconnect.base/build.gradle b/ragconnect.base/build.gradle
index c138189d065693a908cd0e276dcddc3eb2320313..b00a4301c412901a14dd13199829a2ad7904eab1 100644
--- a/ragconnect.base/build.gradle
+++ b/ragconnect.base/build.gradle
@@ -31,7 +31,7 @@ configurations {
 }
 
 dependencies {
-    relast group: 'org.jastadd', name: 'relast', version: "0.3.0-137"
+    relast group: 'org.jastadd', name: 'relast', version: "${relast_version}"
     implementation group: 'org.jastadd', name: 'relast-preprocessor', version: "${preprocessor_version}"
     implementation group: 'com.github.spullara.mustache.java', name: 'compiler', version: "${mustache_java_version}"
     jastadd2 group: 'org.jastadd', name: 'jastadd2', version: '2.3.5-dresden'
diff --git a/ragconnect.base/src/main/jastadd/Analysis.jrag b/ragconnect.base/src/main/jastadd/Analysis.jrag
index 4baff8e5763af0b3a1c8870fa1d2b0a935bb9a24..79f37b38b3839982f784f1dd79b094a663aaa921 100644
--- a/ragconnect.base/src/main/jastadd/Analysis.jrag
+++ b/ragconnect.base/src/main/jastadd/Analysis.jrag
@@ -1,13 +1,13 @@
 aspect Analysis {
   // --- isAlreadyDefined ---
-  syn boolean EndpointDefinition.isAlreadyDefined() = getEndpointTarget().isAlreadyDefined();
-  syn boolean EndpointTarget.isAlreadyDefined();
-  eq AttributeEndpointTarget.isAlreadyDefined() {
+  syn boolean PortDefinition.isAlreadyDefined() = getPortTarget().isAlreadyDefined();
+  syn boolean PortTarget.isAlreadyDefined();
+  eq AttributePortTarget.isAlreadyDefined() {
     // define lookup here, as not used elsewhere
     int numberOfSameDefs = 0;
-    for (EndpointTarget target : ragconnect().givenEndpointTargetList()) {
-      if (target.isAttributeEndpointTarget()) {
-        AttributeEndpointTarget other = target.asAttributeEndpointTarget();
+    for (PortTarget target : ragconnect().givenPortTargetList()) {
+      if (target.isAttributePortTarget()) {
+        AttributePortTarget other = target.asAttributePortTarget();
         if (other.getParentTypeDecl().equals(this.getParentTypeDecl()) && other.getName().equals(this.getName())) {
           numberOfSameDefs += 1;
         }
@@ -15,12 +15,12 @@ aspect Analysis {
     }
     return numberOfSameDefs > 1;
   }
-  eq RelationEndpointTarget.isAlreadyDefined() {
+  eq RelationPortTarget.isAlreadyDefined() {
     // define lookup here, as not used elsewhere
     int numberOfSameDefs = 0;
-    for (EndpointTarget target : ragconnect().givenEndpointTargetList()) {
-      if (target.isRelationEndpointTarget()) {
-        RelationEndpointTarget other = target.asRelationEndpointTarget();
+    for (PortTarget target : ragconnect().givenPortTargetList()) {
+      if (target.isRelationPortTarget()) {
+        RelationPortTarget other = target.asRelationPortTarget();
         if (other.getRole().equals(this.getRole())) {
           numberOfSameDefs += 1;
         }
@@ -28,25 +28,25 @@ aspect Analysis {
     }
     return numberOfSameDefs > 1;
   }
-  eq TokenEndpointTarget.isAlreadyDefined() {
-    return lookupTokenEndpointDefinitions(getToken()).stream()
-        .filter(containingEndpointDefinition()::matchesType)
+  eq TokenPortTarget.isAlreadyDefined() {
+    return lookupTokenPortDefinitions(getToken()).stream()
+        .filter(containingPortDefinition()::matchesType)
         .count() > 1;
   }
-  eq TypeEndpointTarget.isAlreadyDefined() {
-    return lookupGivenTypeEndpointDefinitions(getType()).stream()
-        .filter(containingEndpointDefinition()::matchesType)
+  eq TypePortTarget.isAlreadyDefined() {
+    return lookupGivenTypePortDefinitions(getType()).stream()
+        .filter(containingPortDefinition()::matchesType)
         .count() > 1;
   }
-  eq ContextFreeTypeEndpointTarget.isAlreadyDefined() {
-    return lookupContextFreeTypeEndpointDefinitions(getTypeDecl()).stream()
-        .filter(containingEndpointDefinition()::matchesType)
+  eq ContextFreeTypePortTarget.isAlreadyDefined() {
+    return lookupContextFreeTypePortDefinitions(getTypeDecl()).stream()
+        .filter(containingPortDefinition()::matchesType)
         .count() > 1;
   }
   syn boolean DependencyDefinition.isAlreadyDefined() = lookupDependencyDefinition(getSource().containingTypeDecl(), getID()) != this;
 
   // --- matchesType ---
-  syn boolean EndpointDefinition.matchesType(EndpointDefinition other) = this.getSend() == other.getSend();
+  syn boolean PortDefinition.matchesType(PortDefinition other) = this.getSend() == other.getSend();
 
   // --- assignableTo ---
   syn boolean MappingDefinitionType.assignableTo(JavaTypeUse target);
@@ -90,24 +90,24 @@ aspect Analysis {
     }
   }
 
-  syn boolean EndpointTarget.hasAttributeResetMethod();
-  eq AttributeEndpointTarget.hasAttributeResetMethod() = false;
-  eq RelationEndpointTarget.hasAttributeResetMethod() = false;
-  eq TokenEndpointTarget.hasAttributeResetMethod() = getToken().getNTA();
-  eq TypeEndpointTarget.hasAttributeResetMethod() = getType().getNTA();
-  eq ContextFreeTypeEndpointTarget.hasAttributeResetMethod() = false;
+  syn boolean PortTarget.hasAttributeResetMethod();
+  eq AttributePortTarget.hasAttributeResetMethod() = false;
+  eq RelationPortTarget.hasAttributeResetMethod() = false;
+  eq TokenPortTarget.hasAttributeResetMethod() = getToken().getNTA();
+  eq TypePortTarget.hasAttributeResetMethod() = getType().getNTA();
+  eq ContextFreeTypePortTarget.hasAttributeResetMethod() = false;
 
   // --- needProxyToken ---
   syn boolean TokenComponent.needProxyToken() = !getDependencySourceDefinitionList().isEmpty() ||
-          getTokenEndpointTargetList().stream()
-                  .map(EndpointTarget::containingEndpointDefinition)
-                  .anyMatch(EndpointDefinition::shouldNotResetValue);
+          getTokenPortTargetList().stream()
+                  .map(PortTarget::containingPortDefinition)
+                  .anyMatch(PortDefinition::shouldNotResetValue);
 
   // --- effectiveUsedAt ---
-  coll Set<EndpointDefinition> MappingDefinition.effectiveUsedAt()
-    [new java.util.HashSet<EndpointDefinition>()]
+  coll Set<PortDefinition> MappingDefinition.effectiveUsedAt()
+    [new java.util.HashSet<PortDefinition>()]
     root RagConnect;
-  EndpointDefinition contributes this
+  PortDefinition 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 922fbf3dda85efa883706c684fe63a559bc65684..5e15584f2188ce2d52ad74c60444a3be0e27eede 100644
--- a/ragconnect.base/src/main/jastadd/Errors.jrag
+++ b/ragconnect.base/src/main/jastadd/Errors.jrag
@@ -3,45 +3,45 @@ aspect Errors {
     [new TreeSet<CompilerMessage>()]
     root RagConnect;
 
-  EndpointDefinition contributes error("Endpoint definition already defined for " + parentTypeName() + "." + entityName())
+  PortDefinition contributes error("Port definition already defined for " + parentTypeName() + "." + entityName())
     when isAlreadyDefined()
     to RagConnect.errors();
 
-  EndpointDefinition contributes error("Receiving target token must not be an NTA token!")
-    when !getSend() && getEndpointTarget().isTokenEndpointTarget() && token().getNTA()
+  PortDefinition contributes error("Receiving target token must not be an NTA token!")
+    when !getSend() && getPortTarget().isTokenPortTarget() && token().getNTA()
     to RagConnect.errors();
 
-  EndpointDefinition contributes error("Indexed based list access may only be used for type endpoint targets!")
-    when getIndexBasedListAccess() && !getEndpointTarget().isTypeEndpointTarget()
+  PortDefinition contributes error("Indexed based list access may only be used for type port targets!")
+    when getIndexBasedListAccess() && !getPortTarget().isTypePortTarget()
     to RagConnect.errors();
 
   // if first mapping is null, then suitableDefaultMapping() == null
-  EndpointDefinition contributes error("No suitable default mapping found for type " +
+  PortDefinition contributes error("No suitable default mapping found for type " +
       ((getMappingList().isEmpty())
           ? token().effectiveJavaTypeUse().prettyPrint()
           : getMappingList().get(0).getFromType().prettyPrint()))
-    when !getSend() && getEndpointTarget().isTokenEndpointTarget() && effectiveMappings().get(0) == null
+    when !getSend() && getPortTarget().isTokenPortTarget() && effectiveMappings().get(0) == null
     to RagConnect.errors();
 
-  EndpointDefinition contributes error("to-type of last mapping (" +
+  PortDefinition contributes error("to-type of last mapping (" +
       effectiveMappings().get(effectiveMappings().size() - 1).getToType().prettyPrint() +
       ") not assignable to type of the token (" + token().effectiveJavaTypeUse().prettyPrint() + ")!")
-    when !getSend() && getEndpointTarget().isTokenEndpointTarget() &&
+    when !getSend() && getPortTarget().isTokenPortTarget() &&
       !effectiveMappings().get(effectiveMappings().size() - 1).getToType().assignableTo(
           token().effectiveJavaTypeUse())
     to RagConnect.errors();
 
-  UntypedEndpointTarget contributes error("Could not resolve endpoint target " + getTypeName() + "." + getChildName())
+  UntypedPortTarget contributes error("Could not resolve port target " + getTypeName() + "." + getChildName())
     to RagConnect.errors();
 
-  ContextFreeTypeEndpointTarget contributes error("Context-Free endpoint not allowed for root node " +
+  ContextFreeTypePortTarget contributes error("Context-Free port not allowed for root node " +
       getTypeDecl().getName() + "!")
     when getTypeDecl().occurencesInProductionRules().isEmpty()
     to RagConnect.errors();
 
-  EndpointDefinition contributes error("Clash with implied, indexed endpoint definition of context-free endpoint in line " +
-          clashingContextFreeEndpointDefinition().getStartLine() + "!")
-    when clashingContextFreeEndpointDefinition() != null && clashingContextFreeEndpointDefinition().matchesType(this)
+  PortDefinition contributes error("Clash with implied, indexed port definition of context-free port in line " +
+          clashingContextFreePortDefinition().getStartLine() + "!")
+    when clashingContextFreePortDefinition() != null && clashingContextFreePortDefinition().matchesType(this)
     to RagConnect.errors();
 
   DependencyDefinition contributes error("Dependency definition already defined for " + getSource().containingTypeDecl().getName() + " with name " + getID())
@@ -63,14 +63,14 @@ aspect ErrorHelpers {
     return false;
   }
 
-  syn EndpointDefinition EndpointDefinition.clashingContextFreeEndpointDefinition() {
+  syn PortDefinition PortDefinition.clashingContextFreePortDefinition() {
     if (getSend() || !typeIsList() || getIndexBasedListAccess()) {
       return null;
     }
-    List<EndpointDefinition> contextFreeEndpointsWithSameType = lookupContextFreeTypeEndpointDefinitions(
-            getEndpointTarget().asTypeEndpointTarget().getType().getTypeDecl());
-    if (!contextFreeEndpointsWithSameType.isEmpty()) {
-      return contextFreeEndpointsWithSameType.get(0);
+    List<PortDefinition> contextFreePortsWithSameType = lookupContextFreeTypePortDefinitions(
+            getPortTarget().asTypePortTarget().getType().getTypeDecl());
+    if (!contextFreePortsWithSameType.isEmpty()) {
+      return contextFreePortsWithSameType.get(0);
     }
     return null;
   }
diff --git a/ragconnect.base/src/main/jastadd/Intermediate.jadd b/ragconnect.base/src/main/jastadd/Intermediate.jadd
index 1a24fa97c1c561bd841944a82231fe2fd40aad69..97cc5148e9e6901d8a1b23cc965adbd57a7c0d44 100644
--- a/ragconnect.base/src/main/jastadd/Intermediate.jadd
+++ b/ragconnect.base/src/main/jastadd/Intermediate.jadd
@@ -44,12 +44,12 @@ aspect SharedMustache {
 
   syn String RagConnect.rootNodeName() = getConfiguration().getRootNode().getName();
 
-  // === EndpointDefinition ===
-  syn String EndpointDefinition.lastResult() = lastDefinition().outputVarName();
+  // === PortDefinition ===
+  syn String PortDefinition.lastResult() = lastDefinition().outputVarName();
 
-  syn boolean EndpointDefinition.typeIsList() = getEndpointTarget().typeIsList();
+  syn boolean PortDefinition.typeIsList() = getPortTarget().typeIsList();
 
-  syn boolean EndpointDefinition.typeIsOpt() = getEndpointTarget().typeIsOpt();
+  syn boolean PortDefinition.typeIsOpt() = getPortTarget().typeIsOpt();
 
   // === attributes needed for computing above ones ===
   syn String RagConnect.logStatement(String level) {
@@ -78,12 +78,12 @@ aspect SharedMustache {
         return "unknownLoggingOptionGiven_" + getConfiguration().getLoggingTarget() + "_";
     }
   }
-  syn boolean EndpointTarget.typeIsList() = false;
-  eq TypeEndpointTarget.typeIsList() = getType().isListComponent();
-  eq RelationEndpointTarget.typeIsList() = getRole().isListRole();
+  syn boolean PortTarget.typeIsList() = false;
+  eq TypePortTarget.typeIsList() = getType().isListComponent();
+  eq RelationPortTarget.typeIsList() = getRole().isListRole();
 
-  syn boolean EndpointTarget.typeIsOpt() = false;
-  eq TypeEndpointTarget.typeIsOpt() {
+  syn boolean PortTarget.typeIsOpt() = false;
+  eq TypePortTarget.typeIsOpt() {
     return getType().isOptComponent();
   }
 }
@@ -137,20 +137,20 @@ aspect MustacheHandleUri { /* empty */ }
 
 aspect MustacheListAspect {
   // === RagConnect ===
-  syn boolean RagConnect.hasTreeListEndpoints() {
-    for (EndpointDefinition endpointDef : allEndpointDefinitionList()) {
-      if (endpointDef.typeIsList()) {
+  syn boolean RagConnect.hasTreeListPorts() {
+    for (PortDefinition portDef : allPortDefinitionList()) {
+      if (portDef.typeIsList()) {
         return true;
       }
     }
     return false;
   }
 
-  syn Set<TypeDecl> RagConnect.typesForReceivingListEndpoints() {
+  syn Set<TypeDecl> RagConnect.typesForReceivingListPorts() {
     Set<TypeDecl> result = new HashSet<>();
-    for (EndpointDefinition endpointDef : allEndpointDefinitionList()) {
-      if (endpointDef.typeIsList() && !endpointDef.getSend()) {
-        result.add(endpointDef.type().getTypeDecl());
+    for (PortDefinition portDef : allPortDefinitionList()) {
+      if (portDef.typeIsList() && !portDef.getSend()) {
+        result.add(portDef.type().getTypeDecl());
       }
     }
     return result;
@@ -158,16 +158,16 @@ aspect MustacheListAspect {
 }
 
 aspect MustacheMappingApplicationAndDefinition {
-  // === EndpointDefinition ===
-  syn String EndpointDefinition.condition() {
+  // === PortDefinition ===
+  syn String PortDefinition.condition() {
     // TODO [OLD] probably, this has to be structured in a better way
     if (lastDefinition().getMappingDefinition().getToType().isArray()) {
       return "java.util.Arrays.equals(" + preemptiveExpectedValue() + ", " + lastResult() + ")";
     }
-    if (getEndpointTarget().isTokenEndpointTarget() && token().isPrimitiveType() && lastDefinition().getMappingDefinition().getToType().isPrimitiveType()) {
+    if (getPortTarget().isTokenPortTarget() && token().isPrimitiveType() && lastDefinition().getMappingDefinition().getToType().isPrimitiveType()) {
       return preemptiveExpectedValue() + " == " + lastResult();
     }
-    if (!getSend() && getEndpointTarget().isTypeEndpointTarget() && getWithAdd()) {
+    if (!getSend() && getPortTarget().isTypePortTarget() && getWithAdd()) {
       // only check if received list is not null
       return lastResult() + " == null";
     }
@@ -176,7 +176,7 @@ aspect MustacheMappingApplicationAndDefinition {
       // for wildcard (list) receive connect
       toPrepend = "index >= 0 && ";
     }
-    if (getEndpointTarget().isTypeEndpointTarget() && type().isOptComponent()) {
+    if (getPortTarget().isTypePortTarget() && type().isOptComponent()) {
       // use "hasX()" instead of "getX() != null" for optionals
       return toPrepend + "has" + typeName() + "()" + " && " + preemptiveExpectedValue() + ".equals(" + lastResult() + ")";
     }
@@ -186,22 +186,22 @@ aspect MustacheMappingApplicationAndDefinition {
     return toPrepend + preemptiveExpectedValue() + " != null ? " + preemptiveExpectedValue() + ".equals(" + lastResult() + ") : " + lastResult() + " == null";
   }
 
-  syn JastAddList<MInnerMappingDefinition> EndpointDefinition.innerMappingDefinitions() = toMustache().getInnerMappingDefinitionList();
+  syn JastAddList<MInnerMappingDefinition> PortDefinition.innerMappingDefinitions() = toMustache().getInnerMappingDefinitionList();
 
-  syn String EndpointDefinition.lastDefinitionToType() = lastDefinition().toType();
+  syn String PortDefinition.lastDefinitionToType() = lastDefinition().toType();
 
-  syn String EndpointDefinition.preemptiveReturn() = toMustache().preemptiveReturn();
+  syn String PortDefinition.preemptiveReturn() = toMustache().preemptiveReturn();
 
   // === (MInner)MappingDefinition ===
   syn String MappingDefinition.fromType() = getFromType().prettyPrint();
 
   inh boolean MInnerMappingDefinition.isLast();
-  eq MEndpointDefinition.getInnerMappingDefinition(int i).isLast() {
+  eq MPortDefinition.getInnerMappingDefinition(int i).isLast() {
     return i == getNumInnerMappingDefinition() - 1;
   }
 
   inh String MInnerMappingDefinition.inputVarName();
-  eq MEndpointDefinition.getInnerMappingDefinition(int i).inputVarName() {
+  eq MPortDefinition.getInnerMappingDefinition(int i).inputVarName() {
     return i == 0
         ? firstInputVarName()
         : getInnerMappingDefinition(i - 1).outputVarName();
@@ -216,12 +216,12 @@ aspect MustacheMappingApplicationAndDefinition {
   syn String MappingDefinition.toType() = getToType().prettyPrint();
 
   // === attributes needed for computing above ones ===
-  syn String EndpointDefinition.preemptiveExpectedValue() = toMustache().preemptiveExpectedValue();
-  syn String EndpointDefinition.firstInputVarName() = toMustache().firstInputVarName();
+  syn String PortDefinition.preemptiveExpectedValue() = toMustache().preemptiveExpectedValue();
+  syn String PortDefinition.firstInputVarName() = toMustache().firstInputVarName();
 
-  syn String MEndpointDefinition.preemptiveExpectedValue();
-  syn String MEndpointDefinition.preemptiveReturn();
-  syn String MEndpointDefinition.firstInputVarName();
+  syn String MPortDefinition.preemptiveExpectedValue();
+  syn String MPortDefinition.preemptiveReturn();
+  syn String MPortDefinition.firstInputVarName();
 
   eq MAttributeSendDefinition.firstInputVarName() = getterMethodCall();
   eq MAttributeSendDefinition.preemptiveExpectedValue() = lastValueGetterCall();
@@ -255,9 +255,9 @@ aspect MustacheMappingApplicationAndDefinition {
   eq MContextFreeTypeSendDefinition.preemptiveExpectedValue() = null;
   eq MContextFreeTypeSendDefinition.preemptiveReturn() = null;
 
-  syn String MEndpointDefinition.parentTypeName() = getEndpointDefinition().parentTypeName();
+  syn String MPortDefinition.parentTypeName() = getPortDefinition().parentTypeName();
 
-  syn String MEndpointDefinition.getterMethodCall() = getEndpointDefinition().getterMethodCall();
+  syn String MPortDefinition.getterMethodCall() = getPortDefinition().getterMethodCall();
 }
 
 aspect MustacheRagConnect {
@@ -270,17 +270,17 @@ aspect MustacheRagConnect {
     return result;
   }
 
-  syn List<EndpointDefinition> RagConnect.allEndpointDefinitionList() {
-    List<EndpointDefinition> result = new ArrayList<>();
-    // first gather all user-defined endpoint definitions, that are not context-free
-    for (EndpointDefinition def : givenEndpointDefinitionList()) {
-      if (!def.hasContextFreeTypeEndpointTarget()) {
+  syn List<PortDefinition> RagConnect.allPortDefinitionList() {
+    List<PortDefinition> result = new ArrayList<>();
+    // first gather all user-defined port definitions, that are not context-free
+    for (PortDefinition def : givenPortDefinitionList()) {
+      if (!def.hasContextFreeTypePortTarget()) {
         result.add(def);
       }
     }
-    // then check for additional endpoints, and add if no conflict with existing definitions exists
-    for (EndpointDefinition def : givenEndpointDefinitionList()) {
-      def.getEndpointTarget().impliedEndpointDefinitions().iterator().forEachRemaining(result::add);
+    // then check for additional ports, and add if no conflict with existing definitions exists
+    for (PortDefinition def : givenPortDefinitionList()) {
+      def.getPortTarget().impliedPortDefinitions().iterator().forEachRemaining(result::add);
     }
 
     return result;
@@ -321,11 +321,11 @@ aspect MustacheRagConnect {
 
   syn String RagConnect.touchedTerminalsMethodName() = internalRagConnectPrefix() + "touchedTerminals";
 
-  syn List<TypeDecl> RagConnect.typeDeclsOfContextFreeEndpointTargets() {
+  syn List<TypeDecl> RagConnect.typeDeclsOfContextFreePortTargets() {
     List<TypeDecl> result = new ArrayList<>();
-    for (EndpointTarget target : givenEndpointTargetList()) {
-      if (target.isContextFreeTypeEndpointTarget()) {
-        result.add(target.asContextFreeTypeEndpointTarget().getTypeDecl());
+    for (PortTarget target : givenPortTargetList()) {
+      if (target.isContextFreeTypePortTarget()) {
+        result.add(target.asContextFreeTypePortTarget().getTypeDecl());
       }
     }
     return result;
@@ -333,42 +333,46 @@ aspect MustacheRagConnect {
 
   // === MappingDefinition ===
   syn boolean MappingDefinition.isUsed() = !effectiveUsedAt().isEmpty();
+  eq SerializeListMapping.isUsed() = ragconnect().defaultListToBytesMapping().isUsed();
+  eq SerializeJavaUtilListMapping.isUsed() = ragconnect().defaultJavaUtilListToBytesMapping().isUsed();
+  eq DeserializeListMapping.isUsed() = program().typeDecls().stream().anyMatch(
+          typeDecl -> ragconnect().defaultBytesToListMapping(typeDecl.getName()).isUsed());
 
   // === attributes needed for computing above ones ===
-  syn List<EndpointDefinition> RagConnect.givenEndpointDefinitionList() {
-    List<EndpointDefinition> result = new ArrayList<>();
+  syn List<PortDefinition> RagConnect.givenPortDefinitionList() {
+    List<PortDefinition> result = new ArrayList<>();
     for (ConnectSpecification spec : getConnectSpecificationFileList()) {
-      spec.getEndpointDefinitionList().forEach(result::add);
+      spec.getPortDefinitionList().forEach(result::add);
     }
     return result;
   }
 
-  syn nta JastAddList<EndpointDefinition> EndpointTarget.impliedEndpointDefinitions() = new JastAddList<>();
-//  eq TypeEndpointTarget.impliedEndpointDefinitions() {
-//    JastAddList<EndpointDefinition> result = super.impliedEndpointDefinitions();
+  syn nta JastAddList<PortDefinition> PortTarget.impliedPortDefinitions() = new JastAddList<>();
+//  eq TypePortTarget.impliedPortDefinitions() {
+//    JastAddList<PortDefinition> result = super.impliedPortDefinitions();
 //    if (!getSend() || !typeIsList() || !getIndexBasedListAccess()) {
 //      return result;
 //    }
-//    // create a new endpoint
+//    // create a new port
 //  }
-  eq ContextFreeTypeEndpointTarget.impliedEndpointDefinitions() {
-    JastAddList<EndpointDefinition> result = super.impliedEndpointDefinitions();
-    EndpointDefinition containingDef = containingEndpointDefinition();
+  eq ContextFreeTypePortTarget.impliedPortDefinitions() {
+    JastAddList<PortDefinition> result = super.impliedPortDefinitions();
+    PortDefinition containingDef = containingPortDefinition();
     for (TypeComponent typeComponent : getTypeDecl().occurencesInProductionRules()) {
-      List<EndpointDefinition> defsForTypeComponent = lookupGivenTypeEndpointDefinitions(typeComponent);
+      List<PortDefinition> defsForTypeComponent = lookupGivenTypePortDefinitions(typeComponent);
       if (!defsForTypeComponent.stream().anyMatch(containingDef::matchesType)) {
-        // there is no user-defined endpoint definition for this typeComponent yet
-        // -> create a new endpoint definition with the same options and mappings as the context-free def
+        // there is no user-defined port definition for this typeComponent yet
+        // -> create a new port definition with the same options and mappings as the context-free def
         //    (except indexed-based for list-types)
-        EndpointDefinition newDef = new EndpointDefinition();
+        PortDefinition newDef = new PortDefinition();
         newDef.setAlwaysApply(containingDef.getAlwaysApply());
         newDef.setIndexBasedListAccess(typeComponent.isListComponent());
         newDef.setSend(containingDef.getSend());
         containingDef.getMappings().forEach(newDef::addMapping);
 
-        TypeEndpointTarget target = new TypeEndpointTarget();
+        TypePortTarget target = new TypePortTarget();
         target.setType(typeComponent);
-        newDef.setEndpointTarget(target);
+        newDef.setPortTarget(target);
 
         result.add(newDef);
       }
@@ -388,24 +392,24 @@ aspect MustacheRagConnect {
 }
 
 aspect MustacheReceiveAndSendAndHandleUri {
-  // === EndpointDefinition ===
-  syn String EndpointDefinition.connectMethodName() = "connect" + capitalize(entityName());
+  // === PortDefinition ===
+  syn String PortDefinition.connectMethodName() = "connect" + capitalize(entityName());
 
-  syn String EndpointDefinition.connectParameterName() = "uriString";
+  syn String PortDefinition.connectParameterName() = "uriString";
 
-  syn String EndpointDefinition.disconnectMethodName() {
-    // if both (send and receive) are defined for an endpoint, ensure methods with different names
+  syn String PortDefinition.disconnectMethodName() {
+    // if both (send and receive) are defined for an port, ensure methods with different names
     String extra;
-    if (getEndpointTarget().isTokenEndpointTarget()) {
-      extra = lookupTokenEndpointDefinitions(token()).size() > 1 ? uniqueSuffix() : "";
-    } else if (getEndpointTarget().isTypeEndpointTarget()) {
-      // here it has to be checked if there are ANY typeEndpointDefinitions within all endpoints (including implied)
-      extra = lookupAllTypeEndpointDefinitions(type()).size() > 1 ? uniqueSuffix() : "";
-    } else if (getEndpointTarget().isContextFreeTypeEndpointTarget()) {
-      // here it has to be checked if there are ANY typeEndpointDefinitions within all endpoints (including implied)
+    if (getPortTarget().isTokenPortTarget()) {
+      extra = lookupTokenPortDefinitions(token()).size() > 1 ? uniqueSuffix() : "";
+    } else if (getPortTarget().isTypePortTarget()) {
+      // here it has to be checked if there are ANY typePortDefinitions within all ports (including implied)
+      extra = lookupAllTypePortDefinitions(type()).size() > 1 ? uniqueSuffix() : "";
+    } else if (getPortTarget().isContextFreeTypePortTarget()) {
+      // here it has to be checked if there are ANY typePortDefinitions within all ports (including implied)
       boolean needExtra = false;
-      for (TypeComponent typeComponent : getEndpointTarget().asContextFreeTypeEndpointTarget().getTypeDecl().occurencesInProductionRules()) {
-        if (lookupAllTypeEndpointDefinitions(typeComponent).size() > 1) {
+      for (TypeComponent typeComponent : getPortTarget().asContextFreeTypePortTarget().getTypeDecl().occurencesInProductionRules()) {
+        if (lookupAllTypePortDefinitions(typeComponent).size() > 1) {
           needExtra = true;
           break;
         }
@@ -417,106 +421,106 @@ aspect MustacheReceiveAndSendAndHandleUri {
     return "disconnect" + extra + capitalize(entityName());
   }
 
-  syn String EndpointDefinition.entityName() = getEndpointTarget().entityName();
+  syn String PortDefinition.entityName() = getPortTarget().entityName();
 
-  syn boolean EndpointDefinition.indexedSend() = getSend() && indexedList();
-  syn boolean EndpointDefinition.indexedList() = typeIsList() && getIndexBasedListAccess();
+  syn boolean PortDefinition.indexedSend() = getSend() && indexedList();
+  syn boolean PortDefinition.indexedList() = typeIsList() && getIndexBasedListAccess();
 
-  syn String EndpointDefinition.getterMethodName() = getEndpointTarget().getterMethodName();
-  syn String EndpointDefinition.getterMethodCall() = getEndpointTarget().getterMethodCall();
-  syn String EndpointDefinition.realGetterMethodName() = getEndpointTarget().realGetterMethodName();
-  syn String EndpointDefinition.realGetterMethodCall() = getEndpointTarget().realGetterMethodCall();
+  syn String PortDefinition.getterMethodName() = getPortTarget().getterMethodName();
+  syn String PortDefinition.getterMethodCall() = getPortTarget().getterMethodCall();
+  syn String PortDefinition.realGetterMethodName() = getPortTarget().realGetterMethodName();
+  syn String PortDefinition.realGetterMethodCall() = getPortTarget().realGetterMethodCall();
 
-  syn String EndpointDefinition.parentTypeName() = getEndpointTarget().parentTypeName();
+  syn String PortDefinition.parentTypeName() = getPortTarget().parentTypeName();
 
   // === attributes needed for computing above ones ===
-  syn String EndpointTarget.getterMethodName();
-  syn String EndpointTarget.getterMethodCall() = getterMethodName() + "()";
-  syn String EndpointTarget.realGetterMethodName() = getterMethodName() + "NoTransform";
-  syn String EndpointTarget.realGetterMethodCall() = realGetterMethodName() + "()";
-  syn String EndpointTarget.parentTypeName();
-  syn String EndpointTarget.entityName();
-
-  eq AttributeEndpointTarget.getterMethodName() = getName();
-  eq AttributeEndpointTarget.parentTypeName() = getParentTypeDecl().getName();
-  eq AttributeEndpointTarget.entityName() = getName();
-
-  eq RelationEndpointTarget.getterMethodName() = forwardingNTA_Name();
-  eq RelationEndpointTarget.parentTypeName() = getRole().getType().getName();
-  eq RelationEndpointTarget.entityName() = getRole().getName();
-  eq RelationEndpointTarget.realGetterMethodName() = "get" + getRole().getterMethodName();
-  eq RelationEndpointTarget.realGetterMethodCall() = realGetterMethodName() + (containingEndpointDefinition().indexedSend() ? "(index)" : "()");
+  syn String PortTarget.getterMethodName();
+  syn String PortTarget.getterMethodCall() = getterMethodName() + "()";
+  syn String PortTarget.realGetterMethodName() = getterMethodName() + "NoTransform";
+  syn String PortTarget.realGetterMethodCall() = realGetterMethodName() + "()";
+  syn String PortTarget.parentTypeName();
+  syn String PortTarget.entityName();
+
+  eq AttributePortTarget.getterMethodName() = getName();
+  eq AttributePortTarget.parentTypeName() = getParentTypeDecl().getName();
+  eq AttributePortTarget.entityName() = getName();
+
+  eq RelationPortTarget.getterMethodName() = forwardingName();
+  eq RelationPortTarget.parentTypeName() = getRole().getType().getName();
+  eq RelationPortTarget.entityName() = getRole().getName();
+  eq RelationPortTarget.realGetterMethodName() = "get" + getRole().getterMethodName();
+  eq RelationPortTarget.realGetterMethodCall() = realGetterMethodName() + (containingPortDefinition().indexedSend() ? "(index)" : "()");
 
   syn String NavigableRole.getterMethodName() = getName();
   eq ListRole.getterMethodName() = getName() + "List";
 
-  eq TokenEndpointTarget.getterMethodName() = "get" + getToken().getName();
-  eq TokenEndpointTarget.parentTypeName() = getToken().containingTypeDecl().getName();
-  eq TokenEndpointTarget.entityName() = getToken().getName();
+  eq TokenPortTarget.getterMethodName() = "get" + getToken().getName();
+  eq TokenPortTarget.parentTypeName() = getToken().containingTypeDecl().getName();
+  eq TokenPortTarget.entityName() = getToken().getName();
 
-  eq TypeEndpointTarget.getterMethodName() = getterMethodeNameHelper(true);
-  eq TypeEndpointTarget.getterMethodCall() = getterMethodName() + (containingEndpointDefinition().indexedList() && !containingEndpointDefinition().getWithAdd() ? "(index)" : "()") + (typeIsOpt() ? ".getChild(0)" : "");
-  eq TypeEndpointTarget.realGetterMethodName() = getterMethodeNameHelper(false);
-  eq TypeEndpointTarget.realGetterMethodCall() = realGetterMethodName() + (containingEndpointDefinition().indexedSend() ? "(index)" : "()");
-  eq TypeEndpointTarget.parentTypeName() = getType().containingTypeDecl().getName();
-  eq TypeEndpointTarget.entityName() = getType().getName() + (typeIsList() && !containingEndpointDefinition().getIndexBasedListAccess() ? "List" : "");
+  eq TypePortTarget.getterMethodName() = getterMethodeNameHelper(true);
+  eq TypePortTarget.getterMethodCall() = getterMethodName() + (containingPortDefinition().indexedList() && !containingPortDefinition().getWithAdd() ? "(index)" : "()") + (typeIsOpt() ? ".getChild(0)" : "");
+  eq TypePortTarget.realGetterMethodName() = getterMethodeNameHelper(false);
+  eq TypePortTarget.realGetterMethodCall() = realGetterMethodName() + (containingPortDefinition().indexedSend() ? "(index)" : "()");
+  eq TypePortTarget.parentTypeName() = getType().containingTypeDecl().getName();
+  eq TypePortTarget.entityName() = getType().getName() + (typeIsList() && !containingPortDefinition().getIndexBasedListAccess() ? "List" : "");
 
-  private String TypeEndpointTarget.getterMethodeNameHelper(boolean useForwarding) {
-    return (useForwarding && needForwardingNTA() ? forwardingNTA_Name() :
-            "get" + getType().getName() + (typeIsList() && (!containingEndpointDefinition().getIndexBasedListAccess() ||
-                    containingEndpointDefinition().getWithAdd()) ? "List" : "") + (typeIsOpt() ? "Opt" : "")
-                    + (needForwardingNTA() ? "NoTransform" : ""));
+  private String TypePortTarget.getterMethodeNameHelper(boolean useForwarding) {
+    return (useForwarding && needForwarding() ? forwardingName() :
+            "get" + getType().getName() + (typeIsList() && (!containingPortDefinition().getIndexBasedListAccess() ||
+                    containingPortDefinition().getWithAdd()) ? "List" : "") + (typeIsOpt() ? "Opt" : "")
+                    + (needForwarding() ? "NoTransform" : ""));
   }
 
-  eq ContextFreeTypeEndpointTarget.getterMethodName() = null;
-  eq ContextFreeTypeEndpointTarget.parentTypeName() = getTypeDecl().getName();
-  eq ContextFreeTypeEndpointTarget.entityName() = "";
+  eq ContextFreeTypePortTarget.getterMethodName() = null;
+  eq ContextFreeTypePortTarget.parentTypeName() = getTypeDecl().getName();
+  eq ContextFreeTypePortTarget.entityName() = "";
 }
 
 aspect MustacheReceiveDefinition {
   // === RagConnect ===
   syn boolean RagConnect.configLoggingEnabledForReads() = getConfiguration().getLoggingEnabledForReads();
 
-  // === EndpointDefinition ===
-  syn boolean EndpointDefinition.hasContextFreeTypeEndpointTarget() = getEndpointTarget().isContextFreeTypeEndpointTarget();
+  // === PortDefinition ===
+  syn boolean PortDefinition.hasContextFreeTypePortTarget() = getPortTarget().isContextFreeTypePortTarget();
 
-  syn boolean EndpointDefinition.hasTypeEndpointTarget() = getEndpointTarget().isTypeEndpointTarget();
+  syn boolean PortDefinition.hasTypePortTarget() = getPortTarget().isTypePortTarget();
 
-  syn String EndpointDefinition.idTokenName() = ragconnect().internalRagConnectPrefix() + "TopicInList";
+  syn String PortDefinition.idTokenName() = ragconnect().internalRagConnectPrefix() + "TopicInList";
 
-  syn String EndpointDefinition.internalConnectMethodName() = ragconnect().internalRagConnectPrefix() + "_internal_" + connectMethodName();
+  syn String PortDefinition.internalConnectMethodName() = ragconnect().internalRagConnectPrefix() + "_internal_" + connectMethodName();
 
-  syn String EndpointDefinition.resolveInListMethodName() = ragconnect().internalRagConnectPrefix() + "_resolve" + entityName() + "InList";
+  syn String PortDefinition.resolveInListMethodName() = ragconnect().internalRagConnectPrefix() + "_resolve" + entityName() + "InList";
 
   // === attributes needed for computing above ones ===
-  syn String EndpointDefinition.uniqueSuffix() = getSend() ? "Send" : "Receive";
+  syn String PortDefinition.uniqueSuffix() = getSend() ? "Send" : "Receive";
 }
 
 aspect MustacheSendDefinition {
   // === RagConnect ===
   syn boolean RagConnect.configLoggingEnabledForWrites() = getConfiguration().getLoggingEnabledForWrites();
 
-  // === EndpointDefinition ===
-  syn String EndpointDefinition.lastValueGetterCall() = senderName() + ".getLastValue(" +
+  // === PortDefinition ===
+  syn String PortDefinition.lastValueGetterCall() = senderName() + ".getLastValue(" +
     (getIndexBasedListAccess() ? "index" : "") + ")";
 
-  syn String EndpointDefinition.lastValueSetter() = senderName() + ".setLastValue";
+  syn String PortDefinition.lastValueSetter() = senderName() + ".setLastValue";
 
-  syn boolean EndpointDefinition.needForwardingNTA() = getEndpointTarget().needForwardingNTA();
-  syn String EndpointDefinition.forwardingNTA_Name() = getEndpointTarget().forwardingNTA_Name();
-  syn String EndpointDefinition.forwardingNTA_Type() = getEndpointTarget().forwardingNTA_Type();
+  syn boolean PortDefinition.needForwarding() = getPortTarget().needForwarding();
+  syn String PortDefinition.forwardingName() = getPortTarget().forwardingName();
+  syn String PortDefinition.forwardingType() = getPortTarget().forwardingType();
 
-  syn boolean EndpointDefinition.targetIsAttribute() = getEndpointTarget().isAttributeEndpointTarget();
+  syn boolean PortDefinition.targetIsAttribute() = getPortTarget().isAttributePortTarget();
 
-  syn boolean EndpointDefinition.indexBasedAccessAndTargetIsNTA() {
-    return typeIsList() && getIndexBasedListAccess() && !needForwardingNTA();
+  syn boolean PortDefinition.indexBasedAccessAndTargetIsNTA() {
+    return typeIsList() && getIndexBasedListAccess() && !needForwarding();
   }
 
-  syn boolean EndpointDefinition.relationEndpointWithListRole() = getEndpointTarget().relationEndpointWithListRole();
+  syn boolean PortDefinition.relationPortWithListRole() = getPortTarget().relationPortWithListRole();
 
-  syn String EndpointDefinition.senderName() = getEndpointTarget().senderName();
+  syn String PortDefinition.senderName() = getPortTarget().senderName();
 
-  syn java.util.List<SendIncrementalObserverEntry> EndpointDefinition.sendIncrementalObserverEntries() {
+  syn java.util.List<SendIncrementalObserverEntry> PortDefinition.sendIncrementalObserverEntries() {
     // todo maybe getterMethodName needs to be change for indexed send
     java.util.List<SendIncrementalObserverEntry> result = new java.util.ArrayList<>();
     // "{{getterMethodName}}{{#IndexBasedListAccess}}_int{{/IndexBasedListAccess}}"
@@ -533,59 +537,59 @@ aspect MustacheSendDefinition {
     return result;
   }
 
-  syn boolean EndpointDefinition.shouldNotResetValue() = getSend() && !getEndpointTarget().hasAttributeResetMethod();
+  syn boolean PortDefinition.shouldNotResetValue() = getSend() && !getPortTarget().hasAttributeResetMethod();
 
-  syn String EndpointDefinition.tokenResetMethodName() = getEndpointTarget().tokenResetMethodName();
+  syn String PortDefinition.tokenResetMethodName() = getPortTarget().tokenResetMethodName();
 
-  syn String EndpointDefinition.updateMethodName() = toMustache().updateMethodName();
+  syn String PortDefinition.updateMethodName() = toMustache().updateMethodName();
 
-  syn String EndpointDefinition.writeMethodName() = toMustache().writeMethodName();
+  syn String PortDefinition.writeMethodName() = toMustache().writeMethodName();
 
   // === attributes needed for computing above ones ===
-  syn boolean EndpointTarget.needForwardingNTA() = false;
-  eq TypeEndpointTarget.needForwardingNTA() = containingEndpointDefinition().getSend() && !getType().getNTA();
-  eq RelationEndpointTarget.needForwardingNTA() = containingEndpointDefinition().getSend();
-
-  syn String EndpointTarget.forwardingNTA_Name() = null;  // only needed, if needForwardingNTA evaluates to true
-  eq TypeEndpointTarget.forwardingNTA_Name() = ragconnect().internalRagConnectPrefix() + getType().getName();
-  eq RelationEndpointTarget.forwardingNTA_Name() = ragconnect().internalRagConnectPrefix() + getRole().getName();
-
-  syn String EndpointTarget.forwardingNTA_Type() = null;  // only needed, if needForwardingNTA evaluates to true
-  eq TypeEndpointTarget.forwardingNTA_Type() = getType().forwardingNTA_Type(
-          containingEndpointDefinition().getIndexBasedListAccess());
-  eq RelationEndpointTarget.forwardingNTA_Type() = getRole().forwardingNTA_Type(
-containingEndpointDefinition().getIndexBasedListAccess());
-
-  syn String TypeComponent.forwardingNTA_Type(boolean indexBasedListAccess);
-  eq NormalComponent.forwardingNTA_Type(boolean indexBasedListAccess) = getTypeDecl().getName();
-  eq OptComponent.forwardingNTA_Type(boolean indexBasedListAccess) =
+  syn boolean PortTarget.needForwarding() = false;
+  eq TypePortTarget.needForwarding() = containingPortDefinition().getSend() && !getType().getNTA();
+  eq RelationPortTarget.needForwarding() = containingPortDefinition().getSend();
+
+  syn String PortTarget.forwardingName() = null;  // only needed, if needForwarding evaluates to true
+  eq TypePortTarget.forwardingName() = ragconnect().internalRagConnectPrefix() + getType().getName();
+  eq RelationPortTarget.forwardingName() = ragconnect().internalRagConnectPrefix() + getRole().getName();
+
+  syn String PortTarget.forwardingType() = null;  // only needed, if needForwarding evaluates to true
+  eq TypePortTarget.forwardingType() = getType().forwardingType(
+          containingPortDefinition().getIndexBasedListAccess());
+  eq RelationPortTarget.forwardingType() = getRole().forwardingType(
+containingPortDefinition().getIndexBasedListAccess());
+
+  syn String TypeComponent.forwardingType(boolean indexBasedListAccess);
+  eq NormalComponent.forwardingType(boolean indexBasedListAccess) = getTypeDecl().getName();
+  eq OptComponent.forwardingType(boolean indexBasedListAccess) =
           ragconnect().configJastAddOpt() + "<" + getTypeDecl().getName() + ">";
-  eq ListComponent.forwardingNTA_Type(boolean indexBasedListAccess) = indexBasedListAccess ?
+  eq ListComponent.forwardingType(boolean indexBasedListAccess) = indexBasedListAccess ?
           getTypeDecl().getName() :
           ragconnect().configJastAddList() + "<" + getTypeDecl().getName() + ">";
 
-  syn String Role.forwardingNTA_Type(boolean indexBasedListAccess) = oppositeRole().getType().getName();
-  eq ListRole.forwardingNTA_Type(boolean indexBasedListAccess) = indexBasedListAccess ?
+  syn String Role.forwardingType(boolean indexBasedListAccess) = oppositeRole().getType().getName();
+  eq ListRole.forwardingType(boolean indexBasedListAccess) = indexBasedListAccess ?
           oppositeRole().getType().getName() :
           "java.util.List<" + oppositeRole().getType().getName() + ">";
 
-  syn boolean EndpointTarget.relationEndpointWithListRole() = false;
-  eq RelationEndpointTarget.relationEndpointWithListRole() = getRole().isListRole();
+  syn boolean PortTarget.relationPortWithListRole() = false;
+  eq RelationPortTarget.relationPortWithListRole() = getRole().isListRole();
 
-  syn String EndpointTarget.senderName() = ragconnect().internalRagConnectPrefix() + "_sender_" + entityName();
-  eq ContextFreeTypeEndpointTarget.senderName() = null;
+  syn String PortTarget.senderName() = ragconnect().internalRagConnectPrefix() + "_sender_" + entityName();
+  eq ContextFreeTypePortTarget.senderName() = null;
 
-  syn String EndpointTarget.tokenResetMethodName() = getterMethodName() + (
-          typeIsList() && containingEndpointDefinition().getIndexBasedListAccess() ? "List" : "") + "_reset";
+  syn String PortTarget.tokenResetMethodName() = getterMethodName() + (
+          typeIsList() && containingPortDefinition().getIndexBasedListAccess() ? "List" : "") + "_reset";
 
-  syn String MEndpointDefinition.updateMethodName();
-  syn String MEndpointDefinition.writeMethodName();
+  syn String MPortDefinition.updateMethodName();
+  syn String MPortDefinition.writeMethodName();
 
-  eq MAttributeSendDefinition.updateMethodName() = ragconnect().internalRagConnectPrefix() + "_update_attr_" + getEndpointDefinition().entityName();
-  eq MAttributeSendDefinition.writeMethodName() = ragconnect().internalRagConnectPrefix() + "_writeLastValue_attr_" + getEndpointDefinition().entityName();
+  eq MAttributeSendDefinition.updateMethodName() = ragconnect().internalRagConnectPrefix() + "_update_attr_" + getPortDefinition().entityName();
+  eq MAttributeSendDefinition.writeMethodName() = ragconnect().internalRagConnectPrefix() + "_writeLastValue_attr_" + getPortDefinition().entityName();
 
-  eq MRelationSendDefinition.updateMethodName() = ragconnect().internalRagConnectPrefix() + "_update_" + getEndpointDefinition().entityName();
-  eq MRelationSendDefinition.writeMethodName() = ragconnect().internalRagConnectPrefix() + "_writeLastValue_" + getEndpointDefinition().entityName();
+  eq MRelationSendDefinition.updateMethodName() = ragconnect().internalRagConnectPrefix() + "_update_" + getPortDefinition().entityName();
+  eq MRelationSendDefinition.writeMethodName() = ragconnect().internalRagConnectPrefix() + "_writeLastValue_" + getPortDefinition().entityName();
 
   eq MTokenReceiveDefinition.updateMethodName() = null;
   eq MTokenReceiveDefinition.writeMethodName() = null;
@@ -605,11 +609,11 @@ containingEndpointDefinition().getIndexBasedListAccess());
   eq MContextFreeTypeSendDefinition.updateMethodName() = null;
   eq MContextFreeTypeSendDefinition.writeMethodName() = null;
 
-  syn String EndpointDefinition.tokenName() = token().getName();
-  syn String MEndpointDefinition.tokenName() = getEndpointDefinition().tokenName();
+  syn String PortDefinition.tokenName() = token().getName();
+  syn String MPortDefinition.tokenName() = getPortDefinition().tokenName();
 
-  syn String EndpointDefinition.typeName() = type().getName();
-  syn String MEndpointDefinition.typeName() = getEndpointDefinition().typeName();
+  syn String PortDefinition.typeName() = type().getName();
+  syn String MPortDefinition.typeName() = getPortDefinition().typeName();
 
   static SendIncrementalObserverEntry SendIncrementalObserverEntry.of(String attributeString, boolean compareParams, Object params) {
     return new SendIncrementalObserverEntry()
@@ -625,10 +629,10 @@ aspect MustacheTokenComponent {
 
   syn String TokenComponent.javaType() = effectiveJavaTypeUse().prettyPrint();
 
-  syn EndpointDefinition TokenComponent.normalTokenSendDef() {
-    for (EndpointTarget target : getTokenEndpointTargetList()) {
-      if (target.isTokenEndpointTarget() && target.containingEndpointDefinition().shouldNotResetValue()) {
-        return target.containingEndpointDefinition();
+  syn PortDefinition TokenComponent.normalTokenSendDef() {
+    for (PortTarget target : getTokenPortTargetList()) {
+      if (target.isTokenPortTarget() && target.containingPortDefinition().shouldNotResetValue()) {
+        return target.containingPortDefinition();
       }
     }
     return null;
@@ -639,20 +643,20 @@ aspect MustacheTokenComponent {
   // > see MustacheDependencyDefinition for internalRelationPrefix, targetParentTypeName
 
   // === DependencyDefinition ===
-  syn EndpointDefinition DependencyDefinition.targetEndpointDefinition() {
+  syn PortDefinition DependencyDefinition.targetPortDefinition() {
     // resolve definition in here, as we do not need resolveMethod in any other place (yet)
-    for (EndpointDefinition endpointDefinition : ragconnect().allEndpointDefinitionList()) {
-      if (!endpointDefinition.getSend()) {
+    for (PortDefinition portDefinition : ragconnect().allPortDefinitionList()) {
+      if (!portDefinition.getSend()) {
         continue;
       }
-      EndpointTarget endpointTarget = endpointDefinition.getEndpointTarget();
-      if (endpointTarget.isTokenEndpointTarget() &&
-          endpointTarget.asTokenEndpointTarget().getToken().equals(this.getTarget())) {
-        return endpointDefinition;
+      PortTarget portTarget = portDefinition.getPortTarget();
+      if (portTarget.isTokenPortTarget() &&
+          portTarget.asTokenPortTarget().getToken().equals(this.getTarget())) {
+        return portDefinition;
       }
-      if (endpointTarget.isTypeEndpointTarget() &&
-          endpointTarget.asTypeEndpointTarget().getType().equals(this.getTarget())) {
-        return endpointDefinition;
+      if (portTarget.isTypePortTarget() &&
+          portTarget.asTypePortTarget().getType().equals(this.getTarget())) {
+        return portDefinition;
       }
     }
     return null;
@@ -665,11 +669,11 @@ aspect MustacheTypeDecl {
   // === TypeComponent ===
   syn String TypeComponent.parentTypeName() = containingTypeDecl().getName();
   syn String TypeComponent.disconnectMethodName() {
-    List<TypeEndpointTarget> typeEndpointTargets = getTypeEndpointTargets();
-    if (typeEndpointTargets.isEmpty()) {
-      return "MISSING_ENDPOINT";
+    List<TypePortTarget> typePortTargets = getTypePortTargets();
+    if (typePortTargets.isEmpty()) {
+      return "MISSING_PORT";
     } else {
-      return typeEndpointTargets.get(0).containingEndpointDefinition().disconnectMethodName();
+      return typePortTargets.get(0).containingPortDefinition().disconnectMethodName();
     }
   }
 
@@ -689,19 +693,19 @@ aspect MustacheTypeDecl {
 }
 
 aspect AttributesForMustache {
-  syn String MEndpointDefinition.lastValueGetterCall() = getEndpointDefinition().lastValueGetterCall();
-  syn String MEndpointDefinition.lastValueSetter() = getEndpointDefinition().lastValueSetter();
+  syn String MPortDefinition.lastValueGetterCall() = getPortDefinition().lastValueGetterCall();
+  syn String MPortDefinition.lastValueSetter() = getPortDefinition().lastValueSetter();
 
-  // token and type are potentially dangerous because asXEndpointTarget can return null
-  syn TokenComponent EndpointDefinition.token() = getEndpointTarget().asTokenEndpointTarget().getToken();
-  syn TypeComponent EndpointDefinition.type() = getEndpointTarget().asTypeEndpointTarget().getType();
+  // token and type are potentially dangerous because asXPortTarget can return null
+  syn TokenComponent PortDefinition.token() = getPortTarget().asTokenPortTarget().getToken();
+  syn TypeComponent PortDefinition.type() = getPortTarget().asTypePortTarget().getType();
 
-  syn MInnerMappingDefinition EndpointDefinition.lastDefinition() = toMustache().lastDefinition();
-  syn MInnerMappingDefinition MEndpointDefinition.lastDefinition() = getInnerMappingDefinition(getNumInnerMappingDefinition() - 1);
+  syn MInnerMappingDefinition PortDefinition.lastDefinition() = toMustache().lastDefinition();
+  syn MInnerMappingDefinition MPortDefinition.lastDefinition() = getInnerMappingDefinition(getNumInnerMappingDefinition() - 1);
 
-  syn nta MEndpointDefinition EndpointDefinition.toMustache() {
-    final MEndpointDefinition result = getEndpointTarget().createMEndpointDefinition(getSend());
-    result.setEndpointDefinition(this);
+  syn nta MPortDefinition PortDefinition.toMustache() {
+    final MPortDefinition result = getPortTarget().createMPortDefinition(getSend());
+    result.setPortDefinition(this);
     for (MappingDefinition def : effectiveMappings()) {
       MInnerMappingDefinition inner = new MInnerMappingDefinition();
       inner.setMappingDefinition(def);
@@ -709,30 +713,30 @@ aspect AttributesForMustache {
     }
     return result;
   }
-  abstract MEndpointDefinition EndpointTarget.createMEndpointDefinition(boolean isSend);
-  MEndpointDefinition AttributeEndpointTarget.createMEndpointDefinition(boolean isSend) {
+  abstract MPortDefinition PortTarget.createMPortDefinition(boolean isSend);
+  MPortDefinition AttributePortTarget.createMPortDefinition(boolean isSend) {
     if (!isSend) {
-      throw new IllegalArgumentException("AttributeEndpointTarget can only be sent!");
+      throw new IllegalArgumentException("AttributePortTarget can only be sent!");
     }
     return new MAttributeSendDefinition();
   }
-  MEndpointDefinition RelationEndpointTarget.createMEndpointDefinition(boolean isSend) {
+  MPortDefinition RelationPortTarget.createMPortDefinition(boolean isSend) {
     if (!isSend) {
-      throw new IllegalArgumentException("RelationEndpointTarget can only be sent!");
+      throw new IllegalArgumentException("RelationPortTarget can only be sent!");
     }
     return new MRelationSendDefinition();
   }
-  MEndpointDefinition TokenEndpointTarget.createMEndpointDefinition(boolean isSend) {
+  MPortDefinition TokenPortTarget.createMPortDefinition(boolean isSend) {
     return isSend ? new MTokenSendDefinition() : new MTokenReceiveDefinition();
   }
-  MEndpointDefinition TypeEndpointTarget.createMEndpointDefinition(boolean isSend) {
+  MPortDefinition TypePortTarget.createMPortDefinition(boolean isSend) {
     return isSend ? new MTypeSendDefinition() : new MTypeReceiveDefinition();
   }
-  MEndpointDefinition ContextFreeTypeEndpointTarget.createMEndpointDefinition(boolean isSend) {
+  MPortDefinition ContextFreeTypePortTarget.createMPortDefinition(boolean isSend) {
     return isSend ? new MContextFreeTypeSendDefinition() : new MContextFreeTypeReceiveDefinition();
   }
-  MEndpointDefinition UntypedEndpointTarget.createMEndpointDefinition(boolean isSend) {
-    throw new RuntimeException("Could not resolve endpoint target '" +
+  MPortDefinition UntypedPortTarget.createMPortDefinition(boolean isSend) {
+    throw new RuntimeException("Could not resolve port target '" +
         getTypeName() + "." + getChildName() + "'");
   }
 }
@@ -760,7 +764,7 @@ aspect GrammarGeneration {
 
   syn java.util.Map<TypeDecl, TokenComponent> RagConnect.additionalTokens() {
     java.util.Map<TypeDecl, TokenComponent> result = new java.util.HashMap<>();
-    for (EndpointDefinition def : allEndpointDefinitionList()) {
+    for (PortDefinition def : allPortDefinitionList()) {
       if (def.getTokenToCreate() != null) {
         result.put(def.type().getTypeDecl(), def.getTokenToCreate());
       }
@@ -768,7 +772,7 @@ aspect GrammarGeneration {
     return result;
   }
 
-  syn TokenComponent EndpointDefinition.getTokenToCreate() {
+  syn TokenComponent PortDefinition.getTokenToCreate() {
     if (typeIsList() && getIndexBasedListAccess()) {
       TokenComponent result = new TokenComponent();
       result.setName(idTokenName());
diff --git a/ragconnect.base/src/main/jastadd/Intermediate.relast b/ragconnect.base/src/main/jastadd/Intermediate.relast
index 0307d8d9a45fc40e2d6fcfa03ab06d25578687d6..3e29f479565db55e894dfbcc60a23cd7a972355b 100644
--- a/ragconnect.base/src/main/jastadd/Intermediate.relast
+++ b/ragconnect.base/src/main/jastadd/Intermediate.relast
@@ -1,17 +1,17 @@
-abstract MEndpointDefinition ::= InnerMappingDefinition:MInnerMappingDefinition*;
-rel MEndpointDefinition.EndpointDefinition -> EndpointDefinition;
+abstract MPortDefinition ::= InnerMappingDefinition:MInnerMappingDefinition*;
+rel MPortDefinition.PortDefinition -> PortDefinition;
 
-MAttributeSendDefinition : MEndpointDefinition;
-MRelationSendDefinition : MEndpointDefinition;
-abstract MTokenEndpointDefinition : MEndpointDefinition;
-MTokenReceiveDefinition : MTokenEndpointDefinition;
-MTokenSendDefinition : MTokenEndpointDefinition;
-abstract MTypeEndpointDefinition : MEndpointDefinition;
-MTypeReceiveDefinition : MTypeEndpointDefinition;
-MTypeSendDefinition : MTypeEndpointDefinition;
-abstract MContextFreeTypeEndpointDefinition : MEndpointDefinition;
-MContextFreeTypeReceiveDefinition : MContextFreeTypeEndpointDefinition;
-MContextFreeTypeSendDefinition : MContextFreeTypeEndpointDefinition;
+MAttributeSendDefinition : MPortDefinition;
+MRelationSendDefinition : MPortDefinition;
+abstract MTokenPortDefinition : MPortDefinition;
+MTokenReceiveDefinition : MTokenPortDefinition;
+MTokenSendDefinition : MTokenPortDefinition;
+abstract MTypePortDefinition : MPortDefinition;
+MTypeReceiveDefinition : MTypePortDefinition;
+MTypeSendDefinition : MTypePortDefinition;
+abstract MContextFreeTypePortDefinition : MPortDefinition;
+MContextFreeTypeReceiveDefinition : MContextFreeTypePortDefinition;
+MContextFreeTypeSendDefinition : MContextFreeTypePortDefinition;
 
 MInnerMappingDefinition;
 rel MInnerMappingDefinition.MappingDefinition -> MappingDefinition;
diff --git a/ragconnect.base/src/main/jastadd/IntermediateToYAML.jrag b/ragconnect.base/src/main/jastadd/IntermediateToYAML.jrag
index 49c2693696a02de14852e252f3aadd3a722dfb95..c247d9d0a69b2f548e04fe69adcf35620fd69255 100644
--- a/ragconnect.base/src/main/jastadd/IntermediateToYAML.jrag
+++ b/ragconnect.base/src/main/jastadd/IntermediateToYAML.jrag
@@ -27,12 +27,12 @@ aspect IntermediateToYAML {
 
     // listAspect
     root.put("configJastAddList" , configJastAddList());
-    root.put("hasTreeListEndpoints" , hasTreeListEndpoints());
-    ListElement typesForReceivingListEndpointsElement = new ListElement();
-    for (TypeDecl typeDecl : typesForReceivingListEndpoints()) {
-      typesForReceivingListEndpointsElement.addElement(typeDecl.toYAML());
+    root.put("hasTreeListPorts" , hasTreeListPorts());
+    ListElement typesForReceivingListPortsElement = new ListElement();
+    for (TypeDecl typeDecl : typesForReceivingListPorts()) {
+      typesForReceivingListPortsElement.addElement(typeDecl.toYAML());
     }
-    root.put("typesForReceivingListEndpoints" , typesForReceivingListEndpointsElement);
+    root.put("typesForReceivingListPorts" , typesForReceivingListPortsElement);
 
     // ragconnect
     ListElement dependencyDefinitionsElement = new ListElement();
@@ -40,11 +40,11 @@ aspect IntermediateToYAML {
       dependencyDefinitionsElement.addElement(def.toYAML());
     }
     root.put("allDependencyDefinitionList" , dependencyDefinitionsElement);
-    ListElement endpointDefinitionsElement = new ListElement();
-    for (EndpointDefinition def : allEndpointDefinitionList()) {
-      endpointDefinitionsElement.addElement(def.toYAML());
+    ListElement portDefinitionsElement = new ListElement();
+    for (PortDefinition def : allPortDefinitionList()) {
+      portDefinitionsElement.addElement(def.toYAML());
     }
-    root.put("allEndpointDefinitionList" , endpointDefinitionsElement);
+    root.put("allPortDefinitionList" , portDefinitionsElement);
     ListElement mappingDefinitionsElement = new ListElement();
     for (MappingDefinition def : allMappingDefinitions()) {
       mappingDefinitionsElement.addElement(def.toYAML());
@@ -70,7 +70,7 @@ aspect IntermediateToYAML {
     return doc;
   }
 
-  syn MappingElement EndpointDefinition.toYAML() {
+  syn MappingElement PortDefinition.toYAML() {
     MappingElement result = new MappingElement();
     // grammar children
     result.put("AlwaysApply" , getAlwaysApply());
@@ -108,16 +108,16 @@ aspect IntermediateToYAML {
       result.put("updateMethodName" , updateMethodName());
       result.put("writeMethodName" , writeMethodName());
     } else {
-      result.put("hasTypeEndpointTarget" , hasTypeEndpointTarget());
+      result.put("hasTypePortTarget" , hasTypePortTarget());
       result.put("idTokenName" , idTokenName());
       result.put("internalConnectMethodName" , internalConnectMethodName());
       result.put("resolveInListMethodName" , resolveInListMethodName());
-      result.put("typeIsList" , getEndpointTarget().typeIsList());
+      result.put("typeIsList" , getPortTarget().typeIsList());
     }
     return result;
   }
 
-  void UntypedEndpointTarget.addToYAML(MappingElement result) {
+  void UntypedPortTarget.addToYAML(MappingElement result) {
     // empty
   }
 
@@ -147,6 +147,17 @@ aspect IntermediateToYAML {
     return result;
   }
 
+  syn Element TemplateDefaultMappingDefinition.toYAML() {
+    MappingElement result = new MappingElement();
+
+    // ragconnect / mapping
+    result.put("isUsed" , isUsed());
+    result.put("isSerializeListMapping" , isSerializeListMapping());
+    result.put("isSerializeJavaUtilListMapping" , isSerializeJavaUtilListMapping());
+    result.put("isDeserializeListMapping" , isDeserializeListMapping());
+    return result;
+  }
+
   syn Element DependencyDefinition.toYAML() {
     MappingElement result = new MappingElement();
     // dependencyDefinition
@@ -156,7 +167,7 @@ aspect IntermediateToYAML {
     result.put("targetParentTypeName" , targetParentTypeName());
 
     // tokenComponent
-    result.put("targetEndpointDefinition" , targetEndpointDefinition().toYAML());
+    result.put("targetPortDefinition" , targetPortDefinition().toYAML());
     return result;
   }
 
diff --git a/ragconnect.base/src/main/jastadd/Mappings.jrag b/ragconnect.base/src/main/jastadd/Mappings.jrag
index e92fc4fa27569ee353b0c16798e2ce3b7eb07a29..75ea75eb90f122bc743a36ac1b2a62b36a065a89 100644
--- a/ragconnect.base/src/main/jastadd/Mappings.jrag
+++ b/ragconnect.base/src/main/jastadd/Mappings.jrag
@@ -27,6 +27,16 @@ aspect DefaultMappings {
     result.setContent(content);
     return result;
   }
+  private TemplateDefaultMappingDefinition RagConnect.initTemplateMappingDefinition(
+      TemplateDefaultMappingDefinition prototype, String id, String type, boolean fromBytes) {
+    MappingDefinitionType givenType = new JavaMappingDefinitionType().setType(new SimpleJavaTypeUse(type));
+    MappingDefinitionType bytesType = new JavaArrayMappingDefinitionType().setType(new SimpleJavaTypeUse("byte"));
+
+    prototype.setID(id);
+    prototype.setFromType(fromBytes ? bytesType : givenType);
+    prototype.setToType(fromBytes ? givenType : bytesType);
+    return prototype;
+  }
 
   private DefaultMappingDefinition RagConnect.baseDefaultMappingDefinition(String fromTypeName, String toTypeName, String content) {
     return createDefaultMappingDefinition("_Default", fromTypeName, toTypeName, content);
@@ -81,7 +91,7 @@ aspect DefaultMappings {
             "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" +
-            configJastAddList() + "<" + typeName + ">" + " result = " + typeName + ".deserializeList((com.fasterxml.jackson.databind.node.ArrayNode)mapper.readTree(parser));\n" +
+            configJastAddList() + "<" + typeName + ">" + " result = " + typeName + ".deserializeListOf" + typeName + "((com.fasterxml.jackson.databind.node.ArrayNode)mapper.readTree(parser));\n" +
             "parser.close();\n" +
             "return result;"
     );
@@ -155,9 +165,18 @@ aspect DefaultMappings {
       "char", "String", "return String.valueOf(input);");
 }
 
+aspect TemplateDefaultMappingDefinitions {
+  syn nta TemplateDefaultMappingDefinition RagConnect.serializeListMapping() = initTemplateMappingDefinition(
+          new SerializeListMapping(), "SerializeListMapping", "JastAddList", false);
+  syn nta TemplateDefaultMappingDefinition RagConnect.serializeJavaUtilListMapping() = initTemplateMappingDefinition(
+          new SerializeJavaUtilListMapping(), "SerializeJavaUtilListMapping", "java.util.List", false);
+  syn nta TemplateDefaultMappingDefinition RagConnect.deserializeListMapping(String name) = initTemplateMappingDefinition(
+          new DeserializeListMapping().setName(name), "DeserializeListMapping", "JastAddList", true);
+}
+
 aspect Mappings {
   // --- effectiveMappings ---
-  syn java.util.List<MappingDefinition> EndpointDefinition.effectiveMappings() {
+  syn java.util.List<MappingDefinition> PortDefinition.effectiveMappings() {
     java.util.List<MappingDefinition> result;
     if (!getSend()) {
       // if no mappings are specified, or if first mapping is not suitable.
@@ -183,10 +202,10 @@ aspect Mappings {
   }
 
   // --- isPrimitiveType ---
-  syn boolean EndpointDefinition.isPrimitiveType() = getEndpointTarget().isPrimitiveType();
-  syn boolean EndpointTarget.isPrimitiveType() = false;
-  eq TokenEndpointTarget.isPrimitiveType() = getToken().isPrimitiveType();
-  eq AttributeEndpointTarget.isPrimitiveType() = new SimpleJavaTypeUse(getTypeName()).isPrimitiveType();
+  syn boolean PortDefinition.isPrimitiveType() = getPortTarget().isPrimitiveType();
+  syn boolean PortTarget.isPrimitiveType() = false;
+  eq TokenPortTarget.isPrimitiveType() = getToken().isPrimitiveType();
+  eq AttributePortTarget.isPrimitiveType() = new SimpleJavaTypeUse(getTypeName()).isPrimitiveType();
   syn boolean TokenComponent.isPrimitiveType() = effectiveJavaTypeUse().isPrimitiveType();
   syn boolean JavaTypeUse.isPrimitiveType() = false;
   eq SimpleJavaTypeUse.isPrimitiveType() {
@@ -212,7 +231,7 @@ aspect Mappings {
   eq JavaArrayMappingDefinitionType.isArray() = true;
 
   // --- suitableReceiveDefaultMapping ---
-  syn DefaultMappingDefinition EndpointDefinition.suitableReceiveDefaultMapping() {
+  syn DefaultMappingDefinition PortDefinition.suitableReceiveDefaultMapping() {
     switch (targetTypeName()) {
       case "boolean":
       case "Boolean":
@@ -240,7 +259,7 @@ aspect Mappings {
       default:
         try {
           TypeDecl typeDecl = program().resolveTypeDecl(targetTypeName());
-          return getEndpointTarget().isTypeEndpointTarget() && typeIsList() && !getIndexBasedListAccess() ? ragconnect().defaultBytesToListMapping(typeDecl.getName()) : ragconnect().defaultBytesToTreeMapping(typeDecl.getName());
+          return getPortTarget().isTypePortTarget() && typeIsList() && !getIndexBasedListAccess() ? ragconnect().defaultBytesToListMapping(typeDecl.getName()) : ragconnect().defaultBytesToTreeMapping(typeDecl.getName());
         } catch (Exception ignore) {
         }
         System.err.println("Could not find suitable default receive mapping for " + targetTypeName() + " on " + this);
@@ -249,7 +268,7 @@ aspect Mappings {
   }
 
   // --- suitableSendDefaultMapping ---
-  syn DefaultMappingDefinition EndpointDefinition.suitableSendDefaultMapping() {
+  syn DefaultMappingDefinition PortDefinition.suitableSendDefaultMapping() {
     switch (targetTypeName()) {
       case "boolean":
       case "Boolean":
@@ -275,10 +294,10 @@ aspect Mappings {
       case "String":
         return ragconnect().defaultStringToBytesMapping();
       default:
-        if (getEndpointTarget().isTypeEndpointTarget() && typeIsList() && !getIndexBasedListAccess()) {
+        if (getPortTarget().isTypePortTarget() && typeIsList() && !getIndexBasedListAccess()) {
           return ragconnect().defaultListToBytesMapping();
         }
-        if (getEndpointTarget().isRelationEndpointTarget() && typeIsList() && !getIndexBasedListAccess()) {
+        if (getPortTarget().isRelationPortTarget() && typeIsList() && !getIndexBasedListAccess()) {
           return ragconnect().defaultJavaUtilListToBytesMapping();
         }
         try {
@@ -293,9 +312,9 @@ aspect Mappings {
   }
 
   // --- targetTypeName ---
-  syn String EndpointDefinition.targetTypeName() {
+  syn String PortDefinition.targetTypeName() {
     if (getMappingList().isEmpty()) {
-      return getEndpointTarget().targetTypeName();
+      return getPortTarget().targetTypeName();
     } else {
       if (getSend()) {
         return getMappingList().get(getMappingList().size() - 1).getToType().prettyPrint();
@@ -304,12 +323,12 @@ aspect Mappings {
       }
     }
   }
-  syn String EndpointTarget.targetTypeName();
-  eq AttributeEndpointTarget.targetTypeName() = getTypeName();
-  eq RelationEndpointTarget.targetTypeName() = getRole().oppositeRole().targetTypeName();
-  eq TokenEndpointTarget.targetTypeName() = getToken().effectiveJavaTypeUse().getName();
-  eq TypeEndpointTarget.targetTypeName() = getType().getTypeDecl().getName();
-  eq ContextFreeTypeEndpointTarget.targetTypeName() = getTypeDecl().getName();
+  syn String PortTarget.targetTypeName();
+  eq AttributePortTarget.targetTypeName() = getTypeName();
+  eq RelationPortTarget.targetTypeName() = getRole().oppositeRole().targetTypeName();
+  eq TokenPortTarget.targetTypeName() = getToken().effectiveJavaTypeUse().getName();
+  eq TypePortTarget.targetTypeName() = getType().getTypeDecl().getName();
+  eq ContextFreeTypePortTarget.targetTypeName() = getTypeDecl().getName();
 
   syn String Role.targetTypeName() = getType().getName();
   eq ListRole.targetTypeName() = "java.util.List<" + getType().getName() + ">";
@@ -390,9 +409,12 @@ aspect Mappings {
       result.add(defaultBytesToTreeMapping(typeDecl.getName()));
       result.add(defaultTreeToBytesMapping(typeDecl.getName()));
       result.add(defaultBytesToListMapping(typeDecl.getName()));
+      result.add(deserializeListMapping(typeDecl.getName()));
     }
     result.add(defaultListToBytesMapping());
     result.add(defaultJavaUtilListToBytesMapping());
+    result.add(serializeListMapping());
+    result.add(serializeJavaUtilListMapping());
 //    // string conversion
 //    result.add(defaultStringToBooleanMapping());
 //    result.add(defaultStringToIntMapping());
diff --git a/ragconnect.base/src/main/jastadd/NameResolution.jrag b/ragconnect.base/src/main/jastadd/NameResolution.jrag
index 0690eca4a260b5f7396dfe2ca6905b3aa98e0aa5..e470430d8f5d355cd90d4e79503ee431de994a18 100644
--- a/ragconnect.base/src/main/jastadd/NameResolution.jrag
+++ b/ragconnect.base/src/main/jastadd/NameResolution.jrag
@@ -1,48 +1,48 @@
 aspect RagConnectNameResolution {
-  // --- lookupTokenEndpointDefinition ---
-  inh java.util.List<EndpointDefinition> EndpointDefinition.lookupTokenEndpointDefinitions(TokenComponent token);
-  inh java.util.List<EndpointDefinition> EndpointTarget.lookupTokenEndpointDefinitions(TokenComponent token);
-  eq RagConnect.getConnectSpecificationFile().lookupTokenEndpointDefinitions(TokenComponent token) = lookupTokenEndpointDefinitions(token);
-  syn java.util.List<EndpointDefinition> RagConnect.lookupTokenEndpointDefinitions(TokenComponent token) {
-    java.util.List<EndpointDefinition> result = new java.util.ArrayList<>();
-    for (EndpointTarget target : givenEndpointTargetList()) {
-      if (target.isTokenEndpointTarget() && target.asTokenEndpointTarget().getToken().equals(token)) {
-        result.add(target.containingEndpointDefinition());
+  // --- lookupTokenPortDefinition ---
+  inh java.util.List<PortDefinition> PortDefinition.lookupTokenPortDefinitions(TokenComponent token);
+  inh java.util.List<PortDefinition> PortTarget.lookupTokenPortDefinitions(TokenComponent token);
+  eq RagConnect.getConnectSpecificationFile().lookupTokenPortDefinitions(TokenComponent token) = lookupTokenPortDefinitions(token);
+  syn java.util.List<PortDefinition> RagConnect.lookupTokenPortDefinitions(TokenComponent token) {
+    java.util.List<PortDefinition> result = new java.util.ArrayList<>();
+    for (PortTarget target : givenPortTargetList()) {
+      if (target.isTokenPortTarget() && target.asTokenPortTarget().getToken().equals(token)) {
+        result.add(target.containingPortDefinition());
       }
     }
     return result;
   }
 
-  // --- lookupGivenTypeEndpointDefinition ---
-  inh java.util.List<EndpointDefinition> EndpointDefinition.lookupGivenTypeEndpointDefinitions(TypeComponent type);
-  inh java.util.List<EndpointDefinition> EndpointTarget.lookupGivenTypeEndpointDefinitions(TypeComponent type);
-  eq RagConnect.getConnectSpecificationFile().lookupGivenTypeEndpointDefinitions(TypeComponent type) = lookupTypeEndpointDefinitions(type, true);
+  // --- lookupGivenTypePortDefinition ---
+  inh java.util.List<PortDefinition> PortDefinition.lookupGivenTypePortDefinitions(TypeComponent type);
+  inh java.util.List<PortDefinition> PortTarget.lookupGivenTypePortDefinitions(TypeComponent type);
+  eq RagConnect.getConnectSpecificationFile().lookupGivenTypePortDefinitions(TypeComponent type) = lookupTypePortDefinitions(type, true);
 
-  // --- lookupAllTypeEndpointDefinition ---
-  inh java.util.List<EndpointDefinition> EndpointDefinition.lookupAllTypeEndpointDefinitions(TypeComponent type);
-  inh java.util.List<EndpointDefinition> EndpointTarget.lookupAllTypeEndpointDefinitions(TypeComponent type);
-  eq RagConnect.getConnectSpecificationFile().lookupAllTypeEndpointDefinitions(TypeComponent type) = lookupTypeEndpointDefinitions(type, false);
+  // --- lookupAllTypePortDefinition ---
+  inh java.util.List<PortDefinition> PortDefinition.lookupAllTypePortDefinitions(TypeComponent type);
+  inh java.util.List<PortDefinition> PortTarget.lookupAllTypePortDefinitions(TypeComponent type);
+  eq RagConnect.getConnectSpecificationFile().lookupAllTypePortDefinitions(TypeComponent type) = lookupTypePortDefinitions(type, false);
 
-  syn java.util.List<EndpointDefinition> RagConnect.lookupTypeEndpointDefinitions(TypeComponent type, boolean onlyGiven) {
-    java.util.List<EndpointDefinition> result = new java.util.ArrayList<>();
-    for (EndpointDefinition def : onlyGiven ? givenEndpointDefinitionList() : allEndpointDefinitionList()) {
-      EndpointTarget target = def.getEndpointTarget();
-      if (target.isTypeEndpointTarget() && target.asTypeEndpointTarget().getType().equals(type)) {
+  syn java.util.List<PortDefinition> RagConnect.lookupTypePortDefinitions(TypeComponent type, boolean onlyGiven) {
+    java.util.List<PortDefinition> result = new java.util.ArrayList<>();
+    for (PortDefinition def : onlyGiven ? givenPortDefinitionList() : allPortDefinitionList()) {
+      PortTarget target = def.getPortTarget();
+      if (target.isTypePortTarget() && target.asTypePortTarget().getType().equals(type)) {
         result.add(def);
       }
     }
     return result;
   }
 
-  // --- lookupContextFreeTypeEndpointDefinition ---
-  inh java.util.List<EndpointDefinition> EndpointDefinition.lookupContextFreeTypeEndpointDefinitions(TypeDecl typeDecl);
-  inh java.util.List<EndpointDefinition> EndpointTarget.lookupContextFreeTypeEndpointDefinitions(TypeDecl typeDecl);
-  eq RagConnect.getConnectSpecificationFile().lookupContextFreeTypeEndpointDefinitions(TypeDecl typeDecl) = lookupContextFreeTypeEndpointDefinitions(typeDecl);
-  syn java.util.List<EndpointDefinition> RagConnect.lookupContextFreeTypeEndpointDefinitions(TypeDecl typeDecl) {
-    java.util.List<EndpointDefinition> result = new java.util.ArrayList<>();
-    for (EndpointTarget target : givenEndpointTargetList()) {
-      if (target.isContextFreeTypeEndpointTarget() && target.asContextFreeTypeEndpointTarget().getTypeDecl().equals(typeDecl)) {
-        result.add(target.containingEndpointDefinition());
+  // --- lookupContextFreeTypePortDefinition ---
+  inh java.util.List<PortDefinition> PortDefinition.lookupContextFreeTypePortDefinitions(TypeDecl typeDecl);
+  inh java.util.List<PortDefinition> PortTarget.lookupContextFreeTypePortDefinitions(TypeDecl typeDecl);
+  eq RagConnect.getConnectSpecificationFile().lookupContextFreeTypePortDefinitions(TypeDecl typeDecl) = lookupContextFreeTypePortDefinitions(typeDecl);
+  syn java.util.List<PortDefinition> RagConnect.lookupContextFreeTypePortDefinitions(TypeDecl typeDecl) {
+    java.util.List<PortDefinition> result = new java.util.ArrayList<>();
+    for (PortTarget target : givenPortTargetList()) {
+      if (target.isContextFreeTypePortTarget() && target.asContextFreeTypePortTarget().getTypeDecl().equals(typeDecl)) {
+        result.add(target.containingPortDefinition());
       }
     }
     return result;
@@ -59,15 +59,15 @@ aspect RagConnectNameResolution {
     return null;
   }
 
-  // rel EndpointDefinition.Mapping* -> MappingDefinition
-  refine RefResolverStubs eq EndpointDefinition.resolveMappingByToken(String id, int position) {
+  // rel PortDefinition.Mapping* -> MappingDefinition
+  refine RefResolverStubs eq PortDefinition.resolveMappingByToken(String id, int position) {
     MappingDefinition result = tryResolveMappingByToken(id);
     if (result == null) {
       System.err.println("Could not resolve MappingDefinition '" + id + "'.");
     }
     return result;
   }
-  syn MappingDefinition EndpointDefinition.tryResolveMappingByToken(String id) {
+  syn MappingDefinition PortDefinition.tryResolveMappingByToken(String id) {
     for (MappingDefinition mappingDefinition : ragconnect().givenMappingDefinitionList()) {
       if (mappingDefinition.getID().equals(id)) {
         return mappingDefinition;
diff --git a/ragconnect.base/src/main/jastadd/Navigation.jrag b/ragconnect.base/src/main/jastadd/Navigation.jrag
index 4119bcc6d9bf23020b5fd7d83f49c074f1a2c5f0..8b23c000fb8136af504d6dc452a42d5dd6ae82ca 100644
--- a/ragconnect.base/src/main/jastadd/Navigation.jrag
+++ b/ragconnect.base/src/main/jastadd/Navigation.jrag
@@ -1,85 +1,110 @@
 aspect GeneratedNavigation {
 
-  /** Tests if EndpointTarget is a TokenEndpointTarget.
-  *  @return 'true' if this is a TokenEndpointTarget, otherwise 'false'
+  /** Tests if PortTarget is a TokenPortTarget.
+  *  @return 'true' if this is a TokenPortTarget, otherwise 'false'
   */
-  syn boolean EndpointTarget.isTokenEndpointTarget() = false;
-  eq TokenEndpointTarget.isTokenEndpointTarget() = true;
+  syn boolean PortTarget.isTokenPortTarget() = false;
+  eq TokenPortTarget.isTokenPortTarget() = true;
 
-  /** Tests if EndpointTarget is a TypeEndpointTarget.
-  *  @return 'true' if this is a TypeEndpointTarget, otherwise 'false'
+  /** Tests if PortTarget is a TypePortTarget.
+  *  @return 'true' if this is a TypePortTarget, otherwise 'false'
   */
-  syn boolean EndpointTarget.isTypeEndpointTarget() = false;
-  eq TypeEndpointTarget.isTypeEndpointTarget() = true;
+  syn boolean PortTarget.isTypePortTarget() = false;
+  eq TypePortTarget.isTypePortTarget() = true;
 
-  /** Tests if EndpointTarget is a ContextFreeTypeEndpointTarget.
-  *  @return 'true' if this is a ContextFreeTypeEndpointTarget, otherwise 'false'
+  /** Tests if PortTarget is a ContextFreeTypePortTarget.
+  *  @return 'true' if this is a ContextFreeTypePortTarget, otherwise 'false'
   */
-  syn boolean EndpointTarget.isContextFreeTypeEndpointTarget() = false;
-  eq ContextFreeTypeEndpointTarget.isContextFreeTypeEndpointTarget() = true;
+  syn boolean PortTarget.isContextFreeTypePortTarget() = false;
+  eq ContextFreeTypePortTarget.isContextFreeTypePortTarget() = true;
 
-  /** Tests if EndpointTarget is a UntypedEndpointTarget.
-  *  @return 'true' if this is a UntypedEndpointTarget, otherwise 'false'
+  /** Tests if PortTarget is a UntypedPortTarget.
+  *  @return 'true' if this is a UntypedPortTarget, otherwise 'false'
   */
-  syn boolean EndpointTarget.isUntypedEndpointTarget() = false;
-  eq UntypedEndpointTarget.isUntypedEndpointTarget() = true;
+  syn boolean PortTarget.isUntypedPortTarget() = false;
+  eq UntypedPortTarget.isUntypedPortTarget() = true;
 
-  /** Tests if EndpointTarget is a AttributeEndpointTarget.
-  *  @return 'true' if this is a AttributeEndpointTarget, otherwise 'false'
+  /** Tests if PortTarget is a AttributePortTarget.
+  *  @return 'true' if this is a AttributePortTarget, otherwise 'false'
   */
-  syn boolean EndpointTarget.isAttributeEndpointTarget() = false;
-  eq AttributeEndpointTarget.isAttributeEndpointTarget() = true;
+  syn boolean PortTarget.isAttributePortTarget() = false;
+  eq AttributePortTarget.isAttributePortTarget() = true;
 
-  /** Tests if EndpointTarget is a RelationEndpointTarget.
-  *  @return 'true' if this is a RelationEndpointTarget, otherwise 'false'
+  /** Tests if PortTarget is a RelationPortTarget.
+  *  @return 'true' if this is a RelationPortTarget, otherwise 'false'
   */
-  syn boolean EndpointTarget.isRelationEndpointTarget() = false;
-  eq RelationEndpointTarget.isRelationEndpointTarget() = true;
+  syn boolean PortTarget.isRelationPortTarget() = false;
+  eq RelationPortTarget.isRelationPortTarget() = true;
 
-  /** casts a EndpointTarget into a TokenEndpointTarget if possible.
-   *  @return 'this' cast to a TokenEndpointTarget or 'null'
+  /** Tests if TemplateDefaultMappingDefinition is a SerializeListMapping.
+  *  @return 'true' if this is a SerializeListMapping, otherwise 'false'
+  */
+  syn boolean TemplateDefaultMappingDefinition.isSerializeListMapping() = false;
+  eq SerializeListMapping.isSerializeListMapping() = true;
+
+  /** Tests if TemplateDefaultMappingDefinition is a SerializeJavaUtilListMapping.
+  *  @return 'true' if this is a SerializeJavaUtilListMapping, otherwise 'false'
+  */
+  syn boolean TemplateDefaultMappingDefinition.isSerializeJavaUtilListMapping() = false;
+  eq SerializeJavaUtilListMapping.isSerializeJavaUtilListMapping() = true;
+
+  /** Tests if TemplateDefaultMappingDefinition is a DeserializeListMapping.
+  *  @return 'true' if this is a DeserializeListMapping, otherwise 'false'
+  */
+  syn boolean TemplateDefaultMappingDefinition.isDeserializeListMapping() = false;
+  eq DeserializeListMapping.isDeserializeListMapping() = true;
+
+  /** casts a PortTarget into a TokenPortTarget if possible.
+   *  @return 'this' cast to a TokenPortTarget or 'null'
    */
-  syn TokenEndpointTarget EndpointTarget.asTokenEndpointTarget();
-  eq EndpointTarget.asTokenEndpointTarget() = null;
-  eq TokenEndpointTarget.asTokenEndpointTarget() = this;
+  syn TokenPortTarget PortTarget.asTokenPortTarget();
+  eq PortTarget.asTokenPortTarget() = null;
+  eq TokenPortTarget.asTokenPortTarget() = this;
 
-  /** casts a EndpointTarget into a TypeEndpointTarget if possible.
-   *  @return 'this' cast to a TypeEndpointTarget or 'null'
+  /** casts a PortTarget into a TypePortTarget if possible.
+   *  @return 'this' cast to a TypePortTarget or 'null'
    */
-  syn TypeEndpointTarget EndpointTarget.asTypeEndpointTarget();
-  eq EndpointTarget.asTypeEndpointTarget() = null;
-  eq TypeEndpointTarget.asTypeEndpointTarget() = this;
+  syn TypePortTarget PortTarget.asTypePortTarget();
+  eq PortTarget.asTypePortTarget() = null;
+  eq TypePortTarget.asTypePortTarget() = this;
 
-  /** casts a EndpointTarget into a ContextFreeTypeEndpointTarget if possible.
-   *  @return 'this' cast to a ContextFreeTypeEndpointTarget or 'null'
+  /** casts a PortTarget into a ContextFreeTypePortTarget if possible.
+   *  @return 'this' cast to a ContextFreeTypePortTarget or 'null'
    */
-  syn ContextFreeTypeEndpointTarget EndpointTarget.asContextFreeTypeEndpointTarget();
-  eq EndpointTarget.asContextFreeTypeEndpointTarget() = null;
-  eq ContextFreeTypeEndpointTarget.asContextFreeTypeEndpointTarget() = this;
+  syn ContextFreeTypePortTarget PortTarget.asContextFreeTypePortTarget();
+  eq PortTarget.asContextFreeTypePortTarget() = null;
+  eq ContextFreeTypePortTarget.asContextFreeTypePortTarget() = this;
 
-  /** casts a EndpointTarget into a UntypedEndpointTarget if possible.
-   *  @return 'this' cast to a UntypedEndpointTarget or 'null'
+  /** casts a PortTarget into a UntypedPortTarget if possible.
+   *  @return 'this' cast to a UntypedPortTarget or 'null'
    */
-  syn UntypedEndpointTarget EndpointTarget.asUntypedEndpointTarget();
-  eq EndpointTarget.asUntypedEndpointTarget() = null;
-  eq UntypedEndpointTarget.asUntypedEndpointTarget() = this;
+  syn UntypedPortTarget PortTarget.asUntypedPortTarget();
+  eq PortTarget.asUntypedPortTarget() = null;
+  eq UntypedPortTarget.asUntypedPortTarget() = this;
 
-  /** casts a EndpointTarget into a AttributeEndpointTarget if possible.
-   *  @return 'this' cast to a AttributeEndpointTarget or 'null'
+  /** casts a PortTarget into a AttributePortTarget if possible.
+   *  @return 'this' cast to a AttributePortTarget or 'null'
    */
-  syn AttributeEndpointTarget EndpointTarget.asAttributeEndpointTarget();
-  eq EndpointTarget.asAttributeEndpointTarget() = null;
-  eq AttributeEndpointTarget.asAttributeEndpointTarget() = this;
+  syn AttributePortTarget PortTarget.asAttributePortTarget();
+  eq PortTarget.asAttributePortTarget() = null;
+  eq AttributePortTarget.asAttributePortTarget() = this;
 
-  /** casts a EndpointTarget into a RelationEndpointTarget if possible.
-   *  @return 'this' cast to a RelationEndpointTarget or 'null'
+  /** casts a PortTarget into a RelationPortTarget if possible.
+   *  @return 'this' cast to a RelationPortTarget or 'null'
    */
-  syn RelationEndpointTarget EndpointTarget.asRelationEndpointTarget();
-  eq EndpointTarget.asRelationEndpointTarget() = null;
-  eq RelationEndpointTarget.asRelationEndpointTarget() = this;
+  syn RelationPortTarget PortTarget.asRelationPortTarget();
+  eq PortTarget.asRelationPortTarget() = null;
+  eq RelationPortTarget.asRelationPortTarget() = this;
 }
 aspect RagConnectNavigation {
 
+  // adapted from generated navigation, was defined only for DefaultMappingDefinition
+  /** Tests if MappingDefinition is a TemplateDefaultMappingDefinition.
+  *  @return 'true' if this is a TemplateDefaultMappingDefinition, otherwise 'false'
+  */
+  syn boolean MappingDefinition.isTemplateDefaultMappingDefinition() = false;
+  eq TemplateDefaultMappingDefinition.isTemplateDefaultMappingDefinition() = true;
+
   // --- program ---
   eq RagConnect.getChild().program() = getProgram();
 
@@ -94,9 +119,9 @@ aspect RagConnectNavigation {
   eq Program.getChild().containedConnectSpecification() = null;
   eq ConnectSpecification.getChild().containedConnectSpecification() = this;
 
-  // --- containingEndpointDefinition ---
-  inh EndpointDefinition EndpointTarget.containingEndpointDefinition();
-  eq EndpointDefinition.getEndpointTarget().containingEndpointDefinition() = this;
+  // --- containingPortDefinition ---
+  inh PortDefinition PortTarget.containingPortDefinition();
+  eq PortDefinition.getPortTarget().containingPortDefinition() = this;
 
   // --- containedFile
   eq RagConnect.getChild().containedFile() = null;
@@ -115,11 +140,11 @@ aspect RagConnectNavigation {
   inh boolean DependencyDefinition.isFirstInList();
   eq ConnectSpecification.getDependencyDefinition(int index).isFirstInList() = index == 0;
 
-  //--- givenEndpointTargetList ---
-  syn List<EndpointTarget> RagConnect.givenEndpointTargetList() {
-    List<EndpointTarget> result = new ArrayList<>();
+  //--- givenPortTargetList ---
+  syn List<PortTarget> RagConnect.givenPortTargetList() {
+    List<PortTarget> result = new ArrayList<>();
     for (ConnectSpecification spec : getConnectSpecificationFileList()) {
-      spec.getEndpointDefinitionList().forEach(endpointDef -> result.add(endpointDef.getEndpointTarget()));
+      spec.getPortDefinitionList().forEach(portDef -> result.add(portDef.getPortTarget()));
     }
     return result;
   }
diff --git a/ragconnect.base/src/main/jastadd/RagConnect.relast b/ragconnect.base/src/main/jastadd/RagConnect.relast
index df50e3dab1dde22e38bca0588396de647b49c8cb..7282bb3fda5771c3b1ea87a4bfae235c3950b640 100644
--- a/ragconnect.base/src/main/jastadd/RagConnect.relast
+++ b/ragconnect.base/src/main/jastadd/RagConnect.relast
@@ -1,23 +1,23 @@
 RagConnect ::= ConnectSpecificationFile* Program Handler* Configuration;
 
-abstract ConnectSpecification ::= EndpointDefinition* DependencyDefinition* MappingDefinition*;
+abstract ConnectSpecification ::= PortDefinition* DependencyDefinition* MappingDefinition*;
 ConnectSpecificationFile : ConnectSpecification ::= <FileName>;
 
-EndpointDefinition ::= <AlwaysApply:boolean> <IndexBasedListAccess:boolean> <WithAdd:boolean> <Send:boolean> EndpointTarget;
-rel EndpointDefinition.Mapping* <-> MappingDefinition.UsedAt*;
+PortDefinition ::= <AlwaysApply:boolean> <IndexBasedListAccess:boolean> <WithAdd:boolean> <Send:boolean> PortTarget;
+rel PortDefinition.Mapping* <-> MappingDefinition.UsedAt*;
 
-abstract EndpointTarget;
-TokenEndpointTarget : EndpointTarget;
-rel TokenEndpointTarget.Token <-> TokenComponent.TokenEndpointTarget*;
-TypeEndpointTarget : EndpointTarget;
-rel TypeEndpointTarget.Type <-> TypeComponent.TypeEndpointTarget*;
-ContextFreeTypeEndpointTarget : EndpointTarget;
-rel ContextFreeTypeEndpointTarget.TypeDecl <-> TypeDecl.ContextFreeTypeEndpointTarget*;
-AttributeEndpointTarget : EndpointTarget ::= <Name> <TypeName> ;
-rel AttributeEndpointTarget.ParentTypeDecl <-> TypeDecl.AttributeEndpointTarget*;
-RelationEndpointTarget : EndpointTarget ;
-rel RelationEndpointTarget.Role <-> NavigableRole.RelationEndpointTarget* ;
-UntypedEndpointTarget : EndpointTarget ::= <TypeName> <ChildName> <IsAttribute:boolean>;  // only used by parser
+abstract PortTarget;
+TokenPortTarget : PortTarget;
+rel TokenPortTarget.Token <-> TokenComponent.TokenPortTarget*;
+TypePortTarget : PortTarget;
+rel TypePortTarget.Type <-> TypeComponent.TypePortTarget*;
+ContextFreeTypePortTarget : PortTarget;
+rel ContextFreeTypePortTarget.TypeDecl <-> TypeDecl.ContextFreeTypePortTarget*;
+AttributePortTarget : PortTarget ::= <Name> <TypeName> ;
+rel AttributePortTarget.ParentTypeDecl <-> TypeDecl.AttributePortTarget*;
+RelationPortTarget : PortTarget ;
+rel RelationPortTarget.Role <-> NavigableRole.RelationPortTarget* ;
+UntypedPortTarget : PortTarget ::= <TypeName> <ChildName> <IsAttribute:boolean>;  // only used by parser
 
 DependencyDefinition ::= <ID>;
 rel DependencyDefinition.Source <-> TokenComponent.DependencySourceDefinition*;
@@ -28,6 +28,10 @@ abstract MappingDefinitionType ::= ;
 JavaMappingDefinitionType : MappingDefinitionType ::= Type:JavaTypeUse;
 JavaArrayMappingDefinitionType : MappingDefinitionType ::= Type:JavaTypeUse;
 DefaultMappingDefinition : MappingDefinition;
+abstract TemplateDefaultMappingDefinition : DefaultMappingDefinition ;
+SerializeListMapping : TemplateDefaultMappingDefinition ;
+SerializeJavaUtilListMapping : TemplateDefaultMappingDefinition ;
+DeserializeListMapping : TemplateDefaultMappingDefinition ::= <Name> ;
 
 Handler ::= <ClassName> <UniqueName> <InUse:boolean>;
 
diff --git a/ragconnect.base/src/main/jastadd/Warnings.jrag b/ragconnect.base/src/main/jastadd/Warnings.jrag
index 8ef06c6f3738e22dcfb66018c333b3c079304ca5..49a36233c463f5c8b0b1eb391e490ff526a94472 100644
--- a/ragconnect.base/src/main/jastadd/Warnings.jrag
+++ b/ragconnect.base/src/main/jastadd/Warnings.jrag
@@ -11,7 +11,7 @@ aspect Warnings {
     when this.isFirstInList()  // print warning only once for first dependency definition
     to RagConnect.warnings();
 
-  EndpointDefinition contributes warning("No dependency definitions are given, and incremental evaluation is disabled. No messages will be sent for this!")
+  PortDefinition contributes warning("No dependency definitions are given, and incremental evaluation is disabled. No messages will be sent for this!")
     when getSend() && ragconnect().allDependencyDefinitionList().isEmpty() && !ragconnect().configIncrementalOptionActive()
     to RagConnect.warnings();
 
diff --git a/ragconnect.base/src/main/jastadd/parser/ParserRewrites.jrag b/ragconnect.base/src/main/jastadd/parser/ParserRewrites.jrag
index 70f778c7b8f63b86d2d60bf0d71d1beb6cdeb41e..364161d52e019d3d72393d277a8d4f71a25d19b5 100644
--- a/ragconnect.base/src/main/jastadd/parser/ParserRewrites.jrag
+++ b/ragconnect.base/src/main/jastadd/parser/ParserRewrites.jrag
@@ -1,40 +1,40 @@
 aspect ParserRewrites {
-  rewrite UntypedEndpointTarget {
+  rewrite UntypedPortTarget {
     when (getChildName() != null && tryGloballyResolveTypeComponentByToken(combinedName()) != null)
-    to TypeEndpointTarget {
-      TypeEndpointTarget result = new TypeEndpointTarget();
+    to TypePortTarget {
+      TypePortTarget result = new TypePortTarget();
       result.copyOtherValuesFrom(this);
       result.setType(TypeComponent.createRef(this.combinedName()));
       return result;
     }
 
     when (getChildName() != null && tryGloballyResolveTokenComponentByToken(combinedName()) != null)
-    to TokenEndpointTarget {
-      TokenEndpointTarget result = new TokenEndpointTarget();
+    to TokenPortTarget {
+      TokenPortTarget result = new TokenPortTarget();
       result.copyOtherValuesFrom(this);
       result.setToken(TokenComponent.createRef(this.combinedName()));
       return result;
     }
 
     when (getChildName() != null && tryGloballyResolveNavigableRoleByToken(combinedName()) != null)
-    to RelationEndpointTarget {
-      RelationEndpointTarget result = new RelationEndpointTarget();
+    to RelationPortTarget {
+      RelationPortTarget result = new RelationPortTarget();
       result.copyOtherValuesFrom(this);
       result.setRole(NavigableRole.createRef(this.combinedName()));
       return result;
     }
 
     when (getChildName() == "")
-    to ContextFreeTypeEndpointTarget {
-      ContextFreeTypeEndpointTarget result = new ContextFreeTypeEndpointTarget();
+    to ContextFreeTypePortTarget {
+      ContextFreeTypePortTarget result = new ContextFreeTypePortTarget();
       result.copyOtherValuesFrom(this);
       result.setTypeDecl(TypeDecl.createRef(getTypeName()));
       return result;
     }
 
     when (getIsAttribute())
-    to AttributeEndpointTarget {
-      AttributeEndpointTarget result = new AttributeEndpointTarget();
+    to AttributePortTarget {
+      AttributePortTarget result = new AttributePortTarget();
       String[] tokens = this.getChildName().split(":");
       String attributeName = tokens[0];
       String attributeTypeName = tokens[1];
@@ -46,19 +46,19 @@ aspect ParserRewrites {
     }
   }
 
-  syn String UntypedEndpointTarget.combinedName() = getTypeName() + "." + getChildName();
+  syn String UntypedPortTarget.combinedName() = getTypeName() + "." + getChildName();
 
-  protected void EndpointTarget.copyOtherValuesFrom(EndpointTarget source) {
+  protected void PortTarget.copyOtherValuesFrom(PortTarget source) {
     this.setStart(source.getStartLine(), source.getStartColumn());
     this.setEnd(source.getEndLine(), source.getEndColumn());
   }
 
-  eq UntypedEndpointTarget.senderName() = "<untyped.senderName>";
-  eq UntypedEndpointTarget.getterMethodName() = "<untyped.getterMethodName>";
-  eq UntypedEndpointTarget.parentTypeName() = "<untyped.parentTypeName>";
-  eq UntypedEndpointTarget.entityName() = "<untyped.entityName>";
-  eq UntypedEndpointTarget.isAlreadyDefined() = false;
-  eq UntypedEndpointTarget.hasAttributeResetMethod() = false;
-  eq UntypedEndpointTarget.targetTypeName() = "<untyped.targetTypeName>";
-  eq UntypedEndpointTarget.isTypeEndpointTarget() = false;
+  eq UntypedPortTarget.senderName() = "<untyped.senderName>";
+  eq UntypedPortTarget.getterMethodName() = "<untyped.getterMethodName>";
+  eq UntypedPortTarget.parentTypeName() = "<untyped.parentTypeName>";
+  eq UntypedPortTarget.entityName() = "<untyped.entityName>";
+  eq UntypedPortTarget.isAlreadyDefined() = false;
+  eq UntypedPortTarget.hasAttributeResetMethod() = false;
+  eq UntypedPortTarget.targetTypeName() = "<untyped.targetTypeName>";
+  eq UntypedPortTarget.isTypePortTarget() = false;
 }
diff --git a/ragconnect.base/src/main/jastadd/parser/RagConnect.parser b/ragconnect.base/src/main/jastadd/parser/RagConnect.parser
index 55eac248e5f48d39aa38db6ac3cd20df61930220..f20588bb5d6cf82fe09f6a525dab543d53340f0d 100644
--- a/ragconnect.base/src/main/jastadd/parser/RagConnect.parser
+++ b/ragconnect.base/src/main/jastadd/parser/RagConnect.parser
@@ -1,7 +1,7 @@
 ConnectSpecificationFile connect_specification_file
-  = endpoint_definition.d connect_specification_file.r
+  = port_definition.d connect_specification_file.r
     {:
-      r.getEndpointDefinitionList().insertChild(d, 0); return r;
+      r.getPortDefinitionList().insertChild(d, 0); return r;
     :}
   | dependency_definition.d connect_specification_file.r
     {:
@@ -22,50 +22,50 @@ ConnectSpecificationFile connect_specification_file
   private Iterable<String> makeMappingDefs(ArrayList<?> raw_mapping_defs) {
     return () -> raw_mapping_defs.stream().map(raw -> ((Symbol) raw).value.toString()).iterator();
   }
-//  private TokenEndpointDefinition enableAlwaysApply(TokenEndpointDefinition def) {
+//  private TokenPortDefinition enableAlwaysApply(TokenPortDefinition def) {
 //    def.setAlwaysApply(true);
 //    return def;
 //  }
-  private EndpointDefinition createEndpointDefinition(
-    EndpointTarget endpointTarget, boolean send,
+  private PortDefinition createPortDefinition(
+    PortTarget portTarget, boolean send,
     boolean indexBasedListAccess, boolean withAdd) {
-      EndpointDefinition result = new EndpointDefinition();
+      PortDefinition result = new PortDefinition();
       result.setSend(send);
       result.setIndexBasedListAccess(indexBasedListAccess);
       result.setWithAdd(withAdd);
-      result.setEndpointTarget(endpointTarget);
+      result.setPortTarget(portTarget);
       return result;
     }
 :} ;
 
-EndpointDefinition endpoint_definition
-  = endpoint_definition_type.endpointDef SCOL
+PortDefinition port_definition
+  = port_definition_type.portDef SCOL
     {:
-      return endpointDef;
+      return portDef;
     :}
-  | endpoint_definition_type.endpointDef USING string_list.mapping_defs SCOL
+  | port_definition_type.portDef USING string_list.mapping_defs SCOL
     {:
       for (String mapping_def : makeMappingDefs(mapping_defs)) {
-        endpointDef.addMapping(MappingDefinition.createRef(mapping_def));
+        portDef.addMapping(MappingDefinition.createRef(mapping_def));
       }
-      return endpointDef;
+      return portDef;
     :}
 ;
 
-EndpointDefinition endpoint_definition_type
-  = SEND endpoint_target.t                        {: return createEndpointDefinition(t, true,  false, false); :}
-  | SEND INDEXED endpoint_target.t                {: return createEndpointDefinition(t, true,  true,  false); :}
-  | RECEIVE endpoint_target.t                     {: return createEndpointDefinition(t, false, false, false); :}
-  | RECEIVE INDEXED endpoint_target.t             {: return createEndpointDefinition(t, false, true,  false); :}
-  | RECEIVE WITH ADD endpoint_target.t            {: return createEndpointDefinition(t, false, false, true ); :}
-  | RECEIVE INDEXED WITH ADD endpoint_target.t    {: return createEndpointDefinition(t, false, true,  true ); :}
+PortDefinition port_definition_type
+  = SEND port_target.t                        {: return createPortDefinition(t, true,  false, false); :}
+  | SEND INDEXED port_target.t                {: return createPortDefinition(t, true,  true,  false); :}
+  | RECEIVE port_target.t                     {: return createPortDefinition(t, false, false, false); :}
+  | RECEIVE INDEXED port_target.t             {: return createPortDefinition(t, false, true,  false); :}
+  | RECEIVE WITH ADD port_target.t            {: return createPortDefinition(t, false, false, true ); :}
+  | RECEIVE INDEXED WITH ADD port_target.t    {: return createPortDefinition(t, false, true,  true ); :}
 ;
 
-EndpointTarget endpoint_target
-  = ID.type_name DOT ID.child_name    {: return new UntypedEndpointTarget(type_name, child_name, false); :}
+PortTarget port_target
+  = ID.type_name DOT ID.child_name    {: return new UntypedPortTarget(type_name, child_name, false); :}
   | ID.type_name DOT ID.child_name BRACKET_LEFT java_type_use.attribute_type_name BRACKET_RIGHT
-     {: return new UntypedEndpointTarget(type_name, child_name + ":" + attribute_type_name.prettyPrint(), true); :}
-  | ID.type_name                      {: return new UntypedEndpointTarget(type_name, "", false); :}
+     {: return new UntypedPortTarget(type_name, child_name + ":" + attribute_type_name.prettyPrint(), true); :}
+  | ID.type_name                      {: return new UntypedPortTarget(type_name, "", false); :}
 ;
 
 ArrayList string_list
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 fdb52151104f2ba8d3eb18a7487f9102ffcd19df..be81bfd4ea84123624e97c0047b85e7b29bda348 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
@@ -364,6 +364,8 @@ public class Compiler extends AbstractCompiler {
     com.github.mustachejava.DefaultMustacheFactory mf = new com.github.mustachejava.DefaultMustacheFactory();
     mf.setObjectHandler(roh);
     com.github.mustachejava.Mustache m = mf.compile("ragconnect.mustache");
+//    String yaml = ragConnect.toYAML().prettyPrint();
+//    Object context = new org.yaml.snakeyaml.Yaml().load(new StringReader(yaml));
     m.execute(new java.io.PrintWriter(new org.jastadd.ragconnect.compiler.AppendableWriter(sb)), ragConnect);
     return sb.toString();
   }
diff --git a/ragconnect.base/src/main/resources/EvaluationCounter.mustache b/ragconnect.base/src/main/resources/EvaluationCounter.mustache
new file mode 100644
index 0000000000000000000000000000000000000000..80563d2b00f3b31f86462bf9b8d037011cc8ac7c
--- /dev/null
+++ b/ragconnect.base/src/main/resources/EvaluationCounter.mustache
@@ -0,0 +1,105 @@
+aspect EvaluationCounter {
+  public String ASTNode.{{evaluationCounterSummaryMethodName}}() {
+  {{#configEvaluationCounter}}
+    return {{evaluationCounterVariable}}.summary();
+  {{/configEvaluationCounter}}
+  {{^configEvaluationCounter}}
+    String message = "Option --evaluationCounter was not set. No Summary available";
+    {{logWarn}}(message);
+    return message;
+  {{/configEvaluationCounter}}
+  }
+{{#configEvaluationCounter}}
+  static EvaluationCounter ASTNode.{{evaluationCounterVariable}} = new EvaluationCounter();
+  public void {{rootNodeName}}.ragconnectResetEvaluationCounter() {
+  {{#configEvaluationCounter}}
+    {{evaluationCounterVariable}}.reset();
+  {{/configEvaluationCounter}}
+  {{^configEvaluationCounter}}
+    {{logWarn}}("Option --evaluationCounter was not set. Nothing to reset!");
+  {{/configEvaluationCounter}}
+  }
+
+  public class EvaluationCounter {
+    private java.util.Map<String, java.util.Map<String, {{evaluationCounterInnerClass}}>> counters = new java.util.HashMap<>();
+    private final java.util.function.Function<? super String, ? extends java.util.Map<String, {{evaluationCounterInnerClass}}>> parentAbsent = key -> {
+      return new java.util.HashMap<>();
+    };
+    private final java.util.function.Function<? super String, ? extends {{evaluationCounterInnerClass}}> entityAbsent = key -> {
+      return new {{evaluationCounterInnerClass}}();
+    };
+
+    public void incrementReceive(String parentTypeName, String entityName) {
+      getCounter(parentTypeName, entityName).receive += 1;
+    }
+
+    public void incrementSend(String parentTypeName, String entityName) {
+      getCounter(parentTypeName, entityName).send += 1;
+    }
+
+    public void incrementCall(String parentTypeName, String entityName) {
+      getCounter(parentTypeName, entityName).call += 1;
+    }
+
+    public void incrementFirstNull(String parentTypeName, String entityName) {
+      getCounter(parentTypeName, entityName).firstNull += 1;
+    }
+
+    public void incrementSkip(String parentTypeName, String entityName) {
+      getCounter(parentTypeName, entityName).skip += 1;
+    }
+
+    public void incrementException(String parentTypeName, String entityName) {
+      getCounter(parentTypeName, entityName).exception += 1;
+    }
+
+    public void incrementReject(String parentTypeName, String entityName) {
+      getCounter(parentTypeName, entityName).reject += 1;
+    }
+
+    public String summary() {
+      StringBuilder sb = new StringBuilder();
+      // header
+      sb.append("parentTypeName,entityName,receive,send,call,firstNull,skip,exception,reject").append("\n");
+      // values
+      java.util.Set<String> sortedParentTypes = new java.util.TreeSet<>(counters.keySet());
+      for (String parentType : sortedParentTypes) {
+        java.util.Set<String> sortedEntityNames = new java.util.TreeSet<>(counters.get(parentType).keySet());
+        for (String entityName : sortedEntityNames) {
+          {{evaluationCounterInnerClass}} count = getCounter(parentType, entityName);
+          java.util.StringJoiner sj = new java.util.StringJoiner(",", "", "\n");
+          sj.add(parentType)
+             .add(entityName)
+             .add(Integer.toString(count.receive))
+             .add(Integer.toString(count.send))
+             .add(Integer.toString(count.call))
+             .add(Integer.toString(count.firstNull))
+             .add(Integer.toString(count.skip))
+             .add(Integer.toString(count.exception))
+             .add(Integer.toString(count.reject))
+          ;
+          sb.append(sj);
+        }
+      }
+      return sb.toString();
+    }
+
+    private {{evaluationCounterInnerClass}} getCounter(String parentTypeName, String entityName) {
+      return counters.computeIfAbsent(parentTypeName, parentAbsent).computeIfAbsent(entityName, entityAbsent);
+    }
+    public void reset() {
+      counters = new java.util.HashMap<>();
+    }
+  }
+
+  class {{evaluationCounterInnerClass}} {
+    int receive = 0;
+    int send = 0;
+    int call = 0;
+    int firstNull = 0;
+    int skip = 0;
+    int exception = 0;
+    int reject = 0;
+  }
+{{/configEvaluationCounter}}
+}
diff --git a/ragconnect.base/src/main/resources/ListAspect.mustache b/ragconnect.base/src/main/resources/ListAspect.mustache
deleted file mode 100644
index e7ec88e5e579e7a7b50bbeb83d0779bc64203991..0000000000000000000000000000000000000000
--- a/ragconnect.base/src/main/resources/ListAspect.mustache
+++ /dev/null
@@ -1,37 +0,0 @@
-{{#hasTreeListEndpoints}}
-public void {{configJastAddList}}.serialize(com.fasterxml.jackson.core.JsonGenerator g) throws SerializationException {
-  try {
-    g.writeStartArray();
-    for (T child : this) {
-      child.serialize(g);
-    }
-    g.writeEndArray();
-  } catch (java.io.IOException e) {
-    throw new SerializationException("unable to serialize {{configJastAddList}}", e);
-  }
-}
-
-protected static <T extends ASTNode> void ASTNode.serializeJavaUtilList(
-        java.util.List<T> input, com.fasterxml.jackson.core.JsonGenerator g) throws SerializationException {
-  try {
-    g.writeStartArray();
-    for (T child : input) {
-      child.serialize(g);
-    }
-    g.writeEndArray();
-  } catch (java.io.IOException e) {
-    throw new SerializationException("unable to serialize list", e);
-  }
-}
-
-{{#typesForReceivingListEndpoints}}
-public static {{configJastAddList}}<{{Name}}> {{Name}}.deserializeList(com.fasterxml.jackson.databind.node.ArrayNode node) throws DeserializationException {
-  {{configJastAddList}}<{{Name}}> result = new {{configJastAddList}}<>();
-  for (java.util.Iterator<com.fasterxml.jackson.databind.JsonNode> it = node.elements(); it.hasNext();) {
-    com.fasterxml.jackson.databind.JsonNode element = it.next();
-    result.add(deserialize(element));
-  }
-  return result;
-}
-{{/typesForReceivingListEndpoints}}
-{{/hasTreeListEndpoints}}
diff --git a/ragconnect.base/src/main/resources/RagConnectObserver.mustache b/ragconnect.base/src/main/resources/RagConnectObserver.mustache
new file mode 100644
index 0000000000000000000000000000000000000000..24ed61c1a2ed6ceabec733b45f76a51eeae5f1e0
--- /dev/null
+++ b/ragconnect.base/src/main/resources/RagConnectObserver.mustache
@@ -0,0 +1,178 @@
+{{#configIncrementalOptionActive}}
+aspect RagConnectObserver {
+
+  class RagConnectObserver implements ASTState.Trace.Receiver {
+
+    class RagConnectObserverEntry {
+      final ASTNode node;
+      final String attributeString;
+      final boolean compareParams;
+      final Object params;
+      final Runnable attributeCall;
+      //final RagConnectToken connectToken;
+      final java.util.List<RagConnectToken> connectList = new java.util.ArrayList<>();
+
+      RagConnectObserverEntry(ASTNode node, String attributeString,
+                              boolean compareParams, Object params, Runnable attributeCall) {
+        this.node = node;
+        this.attributeString = attributeString;
+        this.compareParams = compareParams;
+        this.params = params;
+        this.attributeCall = attributeCall;
+      }
+
+      boolean baseMembersEqualTo(RagConnectObserverEntry other) {
+        return baseMembersEqualTo(other.node, other.attributeString, other.compareParams, other.params);
+      }
+
+      boolean baseMembersEqualTo(ASTNode otherNode, String otherAttributeString,
+          boolean forceCompareParams, Object otherParams) {
+        return this.node.equals(otherNode) &&
+            this.attributeString.equals(otherAttributeString) &&
+            //this.compareParams == otherCompareParams &&
+            (!(this.compareParams || forceCompareParams) || java.util.Objects.equals(this.params, otherParams));
+      }
+    }
+
+{{#configExperimentalJastAdd329}}
+    class RagConnectObserverStartEntry {
+      final ASTNode node;
+      final String attributeString;
+      final Object flushIncToken;
+      RagConnectObserverStartEntry(ASTNode node, String attributeString, Object flushIncToken) {
+        this.node = node;
+        this.attributeString = attributeString;
+        this.flushIncToken = flushIncToken;
+      }
+    }
+{{/configExperimentalJastAdd329}}
+
+    ASTState.Trace.Receiver oldReceiver;
+
+    java.util.List<RagConnectObserverEntry> observedNodes = new java.util.ArrayList<>();
+
+{{#configExperimentalJastAdd329}}
+    java.util.Set<RagConnectObserverEntry> entryQueue = new java.util.HashSet<>();
+    java.util.Deque<RagConnectObserverStartEntry> startEntries = new java.util.LinkedList<>();
+{{/configExperimentalJastAdd329}}
+
+    RagConnectObserver(ASTNode node) {
+      // set the receiver. potentially dangerous because overriding existing receiver!
+      oldReceiver = node.trace().getReceiver();
+      node.trace().setReceiver(this);
+    }
+
+    void add(RagConnectToken connectToken, ASTNode node, boolean compareParams, Object params,
+             Runnable attributeCall, String attributeString) {
+      {{#configLoggingEnabledForIncremental}}
+      {{logDebug}}("** observer add: {{log_}} on {{log_}}{{log_}}",
+        node, attributeString, (compareParams ? " (parameterized)" : ""));
+      {{/configLoggingEnabledForIncremental}}
+      // either add to an existing entry (with same node, attribute, params) or create new entry
+      boolean needNewEntry = true;
+      for (RagConnectObserverEntry entry : observedNodes) {
+        if (entry.baseMembersEqualTo(node, attributeString, true, params)) {
+          entry.connectList.add(connectToken);
+          needNewEntry = false;
+          break;
+        }
+      }
+      if (needNewEntry) {
+        RagConnectObserverEntry newEntry = new RagConnectObserverEntry(node, attributeString,
+            compareParams, params, attributeCall);
+        newEntry.connectList.add(connectToken);
+        observedNodes.add(newEntry);
+      }
+    }
+
+    void remove(RagConnectToken connectToken) {
+      java.util.List<RagConnectObserverEntry> entriesToDelete = new java.util.ArrayList<>();
+      for (RagConnectObserverEntry entry : observedNodes) {
+        entry.connectList.remove(connectToken);
+        if (entry.connectList.isEmpty()) {
+          entriesToDelete.add(entry);
+        }
+      }
+      observedNodes.removeAll(entriesToDelete);
+    }
+
+    @Override
+    public void accept(ASTState.Trace.Event event, ASTNode node, String attribute, Object params, Object value) {
+      oldReceiver.accept(event, node, attribute, params, value);
+{{#configExperimentalJastAdd329}}
+      // react to INC_FLUSH_START and remember entry
+      if (event == ASTState.Trace.Event.INC_FLUSH_START) {
+        {{#configLoggingEnabledForIncremental}}
+        {{logDebug}}("** observer start: {{log_}} on {{log_}}", node, attribute);
+        {{/configLoggingEnabledForIncremental}}
+        startEntries.addFirst(new RagConnectObserverStartEntry(node, attribute, value));
+        return;
+      }
+
+      // react to INC_FLUSH_END and process queued entries, if it matches start entry
+      if (event == ASTState.Trace.Event.INC_FLUSH_END) {
+        if (startEntries.isEmpty()) {
+          {{#configLoggingEnabledForIncremental}}
+          {{logDebug}}("** observer end without start! for {{log_}} on {{log_}}", node, attribute);
+          {{/configLoggingEnabledForIncremental}}
+          return;
+        }
+        RagConnectObserverStartEntry startEntry = startEntries.peekFirst();
+        if (node == startEntry.node &&
+            attribute == startEntry.attributeString &&
+            value == startEntry.flushIncToken) {
+          // create a copy of the queue to avoid entering this again causing an endless recursion
+          RagConnectObserverEntry[] entriesToProcess = entryQueue.toArray(new RagConnectObserverEntry[entryQueue.size()]);
+          entryQueue.clear();
+          startEntries.removeFirst();
+          {{#configLoggingEnabledForIncremental}}
+          {{logDebug}}("** observer process (entries: {{log_}}): {{log_}} on {{log_}}",
+            entriesToProcess.length, node, attribute);
+          {{/configLoggingEnabledForIncremental}}
+          for (RagConnectObserverEntry entry : entriesToProcess) {
+            entry.attributeCall.run();
+          }
+          return;
+        }
+      }
+
+{{/configExperimentalJastAdd329}}
+      // ignore all other events but INC_FLUSH_ATTR
+      if (event != ASTState.Trace.Event.INC_FLUSH_ATTR) {
+        return;
+      }
+
+      {{#configLoggingEnabledForIncremental}}
+      {{logDebug}}("** observer check INC_FLUSH_ATTR event: {{log_}} on {{log_}}", node, attribute);
+      {{/configLoggingEnabledForIncremental}}
+      // iterate through list, if matching pair. could maybe be more efficient.
+      for (RagConnectObserverEntry entry : observedNodes) {
+        if (entry.baseMembersEqualTo(node, attribute, false, params)) {
+          // hit. call the attribute/nta-token
+          {{#configLoggingEnabledForIncremental}}
+          {{logDebug}}("** observer hit: {{log_}} on {{log_}}", entry.node, entry.attributeString);
+          {{/configLoggingEnabledForIncremental}}
+{{#configExperimentalJastAdd329}}
+          entryQueue.add(entry);
+{{/configExperimentalJastAdd329}}
+{{^configExperimentalJastAdd329}}
+          entry.attributeCall.run();
+{{/configExperimentalJastAdd329}}
+        }
+      }
+    }
+  }
+
+  private static RagConnectObserver ASTNode.{{observerInstanceFieldName}};
+  RagConnectObserver ASTNode.{{observerInstanceSingletonMethodName}}() {
+    if ({{observerInstanceFieldName}} == null) {
+      // does not matter, which node is used to create the observer as ASTState/tracing is also static
+      {{observerInstanceFieldName}} = new RagConnectObserver(this);
+    }
+    return {{observerInstanceFieldName}};
+  }
+  void ASTNode.{{observerInstanceResetMethodName}}() {
+    {{observerInstanceFieldName}} = null;
+  }
+}
+{{/configIncrementalOptionActive}}
diff --git a/ragconnect.base/src/main/resources/mappingDefinition.mustache b/ragconnect.base/src/main/resources/mappingDefinition.mustache
index b6a035eb41b9d3f877b731293203de399399e6c0..392f44418129861ebc51b7c5e91c5c2721cf58e1 100644
--- a/ragconnect.base/src/main/resources/mappingDefinition.mustache
+++ b/ragconnect.base/src/main/resources/mappingDefinition.mustache
@@ -1,3 +1,46 @@
+{{#isUsed}}
+  {{#isTemplateDefaultMappingDefinition}}
+    {{#isSerializeListMapping}}
+public void {{configJastAddList}}.serialize(com.fasterxml.jackson.core.JsonGenerator g) throws SerializationException {
+  try {
+    g.writeStartArray();
+    for (T child : this) {
+      child.serialize(g);
+    }
+    g.writeEndArray();
+  } catch (java.io.IOException e) {
+    throw new SerializationException("unable to serialize {{configJastAddList}}", e);
+  }
+}
+    {{/isSerializeListMapping}}
+    {{#SerializeJavaUtilListMapping}}
+protected static <T extends ASTNode> void ASTNode.serializeJavaUtilList(
+        java.util.List<T> input, com.fasterxml.jackson.core.JsonGenerator g) throws SerializationException {
+  try {
+    g.writeStartArray();
+    for (T child : input) {
+      child.serialize(g);
+    }
+    g.writeEndArray();
+  } catch (java.io.IOException e) {
+    throw new SerializationException("unable to serialize list", e);
+  }
+}
+    {{/SerializeJavaUtilListMapping}}
+    {{#DeserializeListMapping}}
+public static {{configJastAddList}}<{{Name}}> {{Name}}.deserializeListOf{{Name}}(com.fasterxml.jackson.databind.node.ArrayNode node) throws DeserializationException {
+  {{configJastAddList}}<{{Name}}> result = new {{configJastAddList}}<>();
+  for (java.util.Iterator<com.fasterxml.jackson.databind.JsonNode> it = node.elements(); it.hasNext();) {
+    com.fasterxml.jackson.databind.JsonNode element = it.next();
+    result.add(deserialize(element));
+  }
+  return result;
+}
+    {{/DeserializeListMapping}}
+  {{/isTemplateDefaultMappingDefinition}}
+  {{^TemplateDefaultMappingDefinition}}
 protected {{{toType}}} ASTNode.{{methodName}}({{{fromType}}} {{FromVariableName}}) throws Exception {
   {{{Content}}}
 }
+  {{/TemplateDefaultMappingDefinition}}
+{{/isUsed}}
diff --git a/ragconnect.base/src/main/resources/ragconnect.mustache b/ragconnect.base/src/main/resources/ragconnect.mustache
index c428021bf1eab32bf8baac8585a3f801ba8ad6ad..5bd0166a46bb6f02be2df10a2bfd26b9cc63d508 100644
--- a/ragconnect.base/src/main/resources/ragconnect.mustache
+++ b/ragconnect.base/src/main/resources/ragconnect.mustache
@@ -1,13 +1,13 @@
 {{> handler}}
 aspect RagConnect {
-  {{#allEndpointDefinitionList}}
+  {{#allPortDefinitionList}}
     {{#Send}}
       {{> sendDefinition}}
     {{/Send}}
     {{^Send}}
       {{> receiveDefinition}}
     {{/Send}}
-  {{/allEndpointDefinitionList}}
+  {{/allPortDefinitionList}}
 
   class RagConnectRejectMappingException extends RuntimeException {}
   private static void ASTNode.reject() {
@@ -15,9 +15,7 @@ aspect RagConnect {
   }
 
   {{#allMappingDefinitions}}
-  {{#isUsed}}
   {{> mappingDefinition}}
-  {{/isUsed}}
   {{/allMappingDefinitions}}
 
   {{#allDependencyDefinitionList}}
@@ -28,9 +26,9 @@ aspect RagConnect {
   {{> tokenComponent}}
   {{/tokenComponentsThatNeedProxy}}
 
-  {{#typeDeclsOfContextFreeEndpointTargets}}
+  {{#typeDeclsOfContextFreePortTargets}}
   {{> typeDecl}}
-  {{/typeDeclsOfContextFreeEndpointTargets}}
+  {{/typeDeclsOfContextFreePortTargets}}
 
   {{! --- touchedTerminals ---}}
   {{#allTypeDecls}}
@@ -65,8 +63,6 @@ aspect RagConnect {
     return this;
   }
 
-  {{> ListAspect}}
-
   static void ASTNode.{{logConsoleOut}}(String message, Object... args) {
     System.out.println(String.format(message, args));
   }
@@ -99,276 +95,6 @@ aspect RagConnect {
   }
 }
 
-{{#configIncrementalOptionActive}}
-aspect RagConnectObserver {
-
-  class RagConnectObserver implements ASTState.Trace.Receiver {
-
-    class RagConnectObserverEntry {
-      final ASTNode node;
-      final String attributeString;
-      final boolean compareParams;
-      final Object params;
-      final Runnable attributeCall;
-      //final RagConnectToken connectToken;
-      final java.util.List<RagConnectToken> connectList = new java.util.ArrayList<>();
-
-      RagConnectObserverEntry(ASTNode node, String attributeString,
-                              boolean compareParams, Object params, Runnable attributeCall) {
-        this.node = node;
-        this.attributeString = attributeString;
-        this.compareParams = compareParams;
-        this.params = params;
-        this.attributeCall = attributeCall;
-      }
+{{> RagConnectObserver}}
 
-      boolean baseMembersEqualTo(RagConnectObserverEntry other) {
-        return baseMembersEqualTo(other.node, other.attributeString, other.compareParams, other.params);
-      }
-
-      boolean baseMembersEqualTo(ASTNode otherNode, String otherAttributeString,
-          boolean forceCompareParams, Object otherParams) {
-        return this.node.equals(otherNode) &&
-            this.attributeString.equals(otherAttributeString) &&
-            //this.compareParams == otherCompareParams &&
-            (!(this.compareParams || forceCompareParams) || java.util.Objects.equals(this.params, otherParams));
-      }
-    }
-
-{{#configExperimentalJastAdd329}}
-    class RagConnectObserverStartEntry {
-      final ASTNode node;
-      final String attributeString;
-      final Object flushIncToken;
-      RagConnectObserverStartEntry(ASTNode node, String attributeString, Object flushIncToken) {
-        this.node = node;
-        this.attributeString = attributeString;
-        this.flushIncToken = flushIncToken;
-      }
-    }
-{{/configExperimentalJastAdd329}}
-
-    ASTState.Trace.Receiver oldReceiver;
-
-    java.util.List<RagConnectObserverEntry> observedNodes = new java.util.ArrayList<>();
-
-{{#configExperimentalJastAdd329}}
-    java.util.Set<RagConnectObserverEntry> entryQueue = new java.util.HashSet<>();
-    java.util.Deque<RagConnectObserverStartEntry> startEntries = new java.util.LinkedList<>();
-{{/configExperimentalJastAdd329}}
-
-    RagConnectObserver(ASTNode node) {
-      // set the receiver. potentially dangerous because overriding existing receiver!
-      oldReceiver = node.trace().getReceiver();
-      node.trace().setReceiver(this);
-    }
-
-    void add(RagConnectToken connectToken, ASTNode node, boolean compareParams, Object params,
-             Runnable attributeCall, String attributeString) {
-      {{#configLoggingEnabledForIncremental}}
-      {{logDebug}}("** observer add: {{log_}} on {{log_}}{{log_}}",
-        node, attributeString, (compareParams ? " (parameterized)" : ""));
-      {{/configLoggingEnabledForIncremental}}
-      // either add to an existing entry (with same node, attribute, params) or create new entry
-      boolean needNewEntry = true;
-      for (RagConnectObserverEntry entry : observedNodes) {
-        if (entry.baseMembersEqualTo(node, attributeString, true, params)) {
-          entry.connectList.add(connectToken);
-          needNewEntry = false;
-          break;
-        }
-      }
-      if (needNewEntry) {
-        RagConnectObserverEntry newEntry = new RagConnectObserverEntry(node, attributeString,
-            compareParams, params, attributeCall);
-        newEntry.connectList.add(connectToken);
-        observedNodes.add(newEntry);
-      }
-    }
-
-    void remove(RagConnectToken connectToken) {
-      java.util.List<RagConnectObserverEntry> entriesToDelete = new java.util.ArrayList<>();
-      for (RagConnectObserverEntry entry : observedNodes) {
-        entry.connectList.remove(connectToken);
-        if (entry.connectList.isEmpty()) {
-          entriesToDelete.add(entry);
-        }
-      }
-      observedNodes.removeAll(entriesToDelete);
-    }
-
-    @Override
-    public void accept(ASTState.Trace.Event event, ASTNode node, String attribute, Object params, Object value) {
-      oldReceiver.accept(event, node, attribute, params, value);
-{{#configExperimentalJastAdd329}}
-      // react to INC_FLUSH_START and remember entry
-      if (event == ASTState.Trace.Event.INC_FLUSH_START) {
-        {{#configLoggingEnabledForIncremental}}
-        {{logDebug}}("** observer start: {{log_}} on {{log_}}", node, attribute);
-        {{/configLoggingEnabledForIncremental}}
-        startEntries.addFirst(new RagConnectObserverStartEntry(node, attribute, value));
-        return;
-      }
-
-      // react to INC_FLUSH_END and process queued entries, if it matches start entry
-      if (event == ASTState.Trace.Event.INC_FLUSH_END) {
-        if (startEntries.isEmpty()) {
-          {{#configLoggingEnabledForIncremental}}
-          {{logDebug}}("** observer end without start! for {{log_}} on {{log_}}", node, attribute);
-          {{/configLoggingEnabledForIncremental}}
-          return;
-        }
-        RagConnectObserverStartEntry startEntry = startEntries.peekFirst();
-        if (node == startEntry.node &&
-            attribute == startEntry.attributeString &&
-            value == startEntry.flushIncToken) {
-          // create a copy of the queue to avoid entering this again causing an endless recursion
-          RagConnectObserverEntry[] entriesToProcess = entryQueue.toArray(new RagConnectObserverEntry[entryQueue.size()]);
-          entryQueue.clear();
-          startEntries.removeFirst();
-          {{#configLoggingEnabledForIncremental}}
-          {{logDebug}}("** observer process (entries: {{log_}}): {{log_}} on {{log_}}",
-            entriesToProcess.length, node, attribute);
-          {{/configLoggingEnabledForIncremental}}
-          for (RagConnectObserverEntry entry : entriesToProcess) {
-            entry.attributeCall.run();
-          }
-          return;
-        }
-      }
-
-{{/configExperimentalJastAdd329}}
-      // ignore all other events but INC_FLUSH_ATTR
-      if (event != ASTState.Trace.Event.INC_FLUSH_ATTR) {
-        return;
-      }
-
-      {{#configLoggingEnabledForIncremental}}
-      {{logDebug}}("** observer check INC_FLUSH_ATTR event: {{log_}} on {{log_}}", node, attribute);
-      {{/configLoggingEnabledForIncremental}}
-      // iterate through list, if matching pair. could maybe be more efficient.
-      for (RagConnectObserverEntry entry : observedNodes) {
-        if (entry.baseMembersEqualTo(node, attribute, false, params)) {
-          // hit. call the attribute/nta-token
-          {{#configLoggingEnabledForIncremental}}
-          {{logDebug}}("** observer hit: {{log_}} on {{log_}}", entry.node, entry.attributeString);
-          {{/configLoggingEnabledForIncremental}}
-{{#configExperimentalJastAdd329}}
-          entryQueue.add(entry);
-{{/configExperimentalJastAdd329}}
-{{^configExperimentalJastAdd329}}
-          entry.attributeCall.run();
-{{/configExperimentalJastAdd329}}
-        }
-      }
-    }
-  }
-
-  private static RagConnectObserver ASTNode.{{observerInstanceFieldName}};
-  RagConnectObserver ASTNode.{{observerInstanceSingletonMethodName}}() {
-    if ({{observerInstanceFieldName}} == null) {
-      // does not matter, which node is used to create the observer as ASTState/tracing is also static
-      {{observerInstanceFieldName}} = new RagConnectObserver(this);
-    }
-    return {{observerInstanceFieldName}};
-  }
-  void ASTNode.{{observerInstanceResetMethodName}}() {
-    {{observerInstanceFieldName}} = null;
-  }
-}
-{{/configIncrementalOptionActive}}
-
-aspect EvaluationCounter {
-  public String ASTNode.{{evaluationCounterSummaryMethodName}}() {
-  {{#configEvaluationCounter}}
-    return {{evaluationCounterVariable}}.summary();
-  {{/configEvaluationCounter}}
-  {{^configEvaluationCounter}}
-    String message = "Option --evaluationCounter was not set. No Summary available";
-    {{logWarn}}(message);
-    return message;
-  {{/configEvaluationCounter}}
-  }
-{{#configEvaluationCounter}}
-  static EvaluationCounter ASTNode.{{evaluationCounterVariable}} = new EvaluationCounter();
-
-  public class EvaluationCounter {
-    private java.util.Map<String, java.util.Map<String, {{evaluationCounterInnerClass}}>> counters = new java.util.HashMap<>();
-    private final java.util.function.Function<? super String, ? extends java.util.Map<String, {{evaluationCounterInnerClass}}>> parentAbsent = key -> {
-      return new java.util.HashMap<>();
-    };
-    private final java.util.function.Function<? super String, ? extends {{evaluationCounterInnerClass}}> entityAbsent = key -> {
-      return new {{evaluationCounterInnerClass}}();
-    };
-
-    public void incrementReceive(String parentTypeName, String entityName) {
-      getCounter(parentTypeName, entityName).receive += 1;
-    }
-
-    public void incrementSend(String parentTypeName, String entityName) {
-      getCounter(parentTypeName, entityName).send += 1;
-    }
-
-    public void incrementCall(String parentTypeName, String entityName) {
-      getCounter(parentTypeName, entityName).call += 1;
-    }
-
-    public void incrementFirstNull(String parentTypeName, String entityName) {
-      getCounter(parentTypeName, entityName).firstNull += 1;
-    }
-
-    public void incrementSkip(String parentTypeName, String entityName) {
-      getCounter(parentTypeName, entityName).skip += 1;
-    }
-
-    public void incrementException(String parentTypeName, String entityName) {
-      getCounter(parentTypeName, entityName).exception += 1;
-    }
-
-    public void incrementReject(String parentTypeName, String entityName) {
-      getCounter(parentTypeName, entityName).reject += 1;
-    }
-
-    public String summary() {
-      StringBuilder sb = new StringBuilder();
-      // header
-      sb.append("parentTypeName,entityName,receive,send,call,firstNull,skip,exception,reject").append("\n");
-      // values
-      java.util.Set<String> sortedParentTypes = new java.util.TreeSet<>(counters.keySet());
-      for (String parentType : sortedParentTypes) {
-        java.util.Set<String> sortedEntityNames = new java.util.TreeSet<>(counters.get(parentType).keySet());
-        for (String entityName : sortedEntityNames) {
-          {{evaluationCounterInnerClass}} count = getCounter(parentType, entityName);
-          java.util.StringJoiner sj = new java.util.StringJoiner(",", "", "\n");
-          sj.add(parentType)
-             .add(entityName)
-             .add(Integer.toString(count.receive))
-             .add(Integer.toString(count.send))
-             .add(Integer.toString(count.call))
-             .add(Integer.toString(count.firstNull))
-             .add(Integer.toString(count.skip))
-             .add(Integer.toString(count.exception))
-             .add(Integer.toString(count.reject))
-          ;
-          sb.append(sj);
-        }
-      }
-      return sb.toString();
-    }
-
-    private {{evaluationCounterInnerClass}} getCounter(String parentTypeName, String entityName) {
-      return counters.computeIfAbsent(parentTypeName, parentAbsent).computeIfAbsent(entityName, entityAbsent);
-    }
-  }
-
-  class {{evaluationCounterInnerClass}} {
-    int receive = 0;
-    int send = 0;
-    int call = 0;
-    int firstNull = 0;
-    int skip = 0;
-    int exception = 0;
-    int reject = 0;
-  }
-{{/configEvaluationCounter}}
-}
+{{> EvaluationCounter}}
diff --git a/ragconnect.base/src/main/resources/receiveDefinition.mustache b/ragconnect.base/src/main/resources/receiveDefinition.mustache
index 81b0ecbbd96aa40155ec8f396549a4af2160b64e..b2e05ce6b2ce246b5225b98eecc7779bf3b0cfa0 100644
--- a/ragconnect.base/src/main/resources/receiveDefinition.mustache
+++ b/ragconnect.base/src/main/resources/receiveDefinition.mustache
@@ -12,7 +12,7 @@ private int {{parentTypeName}}.{{resolveInListMethodName}}(String topic) {
 {{/typeIsList}}
 
 /**
- * Connects the receive endpoint {{entityName}}.
+ * Connects the receive port {{entityName}}.
 {{#typeIsList}}{{#WithAdd}}
  * New values are appended to the end of the list.
 {{/WithAdd}}{{/typeIsList}}
@@ -25,14 +25,11 @@ private int {{parentTypeName}}.{{resolveInListMethodName}}(String topic) {
  */
 public boolean {{parentTypeName}}.{{connectMethodName}}(String {{connectParameterName}}{{#typeIsList}}{{#IndexBasedListAccess}}{{^WithAdd}}, int index{{/WithAdd}}{{/IndexBasedListAccess}}{{/typeIsList}}) throws java.io.IOException {
   java.util.function.BiConsumer<String, byte[]> consumer = (topic, message) -> {
-    {{#configEvaluationCounter}}
-      {{evaluationCounterVariable}}.incrementReceive("{{parentTypeName}}", "{{entityName}}");
-    {{/configEvaluationCounter}}
     {{> mappingApplication}}
 {{#configLoggingEnabledForReads}}
     {{logDebug}}("[Receive] {{log_}} -> {{entityName}} = {{log_}}", {{connectParameterName}}, {{lastResult}});
 {{/configLoggingEnabledForReads}}
-{{#hasTypeEndpointTarget}}
+{{#hasTypePortTarget}}
     {{lastResult}}.treeResolveAll();
  {{#typeIsList}}
   {{^IndexBasedListAccess}}
@@ -56,17 +53,20 @@ public boolean {{parentTypeName}}.{{connectMethodName}}(String {{connectParamete
  {{^typeIsList}}
     set{{entityName}}({{lastResult}});
  {{/typeIsList}}
-{{/hasTypeEndpointTarget}}
-{{^hasTypeEndpointTarget}}
+{{/hasTypePortTarget}}
+{{^hasTypePortTarget}}
     set{{entityName}}({{lastResult}});
-{{/hasTypeEndpointTarget}}
+{{/hasTypePortTarget}}
+{{#configEvaluationCounter}}
+    {{evaluationCounterVariable}}.incrementReceive("{{parentTypeName}}", "{{entityName}}");
+{{/configEvaluationCounter}}
   };
   return {{internalConnectMethodName}}({{connectParameterName}}, consumer);
 }
 
 {{#typeIsList}}{{#IndexBasedListAccess}}{{^WithAdd}}
 /**
- * Connects the receive endpoint {{entityName}} using a "wildcard" URI (if supported by the chosen protocol).
+ * Connects the receive port {{entityName}} using a "wildcard" URI (if supported by the chosen protocol).
  * @param {{connectParameterName}} string describing protocol and path as an URI
  * @return true if connect was successful, false otherwise
  * @throws java.io.IOException if connect failed
diff --git a/ragconnect.base/src/main/resources/sendDefinition.mustache b/ragconnect.base/src/main/resources/sendDefinition.mustache
index 2b8a6eb3536e81d9621f56866a375462562cf299..9c7dac4fc7a9bccc449fcdfced102c4ba865fbb2 100644
--- a/ragconnect.base/src/main/resources/sendDefinition.mustache
+++ b/ragconnect.base/src/main/resources/sendDefinition.mustache
@@ -159,21 +159,21 @@ protected void {{parentTypeName}}.{{writeMethodName}}({{#IndexBasedListAccess}}i
   {{senderName}}.run({{#IndexBasedListAccess}}index, {{/IndexBasedListAccess}}token);
 }
 
-{{#needForwardingNTA}}
-syn {{{forwardingNTA_Type}}} {{parentTypeName}}.{{forwardingNTA_Name}}({{#IndexBasedListAccess}}int index{{/IndexBasedListAccess}}) {
-{{#relationEndpointWithListRole}}
+{{#needForwarding}}
+syn {{{forwardingType}}} {{parentTypeName}}.{{forwardingName}}({{#IndexBasedListAccess}}int index{{/IndexBasedListAccess}}) {
+{{#relationPortWithListRole}}
 //  for (var element : {{realGetterMethodCall}}) {
 //    element.{{touchedTerminalsMethodName}}();
 //  }
   {{realGetterMethodCall}}.stream().forEach(element -> element.{{touchedTerminalsMethodName}}());
   return {{realGetterMethodCall}};
-{{/relationEndpointWithListRole}}
-{{^relationEndpointWithListRole}}
-  {{{forwardingNTA_Type}}} result = {{realGetterMethodCall}};
+{{/relationPortWithListRole}}
+{{^relationPortWithListRole}}
+  {{{forwardingType}}} result = {{realGetterMethodCall}};
   if (result == null) {
     return null;
   }
   return result.{{touchedTerminalsMethodName}}();
-{{/relationEndpointWithListRole}}
+{{/relationPortWithListRole}}
 }
-{{/needForwardingNTA}}
+{{/needForwarding}}
diff --git a/ragconnect.base/src/main/resources/tokenComponent.mustache b/ragconnect.base/src/main/resources/tokenComponent.mustache
index 3b0ae62080f39f17a329a34caeb410da66365f5d..12238f4428bf2ef79951015425cf45285cca624b 100644
--- a/ragconnect.base/src/main/resources/tokenComponent.mustache
+++ b/ragconnect.base/src/main/resources/tokenComponent.mustache
@@ -2,11 +2,11 @@ public {{parentTypeName}} {{parentTypeName}}.set{{Name}}({{javaType}} value) {
   set{{internalName}}(value);
   {{#DependencySourceDefinitions}}
   for ({{targetParentTypeName}} target : get{{internalRelationPrefix}}TargetList()) {
-    {{#targetEndpointDefinition}}
+    {{#targetPortDefinition}}
     if (target.{{updateMethodName}}()) {
       target.{{writeMethodName}}();
     }
-    {{/targetEndpointDefinition}}
+    {{/targetPortDefinition}}
   }
   {{/DependencySourceDefinitions}}
   {{#normalTokenSendDef}}
diff --git a/ragconnect.base/src/main/resources/typeDecl.mustache b/ragconnect.base/src/main/resources/typeDecl.mustache
index 1f674da36852786afab198b086b39b3cbf543b7b..95cb3ee84d3df8f86111efbbfcc2bf61a06dab6e 100644
--- a/ragconnect.base/src/main/resources/typeDecl.mustache
+++ b/ragconnect.base/src/main/resources/typeDecl.mustache
@@ -14,11 +14,11 @@ inh int {{Name}}._ragconnect_myIndexInList();
   {{/isListComponent}}
 {{/occurencesInProductionRules}}
 
-{{#ContextFreeTypeEndpointTargets}}{{#containingEndpointDefinition}}
+{{#ContextFreeTypePortTargets}}{{#containingPortDefinition}}
 public boolean {{Name}}.{{connectMethodName}}(String {{connectParameterName}}{{#Send}}, boolean writeCurrentValue{{/Send}}) throws java.io.IOException {
   switch (_ragconnect_myContext()) {
   {{#occurencesInProductionRules}}
-    {{!TODO only using "connectMethodName" is not correct, since the actual name might be different, e.g., if both send and receive are defined. need a reference to the actual endpoint-definition here}}
+    {{!TODO only using "connectMethodName" is not correct, since the actual name might be different, e.g., if both send and receive are defined. need a reference to the actual port-definition here}}
     {{^isListComponent}}
     case "{{parentTypeName}}.{{Name}}": return (({{parentTypeName}}) _ragconnect_myParent()).{{connectMethodName}}{{Name}}({{connectParameterName}}{{#Send}}, writeCurrentValue{{/Send}});
     {{/isListComponent}}
@@ -42,4 +42,4 @@ public boolean {{Name}}.{{disconnectMethodName}}(String {{connectParameterName}}
       return false;
   }
 }
-{{/containingEndpointDefinition}}{{/ContextFreeTypeEndpointTargets}}
+{{/containingPortDefinition}}{{/ContextFreeTypePortTargets}}
diff --git a/ragconnect.tests/build.gradle b/ragconnect.tests/build.gradle
index 0b5fc98a9dae3b488e5f9b1a6a3cfe5334856472..c5e6f13b574d6e93ee635a265c6f0cfe9b718793 100644
--- a/ragconnect.tests/build.gradle
+++ b/ragconnect.tests/build.gradle
@@ -47,7 +47,7 @@ dependencies {
     testImplementation project(':ragconnect.base')
 
     implementation group: 'org.jastadd', name: 'jastadd2', version: '2.3.5-dresden-7'
-    relast group: 'org.jastadd', name: 'relast', version: "0.3.0-137"
+    relast group: 'org.jastadd', name: 'relast', version: "${relast_version}"
 
     testImplementation group: 'org.junit.jupiter', name: 'junit-jupiter-api', version: '5.9.0'
     testRuntimeOnly group: 'org.junit.jupiter', name: 'junit-jupiter-engine', version: '5.9.0'
@@ -64,7 +64,7 @@ dependencies {
 
     // rest and client
     testImplementation group: 'com.sparkjava', name: 'spark-core', version: '2.9.3'
-    testImplementation group: 'org.apache.logging.log4j', name: 'log4j-slf4j-impl', version: '2.17.1'
+    testImplementation group: 'org.apache.logging.log4j', name: 'log4j-slf4j-impl', version: "${log4j_version}"
     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'
@@ -72,7 +72,7 @@ dependencies {
     testImplementation group: 'net.sf.beaver', name: 'beaver-rt', version: '0.9.11'
     api group: 'com.google.protobuf', name: 'protobuf-java', version: '3.0.0'
 
-    implementation group: 'de.tudresden.inf.st', name: 'dumpAst', version: "1.0.2-68"
+    implementation group: 'de.tudresden.inf.st', name: 'dumpAst', version: "${dumpAst_version}"
 }
 
 // --- Preprocessors ---
diff --git a/ragconnect.tests/src/test/01-input/errors/Errors.relast b/ragconnect.tests/src/test/01-input/errors/Errors.relast
index dd3fc0faf5bea8d4718f4cc429605758fb2bd972..85e14e3a44d5ea950498f6d4fa092d2aa4fe4be0 100644
--- a/ragconnect.tests/src/test/01-input/errors/Errors.relast
+++ b/ragconnect.tests/src/test/01-input/errors/Errors.relast
@@ -14,5 +14,5 @@ D ::= <SourceNonExistingTarget>
       <SourceDoubledValue> /<TargetDoubledValue>/
       MyList:D* ;
 
-// context-free endpoints
+// context-free ports
 E ::= ;
diff --git a/ragconnect.tests/src/test/01-input/errors/Part.expected b/ragconnect.tests/src/test/01-input/errors/Part.expected
index d5e2f0db2b1f5a20af7e4ceb50741c6d8e4dce45..7c212f1fff58e2d1e15f5090485c807ae376f6ef 100644
--- a/ragconnect.tests/src/test/01-input/errors/Part.expected
+++ b/ragconnect.tests/src/test/01-input/errors/Part.expected
@@ -1,11 +1,11 @@
-Part1.connect Line 3, column 1: Endpoint definition already defined for B.DoubledValue
-Part1.connect Line 4, column 1: Endpoint definition already defined for B.DoubledValue
+Part1.connect Line 3, column 1: Port definition already defined for B.DoubledValue
+Part1.connect Line 4, column 1: Port definition already defined for B.DoubledValue
 Part1.connect Line 10, column 1: Receiving target token must not be an NTA token!
 Part1.connect Line 13, column 1: No suitable default mapping found for type java.util.List<String>
 Part1.connect Line 16, column 1: to-type of last mapping (java.util.List<String>) not assignable to type of the token (String)!
 Part1.connect Line 19, column 1: to-type of last mapping (int) not assignable to type of the token (String)!
-Part1.connect Line 26, column 6: Could not resolve endpoint target C.ErrorNotResolved
-Part2.connect Line 5, column 1: Endpoint definition already defined for C.DoubledValue
-Part2.connect Line 6, column 1: Endpoint definition already defined for C.DoubledValue
+Part1.connect Line 26, column 6: Could not resolve port target C.ErrorNotResolved
+Part2.connect Line 5, column 1: Port definition already defined for C.DoubledValue
+Part2.connect Line 6, column 1: Port definition already defined for C.DoubledValue
 Part2.connect Line 17, column 1: The name of a dependency definition must not be equal to a list-node on the source
 Part2.connect Line 22, column 1: Dependency definition already defined for D with name DoubledValue
diff --git a/ragconnect.tests/src/test/01-input/errors/Standard.connect b/ragconnect.tests/src/test/01-input/errors/Standard.connect
index fb2c7cb2400d1b8ca44903a9aeb3bb0bfcf28537..68a31b844473634bfaca48d04dd177ce9c2133d3 100644
--- a/ragconnect.tests/src/test/01-input/errors/Standard.connect
+++ b/ragconnect.tests/src/test/01-input/errors/Standard.connect
@@ -18,10 +18,10 @@ receive B.ErrorTypeOfLastMapping using StringToString, StringToList ;
 // Error: types of mappings must match (modulo inheritance)
 receive B.ErrorTypeMismatch using StringToList, IntToInt ;
 
-// Error: Context-Free endpoint not allowed for root nodes
+// Error: Context-Free port not allowed for root nodes
 receive A;
 
-// Error: Clash with implied endpoint definition of context-free endpoint
+// Error: Clash with implied port definition of context-free port
 receive E;
 receive A.E;
 
@@ -63,7 +63,7 @@ send D.TargetDoubledValue;
 // non-existence of attributes is not checked by RagConnect
 send A.nonExistingAttribute(int);
 
-// Already defined endpoints for attributes will be reported, however
+// Already defined ports for attributes will be reported, however
 send A.nonExistingAttribute(int);
 
 // mappings are not checked, here string would not match
diff --git a/ragconnect.tests/src/test/01-input/errors/Standard.expected b/ragconnect.tests/src/test/01-input/errors/Standard.expected
index 1f2b02da133cc26bc9660b466112cb73421ab529..c8ce47b509c4f84f1dc36d2465b0910d506cd5fc 100644
--- a/ragconnect.tests/src/test/01-input/errors/Standard.expected
+++ b/ragconnect.tests/src/test/01-input/errors/Standard.expected
@@ -1,15 +1,15 @@
-Standard.connect Line 3, column 1: Endpoint definition already defined for B.DoubledValue
-Standard.connect Line 4, column 1: Endpoint definition already defined for B.DoubledValue
+Standard.connect Line 3, column 1: Port definition already defined for B.DoubledValue
+Standard.connect Line 4, column 1: Port definition already defined for B.DoubledValue
 Standard.connect Line 10, column 1: Receiving target token must not be an NTA token!
 Standard.connect Line 13, column 1: No suitable default mapping found for type java.util.List<String>
 Standard.connect Line 16, column 1: to-type of last mapping (java.util.List<String>) not assignable to type of the token (String)!
 Standard.connect Line 19, column 1: to-type of last mapping (int) not assignable to type of the token (String)!
-Standard.connect Line 22, column 9: Context-Free endpoint not allowed for root node A!
-Standard.connect Line 26, column 1: Clash with implied, indexed endpoint definition of context-free endpoint in line 25!
-Standard.connect Line 30, column 9: Could not resolve endpoint target C.NonExisting
-Standard.connect Line 43, column 1: Endpoint definition already defined for C.DoubledValue
-Standard.connect Line 44, column 1: Endpoint definition already defined for C.DoubledValue
+Standard.connect Line 22, column 9: Context-Free port not allowed for root node A!
+Standard.connect Line 26, column 1: Clash with implied, indexed port definition of context-free port in line 25!
+Standard.connect Line 30, column 9: Could not resolve port target C.NonExisting
+Standard.connect Line 43, column 1: Port definition already defined for C.DoubledValue
+Standard.connect Line 44, column 1: Port definition already defined for C.DoubledValue
 Standard.connect Line 55, column 1: The name of a dependency definition must not be equal to a list-node on the source
 Standard.connect Line 60, column 1: Dependency definition already defined for D with name DoubledValue
-Standard.connect Line 64, column 1: Endpoint definition already defined for A.nonExistingAttribute
-Standard.connect Line 67, column 1: Endpoint definition already defined for A.nonExistingAttribute
+Standard.connect Line 64, column 1: Port definition already defined for A.nonExistingAttribute
+Standard.connect Line 67, column 1: Port definition already defined for A.nonExistingAttribute
diff --git a/ragconnect.tests/src/test/01-input/forwarding/README.md b/ragconnect.tests/src/test/01-input/forwarding/README.md
index 522815706491182dbd0766ee6b2e83e6a5e5d64a..9b12df3139ec9194bfab45996b823fd0578d1b12 100644
--- a/ragconnect.tests/src/test/01-input/forwarding/README.md
+++ b/ragconnect.tests/src/test/01-input/forwarding/README.md
@@ -1,6 +1,6 @@
 # Forwarding
 
 Idea: Use send definitions targeting only non-NTAs (thus, requiring the creation of implicit NTAs).
-Also test context-free endpoint definitions targeting non-NTAs to test compatibility of those two features.
+Also test context-free port definitions targeting non-NTAs to test compatibility of those two features.
 
-Note: When a type occurs in a list, the context-free endpoint definition will force an (implicit) indexed send for that context.
+Note: When a type occurs in a list, the context-free port definition will force an (implicit) indexed send for that context.
diff --git a/ragconnect.tests/src/test/01-input/incremental/Test.connect b/ragconnect.tests/src/test/01-input/incremental/Test.connect
index 8135be4e3afc9801b45d858ce9bda9016351e4a4..070f4e1b159a5537d263067d617a2cab22303fb9 100644
--- a/ragconnect.tests/src/test/01-input/incremental/Test.connect
+++ b/ragconnect.tests/src/test/01-input/incremental/Test.connect
@@ -1,4 +1,4 @@
-// endpoint definitions
+// port definitions
 receive A.Input ;
 send A.OutputOnA ;
 send B.OutputOnB using Transformation ;
diff --git a/ragconnect.tests/src/test/01-input/singleList/README.md b/ragconnect.tests/src/test/01-input/singleList/README.md
index 5d18043d85e96ec4b44cc6a2c3803b8f215d2edc..8c062b6d8c9fc58a2b21a070559938f75e63cbd0 100644
--- a/ragconnect.tests/src/test/01-input/singleList/README.md
+++ b/ragconnect.tests/src/test/01-input/singleList/README.md
@@ -22,26 +22,26 @@ A _n_ = Input _n_ + _n_, e.g., A1 = Input1 + 1 and A3 = Input3 + 3
 
 ## Execution-Trace (SendInitialValue)
 
-| Input | [A1,A2,A3,A4,IO] | # | A* | UsingWcA | WithAddA | UsingWcWithAddA:A |
-|---|---|---|---|---|---|---|
-| * | [1,2,3,4,0] | 5 | [1,2,3,4,0] | [1,2,3,4,0] | [1,2,3,4,0] | [1,2,3,4,0] |
-| I1:1 | [2,2,3,4,0] | 6 | [2,2,3,4,0] | [2,2,3,4,0] | [1,2,3,4,0,2] | [1,2,3,4,0,2] |
-| I1:1 | [2,2,3,4,0] | 6 | [2,2,3,4,0] | [2,2,3,4,0] | [1,2,3,4,0,2] | [1,2,3,4,0,2] |
-| I1:2 | [3,2,3,4,0] | 7 | [3,2,3,4,0] | [3,2,3,4,0] | [1,2,3,4,0,2,3] | [1,2,3,4,0,2,3] |
-| IO:5 | [3,2,3,4,5] | 8 | [3,2,3,4,5] | [3,2,3,4,5] | [1,2,3,4,0,2,3,5] | [1,2,3,4,0,2,3,5]
-| I3:4 | [3,2,7,4,5] | 9 | [3,2,7,4,5] | [3,2,7,4,5] | [1,2,3,4,0,2,3,5,7] | [1,2,3,4,0,2,3,5,7] |
+| Input | [A1,A2,A3,A4,IO] | #   | A*          | UsingWcA    | WithAddA            | UsingWcWithAddA:A   |
+|-------|------------------|-----|-------------|-------------|---------------------|---------------------|
+| *     | [1,2,3,4,0]      | 5   | [1,2,3,4,0] | [1,2,3,4,0] | [1,2,3,4,0]         | [1,2,3,4,0]         |
+| I1:1  | [2,2,3,4,0]      | 6   | [2,2,3,4,0] | [2,2,3,4,0] | [1,2,3,4,0,2]       | [1,2,3,4,0,2]       |
+| I1:1  | [2,2,3,4,0]      | 6   | [2,2,3,4,0] | [2,2,3,4,0] | [1,2,3,4,0,2]       | [1,2,3,4,0,2]       |
+| I1:2  | [3,2,3,4,0]      | 7   | [3,2,3,4,0] | [3,2,3,4,0] | [1,2,3,4,0,2,3]     | [1,2,3,4,0,2,3]     |
+| IO:5  | [3,2,3,4,5]      | 8   | [3,2,3,4,5] | [3,2,3,4,5] | [1,2,3,4,0,2,3,5]   | [1,2,3,4,0,2,3,5]   |
+| I3:4  | [3,2,7,4,5]      | 9   | [3,2,7,4,5] | [3,2,7,4,5] | [1,2,3,4,0,2,3,5,7] | [1,2,3,4,0,2,3,5,7] |
 
 *: (1:0, 2:0, 3:0, 4:0, 5:0)
 
 ## Execution-Trace (OnlyUpdate)
 
-| Input | [A1,A2,A3,A4,IO] | # | A* | UsingWcA | WithAddA | UsingWcWithAddA:A |
-|---|---|---|---|---|---|---|
-| * | [-,-,-,-,-] | 0 | [0,0,0,0,0] | [] | [] | [] |
-| I1:1 | [2,-,-,-,-] | 1 | [2,0,0,0,0] | [2] | [2] | [2] |
-| I1:1 | [2,-,-,-,-] | 1 | [2,0,0,0,0] | [2] | [2] | [2] |
-| I1:2 | [3,-,-,-,-] | 2 | [3,0,0,0,0] | [3] | [2,3] | [2,3] |
-| IO:5 | [2,-,-,-,5] | 3 | [3,0,0,0,5] | [3,5] | [2,3,5] | [2,3,5]
-| I3:4 | [2,-,7,-,5] | 4 | [3,0,7,0,5] | [3,5,7] | [2,3,5,7] | [2,3,5,7] |
+| Input | [A1,A2,A3,A4,IO] | #   | A*          | UsingWcA | WithAddA  | UsingWcWithAddA:A |
+|-------|------------------|-----|-------------|----------|-----------|-------------------|
+| *     | [-,-,-,-,-]      | 0   | [0,0,0,0,0] | []       | []        | []                |
+| I1:1  | [2,-,-,-,-]      | 1   | [2,0,0,0,0] | [2]      | [2]       | [2]               |
+| I1:1  | [2,-,-,-,-]      | 1   | [2,0,0,0,0] | [2]      | [2]       | [2]               |
+| I1:2  | [3,-,-,-,-]      | 2   | [3,0,0,0,0] | [3]      | [2,3]     | [2,3]             |
+| IO:5  | [2,-,-,-,5]      | 3   | [3,0,0,0,5] | [3,5]    | [2,3,5]   | [2,3,5]           |
+| I3:4  | [2,-,7,-,5]      | 4   | [3,0,7,0,5] | [3,5,7]  | [2,3,5,7] | [2,3,5,7]         |
 
 *: (1:0, 2:0, 3:0, 4:0, 5:0)
diff --git a/ragconnect.tests/src/test/01-input/tutorial/Test.connect b/ragconnect.tests/src/test/01-input/tutorial/Test.connect
index c6bdc7b3c4f5dcf661ce2ce757301e42eb39a1a7..f1f3af2d719924b5c5ddaccfd10771c5a2240c0f 100644
--- a/ragconnect.tests/src/test/01-input/tutorial/Test.connect
+++ b/ragconnect.tests/src/test/01-input/tutorial/Test.connect
@@ -1,4 +1,4 @@
-// endpoint definitions
+// port definitions
 receive A.Input ;
 send A.OutputOnA ;
 send B.OutputOnB using Transformation ;
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 b8f31b89aced9f362f6b6961fd476ccea6ab7660..9720f2f943ec1914f5efc813d5a24b3eec3e2a23 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
@@ -2,9 +2,8 @@ package org.jastadd.ragconnect.tests;
 
 import defaultOnlyRead.ast.MqttHandler;
 import io.github.artsok.RepeatedIfExceptionsTest;
+import org.jastadd.ragconnect.tests.utils.TestUtils;
 import org.junit.jupiter.api.*;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 
 import java.io.IOException;
 import java.util.concurrent.TimeUnit;
diff --git a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/AttributeTest.java b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/AttributeTest.java
index 20ff9f3f791c35b47ac24ead95990076edd41a93..7073e6d4f05d35ab7d42d84b0d1d31072b92efc2 100644
--- a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/AttributeTest.java
+++ b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/AttributeTest.java
@@ -1,19 +1,23 @@
 package org.jastadd.ragconnect.tests;
 
 import attributeInc.ast.*;
+import de.tudresden.inf.st.jastadd.dumpAst.ast.Dumper;
+import org.jastadd.ragconnect.tests.utils.TestUtils;
 import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.Tag;
 
 import java.io.IOException;
+import java.nio.file.Paths;
 import java.util.Objects;
 import java.util.concurrent.Callable;
 import java.util.concurrent.TimeUnit;
 import java.util.function.Predicate;
 import java.util.function.Supplier;
+import org.jastadd.ragconnect.tests.utils.TestChecker;
 
 import static java.util.function.Predicate.isEqual;
 import static org.assertj.core.api.Assertions.assertThat;
-import static org.jastadd.ragconnect.tests.TestUtils.*;
+import static org.jastadd.ragconnect.tests.utils.TestUtils.*;
 import static org.junit.jupiter.api.Assertions.*;
 
 /**
@@ -22,6 +26,7 @@ import static org.junit.jupiter.api.Assertions.*;
  * @author rschoene - Initial contribution
  */
 @Tag("Incremental")
+@Tag("New")
 public class AttributeTest extends AbstractMqttTest {
 
   private static final String TOPIC_WILDCARD = "attr/#";
@@ -84,7 +89,7 @@ public class AttributeTest extends AbstractMqttTest {
     data = new ReceiverData();
     assertTrue(handler.newConnection(TOPIC_WILDCARD, bytes -> data.numberOfValues += 1));
 
-    checker = new TestUtils.TestChecker();
+    checker = new TestChecker();
     checker.setActualNumberOfValues(() -> data.numberOfValues)
             .setCheckForString(CHECK_BASIC, this::checkBasic)
             .setCheckForString(CHECK_SIMPLE, this::checkSimple)
@@ -147,6 +152,35 @@ public class AttributeTest extends AbstractMqttTest {
             .put(CHECK_CIRCULAR, INITIAL_STRING_FOR_INT_PLUS_2)
             .put(CHECK_COLLECTION, "[" + INITIAL_STRING + "]");
 
+    if (!TestUtils.isCi()) {
+      Dumper.read(model)
+              .includeAttributeWhen((node, attributeName, isNTA, value) -> {
+                switch (attributeName) {
+                  case "basic":
+                  case "simple":
+                  case "collectionAttribute":
+                    return node.equals(senderString);
+                  case "transformed":
+                  case "circularAttribute":
+                    return node.equals(senderInt);
+                  case "toReferenceType":
+                  case "toNTA":
+                    return node.equals(senderA);
+                }
+                return false;
+              })
+              .includeChildWhen((parentNode, childNode, contextName) -> !contextName.equals("Inner"))
+              .setNameMethod(node -> {
+                if (node instanceof SenderRoot) {
+                  String suffix = node.equals(senderString) ? "(String)" :
+                          node.equals(senderInt) ? "(int)" : "(reference type)";
+                  return "SenderRoot " + suffix;
+                }
+                return node.getClass().getSimpleName();
+              })
+              .dumpAsPNG(Paths.get("attribute.png"));
+    }
+
     communicateBoth();
   }
 
diff --git a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/ContextFreeSimpleTest.java b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/ContextFreeSimpleTest.java
index 82588b72a8629d4e7943068770dc46977413f594..a74b4d9bc47b0a35a413dd7259fc8947f8c6c27c 100644
--- a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/ContextFreeSimpleTest.java
+++ b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/ContextFreeSimpleTest.java
@@ -4,13 +4,16 @@ import contextFreeSimpleInc.ast.A;
 import contextFreeSimpleInc.ast.MqttHandler;
 import contextFreeSimpleInc.ast.Root;
 import contextFreeSimpleInc.ast.SerializationException;
+import org.jastadd.ragconnect.tests.utils.DefaultMappings;
+import org.jastadd.ragconnect.tests.utils.TestChecker;
+import org.jastadd.ragconnect.tests.utils.TestUtils;
 import org.junit.jupiter.api.Tag;
 
 import java.io.IOException;
 import java.util.Objects;
 import java.util.concurrent.TimeUnit;
 
-import static org.jastadd.ragconnect.tests.TestUtils.mqttUri;
+import static org.jastadd.ragconnect.tests.utils.TestUtils.mqttUri;
 import static org.junit.jupiter.api.Assertions.*;
 
 /**
@@ -41,7 +44,7 @@ public class ContextFreeSimpleTest extends AbstractMqttTest {
 
   private ReceiverData data;
   private MqttHandler handler;
-  private TestUtils.TestChecker checker;
+  private TestChecker checker;
 
   @Override
   protected void createModel() {
@@ -70,7 +73,7 @@ public class ContextFreeSimpleTest extends AbstractMqttTest {
 
     handler.newConnection(TOPIC_WILDCARD, (topic, bytes) -> data.valuesSent += 1);
 
-    checker = new TestUtils.TestChecker();
+    checker = new TestChecker();
     checker.alwaysWait()
             .setCheckForString(TOPIC_UNNAMED, (name, expected) -> this.check(name, expected, model.getA(), unnamedA))
             .setCheckForString(TOPIC_SINGLE, (name, expected) -> this.check(name, expected, model.getSingleA(), singleA))
@@ -143,7 +146,7 @@ public class ContextFreeSimpleTest extends AbstractMqttTest {
   private void send(String topic, String value) throws IOException {
     A a = new A().setValue(value);
     try {
-      publisher.publish(topic, TestUtils.DefaultMappings.TreeToBytes(a::serialize));
+      publisher.publish(topic, DefaultMappings.TreeToBytes(a::serialize));
     } catch (SerializationException e) {
       throw new IOException(e);
     }
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 356f2b139107223cecde7645f2e4908824aa4355..f72a80a781abfd6e22bebfbc467541c0d371aedd 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
@@ -3,14 +3,16 @@ package org.jastadd.ragconnect.tests;
 import defaultOnlyRead.ast.A;
 import defaultOnlyRead.ast.BoxedTypes;
 import defaultOnlyRead.ast.NativeTypes;
+import org.jastadd.ragconnect.tests.utils.DefaultMappings;
+import org.jastadd.ragconnect.tests.utils.TestUtils;
 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.testJaddContainReferenceToJackson;
+import static org.jastadd.ragconnect.tests.utils.TestUtils.mqttUri;
+import static org.jastadd.ragconnect.tests.utils.TestUtils.testJaddContainReferenceToJackson;
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 
@@ -119,22 +121,22 @@ public class DefaultOnlyReadTest extends AbstractMqttTest {
     final char expectedCharValue = 'c';
     final String expectedStringValue = "6.3";
 
-    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));
+    publisher.publish(TOPIC_NATIVE_BOOLEAN, DefaultMappings.BoolToBytes(expectedBooleanValue));
+    publisher.publish(TOPIC_NATIVE_INT, DefaultMappings.IntToBytes(expectedIntValue));
+    publisher.publish(TOPIC_NATIVE_SHORT, DefaultMappings.ShortToBytes(expectedShortValue));
+    publisher.publish(TOPIC_NATIVE_LONG, DefaultMappings.LongToBytes(expectedLongValue));
+    publisher.publish(TOPIC_NATIVE_FLOAT, DefaultMappings.FloatToBytes(expectedFloatValue));
+    publisher.publish(TOPIC_NATIVE_DOUBLE, DefaultMappings.DoubleToBytes(expectedDoubleValue));
+    publisher.publish(TOPIC_NATIVE_CHAR, DefaultMappings.CharToBytes(expectedCharValue));
+    publisher.publish(TOPIC_NATIVE_STRING, DefaultMappings.StringToBytes(expectedStringValue));
+
+    publisher.publish(TOPIC_BOXED_BOOLEAN, DefaultMappings.BoolToBytes(expectedBooleanValue));
+    publisher.publish(TOPIC_BOXED_INTEGER, DefaultMappings.IntToBytes(expectedIntValue));
+    publisher.publish(TOPIC_BOXED_SHORT, DefaultMappings.ShortToBytes(expectedShortValue));
+    publisher.publish(TOPIC_BOXED_LONG, DefaultMappings.LongToBytes(expectedLongValue));
+    publisher.publish(TOPIC_BOXED_FLOAT, DefaultMappings.FloatToBytes(expectedFloatValue));
+    publisher.publish(TOPIC_BOXED_DOUBLE, DefaultMappings.DoubleToBytes(expectedDoubleValue));
+    publisher.publish(TOPIC_BOXED_CHARACTER, DefaultMappings.CharToBytes(expectedCharValue));
 
     TestUtils.waitForMqtt();
 
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 d30912c88f4f49ad9dc776aecabf492ae4a77bec..b56f5ddd0e8fd6e35fa5a37013d86cae91e99b39 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,13 +4,15 @@ import defaultOnlyWrite.ast.A;
 import defaultOnlyWrite.ast.BoxedTypesSyn;
 import defaultOnlyWrite.ast.MqttHandler;
 import defaultOnlyWrite.ast.NativeTypesSyn;
+import org.jastadd.ragconnect.tests.utils.DefaultMappings;
+import org.jastadd.ragconnect.tests.utils.TestUtils;
 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.*;
+import static org.jastadd.ragconnect.tests.utils.TestUtils.*;
 import static org.junit.jupiter.api.Assertions.*;
 
 /**
@@ -177,63 +179,63 @@ public class DefaultOnlyWriteTest extends AbstractMqttTest {
 
     receiver.newConnection(transformed ? TOPIC_NATIVE_BOOLEAN_TRANSFORMED : TOPIC_NATIVE_BOOLEAN, bytes -> {
       result.numberOfNativeBoolValues += 1;
-      result.lastNativeBoolValue = TestUtils.DefaultMappings.BytesToBool(bytes);
+      result.lastNativeBoolValue = DefaultMappings.BytesToBool(bytes);
     });
     receiver.newConnection(transformed ? TOPIC_NATIVE_INT_TRANSFORMED : TOPIC_NATIVE_INT, bytes -> {
       result.numberOfNativeIntValues += 1;
-      result.lastNativeIntValue = TestUtils.DefaultMappings.BytesToInt(bytes);
+      result.lastNativeIntValue = DefaultMappings.BytesToInt(bytes);
     });
     receiver.newConnection(transformed ? TOPIC_NATIVE_SHORT_TRANSFORMED : TOPIC_NATIVE_SHORT, bytes -> {
       result.numberOfNativeShortValues += 1;
-      result.lastNativeShortValue = TestUtils.DefaultMappings.BytesToShort(bytes);
+      result.lastNativeShortValue = DefaultMappings.BytesToShort(bytes);
     });
     receiver.newConnection(transformed ? TOPIC_NATIVE_LONG_TRANSFORMED : TOPIC_NATIVE_LONG, bytes -> {
       result.numberOfNativeLongValues += 1;
-      result.lastNativeLongValue = TestUtils.DefaultMappings.BytesToLong(bytes);
+      result.lastNativeLongValue = DefaultMappings.BytesToLong(bytes);
     });
     receiver.newConnection(transformed ? TOPIC_NATIVE_FLOAT_TRANSFORMED : TOPIC_NATIVE_FLOAT, bytes -> {
       result.numberOfNativeFloatValues += 1;
-      result.lastNativeFloatValue = TestUtils.DefaultMappings.BytesToFloat(bytes);
+      result.lastNativeFloatValue = DefaultMappings.BytesToFloat(bytes);
     });
     receiver.newConnection(transformed ? TOPIC_NATIVE_DOUBLE_TRANSFORMED : TOPIC_NATIVE_DOUBLE, bytes -> {
       result.numberOfNativeDoubleValues += 1;
-      result.lastNativeDoubleValue = TestUtils.DefaultMappings.BytesToDouble(bytes);
+      result.lastNativeDoubleValue = DefaultMappings.BytesToDouble(bytes);
     });
     receiver.newConnection(transformed ? TOPIC_NATIVE_CHAR_TRANSFORMED : TOPIC_NATIVE_CHAR, bytes -> {
       result.numberOfNativeCharValues += 1;
-      result.lastNativeCharValue = TestUtils.DefaultMappings.BytesToChar(bytes);
+      result.lastNativeCharValue = DefaultMappings.BytesToChar(bytes);
     });
     receiver.newConnection(transformed ? TOPIC_NATIVE_STRING_TRANSFORMED : TOPIC_NATIVE_STRING, bytes -> {
       result.numberOfNativeStringValues += 1;
-      result.lastNativeStringValue = TestUtils.DefaultMappings.BytesToString(bytes);
+      result.lastNativeStringValue = DefaultMappings.BytesToString(bytes);
     });
     receiver.newConnection(transformed ? TOPIC_BOXED_BOOLEAN_TRANSFORMED : TOPIC_BOXED_BOOLEAN, bytes -> {
       result.numberOfBoxedBoolValues += 1;
-      result.lastBoxedBoolValue = TestUtils.DefaultMappings.BytesToBool(bytes);
+      result.lastBoxedBoolValue = DefaultMappings.BytesToBool(bytes);
     });
     receiver.newConnection(transformed ? TOPIC_BOXED_INTEGER_TRANSFORMED : TOPIC_BOXED_INTEGER, bytes -> {
       result.numberOfBoxedIntValues += 1;
-      result.lastBoxedIntValue = TestUtils.DefaultMappings.BytesToInt(bytes);
+      result.lastBoxedIntValue = DefaultMappings.BytesToInt(bytes);
     });
     receiver.newConnection(transformed ? TOPIC_BOXED_SHORT_TRANSFORMED : TOPIC_BOXED_SHORT, bytes -> {
       result.numberOfBoxedShortValues += 1;
-      result.lastBoxedShortValue = TestUtils.DefaultMappings.BytesToShort(bytes);
+      result.lastBoxedShortValue = DefaultMappings.BytesToShort(bytes);
     });
     receiver.newConnection(transformed ? TOPIC_BOXED_LONG_TRANSFORMED : TOPIC_BOXED_LONG, bytes -> {
       result.numberOfBoxedLongValues += 1;
-      result.lastBoxedLongValue = TestUtils.DefaultMappings.BytesToLong(bytes);
+      result.lastBoxedLongValue = DefaultMappings.BytesToLong(bytes);
     });
     receiver.newConnection(transformed ? TOPIC_BOXED_FLOAT_TRANSFORMED : TOPIC_BOXED_FLOAT, bytes -> {
       result.numberOfBoxedFloatValues += 1;
-      result.lastBoxedFloatValue = TestUtils.DefaultMappings.BytesToFloat(bytes);
+      result.lastBoxedFloatValue = DefaultMappings.BytesToFloat(bytes);
     });
     receiver.newConnection(transformed ? TOPIC_BOXED_DOUBLE_TRANSFORMED : TOPIC_BOXED_DOUBLE, bytes -> {
       result.numberOfBoxedDoubleValues += 1;
-      result.lastBoxedDoubleValue = TestUtils.DefaultMappings.BytesToDouble(bytes);
+      result.lastBoxedDoubleValue = DefaultMappings.BytesToDouble(bytes);
     });
     receiver.newConnection(transformed ? TOPIC_BOXED_CHARACTER_TRANSFORMED : TOPIC_BOXED_CHARACTER, bytes -> {
       result.numberOfBoxedCharValues += 1;
-      result.lastBoxedCharValue = TestUtils.DefaultMappings.BytesToChar(bytes);
+      result.lastBoxedCharValue = DefaultMappings.BytesToChar(bytes);
     });
     return result;
   }
diff --git a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/ErrorsTest.java b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/ErrorsTest.java
index 4b3f57355621ddb91a1f66b8662c7c36e2a8b30c..4853b6a7e62f6e71878b37700a72fe2515655200 100644
--- a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/ErrorsTest.java
+++ b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/ErrorsTest.java
@@ -1,5 +1,6 @@
 package org.jastadd.ragconnect.tests;
 
+import org.jastadd.ragconnect.tests.utils.TestUtils;
 import org.junit.jupiter.api.BeforeAll;
 import org.junit.jupiter.api.Test;
 
@@ -12,7 +13,7 @@ import java.util.List;
 import java.util.stream.Collectors;
 
 import static org.assertj.core.api.Assertions.assertThat;
-import static org.jastadd.ragconnect.tests.TestUtils.readFile;
+import static org.jastadd.ragconnect.tests.utils.TestUtils.readFile;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 
 /**
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 143c76b59e81afc5998c87533df0cb842a9392bd..cc9facae3c261e82600ff15779406e5196c30403 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
@@ -3,7 +3,8 @@ package org.jastadd.ragconnect.tests;
 import com.google.protobuf.InvalidProtocolBufferException;
 import config.Config.RobotConfig;
 import example.ast.*;
-import org.jastadd.ragconnect.tests.TestUtils.TestChecker;
+import org.jastadd.ragconnect.tests.utils.TestUtils;
+import org.jastadd.ragconnect.tests.utils.TestChecker;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 import robot.RobotStateOuterClass.RobotState;
@@ -11,8 +12,8 @@ import robot.RobotStateOuterClass.RobotState;
 import java.io.IOException;
 import java.util.concurrent.TimeUnit;
 
-import static org.jastadd.ragconnect.tests.TestUtils.mqttUri;
-import static org.jastadd.ragconnect.tests.TestUtils.waitForMqtt;
+import static org.jastadd.ragconnect.tests.utils.TestUtils.mqttUri;
+import static org.jastadd.ragconnect.tests.utils.TestUtils.waitForMqtt;
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 
diff --git a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/ForwardingTest.java b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/ForwardingTest.java
index 314ad3f1c75adcb0438267889ff358352bb8df9e..3efa2e6ee34a711d14aecfaa34aa163f7efc0944 100644
--- a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/ForwardingTest.java
+++ b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/ForwardingTest.java
@@ -1,6 +1,8 @@
 package org.jastadd.ragconnect.tests;
 
 import forwardingInc.ast.*;
+import org.jastadd.ragconnect.tests.utils.TestChecker;
+import org.jastadd.ragconnect.tests.utils.TestUtils;
 import org.junit.jupiter.api.Tag;
 
 import java.io.IOException;
@@ -11,7 +13,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
 
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.assertj.core.groups.Tuple.tuple;
-import static org.jastadd.ragconnect.tests.TestUtils.*;
+import static org.jastadd.ragconnect.tests.utils.TestUtils.*;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 
 /**
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 46e944c560e072583982a2840bce57f9e66f6bab..2d441addf370f962e9ff40d71b963d00e77e7bbe 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,12 +3,14 @@ package org.jastadd.ragconnect.tests;
 import incremental.ast.A;
 import incremental.ast.B;
 import incremental.ast.MqttHandler;
+import org.jastadd.ragconnect.tests.utils.DefaultMappings;
+import org.jastadd.ragconnect.tests.utils.TestUtils;
 import org.junit.jupiter.api.Tag;
 
 import java.io.IOException;
 import java.util.concurrent.TimeUnit;
 
-import static org.jastadd.ragconnect.tests.TestUtils.mqttUri;
+import static org.jastadd.ragconnect.tests.utils.TestUtils.mqttUri;
 import static org.junit.jupiter.api.Assertions.*;
 
 /**
@@ -61,15 +63,15 @@ public class IncrementalDependencyTest extends AbstractMqttTest {
 
     handler.newConnection(TOPIC_OUT_A, bytes -> {
       dataA.numberOfStringValues += 1;
-      dataA.lastStringValue = TestUtils.DefaultMappings.BytesToString(bytes);
+      dataA.lastStringValue = DefaultMappings.BytesToString(bytes);
     });
     handler.newConnection(TOPIC_OUT_B1, bytes -> {
       dataB1.numberOfStringValues += 1;
-      dataB1.lastStringValue = TestUtils.DefaultMappings.BytesToString(bytes);
+      dataB1.lastStringValue = DefaultMappings.BytesToString(bytes);
     });
     handler.newConnection(TOPIC_OUT_B2, bytes -> {
       dataB2.numberOfStringValues += 1;
-      dataB2.lastStringValue = TestUtils.DefaultMappings.BytesToString(bytes);
+      dataB2.lastStringValue = DefaultMappings.BytesToString(bytes);
     });
 
     assertTrue(model.connectInput(mqttUri(TOPIC_IN)));
diff --git a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/IndexedSendTest.java b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/IndexedSendTest.java
index dc1bdda4a03c1b228a1c0214811ebb00b417f004..7ea7ed89fd6f6f57951082f433e4ef530e965ef5 100644
--- a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/IndexedSendTest.java
+++ b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/IndexedSendTest.java
@@ -4,6 +4,8 @@ import indexedSendInc.ast.*;
 import io.github.artsok.RepeatedIfExceptionsTest;
 import org.assertj.core.api.Assertions;
 import org.assertj.core.groups.Tuple;
+import org.jastadd.ragconnect.tests.utils.TestChecker;
+import org.jastadd.ragconnect.tests.utils.TestUtils;
 import org.junit.jupiter.api.Tag;
 
 import java.io.IOException;
@@ -13,7 +15,7 @@ import java.util.function.Predicate;
 import java.util.function.Supplier;
 
 import static org.assertj.core.groups.Tuple.tuple;
-import static org.jastadd.ragconnect.tests.TestUtils.*;
+import static org.jastadd.ragconnect.tests.utils.TestUtils.*;
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 
@@ -40,7 +42,7 @@ public class IndexedSendTest extends AbstractMqttTest {
 
   private MqttHandler handler;
   private ReceiverData data;
-  private TestUtils.TestChecker checker;
+  private TestChecker checker;
 
   private Root model;
   private SenderRoot senderRoot;
diff --git a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/JavaTest.java b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/JavaTest.java
index 0c33e9c65e346a45fd4458428a81a273a9cf8148..81d2817c5a1453bb82728edb496d1b41042d31ba 100644
--- a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/JavaTest.java
+++ b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/JavaTest.java
@@ -2,18 +2,15 @@ package org.jastadd.ragconnect.tests;
 
 import javaInc.ast.*;
 import org.assertj.core.groups.Tuple;
-import org.jastadd.ragconnect.tests.TestUtils.TestChecker;
+import org.jastadd.ragconnect.tests.utils.TestChecker;
 import org.junit.jupiter.api.AfterEach;
-import org.junit.jupiter.api.Tag;
 import org.junit.jupiter.api.Test;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 
 import java.io.IOException;
 import java.util.List;
 
 import static org.assertj.core.api.Assertions.tuple;
-import static org.jastadd.ragconnect.tests.TestUtils.javaUri;
+import static org.jastadd.ragconnect.tests.utils.TestUtils.javaUri;
 import static org.junit.jupiter.api.Assertions.*;
 
 /**
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 f1cafb03ff6900b9859600da79b4d6103bc140f8..519b802bbc7a6b7c3c7719fc13debde86006d2c5 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
@@ -4,11 +4,13 @@ import mapping.ast.A;
 import mapping.ast.BoxedTypes;
 import mapping.ast.MqttHandler;
 import mapping.ast.NativeTypes;
+import org.jastadd.ragconnect.tests.utils.DefaultMappings;
+import org.jastadd.ragconnect.tests.utils.TestUtils;
 
 import java.io.IOException;
 import java.util.concurrent.TimeUnit;
 
-import static org.jastadd.ragconnect.tests.TestUtils.mqttUri;
+import static org.jastadd.ragconnect.tests.utils.TestUtils.mqttUri;
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 
@@ -62,31 +64,31 @@ public class MappingTest extends AbstractMqttTest {
     data = new ReceiverData();
     handler.newConnection(TOPIC_WRITE_NATIVE_INT, bytes -> {
       data.numberOfNativeIntValues += 1;
-      data.lastNativeIntValue = TestUtils.DefaultMappings.BytesToInt(bytes);
+      data.lastNativeIntValue = DefaultMappings.BytesToInt(bytes);
     });
     handler.newConnection(TOPIC_WRITE_NATIVE_SHORT, bytes -> {
       data.numberOfNativeShortValues += 1;
-      data.lastNativeShortValue = TestUtils.DefaultMappings.BytesToShort(bytes);
+      data.lastNativeShortValue = DefaultMappings.BytesToShort(bytes);
     });
     handler.newConnection(TOPIC_WRITE_NATIVE_LONG, bytes -> {
       data.numberOfNativeLongValues += 1;
-      data.lastNativeLongValue = TestUtils.DefaultMappings.BytesToLong(bytes);
+      data.lastNativeLongValue = DefaultMappings.BytesToLong(bytes);
     });
     handler.newConnection(TOPIC_WRITE_NATIVE_FLOAT, bytes -> {
       data.numberOfNativeFloatValues += 1;
-      data.lastNativeFloatValue = TestUtils.DefaultMappings.BytesToFloat(bytes);
+      data.lastNativeFloatValue = DefaultMappings.BytesToFloat(bytes);
     });
     handler.newConnection(TOPIC_WRITE_NATIVE_DOUBLE, bytes -> {
       data.numberOfNativeDoubleValues += 1;
-      data.lastNativeDoubleValue = TestUtils.DefaultMappings.BytesToDouble(bytes);
+      data.lastNativeDoubleValue = DefaultMappings.BytesToDouble(bytes);
     });
     handler.newConnection(TOPIC_WRITE_NATIVE_CHAR, bytes -> {
       data.numberOfNativeCharValues += 1;
-      data.lastNativeCharValue = TestUtils.DefaultMappings.BytesToChar(bytes);
+      data.lastNativeCharValue = DefaultMappings.BytesToChar(bytes);
     });
     handler.newConnection(TOPIC_WRITE_NATIVE_BOOLEAN, bytes -> {
       data.numberOfNativeBooleanValues += 1;
-      data.lastNativeBooleanValue = TestUtils.DefaultMappings.BytesToBool(bytes);
+      data.lastNativeBooleanValue = DefaultMappings.BytesToBool(bytes);
     });
 
     assertTrue(natives.connectWriteIntValue(mqttUri(TOPIC_WRITE_NATIVE_INT), isWriteCurrentValue()));
diff --git a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/MqttHandlerTest.java b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/MqttHandlerTest.java
index be61201ef687a72bc252380166cf1abbf5b82eb4..f8b127ea58ede616ede4c2c44def9d0086877c68 100644
--- a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/MqttHandlerTest.java
+++ b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/MqttHandlerTest.java
@@ -1,6 +1,7 @@
 package org.jastadd.ragconnect.tests;
 
 import example.ast.MqttHandler;
+import org.jastadd.ragconnect.tests.utils.TestUtils;
 import org.junit.jupiter.api.Tag;
 import org.junit.jupiter.api.Test;
 
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 4deccefe8865be8a3bab05ba7ffad560ab83c09f..155109f8a4010e1bde66c37ad34696b3a1b7964b 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
@@ -1,11 +1,13 @@
 package org.jastadd.ragconnect.tests;
 
+import org.jastadd.ragconnect.tests.utils.DefaultMappings;
+import org.jastadd.ragconnect.tests.utils.TestUtils;
 import read1write2.ast.*;
 
 import java.io.IOException;
 import java.util.concurrent.TimeUnit;
 
-import static org.jastadd.ragconnect.tests.TestUtils.mqttUri;
+import static org.jastadd.ragconnect.tests.utils.TestUtils.mqttUri;
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 
@@ -75,29 +77,29 @@ public class Read1Write2Test extends AbstractMqttTest {
 
     handler.newConnection(TOPIC_SAME_WRITE_INT, bytes -> {
       dataSame.numberOfIntValues += 1;
-      dataSame.lastIntValue = TestUtils.DefaultMappings.BytesToInt(bytes);
+      dataSame.lastIntValue = DefaultMappings.BytesToInt(bytes);
     });
     handler.newConnection(TOPIC_SAME_WRITE_STRING, bytes -> {
       dataSame.numberOfStringValues += 1;
-      dataSame.lastStringValue = TestUtils.DefaultMappings.BytesToString(bytes);
+      dataSame.lastStringValue = DefaultMappings.BytesToString(bytes);
     });
 
     handler.newConnection(TOPIC_DIFFERENT_WRITE1_INT, bytes -> {
       dataOther1.numberOfIntValues += 1;
-      dataOther1.lastIntValue = TestUtils.DefaultMappings.BytesToInt(bytes);
+      dataOther1.lastIntValue = DefaultMappings.BytesToInt(bytes);
     });
     handler.newConnection(TOPIC_DIFFERENT_WRITE1_STRING, bytes -> {
       dataOther1.numberOfStringValues += 1;
-      dataOther1.lastStringValue = TestUtils.DefaultMappings.BytesToString(bytes);
+      dataOther1.lastStringValue = DefaultMappings.BytesToString(bytes);
     });
 
     handler.newConnection(TOPIC_DIFFERENT_WRITE2_INT, bytes -> {
       dataOther2.numberOfIntValues += 1;
-      dataOther2.lastIntValue = TestUtils.DefaultMappings.BytesToInt(bytes);
+      dataOther2.lastIntValue = DefaultMappings.BytesToInt(bytes);
     });
     handler.newConnection(TOPIC_DIFFERENT_WRITE2_STRING, bytes -> {
       dataOther2.numberOfStringValues += 1;
-      dataOther2.lastStringValue = TestUtils.DefaultMappings.BytesToString(bytes);
+      dataOther2.lastStringValue = DefaultMappings.BytesToString(bytes);
     });
 
     assertTrue(onSameNonterminal.connectInput(mqttUri(TOPIC_SAME_READ)));
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 f931c13c92616e52716fd1b4f5aa26e3f838dac9..0bd22195a4fc2e2fab77b690a65b65e6f8080514 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
@@ -1,11 +1,13 @@
 package org.jastadd.ragconnect.tests;
 
+import org.jastadd.ragconnect.tests.utils.DefaultMappings;
+import org.jastadd.ragconnect.tests.utils.TestUtils;
 import read2write1.ast.*;
 
 import java.io.IOException;
 import java.util.concurrent.TimeUnit;
 
-import static org.jastadd.ragconnect.tests.TestUtils.mqttUri;
+import static org.jastadd.ragconnect.tests.utils.TestUtils.mqttUri;
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 
@@ -76,17 +78,17 @@ public class Read2Write1Test extends AbstractMqttTest {
 
     handler.newConnection(TOPIC_SAME_WRITE_INT, bytes -> {
       dataSame.numberOfIntValues += 1;
-      dataSame.lastIntValue = TestUtils.DefaultMappings.BytesToInt(bytes);
+      dataSame.lastIntValue = DefaultMappings.BytesToInt(bytes);
     });
 
     handler.newConnection(TOPIC_DIFFERENT_WRITE1_INT, bytes -> {
       dataOther1.numberOfIntValues += 1;
-      dataOther1.lastIntValue = TestUtils.DefaultMappings.BytesToInt(bytes);
+      dataOther1.lastIntValue = DefaultMappings.BytesToInt(bytes);
     });
 
     handler.newConnection(TOPIC_DIFFERENT_WRITE2_INT, bytes -> {
       dataOther2.numberOfIntValues += 1;
-      dataOther2.lastIntValue = TestUtils.DefaultMappings.BytesToInt(bytes);
+      dataOther2.lastIntValue = DefaultMappings.BytesToInt(bytes);
     });
 
     assertTrue(onSameNonterminal.connectInput1(mqttUri(TOPIC_SAME_READ1)));
diff --git a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/RegressionTests.java b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/RegressionTests.java
index 5f74942484f67dae810feb70e92d573ecebf9aba..92a2c4b978ed6d930bdc9b104db1d6043512fe78 100644
--- a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/RegressionTests.java
+++ b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/RegressionTests.java
@@ -1,5 +1,6 @@
 package org.jastadd.ragconnect.tests;
 
+import org.jastadd.ragconnect.tests.utils.TestUtils;
 import org.junit.jupiter.api.Test;
 import via.ast.A;
 
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
deleted file mode 100644
index 4a74e6b3a205f092a53b73897656151464f8e8e1..0000000000000000000000000000000000000000
--- a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/TestUtils.java
+++ /dev/null
@@ -1,638 +0,0 @@
-package org.jastadd.ragconnect.tests;
-
-import com.fasterxml.jackson.core.JsonEncoding;
-import com.fasterxml.jackson.core.JsonFactory;
-import com.fasterxml.jackson.core.JsonGenerator;
-import org.assertj.core.groups.Tuple;
-import org.awaitility.Awaitility;
-import org.awaitility.core.ConditionFactory;
-import org.jastadd.ragconnect.compiler.Compiler;
-import org.junit.jupiter.api.Assertions;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.io.ByteArrayOutputStream;
-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.*;
-import java.util.concurrent.Callable;
-import java.util.concurrent.TimeUnit;
-import java.util.function.BiConsumer;
-import java.util.function.Function;
-import java.util.function.Predicate;
-import java.util.function.Supplier;
-import java.util.stream.Collectors;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.junit.jupiter.api.Assertions.assertTrue;
-import static org.junit.jupiter.api.Assertions.fail;
-
-/**
- * Utility methods for tests.
- *
- * @author rschoene - Initial contribution
- */
-public class TestUtils {
-
-  private static final Logger logger = LoggerFactory.getLogger(TestUtils.class);
-  public static final double DELTA = 0.001d;
-  public static final String INPUT_DIRECTORY_PREFIX = "./src/test/01-input/";
-  public static final String OUTPUT_DIRECTORY_PREFIX = "./src/test/02-after-ragconnect/";
-
-  public static String getMqttHost() {
-    if (System.getenv("GITLAB_CI") != null) {
-      // we are in the CI, so use "mqtt" as host
-      return "mqtt";
-    } {
-      // else assume a locally running mqtt broker
-      return "localhost";
-    }
-  }
-
-  public static String mqttUri(String path) {
-    return "mqtt://" + getMqttHost() + "/" + path;
-  }
-
-  public static String restUri(String path, int port) {
-    return "rest://localhost:" + port + "/" + path;
-  }
-
-  public static String javaUri(String path) {
-    return "java://localhost/" + path;
-  }
-
-  public static int getMqttDefaultPort() {
-    return 1883;
-  }
-
-  public static Path runCompiler(String grammarFile, Iterable<String> connectFiles, String rootNode, String outputDirectory, int expectedReturnValue, String... additionalArguments) {
-
-    assertThat(connectFiles).isNotEmpty();
-
-    Path outPath = Paths.get(OUTPUT_DIRECTORY_PREFIX)
-        .resolve(outputDirectory)
-        .resolve("Compiler.out");
-    ensureCreated(outPath.getParent());
-
-    try {
-      logger.debug("user.dir: {}", System.getProperty("user.dir"));
-      List<String> args = new ArrayList<>() {{
-        add("--o=" + OUTPUT_DIRECTORY_PREFIX + outputDirectory);
-        add("--rootNode=" + rootNode);
-        add("--verbose");
-        add(INPUT_DIRECTORY_PREFIX + grammarFile);
-      }};
-      connectFiles.forEach(connectFile -> args.add(INPUT_DIRECTORY_PREFIX + connectFile));
-      args.addAll(Arrays.asList(additionalArguments));
-
-      int returnValue = exec(Compiler.class, args.toArray(new String[0]), outPath.toFile());
-      Assertions.assertEquals(expectedReturnValue, returnValue, "RagConnect did not return with value " + expectedReturnValue);
-    } catch (IOException | InterruptedException e) {
-      fail(e);
-    }
-    return outPath;
-  }
-
-  public static <T> String prettyPrint(Iterable<T> aList, Function<T, String> elementPrinter) {
-    StringJoiner sj = new StringJoiner(", ", "[", "]");
-    aList.forEach(element -> sj.add(elementPrinter.apply(element)));
-    return sj.toString();
-  }
-
-  public static void assertLinesMatch(String directory, String expectedName, String out) throws IOException {
-    Path expectedPath = Paths.get(TestUtils.INPUT_DIRECTORY_PREFIX)
-            .resolve(directory)
-            .resolve(expectedName + ".expected");
-    String expected = readFile(expectedPath, Charset.defaultCharset());
-    List<String> outList = Arrays.asList(out.split("\n"));
-    Collections.sort(outList);
-    List<String> expectedList = Arrays.stream(expected.split("\n"))
-            .sorted()
-            .filter(s -> !s.isEmpty() && !s.startsWith("//"))
-            .collect(Collectors.toList());
-
-    Assertions.assertLinesMatch(expectedList, outList);
-  }
-
-  private static void ensureCreated(Path directory) {
-    File directoryFile = directory.toFile();
-    if (directoryFile.exists() && directoryFile.isDirectory()) {
-      return;
-    }
-    assertTrue(directoryFile.mkdirs());
-  }
-
-  public static int exec(Class<?> klass, String[] args, File err) throws IOException,
-      InterruptedException {
-    String javaHome = System.getProperty("java.home");
-    String javaBin = javaHome + File.separator + "bin" + File.separator + "java";
-    String classpath = System.getProperty("java.class.path");
-    String className = klass.getName();
-
-    String[] newArgs = new String[args.length + 4];
-    newArgs[0] = javaBin;
-    newArgs[1] = "-cp";
-    newArgs[2] = classpath;
-    newArgs[3] = className;
-    System.arraycopy(args, 0, newArgs, 4, args.length);
-
-    ProcessBuilder builder = new ProcessBuilder(newArgs);
-//    builder.redirectOutput(err);
-    builder.redirectError(err);
-
-    Process process = builder.start();
-    process.waitFor();
-    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(Path path, Charset encoding)
-      throws IOException {
-    byte[] encoded = Files.readAllBytes(path);
-    return new String(encoded, encoding);
-  }
-
-  public static void waitForMqtt() throws InterruptedException {
-    TimeUnit.MILLISECONDS.sleep(1500);
-  }
-
-  public static ConditionFactory awaitMqtt() {
-    return Awaitility.await().atMost(1500, TimeUnit.MILLISECONDS);
-  }
-
-  static <T_Event, T_ASTNode> void logEvent(T_Event event, T_ASTNode node, String attribute, Object params, Object value) {
-    logger.info("event: {}, node: {}, attribute: {}, params: {}, value: {}",
-            event, node, attribute, params, value);
-  }
-
-  public static class TestChecker {
-    static class ActualAndExpected<T> {
-      Callable<T> actual;
-      T expected;
-      BiConsumer<String, T> customCheck;
-
-      ActualAndExpected(String key) {
-        expected = null;
-      }
-
-      void check(String name) {
-        if (customCheck != null) {
-          customCheck.accept(name, expected);
-          return;
-        }
-        if (actual == null) {
-          fail("No actual getter defined for " + name);
-        }
-        T actualValue = null;
-        try {
-          actualValue = this.actual.call();
-        } catch (Exception e) {
-          fail(e);
-        }
-        assertThat(actualValue).as(name).isEqualTo(expected);
-      }
-
-      void checkAwait(String name) {
-        if (customCheck != null) {
-          logger.warn("Custom check set for {}. Can't await for that.", name);
-          customCheck.accept(name, expected);
-          return;
-        }
-        if (actual == null) {
-          fail("No actual getter defined for " + name);
-        }
-        awaitMqtt().alias(name + " == " + expected).until(actual, Predicate.isEqual(expected));
-      }
-    }
-    static class ValuesToCompare<T> {
-      protected final Map<String, ActualAndExpected<T>> values = new HashMap<>();
-      protected final TestChecker parent;
-
-      ValuesToCompare(TestChecker parent) {
-        this.parent = parent;
-      }
-
-      public TestChecker setActual(String name, Callable<T> actual) {
-        _computeIfAbsent(name).actual = actual;
-        return parent;
-      }
-
-      public TestChecker setCheck(String name, BiConsumer<String, T> check) {
-        _computeIfAbsent(name).customCheck = check;
-        return parent;
-      }
-
-      public TestChecker put(String name, T expected) {
-        _computeIfAbsent(name).expected = expected;
-        return parent;
-      }
-
-      public TestChecker updateExpected(String name, Function<T, T> updater) {
-        ActualAndExpected<T> aae = _computeIfAbsent(name);
-        aae.expected = updater.apply(aae.expected);
-        return parent;
-      }
-
-      private ActualAndExpected<T> _computeIfAbsent(String name) {
-        return values.computeIfAbsent(name, ActualAndExpected::new);
-      }
-
-      ActualAndExpected<T> get(String name) {
-        return values.get(name);
-      }
-
-      void forEach(BiConsumer<? super String, ? super ActualAndExpected<T>> action) {
-        values.forEach(action);
-      }
-    }
-    static class IntegerValuesToCompare extends ValuesToCompare<Integer> {
-      IntegerValuesToCompare(TestChecker parent) {
-        super(parent);
-      }
-
-      public TestChecker incNumberOfValues() {
-        return addToNumberOfValues(1);
-      }
-
-      public TestChecker addToNumberOfValues(int increment) {
-        // if there is at least one call to this, we do not need to manually wait in the next check()
-        parent.needManualWait = false;
-        Integer currentExpected = values.computeIfAbsent(NUMBER_OF_VALUES, ActualAndExpected::new).expected;
-        return put(NUMBER_OF_VALUES, currentExpected + increment);
-      }
-
-      public TestChecker setActualNumberOfValues(Callable<Integer> actual) {
-        setActual(NUMBER_OF_VALUES, actual);
-        values.get(NUMBER_OF_VALUES).expected = 0;
-        return parent;
-      }
-    }
-    private final static String NUMBER_OF_VALUES = "numberOfValues";
-    public final ValuesToCompare<Object> objectValues = new ValuesToCompare<>(this);
-    public final ValuesToCompare<String> stringValues = new ValuesToCompare<>(this);
-    public final ValuesToCompare<Tuple> tupleValues = new ValuesToCompare<>(this);
-    public final IntegerValuesToCompare intValues = new IntegerValuesToCompare(this);
-    private boolean needManualWait = true;
-    private boolean useManualWait = true;
-
-    public TestChecker incNumberOfValues() {
-      return intValues.incNumberOfValues();
-    }
-
-    public TestChecker addToNumberOfValues(int increment) {
-      return intValues.addToNumberOfValues(increment);
-    }
-
-    public TestChecker setActualNumberOfValues(Callable<Integer> actual) {
-      return intValues.setActualNumberOfValues(actual);
-    }
-
-    public TestChecker alwaysWait() {
-      return setActualNumberOfValues(() -> 0);
-    }
-
-    public TestChecker put(String name, Object expected) {
-      return objectValues.put(name, expected);
-    }
-
-    public TestChecker setCheckForObject(String name, BiConsumer<String, Object> check) {
-      return objectValues.setCheck(name, check);
-    }
-
-    public TestChecker setActualString(String name, Callable<String> actual) {
-      return stringValues.setActual(name, actual);
-    }
-
-    public TestChecker setCheckForString(String name, BiConsumer<String, String> check) {
-      return stringValues.setCheck(name, check);
-    }
-
-    public TestChecker put(String name, String expected) {
-      return stringValues.put(name, expected);
-    }
-
-    public TestChecker setActualTuple(String name, Callable<Tuple> actual) {
-      return tupleValues.setActual(name, actual);
-    }
-
-    public TestChecker setCheckForTuple(String name, BiConsumer<String, Tuple> check) {
-      return tupleValues.setCheck(name, check);
-    }
-
-    public TestChecker put(String name, Tuple expected) {
-      return tupleValues.put(name, expected);
-    }
-
-    public TestChecker setActualInteger(String name, Callable<Integer> actual) {
-      return intValues.setActual(name, actual);
-    }
-
-    public TestChecker setCheckForInteger(String name, BiConsumer<String, Integer> check) {
-      return intValues.setCheck(name, check);
-    }
-
-    public TestChecker put(String name, Integer expected) {
-      return intValues.put(name, expected);
-    }
-
-    public TestChecker disableManualWait() {
-      useManualWait = false;
-      needManualWait = false;
-      return this;
-    }
-
-    public void check() {
-      if (needManualWait) {
-        try {
-          waitForMqtt();
-        } catch (InterruptedException e) {
-          fail(e);
-        }
-      }
-      intValues.get(NUMBER_OF_VALUES).checkAwait(NUMBER_OF_VALUES);
-
-      objectValues.forEach((name, aae) -> aae.check(name));
-      stringValues.forEach((name, aae) -> aae.check(name));
-      tupleValues.forEach((name, aae) -> aae.check(name));
-      intValues.forEach((name, aae) -> {
-        if (!name.equals(NUMBER_OF_VALUES)) {
-          aae.check(name);
-        }
-      });
-      needManualWait = useManualWait;
-    }
-  }
-
-  public static class IntList {
-    private final List<Integer> integers;
-    public IntList(Integer... values) {
-      integers = Arrays.stream(values).filter(Objects::nonNull).collect(Collectors.toList());
-    }
-
-    public List<Integer> toList() {
-      return integers;
-    }
-
-    public List<Integer> toAbsList() {
-      return integers.stream().map(Math::abs).collect(Collectors.toList());
-    }
-
-    public static IntList list(Integer... values) {
-      return new IntList(values);
-    }
-  }
-
-  @SuppressWarnings({"unused", "rawtypes"})
-  public static class DefaultMappings {
-    static class ReadNode extends defaultOnlyRead.ast.ASTNode {
-      public boolean DefaultBytesToBooleanMapping(byte[] input) throws Exception {
-        return _ragconnect__apply__DefaultBytesToBooleanMapping(input);
-      }
-      public int DefaultBytesToIntMapping(byte[] input) throws Exception {
-        return _ragconnect__apply__DefaultBytesToIntMapping(input);
-      }
-      public short DefaultBytesToShortMapping(byte[] input) throws Exception {
-        return _ragconnect__apply__DefaultBytesToShortMapping(input);
-      }
-      public long DefaultBytesToLongMapping(byte[] input) throws Exception {
-        return _ragconnect__apply__DefaultBytesToLongMapping(input);
-      }
-      public float DefaultBytesToFloatMapping(byte[] input) throws Exception {
-        return _ragconnect__apply__DefaultBytesToFloatMapping(input);
-      }
-      public double DefaultBytesToDoubleMapping(byte[] input) throws Exception {
-        return _ragconnect__apply__DefaultBytesToDoubleMapping(input);
-      }
-      public char DefaultBytesToCharMapping(byte[] input) throws Exception {
-        return _ragconnect__apply__DefaultBytesToCharMapping(input);
-      }
-      public String DefaultBytesToStringMapping(byte[] input) throws Exception {
-        return _ragconnect__apply__DefaultBytesToStringMapping(input);
-      }
-    }
-
-    static class WriteNode extends defaultOnlyWrite.ast.ASTNode {
-      public byte[] DefaultBooleanToBytesMapping(boolean input) throws Exception {
-        return _ragconnect__apply__DefaultBooleanToBytesMapping(input);
-      }
-      public byte[] DefaultIntToBytesMapping(int input) throws Exception {
-        return _ragconnect__apply__DefaultIntToBytesMapping(input);
-      }
-      public byte[] DefaultShortToBytesMapping(short input) throws Exception {
-        return _ragconnect__apply__DefaultShortToBytesMapping(input);
-      }
-      public byte[] DefaultLongToBytesMapping(long input) throws Exception {
-        return _ragconnect__apply__DefaultLongToBytesMapping(input);
-      }
-      public byte[] DefaultFloatToBytesMapping(float input) throws Exception {
-        return _ragconnect__apply__DefaultFloatToBytesMapping(input);
-      }
-      public byte[] DefaultDoubleToBytesMapping(double input) throws Exception {
-        return _ragconnect__apply__DefaultDoubleToBytesMapping(input);
-      }
-      public byte[] DefaultCharToBytesMapping(char input) throws Exception {
-        return _ragconnect__apply__DefaultCharToBytesMapping(input);
-      }
-      public byte[] DefaultStringToBytesMapping(String input) throws Exception {
-        return _ragconnect__apply__DefaultStringToBytesMapping(input);
-      }
-    }
-    @FunctionalInterface
-    interface SerializeFunction<E extends Throwable> {
-      void accept(JsonGenerator g, String fieldName) throws E;
-    }
-
-    static ReadNode readNode = new ReadNode();
-    static WriteNode writeNode = new WriteNode();
-
-    public static boolean BytesToBool(byte[] input) {
-      try {
-        return readNode.DefaultBytesToBooleanMapping(input);
-      } catch (Exception e) {
-        e.printStackTrace();
-        return false;
-      }
-    }
-    public static int BytesToInt(byte[] input) {
-      try {
-        return readNode.DefaultBytesToIntMapping(input);
-      } catch (Exception e) {
-        e.printStackTrace();
-        return 0;
-      }
-    }
-    public static short BytesToShort(byte[] input) {
-      try {
-        return readNode.DefaultBytesToShortMapping(input);
-      } catch (Exception e) {
-        e.printStackTrace();
-        return 0;
-      }
-    }
-    public static long BytesToLong(byte[] input) {
-      try {
-        return readNode.DefaultBytesToLongMapping(input);
-      } catch (Exception e) {
-        e.printStackTrace();
-        return 0;
-      }
-    }
-    public static float BytesToFloat(byte[] input) {
-      try {
-        return readNode.DefaultBytesToFloatMapping(input);
-      } catch (Exception e) {
-        e.printStackTrace();
-        return 0;
-      }
-    }
-    public static double BytesToDouble(byte[] input) {
-      try {
-        return readNode.DefaultBytesToDoubleMapping(input);
-      } catch (Exception e) {
-        e.printStackTrace();
-        return 0;
-      }
-    }
-    public static char BytesToChar(byte[] input) {
-      try {
-        return readNode.DefaultBytesToCharMapping(input);
-      } catch (Exception e) {
-        e.printStackTrace();
-        return 0;
-      }
-    }
-    public static String BytesToString(byte[] input) {
-      try {
-        return readNode.DefaultBytesToStringMapping(input);
-      } catch (Exception e) {
-        e.printStackTrace();
-        return null;
-      }
-    }
-    public static byte[] BoolToBytes(boolean input) {
-      try {
-        return writeNode.DefaultBooleanToBytesMapping(input);
-      } catch (Exception e) {
-        e.printStackTrace();
-        return null;
-      }
-    }
-    public static byte[] IntToBytes(int input) {
-      try {
-        return writeNode.DefaultIntToBytesMapping(input);
-      } catch (Exception e) {
-        e.printStackTrace();
-        return null;
-      }
-    }
-    public static byte[] ShortToBytes(short input) {
-      try {
-        return writeNode.DefaultShortToBytesMapping(input);
-      } catch (Exception e) {
-        e.printStackTrace();
-        return null;
-      }
-    }
-    public static byte[] LongToBytes(long input) {
-      try {
-        return writeNode.DefaultLongToBytesMapping(input);
-      } catch (Exception e) {
-        e.printStackTrace();
-        return null;
-      }
-    }
-    public static byte[] FloatToBytes(float input) {
-      try {
-        return writeNode.DefaultFloatToBytesMapping(input);
-      } catch (Exception e) {
-        e.printStackTrace();
-        return null;
-      }
-    }
-    public static byte[] DoubleToBytes(double input) {
-      try {
-        return writeNode.DefaultDoubleToBytesMapping(input);
-      } catch (Exception e) {
-        e.printStackTrace();
-        return null;
-      }
-    }
-    public static byte[] CharToBytes(char input) {
-      try {
-        return writeNode.DefaultCharToBytesMapping(input);
-      } catch (Exception e) {
-        e.printStackTrace();
-        return null;
-      }
-    }
-    public static byte[] StringToBytes(String input) {
-      try {
-        return writeNode.DefaultStringToBytesMapping(input);
-      } catch (Exception e) {
-        e.printStackTrace();
-        return null;
-      }
-    }
-    public static <E extends Throwable> byte[] TreeToBytes(SerializeFunction<E> serializeFunction) throws E, IOException {
-      ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
-      JsonFactory factory = new JsonFactory();
-      JsonGenerator generator = factory.createGenerator(outputStream, JsonEncoding.UTF8);
-      serializeFunction.accept(generator, null);
-      generator.flush();
-      return outputStream.toString().getBytes();
-    }
-  }
-
-  public static class ChangeObserver {
-    Map<Supplier<String>, String> callableToPrevious = new HashMap<>();
-    private Callable<Boolean> hasChanged;
-
-    @SafeVarargs
-    public final void init(Supplier<String>... suppliers) {
-      callableToPrevious.clear();
-      Arrays.stream(suppliers).forEach(callable -> callableToPrevious.put(callable, callable.get()));
-      hasChanged = () -> callableToPrevious.entrySet().stream()
-              .anyMatch(entry -> !entry.getKey().get().equals(entry.getValue()));
-    }
-
-    public void start() {
-      updatePrevious();
-    }
-
-    private void updatePrevious() {
-      callableToPrevious.keySet().forEach(callable -> callableToPrevious.put(callable, callable.get()));
-    }
-
-    public void awaitChange() {
-      awaitMqtt().until(hasChanged);
-      updatePrevious();
-    }
-
-    public boolean hasChanged() {
-      try {
-        return hasChanged.call();
-      } catch (Exception e) {
-        fail(e);
-        return false;
-      }
-    }
-  }
-}
diff --git a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/TutorialTest.java b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/TutorialTest.java
index 27edceebbfe77fc165d2f8bb19b0ff37c8e21a8c..490f8c5a752290964fa67561b272c22984d1501a 100644
--- a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/TutorialTest.java
+++ b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/TutorialTest.java
@@ -5,7 +5,7 @@ import tutorial.ast.B;
 
 import java.io.IOException;
 
-import static org.jastadd.ragconnect.tests.TestUtils.mqttUri;
+import static org.jastadd.ragconnect.tests.utils.TestUtils.mqttUri;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 
 /**
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 4c1be6b4c25ef67360f7fc7dd898afc3ad1d8168..9401cf6180733c316560509e98af2fbc9922259f 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
@@ -1,6 +1,8 @@
 package org.jastadd.ragconnect.tests;
 
-import org.jastadd.ragconnect.tests.TestUtils.TestChecker;
+import org.jastadd.ragconnect.tests.utils.DefaultMappings;
+import org.jastadd.ragconnect.tests.utils.TestUtils;
+import org.jastadd.ragconnect.tests.utils.TestChecker;
 import org.junit.jupiter.api.Tag;
 import via.ast.A;
 import via.ast.MqttHandler;
@@ -13,8 +15,8 @@ import javax.ws.rs.core.MediaType;
 import java.io.IOException;
 import java.util.concurrent.TimeUnit;
 
-import static org.jastadd.ragconnect.tests.TestUtils.mqttUri;
-import static org.jastadd.ragconnect.tests.TestUtils.restUri;
+import static org.jastadd.ragconnect.tests.utils.TestUtils.mqttUri;
+import static org.jastadd.ragconnect.tests.utils.TestUtils.restUri;
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 
@@ -91,15 +93,15 @@ public class ViaTest extends AbstractMqttTest {
 
     handler.newConnection(TOPIC_MQTT_2_MQTT_SEND, bytes -> {
       dataMqtt2Mqtt.numberOfStringValues += 1;
-      dataMqtt2Mqtt.lastStringValue = TestUtils.DefaultMappings.BytesToString(bytes);
+      dataMqtt2Mqtt.lastStringValue = DefaultMappings.BytesToString(bytes);
     });
     handler.newConnection(TOPIC_REST_2_MQTT_SEND, bytes -> {
       dataRest2Mqtt.numberOfStringValues += 1;
-      dataRest2Mqtt.lastStringValue = TestUtils.DefaultMappings.BytesToString(bytes);
+      dataRest2Mqtt.lastStringValue = DefaultMappings.BytesToString(bytes);
     });
     handler.newConnection(TOPIC_BOTH_2_MQTT_SEND, bytes -> {
       dataBoth2Mqtt.numberOfStringValues += 1;
-      dataBoth2Mqtt.lastStringValue = TestUtils.DefaultMappings.BytesToString(bytes);
+      dataBoth2Mqtt.lastStringValue = DefaultMappings.BytesToString(bytes);
     });
 
     checker = new TestChecker();
diff --git a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/WarningsTest.java b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/WarningsTest.java
index 0ce33be11648a41cd3a6c84c7ca61e848551afa0..50dc46d2bff45651d46b473c7bf6a8268717385b 100644
--- a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/WarningsTest.java
+++ b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/WarningsTest.java
@@ -1,5 +1,6 @@
 package org.jastadd.ragconnect.tests;
 
+import org.jastadd.ragconnect.tests.utils.TestUtils;
 import org.junit.jupiter.api.BeforeAll;
 import org.junit.jupiter.api.Test;
 
@@ -11,7 +12,7 @@ import java.util.Collections;
 import java.util.List;
 
 import static org.assertj.core.api.Assertions.assertThat;
-import static org.jastadd.ragconnect.tests.TestUtils.readFile;
+import static org.jastadd.ragconnect.tests.utils.TestUtils.readFile;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 
 /**
diff --git a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/list/AbstractListTest.java b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/list/AbstractListTest.java
index c22207d92347ec7879a98b1cfb29ed88890405d8..d4a432f93e0dc6b1b930a2d2feb05d206fcec15d 100644
--- a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/list/AbstractListTest.java
+++ b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/list/AbstractListTest.java
@@ -1,8 +1,8 @@
 package org.jastadd.ragconnect.tests.list;
 
 import org.jastadd.ragconnect.tests.AbstractMqttTest;
-import org.jastadd.ragconnect.tests.TestUtils.IntList;
-import org.jastadd.ragconnect.tests.TestUtils.TestChecker;
+import org.jastadd.ragconnect.tests.utils.IntList;
+import org.jastadd.ragconnect.tests.utils.TestChecker;
 import org.junit.jupiter.api.Tag;
 import org.junit.jupiter.api.Test;
 
@@ -11,8 +11,8 @@ import java.nio.file.Paths;
 import java.util.List;
 import java.util.function.Function;
 
-import static org.jastadd.ragconnect.tests.TestUtils.IntList.list;
-import static org.jastadd.ragconnect.tests.TestUtils.testJaddContainReferenceToJackson;
+import static org.jastadd.ragconnect.tests.utils.IntList.list;
+import static org.jastadd.ragconnect.tests.utils.TestUtils.testJaddContainReferenceToJackson;
 import static org.junit.jupiter.api.Assertions.assertEquals;
 
 /**
diff --git a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/list/ListIncrementalTest.java b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/list/ListIncrementalTest.java
index 75010c83a554a7bbd0955a1574fab5d676bf7d30..1aabdc43ed94a5f3efaf0e01184beeaba3b56c23 100644
--- a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/list/ListIncrementalTest.java
+++ b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/list/ListIncrementalTest.java
@@ -1,13 +1,13 @@
 package org.jastadd.ragconnect.tests.list;
 
 import listInc.ast.*;
-import org.jastadd.ragconnect.tests.TestUtils;
+import org.jastadd.ragconnect.tests.utils.TestUtils;
 import org.junit.jupiter.api.Tag;
 
 import java.io.IOException;
 import java.util.concurrent.TimeUnit;
 
-import static org.jastadd.ragconnect.tests.TestUtils.mqttUri;
+import static org.jastadd.ragconnect.tests.utils.TestUtils.mqttUri;
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 
diff --git a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/list/ListManualTest.java b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/list/ListManualTest.java
index a0e9aa85586b2cc5196f526781d33879145f96b2..1b9aadcb3d25a69d1405798e4646061fa659f3fe 100644
--- a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/list/ListManualTest.java
+++ b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/list/ListManualTest.java
@@ -4,12 +4,12 @@ import list.ast.MqttHandler;
 import list.ast.ReceiverRoot;
 import list.ast.Root;
 import list.ast.SenderRoot;
-import org.jastadd.ragconnect.tests.TestUtils;
+import org.jastadd.ragconnect.tests.utils.TestUtils;
 
 import java.io.IOException;
 import java.util.concurrent.TimeUnit;
 
-import static org.jastadd.ragconnect.tests.TestUtils.mqttUri;
+import static org.jastadd.ragconnect.tests.utils.TestUtils.mqttUri;
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 
diff --git a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/relation/RelationTest.java b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/relation/RelationTest.java
index 9d38f1836f12c2eb95e22f44f9b21c1e0af7b013..f4ebfc5c10bbbed01ab133445ad65a3728b92a69 100644
--- a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/relation/RelationTest.java
+++ b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/relation/RelationTest.java
@@ -1,7 +1,7 @@
 package org.jastadd.ragconnect.tests.relation;
 
 import org.jastadd.ragconnect.tests.AbstractMqttTest;
-import org.jastadd.ragconnect.tests.TestUtils;
+import org.jastadd.ragconnect.tests.utils.TestUtils;
 import org.junit.jupiter.api.Tag;
 import relationInc.ast.*;
 
@@ -11,8 +11,9 @@ import java.util.concurrent.TimeUnit;
 
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.assertj.core.groups.Tuple.tuple;
-import static org.jastadd.ragconnect.tests.TestUtils.TestChecker;
-import static org.jastadd.ragconnect.tests.TestUtils.mqttUri;
+
+import org.jastadd.ragconnect.tests.utils.TestChecker;
+import static org.jastadd.ragconnect.tests.utils.TestUtils.mqttUri;
 import static org.junit.jupiter.api.Assertions.*;
 
 /**
diff --git a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/singleList/AbstractSingleListTest.java b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/singleList/AbstractSingleListTest.java
index 2cc90eff6205674a313b30c984a4b7f08c5cd082..f3b792d017ef831cd7b29ea6fb05e092cac1f38e 100644
--- a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/singleList/AbstractSingleListTest.java
+++ b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/singleList/AbstractSingleListTest.java
@@ -1,7 +1,9 @@
 package org.jastadd.ragconnect.tests.singleList;
 
 import org.jastadd.ragconnect.tests.AbstractMqttTest;
-import org.jastadd.ragconnect.tests.TestUtils;
+import org.jastadd.ragconnect.tests.utils.IntList;
+import org.jastadd.ragconnect.tests.utils.TestChecker;
+import org.jastadd.ragconnect.tests.utils.TestUtils;
 import org.junit.jupiter.api.Tag;
 import org.junit.jupiter.api.Test;
 import singleList.ast.MqttHandler;
@@ -13,8 +15,8 @@ import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.function.Function;
 
-import static org.jastadd.ragconnect.tests.TestUtils.*;
-import static org.jastadd.ragconnect.tests.TestUtils.IntList.list;
+import static org.jastadd.ragconnect.tests.utils.TestUtils.*;
+import static org.jastadd.ragconnect.tests.utils.IntList.list;
 import static org.junit.jupiter.api.Assertions.*;
 
 /**
diff --git a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/singleListVariant/AbstractSingleListVariantTest.java b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/singleListVariant/AbstractSingleListVariantTest.java
index f84cd7946c25a25bebd46f9291b323f20c16377a..6bd40a7a5e8135aec0e2e2f45ed5e271fff1239c 100644
--- a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/singleListVariant/AbstractSingleListVariantTest.java
+++ b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/singleListVariant/AbstractSingleListVariantTest.java
@@ -2,8 +2,8 @@ package org.jastadd.ragconnect.tests.singleListVariant;
 
 import org.assertj.core.api.Assertions;
 import org.jastadd.ragconnect.tests.AbstractMqttTest;
-import org.jastadd.ragconnect.tests.TestUtils.IntList;
-import org.jastadd.ragconnect.tests.TestUtils.TestChecker;
+import org.jastadd.ragconnect.tests.utils.IntList;
+import org.jastadd.ragconnect.tests.utils.TestChecker;
 import org.junit.jupiter.api.Tag;
 import org.junit.jupiter.api.Test;
 
@@ -13,9 +13,9 @@ import java.util.List;
 import java.util.function.BiConsumer;
 
 import static java.lang.Math.abs;
-import static org.jastadd.ragconnect.tests.TestUtils.IntList.list;
-import static org.jastadd.ragconnect.tests.TestUtils.mqttUri;
-import static org.jastadd.ragconnect.tests.TestUtils.testJaddContainReferenceToJackson;
+import static org.jastadd.ragconnect.tests.utils.IntList.list;
+import static org.jastadd.ragconnect.tests.utils.TestUtils.mqttUri;
+import static org.jastadd.ragconnect.tests.utils.TestUtils.testJaddContainReferenceToJackson;
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 
@@ -372,10 +372,10 @@ public abstract class AbstractSingleListVariantTest extends AbstractMqttTest {
    * Check against expected lists of IDs.
    * If an ID is negative, do not check Opts and Lists, but use absolute value for comparison.
    * The tests starts with ID 0 and does not use opts and lists at this point, so checking with > 0 is used.
-   * @param expectedList                     ids for unnamed and named endpoints without add
-   * @param expectedWithAddList              ids for endpoints with add, but not those with opts and lists
+   * @param expectedList                     ids for unnamed and named ports without add
+   * @param expectedWithAddList              ids for ports with add, but not those with opts and lists
    *                                         (only positive numbers can/need to be passed)
-   * @param expectedWithAddListForOptAndList ids for endpoints with add and with opts and lists
+   * @param expectedWithAddListForOptAndList ids for ports with add and with opts and lists
    */
   private void checkTree(IntList expectedList,
                          IntList expectedWithAddList,
diff --git a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/singleListVariant/SingleListVariantIncrementalTest.java b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/singleListVariant/SingleListVariantIncrementalTest.java
index c1b37cc695a1470896da2e19b18a296bcb008ec7..1d74649ccb0c714f7a3ee39beeb1d512e560dcf5 100644
--- a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/singleListVariant/SingleListVariantIncrementalTest.java
+++ b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/singleListVariant/SingleListVariantIncrementalTest.java
@@ -1,6 +1,6 @@
 package org.jastadd.ragconnect.tests.singleListVariant;
 
-import org.jastadd.ragconnect.tests.TestUtils;
+import org.jastadd.ragconnect.tests.utils.TestUtils;
 import org.junit.jupiter.api.Tag;
 import singleListVariantInc.ast.MqttHandler;
 import singleListVariantInc.ast.ReceiverRoot;
diff --git a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/singleListVariant/SingleListVariantManualTest.java b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/singleListVariant/SingleListVariantManualTest.java
index 60238c8a1ca3c1301d08512cc469ce78bdc13e18..907826d455e334b5b781b2fbeb1b4dea0d6ff4af 100644
--- a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/singleListVariant/SingleListVariantManualTest.java
+++ b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/singleListVariant/SingleListVariantManualTest.java
@@ -1,6 +1,6 @@
 package org.jastadd.ragconnect.tests.singleListVariant;
 
-import org.jastadd.ragconnect.tests.TestUtils;
+import org.jastadd.ragconnect.tests.utils.TestUtils;
 import singleListVariant.ast.*;
 
 import java.io.IOException;
diff --git a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/tokenValue/AbstractTokenValueSendTest.java b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/tokenValue/AbstractTokenValueSendTest.java
index fcc3e8a9b60c7aebefb0884418959e6ea194c158..5d55b729f26fb4d95fd7898b30d8ff1647deafdf 100644
--- a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/tokenValue/AbstractTokenValueSendTest.java
+++ b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/tokenValue/AbstractTokenValueSendTest.java
@@ -1,9 +1,9 @@
 package org.jastadd.ragconnect.tests.tokenValue;
 
 import org.jastadd.ragconnect.tests.AbstractMqttTest;
-import org.jastadd.ragconnect.tests.TestUtils;
-import org.jastadd.ragconnect.tests.TestUtils.TestChecker;
-import org.junit.jupiter.api.Tag;
+import org.jastadd.ragconnect.tests.utils.DefaultMappings;
+import org.jastadd.ragconnect.tests.utils.TestUtils;
+import org.jastadd.ragconnect.tests.utils.TestChecker;
 import org.junit.jupiter.api.Test;
 import tokenValueSend.ast.MqttHandler;
 
@@ -11,8 +11,8 @@ 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.jastadd.ragconnect.tests.utils.TestUtils.mqttUri;
+import static org.jastadd.ragconnect.tests.utils.TestUtils.testJaddContainReferenceToJackson;
 import static org.junit.jupiter.api.Assertions.*;
 
 /**
@@ -68,20 +68,20 @@ public abstract class AbstractTokenValueSendTest extends AbstractMqttTest {
 
     handler.newConnection(TOPIC_SEND_ONE, bytes -> {
       dataOne.numberOfStringValues += 1;
-      dataOne.lastStringValue = TestUtils.DefaultMappings.BytesToString(bytes);
+      dataOne.lastStringValue = DefaultMappings.BytesToString(bytes);
     });
     handler.newConnection(TOPIC_SEND_TWO, bytes -> {
       dataTwo.numberOfStringValues += 1;
-      dataTwo.lastStringValue = TestUtils.DefaultMappings.BytesToString(bytes);
+      dataTwo.lastStringValue = DefaultMappings.BytesToString(bytes);
     });
 
     handler.newConnection(TOPIC_SEND_THREE_VALUE, bytes -> {
       dataThree.numberOfStringValues += 1;
-      dataThree.lastStringValue = TestUtils.DefaultMappings.BytesToString(bytes);
+      dataThree.lastStringValue = DefaultMappings.BytesToString(bytes);
     });
     handler.newConnection(TOPIC_SEND_THREE_OTHER, bytes -> {
       dataThreeOther.numberOfStringValues += 1;
-      dataThreeOther.lastStringValue = TestUtils.DefaultMappings.BytesToString(bytes);
+      dataThreeOther.lastStringValue = DefaultMappings.BytesToString(bytes);
     });
 
     connectModel();
diff --git a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/tokenValue/TokenValueSendIncrementalTest.java b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/tokenValue/TokenValueSendIncrementalTest.java
index 9676b9ca75daba39580da7eb848f6d088f5073e8..bdb1ab9bf735db280d6022f445140495d3ff955f 100644
--- a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/tokenValue/TokenValueSendIncrementalTest.java
+++ b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/tokenValue/TokenValueSendIncrementalTest.java
@@ -6,7 +6,7 @@ import tokenValueSendInc.ast.*;
 import java.io.IOException;
 import java.util.concurrent.TimeUnit;
 
-import static org.jastadd.ragconnect.tests.TestUtils.mqttUri;
+import static org.jastadd.ragconnect.tests.utils.TestUtils.mqttUri;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 
 /**
diff --git a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/tokenValue/TokenValueSendManualTest.java b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/tokenValue/TokenValueSendManualTest.java
index a24f8a8be3358118978019bb25d26e2a276a897b..ac13bfacb45cfdb72ac9472dcba6e176dc553680 100644
--- a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/tokenValue/TokenValueSendManualTest.java
+++ b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/tokenValue/TokenValueSendManualTest.java
@@ -5,7 +5,7 @@ import tokenValueSend.ast.*;
 import java.io.IOException;
 import java.util.concurrent.TimeUnit;
 
-import static org.jastadd.ragconnect.tests.TestUtils.mqttUri;
+import static org.jastadd.ragconnect.tests.utils.TestUtils.mqttUri;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 
 /**
diff --git a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/tree/AbstractTreeTest.java b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/tree/AbstractTreeTest.java
index 22cc2c0c4334d86c96fdec48682b7a92842fbc9f..9b2e18f5a5fd075843b4022386408c944506f0be 100644
--- a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/tree/AbstractTreeTest.java
+++ b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/tree/AbstractTreeTest.java
@@ -1,7 +1,7 @@
 package org.jastadd.ragconnect.tests.tree;
 
 import org.jastadd.ragconnect.tests.AbstractMqttTest;
-import org.jastadd.ragconnect.tests.TestUtils;
+import org.jastadd.ragconnect.tests.utils.TestUtils;
 import org.junit.jupiter.api.Tag;
 
 import java.io.IOException;
@@ -16,6 +16,7 @@ import static org.junit.jupiter.api.Assertions.*;
  * @author rschoene - Initial contribution
  */
 @Tag("Tree")
+@Tag("New")
 public abstract class AbstractTreeTest extends AbstractMqttTest {
   protected static final String TOPIC_ALFA = "alfa";
   protected ReceiverData data;
diff --git a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/tree/TreeIncrementalTest.java b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/tree/TreeIncrementalTest.java
index 542d4a3ed9ce1ec0e22f41dfc7e7b5fe495ad6de..419e5a4aae21dbcfc3018a289bc91e0b2753ba17 100644
--- a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/tree/TreeIncrementalTest.java
+++ b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/tree/TreeIncrementalTest.java
@@ -1,6 +1,6 @@
 package org.jastadd.ragconnect.tests.tree;
 
-import org.jastadd.ragconnect.tests.TestUtils;
+import org.jastadd.ragconnect.tests.utils.TestUtils;
 import org.junit.jupiter.api.Tag;
 import org.junit.jupiter.api.Test;
 import treeInc.ast.*;
@@ -10,8 +10,8 @@ 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.jastadd.ragconnect.tests.utils.TestUtils.mqttUri;
+import static org.jastadd.ragconnect.tests.utils.TestUtils.testJaddContainReferenceToJackson;
 import static org.junit.jupiter.api.Assertions.*;
 
 /**
diff --git a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/tree/TreeManualTest.java b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/tree/TreeManualTest.java
index c48335ffb07699cc22e7327943c6c2fc89341213..c0845d514f1fe5e62f1c8af85e9feadff2f903c4 100644
--- a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/tree/TreeManualTest.java
+++ b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/tree/TreeManualTest.java
@@ -1,7 +1,6 @@
 package org.jastadd.ragconnect.tests.tree;
 
-import org.jastadd.ragconnect.tests.TestUtils;
-import org.junit.jupiter.api.Tag;
+import org.jastadd.ragconnect.tests.utils.TestUtils;
 import org.junit.jupiter.api.Test;
 import tree.ast.MqttHandler;
 import tree.ast.ReceiverRoot;
@@ -12,8 +11,8 @@ 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.jastadd.ragconnect.tests.utils.TestUtils.mqttUri;
+import static org.jastadd.ragconnect.tests.utils.TestUtils.testJaddContainReferenceToJackson;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 
 /**
diff --git a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/treeAllowedTokens/AbstractTreeAllowedTokensTest.java b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/treeAllowedTokens/AbstractTreeAllowedTokensTest.java
index 7c29538a71609e73a82fe0e22ad6c8bd74582f8c..533d5d1ebb1fe1f82547a7b3120ddcb636432e63 100644
--- a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/treeAllowedTokens/AbstractTreeAllowedTokensTest.java
+++ b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/treeAllowedTokens/AbstractTreeAllowedTokensTest.java
@@ -1,7 +1,8 @@
 package org.jastadd.ragconnect.tests.treeAllowedTokens;
 
 import org.jastadd.ragconnect.tests.AbstractMqttTest;
-import org.jastadd.ragconnect.tests.TestUtils;
+import org.jastadd.ragconnect.tests.utils.DefaultMappings;
+import org.jastadd.ragconnect.tests.utils.TestUtils;
 import org.junit.jupiter.api.Tag;
 
 import java.io.IOException;
@@ -183,19 +184,19 @@ public abstract class AbstractTreeAllowedTokensTest extends AbstractMqttTest {
   protected abstract void disconnectSend() throws IOException;
 
   protected void sendInput1WhenFalse(int value) {
-    publisher.publish(TOPIC_INPUT1FALSE, TestUtils.DefaultMappings.IntToBytes(value));
+    publisher.publish(TOPIC_INPUT1FALSE, DefaultMappings.IntToBytes(value));
   }
 
   protected void sendInput1WhenTrue(int value) {
-    publisher.publish(TOPIC_INPUT1TRUE, TestUtils.DefaultMappings.IntToBytes(value));
+    publisher.publish(TOPIC_INPUT1TRUE, DefaultMappings.IntToBytes(value));
   }
 
   protected void sendInput2(String value) {
-    publisher.publish(TOPIC_INPUT2, TestUtils.DefaultMappings.StringToBytes(value));
+    publisher.publish(TOPIC_INPUT2, DefaultMappings.StringToBytes(value));
   }
 
   protected void sendInput3(double value) {
-    publisher.publish(TOPIC_INPUT3, TestUtils.DefaultMappings.DoubleToBytes(value));
+    publisher.publish(TOPIC_INPUT3, DefaultMappings.DoubleToBytes(value));
   }
 
   protected abstract void setFlag(boolean value);
diff --git a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/treeAllowedTokens/TreeAllowedTokensIncrementalTest.java b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/treeAllowedTokens/TreeAllowedTokensIncrementalTest.java
index d4cc6d5b76e510ad504b30489217f37bd0f0b834..7d6ab574dafd79d1948412408f45ffaed54db449 100644
--- a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/treeAllowedTokens/TreeAllowedTokensIncrementalTest.java
+++ b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/treeAllowedTokens/TreeAllowedTokensIncrementalTest.java
@@ -1,6 +1,6 @@
 package org.jastadd.ragconnect.tests.treeAllowedTokens;
 
-import org.jastadd.ragconnect.tests.TestUtils;
+import org.jastadd.ragconnect.tests.utils.TestUtils;
 import org.junit.jupiter.api.Tag;
 import org.junit.jupiter.api.Test;
 import treeAllowedTokensInc.ast.*;
@@ -9,8 +9,8 @@ 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.jastadd.ragconnect.tests.utils.TestUtils.mqttUri;
+import static org.jastadd.ragconnect.tests.utils.TestUtils.testJaddContainReferenceToJackson;
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 
diff --git a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/treeAllowedTokens/TreeAllowedTokensManualTest.java b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/treeAllowedTokens/TreeAllowedTokensManualTest.java
index 96e0080ae07c462f2b7902c7be9e56b417ac4ed4..7479396772bc83b8e4ba5c35151145af3c60c3f6 100644
--- a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/treeAllowedTokens/TreeAllowedTokensManualTest.java
+++ b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/treeAllowedTokens/TreeAllowedTokensManualTest.java
@@ -1,7 +1,6 @@
 package org.jastadd.ragconnect.tests.treeAllowedTokens;
 
-import org.jastadd.ragconnect.tests.TestUtils;
-import org.junit.jupiter.api.Tag;
+import org.jastadd.ragconnect.tests.utils.TestUtils;
 import org.junit.jupiter.api.Test;
 import treeAllowedTokens.ast.*;
 
@@ -9,8 +8,8 @@ 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.jastadd.ragconnect.tests.utils.TestUtils.mqttUri;
+import static org.jastadd.ragconnect.tests.utils.TestUtils.testJaddContainReferenceToJackson;
 import static org.junit.jupiter.api.Assertions.*;
 
 /**
diff --git a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/utils/ChangeObserver.java b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/utils/ChangeObserver.java
new file mode 100644
index 0000000000000000000000000000000000000000..2b0dbef6668a98e5d8aa4e9a64d9687f6885ca2c
--- /dev/null
+++ b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/utils/ChangeObserver.java
@@ -0,0 +1,49 @@
+package org.jastadd.ragconnect.tests.utils;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.Callable;
+import java.util.function.Supplier;
+
+import static org.junit.jupiter.api.Assertions.fail;
+
+/**
+ * TODO: Add description.
+ *
+ * @author rschoene - Initial contribution
+ */
+public class ChangeObserver {
+  Map<Supplier<String>, String> callableToPrevious = new HashMap<>();
+  private Callable<Boolean> hasChanged;
+
+  @SafeVarargs
+  public final void init(Supplier<String>... suppliers) {
+    callableToPrevious.clear();
+    Arrays.stream(suppliers).forEach(callable -> callableToPrevious.put(callable, callable.get()));
+    hasChanged = () -> callableToPrevious.entrySet().stream()
+            .anyMatch(entry -> !entry.getKey().get().equals(entry.getValue()));
+  }
+
+  public void start() {
+    updatePrevious();
+  }
+
+  private void updatePrevious() {
+    callableToPrevious.keySet().forEach(callable -> callableToPrevious.put(callable, callable.get()));
+  }
+
+  public void awaitChange() {
+    TestUtils.awaitMqtt().until(hasChanged);
+    updatePrevious();
+  }
+
+  public boolean hasChanged() {
+    try {
+      return hasChanged.call();
+    } catch (Exception e) {
+      fail(e);
+      return false;
+    }
+  }
+}
diff --git a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/utils/DefaultMappings.java b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/utils/DefaultMappings.java
new file mode 100644
index 0000000000000000000000000000000000000000..81711cfda338ca76311a5ec7f5a69f19497ac61d
--- /dev/null
+++ b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/utils/DefaultMappings.java
@@ -0,0 +1,246 @@
+package org.jastadd.ragconnect.tests.utils;
+
+import com.fasterxml.jackson.core.JsonEncoding;
+import com.fasterxml.jackson.core.JsonFactory;
+import com.fasterxml.jackson.core.JsonGenerator;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+
+/**
+ * TODO: Add description.
+ *
+ * @author rschoene - Initial contribution
+ */
+public class DefaultMappings {
+  @SuppressWarnings("rawtypes")
+  static class ReadNode extends defaultOnlyRead.ast.ASTNode {
+    public boolean DefaultBytesToBooleanMapping(byte[] input) throws Exception {
+      return _ragconnect__apply__DefaultBytesToBooleanMapping(input);
+    }
+
+    public int DefaultBytesToIntMapping(byte[] input) throws Exception {
+      return _ragconnect__apply__DefaultBytesToIntMapping(input);
+    }
+
+    public short DefaultBytesToShortMapping(byte[] input) throws Exception {
+      return _ragconnect__apply__DefaultBytesToShortMapping(input);
+    }
+
+    public long DefaultBytesToLongMapping(byte[] input) throws Exception {
+      return _ragconnect__apply__DefaultBytesToLongMapping(input);
+    }
+
+    public float DefaultBytesToFloatMapping(byte[] input) throws Exception {
+      return _ragconnect__apply__DefaultBytesToFloatMapping(input);
+    }
+
+    public double DefaultBytesToDoubleMapping(byte[] input) throws Exception {
+      return _ragconnect__apply__DefaultBytesToDoubleMapping(input);
+    }
+
+    public char DefaultBytesToCharMapping(byte[] input) throws Exception {
+      return _ragconnect__apply__DefaultBytesToCharMapping(input);
+    }
+
+    public String DefaultBytesToStringMapping(byte[] input) throws Exception {
+      return _ragconnect__apply__DefaultBytesToStringMapping(input);
+    }
+  }
+
+  @SuppressWarnings("rawtypes")
+  static class WriteNode extends defaultOnlyWrite.ast.ASTNode {
+    public byte[] DefaultBooleanToBytesMapping(boolean input) throws Exception {
+      return _ragconnect__apply__DefaultBooleanToBytesMapping(input);
+    }
+
+    public byte[] DefaultIntToBytesMapping(int input) throws Exception {
+      return _ragconnect__apply__DefaultIntToBytesMapping(input);
+    }
+
+    public byte[] DefaultShortToBytesMapping(short input) throws Exception {
+      return _ragconnect__apply__DefaultShortToBytesMapping(input);
+    }
+
+    public byte[] DefaultLongToBytesMapping(long input) throws Exception {
+      return _ragconnect__apply__DefaultLongToBytesMapping(input);
+    }
+
+    public byte[] DefaultFloatToBytesMapping(float input) throws Exception {
+      return _ragconnect__apply__DefaultFloatToBytesMapping(input);
+    }
+
+    public byte[] DefaultDoubleToBytesMapping(double input) throws Exception {
+      return _ragconnect__apply__DefaultDoubleToBytesMapping(input);
+    }
+
+    public byte[] DefaultCharToBytesMapping(char input) throws Exception {
+      return _ragconnect__apply__DefaultCharToBytesMapping(input);
+    }
+
+    public byte[] DefaultStringToBytesMapping(String input) throws Exception {
+      return _ragconnect__apply__DefaultStringToBytesMapping(input);
+    }
+  }
+
+  @FunctionalInterface
+  public interface SerializeFunction<E extends Throwable> {
+    void accept(JsonGenerator g, String fieldName) throws E;
+  }
+
+  static ReadNode readNode = new ReadNode();
+  static WriteNode writeNode = new WriteNode();
+
+  public static boolean BytesToBool(byte[] input) {
+    try {
+      return readNode.DefaultBytesToBooleanMapping(input);
+    } catch (Exception e) {
+      e.printStackTrace();
+      return false;
+    }
+  }
+
+  public static int BytesToInt(byte[] input) {
+    try {
+      return readNode.DefaultBytesToIntMapping(input);
+    } catch (Exception e) {
+      e.printStackTrace();
+      return 0;
+    }
+  }
+
+  public static short BytesToShort(byte[] input) {
+    try {
+      return readNode.DefaultBytesToShortMapping(input);
+    } catch (Exception e) {
+      e.printStackTrace();
+      return 0;
+    }
+  }
+
+  public static long BytesToLong(byte[] input) {
+    try {
+      return readNode.DefaultBytesToLongMapping(input);
+    } catch (Exception e) {
+      e.printStackTrace();
+      return 0;
+    }
+  }
+
+  public static float BytesToFloat(byte[] input) {
+    try {
+      return readNode.DefaultBytesToFloatMapping(input);
+    } catch (Exception e) {
+      e.printStackTrace();
+      return 0;
+    }
+  }
+
+  public static double BytesToDouble(byte[] input) {
+    try {
+      return readNode.DefaultBytesToDoubleMapping(input);
+    } catch (Exception e) {
+      e.printStackTrace();
+      return 0;
+    }
+  }
+
+  public static char BytesToChar(byte[] input) {
+    try {
+      return readNode.DefaultBytesToCharMapping(input);
+    } catch (Exception e) {
+      e.printStackTrace();
+      return 0;
+    }
+  }
+
+  public static String BytesToString(byte[] input) {
+    try {
+      return readNode.DefaultBytesToStringMapping(input);
+    } catch (Exception e) {
+      e.printStackTrace();
+      return null;
+    }
+  }
+
+  public static byte[] BoolToBytes(boolean input) {
+    try {
+      return writeNode.DefaultBooleanToBytesMapping(input);
+    } catch (Exception e) {
+      e.printStackTrace();
+      return null;
+    }
+  }
+
+  public static byte[] IntToBytes(int input) {
+    try {
+      return writeNode.DefaultIntToBytesMapping(input);
+    } catch (Exception e) {
+      e.printStackTrace();
+      return null;
+    }
+  }
+
+  public static byte[] ShortToBytes(short input) {
+    try {
+      return writeNode.DefaultShortToBytesMapping(input);
+    } catch (Exception e) {
+      e.printStackTrace();
+      return null;
+    }
+  }
+
+  public static byte[] LongToBytes(long input) {
+    try {
+      return writeNode.DefaultLongToBytesMapping(input);
+    } catch (Exception e) {
+      e.printStackTrace();
+      return null;
+    }
+  }
+
+  public static byte[] FloatToBytes(float input) {
+    try {
+      return writeNode.DefaultFloatToBytesMapping(input);
+    } catch (Exception e) {
+      e.printStackTrace();
+      return null;
+    }
+  }
+
+  public static byte[] DoubleToBytes(double input) {
+    try {
+      return writeNode.DefaultDoubleToBytesMapping(input);
+    } catch (Exception e) {
+      e.printStackTrace();
+      return null;
+    }
+  }
+
+  public static byte[] CharToBytes(char input) {
+    try {
+      return writeNode.DefaultCharToBytesMapping(input);
+    } catch (Exception e) {
+      e.printStackTrace();
+      return null;
+    }
+  }
+
+  public static byte[] StringToBytes(String input) {
+    try {
+      return writeNode.DefaultStringToBytesMapping(input);
+    } catch (Exception e) {
+      e.printStackTrace();
+      return null;
+    }
+  }
+
+  public static <E extends Throwable> byte[] TreeToBytes(SerializeFunction<E> serializeFunction) throws E, IOException {
+    ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+    JsonFactory factory = new JsonFactory();
+    JsonGenerator generator = factory.createGenerator(outputStream, JsonEncoding.UTF8);
+    serializeFunction.accept(generator, null);
+    generator.flush();
+    return outputStream.toString().getBytes();
+  }
+}
diff --git a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/utils/IntList.java b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/utils/IntList.java
new file mode 100644
index 0000000000000000000000000000000000000000..7dba3a4c3c161519d26f5db3021aa2a53df513bf
--- /dev/null
+++ b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/utils/IntList.java
@@ -0,0 +1,31 @@
+package org.jastadd.ragconnect.tests.utils;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Objects;
+import java.util.stream.Collectors;
+
+/**
+ * TODO: Add description.
+ *
+ * @author rschoene - Initial contribution
+ */
+public class IntList {
+  private final List<Integer> integers;
+
+  public IntList(Integer... values) {
+    integers = Arrays.stream(values).filter(Objects::nonNull).collect(Collectors.toList());
+  }
+
+  public List<Integer> toList() {
+    return integers;
+  }
+
+  public List<Integer> toAbsList() {
+    return integers.stream().map(Math::abs).collect(Collectors.toList());
+  }
+
+  public static IntList list(Integer... values) {
+    return new IntList(values);
+  }
+}
diff --git a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/utils/TestChecker.java b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/utils/TestChecker.java
new file mode 100644
index 0000000000000000000000000000000000000000..ed54fb954362c77bdbde120b127afb0deff34060
--- /dev/null
+++ b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/utils/TestChecker.java
@@ -0,0 +1,222 @@
+package org.jastadd.ragconnect.tests.utils;
+
+import org.assertj.core.groups.Tuple;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.Callable;
+import java.util.function.BiConsumer;
+import java.util.function.Function;
+import java.util.function.Predicate;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.junit.jupiter.api.Assertions.fail;
+
+/**
+ * Lean checking of constraints in tests.
+ *
+ * @author rschoene - Initial contribution
+ */
+public class TestChecker {
+  private static final Logger logger = LoggerFactory.getLogger(TestChecker.class);
+  private final static String NUMBER_OF_VALUES = "numberOfValues";
+  public final ValuesToCompare<Object> objectValues = new ValuesToCompare<>(this);
+  public final ValuesToCompare<String> stringValues = new ValuesToCompare<>(this);
+  public final ValuesToCompare<Tuple> tupleValues = new ValuesToCompare<>(this);
+  public final IntegerValuesToCompare intValues = new IntegerValuesToCompare(this);
+  private boolean needManualWait = true;
+  private boolean useManualWait = true;
+
+  public TestChecker incNumberOfValues() {
+    return intValues.incNumberOfValues();
+  }
+
+  public TestChecker addToNumberOfValues(int increment) {
+    return intValues.addToNumberOfValues(increment);
+  }
+
+  public TestChecker setActualNumberOfValues(Callable<Integer> actual) {
+    return intValues.setActualNumberOfValues(actual);
+  }
+
+  public TestChecker alwaysWait() {
+    return setActualNumberOfValues(() -> 0);
+  }
+
+  public TestChecker put(String name, Object expected) {
+    return objectValues.put(name, expected);
+  }
+
+  public TestChecker setCheckForObject(String name, BiConsumer<String, Object> check) {
+    return objectValues.setCheck(name, check);
+  }
+
+  public TestChecker setActualString(String name, Callable<String> actual) {
+    return stringValues.setActual(name, actual);
+  }
+
+  public TestChecker setCheckForString(String name, BiConsumer<String, String> check) {
+    return stringValues.setCheck(name, check);
+  }
+
+  public TestChecker put(String name, String expected) {
+    return stringValues.put(name, expected);
+  }
+
+  public TestChecker setActualTuple(String name, Callable<Tuple> actual) {
+    return tupleValues.setActual(name, actual);
+  }
+
+  public TestChecker setCheckForTuple(String name, BiConsumer<String, Tuple> check) {
+    return tupleValues.setCheck(name, check);
+  }
+
+  public TestChecker put(String name, Tuple expected) {
+    return tupleValues.put(name, expected);
+  }
+
+  public TestChecker setActualInteger(String name, Callable<Integer> actual) {
+    return intValues.setActual(name, actual);
+  }
+
+  public TestChecker setCheckForInteger(String name, BiConsumer<String, Integer> check) {
+    return intValues.setCheck(name, check);
+  }
+
+  public TestChecker put(String name, Integer expected) {
+    return intValues.put(name, expected);
+  }
+
+  public TestChecker disableManualWait() {
+    useManualWait = false;
+    needManualWait = false;
+    return this;
+  }
+
+  public void check() {
+    if (needManualWait) {
+      try {
+        TestUtils.waitForMqtt();
+      } catch (InterruptedException e) {
+        fail(e);
+      }
+    }
+    intValues.get(NUMBER_OF_VALUES).checkAwait(NUMBER_OF_VALUES);
+
+    objectValues.forEach((name, aae) -> aae.check(name));
+    stringValues.forEach((name, aae) -> aae.check(name));
+    tupleValues.forEach((name, aae) -> aae.check(name));
+    intValues.forEach((name, aae) -> {
+      if (!name.equals(NUMBER_OF_VALUES)) {
+        aae.check(name);
+      }
+    });
+    needManualWait = useManualWait;
+  }
+
+  static class ActualAndExpected<T> {
+    Callable<T> actual;
+    T expected;
+    BiConsumer<String, T> customCheck;
+
+    ActualAndExpected(String key) {
+      expected = null;
+    }
+
+    void check(String name) {
+      if (customCheck != null) {
+        customCheck.accept(name, expected);
+        return;
+      }
+      if (actual == null) {
+        fail("No actual getter defined for " + name);
+      }
+      T actualValue = null;
+      try {
+        actualValue = this.actual.call();
+      } catch (Exception e) {
+        fail(e);
+      }
+      assertThat(actualValue).as(name).isEqualTo(expected);
+    }
+
+    void checkAwait(String name) {
+      if (customCheck != null) {
+        logger.warn("Custom check set for {}. Can't await for that.", name);
+        customCheck.accept(name, expected);
+        return;
+      }
+      if (actual == null) {
+        fail("No actual getter defined for " + name);
+      }
+      TestUtils.awaitMqtt().alias(name + " == " + expected).until(actual, Predicate.isEqual(expected));
+    }
+  }
+
+  static class ValuesToCompare<T> {
+    protected final Map<String, ActualAndExpected<T>> values = new HashMap<>();
+    protected final TestChecker parent;
+
+    ValuesToCompare(TestChecker parent) {
+      this.parent = parent;
+    }
+
+    public TestChecker setActual(String name, Callable<T> actual) {
+      _computeIfAbsent(name).actual = actual;
+      return parent;
+    }
+
+    public TestChecker setCheck(String name, BiConsumer<String, T> check) {
+      _computeIfAbsent(name).customCheck = check;
+      return parent;
+    }
+
+    public TestChecker put(String name, T expected) {
+      _computeIfAbsent(name).expected = expected;
+      return parent;
+    }
+
+    public TestChecker updateExpected(String name, Function<T, T> updater) {
+      ActualAndExpected<T> aae = _computeIfAbsent(name);
+      aae.expected = updater.apply(aae.expected);
+      return parent;
+    }
+
+    private ActualAndExpected<T> _computeIfAbsent(String name) {
+      return values.computeIfAbsent(name, ActualAndExpected::new);
+    }
+
+    ActualAndExpected<T> get(String name) {
+      return values.get(name);
+    }
+
+    void forEach(BiConsumer<? super String, ? super ActualAndExpected<T>> action) {
+      values.forEach(action);
+    }
+  }
+
+  static class IntegerValuesToCompare extends ValuesToCompare<Integer> {
+    IntegerValuesToCompare(TestChecker parent) {
+      super(parent);
+    }
+
+    public TestChecker incNumberOfValues() {
+      return addToNumberOfValues(1);
+    }
+
+    public TestChecker addToNumberOfValues(int increment) {
+      // if there is at least one call to this, we do not need to manually wait in the next check()
+      parent.needManualWait = false;
+      Integer currentExpected = values.computeIfAbsent(NUMBER_OF_VALUES, ActualAndExpected::new).expected;
+      return put(NUMBER_OF_VALUES, currentExpected + increment);
+    }
+
+    public TestChecker setActualNumberOfValues(Callable<Integer> actual) {
+      setActual(NUMBER_OF_VALUES, actual);
+      values.get(NUMBER_OF_VALUES).expected = 0;
+      return parent;
+    }
+  }
+}
diff --git a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/utils/TestUtils.java b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/utils/TestUtils.java
new file mode 100644
index 0000000000000000000000000000000000000000..c77192b09c2e1c16b2728bcf6baf91d6209db541
--- /dev/null
+++ b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/utils/TestUtils.java
@@ -0,0 +1,181 @@
+package org.jastadd.ragconnect.tests.utils;
+
+import org.awaitility.Awaitility;
+import org.awaitility.core.ConditionFactory;
+import org.jastadd.ragconnect.compiler.Compiler;
+import org.junit.jupiter.api.Assertions;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+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.*;
+import java.util.concurrent.TimeUnit;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assertions.fail;
+
+/**
+ * Utility methods for tests.
+ *
+ * @author rschoene - Initial contribution
+ */
+public class TestUtils {
+
+  private static final Logger logger = LoggerFactory.getLogger(TestUtils.class);
+  public static final double DELTA = 0.001d;
+  public static final String INPUT_DIRECTORY_PREFIX = "./src/test/01-input/";
+  public static final String OUTPUT_DIRECTORY_PREFIX = "./src/test/02-after-ragconnect/";
+
+  public static boolean isCi() {
+    return System.getenv("GITLAB_CI") != null;
+  }
+
+  public static String getMqttHost() {
+    if (isCi()) {
+      // we are in the CI, so use "mqtt" as host
+      return "mqtt";
+    } {
+      // else assume a locally running mqtt broker
+      return "localhost";
+    }
+  }
+
+  public static String mqttUri(String path) {
+    return "mqtt://" + getMqttHost() + "/" + path;
+  }
+
+  public static String restUri(String path, int port) {
+    return "rest://localhost:" + port + "/" + path;
+  }
+
+  public static String javaUri(String path) {
+    return "java://localhost/" + path;
+  }
+
+  public static int getMqttDefaultPort() {
+    return 1883;
+  }
+
+  public static Path runCompiler(String grammarFile, Iterable<String> connectFiles, String rootNode, String outputDirectory, int expectedReturnValue, String... additionalArguments) {
+
+    assertThat(connectFiles).isNotEmpty();
+
+    Path outPath = Paths.get(OUTPUT_DIRECTORY_PREFIX)
+        .resolve(outputDirectory)
+        .resolve("Compiler.out");
+    ensureCreated(outPath.getParent());
+
+    try {
+      logger.debug("user.dir: {}", System.getProperty("user.dir"));
+      List<String> args = new ArrayList<>() {{
+        add("--o=" + OUTPUT_DIRECTORY_PREFIX + outputDirectory);
+        add("--rootNode=" + rootNode);
+        add("--verbose");
+        add(INPUT_DIRECTORY_PREFIX + grammarFile);
+      }};
+      connectFiles.forEach(connectFile -> args.add(INPUT_DIRECTORY_PREFIX + connectFile));
+      args.addAll(Arrays.asList(additionalArguments));
+
+      int returnValue = exec(Compiler.class, args.toArray(new String[0]), outPath.toFile());
+      Assertions.assertEquals(expectedReturnValue, returnValue, "RagConnect did not return with value " + expectedReturnValue);
+    } catch (IOException | InterruptedException e) {
+      fail(e);
+    }
+    return outPath;
+  }
+
+  public static <T> String prettyPrint(Iterable<T> aList, Function<T, String> elementPrinter) {
+    StringJoiner sj = new StringJoiner(", ", "[", "]");
+    aList.forEach(element -> sj.add(elementPrinter.apply(element)));
+    return sj.toString();
+  }
+
+  public static void assertLinesMatch(String directory, String expectedName, String out) throws IOException {
+    Path expectedPath = Paths.get(TestUtils.INPUT_DIRECTORY_PREFIX)
+            .resolve(directory)
+            .resolve(expectedName + ".expected");
+    String expected = readFile(expectedPath, Charset.defaultCharset());
+    List<String> outList = Arrays.asList(out.split("\n"));
+    Collections.sort(outList);
+    List<String> expectedList = Arrays.stream(expected.split("\n"))
+            .sorted()
+            .filter(s -> !s.isEmpty() && !s.startsWith("//"))
+            .collect(Collectors.toList());
+
+    Assertions.assertLinesMatch(expectedList, outList);
+  }
+
+  private static void ensureCreated(Path directory) {
+    File directoryFile = directory.toFile();
+    if (directoryFile.exists() && directoryFile.isDirectory()) {
+      return;
+    }
+    assertTrue(directoryFile.mkdirs());
+  }
+
+  public static int exec(Class<?> klass, String[] args, File err) throws IOException,
+      InterruptedException {
+    String javaHome = System.getProperty("java.home");
+    String javaBin = javaHome + File.separator + "bin" + File.separator + "java";
+    String classpath = System.getProperty("java.class.path");
+    String className = klass.getName();
+
+    String[] newArgs = new String[args.length + 4];
+    newArgs[0] = javaBin;
+    newArgs[1] = "-cp";
+    newArgs[2] = classpath;
+    newArgs[3] = className;
+    System.arraycopy(args, 0, newArgs, 4, args.length);
+
+    ProcessBuilder builder = new ProcessBuilder(newArgs);
+//    builder.redirectOutput(err);
+    builder.redirectError(err);
+
+    Process process = builder.start();
+    process.waitFor();
+    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(Path path, Charset encoding)
+      throws IOException {
+    byte[] encoded = Files.readAllBytes(path);
+    return new String(encoded, encoding);
+  }
+
+  public static void waitForMqtt() throws InterruptedException {
+    TimeUnit.MILLISECONDS.sleep(1500);
+  }
+
+  public static ConditionFactory awaitMqtt() {
+    return Awaitility.await().atMost(1500, TimeUnit.MILLISECONDS);
+  }
+
+  static <T_Event, T_ASTNode> void logEvent(T_Event event, T_ASTNode node, String attribute, Object params, Object value) {
+    logger.info("event: {}, node: {}, attribute: {}, params: {}, value: {}",
+            event, node, attribute, params, value);
+  }
+
+}
diff --git a/settings.gradle b/settings.gradle
index 4340995b2e4d75b931edf7ea1d104c4900da748a..fb822723486ddf9b5c6937af69332d8409d52e1b 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -8,3 +8,4 @@ rootProject.name = 'ragconnect'
 
 include 'ragconnect.base'
 include 'ragconnect.tests'
+include 'ragconnect.featureTest'