diff --git a/.gitignore b/.gitignore
index a78076875b36829500e0d58bf05998ee5ade757a..78aba4bb88ae15cc77fd1bb1b454f37c145e3839 100644
--- a/.gitignore
+++ b/.gitignore
@@ -5,3 +5,4 @@
 .classpath
 .idea/
 .gradle/
+build/
diff --git a/ragconnect.base/build.gradle b/ragconnect.base/build.gradle
index 7bfebcfe84f3d6f53a6ceb997eb0108610135e31..c986f2a61adf610a9c2e5fdc7fd20eb193a9a46a 100644
--- a/ragconnect.base/build.gradle
+++ b/ragconnect.base/build.gradle
@@ -69,19 +69,12 @@ task relast(type: JavaExec) {
     group = 'Build'
     main = "-jar"
 
-    doFirst {
-        delete "src/gen/jastadd/*.ast"
-        delete "src/gen/jastadd/RagConnect.jadd"
-        delete "src/gen/jastadd/RagConnectRefResolver.jadd"
-        delete "src/gen/jastadd/RagConnectResolverStubs.jrag"
-        mkdir  "src/gen/jastadd/"
-    }
-
     args = [
             "../libs/relast.jar",
             "../relast.preprocessor/src/main/jastadd/RelAst.relast",
             "./src/main/jastadd/RagConnect.relast",
             "./src/main/jastadd/MustacheNodes.relast",
+            "./src/main/jastadd/YAML.relast",
             "--listClass=java.util.ArrayList",
             "--jastAddList=JastAddList",
             "--useJastAddNames",
@@ -90,14 +83,15 @@ task relast(type: JavaExec) {
             "--grammarName=./src/gen/jastadd/RagConnect"
     ]
 
-    inputs.files file("../libs/relast.jar"),
-            file("../relast.preprocessor/src/main/jastadd/RelAST.relast"),
-            file("./src/main/jastadd/RagConnect.relast")
-            file("./src/main/jastadd/MustacheNodes.relast")
-    outputs.files file("./src/gen/jastadd/RagConnect.ast"),
-            file("./src/gen/jastadd/RagConnect.jadd"),
-            file("./src/gen/jastadd/RagConnectRefResolver.jadd"),
-            file('./src/gen/jastadd/RagConnectResolverStubs.jrag')
+    inputs.files("../libs/relast.jar",
+            "../relast.preprocessor/src/main/jastadd/RelAst.relast",
+            "./src/main/jastadd/RagConnect.relast",
+            "./src/main/jastadd/MustacheNodes.relast",
+            "./src/main/jastadd/YAML.relast")
+    outputs.files("./src/gen/jastadd/RagConnect.ast",
+            "./src/gen/jastadd/RagConnect.jadd",
+            "./src/gen/jastadd/RagConnectRefResolver.jadd",
+            './src/gen/jastadd/RagConnectResolverStubs.jrag')
 }
 
 jastadd {
diff --git a/ragconnect.base/src/main/jastadd/Analysis.jrag b/ragconnect.base/src/main/jastadd/Analysis.jrag
index 623a3f0ad8c0457d8d0606bf70dea0adfea89ea4..b62399960f34a4b8c23a8d23762ec195491ce4e3 100644
--- a/ragconnect.base/src/main/jastadd/Analysis.jrag
+++ b/ragconnect.base/src/main/jastadd/Analysis.jrag
@@ -1,13 +1,15 @@
 aspect Analysis {
   // --- lookupTokenEndpointDefinition ---
-  inh TokenEndpointDefinition TokenEndpointDefinition.lookupTokenEndpointDefinition(TokenComponent token);
-  eq RagConnect.getEndpointDefinition().lookupTokenEndpointDefinition(TokenComponent token) {
+  inh java.util.List<TokenEndpointDefinition> TokenEndpointDefinition.lookupTokenEndpointDefinitions(TokenComponent token);
+  eq RagConnect.getEndpointDefinition().lookupTokenEndpointDefinitions(TokenComponent token) = lookupTokenEndpointDefinitions(token);
+  syn java.util.List<TokenEndpointDefinition> RagConnect.lookupTokenEndpointDefinitions(TokenComponent token) {
+    java.util.List<TokenEndpointDefinition> result = new java.util.ArrayList<>();
     for (EndpointDefinition def : getEndpointDefinitionList()) {
       if (def.isTokenEndpointDefinition() && def.asTokenEndpointDefinition().getToken().equals(token)) {
-        return def.asTokenEndpointDefinition();
+        result.add(def.asTokenEndpointDefinition());
       }
     }
-    return null;
+    return result;
   }
 
   // --- lookupDependencyDefinition ---
@@ -22,6 +24,34 @@ aspect Analysis {
   }
 
   // --- isAlreadyDefined ---
-  syn boolean TokenEndpointDefinition.isAlreadyDefined() = lookupTokenEndpointDefinition(getToken()) != this;
+  syn boolean TokenEndpointDefinition.isAlreadyDefined() = lookupTokenEndpointDefinitions(getToken()).size() > 1;
   syn boolean DependencyDefinition.isAlreadyDefined() = lookupDependencyDefinition(getSource().containingTypeDecl(), getID()) != this;
+
+  syn boolean MappingDefinitionType.assignableTo(JavaTypeUse target);
+  eq JavaMappingDefinitionType.assignableTo(JavaTypeUse target) = getType().assignableTo(target);
+  eq JavaArrayMappingDefinitionType.assignableTo(JavaTypeUse target) {
+    if (!target.getName().endsWith("[]")) { return false; }
+    return getType().assignableTo(new SimpleJavaTypeUse(target.getName().replace("[]", "")));
+  }
+  syn boolean JavaTypeUse.assignableTo(JavaTypeUse target) {
+    // target var = this;
+    return target.primitivePrettyPrint().equals(this.primitivePrettyPrint());
+  }
+  syn String JavaTypeUse.primitivePrettyPrint() {
+    switch(getName()) {
+      case "int":
+      case "Integer": return "int";
+      case "short":
+      case "Short": return "short";
+      case "long":
+      case "Long": return "long";
+      case "float":
+      case "Float": return "float";
+      case "double":
+      case "Double": return "double";
+      case "char":
+      case "Character": return "char";
+      default: return getName();
+    }
+  }
 }
diff --git a/ragconnect.base/src/main/jastadd/Errors.jrag b/ragconnect.base/src/main/jastadd/Errors.jrag
index bfee2545d4bee28223b04ce931ccd6a6429cf13f..fe2fcd59efd365174ca2803affebc34819cf3d7e 100644
--- a/ragconnect.base/src/main/jastadd/Errors.jrag
+++ b/ragconnect.base/src/main/jastadd/Errors.jrag
@@ -7,32 +7,32 @@ aspect Errors {
     [new TreeSet<ErrorMessage>()]
     root RagConnect;
 
-    ReceiveFromMqttDefinition contributes error("Receive definition already defined for " + getToken().getName())
+    ReceiveTokenEndpointDefinition contributes error("Receive definition already defined for " + getToken().getName())
       when isAlreadyDefined()
       to RagConnect.errors();
 
-    ReceiveFromMqttDefinition contributes error("Receiving target token must not be an NTA token!")
+    ReceiveTokenEndpointDefinition contributes error("Receiving target token must not be an NTA token!")
       when getToken().getNTA()
       to RagConnect.errors();
 
     // if first mapping is null, then suitableDefaultMapping() == null
-    ReceiveFromMqttDefinition contributes error("No suitable default mapping found for type " +
+    ReceiveTokenEndpointDefinition contributes error("No suitable default mapping found for type " +
       ((getMappingList().isEmpty())
-        ? getToken().getJavaTypeUse().prettyPrint()
+        ? getToken().effectiveJavaTypeUse().prettyPrint()
         : getMappingList().get(0).getFromType().prettyPrint()))
       when effectiveMappings().get(0) == null
       to RagConnect.errors();
 
-    ReceiveFromMqttDefinition contributes error("to-type of last mapping must be type of the Token!")
-      when getToken().getJavaTypeUse().prettyPrint().equals(
-          effectiveMappings().get(effectiveMappings().size() - 1))
+    ReceiveTokenEndpointDefinition contributes error("to-type of last mapping (" + effectiveMappings().get(effectiveMappings().size() - 1).getToType().prettyPrint() + ") not assignable to type of the Token (" + getToken().effectiveJavaTypeUse().prettyPrint() + ")!")
+      when !effectiveMappings().get(effectiveMappings().size() - 1).getToType().assignableTo(
+          getToken().effectiveJavaTypeUse())
       to RagConnect.errors();
 
-    SendToMqttDefinition contributes error("Sending target token must be an NTA token!")
+    SendTokenEndpointDefinition contributes error("Sending target token must be an NTA token!")
       when !getToken().getNTA()
       to RagConnect.errors();
 
-    SendToMqttDefinition contributes error("Send definition already defined for " + getToken().getName())
+    SendTokenEndpointDefinition contributes error("Send definition already defined for " + getToken().getName())
       when isAlreadyDefined()
       to RagConnect.errors();
 
@@ -44,7 +44,7 @@ aspect Errors {
       when isAlreadyDefinedAsList()
       to RagConnect.errors();
 
-    DependencyDefinition contributes error("There must be a send update definition targeting " + getSource().parentTypeypeAndName() + " for dependency definition " + getID())
+    DependencyDefinition contributes error("There must be a send endpoint definition targeting " + getSource().parentTypeypeAndName() + " for dependency definition " + getID())
       when targetEndpointDefinition() == null
       to RagConnect.errors();
 }
diff --git a/ragconnect.base/src/main/jastadd/MustacheNodes.relast b/ragconnect.base/src/main/jastadd/MustacheNodes.relast
index ce85caba554d1f582079b299f457d7675ab12dfa..62ecdb6166a783e125cefef2826d550b73ab9e1b 100644
--- a/ragconnect.base/src/main/jastadd/MustacheNodes.relast
+++ b/ragconnect.base/src/main/jastadd/MustacheNodes.relast
@@ -1,6 +1,3 @@
-//TypeComponentMustache ;
-//rel TypeComponentMustache.TypeComponent -> TypeComponent ;
-
 MRagConnect ::= ReceiveDefinition:MReceiveDefinition* SendDefinition:MSendDefinition* MappingDefinition:MMappingDefinition* DependencyDefinition:MDependencyDefinition* RootTypeComponent:MTypeComponent* TokenComponent:MTokenComponent*;
 abstract MEndpointDefinition ::= InnerMappingDefinition:MInnerMappingDefinition*;
 MReceiveDefinition : MEndpointDefinition;
@@ -12,9 +9,9 @@ MTypeComponent;
 MTokenComponent;
 
 rel MRagConnect.RagConnect -> RagConnect;
-rel MInnerMappingDefinition.MappingDefinition -> MappingDefinition;
-rel MReceiveDefinition.ReceiveFromMqttDefinition -> ReceiveFromMqttDefinition;
-rel MSendDefinition.SendToMqttDefinition -> SendToMqttDefinition;
+rel MInnerMappingDefinition.MMappingDefinition -> MMappingDefinition;
+rel MReceiveDefinition.ReceiveTokenEndpointDefinition -> ReceiveTokenEndpointDefinition;
+rel MSendDefinition.SendTokenEndpointDefinition -> SendTokenEndpointDefinition;
 rel MMappingDefinition.MappingDefinition -> MappingDefinition;
 rel MDependencyDefinition.DependencyDefinition -> DependencyDefinition;
 rel MTypeComponent.TypeComponent -> TypeComponent;
diff --git a/ragconnect.base/src/main/jastadd/Navigation.jrag b/ragconnect.base/src/main/jastadd/Navigation.jrag
index 2314386eb9f74ecb5699d6fba91956e80f75fa63..752fc36aea4c59348874c1a08134696a9543b6cb 100644
--- a/ragconnect.base/src/main/jastadd/Navigation.jrag
+++ b/ragconnect.base/src/main/jastadd/Navigation.jrag
@@ -10,9 +10,12 @@ aspect Navigation {
   eq MRagConnect.getChild().ragconnect() = getRagConnect();
 
   // --- containedFile
+  eq Grammar.getChild().containedFile() = null;
   eq RagConnect.getChild().containedFile() = null;
+  eq MRagConnect.getChild().containedFile() = null;
 
   // --- containedFileName ---
+  eq Grammar.getChild().containedFileName() = null;  // should be in PP
   eq RagConnect.getChild().containedFileName() = getFileName();
   eq MRagConnect.getChild().containedFileName() = null;
 
@@ -24,30 +27,33 @@ aspect Navigation {
   syn TokenEndpointDefinition EndpointDefinition.asTokenEndpointDefinition() = null;
   eq TokenEndpointDefinition.asTokenEndpointDefinition() = this;
 
-  // --- isSendToMqttDefinition ---
-  syn boolean EndpointDefinition.isSendToMqttDefinition() = false;
-  eq SendToMqttDefinition.isSendToMqttDefinition() = true;
+  // --- isSendTokenEndpointDefinition ---
+  syn boolean EndpointDefinition.isSendTokenEndpointDefinition() = false;
+  eq SendTokenEndpointDefinition.isSendTokenEndpointDefinition() = true;
 
-  // --- asSendToMqttDefinition ---
-  syn SendToMqttDefinition EndpointDefinition.asSendToMqttDefinition() = null;
-  eq SendToMqttDefinition.asSendToMqttDefinition() = this;
+  // --- asSendTokenEndpointDefinition ---
+  syn SendTokenEndpointDefinition EndpointDefinition.asSendTokenEndpointDefinition() = null;
+  eq SendTokenEndpointDefinition.asSendTokenEndpointDefinition() = this;
 
-  // --- asReceiveFromMqttDefinition ---
-  syn ReceiveFromMqttDefinition EndpointDefinition.asReceiveFromMqttDefinition() = null;
-  eq ReceiveFromMqttDefinition.asReceiveFromMqttDefinition() = this;
+  // --- asReceiveTokenEndpointDefinition ---
+  syn ReceiveTokenEndpointDefinition EndpointDefinition.asReceiveTokenEndpointDefinition() = null;
+  eq ReceiveTokenEndpointDefinition.asReceiveTokenEndpointDefinition() = this;
 
   // --- targetEndpointDefinition ---
-  syn SendToMqttDefinition DependencyDefinition.targetEndpointDefinition() {
+  syn SendTokenEndpointDefinition DependencyDefinition.targetEndpointDefinition() {
     // resolve definition in here, as we do not need resolveMethod in any other place (yet)
     for (EndpointDefinition endpointDefinition : ragconnect().getEndpointDefinitionList()) {
-      if (endpointDefinition.isSendToMqttDefinition() &&
-          endpointDefinition.asSendToMqttDefinition().getToken().equals(this.getTarget())) {
-        return endpointDefinition.asSendToMqttDefinition();
+      if (endpointDefinition.isSendTokenEndpointDefinition() &&
+          endpointDefinition.asSendTokenEndpointDefinition().getToken().equals(this.getTarget())) {
+        return endpointDefinition.asSendTokenEndpointDefinition();
       }
     }
     return null;
   }
 
+  // --- effectiveJavaTypeUse (should be in preprocessor) ---
+  syn lazy JavaTypeUse TokenComponent.effectiveJavaTypeUse() = hasJavaTypeUse() ? getJavaTypeUse() : new SimpleJavaTypeUse("String");
+
   // --- isDefaultMappingDefinition ---
   syn boolean MappingDefinition.isDefaultMappingDefinition() = false;
   eq DefaultMappingDefinition.isDefaultMappingDefinition() = true;
diff --git a/ragconnect.base/src/main/jastadd/Printing.jrag b/ragconnect.base/src/main/jastadd/Printing.jrag
new file mode 100644
index 0000000000000000000000000000000000000000..74607aeb7737e1e43c9565b799693889a648a9df
--- /dev/null
+++ b/ragconnect.base/src/main/jastadd/Printing.jrag
@@ -0,0 +1,75 @@
+aspect Printing {
+  String ASTNode.PRINT_INDENT = "  ";
+
+  syn String MappingDefinitionType.prettyPrint();
+  eq JavaMappingDefinitionType.prettyPrint() = getType().getName();
+  eq JavaArrayMappingDefinitionType.prettyPrint() = getType().getName() + "[]";
+
+  syn String JavaTypeUse.prettyPrint() {
+    StringBuilder sb = new StringBuilder();
+    generateAbstractGrammar(sb);
+    return sb.toString();
+  }
+
+  syn String Document.prettyPrint() {
+    StringBuilder sb = new StringBuilder();
+    sb.append("# RagConnect created at ").append(java.time.Instant.now()).append("\n");
+    for (ComplexElement element : getComplexElementList()) {
+      element.prettyPrint(sb, false, "");
+    }
+    if (sb.charAt(sb.length() - 1) != '\n') {
+      sb.append("\n");
+    }
+    return sb.toString();
+  }
+  syn StringBuilder Element.prettyPrint(StringBuilder sb, boolean printIndent, String indent);
+  eq ValueElement.prettyPrint(StringBuilder sb, boolean printIndent, String indent) {
+    sb.append(getValue());
+    return sb;
+  }
+  eq StringElement.prettyPrint(StringBuilder sb, boolean printIndent, String indent) {
+    sb.append("\"").append(getValue()).append("\"");
+    return sb;
+  }
+  eq ListElement.prettyPrint(StringBuilder sb, boolean printIndent, String indent) {
+    if (isEmpty()) {
+      sb.append("[]");
+    } else {
+      for (Element element : getElementList()) {
+        sb.append(indent).append("- ");
+        element.prettyPrint(sb, false, indent + PRINT_INDENT);
+        sb.append("\n");
+      }
+      // delete last newline
+      sb.deleteCharAt(sb.length() - 1);
+    }
+    return sb;
+  }
+  eq KeyValuePair.prettyPrint(StringBuilder sb, boolean printIndent, String indent) {
+    if (printIndent) sb.append(indent);
+    sb.append(getKey()).append(": ");
+    if (getValue().isComplex() && !getValue().isEmpty()) {
+      sb.append("\n");
+      getValue().prettyPrint(sb, true, indent + PRINT_INDENT);  //);
+    } else {
+      getValue().prettyPrint(sb, false, indent);
+    }
+    return sb;
+  }
+  eq MappingElement.prettyPrint(StringBuilder sb, boolean printIndent, String indent) {
+    if (isEmpty()) {
+      sb.append("{}");
+    } else {
+      boolean first = true;
+      for (KeyValuePair pair : getKeyValuePairList()) {
+        if (!first || printIndent) sb.append(indent);
+        first = false;
+        pair.prettyPrint(sb, false, indent);  // + PRINT_INDENT
+        sb.append("\n");
+      }
+      // delete last newline
+      sb.deleteCharAt(sb.length() - 1);
+    }
+    return sb;
+  }
+}
diff --git a/ragconnect.base/src/main/jastadd/RagConnect.relast b/ragconnect.base/src/main/jastadd/RagConnect.relast
index d30116efae1a6735e88922e31f42f99911c6532a..accc617714ff6bdd24a22831ea84e5d93e6a15b8 100644
--- a/ragconnect.base/src/main/jastadd/RagConnect.relast
+++ b/ragconnect.base/src/main/jastadd/RagConnect.relast
@@ -2,13 +2,13 @@ RagConnect ::= EndpointDefinition* DependencyDefinition* MappingDefinition* Prog
 
 abstract EndpointDefinition ::= <AlwaysApply:boolean> ;
 
-rel EndpointDefinition.Mapping* -> MappingDefinition;
+rel EndpointDefinition.Mapping* <-> MappingDefinition.UsedAt*;
 
 abstract TokenEndpointDefinition : EndpointDefinition;
 rel TokenEndpointDefinition.Token -> TokenComponent;
 
-ReceiveFromMqttDefinition : TokenEndpointDefinition;
-SendToMqttDefinition  : TokenEndpointDefinition;
+ReceiveTokenEndpointDefinition : TokenEndpointDefinition;
+SendTokenEndpointDefinition : TokenEndpointDefinition;
 
 DependencyDefinition ::= <ID>;
 rel DependencyDefinition.Source <-> TokenComponent.DependencySourceDefinition*;
diff --git a/ragconnect.base/src/main/jastadd/Util.jadd b/ragconnect.base/src/main/jastadd/Util.jadd
new file mode 100644
index 0000000000000000000000000000000000000000..3b59b13c53a8d0e1ca2a05feba43294797a4577e
--- /dev/null
+++ b/ragconnect.base/src/main/jastadd/Util.jadd
@@ -0,0 +1,5 @@
+aspect Util {
+  static String ASTNode.capitalize(String s) {
+    return Character.toUpperCase(s.charAt(0)) + s.substring(1);
+  }
+}
diff --git a/ragconnect.base/src/main/jastadd/YAML.jrag b/ragconnect.base/src/main/jastadd/YAML.jrag
new file mode 100644
index 0000000000000000000000000000000000000000..93dd0f34b7b7b0505cf0970b6541fa3ae5bee7fa
--- /dev/null
+++ b/ragconnect.base/src/main/jastadd/YAML.jrag
@@ -0,0 +1,31 @@
+aspect Navigation {
+  eq Document.getChild().program() = null;
+  eq Document.getChild().ragconnect() = null;
+  eq Document.getChild().containedFile() = null;
+  eq Document.getChild().containedFileName() = getFileName();
+
+  syn boolean Element.isComplex() = false;
+  eq ComplexElement.isComplex() = true;
+
+  syn boolean Element.isEmpty() = false;
+  eq MappingElement.isEmpty() = getNumKeyValuePair() == 0;
+  eq ListElement.isEmpty() = getNumElement() == 0;
+}
+
+aspect Helper {
+  public static ValueElement ValueElement.of(int value) {
+    return new ValueElement(String.valueOf(value));
+  }
+  public static ValueElement ValueElement.of(boolean value) {
+    return new ValueElement(String.valueOf(value));
+  }
+  public static ValueElement ValueElement.of(String value) {
+    return new ValueElement(value);
+  }
+  public static StringElement StringElement.of(String value) {
+    return new StringElement(value);
+  }
+  public void MappingElement.addKeyValuePair(String key, Element value) {
+    addKeyValuePair(new KeyValuePair(key, value));
+  }
+}
diff --git a/ragconnect.base/src/main/jastadd/YAML.relast b/ragconnect.base/src/main/jastadd/YAML.relast
new file mode 100644
index 0000000000000000000000000000000000000000..94f4ab20fdc3838348bd53b812b3d1477cae3558
--- /dev/null
+++ b/ragconnect.base/src/main/jastadd/YAML.relast
@@ -0,0 +1,9 @@
+Document ::= <FileName> ComplexElement* ;
+abstract Element ;
+abstract ComplexElement : Element ;
+MappingElement : ComplexElement ::= KeyValuePair* ;
+KeyValuePair : ComplexElement ::= <Key> Value:Element ;
+ListElement : ComplexElement ::= Element* ;
+abstract SimpleElement : Element ;
+ValueElement : SimpleElement ::= <Value> ;
+StringElement : SimpleElement ::= <Value> ;
diff --git a/ragconnect.base/src/main/jastadd/backend/Configuration.jadd b/ragconnect.base/src/main/jastadd/backend/Configuration.jadd
index d4f5b43f27cb329f1ff82318c6f383f0f69ebbd6..8d3b16c3f99014408c6aefa5655201d222c3d892 100644
--- a/ragconnect.base/src/main/jastadd/backend/Configuration.jadd
+++ b/ragconnect.base/src/main/jastadd/backend/Configuration.jadd
@@ -2,4 +2,6 @@ aspect Configuration {
   public static boolean ASTNode.loggingEnabledForReads = false;
   public static boolean ASTNode.loggingEnabledForWrites = false;
   public static TypeDecl ASTNode.rootNode;
+  public static boolean ASTNode.usesMqtt;
+  public static boolean ASTNode.usesRest;
 }
diff --git a/ragconnect.base/src/main/jastadd/backend/Generation.jadd b/ragconnect.base/src/main/jastadd/backend/Generation.jadd
index 067626ed42f12add896ca4eea7c65917309c0e2e..4b8d357e82e82eb8454451978589434f1ef44505 100644
--- a/ragconnect.base/src/main/jastadd/backend/Generation.jadd
+++ b/ragconnect.base/src/main/jastadd/backend/Generation.jadd
@@ -1,41 +1,19 @@
-aspect GenerationUtils {
-  public static final String ASTNode.aspectIndent = "  ";
-
-  public String ASTNode.ind(int n) {
-    StringBuilder s = new StringBuilder();
-    for (int i = 0; i < n; i++) {
-      s.append(aspectIndent);
-    }
-    return s.toString();
-  }
-
-  // --- prettyPrint ---
-  syn String MappingDefinitionType.prettyPrint();
-  eq JavaMappingDefinitionType.prettyPrint() = getType().getName();
-  eq JavaArrayMappingDefinitionType.prettyPrint() = getType().getName() + "[]";
-
-  syn String JavaTypeUse.prettyPrint() {
-    StringBuilder sb = new StringBuilder();
-    generateAbstractGrammar(sb);
-    return sb.toString();
-  }
-}
-
-/* Open questions
-- Should all string constants be defined on the normal AST, or on the special mustache AST?
+/*
+Design considerations
+- InnerMappingDefinition needed for iteration attribute (first, last) - not easily possible with list-relation
 */
 
 aspect AttributesForMustache {
   // --- MRagConnect ---
-  eq MRagConnect.getChild().mqttHandlerAttribute() = mqttHandlerAttribute();
-  eq MRagConnect.getChild().mqttHandlerField() = mqttHandlerField();
   eq MRagConnect.getRootTypeComponent(int i).isFirst() = i == 0;
 
-  syn String MRagConnect.mqttHandlerAttribute() = getRagConnect().mqttHandlerAttribute();
-  syn String MRagConnect.mqttHandlerField() = getRagConnect().mqttHandlerField();
-  syn String MRagConnect.mqttSetHostMethod() = getRagConnect().mqttSetHostMethod();
-  syn String MRagConnect.mqttWaitUntilReadyMethod() = getRagConnect().mqttWaitUntilReadyMethod();
-  syn String MRagConnect.mqttCloseMethod() = getRagConnect().mqttCloseMethod();
+  syn String MRagConnect.closeMethod() = "ragconnectCloseConnections";
+  syn String MRagConnect.mqttHandlerAttribute() = "_mqttHandler";
+  syn String MRagConnect.mqttHandlerField() = "_mqttHandler";
+  syn String MRagConnect.mqttSetupWaitUntilReadyMethod() = "ragconnectSetupMqttWaitUntilReady";
+
+  syn String MRagConnect.restHandlerAttribute() = "_restHandler";
+  syn String MRagConnect.restHandlerField() = "_restHandler";
 
   // --- MEndpointDefinition ---
   syn String MEndpointDefinition.preemptiveExpectedValue();
@@ -44,29 +22,25 @@ aspect AttributesForMustache {
   syn String MEndpointDefinition.firstInputVarName();
 
   eq MEndpointDefinition.getInnerMappingDefinition(int i).isLast() = i == getNumInnerMappingDefinition() - 1;
-  eq MEndpointDefinition.getInnerMappingDefinition().resultVarPrefix() = resultVarPrefix();
-  eq MEndpointDefinition.getInnerMappingDefinition(int i).inputVarName() = i == 0 ? firstInputVarName() : resultVarPrefix() + getInnerMappingDefinition(i - 1).getMappingDefinition().methodName();
-
-  inh String MEndpointDefinition.mqttHandlerAttribute();
+  eq MEndpointDefinition.getInnerMappingDefinition(int i).inputVarName() = i == 0 ? firstInputVarName() : getInnerMappingDefinition(i - 1).outputVarName();
 
-  syn String MEndpointDefinition.connectMethod() = endpointDef().connectMethod();
+  syn String MEndpointDefinition.connectParameterName() = "uriString";
+  syn String MEndpointDefinition.connectMethod() = "connect" + tokenName();
   syn TokenComponent MEndpointDefinition.token() = endpointDef().getToken();
   syn boolean MEndpointDefinition.alwaysApply() = endpointDef().getAlwaysApply();
-  syn String MEndpointDefinition.resultVarPrefix() = "result";  // we do not need "_" here, because methodName begins with one
   syn String MEndpointDefinition.parentTypeName() = token().containingTypeDecl().getName();
   syn String MEndpointDefinition.tokenName() = token().getName();
   syn MInnerMappingDefinition MEndpointDefinition.lastDefinition() = getInnerMappingDefinition(getNumInnerMappingDefinition() - 1);
-  syn String MEndpointDefinition.lastDefinitionToType() = lastDefinition().ToType();
-  syn String MEndpointDefinition.lastDefinitionName() = lastDefinition().methodName();
-  syn String MEndpointDefinition.lastResult() = resultVarPrefix() + lastDefinitionName();
+  syn String MEndpointDefinition.lastDefinitionToType() = lastDefinition().toType();
+  syn String MEndpointDefinition.lastResult() = lastDefinition().outputVarName();
   syn String MEndpointDefinition.condition() {
-    if (lastDefinition().getMappingDefinition().getToType().isArray()) {
+    if (lastDefinition().mappingDef().getToType().isArray()) {
       return "java.util.Arrays.equals(" + preemptiveExpectedValue() + ", " + lastResult() + ")";
     }
-    if (token().isPrimitiveType() && lastDefinition().getMappingDefinition().getToType().isPrimitiveType()) {
+    if (token().isPrimitiveType() && lastDefinition().mappingDef().getToType().isPrimitiveType()) {
       return preemptiveExpectedValue() + " == " + lastResult();
     }
-    if (lastDefinition().getMappingDefinition().isDefaultMappingDefinition()) {
+    if (lastDefinition().mappingDef().isDefaultMappingDefinition()) {
       return preemptiveExpectedValue() + " != null && " + preemptiveExpectedValue() + ".equals(" + lastResult() + ")";
     }
     return preemptiveExpectedValue() + " != null ? " + preemptiveExpectedValue() + ".equals(" + lastResult() + ") : " + lastResult() + " == null";
@@ -74,66 +48,67 @@ aspect AttributesForMustache {
 
   // --- MInnerMappingDefinition ---
   inh boolean MInnerMappingDefinition.isLast();
-  inh String MInnerMappingDefinition.resultVarPrefix();
-  syn String MInnerMappingDefinition.ToType() = getMappingDefinition().getToType().prettyPrint();
-  syn String MInnerMappingDefinition.methodName() = getMappingDefinition().methodName();
   inh String MInnerMappingDefinition.inputVarName();
+  syn String MInnerMappingDefinition.toType() = mappingDef().getToType().prettyPrint();
+  syn String MInnerMappingDefinition.methodName() = getMMappingDefinition().methodName();
+  syn MappingDefinition MInnerMappingDefinition.mappingDef() = getMMappingDefinition().getMappingDefinition();
+  syn String MInnerMappingDefinition.outputVarName() = "result" + methodName();  // we do not need "_" in between here, because methodName begins with one
 
   // --- MReceiveDefinition ---
   eq MReceiveDefinition.preemptiveExpectedValue() = "get" + tokenName() + "()";
   eq MReceiveDefinition.preemptiveReturn() = "return;";
-  eq MReceiveDefinition.endpointDef() = getReceiveFromMqttDefinition();
+  eq MReceiveDefinition.endpointDef() = getReceiveTokenEndpointDefinition();
   eq MReceiveDefinition.firstInputVarName() = "message";
 
   // --- MSendDefinition ---
   eq MSendDefinition.preemptiveExpectedValue() = lastValue();
   eq MSendDefinition.preemptiveReturn() = "return false;";
-  eq MSendDefinition.endpointDef() = getSendToMqttDefinition();
+  eq MSendDefinition.endpointDef() = getSendTokenEndpointDefinition();
   eq MSendDefinition.firstInputVarName() = "get" + tokenName() + "()";
 
-  syn String MSendDefinition.sendTopic() = getSendToMqttDefinition().sendTopic();
-  syn String MSendDefinition.lastValue() = getSendToMqttDefinition().lastValue();
-  syn String MSendDefinition.updateMethod() = getSendToMqttDefinition().updateMethod();
-  syn String MSendDefinition.writeMethod() = getSendToMqttDefinition().writeMethod();
-  syn String MSendDefinition.tokenResetMethod() = getSendToMqttDefinition().tokenResetMethod();
+  syn String MSendDefinition.sender() = "_sender_" + tokenName();
+  syn String MSendDefinition.lastValue() = "_lastValue" + tokenName();
+  syn String MSendDefinition.updateMethod() = "_update_" + tokenName();
+  syn String MSendDefinition.writeMethod() = "_writeLastValue_" + tokenName();
+  syn String MSendDefinition.tokenResetMethod() = "get" + tokenName() + "_reset";
 
   // --- MMappingDefinition ---
   syn String MMappingDefinition.toType() = getMappingDefinition().getToType().prettyPrint();
-  syn String MMappingDefinition.methodName() = getMappingDefinition().methodName();
+  syn String MMappingDefinition.methodName() = "_apply_" + getMappingDefinition().getID();
   syn String MMappingDefinition.fromType() = getMappingDefinition().getFromType().prettyPrint();
   syn String MMappingDefinition.fromVariableName() = getMappingDefinition().getFromVariableName();
   syn String MMappingDefinition.content() = getMappingDefinition().getContent();
 
   // --- MDependencyDefinition ---
   syn String MDependencyDefinition.targetParentTypeName() = getDependencyDefinition().getTarget().containingTypeDecl().getName();
-  syn String MDependencyDefinition.dependencyMethod() = getDependencyDefinition().dependencyMethod();
+  syn String MDependencyDefinition.dependencyMethod() = "add" + capitalize(getDependencyDefinition().getID());
   syn String MDependencyDefinition.sourceParentTypeName() = getDependencyDefinition().getSource().containingTypeDecl().getName();
-  syn String MDependencyDefinition.internalRelationPrefix() = getDependencyDefinition().internalRelationPrefix();
-  syn nta MEndpointDefinition MDependencyDefinition.targetEndpointDefinition() {
+  syn String MDependencyDefinition.internalRelationPrefix() = "_internal_" + getDependencyDefinition().getID();
+  syn nta MSendDefinition MDependencyDefinition.targetEndpointDefinition() {
     return getDependencyDefinition().targetEndpointDefinition().toMustache();
   }
 
   // --- MTypeComponent ---
   syn String MTypeComponent.name() = getTypeComponent().getName();
-  inh String MTypeComponent.mqttHandlerAttribute();
-  inh String MTypeComponent.mqttHandlerField();
   inh boolean MTypeComponent.isFirst();
 
   // --- MTokenComponent ---
   syn String MTokenComponent.parentTypeName() = getTokenComponent().containingTypeDecl().getName();
   syn String MTokenComponent.name() = getTokenComponent().getName();
-  syn String MTokenComponent.javaType() = getTokenComponent().getJavaTypeUse().prettyPrint();
-  syn String MTokenComponent.internalName() = getTokenComponent().internalName();
+  syn String MTokenComponent.javaType() = getTokenComponent().effectiveJavaTypeUse().prettyPrint();
+  syn String MTokenComponent.internalName() = getTokenComponent().getDependencySourceDefinitionList().isEmpty() ? externalName() : "_internal_" + name();
+  syn String MTokenComponent.externalName() = name();
 
   // --- toMustache ---
   syn lazy MRagConnect RagConnect.toMustache() {
     MRagConnect result = new MRagConnect();
     result.setRagConnect(this);
     for (EndpointDefinition def : getEndpointDefinitionList()) {
-      if (def.isSendToMqttDefinition()) {
-        result.addSendDefinition(def.asSendToMqttDefinition().toMustache());
+      if (def.isSendTokenEndpointDefinition()) {
+        SendTokenEndpointDefinition sendDef = def.asSendTokenEndpointDefinition();
+        result.addSendDefinition(sendDef.toMustache());
       } else {
-        result.addReceiveDefinition(def.asReceiveFromMqttDefinition().toMustache());
+        result.addReceiveDefinition(def.asReceiveTokenEndpointDefinition().toMustache());
       }
     }
     for (MappingDefinition def : allMappingDefinitions()) {
@@ -155,41 +130,46 @@ aspect AttributesForMustache {
     return result;
   }
 
-//MInnerMappingDefinition.MappingDefinition -> MappingDefinition;
   protected void MEndpointDefinition.addInnerMappings() {
     for (MappingDefinition def : endpointDef().effectiveMappings()) {
       MInnerMappingDefinition inner = new MInnerMappingDefinition();
-      inner.setMappingDefinition(def);
+      inner.setMMappingDefinition(def.toMustache());
       addInnerMappingDefinition(inner);
     }
   }
-  syn lazy MReceiveDefinition ReceiveFromMqttDefinition.toMustache() {
+
+  syn lazy MReceiveDefinition ReceiveTokenEndpointDefinition.toMustache() {
     MReceiveDefinition result = new MReceiveDefinition();
-    result.setReceiveFromMqttDefinition(this);
+    result.setReceiveTokenEndpointDefinition(this);
     result.addInnerMappings();
     return result;
   }
-  syn lazy MSendDefinition SendToMqttDefinition.toMustache() {
+
+  syn lazy MSendDefinition SendTokenEndpointDefinition.toMustache() {
     MSendDefinition result = new MSendDefinition();
-    result.setSendToMqttDefinition(this);
+    result.setSendTokenEndpointDefinition(this);
     result.addInnerMappings();
     return result;
   }
+
   syn lazy MMappingDefinition MappingDefinition.toMustache() {
     MMappingDefinition result = new MMappingDefinition();
     result.setMappingDefinition(this);
     return result;
   }
+
   syn lazy MDependencyDefinition DependencyDefinition.toMustache() {
     MDependencyDefinition result = new MDependencyDefinition();
     result.setDependencyDefinition(this);
     return result;
   }
+
   syn lazy MTypeComponent TypeComponent.toMustache() {
     MTypeComponent result = new MTypeComponent();
     result.setTypeComponent(this);
     return result;
   }
+
   syn lazy MTokenComponent TokenComponent.toMustache() {
     MTokenComponent result = new MTokenComponent();
     result.setTokenComponent(this);
@@ -201,37 +181,6 @@ aspect AttributesForMustache {
 }
 
 aspect AspectGeneration {
-  // naming convention attributes
-  syn String TokenComponent.internalName() = getDependencySourceDefinitionList().isEmpty() ? externalName() : "_internal_" + getName();
-  syn String TokenComponent.externalName() = getName();
-
-  syn String TokenEndpointDefinition.connectMethod() = "connect" + getToken().getName();
-  syn String SendToMqttDefinition.sendTopic() = "_topic_" + getToken().getName();
-  syn String SendToMqttDefinition.lastValue() = "_lastValue" + getToken().getName();
-  syn String SendToMqttDefinition.updateMethod() = "_update_" + getToken().getName();
-  syn String SendToMqttDefinition.writeMethod() = "_writeLastValue_" + getToken().getName();
-  syn String SendToMqttDefinition.tokenResetMethod() = "get" + getToken().getName() + "_reset";
-  syn String MappingDefinition.methodName() = "_apply_" + getID();
-  syn String DependencyDefinition.dependencyMethod() = "add" +
-    Character.toUpperCase(getID().charAt(0)) +
-    getID().substring(1);
-  syn String DependencyDefinition.internalRelationPrefix() = "_internal_" + getID();
-  syn String DependencyDefinition.internalTokenName() = getSource().internalName();
-
-  syn String RagConnect.mqttHandlerAttribute() = "_mqttHandler";
-  syn String RagConnect.mqttHandlerField() = "_mqttHandler";
-
-  syn String RagConnect.mqttSetHostMethod() = "MqttSetHost";
-  syn String RagConnect.mqttWaitUntilReadyMethod() = "MqttWaitUntilReady";
-  syn String RagConnect.mqttCloseMethod() = "MqttCloseConnections";
-
-  // naming copy attributes
-  // --- mqttHandlerAttribute ---
-  inh String EndpointDefinition.mqttHandlerAttribute();
-  inh String MappingDefinition.mqttHandlerAttribute();
-  inh String DependencyDefinition.mqttHandlerAttribute();
-  eq RagConnect.getChild().mqttHandlerAttribute() = mqttHandlerAttribute();
-
   // --- rootNodeName ---
   syn String ASTNode.rootNodeName() = rootNode.getName();
 
@@ -275,10 +224,11 @@ aspect RelationGeneration {
   }
 
   syn nta Relation DependencyDefinition.getRelationToCreate() {
+    String internalRelationPrefix = toMustache().internalRelationPrefix();
     BidirectionalRelation result = new BidirectionalRelation();
-    NavigableRole left = new ListRole(internalRelationPrefix() + "Source");
+    NavigableRole left = new ListRole(internalRelationPrefix + "Source");
     left.setType(getTarget().containingTypeDecl());
-    NavigableRole right = new ListRole(internalRelationPrefix() + "Target");
+    NavigableRole right = new ListRole(internalRelationPrefix + "Target");
     right.setType(getSource().containingTypeDecl());
     result.setLeft(left);
     result.setRight(right);
@@ -294,9 +244,9 @@ aspect GrammarExtension {
     }
     b.append("<");
     if (!getName().equals("")) {
-      b.append(internalName()).append(":");
+      b.append(toMustache().internalName()).append(":");
     }
-    getJavaTypeUse().generateAbstractGrammar(b);
+    effectiveJavaTypeUse().generateAbstractGrammar(b);
     b.append(">");
     if (getNTA()) {
       b.append("/");
diff --git a/ragconnect.base/src/main/jastadd/backend/Mappings.jrag b/ragconnect.base/src/main/jastadd/backend/Mappings.jrag
index 07cbf5b81a531638ac092635f915ed856a65df4c..7aa82d304079592d605997484d1348a3d3681875 100644
--- a/ragconnect.base/src/main/jastadd/backend/Mappings.jrag
+++ b/ragconnect.base/src/main/jastadd/backend/Mappings.jrag
@@ -1,117 +1,91 @@
 aspect DefaultMappings {
 
-  private DefaultMappingDefinition RagConnect.baseDefaultMappingDefinitionFromBytes(String typeName) {
-    DefaultMappingDefinition result = new DefaultMappingDefinition();
-    result.setID("_DefaultBytesTo" + Character.toUpperCase(typeName.charAt(0)) + typeName.substring(1) + "Mapping");
-    result.setFromType(new JavaArrayMappingDefinitionType(new SimpleJavaTypeUse("byte")));
-    result.setFromVariableName("bytes");
-    result.setToType(new JavaMappingDefinitionType(new SimpleJavaTypeUse(typeName)));
-    return result;
-  }
-
-  private DefaultMappingDefinition RagConnect.baseDefaultMappingDefinitionToBytes(String typeName) {
-    DefaultMappingDefinition result = new DefaultMappingDefinition();
-    result.setID("_Default" + Character.toUpperCase(typeName.charAt(0)) + typeName.substring(1) + "ToBytesMapping");
-    result.setFromType(new JavaMappingDefinitionType(new SimpleJavaTypeUse(typeName)));
-    result.setFromVariableName("input");
-    result.setToType(new JavaArrayMappingDefinitionType(new SimpleJavaTypeUse("byte")));
-    return result;
-  }
-
-  syn nta DefaultMappingDefinition RagConnect.defaultBytesToIntMapping() {
-    DefaultMappingDefinition result = baseDefaultMappingDefinitionFromBytes("int");
-    result.setContent("return java.nio.ByteBuffer.wrap(bytes).getInt();");
-    return result;
-  }
-
-  syn nta DefaultMappingDefinition RagConnect.defaultBytesToShortMapping() {
-    DefaultMappingDefinition result = baseDefaultMappingDefinitionFromBytes("short");
-    result.setContent("return java.nio.ByteBuffer.wrap(bytes).getShort();");
-    return result;
-  }
-
-  syn nta DefaultMappingDefinition RagConnect.defaultBytesToLongMapping() {
-    DefaultMappingDefinition result = baseDefaultMappingDefinitionFromBytes("long");
-    result.setContent("return java.nio.ByteBuffer.wrap(bytes).getLong();");
-    return result;
-  }
-
-  syn nta DefaultMappingDefinition RagConnect.defaultBytesToFloatMapping() {
-    DefaultMappingDefinition result = baseDefaultMappingDefinitionFromBytes("float");
-    result.setContent("return java.nio.ByteBuffer.wrap(bytes).getFloat();");
-    return result;
-  }
-
-  syn nta DefaultMappingDefinition RagConnect.defaultBytesToDoubleMapping() {
-    DefaultMappingDefinition result = baseDefaultMappingDefinitionFromBytes("double");
-    result.setContent("return java.nio.ByteBuffer.wrap(bytes).getDouble();");
-    return result;
+  private String RagConnect.baseDefaultMappingTypeNamePart(String typeName) {
+    return capitalize(typeName).replace("[]", "s");
   }
 
-  syn nta DefaultMappingDefinition RagConnect.defaultBytesToCharMapping() {
-    DefaultMappingDefinition result = baseDefaultMappingDefinitionFromBytes("char");
-    result.setContent("return java.nio.ByteBuffer.wrap(bytes).getChar();");
-    return result;
+  private MappingDefinitionType RagConnect.baseDefaultMappingTypeFromName(String typeName) {
+    return typeName.endsWith("[]") ?
+        new JavaArrayMappingDefinitionType(new SimpleJavaTypeUse(typeName.replace("[]", ""))) :
+        new JavaMappingDefinitionType(new SimpleJavaTypeUse(typeName));
   }
 
-  syn nta DefaultMappingDefinition RagConnect.defaultBytesToStringMapping() {
-    DefaultMappingDefinition result = baseDefaultMappingDefinitionFromBytes("String");
-    result.setContent("return new String(bytes);");
-    return result;
-  }
-
-  syn nta DefaultMappingDefinition RagConnect.defaultIntToBytesMapping() {
-    DefaultMappingDefinition result = baseDefaultMappingDefinitionToBytes("int");
-    result.setContent("return java.nio.ByteBuffer.allocate(4).putInt(input).array();");
+  private DefaultMappingDefinition RagConnect.baseDefaultMappingDefinition(String fromTypeName, String toTypeName, String content) {
+    DefaultMappingDefinition result = new DefaultMappingDefinition();
+    result.setID("_Default" + baseDefaultMappingTypeNamePart(fromTypeName) + "To" + baseDefaultMappingTypeNamePart(toTypeName) + "Mapping");
+    result.setFromType(baseDefaultMappingTypeFromName(fromTypeName));
+    result.setFromVariableName("input");
+    result.setToType(baseDefaultMappingTypeFromName(toTypeName));
+    result.setContent(content);
     return result;
   }
 
-  syn nta DefaultMappingDefinition RagConnect.defaultShortToBytesMapping() {
-    DefaultMappingDefinition result = baseDefaultMappingDefinitionToBytes("short");
-    result.setContent("return java.nio.ByteBuffer.allocate(2).putShort(input).array();");
-    return result;
-  }
+  syn nta DefaultMappingDefinition RagConnect.defaultBytesToIntMapping() = baseDefaultMappingDefinition(
+      "byte[]", "int", "return java.nio.ByteBuffer.wrap(input).getInt();");
+  syn nta DefaultMappingDefinition RagConnect.defaultBytesToShortMapping() = baseDefaultMappingDefinition(
+      "byte[]", "short", "return java.nio.ByteBuffer.wrap(input).getShort();");
+  syn nta DefaultMappingDefinition RagConnect.defaultBytesToLongMapping() = baseDefaultMappingDefinition(
+      "byte[]", "long", "return java.nio.ByteBuffer.wrap(input).getLong();");
+  syn nta DefaultMappingDefinition RagConnect.defaultBytesToFloatMapping() = baseDefaultMappingDefinition(
+      "byte[]", "float", "return java.nio.ByteBuffer.wrap(input).getFloat();");
+  syn nta DefaultMappingDefinition RagConnect.defaultBytesToDoubleMapping() = baseDefaultMappingDefinition(
+      "byte[]", "double", "return java.nio.ByteBuffer.wrap(input).getDouble();");
+  syn nta DefaultMappingDefinition RagConnect.defaultBytesToCharMapping() = baseDefaultMappingDefinition(
+      "byte[]", "char", "return java.nio.ByteBuffer.wrap(input).getChar();");
+  syn nta DefaultMappingDefinition RagConnect.defaultBytesToStringMapping() = baseDefaultMappingDefinition(
+      "byte[]", "String", "return new String(input);");
 
-  syn nta DefaultMappingDefinition RagConnect.defaultLongToBytesMapping() {
-    DefaultMappingDefinition result = baseDefaultMappingDefinitionToBytes("long");
-    result.setContent("return java.nio.ByteBuffer.allocate(8).putLong(input).array();");
-    return result;
-  }
+  syn nta DefaultMappingDefinition RagConnect.defaultIntToBytesMapping() = baseDefaultMappingDefinition(
+      "int", "byte[]", "return java.nio.ByteBuffer.allocate(Integer.BYTES).putInt(input).array();");
+  syn nta DefaultMappingDefinition RagConnect.defaultShortToBytesMapping() = baseDefaultMappingDefinition(
+      "short", "byte[]", "return java.nio.ByteBuffer.allocate(Short.BYTES).putShort(input).array();");
+  syn nta DefaultMappingDefinition RagConnect.defaultLongToBytesMapping() = baseDefaultMappingDefinition(
+      "long", "byte[]", "return java.nio.ByteBuffer.allocate(Long.BYTES).putLong(input).array();");
+  syn nta DefaultMappingDefinition RagConnect.defaultFloatToBytesMapping() = baseDefaultMappingDefinition(
+      "float", "byte[]", "return java.nio.ByteBuffer.allocate(Float.BYTES).putFloat(input).array();");
+  syn nta DefaultMappingDefinition RagConnect.defaultDoubleToBytesMapping() = baseDefaultMappingDefinition(
+      "double", "byte[]", "return java.nio.ByteBuffer.allocate(Double.BYTES).putDouble(input).array();");
+  syn nta DefaultMappingDefinition RagConnect.defaultCharToBytesMapping() = baseDefaultMappingDefinition(
+      "char", "byte[]", "return java.nio.ByteBuffer.allocate(Character.BYTES).putChar(input).array();");
+  syn nta DefaultMappingDefinition RagConnect.defaultStringToBytesMapping() = baseDefaultMappingDefinition(
+      "String", "byte[]", "return input.getBytes();");
 
-  syn nta DefaultMappingDefinition RagConnect.defaultFloatToBytesMapping() {
-    DefaultMappingDefinition result = baseDefaultMappingDefinitionToBytes("float");
-    result.setContent("return java.nio.ByteBuffer.allocate(4).putFloat(input).array();");
-    return result;
-  }
+  syn nta DefaultMappingDefinition RagConnect.defaultStringToIntMapping() = baseDefaultMappingDefinition(
+      "String", "int", "return Integer.parseInt(input);");
+  syn nta DefaultMappingDefinition RagConnect.defaultStringToShortMapping() = baseDefaultMappingDefinition(
+      "String", "short", "return Short.parseShort(input);");
+  syn nta DefaultMappingDefinition RagConnect.defaultStringToLongMapping() = baseDefaultMappingDefinition(
+      "String", "long", "return Long.parseLong(input);");
+  syn nta DefaultMappingDefinition RagConnect.defaultStringToFloatMapping() = baseDefaultMappingDefinition(
+      "String", "float", "return Float.parseFloat(input);");
+  syn nta DefaultMappingDefinition RagConnect.defaultStringToDoubleMapping() = baseDefaultMappingDefinition(
+      "String", "double", "return Double.parseDouble(input);");
+  syn nta DefaultMappingDefinition RagConnect.defaultStringToCharMapping() = baseDefaultMappingDefinition(
+      "String", "char", "return input.charAt(0);");
 
-  syn nta DefaultMappingDefinition RagConnect.defaultDoubleToBytesMapping() {
-    DefaultMappingDefinition result = baseDefaultMappingDefinitionToBytes("double");
-    result.setContent("return java.nio.ByteBuffer.allocate(8).putDouble(input).array();");
-    return result;
-  }
-
-  syn nta DefaultMappingDefinition RagConnect.defaultCharToBytesMapping() {
-    DefaultMappingDefinition result = baseDefaultMappingDefinitionToBytes("char");
-    result.setContent("return java.nio.ByteBuffer.allocate(2).putChar(input).array();");
-    return result;
-  }
-
-  syn nta DefaultMappingDefinition RagConnect.defaultStringToBytesMapping() {
-    DefaultMappingDefinition result = baseDefaultMappingDefinitionToBytes("String");
-    result.setContent("return input.getBytes();");
-    return result;
-  }
+  syn nta DefaultMappingDefinition RagConnect.defaultIntToStringMapping() = baseDefaultMappingDefinition(
+      "int", "String", "return String.valueOf(input);");
+  syn nta DefaultMappingDefinition RagConnect.defaultShortToStringMapping() = baseDefaultMappingDefinition(
+      "short", "String", "return String.valueOf(input);");
+  syn nta DefaultMappingDefinition RagConnect.defaultLongToStringMapping() = baseDefaultMappingDefinition(
+      "long", "String", "return String.valueOf(input);");
+  syn nta DefaultMappingDefinition RagConnect.defaultFloatToStringMapping() = baseDefaultMappingDefinition(
+      "float", "String", "return String.valueOf(input);");
+  syn nta DefaultMappingDefinition RagConnect.defaultDoubleToStringMapping() = baseDefaultMappingDefinition(
+      "double", "String", "return String.valueOf(input);");
+  syn nta DefaultMappingDefinition RagConnect.defaultCharToStringMapping() = baseDefaultMappingDefinition(
+      "char", "String", "return String.valueOf(input);");
 }
 
 aspect Mappings {
   // --- effectiveMappings ---
   syn java.util.List<MappingDefinition> EndpointDefinition.effectiveMappings();
-  eq ReceiveFromMqttDefinition.effectiveMappings() {
-    // if there is a first mapping, check if its input type is byte[].
+  eq ReceiveTokenEndpointDefinition.effectiveMappings() {
+    // if there is a first mapping, check if it is suitable.
     //  or if no mappings are specified.
     // then prepend the suitable default mapping
     java.util.List<MappingDefinition> result;
-    if (getMappingList().isEmpty() || !getMappingList().get(0).getFromType().isByteArray()) {
+    if (getMappingList().isEmpty() || !hasSuitableEdgeMapping()) {
       result = new java.util.ArrayList();
       result.add(suitableDefaultMapping());
       result.addAll(getMappingList());
@@ -120,15 +94,13 @@ aspect Mappings {
     }
     return result;
   }
-  eq SendToMqttDefinition.effectiveMappings() {
-    // if there is a mapping, check if the output type of the last mapping is byte[].
+  eq SendTokenEndpointDefinition.effectiveMappings() {
+    // if there is a mapping, check if it is suitable.
     //  or if no mappings are specified.
     // then append the suitable default mapping
     java.util.List<MappingDefinition> result;
-    int numMappings = getMappingList().size();
-    if (numMappings == 0 || !getMappingList().get(numMappings - 1).getToType().isByteArray()) {
-      result = new java.util.ArrayList();
-      result.addAll(getMappingList());
+    if (getMappingList().isEmpty() || !hasSuitableEdgeMapping()) {
+      result = new java.util.ArrayList(getMappingList());
       result.add(suitableDefaultMapping());
     } else {
       result = getMappingList();
@@ -136,8 +108,18 @@ aspect Mappings {
     return result;
   }
 
+  // --- hasSuitableEdgeMapping ---
+  syn boolean TokenEndpointDefinition.hasSuitableEdgeMapping();
+  eq ReceiveTokenEndpointDefinition.hasSuitableEdgeMapping() = isSuitableEdgeMapping(getMappingList().get(0));
+  eq SendTokenEndpointDefinition.hasSuitableEdgeMapping() = isSuitableEdgeMapping(getMappingList().get(getMappingList().size() - 1));
+
+  // --- isSuitableEdgeMapping(def) ---
+  syn boolean TokenEndpointDefinition.isSuitableEdgeMapping(MappingDefinition def);
+  eq ReceiveTokenEndpointDefinition.isSuitableEdgeMapping(MappingDefinition def) = def.getFromType().isByteArray();
+  eq SendTokenEndpointDefinition.isSuitableEdgeMapping(MappingDefinition def) = def.getToType().isByteArray();
+
   // --- isPrimitiveType ---
-  syn boolean TokenComponent.isPrimitiveType() = getJavaTypeUse().isPrimitiveType();
+  syn boolean TokenComponent.isPrimitiveType() = effectiveJavaTypeUse().isPrimitiveType();
   syn boolean JavaTypeUse.isPrimitiveType() = false;
   eq SimpleJavaTypeUse.isPrimitiveType() {
     switch(getName()) {
@@ -160,9 +142,9 @@ aspect Mappings {
 
   // --- suitableDefaultMapping ---
   syn DefaultMappingDefinition EndpointDefinition.suitableDefaultMapping();
-  eq ReceiveFromMqttDefinition.suitableDefaultMapping() {
+  eq ReceiveTokenEndpointDefinition.suitableDefaultMapping() {
     String typeName = getMappingList().isEmpty() ?
-        getToken().getJavaTypeUse().getName() :
+        getToken().effectiveJavaTypeUse().getName() :
         getMappingList().get(0).getFromType().prettyPrint();
     switch(typeName) {
       case "int":
@@ -181,9 +163,9 @@ aspect Mappings {
       default: return null;
     }
   }
-  eq SendToMqttDefinition.suitableDefaultMapping() {
+  eq SendTokenEndpointDefinition.suitableDefaultMapping() {
     String typeName = getMappingList().isEmpty() ?
-        getToken().getJavaTypeUse().getName() :
+        getToken().effectiveJavaTypeUse().getName() :
         getMappingList().get(getMappingList().size() - 1).getFromType().prettyPrint();
     switch(typeName) {
       case "int":
@@ -202,15 +184,60 @@ aspect Mappings {
       default: return null;
     }
   }
+//  eq ReceiveFromRestDefinition.suitableDefaultMapping() {
+//    String typeName = getMappingList().isEmpty() ?
+//        getToken().getJavaTypeUse().getName() :
+//        getMappingList().get(0).getFromType().prettyPrint();
+//    switch(typeName) {
+//      case "int":
+//      case "Integer": return ragconnect().defaultStringToIntMapping();
+//      case "short":
+//      case "Short": return ragconnect().defaultStringToShortMapping();
+//      case "long":
+//      case "Long": return ragconnect().defaultStringToLongMapping();
+//      case "float":
+//      case "Float": return ragconnect().defaultStringToFloatMapping();
+//      case "double":
+//      case "Double": return ragconnect().defaultStringToDoubleMapping();
+//      case "char":
+//      case "Character": return ragconnect().defaultStringToCharMapping();
+//      default: return null;
+//    }
+//  }
+//  eq SendToRestDefinition.suitableDefaultMapping() {
+//    String typeName = getMappingList().isEmpty() ?
+//        getToken().getJavaTypeUse().getName() :
+//        getMappingList().get(getMappingList().size() - 1).getFromType().prettyPrint();
+//    switch(typeName) {
+//      case "int":
+//      case "Integer": return ragconnect().defaultIntToStringMapping();
+//      case "short":
+//      case "Short": return ragconnect().defaultShortToStringMapping();
+//      case "long":
+//      case "Long": return ragconnect().defaultLongToStringMapping();
+//      case "float":
+//      case "Float": return ragconnect().defaultFloatToStringMapping();
+//      case "double":
+//      case "Double": return ragconnect().defaultDoubleToStringMapping();
+//      case "char":
+//      case "Character": return ragconnect().defaultCharToStringMapping();
+//      default: return null;
+//    }
+//  }
 
   // --- isByteArray ---
   syn boolean MappingDefinitionType.isByteArray() = false;
   eq JavaArrayMappingDefinitionType.isByteArray() = getType().getName().equals("byte");
 
+//  // --- isString ---
+//  syn boolean MappingDefinitionType.isString() = false;
+//  eq JavaMappingDefinitionType.isString() = getType().getName().equals("String");
+
   // --- allMappingDefinitions ---
   syn java.util.List<MappingDefinition> RagConnect.allMappingDefinitions() {
     java.util.List<MappingDefinition> result = new java.util.ArrayList<>();
     getMappingDefinitionList().iterator().forEachRemaining(result::add);
+    // byte[] conversion
     result.add(defaultBytesToIntMapping());
     result.add(defaultBytesToShortMapping());
     result.add(defaultBytesToLongMapping());
@@ -225,6 +252,19 @@ aspect Mappings {
     result.add(defaultDoubleToBytesMapping());
     result.add(defaultCharToBytesMapping());
     result.add(defaultStringToBytesMapping());
+//    // string conversion
+//    result.add(defaultStringToIntMapping());
+//    result.add(defaultStringToShortMapping());
+//    result.add(defaultStringToLongMapping());
+//    result.add(defaultStringToFloatMapping());
+//    result.add(defaultStringToDoubleMapping());
+//    result.add(defaultStringToCharMapping());
+//    result.add(defaultIntToStringMapping());
+//    result.add(defaultShortToStringMapping());
+//    result.add(defaultLongToStringMapping());
+//    result.add(defaultFloatToStringMapping());
+//    result.add(defaultDoubleToStringMapping());
+//    result.add(defaultCharToStringMapping());
     return result;
   }
 }
diff --git a/ragconnect.base/src/main/jastadd/backend/MustacheNodesToYAML.jrag b/ragconnect.base/src/main/jastadd/backend/MustacheNodesToYAML.jrag
new file mode 100644
index 0000000000000000000000000000000000000000..2894d3a8a531a190e8146121dc8e7c97f2cecc91
--- /dev/null
+++ b/ragconnect.base/src/main/jastadd/backend/MustacheNodesToYAML.jrag
@@ -0,0 +1,155 @@
+aspect MustacheNodesToYAML {
+  syn Document MRagConnect.toYAML() {
+    Document doc = new Document();
+    MappingElement root = new MappingElement();
+    root.addKeyValuePair("rootNodeName", StringElement.of(rootNodeName()));
+    root.addKeyValuePair("closeMethod", StringElement.of(closeMethod()));
+    root.addKeyValuePair("usesMqtt", ValueElement.of(usesMqtt));
+    root.addKeyValuePair("usesRest", ValueElement.of(usesRest));
+    // mqtt
+    root.addKeyValuePair("mqttHandlerField", StringElement.of(mqttHandlerField()));
+    root.addKeyValuePair("mqttHandlerAttribute", StringElement.of(mqttHandlerAttribute()));
+    root.addKeyValuePair("mqttSetupWaitUntilReadyMethod", StringElement.of(mqttSetupWaitUntilReadyMethod()));
+
+    // rootTypeComponents
+    ListElement rootTypeComponents = new ListElement();
+    for (MTypeComponent comp : getRootTypeComponentList()) {
+      MappingElement inner = new MappingElement();
+      inner.addKeyValuePair("first", ValueElement.of(comp.isFirst()));
+      inner.addKeyValuePair("name", StringElement.of(comp.name()));
+      rootTypeComponents.addElement(inner);
+    }
+    root.addKeyValuePair("RootTypeComponents", rootTypeComponents);
+
+    // rest
+    root.addKeyValuePair("restHandlerField", StringElement.of(restHandlerField()));
+    root.addKeyValuePair("restHandlerAttribute", StringElement.of(restHandlerAttribute()));
+
+    // ReceiveDefinitions
+    ListElement receiveDefinitions = new ListElement();
+    for (MReceiveDefinition def : getReceiveDefinitionList()) {
+      receiveDefinitions.addElement(def.toYAML());
+    }
+    root.addKeyValuePair("ReceiveDefinitions", receiveDefinitions);
+
+    // SendDefinitions
+    ListElement sendDefinitions = new ListElement();
+    for (MSendDefinition def : getSendDefinitionList()) {
+      sendDefinitions.addElement(def.toYAML());
+    }
+    root.addKeyValuePair("SendDefinitions", sendDefinitions);
+
+    // MappingDefinitions
+    ListElement mappingDefinitions = new ListElement();
+    for (MMappingDefinition def : getMappingDefinitionList()) {
+      mappingDefinitions.addElement(def.toYAML());
+    }
+    root.addKeyValuePair("MappingDefinitions", mappingDefinitions);
+
+    // DependencyDefinitions
+    ListElement dependencyDefinitions = new ListElement();
+    for (MDependencyDefinition def : getDependencyDefinitionList()) {
+      dependencyDefinitions.addElement(def.toYAML());
+    }
+    root.addKeyValuePair("DependencyDefinitions", dependencyDefinitions);
+
+    // TokenComponents
+    ListElement tokenComponents = new ListElement();
+    for (MTokenComponent comp : getTokenComponentList()) {
+      tokenComponents.addElement(comp.toYAML());
+    }
+    root.addKeyValuePair("TokenComponents", tokenComponents);
+
+    doc.addComplexElement(root);
+    return doc;
+  }
+
+  syn MappingElement MEndpointDefinition.toYAML() {
+    MappingElement result = new MappingElement();
+    result.addKeyValuePair("parentTypeName", StringElement.of(parentTypeName()));
+    result.addKeyValuePair("connectMethod", StringElement.of(connectMethod()));
+    result.addKeyValuePair("connectParameterName", StringElement.of(connectParameterName()));
+    result.addKeyValuePair("lastDefinitionToType", StringElement.of(lastDefinitionToType()));
+    result.addKeyValuePair("preemptiveReturn", StringElement.of(preemptiveReturn()));
+    result.addKeyValuePair("alwaysApply", ValueElement.of(alwaysApply()));
+    result.addKeyValuePair("condition", StringElement.of(
+    condition().replace("\"", "\\\"").replace("\n", "\\n")));
+    result.addKeyValuePair("lastResult", StringElement.of(lastResult()));
+    result.addKeyValuePair("tokenName", StringElement.of(tokenName()));
+    result.addKeyValuePair("InnerMappingDefinitions", innerMappingDefinitionsAsListElement());
+    return result;
+  }
+
+  syn MappingElement MReceiveDefinition.toYAML() {
+    MappingElement result = super.toYAML();
+    result.addKeyValuePair("loggingEnabledForReads", ValueElement.of(loggingEnabledForReads));
+    return result;
+  }
+
+  syn MappingElement MSendDefinition.toYAML() {
+    MappingElement result = super.toYAML();
+    result.addKeyValuePair("sender", StringElement.of(sender()));
+    result.addKeyValuePair("lastValue", StringElement.of(lastValue()));
+    result.addKeyValuePair("loggingEnabledForWrites", ValueElement.of(loggingEnabledForWrites));
+    result.addKeyValuePair("updateMethod", StringElement.of(updateMethod()));
+    result.addKeyValuePair("writeMethod", StringElement.of(writeMethod()));
+    result.addKeyValuePair("tokenResetMethod", StringElement.of(tokenResetMethod()));
+    return result;
+  }
+
+  syn Element MMappingDefinition.toYAML() {
+    MappingElement result = new MappingElement();
+    result.addKeyValuePair("toType", StringElement.of(toType()));
+    result.addKeyValuePair("methodName", StringElement.of(methodName()));
+    result.addKeyValuePair("fromType", StringElement.of(fromType()));
+    result.addKeyValuePair("fromVariableName", StringElement.of(fromVariableName()));
+    result.addKeyValuePair("content", StringElement.of(
+        content().replace("\"", "\\\"").replace("\n", "\\n")));
+    return result;
+  }
+
+  syn Element MDependencyDefinition.toYAML() {
+    MappingElement result = new MappingElement();
+    result.addKeyValuePair("targetParentTypeName", StringElement.of(targetParentTypeName()));
+    result.addKeyValuePair("dependencyMethod", StringElement.of(dependencyMethod()));
+    result.addKeyValuePair("sourceParentTypeName", StringElement.of(sourceParentTypeName()));
+    result.addKeyValuePair("internalRelationPrefix", StringElement.of(internalRelationPrefix()));
+    return result;
+  }
+
+  syn Element MTokenComponent.toYAML() {
+    MappingElement result = new MappingElement();
+    result.addKeyValuePair("parentTypeName", StringElement.of(parentTypeName()));
+    result.addKeyValuePair("name", StringElement.of(name()));
+    result.addKeyValuePair("javaType", StringElement.of(javaType()));
+    result.addKeyValuePair("internalName", StringElement.of(internalName()));
+    ListElement dependencyDefinitions = new ListElement();
+    for (MDependencyDefinition def : getDependencyDefinitionList()) {
+      MappingElement inner = new MappingElement();
+      inner.addKeyValuePair("targetParentTypeName", StringElement.of(def.targetParentTypeName()));
+      inner.addKeyValuePair("internalRelationPrefix", StringElement.of(def.internalRelationPrefix()));
+      MappingElement targetEndpointDefinition = new MappingElement();
+    targetEndpointDefinition.addKeyValuePair("updateMethod", StringElement.of(def.targetEndpointDefinition().updateMethod()));
+    targetEndpointDefinition.addKeyValuePair("writeMethod", StringElement.of(def.targetEndpointDefinition().writeMethod()));
+      inner.addKeyValuePair("targetEndpointDefinition", targetEndpointDefinition);
+      dependencyDefinitions.addElement(inner);
+    }
+    result.addKeyValuePair("DependencyDefinitions", dependencyDefinitions);
+    return result;
+  }
+
+  ListElement MEndpointDefinition.innerMappingDefinitionsAsListElement() {
+    ListElement innerMappingDefinitions = new ListElement();
+    for (MInnerMappingDefinition def : getInnerMappingDefinitionList()) {
+      MappingElement inner = new MappingElement();
+      inner.addKeyValuePair("toType", StringElement.of(def.toType()));
+      inner.addKeyValuePair("methodName", StringElement.of(def.methodName()));
+      inner.addKeyValuePair("inputVarName", StringElement.of(def.inputVarName()));
+      inner.addKeyValuePair("outputVarName", StringElement.of(def.outputVarName()));
+      inner.addKeyValuePair("last", ValueElement.of(def.isLast()));
+      innerMappingDefinitions.addElement(inner);
+    }
+    return innerMappingDefinitions;
+  }
+
+}
diff --git a/ragconnect.base/src/main/jastadd/parser/RagConnect.parser b/ragconnect.base/src/main/jastadd/parser/RagConnect.parser
index e2685c15555056c64d4f5e8bebdc458ecea981a3..0432b0df96c44060f6df1d6918a856f62810230d 100644
--- a/ragconnect.base/src/main/jastadd/parser/RagConnect.parser
+++ b/ragconnect.base/src/main/jastadd/parser/RagConnect.parser
@@ -10,41 +10,35 @@ RagConnect ragconnect
   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) {
+    def.setAlwaysApply(true);
+    return def;
+  }
 :} ;
 
 EndpointDefinition endpoint_definition
-  = RECEIVE ID.type_name DOT ID.token_name SCOL
+  = endpoint_definition_type.endpointDef SCOL
     {:
-      ReceiveFromMqttDefinition result = new ReceiveFromMqttDefinition();
-      result.setToken(TokenComponent.createRef(type_name + "." + token_name));
-      return result;
+      return endpointDef;
     :}
-  | RECEIVE ID.type_name DOT ID.token_name USING string_list.mapping_defs SCOL
+  | endpoint_definition_type.endpointDef USING string_list.mapping_defs SCOL
     {:
-      ReceiveFromMqttDefinition result = new ReceiveFromMqttDefinition();
-      result.setToken(TokenComponent.createRef(type_name + "." + token_name));
       for (String mapping_def : makeMappingDefs(mapping_defs)) {
-        result.addMapping(MappingDefinition.createRef(mapping_def));
+        endpointDef.addMapping(MappingDefinition.createRef(mapping_def));
       }
-      return result;
-    :}
-  | SEND ID.type_name DOT ID.token_name SCOL
-    {:
-      SendToMqttDefinition result = new SendToMqttDefinition();
-      result.setToken(TokenComponent.createRef(type_name + "." + token_name));
-      return result;
-    :}
-  | SEND ID.type_name DOT ID.token_name USING string_list.mapping_defs SCOL
-    {:
-      SendToMqttDefinition result = new SendToMqttDefinition();
-      result.setToken(TokenComponent.createRef(type_name + "." + token_name));
-      for (String mapping_def : makeMappingDefs(mapping_defs)) {
-        result.addMapping(MappingDefinition.createRef(mapping_def));
-      }
-      return result;
+      return endpointDef;
     :}
 ;
 
+EndpointDefinition endpoint_definition_type
+  = RECEIVE token_ref           {: return new ReceiveTokenEndpointDefinition().setToken(token_ref); :}
+  | SEND token_ref              {: return new SendTokenEndpointDefinition().setToken(token_ref); :}
+;
+
+TokenComponent token_ref
+  = ID.type_name DOT ID.token_name  {: return TokenComponent.createRef(type_name + "." + token_name); :}
+;
+
 ArrayList string_list
   = ID
   | string_list COMMA ID
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 8b33412c7994b5443088de6249273b9b83374414..711169ec2ea7d9c21b48dc52c5a7d2cf4cc8b427 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
@@ -14,18 +14,21 @@ import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.Paths;
 import java.nio.file.StandardCopyOption;
-import java.util.Collection;
-import java.util.MissingResourceException;
-import java.util.ResourceBundle;
+import java.util.*;
 
 public class Compiler extends AbstractCompiler {
 
 //  private ValueOption optionOutputDir;
   private ValueOption optionRootNode;
+  private ValueOption optionProtocols;
+  private BooleanOption optionPrintYaml;
   private BooleanOption optionVerbose;
   private BooleanOption optionLogReads;
   private BooleanOption optionLogWrites;
 
+  private static final String OPTION_PROTOCOL_MQTT = "mqtt";
+  private static final String OPTION_PROTOCOL_REST = "rest";
+
   public Compiler() {
     super("ragconnect", true);
   }
@@ -51,6 +54,10 @@ public class Compiler extends AbstractCompiler {
       }
     }
 
+    if (!optionRootNode.isMatched()) {
+      return error("Root node not specified");
+    }
+
     RagConnect ragConnect = parseProgram(getConfiguration().getFiles());
 
     if (!ragConnect.errors().isEmpty()) {
@@ -61,25 +68,41 @@ public class Compiler extends AbstractCompiler {
       System.exit(1);
     }
 
+    if (optionPrintYaml.value()) {
+      ASTNode.rootNode = ragConnect.getProgram().resolveTypeDecl(optionRootNode.value());
+      String yamlContent = ragConnect.toMustache().toYAML().prettyPrint();
+      System.out.println(yamlContent);
+      writeToFile(getConfiguration().outputDir().toPath().resolve("RagConnect.yml"), yamlContent);
+      return 0;
+    }
+
     printMessage("Writing output files");
-    // copy MqttHandler into outputDir
-    final String mqttHandlerFileName = "MqttHandler.jadd";
-    try {
-      InputStream inputStream = Compiler.class.getClassLoader().getResourceAsStream(mqttHandlerFileName);
-      if (inputStream == null) {
-        throw new CompilerException("Could not open " + mqttHandlerFileName);
+    final List<String> handlers = new ArrayList<>();
+    if (ASTNode.usesMqtt) {
+      handlers.add("MqttHandler.jadd");
+    }
+    if (ASTNode.usesRest) {
+      handlers.add("RestHandler.jadd");
+    }
+    // copy handlers into outputDir
+    for (String handlerFileName : handlers) {
+      try {
+        InputStream inputStream = Compiler.class.getClassLoader().getResourceAsStream(handlerFileName);
+        if (inputStream == null) {
+          throw new CompilerException("Could not open " + handlerFileName);
+        }
+        Files.copy(inputStream,
+            getConfiguration().outputDir().toPath().resolve(handlerFileName),
+            StandardCopyOption.REPLACE_EXISTING);
+      } catch (IOException e) {
+        throw new CompilerException("Could not copy " + handlerFileName, e);
       }
-      Files.copy(inputStream,
-          getConfiguration().outputDir().toPath().resolve(mqttHandlerFileName),
-          StandardCopyOption.REPLACE_EXISTING);
-    } catch (IOException e) {
-      throw new CompilerException("Could not copy " + mqttHandlerFileName, e);
     }
     for (GrammarFile grammarFile : ragConnect.getProgram().getGrammarFileList()) {
       Path outputFile = getConfiguration().outputDir().toPath().resolve(grammarFile.getFileName());
-      sendToFile(outputFile, grammarFile.generateAbstractGrammar());
+      writeToFile(outputFile, grammarFile.generateAbstractGrammar());
     }
-    sendToFile(getConfiguration().outputDir().toPath().resolve("RagConnect.jadd"), ragConnect.generateAspect(optionRootNode.value()));
+    writeToFile(getConfiguration().outputDir().toPath().resolve("RagConnect.jadd"), ragConnect.generateAspect(optionRootNode.value()));
     return 0;
   }
 
@@ -89,8 +112,8 @@ public class Compiler extends AbstractCompiler {
     try {
       new Compiler().run(args);
     } catch (CompilerException e) {
-      System.err.println(e.getMessage());
-      System.exit(-1);
+      e.printStackTrace();
+      System.exit(1);
     }
   }
 
@@ -118,7 +141,7 @@ public class Compiler extends AbstractCompiler {
     System.out.println(message);
   }
 
-  private void sendToFile(Path path, String str) throws CompilerException {
+  private void writeToFile(Path path, String str) throws CompilerException {
     try (BufferedWriter writer = Files.newBufferedWriter(path)) {
       writer.append(str);
     } catch (Exception e) {
@@ -132,6 +155,15 @@ public class Compiler extends AbstractCompiler {
         new ValueOption("rootNode", "root node in the base grammar.")
         .acceptAnyValue()
         .needsValue(true));
+    optionProtocols = addOption(
+        new ValueOption("protocols", "Protocols to enable")
+        .acceptMultipleValues(true)
+        .addDefaultValue(OPTION_PROTOCOL_MQTT, "Enable MQTT")
+        .addAcceptedValue(OPTION_PROTOCOL_REST, "Enable REST")
+    );
+    optionPrintYaml = addOption(
+        new BooleanOption("printYaml", "Print out YAML instead of generating files")
+        .defaultValue(false));
     optionVerbose = addOption(
         new BooleanOption("verbose", "Print more messages while compiling.")
         .defaultValue(false));
@@ -145,6 +177,8 @@ public class Compiler extends AbstractCompiler {
 
   private RagConnect parseProgram(Collection<String> files) throws CompilerException {
     Program program = new Program();
+    boolean atLeastOneGrammar = false;
+    boolean atLeastOneRagConnect = false;
 
     RagConnect ragConnect = new RagConnect();
     ragConnect.setProgram(program);
@@ -160,20 +194,34 @@ public class Compiler extends AbstractCompiler {
         case "relast":
           // processGrammar
           parseGrammar(program, filename);
+          atLeastOneGrammar = true;
           break;
         case "connect":
+        case "ragconnect":
           // process ragConnect
           RagConnect parsedRagConnect = parseRagConnect(program, filename);
           mergeRagConnectDefinitions(ragConnect, parsedRagConnect);
+          atLeastOneRagConnect = true;
           break;
         default:
           throw new CompilerException("Unknown file extension in " + filename);
       }
     }
+    if (!atLeastOneGrammar) {
+      System.err.println("No grammar file specified! (*.ast, *.relast)");
+    }
+    if (!atLeastOneRagConnect) {
+      System.err.println("No ragconnect file specified! (*.connect, *.ragconnect)");
+    }
+    if (!atLeastOneGrammar && !atLeastOneRagConnect) {
+      System.exit(1);
+    }
     ragConnect.treeResolveAll();
     ragConnect.additionalRelations().forEach(ragConnectGrammarPart::addDeclaration);
     ASTNode.loggingEnabledForReads = optionLogReads.value();
     ASTNode.loggingEnabledForWrites = optionLogWrites.value();
+    ASTNode.usesMqtt = optionProtocols.hasValue(OPTION_PROTOCOL_MQTT);
+    ASTNode.usesRest = optionProtocols.hasValue(OPTION_PROTOCOL_REST);
     return ragConnect;
   }
 
diff --git a/ragconnect.base/src/main/java/org/jastadd/ragconnect/compiler/SimpleMain.java b/ragconnect.base/src/main/java/org/jastadd/ragconnect/compiler/SimpleMain.java
index ac1109fef423ed58ebbbdfcaeacbdca42745e341..ee9ed046348efeaa4b12dbc816c55414d93289cf 100644
--- a/ragconnect.base/src/main/java/org/jastadd/ragconnect/compiler/SimpleMain.java
+++ b/ragconnect.base/src/main/java/org/jastadd/ragconnect/compiler/SimpleMain.java
@@ -1,15 +1,11 @@
 package org.jastadd.ragconnect.compiler;
 
-import beaver.Parser;
 import org.jastadd.ragconnect.ast.*;
 import org.jastadd.ragconnect.parser.RagConnectParser;
 import org.jastadd.ragconnect.scanner.RagConnectScanner;
 
-import java.io.BufferedReader;
-import java.io.IOException;
-import java.nio.ByteBuffer;
+import java.io.Reader;
 import java.nio.file.Files;
-import java.nio.file.Path;
 import java.nio.file.Paths;
 
 /**
@@ -19,141 +15,87 @@ import java.nio.file.Paths;
  */
 public class SimpleMain {
 
-  // --- just testing byte[] conversion ---
-  public static void testing() {
-    byte[] bytes;
-    int i = 1;
-    double d = 2.3d;
-    float f = 4.2f;
-    short sh = 13;
-    long l = 7L;
-    String s = "Hello";
-    char c = 'a';
-
-    Integer ii = Integer.valueOf(1);
-    if (!ii.equals(i)) throw new AssertionError("Ints not equal");
-
-    // int to byte
-    ByteBuffer i2b = ByteBuffer.allocate(4);
-    i2b.putInt(i);
-    bytes = i2b.array();
-
-    // byte to int
-    ByteBuffer b2i = ByteBuffer.wrap(bytes);
-    int actual_i = b2i.getInt();
-    if (i != actual_i) throw new AssertionError("Ints not equal");
-
-    // double to byte
-    ByteBuffer d2b = ByteBuffer.allocate(8);
-    d2b.putDouble(d);
-    bytes = d2b.array();
-
-    // byte to double
-    ByteBuffer b2d = ByteBuffer.wrap(bytes);
-    double actual_d = b2d.getDouble();
-    if (d != actual_d) throw new AssertionError("Doubles not equal");
-
-    // float to byte
-    ByteBuffer f2b = ByteBuffer.allocate(4);
-    f2b.putFloat(f);
-    bytes = f2b.array();
-
-    // byte to float
-    ByteBuffer b2f = ByteBuffer.wrap(bytes);
-    float actual_f = b2f.getFloat();
-    if (f != actual_f) throw new AssertionError("Floats not equal");
-
-    // short to byte
-    ByteBuffer sh2b = ByteBuffer.allocate(2);
-    sh2b.putShort(sh);
-    bytes = sh2b.array();
-
-    // byte to short
-    ByteBuffer b2sh = ByteBuffer.wrap(bytes);
-    short actual_sh = b2sh.getShort();
-    if (sh != actual_sh) throw new AssertionError("Shorts not equal");
-
-    // long to byte
-    ByteBuffer l2b = ByteBuffer.allocate(8);
-    l2b.putLong(l);
-    bytes = l2b.array();
-
-    // byte to long
-    ByteBuffer b2l = ByteBuffer.wrap(bytes);
-    long actual_l = b2l.getLong();
-    if (l != actual_l) throw new AssertionError("Longs not equal");
-
-    // String to byte
-    bytes = s.getBytes();
-
-    // byte to String
-    String actual_s = new String(bytes);
-    if (!s.equals(actual_s)) throw new AssertionError("Strings not equal");
-
-    // char to byte
-    ByteBuffer c2b = ByteBuffer.allocate(2);
-    c2b.putChar(c);
-    bytes = c2b.array();
-
-    // byte to char
-    ByteBuffer b2c = ByteBuffer.wrap(bytes);
-    char actual_c = b2c.getChar();
-    if (c != actual_c) throw new AssertionError("Floats not equal");
-  }
-
-  public static void main(String[] args) {
-//    testing();
-    createManualAST();
-  }
-
-  private static void createManualAST() {
-    RagConnect model = new RagConnect();
-    Program program = parseProgram(Paths.get("ros2rag.starter","src", "main", "jastadd", "RobotModel.relast"));
-    model.setProgram(program);
-
-    MappingDefinition mappingDefinition = new MappingDefinition();
-    mappingDefinition.setID("PoseToPosition");
-    mappingDefinition.setFromType(makeMappingDefinitionType("int"));
-    mappingDefinition.setFromVariableName("x");
-    mappingDefinition.setToType(makeMappingDefinitionType("Position"));
-    mappingDefinition.setContent("      pose.position.x += sqrt(.5 * size.x)\n" +
-        "      MAP round(2)\n" +
-        "      x = x / 100\n" +
-        "      IGNORE_IF_SAME\n" +
-        "    ;");
-    model.addMappingDefinition(mappingDefinition);
-
-    ReceiveFromMqttDefinition receiveFromMqttDefinition = new ReceiveFromMqttDefinition();
-    receiveFromMqttDefinition.setAlwaysApply(false);
-    receiveFromMqttDefinition.setToken(TokenComponent.createRef("Link.CurrentPosition"));
-    receiveFromMqttDefinition.addMapping(mappingDefinition);
-    model.addEndpointDefinition(receiveFromMqttDefinition);
-
-    model.treeResolveAll();
-    for (ErrorMessage error : model.errors()) {
-      System.err.println(error);
-    }
-
-    System.out.println(model.generateAspect("Model"));
+  private static void printManualYAML() {
+    Document doc = new Document();
+    KeyValuePair root = new KeyValuePair();
+    root.setKey("panda_mqtt_connector");
+    MappingElement firstLevel = new MappingElement();
+    firstLevel.addKeyValuePair(new KeyValuePair("server", new StringElement("tcp://localhost:1883")));
+    firstLevel.addKeyValuePair(new KeyValuePair("robot_speed_factor", new ValueElement(".7")));
+
+    KeyValuePair topics = new KeyValuePair();
+    topics.setKey("topics");
+    MappingElement theTopics = new MappingElement();
+    theTopics.addKeyValuePair(new KeyValuePair("robotConfig", new StringElement("robotconfig")));
+    theTopics.addKeyValuePair(new KeyValuePair("trajectory", new StringElement("trajectory")));
+    theTopics.addKeyValuePair(new KeyValuePair("nextStep", new StringElement("ros2rag/nextStep")));
+    topics.setValue(theTopics);
+    firstLevel.addKeyValuePair(topics);
+
+    firstLevel.addKeyValuePair(new KeyValuePair("zone_size", new ValueElement("0.5")));
+
+    KeyValuePair zones = new KeyValuePair();
+    zones.setKey("zones");
+    ListElement theZones = new ListElement();
+    theZones.addElement(new StringElement("1 1"));
+    theZones.addElement(new StringElement("0 1"));
+    theZones.addElement(new StringElement("-1 1"));
+    zones.setValue(theZones);
+    firstLevel.addKeyValuePair(zones);
+
+    KeyValuePair parts = new KeyValuePair();
+    parts.setKey("end_effectors");
+    KeyValuePair pandaParts = new KeyValuePair();
+    pandaParts.setKey("panda");
+    MappingElement thePanda = new MappingElement();
+    thePanda.addKeyValuePair(new KeyValuePair("Link0", new StringElement("panda_link0")));
+    thePanda.addKeyValuePair(new KeyValuePair("Link1", new StringElement("panda_link1")));
+    thePanda.addKeyValuePair(new KeyValuePair("Link2", new StringElement("panda_link2")));
+    thePanda.addKeyValuePair(new KeyValuePair("Link3", new StringElement("panda_link3")));
+    thePanda.addKeyValuePair(new KeyValuePair("Link4", new StringElement("panda_link4")));
+    thePanda.addKeyValuePair(new KeyValuePair("Link5", new StringElement("panda_link5")));
+    thePanda.addKeyValuePair(new KeyValuePair("Link6", new StringElement("panda_link6")));
+    thePanda.addKeyValuePair(new KeyValuePair("RightFinger", new StringElement("panda_rightfinger")));
+    thePanda.addKeyValuePair(new KeyValuePair("LeftFinger", new StringElement("panda_leftfinger")));
+    pandaParts.setValue(thePanda);
+    parts.setValue(pandaParts);
+    firstLevel.addKeyValuePair(parts);
+
+    KeyValuePair end_effectors = new KeyValuePair();
+    end_effectors.setKey("end_effectors");
+    KeyValuePair endEffectorParts = new KeyValuePair();
+    endEffectorParts.setKey("panda");
+    endEffectorParts.setValue(new KeyValuePair("EndEffector", new StringElement("panda_hand")));
+    end_effectors.setValue(endEffectorParts);
+    firstLevel.addKeyValuePair(end_effectors);
+
+    KeyValuePair goalPoses = new KeyValuePair();
+    goalPoses.setKey("goal_poses");
+    ListElement theGoalPoses = new ListElement();
+    addPose(theGoalPoses, "0.4 0.4 0.3");
+    addPose(theGoalPoses, "-0.4 0.4 0.3");
+    addPose(theGoalPoses, "-0.4 -0.4 0.3");
+    addPose(theGoalPoses, "0.4 0.4 0.3");
+    addPose(theGoalPoses, "-0.4 0.4 0.3");
+    addPose(theGoalPoses, "0.4 0.4 0.3");
+    goalPoses.setValue(theGoalPoses);
+    firstLevel.addKeyValuePair(goalPoses);
+
+    root.setValue(firstLevel);
+    doc.addComplexElement(root);
+
+    System.out.println(doc.prettyPrint());
   }
 
-  private static MappingDefinitionType makeMappingDefinitionType(String type) {
-    JavaMappingDefinitionType result = new JavaMappingDefinitionType();
-    result.setType(new SimpleJavaTypeUse(type));
-    return result;
+  private static void addPose(ListElement theGoalPoses, String position) {
+    MappingElement goalPose1 = new MappingElement();
+    goalPose1.addKeyValuePair(new KeyValuePair("position", new StringElement(position)));
+    goalPose1.addKeyValuePair(new KeyValuePair("orientation", new StringElement("1 1 0 0")));
+    goalPose1.addKeyValuePair(new KeyValuePair("work", new StringElement("20000")));
+    theGoalPoses.addElement(goalPose1);
   }
 
-  private static Program parseProgram(Path path) {
-    try (BufferedReader reader = Files.newBufferedReader(path)) {
-      RagConnectScanner scanner = new RagConnectScanner(reader);
-      RagConnectParser parser = new RagConnectParser();
-      GrammarFile grammarFile = (GrammarFile) parser.parse(scanner);
-      Program program = new Program();
-      program.addGrammarFile(grammarFile);
-      return program;
-    } catch (IOException | Parser.Exception e) {
-      e.printStackTrace();
-    }
-    return null;
+  public static void main(String[] args) {
+    printManualYAML();
   }
 }
diff --git a/ragconnect.base/src/main/resources/MqttHandler.jadd b/ragconnect.base/src/main/resources/MqttHandler.jadd
index 493d8e4452e81c5dbfa45a522a6366345908cdcc..6f40612520cfb97151600dff53c8e0d384ab441e 100644
--- a/ragconnect.base/src/main/resources/MqttHandler.jadd
+++ b/ragconnect.base/src/main/resources/MqttHandler.jadd
@@ -1,4 +1,73 @@
-aspect MqttHandler {
+import java.io.IOException;
+import java.util.concurrent.TimeUnit;aspect MqttHandler {
+public class MqttServerHandler {
+  private final java.util.Map<String, MqttHandler> handlers = new java.util.HashMap<>();
+  private long time;
+  private java.util.concurrent.TimeUnit unit;
+  private String name;
+
+  public MqttServerHandler() {
+    this("RagConnect");
+  }
+
+  public MqttServerHandler(String name) {
+    this.name = name;
+    setupWaitUntilReady(1, TimeUnit.SECONDS);
+  }
+
+  public void setupWaitUntilReady(long time, java.util.concurrent.TimeUnit unit) {
+    this.time = time;
+    this.unit = unit;
+  }
+
+  public MqttHandler resolveHandler(java.net.URI uri) throws IOException {
+    MqttHandler handler = handlers.get(uri.getHost());
+    if (handler == null) {
+      // first connect to that server
+      handler = new MqttHandler();
+      if (uri.getPort() == -1) {
+        handler.setHost(uri.getHost());
+      } else {
+        handler.setHost(uri.getHost(), uri.getPort());
+      }
+      handlers.put(uri.getHost(), handler);
+    }
+    handler.waitUntilReady(this.time, this.unit);
+    return handler;
+  }
+
+  public boolean newConnection(java.net.URI uri, java.util.function.Consumer<byte[]> callback) throws IOException {
+    return resolveHandler(uri).newConnection(extractTopic(uri), callback);
+  }
+
+  public void publish(java.net.URI uri, byte[] bytes) throws IOException {
+    resolveHandler(uri).publish(extractTopic(uri), bytes);
+  }
+
+  public void publish(java.net.URI uri, byte[] bytes, boolean retain) throws IOException {
+    resolveHandler(uri).publish(extractTopic(uri), bytes, retain);
+  }
+
+  public void publish(java.net.URI uri, byte[] bytes,
+                      org.fusesource.mqtt.client.QoS qos, boolean retain) throws IOException {
+    resolveHandler(uri).publish(extractTopic(uri), bytes, qos, retain);
+  }
+
+  private String extractTopic(java.net.URI uri) {
+    String path = uri.getPath();
+    if (path.charAt(0) == '/') {
+      path = path.substring(1);
+    }
+    return path;
+  }
+
+  public void close() {
+    for (MqttHandler handler : handlers.values()) {
+      handler.close();
+    }
+  }
+
+}
 /**
  * Helper class to receive updates via MQTT and use callbacks to handle those messages.
  *
@@ -14,10 +83,8 @@ public class MqttHandler {
   private java.net.URI host;
   /** The connection to the MQTT broker. */
   private org.fusesource.mqtt.client.CallbackConnection connection;
-  /** Whether we are subscribed to the topics yet */
-  private final java.util.concurrent.locks.Condition readyCondition;
-  private final java.util.concurrent.locks.Lock readyLock;
-  private boolean ready;
+  /** Whether we are connected yet */
+  private final java.util.concurrent.CountDownLatch readyLatch;
   private boolean sendWelcomeMessage = true;
   private org.fusesource.mqtt.client.QoS qos;
   /** Dispatch knowledge */
@@ -31,9 +98,7 @@ public class MqttHandler {
     this.name = java.util.Objects.requireNonNull(name, "Name must be set");
     this.logger = org.apache.logging.log4j.LogManager.getLogger(MqttHandler.class);
     this.callbacks = new java.util.HashMap<>();
-    this.readyLock = new java.util.concurrent.locks.ReentrantLock();
-    this.readyCondition = readyLock.newCondition();
-    this.ready = false;
+    this.readyLatch = new java.util.concurrent.CountDownLatch(1);
     this.qos = org.fusesource.mqtt.client.QoS.AT_LEAST_ONCE;
   }
 
@@ -83,7 +148,7 @@ public class MqttHandler {
         String topicString = topic.toString();
         java.util.List<java.util.function.Consumer<byte[]>> callbackList = callbacks.get(topicString);
         if (callbackList == null || callbackList.isEmpty()) {
-          logger.debug("Got a message, but no callback to call. Forgot to unsubscribe?");
+          logger.debug("Got a message, but no callback to call. Forgot to subscribe?");
         } else {
           byte[] message = body.toByteArray();
 //          System.out.println("message = " + Arrays.toString(message));
@@ -144,13 +209,7 @@ public class MqttHandler {
   }
 
   private void setReady() {
-    try {
-      readyLock.lock();
-      ready = true;
-      readyCondition.signalAll();
-    } finally {
-      readyLock.unlock();
-    }
+    readyLatch.countDown();
   }
 
   private void throwIf(java.util.concurrent.atomic.AtomicReference<Throwable> error) throws java.io.IOException {
@@ -163,12 +222,15 @@ public class MqttHandler {
     this.qos = qos;
   }
 
-  public void newConnection(String topic, java.util.function.Consumer<byte[]> callback) {
-    if (!ready) {
-      // should maybe be something more kind than throwing an exception here
-      throw new IllegalStateException("Updater not ready");
+  public boolean newConnection(String topic, java.util.function.Consumer<byte[]> callback) {
+    if (readyLatch.getCount() > 0) {
+      System.err.println("Handler not ready");
+      return false;
+//      // should maybe be something more kind than throwing an exception here
+//      throw new IllegalStateException("Updater not ready");
     }
     // register callback
+    logger.debug("new connection for {}", topic);
     if (callbacks.get(topic) == null) {
       callbacks.put(topic, new java.util.ArrayList<>());
 
@@ -189,6 +251,7 @@ public class MqttHandler {
       });
     }
     callbacks.get(topic).add(callback);
+    return true;
   }
 
   /**
@@ -202,15 +265,9 @@ public class MqttHandler {
    */
   public boolean waitUntilReady(long time, java.util.concurrent.TimeUnit unit) {
     try {
-      readyLock.lock();
-      if (ready) {
-        return true;
-      }
-      return readyCondition.await(time, unit);
+      return readyLatch.await(time, unit);
     } catch (InterruptedException e) {
       e.printStackTrace();
-    } finally {
-      readyLock.unlock();
     }
     return false;
   }
@@ -253,7 +310,7 @@ public class MqttHandler {
 
         @Override
         public void onFailure(Throwable value) {
-          logger.warn("Could not publish on topic '{}'", topic);
+          logger.warn("Could not publish on topic '{}'", topic, value);
         }
       });
     });
diff --git a/ragconnect.base/src/main/resources/RestHandler.jadd b/ragconnect.base/src/main/resources/RestHandler.jadd
new file mode 100644
index 0000000000000000000000000000000000000000..f7b1a83304fe7aaebe97d105514c3f192d052aec
--- /dev/null
+++ b/ragconnect.base/src/main/resources/RestHandler.jadd
@@ -0,0 +1,140 @@
+import java.util.concurrent.TimeUnit;aspect RestHandler {
+public class RestServerHandler {
+  private static final int DEFAULT_PORT = 4567;
+  private final java.util.Map<Integer, RestHandler> handlers = new java.util.HashMap<>();
+  private String name;
+
+  public RestServerHandler() {
+    this("RagConnect");
+  }
+
+  public RestServerHandler(String name) {
+    this.name = name;
+  }
+
+  private RestHandler resolveHandler(java.net.URI uri) {
+    int port = uri.getPort() != -1 ? uri.getPort() : DEFAULT_PORT;
+    RestHandler handler = handlers.get(port);
+    if (handler == null) {
+      // first connect to that server
+      handler = new RestHandler();
+      handler.setPort(port);
+      handlers.put(port, handler);
+    }
+    return handler;
+  }
+
+  public boolean newPUTConnection(java.net.URI uri, java.util.function.Consumer<String> callback) {
+    resolveHandler(uri).newPUTConnection(uri.getPath(), callback);
+    return true;
+  }
+
+  public boolean newGETConnection(java.net.URI uri, SupplierWithException<String> supplier) {
+    resolveHandler(uri).newGETConnection(uri.getPath(), supplier);
+    return true;
+  }
+
+  public void close() {
+    for (RestHandler handler : handlers.values()) {
+      handler.close();
+    }
+  }
+}
+/**
+ * Helper class to receive updates and publishes information via REST.
+ * @author rschoene - Initial contribution
+ */
+public class RestHandler {
+  private static final int DEFAULT_PORT = 4567;
+
+  private final org.apache.logging.log4j.Logger logger;
+  private final String name;
+  private int port;
+  private final java.util.concurrent.CountDownLatch exitCondition;
+  /** Dispatch knowledge */
+  private final java.util.Map<String, java.util.List<java.util.function.Consumer<String>>> callbacks;
+  private final java.util.Map<String, SupplierWithException<String>> suppliers;
+
+  public RestHandler() {
+    this("RagConnect");
+  }
+
+  public RestHandler(String name) {
+    this.logger = org.apache.logging.log4j.LogManager.getLogger(RestHandler.class);
+    this.name = name;
+    this.port = DEFAULT_PORT;
+    this.exitCondition = new java.util.concurrent.CountDownLatch(1);
+    this.callbacks = new java.util.HashMap<>();
+    this.suppliers = new java.util.HashMap<>();
+  }
+
+  public RestHandler setPort(int port) {
+    this.port = port;
+    start();
+    return this;
+  }
+
+  public void newPUTConnection(String path, java.util.function.Consumer<String> callback) {
+    if (callbacks.containsKey(path)) {
+      callbacks.get(path).add(callback);
+    } else {
+      // setup path
+      java.util.List<java.util.function.Consumer<String>> callbackList = new java.util.ArrayList<>();
+      callbackList.add(callback);
+      callbacks.put(path, callbackList);
+      spark.Spark.put(path, (request, response) -> {
+        String content = request.body();
+        java.util.Set<String> errors = new java.util.HashSet<>();
+        for (java.util.function.Consumer<String> f : callbackList) {
+          try {
+            f.accept(content);
+          } catch (Exception e) {
+            errors.add(e.getMessage());
+          }
+        }
+        if (errors.isEmpty()) {
+          return "OK";
+        } else {
+          return makeError(response, 500, errors.stream().collect(java.util.stream.Collectors.joining("\n", "The following error(s) happened: [", "]")));
+        }
+      });
+    }
+  }
+
+  public void newGETConnection(String path, SupplierWithException<String> supplier) {
+    if (suppliers.get(path) != null) {
+      logger.warn("Overriding existing supplier for '{}'", path);
+    }
+    suppliers.put(path, supplier);
+    spark.Spark.get(path, (request, response) -> {
+      try {
+        return supplier.get();
+      } catch (Exception e) {
+        return makeError(response, 500, e.getMessage());
+      }
+    });
+  }
+
+  private String makeError(spark.Response response, int statusCode, String message) {
+    response.status(statusCode);
+    return message;
+  }
+
+  private void start() {
+    logger.info("Starting REST server at {}", this.port);
+    spark.Spark.port(this.port);
+    spark.Spark.init();
+    spark.Spark.awaitInitialization();
+  }
+
+  public void close() {
+    spark.Spark.stop();
+    spark.Spark.awaitStop();
+  }
+
+}
+@FunctionalInterface
+public interface SupplierWithException<T> {
+  public T get() throws Exception;
+}
+}
diff --git a/ragconnect.base/src/main/resources/dependencyDefinition.mustache b/ragconnect.base/src/main/resources/dependencyDefinition.mustache
index b8d74ffe8f8aa5aaa8979682e4442215af1974e8..0ab8d55242816a7d3ada0491c8c7db6dcd70ee42 100644
--- a/ragconnect.base/src/main/resources/dependencyDefinition.mustache
+++ b/ragconnect.base/src/main/resources/dependencyDefinition.mustache
@@ -1,3 +1,3 @@
-  public void {{targetParentTypeName}}.{{dependencyMethod}}({{sourceParentTypeName}} source) {
-    add{{internalRelationPrefix}}Source(source);
-  }
+public void {{targetParentTypeName}}.{{dependencyMethod}}({{sourceParentTypeName}} source) {
+  add{{internalRelationPrefix}}Source(source);
+}
diff --git a/ragconnect.base/src/main/resources/handleUri.mustache b/ragconnect.base/src/main/resources/handleUri.mustache
new file mode 100644
index 0000000000000000000000000000000000000000..1a20e7c4df2a4c54ccba4c825fff55ca89a51dec
--- /dev/null
+++ b/ragconnect.base/src/main/resources/handleUri.mustache
@@ -0,0 +1,11 @@
+String scheme,host, path;
+java.net.URI uri;
+try {
+  uri = new java.net.URI({{connectParameterName}});
+  scheme = uri.getScheme();
+  host = uri.getHost();
+  path = uri.getPath();
+} catch (java.net.URISyntaxException e) {
+  System.err.println(e.getMessage());  // Maybe re-throw error?
+  return false;
+}
diff --git a/ragconnect.base/src/main/resources/handler.mustache b/ragconnect.base/src/main/resources/handler.mustache
new file mode 100644
index 0000000000000000000000000000000000000000..e89c451007ff309a77297b9b66fe0e20dece73b8
--- /dev/null
+++ b/ragconnect.base/src/main/resources/handler.mustache
@@ -0,0 +1,7 @@
+aspect RagConnectHandler {
+interface RagConnectHandler<T> {
+  boolean connectReceive(String path, java.util.function.Consumer<T> callback);
+  boolean sendPush(String path, T value);
+  boolean connectSendPull(String path, SupplierWithException<T> supplier);
+}
+}
diff --git a/ragconnect.base/src/main/resources/mappingApplication.mustache b/ragconnect.base/src/main/resources/mappingApplication.mustache
index 95ce2fe29329d87f880497b37697d50c8c0687be..064004b94886c155be883e7b186557c6fa87d38c 100644
--- a/ragconnect.base/src/main/resources/mappingApplication.mustache
+++ b/ragconnect.base/src/main/resources/mappingApplication.mustache
@@ -1,7 +1,7 @@
-{{lastDefinitionToType}} {{resultVarPrefix}}{{lastDefinitionName}};
+{{lastDefinitionToType}} {{lastResult}};
 try {
   {{#InnerMappingDefinitions}}
-  {{^last}}{{ToType}} {{/last}}{{resultVarPrefix}}{{methodName}} = {{methodName}}({{inputVarName}});{{!inputVarName has to be computed beforehand}}
+  {{^last}}{{toType}} {{/last}}{{outputVarName}} = {{methodName}}({{inputVarName}});
   {{/InnerMappingDefinitions}}
 } catch (Exception e) {
   e.printStackTrace();
diff --git a/ragconnect.base/src/main/resources/mappingDefinition.mustache b/ragconnect.base/src/main/resources/mappingDefinition.mustache
index 920c5a610b0ce0cff1687122c97b6e18ce63bf7f..fe26e949b7a8e446bf97947e1632624741a83726 100644
--- a/ragconnect.base/src/main/resources/mappingDefinition.mustache
+++ b/ragconnect.base/src/main/resources/mappingDefinition.mustache
@@ -1,3 +1,3 @@
-  protected static {{toType}} ASTNode.{{methodName}}({{fromType}} {{fromVariableName}}) throws Exception {
-    {{{content}}}{{!maybe print line by line to get better indentation}}
-  }
+protected static {{toType}} ASTNode.{{methodName}}({{fromType}} {{fromVariableName}}) throws Exception {
+  {{{content}}}{{!maybe print line by line to get better indentation}}
+}
diff --git a/ragconnect.base/src/main/resources/mqtt.mustache b/ragconnect.base/src/main/resources/mqtt.mustache
index dbdb85e7954dbf50ae7e150ffc43dd4cbda0fd80..9391d1091c41eee257f9e6a1368297ef743e39ce 100644
--- a/ragconnect.base/src/main/resources/mqtt.mustache
+++ b/ragconnect.base/src/main/resources/mqtt.mustache
@@ -1,23 +1,16 @@
 aspect MQTT {
-  private String {{rootNodeName}}.MqttName() { return "Ros2Rag"; }
-  private MqttHandler {{rootNodeName}}.{{mqttHandlerField}} = new MqttHandler(MqttName());
-  public void {{rootNodeName}}.{{mqttSetHostMethod}}(String host) throws java.io.IOException {
-    {{mqttHandlerField}}.setHost(host);
-  }
-  public void {{rootNodeName}}.{{mqttSetHostMethod}}(String host, int port) throws java.io.IOException {
-    {{mqttHandlerField}}.setHost(host, port);
-  }
-
-  public boolean {{rootNodeName}}.{{mqttWaitUntilReadyMethod}}(long time, java.util.concurrent.TimeUnit unit) {
-    return {{mqttHandlerField}}.waitUntilReady(time, unit);
-  }
+  private String {{rootNodeName}}.MqttName() { return "RagConnectMQTT"; }
+  private MqttServerHandler {{rootNodeName}}.{{mqttHandlerField}} = new MqttServerHandler(MqttName());
 
-  public void {{rootNodeName}}.{{mqttCloseMethod}}() {
-    {{mqttHandlerField}}.close();
+  public void {{rootNodeName}}.{{mqttSetupWaitUntilReadyMethod}}(long time, java.util.concurrent.TimeUnit unit) {
+    {{mqttHandlerField}}.setupWaitUntilReady(time, unit);
   }
 
-  {{#getRootTypeComponents}}
-  {{#first}}inh MqttHandler ASTNode.{{mqttHandlerAttribute}}();{{/first}}
+  {{#RootTypeComponents}}
+  {{#first}}inh MqttServerHandler ASTNode.{{mqttHandlerAttribute}}();{{/first}}
   eq {{rootNodeName}}.get{{name}}().{{mqttHandlerAttribute}}() = {{mqttHandlerField}};
-  {{/getRootTypeComponents}}
+  {{/RootTypeComponents}}
+  {{^RootTypeComponents}}
+  syn MqttServerHandler {{rootNodeName}}.{{mqttHandlerAttribute}}() = {{mqttHandlerField}};
+  {{/RootTypeComponents}}
 }
diff --git a/ragconnect.base/src/main/resources/ragconnect.mustache b/ragconnect.base/src/main/resources/ragconnect.mustache
index 0b455283977cf812a47b5266ee593c6597e97cc9..a39510d73854301efb1f05fe7f9c1fd476916ef7 100644
--- a/ragconnect.base/src/main/resources/ragconnect.mustache
+++ b/ragconnect.base/src/main/resources/ragconnect.mustache
@@ -1,22 +1,27 @@
-{{> mqtt}}
+{{#usesMqtt}}{{> mqtt}}{{/usesMqtt}}
+{{#usesRest}}{{> rest}}{{/usesRest}}
 aspect ROS2RAG {
+  public void {{rootNodeName}}.{{closeMethod}}() {
+    {{#usesMqtt}}{{mqttHandlerField}}.close();{{/usesMqtt}}
+    {{#usesRest}}{{restHandlerField}}.close();{{/usesRest}}
+  }
   {{#ReceiveDefinitions}}
-    {{> receiveDefinition}}
+  {{> receiveDefinition}}
   {{/ReceiveDefinitions}}
 
   {{#SendDefinitions}}
-    {{> sendDefinition}}
+  {{> sendDefinition}}
   {{/SendDefinitions}}
 
   {{#MappingDefinitions}}
-    {{> mappingDefinition}}
+  {{> mappingDefinition}}
   {{/MappingDefinitions}}
 
   {{#DependencyDefinitions}}
-    {{> dependencyDefinition}}
+  {{> dependencyDefinition}}
   {{/DependencyDefinitions}}
 
   {{#TokenComponents}}
-    {{> tokenComponent}}
+  {{> tokenComponent}}
   {{/TokenComponents}}
 }
diff --git a/ragconnect.base/src/main/resources/receiveDefinition.mustache b/ragconnect.base/src/main/resources/receiveDefinition.mustache
index e2df0433fe82b37df2c016887da52a11cfd2692b..1dd0e0ddbfbfd2acf03536ff41a2d740043eb34b 100644
--- a/ragconnect.base/src/main/resources/receiveDefinition.mustache
+++ b/ragconnect.base/src/main/resources/receiveDefinition.mustache
@@ -1,9 +1,23 @@
-  public void {{parentTypeName}}.{{connectMethod}}(String topic) {
-    {{mqttHandlerAttribute}}().newConnection(topic, message -> {
-      {{> mappingApplication}}
-      {{#loggingEnabledForReads}}
-      System.out.println("[Receive] " + topic + " -> {{tokenName}} = " + {{lastResult}});{{!lastResult has to be a new attribute}}
-      {{/loggingEnabledForReads}}
-      set{{tokenName}}({{lastResult}});
+public boolean {{parentTypeName}}.{{connectMethod}}(String {{connectParameterName}}) throws java.io.IOException {
+  {{>handleUri}}
+  java.util.function.Consumer<byte[]> consumer = message -> {
+    {{> mappingApplication}}
+    {{#loggingEnabledForReads}}
+    System.out.println("[Receive] " + {{connectParameterName}} + " -> {{tokenName}} = " + {{lastResult}});
+    {{/loggingEnabledForReads}}
+    set{{tokenName}}({{lastResult}});
+  };
+  switch (scheme) {
+  {{#usesMqtt}}
+    case "mqtt": return {{mqttHandlerAttribute}}().newConnection(uri, consumer);
+  {{/usesMqtt}}
+  {{#usesRest}}
+    case "rest": return {{restHandlerAttribute}}().newPUTConnection(uri, input -> {
+      consumer.accept(input.getBytes());
     });
+  {{/usesRest}}
+    default:
+      System.err.println("Unknown protocol '" + scheme + "'.");
+      return false;
   }
+}
diff --git a/ragconnect.base/src/main/resources/rest.mustache b/ragconnect.base/src/main/resources/rest.mustache
new file mode 100644
index 0000000000000000000000000000000000000000..6bbde2be4d26e519635c85a6f39f9a18514bfb19
--- /dev/null
+++ b/ragconnect.base/src/main/resources/rest.mustache
@@ -0,0 +1,12 @@
+aspect REST {
+  private String {{rootNodeName}}.RestName() { return "RagConnectREST"; }
+  private RestServerHandler {{rootNodeName}}.{{restHandlerField}} = new RestServerHandler(RestName());
+
+  {{#getRootTypeComponents}}
+  {{#first}}inh RestServerHandler ASTNode.{{restHandlerAttribute}}();{{/first}}
+  eq {{rootNodeName}}.get{{name}}().{{restHandlerAttribute}}() = {{restHandlerField}};
+  {{/getRootTypeComponents}}
+  {{^getRootTypeComponents}}
+  syn RestServerHandler {{rootNodeName}}.{{restHandlerAttribute}}() = {{restHandlerField}};
+  {{/getRootTypeComponents}}
+}
diff --git a/ragconnect.base/src/main/resources/sendDefinition.mustache b/ragconnect.base/src/main/resources/sendDefinition.mustache
index 477dbc550132af842e53e8fed2cbbee0c8996296..fac4ecd37967b8047c9101b58fab273527979ab2 100644
--- a/ragconnect.base/src/main/resources/sendDefinition.mustache
+++ b/ragconnect.base/src/main/resources/sendDefinition.mustache
@@ -1,24 +1,51 @@
-  private String {{parentTypeName}}.{{sendTopic}} = null;
-  private byte[] {{parentTypeName}}.{{lastValue}} = null;
+private Runnable {{parentTypeName}}.{{sender}} = null;
+private byte[] {{parentTypeName}}.{{lastValue}} = null;
 
-  public void {{parentTypeName}}.{{connectMethod}}(String topic, boolean writeCurrentValue) {
-    {{sendTopic}} = topic;
-    {{updateMethod}}();
-    if (writeCurrentValue) {
-      {{writeMethod}}();
-    }
+public boolean {{parentTypeName}}.{{connectMethod}}(String {{connectParameterName}}, boolean writeCurrentValue) {
+  {{>handleUri}}
+  switch (scheme) {
+  {{#usesMqtt}}
+    case "mqtt":
+      // MqttHandler handler = {{mqttHandlerAttribute}}().resolveHandler(uri);{{!optimize later}}
+      {{sender}} = () -> {
+        {{#loggingEnabledForWrites}}
+        System.out.println("[Send] {{tokenName}} = " + get{{tokenName}}() + " -> " + {{connectParameterName}});
+        {{/loggingEnabledForWrites}}
+        try {
+          {{mqttHandlerAttribute}}().publish(uri, {{lastValue}});
+        } catch (java.io.IOException e) {
+          e.printStackTrace();
+        }
+      };
+      {{updateMethod}}();
+      if (writeCurrentValue) {
+        {{writeMethod}}();
+      }
+      break;
+  {{/usesMqtt}}
+  {{#usesRest}}
+    case "rest":
+      {{restHandlerAttribute}}().newGETConnection(uri, () -> {
+        {{updateMethod}}();
+        return new String({{lastValue}});
+      });
+      break;
+  {{/usesRest}}
+    default:
+      System.err.println("Unknown protocol '" + scheme + "'.");
+      return false;
   }
+  return true;
+}
 
-  protected boolean {{parentTypeName}}.{{updateMethod}}() {
-    {{tokenResetMethod}}();
-    {{> mappingApplication}}
-    {{lastValue}} = {{lastResult}};
-    return true;
-  }
+protected boolean {{parentTypeName}}.{{updateMethod}}() {
+  {{tokenResetMethod}}();
+  {{> mappingApplication}}
+  {{lastValue}} = {{lastResult}};
+  // normally we would return true here. unless no connect method was called so far to initialize {{sender}} yet
+  return {{sender}} != null;
+}
 
-  protected void {{parentTypeName}}.{{writeMethod}}() {
-    {{#loggingEnabledForWrites}}
-    System.out.println("[Send] {{tokenName}} = " + get{{tokenName}}() + " -> " + {{sendTopic}});
-    {{/loggingEnabledForWrites}}
-    {{mqttHandlerAttribute}}().publish({{sendTopic}}, {{lastValue}});
-  }
+protected void {{parentTypeName}}.{{writeMethod}}() {
+  {{sender}}.run();
+}
diff --git a/ragconnect.base/src/main/resources/tokenComponent.mustache b/ragconnect.base/src/main/resources/tokenComponent.mustache
index e6a136d1c8de08b6dce9104fd46fafa5d91b67b6..e78efb96b846bd7aa814723472032299a70c7de3 100644
--- a/ragconnect.base/src/main/resources/tokenComponent.mustache
+++ b/ragconnect.base/src/main/resources/tokenComponent.mustache
@@ -1,17 +1,19 @@
-  public {{parentTypeName}} {{parentTypeName}}.set{{name}}({{javaType}} value) {
-    set{{internalName}}(value);
-    {{#DependencyDefinitions}}
-    for ({{targetParentTypeName}} target : get{{internalRelationPrefix}}TargetList()) {
-      {{#targetEndpointDefinition}}
-      if (target.{{updateMethod}}()) {
-        target.{{writeMethod}}();
-      }
-      {{/targetEndpointDefinition}}
+public {{parentTypeName}} {{parentTypeName}}.set{{name}}({{javaType}} value) {
+  set{{internalName}}(value);
+  {{#DependencyDefinitions}}
+  for ({{targetParentTypeName}} target : get{{internalRelationPrefix}}TargetList()) {
+    {{#targetEndpointDefinition}}
+    {{!#isPush}}
+    if (target.{{updateMethod}}()) {
+      target.{{writeMethod}}();
     }
-    {{/DependencyDefinitions}}
-    return this;
+    {{!/isPush}}
+    {{/targetEndpointDefinition}}
   }
+  {{/DependencyDefinitions}}
+  return this;
+}
 
-  public {{javaType}} {{parentTypeName}}.get{{name}}() {
-    return get{{internalName}}();
-  }
+public {{javaType}} {{parentTypeName}}.get{{name}}() {
+  return get{{internalName}}();
+}
diff --git a/ragconnect.tests/build.gradle b/ragconnect.tests/build.gradle
index d0a6997e48aac4caa7b4c5c0c249210c7276271d..d638ec5001a8a58505045e37e480ac7c5d08079d 100644
--- a/ragconnect.tests/build.gradle
+++ b/ragconnect.tests/build.gradle
@@ -25,7 +25,16 @@ dependencies {
     testImplementation group: 'org.junit.jupiter', name: 'junit-jupiter-api', version: '5.4.0'
     testRuntimeOnly group: 'org.junit.jupiter', name: 'junit-jupiter-engine', version: '5.4.0'
     testImplementation group: 'org.assertj', name: 'assertj-core', version: '3.12.1'
+
+    // mqtt
     testImplementation group: 'org.fusesource.mqtt-client', name: 'mqtt-client', version: '1.15'
+
+    // rest and client
+    testImplementation group: 'com.sparkjava', name: 'spark-core', version: '2.9.2'
+    testImplementation group: 'org.apache.logging.log4j', name: 'log4j-slf4j-impl', version: '2.11.2'
+    testImplementation group: 'org.glassfish.jersey.core', name: 'jersey-client', version: '2.31'
+    testImplementation group: 'org.glassfish.jersey.inject', name: 'jersey-hk2', version: '2.31'
+
     testImplementation group: 'net.sf.beaver', name: 'beaver-rt', version: '0.9.11'
     api group: 'com.google.protobuf', name: 'protobuf-java', version: '3.0.0'
 }
@@ -58,10 +67,12 @@ relastTest {
     compilerLocation = '../libs/relast.jar'
 }
 
-sourceSets {
-    test {
-        java.srcDir "src/test/java-gen"
-    }
+File genSrc = file("src/test/java-gen");
+sourceSets.test.java.srcDir genSrc
+idea.module.generatedSourceDirs += genSrc
+
+clean {
+    delete 'src/test/02-after-ragconnect/*/', 'src/test/03-after-relast/*/', 'src/test/java-gen/*/'
 }
 
 // --- Test: Example ---
@@ -93,7 +104,7 @@ task compileExampleTest(type: RelastTest) {
             'src/test/02-after-ragconnect/example/RagConnect.jadd'
 }
 
-testClasses.dependsOn compileExampleTest
+compileTestJava.dependsOn compileExampleTest
 compileExampleTest.dependsOn preprocessExampleTest
 
 // --- Test: default-only-read ---
@@ -109,8 +120,7 @@ task preprocessDefaultOnlyReadTest(type: JavaExec, group: 'verification') {
     args '--o=src/test/02-after-ragconnect/defaultOnlyRead',
             'src/test/01-input/defaultOnlyRead/Test.relast',
             'src/test/01-input/defaultOnlyRead/Test.connect',
-            '--rootNode=A',
-            '--verbose'
+            '--rootNode=A'
 }
 
 task compileDefaultOnlyReadTest(type: RelastTest) {
@@ -124,7 +134,7 @@ task compileDefaultOnlyReadTest(type: RelastTest) {
             'src/test/02-after-ragconnect/defaultOnlyRead/RagConnect.jadd'
 }
 
-testClasses.dependsOn compileDefaultOnlyReadTest
+compileTestJava.dependsOn compileDefaultOnlyReadTest
 compileDefaultOnlyReadTest.dependsOn preprocessDefaultOnlyReadTest
 
 // --- Test: default-only-write ---
@@ -140,8 +150,7 @@ task preprocessDefaultOnlyWriteTest(type: JavaExec, group: 'verification') {
     args '--o=src/test/02-after-ragconnect/defaultOnlyWrite',
             'src/test/01-input/defaultOnlyWrite/Test.relast',
             'src/test/01-input/defaultOnlyWrite/Test.connect',
-            '--rootNode=A',
-            '--verbose'
+            '--rootNode=A'
 }
 
 task compileDefaultOnlyWriteTest(type: RelastTest) {
@@ -156,7 +165,7 @@ task compileDefaultOnlyWriteTest(type: RelastTest) {
             'src/test/02-after-ragconnect/defaultOnlyWrite/RagConnect.jadd'
 }
 
-testClasses.dependsOn compileDefaultOnlyWriteTest
+compileTestJava.dependsOn compileDefaultOnlyWriteTest
 compileDefaultOnlyWriteTest.dependsOn preprocessDefaultOnlyWriteTest
 
 // --- Test: read1write2 ---
@@ -188,7 +197,7 @@ task compileRead1Write2Test(type: RelastTest) {
             'src/test/02-after-ragconnect/read1write2/RagConnect.jadd'
 }
 
-testClasses.dependsOn compileRead1Write2Test
+compileTestJava.dependsOn compileRead1Write2Test
 compileRead1Write2Test.dependsOn preprocessRead1Write2Test
 
 // --- Test: read2write1 ---
@@ -204,7 +213,7 @@ task preprocessRead2Write1Test(type: JavaExec, group: 'verification') {
     args '--o=src/test/02-after-ragconnect/read2write1',
             'src/test/01-input/read2write1/Test.relast',
             'src/test/01-input/read2write1/Test.connect',
-            '--rootNode=A', '--verbose',
+            '--rootNode=A',
             '--logReads', '--logWrites'
 }
 
@@ -220,9 +229,40 @@ task compileRead2Write1Test(type: RelastTest) {
             'src/test/02-after-ragconnect/read2write1/RagConnect.jadd'
 }
 
-testClasses.dependsOn compileRead2Write1Test
+compileTestJava.dependsOn compileRead2Write1Test
 compileRead2Write1Test.dependsOn preprocessRead2Write1Test
 
-clean {
-    delete 'src/test/02-after-ragconnect/*/', 'src/test/03-after-relast/*/', 'src/test/java-gen/*/'
+// --- Test: via ---
+task preprocessViaTest(type: JavaExec, group: 'verification') {
+    doFirst {
+        delete 'src/test/02-after-ragconnect/via/Test.relast',
+                'src/test/02-after-ragconnect/via/MqttHandler.java',
+                'src/test/02-after-ragconnect/via/RestHandler.java',
+                'src/test/02-after-ragconnect/via/RagConnect.jadd'
+    }
+
+    classpath = sourceSets.main.runtimeClasspath
+    main = 'org.jastadd.ragconnect.compiler.Compiler'
+    args '--o=src/test/02-after-ragconnect/via',
+            'src/test/01-input/via/Test.relast',
+            'src/test/01-input/via/Test.connect',
+            '--rootNode=A', '--verbose',
+            '--protocols=mqtt,rest',
+            '--logReads', '--logWrites'
+}
+
+task compileViaTest(type: RelastTest) {
+    useJastAddNames = true
+    jastAddList = 'JastAddList'
+    relastFiles 'src/test/02-after-ragconnect/via/Test.relast',
+            'src/test/02-after-ragconnect/via/RagConnect.relast'
+    grammarName = 'src/test/03-after-relast/via/via'
+    packageName = 'via.ast'
+    moreInputFiles 'src/test/01-input/via/Test.jadd',
+            'src/test/02-after-ragconnect/via/MqttHandler.jadd',
+            'src/test/02-after-ragconnect/via/RestHandler.jadd',
+            'src/test/02-after-ragconnect/via/RagConnect.jadd'
 }
+
+compileTestJava.dependsOn compileViaTest
+compileViaTest.dependsOn preprocessViaTest
diff --git a/ragconnect.tests/src/test/01-input/via/Test.connect b/ragconnect.tests/src/test/01-input/via/Test.connect
new file mode 100644
index 0000000000000000000000000000000000000000..e4d2862a651dd6e68f67d2a492b6105a15f0a77c
--- /dev/null
+++ b/ragconnect.tests/src/test/01-input/via/Test.connect
@@ -0,0 +1,33 @@
+receive A.Mqtt2MqttInput using MarkMqttInput ;
+receive A.Rest2RestInput using MarkRestInput ;
+receive A.Mqtt2RestInput using MarkMqttInput ;
+receive A.Rest2MqttInput using MarkRestInput ;
+receive A.Both2BothInput ;
+
+send A.Mqtt2MqttOutput using MarkMqttOutput ;
+send A.Rest2RestOutput using MarkRestOutput ;
+send A.Mqtt2RestOutput using MarkRestOutput ;
+send A.Rest2MqttOutput using MarkMqttOutput ;
+send A.Both2RestOutput using MarkRestOutput ;
+send A.Both2MqttOutput using MarkMqttOutput ;
+
+A.Mqtt2MqttOutput canDependOn A.Mqtt2MqttInput as dependencyMqtt2Mqtt ;
+A.Rest2RestOutput canDependOn A.Rest2RestInput as dependencyRest2Rest ;
+A.Mqtt2RestOutput canDependOn A.Mqtt2RestInput as dependencyMqtt2Rest ;
+A.Rest2MqttOutput canDependOn A.Rest2MqttInput as dependencyRest2Mqtt ;
+A.Both2RestOutput canDependOn A.Both2BothInput as dependencyBoth2Rest ;
+A.Both2MqttOutput canDependOn A.Both2BothInput as dependencyBoth2Mqtt ;
+
+MarkMqttInput maps String s to String {:
+  return "FromMqtt-" + s;
+:}
+MarkRestInput maps String s to String {:
+  return "FromRest-" + s;
+:}
+
+MarkMqttOutput maps String s to String {:
+  return s + "-ToMqtt";
+:}
+MarkRestOutput maps String s to String {:
+  return s + "-ToRest";
+:}
diff --git a/ragconnect.tests/src/test/01-input/via/Test.jadd b/ragconnect.tests/src/test/01-input/via/Test.jadd
new file mode 100644
index 0000000000000000000000000000000000000000..9d25387d26e651fe25a937a56a863311b4b7b69a
--- /dev/null
+++ b/ragconnect.tests/src/test/01-input/via/Test.jadd
@@ -0,0 +1,8 @@
+aspect Computation {
+  syn lazy String A.getMqtt2MqttOutput() = getMqtt2MqttInput() + "-M2M" ;
+  syn lazy String A.getRest2RestOutput() = getRest2RestInput() + "-R2R" ;
+  syn lazy String A.getMqtt2RestOutput() = getMqtt2RestInput() + "-M2R" ;
+  syn lazy String A.getRest2MqttOutput() = getRest2MqttInput() + "-R2M" ;
+  syn lazy String A.getBoth2MqttOutput() = getBoth2BothInput() + "-B2M" ;
+  syn lazy String A.getBoth2RestOutput() = getBoth2BothInput() + "-B2R" ;
+}
diff --git a/ragconnect.tests/src/test/01-input/via/Test.relast b/ragconnect.tests/src/test/01-input/via/Test.relast
new file mode 100644
index 0000000000000000000000000000000000000000..1e707fbbc04b1204381351d0c38e826fcbcc8b80
--- /dev/null
+++ b/ragconnect.tests/src/test/01-input/via/Test.relast
@@ -0,0 +1,5 @@
+A ::= <Mqtt2MqttInput> /<Mqtt2MqttOutput>/
+      <Rest2RestInput> /<Rest2RestOutput>/
+      <Mqtt2RestInput> /<Mqtt2RestOutput>/
+      <Rest2MqttInput> /<Rest2MqttOutput>/
+      <Both2BothInput> /<Both2MqttOutput>/ /<Both2RestOutput>/;
diff --git a/ragconnect.tests/src/test/java/org/jastadd/ros2rag/tests/AbstractMqttTest.java b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/AbstractMqttTest.java
similarity index 95%
rename from ragconnect.tests/src/test/java/org/jastadd/ros2rag/tests/AbstractMqttTest.java
rename to ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/AbstractMqttTest.java
index 7b7051348445d10305afb7618dedb0264adf9a8d..efaa302508d351f8d1fce557b49634527b54ac6e 100644
--- a/ragconnect.tests/src/test/java/org/jastadd/ros2rag/tests/AbstractMqttTest.java
+++ b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/AbstractMqttTest.java
@@ -1,4 +1,4 @@
-package org.jastadd.ros2rag.tests;
+package org.jastadd.ragconnect.tests;
 
 import defaultOnlyRead.ast.MqttHandler;
 import org.junit.jupiter.api.BeforeAll;
diff --git a/ragconnect.tests/src/test/java/org/jastadd/ros2rag/tests/DefaultOnlyReadTest.java b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/DefaultOnlyReadTest.java
similarity index 83%
rename from ragconnect.tests/src/test/java/org/jastadd/ros2rag/tests/DefaultOnlyReadTest.java
rename to ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/DefaultOnlyReadTest.java
index 36795127d7a10763e4aceac43e90acb0680b4c15..3746ceb3eaf8c83671747000e1fbf70ff37b9e96 100644
--- a/ragconnect.tests/src/test/java/org/jastadd/ros2rag/tests/DefaultOnlyReadTest.java
+++ b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/DefaultOnlyReadTest.java
@@ -1,4 +1,4 @@
-package org.jastadd.ros2rag.tests;
+package org.jastadd.ragconnect.tests;
 
 import defaultOnlyRead.ast.A;
 import defaultOnlyRead.ast.BoxedTypes;
@@ -11,6 +11,7 @@ import java.io.IOException;
 import java.nio.ByteBuffer;
 import java.util.concurrent.TimeUnit;
 
+import static org.jastadd.ragconnect.tests.TestUtils.mqttUri;
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 
@@ -49,7 +50,7 @@ public class DefaultOnlyReadTest extends AbstractMqttTest {
       sender.close();
     }
     if (model != null) {
-      model.MqttCloseConnections();
+      model.ragconnectCloseConnections();
     }
   }
 
@@ -62,23 +63,21 @@ public class DefaultOnlyReadTest extends AbstractMqttTest {
   @Test
   public void communicate() throws IOException, InterruptedException {
     createModel();
-
-    model.MqttSetHost(TestUtils.getMqttHost());
-    assertTrue(model.MqttWaitUntilReady(2, TimeUnit.SECONDS));
-
-    integers.connectIntValue(TOPIC_NATIVE_INT);
-    integers.connectShortValue(TOPIC_NATIVE_SHORT);
-    integers.connectLongValue(TOPIC_NATIVE_LONG);
-    floats.connectFloatValue(TOPIC_NATIVE_FLOAT);
-    floats.connectDoubleValue(TOPIC_NATIVE_DOUBLE);
-    chars.connectCharValue(TOPIC_NATIVE_CHAR);
-    chars.connectStringValue(TOPIC_NATIVE_STRING);
-    allBoxed.connectIntValue(TOPIC_BOXED_INTEGER);
-    allBoxed.connectShortValue(TOPIC_BOXED_SHORT);
-    allBoxed.connectLongValue(TOPIC_BOXED_LONG);
-    allBoxed.connectFloatValue(TOPIC_BOXED_FLOAT);
-    allBoxed.connectDoubleValue(TOPIC_BOXED_DOUBLE);
-    allBoxed.connectCharValue(TOPIC_BOXED_CHARACTER);
+    model.ragconnectSetupMqttWaitUntilReady(2, TimeUnit.SECONDS);
+
+    integers.connectIntValue(mqttUri(TOPIC_NATIVE_INT));
+    integers.connectShortValue(mqttUri(TOPIC_NATIVE_SHORT));
+    integers.connectLongValue(mqttUri(TOPIC_NATIVE_LONG));
+    floats.connectFloatValue(mqttUri(TOPIC_NATIVE_FLOAT));
+    floats.connectDoubleValue(mqttUri(TOPIC_NATIVE_DOUBLE));
+    chars.connectCharValue(mqttUri(TOPIC_NATIVE_CHAR));
+    chars.connectStringValue(mqttUri(TOPIC_NATIVE_STRING));
+    allBoxed.connectIntValue(mqttUri(TOPIC_BOXED_INTEGER));
+    allBoxed.connectShortValue(mqttUri(TOPIC_BOXED_SHORT));
+    allBoxed.connectLongValue(mqttUri(TOPIC_BOXED_LONG));
+    allBoxed.connectFloatValue(mqttUri(TOPIC_BOXED_FLOAT));
+    allBoxed.connectDoubleValue(mqttUri(TOPIC_BOXED_DOUBLE));
+    allBoxed.connectCharValue(mqttUri(TOPIC_BOXED_CHARACTER));
 
     sender = new MqttHandler().dontSendWelcomeMessage().setHost(TestUtils.getMqttHost());
     assertTrue(sender.waitUntilReady(2, TimeUnit.SECONDS));
diff --git a/ragconnect.tests/src/test/java/org/jastadd/ros2rag/tests/DefaultOnlyWriteTest.java b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/DefaultOnlyWriteTest.java
similarity index 89%
rename from ragconnect.tests/src/test/java/org/jastadd/ros2rag/tests/DefaultOnlyWriteTest.java
rename to ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/DefaultOnlyWriteTest.java
index df75fa7a2594a46556b4a099354ef7d69859bb4c..80bd95c603da385184927862b42a6f54b1d4f57e 100644
--- a/ragconnect.tests/src/test/java/org/jastadd/ros2rag/tests/DefaultOnlyWriteTest.java
+++ b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/DefaultOnlyWriteTest.java
@@ -1,4 +1,4 @@
-package org.jastadd.ros2rag.tests;
+package org.jastadd.ragconnect.tests;
 
 import defaultOnlyWrite.ast.A;
 import defaultOnlyWrite.ast.BoxedTypesSyn;
@@ -10,6 +10,7 @@ import org.junit.jupiter.api.Test;
 import java.io.IOException;
 import java.util.concurrent.TimeUnit;
 
+import static org.jastadd.ragconnect.tests.TestUtils.mqttUri;
 import static org.junit.jupiter.api.Assertions.*;
 
 /**
@@ -50,7 +51,7 @@ public class DefaultOnlyWriteTest extends AbstractMqttTest {
       receiver.close();
     }
     if (model != null) {
-      model.MqttCloseConnections();
+      model.ragconnectCloseConnections();
     }
   }
 
@@ -129,8 +130,7 @@ public class DefaultOnlyWriteTest extends AbstractMqttTest {
   }
 
   private void setupReceiverAndConnect(boolean writeCurrentValue) throws IOException {
-    model.MqttSetHost(TestUtils.getMqttHost());
-    assertTrue(model.MqttWaitUntilReady(2, TimeUnit.SECONDS));
+    model.ragconnectSetupMqttWaitUntilReady(2, TimeUnit.SECONDS);
 
     receiver = new MqttHandler().dontSendWelcomeMessage().setHost(TestUtils.getMqttHost());
     assertTrue(receiver.waitUntilReady(2, TimeUnit.SECONDS));
@@ -205,19 +205,19 @@ public class DefaultOnlyWriteTest extends AbstractMqttTest {
       data.lastBoxedCharValue = java.nio.ByteBuffer.wrap(bytes).getChar();
     });
 
-    nativeIntegers.connectIntValue(TOPIC_NATIVE_INT, writeCurrentValue);
-    nativeIntegers.connectShortValue(TOPIC_NATIVE_SHORT, writeCurrentValue);
-    nativeIntegers.connectLongValue(TOPIC_NATIVE_LONG, writeCurrentValue);
-    nativeFloats.connectFloatValue(TOPIC_NATIVE_FLOAT, writeCurrentValue);
-    nativeFloats.connectDoubleValue(TOPIC_NATIVE_DOUBLE, writeCurrentValue);
-    nativeChars.connectCharValue(TOPIC_NATIVE_CHAR, writeCurrentValue);
-    nativeChars.connectStringValue(TOPIC_NATIVE_STRING, writeCurrentValue);
-    boxedIntegers.connectIntValue(TOPIC_BOXED_INTEGER, writeCurrentValue);
-    boxedIntegers.connectShortValue(TOPIC_BOXED_SHORT, writeCurrentValue);
-    boxedIntegers.connectLongValue(TOPIC_BOXED_LONG, writeCurrentValue);
-    boxedFloats.connectFloatValue(TOPIC_BOXED_FLOAT, writeCurrentValue);
-    boxedFloats.connectDoubleValue(TOPIC_BOXED_DOUBLE, writeCurrentValue);
-    boxedChars.connectCharValue(TOPIC_BOXED_CHARACTER, writeCurrentValue);
+    nativeIntegers.connectIntValue(mqttUri(TOPIC_NATIVE_INT), writeCurrentValue);
+    nativeIntegers.connectShortValue(mqttUri(TOPIC_NATIVE_SHORT), writeCurrentValue);
+    nativeIntegers.connectLongValue(mqttUri(TOPIC_NATIVE_LONG), writeCurrentValue);
+    nativeFloats.connectFloatValue(mqttUri(TOPIC_NATIVE_FLOAT), writeCurrentValue);
+    nativeFloats.connectDoubleValue(mqttUri(TOPIC_NATIVE_DOUBLE), writeCurrentValue);
+    nativeChars.connectCharValue(mqttUri(TOPIC_NATIVE_CHAR), writeCurrentValue);
+    nativeChars.connectStringValue(mqttUri(TOPIC_NATIVE_STRING), writeCurrentValue);
+    boxedIntegers.connectIntValue(mqttUri(TOPIC_BOXED_INTEGER), writeCurrentValue);
+    boxedIntegers.connectShortValue(mqttUri(TOPIC_BOXED_SHORT), writeCurrentValue);
+    boxedIntegers.connectLongValue(mqttUri(TOPIC_BOXED_LONG), writeCurrentValue);
+    boxedFloats.connectFloatValue(mqttUri(TOPIC_BOXED_FLOAT), writeCurrentValue);
+    boxedFloats.connectDoubleValue(mqttUri(TOPIC_BOXED_DOUBLE), writeCurrentValue);
+    boxedChars.connectCharValue(mqttUri(TOPIC_BOXED_CHARACTER), writeCurrentValue);
   }
 
   private void setData(String integerDriver, String floatDriver, String stringDriver) {
diff --git a/ragconnect.tests/src/test/java/org/jastadd/ros2rag/tests/Errors.java b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/Errors.java
similarity index 82%
rename from ragconnect.tests/src/test/java/org/jastadd/ros2rag/tests/Errors.java
rename to ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/Errors.java
index be45a8080cc251191c62fcbc3b1793129a45dee3..17d853fba71c2ee46c6c6c7b5424958af0a5f8d2 100644
--- a/ragconnect.tests/src/test/java/org/jastadd/ros2rag/tests/Errors.java
+++ b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/Errors.java
@@ -1,4 +1,4 @@
-package org.jastadd.ros2rag.tests;
+package org.jastadd.ragconnect.tests;
 
 import org.apache.logging.log4j.LogManager;
 import org.apache.logging.log4j.Logger;
@@ -15,8 +15,8 @@ import java.util.Collections;
 import java.util.List;
 import java.util.stream.Collectors;
 
-import static org.jastadd.ros2rag.tests.TestUtils.exec;
-import static org.jastadd.ros2rag.tests.TestUtils.readFile;
+import static org.jastadd.ragconnect.tests.TestUtils.exec;
+import static org.jastadd.ragconnect.tests.TestUtils.readFile;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 
 class Errors {
@@ -24,7 +24,7 @@ class Errors {
   private static final Logger logger = LogManager.getLogger(Errors.class);
   private static final String FILENAME_PATTERN = "$FILENAME";
   private static final String INPUT_DIRECTORY = "./src/test/01-input/errors/";
-  private static final String OUTPUT_DIRECTORY = "./src/test/02-after-ros2rag/errors/";
+  private static final String OUTPUT_DIRECTORY = "./src/test/02-after-ragconnect/errors/";
 
   @BeforeAll
   public static void createOutputDirectory() {
@@ -40,21 +40,21 @@ class Errors {
   @SuppressWarnings("SameParameterValue")
   private void test(String name, String rootNode) throws IOException {
     String grammarFile = INPUT_DIRECTORY + name + ".relast";
-    String ros2ragFile = INPUT_DIRECTORY + name + ".ros2rag";
+    String ragconnectFile = INPUT_DIRECTORY + name + ".connect";
     String outFile = OUTPUT_DIRECTORY + name + ".out";
     String expectedFile = INPUT_DIRECTORY + name + ".expected";
 
     try {
       logger.debug("user.dir: {}", System.getProperty("user.dir"));
       String[] args = {
-          "--outputDir=" + OUTPUT_DIRECTORY,
-          "--inputGrammar=" + grammarFile,
-          "--inputRos2Rag=" + ros2ragFile,
+          "--o=" + OUTPUT_DIRECTORY,
+          grammarFile,
+          ragconnectFile,
           "--rootNode=" + rootNode,
           "--verbose"
       };
       int returnValue = exec(Compiler.class, args, new File(outFile));
-      Assertions.assertEquals(1, returnValue, "Ros2Rag did not return with value 1");
+      Assertions.assertEquals(1, returnValue, "RagConnect did not return with value 1");
     } catch (IOException | InterruptedException e) {
       e.printStackTrace();
     }
@@ -78,7 +78,7 @@ class Errors {
     // FIXME errors not handled correctly at the moment
 //    Assertions.assertLinesMatch(expectedList, outList);
 
-    logger.info("ros2rag for " + name + " returned:\n{}", out);
+    logger.info("ragconnect for " + name + " returned:\n{}", out);
   }
 
 }
diff --git a/ragconnect.tests/src/test/java/org/jastadd/ros2rag/tests/ExampleTest.java b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/ExampleTest.java
similarity index 96%
rename from ragconnect.tests/src/test/java/org/jastadd/ros2rag/tests/ExampleTest.java
rename to ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/ExampleTest.java
index 9b1ebceb541bddd4cafc3648b10c940af79bc952..3d0706c075d8e4af3b3c5e5fa703f3885b80ddf3 100644
--- a/ragconnect.tests/src/test/java/org/jastadd/ros2rag/tests/ExampleTest.java
+++ b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/ExampleTest.java
@@ -1,4 +1,4 @@
-package org.jastadd.ros2rag.tests;
+package org.jastadd.ragconnect.tests;
 
 import com.google.protobuf.InvalidProtocolBufferException;
 import config.Config.RobotConfig;
@@ -11,6 +11,7 @@ import robot.RobotStateOuterClass.RobotState;
 import java.io.IOException;
 import java.util.concurrent.TimeUnit;
 
+import static org.jastadd.ragconnect.tests.TestUtils.mqttUri;
 import static org.junit.jupiter.api.Assertions.*;
 
 /**
@@ -42,7 +43,7 @@ public class ExampleTest extends AbstractMqttTest {
       handler.close();
     }
     if (model != null) {
-      model.MqttCloseConnections();
+      model.ragconnectCloseConnections();
     }
   }
 
@@ -210,8 +211,7 @@ public class ExampleTest extends AbstractMqttTest {
   }
 
   private void setupReceiverAndConnect(boolean writeCurrentValue) throws IOException {
-    model.MqttSetHost(TestUtils.getMqttHost());
-    assertTrue(model.MqttWaitUntilReady(2, TimeUnit.SECONDS));
+    model.ragconnectSetupMqttWaitUntilReady(2, TimeUnit.SECONDS);
 
     handler = new MqttHandler().dontSendWelcomeMessage().setHost(TestUtils.getMqttHost());
     assertTrue(handler.waitUntilReady(2, TimeUnit.SECONDS));
@@ -233,9 +233,9 @@ public class ExampleTest extends AbstractMqttTest {
       }
     });
 
-    robotArm.connectAppropriateSpeed(TOPIC_CONFIG, writeCurrentValue);
-    link1.connectCurrentPosition(TOPIC_JOINT1);
-    link2.connectCurrentPosition(TOPIC_JOINT2);
+    robotArm.connectAppropriateSpeed(mqttUri(TOPIC_CONFIG), writeCurrentValue);
+    link1.connectCurrentPosition(mqttUri(TOPIC_JOINT1));
+    link2.connectCurrentPosition(mqttUri(TOPIC_JOINT2));
   }
 
   private void createModel() {
diff --git a/ragconnect.tests/src/test/java/org/jastadd/ros2rag/tests/Read1Write2Test.java b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/Read1Write2Test.java
similarity index 90%
rename from ragconnect.tests/src/test/java/org/jastadd/ros2rag/tests/Read1Write2Test.java
rename to ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/Read1Write2Test.java
index 4e21608a9cee5d8a2c3b18f64ef198760d982874..5ede1e1896afadc70da7b868cf4272eafa250514 100644
--- a/ragconnect.tests/src/test/java/org/jastadd/ros2rag/tests/Read1Write2Test.java
+++ b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/Read1Write2Test.java
@@ -1,4 +1,4 @@
-package org.jastadd.ros2rag.tests;
+package org.jastadd.ragconnect.tests;
 
 import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.Test;
@@ -7,6 +7,7 @@ import read1write2.ast.*;
 import java.io.IOException;
 import java.util.concurrent.TimeUnit;
 
+import static org.jastadd.ragconnect.tests.TestUtils.mqttUri;
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 
@@ -44,7 +45,7 @@ public class Read1Write2Test extends AbstractMqttTest {
       handler.close();
     }
     if (model != null) {
-      model.MqttCloseConnections();
+      model.ragconnectCloseConnections();
     }
   }
 
@@ -137,8 +138,7 @@ public class Read1Write2Test extends AbstractMqttTest {
   }
 
   private void setupReceiverAndConnect(boolean writeCurrentValue) throws IOException {
-    model.MqttSetHost(TestUtils.getMqttHost());
-    assertTrue(model.MqttWaitUntilReady(2, TimeUnit.SECONDS));
+    model.ragconnectSetupMqttWaitUntilReady(2, TimeUnit.SECONDS);
 
     handler = new MqttHandler().dontSendWelcomeMessage().setHost(TestUtils.getMqttHost());
     assertTrue(handler.waitUntilReady(2, TimeUnit.SECONDS));
@@ -181,15 +181,15 @@ public class Read1Write2Test extends AbstractMqttTest {
       dataOther2.lastStringValue = new String(bytes);
     });
 
-    onSameNonterminal.connectInput(TOPIC_SAME_READ);
-    onSameNonterminal.connectOutInteger(TOPIC_SAME_WRITE_INT, writeCurrentValue);
-    onSameNonterminal.connectOutString(TOPIC_SAME_WRITE_STRING, writeCurrentValue);
+    onSameNonterminal.connectInput(mqttUri(TOPIC_SAME_READ));
+    onSameNonterminal.connectOutInteger(mqttUri(TOPIC_SAME_WRITE_INT), writeCurrentValue);
+    onSameNonterminal.connectOutString(mqttUri(TOPIC_SAME_WRITE_STRING), writeCurrentValue);
 
-    onDifferentNonterminal.connectInput(TOPIC_DIFFERENT_READ);
-    other1.connectOutInteger(TOPIC_DIFFERENT_WRITE1_INT, writeCurrentValue);
-    other1.connectOutString(TOPIC_DIFFERENT_WRITE1_STRING, writeCurrentValue);
-    other2.connectOutInteger(TOPIC_DIFFERENT_WRITE2_INT, writeCurrentValue);
-    other2.connectOutString(TOPIC_DIFFERENT_WRITE2_STRING, writeCurrentValue);
+    onDifferentNonterminal.connectInput(mqttUri(TOPIC_DIFFERENT_READ));
+    other1.connectOutInteger(mqttUri(TOPIC_DIFFERENT_WRITE1_INT), writeCurrentValue);
+    other1.connectOutString(mqttUri(TOPIC_DIFFERENT_WRITE1_STRING), writeCurrentValue);
+    other2.connectOutInteger(mqttUri(TOPIC_DIFFERENT_WRITE2_INT), writeCurrentValue);
+    other2.connectOutString(mqttUri(TOPIC_DIFFERENT_WRITE2_STRING), writeCurrentValue);
   }
 
   private void sendData(String inputSame, String inputDifferent) {
diff --git a/ragconnect.tests/src/test/java/org/jastadd/ros2rag/tests/Read2Write1Test.java b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/Read2Write1Test.java
similarity index 90%
rename from ragconnect.tests/src/test/java/org/jastadd/ros2rag/tests/Read2Write1Test.java
rename to ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/Read2Write1Test.java
index a7cf6c7822aa9b8f77cad90551bf2bca05e149a3..9881eb7110f98a3bc36301f2f7a163c417e9a54e 100644
--- a/ragconnect.tests/src/test/java/org/jastadd/ros2rag/tests/Read2Write1Test.java
+++ b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/Read2Write1Test.java
@@ -1,4 +1,4 @@
-package org.jastadd.ros2rag.tests;
+package org.jastadd.ragconnect.tests;
 
 import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.Test;
@@ -7,6 +7,7 @@ import read2write1.ast.*;
 import java.io.IOException;
 import java.util.concurrent.TimeUnit;
 
+import static org.jastadd.ragconnect.tests.TestUtils.mqttUri;
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 
@@ -43,7 +44,7 @@ public class Read2Write1Test extends AbstractMqttTest {
       handler.close();
     }
     if (model != null) {
-      model.MqttCloseConnections();
+      model.ragconnectCloseConnections();
     }
   }
 
@@ -146,8 +147,7 @@ public class Read2Write1Test extends AbstractMqttTest {
   }
 
   private void setupReceiverAndConnect(boolean writeCurrentValue) throws IOException {
-    model.MqttSetHost(TestUtils.getMqttHost());
-    assertTrue(model.MqttWaitUntilReady(2, TimeUnit.SECONDS));
+    model.ragconnectSetupMqttWaitUntilReady(2, TimeUnit.SECONDS);
 
     handler = new MqttHandler().dontSendWelcomeMessage().setHost(TestUtils.getMqttHost());
     assertTrue(handler.waitUntilReady(2, TimeUnit.SECONDS));
@@ -178,14 +178,14 @@ public class Read2Write1Test extends AbstractMqttTest {
       dataOther2.lastIntValue = java.nio.ByteBuffer.wrap(bytes).getInt();
     });
 
-    onSameNonterminal.connectInput1(TOPIC_SAME_READ1);
-    onSameNonterminal.connectInput2(TOPIC_SAME_READ2);
-    onSameNonterminal.connectOutInteger(TOPIC_SAME_WRITE_INT, writeCurrentValue);
+    onSameNonterminal.connectInput1(mqttUri(TOPIC_SAME_READ1));
+    onSameNonterminal.connectInput2(mqttUri(TOPIC_SAME_READ2));
+    onSameNonterminal.connectOutInteger(mqttUri(TOPIC_SAME_WRITE_INT), writeCurrentValue);
 
-    onDifferentNonterminal.connectInput1(TOPIC_DIFFERENT_READ1);
-    onDifferentNonterminal.connectInput2(TOPIC_DIFFERENT_READ2);
-    other1.connectOutInteger(TOPIC_DIFFERENT_WRITE1_INT, writeCurrentValue);
-    other2.connectOutInteger(TOPIC_DIFFERENT_WRITE2_INT, writeCurrentValue);
+    onDifferentNonterminal.connectInput1(mqttUri(TOPIC_DIFFERENT_READ1));
+    onDifferentNonterminal.connectInput2(mqttUri(TOPIC_DIFFERENT_READ2));
+    other1.connectOutInteger(mqttUri(TOPIC_DIFFERENT_WRITE1_INT), writeCurrentValue);
+    other2.connectOutInteger(mqttUri(TOPIC_DIFFERENT_WRITE2_INT), writeCurrentValue);
   }
 
   private void sendData(boolean useSameInput1, String inputSame,
diff --git a/ragconnect.tests/src/test/java/org/jastadd/ros2rag/tests/TestUtils.java b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/TestUtils.java
similarity index 83%
rename from ragconnect.tests/src/test/java/org/jastadd/ros2rag/tests/TestUtils.java
rename to ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/TestUtils.java
index 05db73e8790a25dd9d730ea637e8be500d568d5f..26c32c5a438bf133d984efa31bde4b05b3b15af1 100644
--- a/ragconnect.tests/src/test/java/org/jastadd/ros2rag/tests/TestUtils.java
+++ b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/TestUtils.java
@@ -1,7 +1,11 @@
-package org.jastadd.ros2rag.tests;
+package org.jastadd.ragconnect.tests;
+
+import org.junit.jupiter.api.Assertions;
 
 import java.io.File;
 import java.io.IOException;
+import java.net.URI;
+import java.net.URISyntaxException;
 import java.nio.charset.Charset;
 import java.nio.file.Files;
 import java.nio.file.Paths;
@@ -26,6 +30,14 @@ public class TestUtils {
     }
   }
 
+  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 int getMqttDefaultPort() {
     return 1883;
   }
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
new file mode 100644
index 0000000000000000000000000000000000000000..896b8711fee14bce4dcf8a12006ddb9e26a5791c
--- /dev/null
+++ b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/ViaTest.java
@@ -0,0 +1,333 @@
+package org.jastadd.ragconnect.tests;
+
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Tag;
+import org.junit.jupiter.api.Test;
+import via.ast.A;
+import via.ast.MqttHandler;
+
+import javax.ws.rs.client.Client;
+import javax.ws.rs.client.ClientBuilder;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.client.WebTarget;
+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.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+/**
+ * Test case "via".
+ *
+ * @author rschoene - Initial contribution
+ */
+@Tag("rest")
+public class ViaTest extends AbstractMqttTest {
+
+  private static final int REST_PORT = 9002;
+
+  private static final String TOPIC_MQTT_2_MQTT_RECEIVE = "mqtt2mqtt/receive";
+  private static final String PATH_REST_2_REST_RECEIVE = "rest2rest/receive";
+  private static final String TOPIC_MQTT_2_REST_RECEIVE = "mqtt2rest/receive";
+  private static final String PATH_REST_2_MQTT_RECEIVE = "rest2mqtt/receive";
+  private static final String TOPIC_BOTH_MQTT_RECEIVE = "both/send";
+  private static final String PATH_BOTH_REST_RECEIVE = "both/send";
+
+  private static final String TOPIC_MQTT_2_MQTT_SEND = "mqtt2mqtt/send";
+  private static final String PATH_REST_2_REST_SEND = "rest2rest/send";
+  private static final String PATH_MQTT_2_REST_SEND = "mqtt2rest/send";
+  private static final String TOPIC_REST_2_MQTT_SEND = "rest2mqtt/send";
+  private static final String TOPIC_BOTH_2_MQTT_SEND = "both2mqtt/send";
+  private static final String PATH_BOTH_2_REST_SEND = "both2rest/send";
+
+  private static final String REST_SERVER_BASE_URL = "http://localhost:" + REST_PORT + "/";
+
+  private MqttHandler handler;
+  private A model;
+  private ReceiverData dataMqtt2Mqtt;
+  private ReceiverData dataRest2Mqtt;
+  private WebTarget dataRest2Rest;
+  private WebTarget dataMqtt2Rest;
+  private ReceiverData dataBoth2Mqtt;
+  private WebTarget dataBoth2Rest;
+
+  private WebTarget senderRest2Rest;
+  private WebTarget senderRest2Mqtt;
+  private WebTarget senderBoth2Rest;
+
+  @AfterEach
+  public void closeConnections() {
+    if (handler != null) {
+      handler.close();
+    }
+    if (model != null) {
+      model.ragconnectCloseConnections();
+    }
+  }
+
+  @Test
+  public void buildModel() {
+    createModel();
+  }
+
+  @Test
+  public void communicateSendInitialValue() throws IOException, InterruptedException {
+    createModel();
+    setupReceiverAndConnect(true);
+
+    // check initial value
+    TestUtils.waitForMqtt();
+    checkData(1, "100-M2M-ToMqtt",
+        "200-R2R-ToRest",
+        "300-M2R-ToRest",
+        1, "400-R2M-ToMqtt",
+        1, "500-B2M-ToMqtt",
+        "500-B2R-ToRest");
+
+    sendData("101", "201", "301", "401");
+    sendDataForBoth("501", true);
+
+    // check new value
+    TestUtils.waitForMqtt();
+    checkData(2, "FromMqtt-101-M2M-ToMqtt",
+        "FromRest-201-R2R-ToRest",
+        "FromMqtt-301-M2R-ToRest",
+        2, "FromRest-401-R2M-ToMqtt",
+        2, "501-B2M-ToMqtt",
+        "501-B2R-ToRest");
+
+    // send value only for bothInput via REST
+    sendDataForBoth("502", false);
+
+    // check this value
+    TestUtils.waitForMqtt();
+    checkData(2, "FromMqtt-101-M2M-ToMqtt",
+        "FromRest-201-R2R-ToRest",
+        "FromMqtt-301-M2R-ToRest",
+        2, "FromRest-401-R2M-ToMqtt",
+        3, "502-B2M-ToMqtt",
+        "502-B2R-ToRest");
+
+    // send same value only for bothInput via MQTT
+    sendDataForBoth("502", true);
+
+    // check this value
+    TestUtils.waitForMqtt();
+    checkData(2, "FromMqtt-101-M2M-ToMqtt",
+        "FromRest-201-R2R-ToRest",
+        "FromMqtt-301-M2R-ToRest",
+        2, "FromRest-401-R2M-ToMqtt",
+        3, "502-B2M-ToMqtt",
+        "502-B2R-ToRest");
+
+    // send values for other things
+    sendData("102", "202", "302", "402");
+
+    // check this value
+    TestUtils.waitForMqtt();
+    checkData(3, "FromMqtt-102-M2M-ToMqtt",
+        "FromRest-202-R2R-ToRest",
+        "FromMqtt-302-M2R-ToRest",
+        3, "FromRest-402-R2M-ToMqtt",
+        3, "502-B2M-ToMqtt",
+        "502-B2R-ToRest");
+
+    // send same values again for other things
+    sendData("102", "202", "302", "402");
+
+    // check this value
+    TestUtils.waitForMqtt();
+    checkData(3, "FromMqtt-102-M2M-ToMqtt",
+        "FromRest-202-R2R-ToRest",
+        "FromMqtt-302-M2R-ToRest",
+        3, "FromRest-402-R2M-ToMqtt",
+        3, "502-B2M-ToMqtt",
+        "502-B2R-ToRest");
+  }
+
+  @Test
+  public void communicateOnlyUpdatedValue() throws IOException, InterruptedException {
+    createModel();
+    setupReceiverAndConnect(false);
+
+    // check initial value
+    TestUtils.waitForMqtt();
+    checkData(0, null,
+        "200-R2R-ToRest",
+        "300-M2R-ToRest",
+        0, null,
+        0, null,
+        "500-B2R-ToRest");
+
+    sendData("111", "211", "311", "411");
+    sendDataForBoth("511", true);
+
+    // check new value
+    TestUtils.waitForMqtt();
+    checkData(1, "FromMqtt-111-M2M-ToMqtt",
+        "FromRest-211-R2R-ToRest",
+        "FromMqtt-311-M2R-ToRest",
+        1, "FromRest-411-R2M-ToMqtt",
+        1, "511-B2M-ToMqtt",
+        "511-B2R-ToRest");
+
+    // send value only for bothInput via REST
+    sendDataForBoth("512", false);
+
+    // check this value
+    TestUtils.waitForMqtt();
+    checkData(1, "FromMqtt-111-M2M-ToMqtt",
+        "FromRest-211-R2R-ToRest",
+        "FromMqtt-311-M2R-ToRest",
+        1, "FromRest-411-R2M-ToMqtt",
+        2, "512-B2M-ToMqtt",
+        "512-B2R-ToRest");
+
+    // send same value only for bothInput via MQTT
+    sendDataForBoth("512", true);
+
+    // check this value
+    TestUtils.waitForMqtt();
+    checkData(1, "FromMqtt-111-M2M-ToMqtt",
+        "FromRest-211-R2R-ToRest",
+        "FromMqtt-311-M2R-ToRest",
+        1, "FromRest-411-R2M-ToMqtt",
+        2, "512-B2M-ToMqtt",
+        "512-B2R-ToRest");
+
+    // send values for other things
+    sendData("112", "212", "312", "412");
+
+    // check this value
+    TestUtils.waitForMqtt();
+    checkData(2, "FromMqtt-112-M2M-ToMqtt",
+        "FromRest-212-R2R-ToRest",
+        "FromMqtt-312-M2R-ToRest",
+        2, "FromRest-412-R2M-ToMqtt",
+        2, "512-B2M-ToMqtt",
+        "512-B2R-ToRest");
+
+    // send same values again for other things
+    sendData("112", "212", "312", "412");
+
+    // check this value
+    TestUtils.waitForMqtt();
+    checkData(2, "FromMqtt-112-M2M-ToMqtt",
+        "FromRest-212-R2R-ToRest",
+        "FromMqtt-312-M2R-ToRest",
+        2, "FromRest-412-R2M-ToMqtt",
+        2, "512-B2M-ToMqtt",
+        "512-B2R-ToRest");
+  }
+
+  private void sendData(String inputMqtt2Mqtt, String inputRest2Rest, String inputMqtt2Rest, String inputRest2Mqtt) {
+    handler.publish(TOPIC_MQTT_2_MQTT_RECEIVE, inputMqtt2Mqtt.getBytes());
+    senderRest2Rest.request().put(Entity.entity(inputRest2Rest, MediaType.TEXT_PLAIN_TYPE));
+    handler.publish(TOPIC_MQTT_2_REST_RECEIVE, inputMqtt2Rest.getBytes());
+    senderRest2Mqtt.request().put(Entity.entity(inputRest2Mqtt, MediaType.TEXT_PLAIN_TYPE));
+  }
+
+  private void sendDataForBoth(String input, boolean useMqtt) {
+    if (useMqtt) {
+      handler.publish(TOPIC_BOTH_MQTT_RECEIVE, input.getBytes());
+    } else {
+      senderBoth2Rest.request().put(Entity.entity(input, MediaType.TEXT_PLAIN_TYPE));
+    }
+  }
+
+  private void checkData(int numberOfMqtt2MqttValues, String mqtt2MqttValue, String rest2RestValue, String mqtt2RestValue, int numberOfRest2MqttValues, String rest2MqttValue, int numberOfBoth2MqttValues, String both2MqttValue, String both2RestValue) {
+    dataMqtt2Mqtt.assertEqualData(numberOfMqtt2MqttValues, mqtt2MqttValue);
+    dataRest2Mqtt.assertEqualData(numberOfRest2MqttValues, rest2MqttValue);
+    dataBoth2Mqtt.assertEqualData(numberOfBoth2MqttValues, both2MqttValue);
+    assertEquals(rest2RestValue, readRest2Rest());
+    assertEquals(mqtt2RestValue, readMqtt2Rest());
+    assertEquals(both2RestValue, readBoth2Rest());
+  }
+
+  private String readRest2Rest() {
+    return dataRest2Rest.request().get().readEntity(String.class);
+  }
+
+  private String readMqtt2Rest() {
+    return dataMqtt2Rest.request().get().readEntity(String.class);
+  }
+
+  private String readBoth2Rest() {
+    return dataBoth2Rest.request().get().readEntity(String.class);
+  }
+
+  private void createModel() {
+    // Setting value for Input without dependencies does not trigger any updates
+    model = new A();
+    model.setMqtt2MqttInput("100");
+    model.setRest2RestInput("200");
+    model.setMqtt2RestInput("300");
+    model.setRest2MqttInput("400");
+    model.setBoth2BothInput("500");
+  }
+
+  private void setupReceiverAndConnect(boolean writeCurrentValue) throws IOException {
+    model.ragconnectSetupMqttWaitUntilReady(2, TimeUnit.SECONDS);
+
+    handler = new MqttHandler().dontSendWelcomeMessage().setHost(TestUtils.getMqttHost());
+    assertTrue(handler.waitUntilReady(2, TimeUnit.SECONDS));
+
+    model.addDependencyMqtt2Mqtt(model);
+    model.addDependencyRest2Rest(model);
+    model.addDependencyMqtt2Rest(model);
+    model.addDependencyRest2Mqtt(model);
+    model.addDependencyBoth2Mqtt(model);
+    model.addDependencyBoth2Rest(model);
+
+    dataMqtt2Mqtt = new ReceiverData();
+    dataRest2Mqtt = new ReceiverData();
+    dataBoth2Mqtt = new ReceiverData();
+
+    handler.newConnection(TOPIC_MQTT_2_MQTT_SEND, bytes -> {
+      dataMqtt2Mqtt.numberOfStringValues += 1;
+      dataMqtt2Mqtt.lastStringValue = new String(bytes);
+    });
+    handler.newConnection(TOPIC_REST_2_MQTT_SEND, bytes -> {
+      dataRest2Mqtt.numberOfStringValues += 1;
+      dataRest2Mqtt.lastStringValue = new String(bytes);
+    });
+    handler.newConnection(TOPIC_BOTH_2_MQTT_SEND, bytes -> {
+      dataBoth2Mqtt.numberOfStringValues += 1;
+      dataBoth2Mqtt.lastStringValue = new String(bytes);
+    });
+
+    Client client = ClientBuilder.newClient();
+    dataRest2Rest = client.target(REST_SERVER_BASE_URL + PATH_REST_2_REST_SEND);
+    dataMqtt2Rest = client.target(REST_SERVER_BASE_URL + PATH_MQTT_2_REST_SEND);
+    dataBoth2Rest = client.target(REST_SERVER_BASE_URL + PATH_BOTH_2_REST_SEND);
+    senderRest2Rest = client.target(REST_SERVER_BASE_URL + PATH_REST_2_REST_RECEIVE);
+    senderRest2Mqtt = client.target(REST_SERVER_BASE_URL + PATH_REST_2_MQTT_RECEIVE);
+    senderBoth2Rest = client.target(REST_SERVER_BASE_URL + PATH_BOTH_REST_RECEIVE);
+
+    model.connectMqtt2MqttInput(mqttUri(TOPIC_MQTT_2_MQTT_RECEIVE));
+    model.connectMqtt2MqttOutput(mqttUri(TOPIC_MQTT_2_MQTT_SEND), writeCurrentValue);
+    model.connectMqtt2RestInput(mqttUri(TOPIC_MQTT_2_REST_RECEIVE));
+    model.connectMqtt2RestOutput(restUri(PATH_MQTT_2_REST_SEND, REST_PORT), writeCurrentValue);
+    model.connectRest2MqttInput(restUri(PATH_REST_2_MQTT_RECEIVE, REST_PORT));
+    model.connectRest2MqttOutput(mqttUri(TOPIC_REST_2_MQTT_SEND), writeCurrentValue);
+    model.connectRest2RestInput(restUri(PATH_REST_2_REST_RECEIVE, REST_PORT));
+    model.connectRest2RestOutput(restUri(PATH_REST_2_REST_SEND, REST_PORT), writeCurrentValue);
+    model.connectBoth2BothInput(mqttUri(TOPIC_BOTH_MQTT_RECEIVE));
+    model.connectBoth2BothInput(restUri(PATH_BOTH_REST_RECEIVE, REST_PORT));
+    model.connectBoth2MqttOutput(mqttUri(TOPIC_BOTH_2_MQTT_SEND), writeCurrentValue);
+    model.connectBoth2RestOutput(restUri(PATH_BOTH_2_REST_SEND, REST_PORT), writeCurrentValue);
+  }
+
+  private static class ReceiverData {
+    String lastStringValue;
+    int numberOfStringValues = 0;
+
+    public void assertEqualData(int expectedNumberOfValues, String expectedLastValue) {
+      assertEquals(expectedNumberOfValues, this.numberOfStringValues);
+      assertEquals(expectedLastValue, this.lastStringValue);
+    }
+  }
+}
diff --git a/ragconnect.tests/src/test/resources/log4j2.xml b/ragconnect.tests/src/test/resources/log4j2.xml
new file mode 100644
index 0000000000000000000000000000000000000000..4c0d4548c61b23abad6aabc6811e68cd8a928871
--- /dev/null
+++ b/ragconnect.tests/src/test/resources/log4j2.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Configuration status="INFO">
+    <Appenders>
+        <Console name="Console" target="SYSTEM_OUT">
+            <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
+        </Console>
+    </Appenders>
+    <Loggers>
+        <Root level="debug">
+            <AppenderRef ref="Console"/>
+        </Root>
+        <Logger name="org.eclipse.jetty" level="info" additivity="false">
+            <AppenderRef ref="Console"/>
+        </Logger>
+    </Loggers>
+</Configuration>
diff --git a/ros2rag.common/src/main/java/de/tudresden/inf/st/ros2rag/common/Util.java b/ros2rag.common/src/main/java/de/tudresden/inf/st/ros2rag/common/Util.java
index 7c6d930aefa358a24b53be7349e1d667fc681380..5ba5e348651a11206b1a01ed919235eaff9d40f8 100644
--- a/ros2rag.common/src/main/java/de/tudresden/inf/st/ros2rag/common/Util.java
+++ b/ros2rag.common/src/main/java/de/tudresden/inf/st/ros2rag/common/Util.java
@@ -9,6 +9,7 @@ import de.tudresden.inf.st.ros2rag.common.DataConfiguration.ActualConfiguration;
 
 import java.io.File;
 import java.io.IOException;
+import java.net.URI;
 import java.util.Map;
 import java.util.SortedMap;
 
@@ -24,7 +25,11 @@ public class Util {
     ObjectMapper mapper = new ObjectMapper(
         new YAMLFactory().configure(JsonParser.Feature.ALLOW_YAML_COMMENTS, true)
     );
-    return mapper.readValue(configFile, DataConfiguration.class).panda_mqtt_connector;
+    ActualConfiguration config = mapper.readValue(configFile, DataConfiguration.class).panda_mqtt_connector;
+    URI serverUri = URI.create(config.server);
+    config.server = serverUri.getHost() + ":" + (serverUri.getPort() == -1 ? 1883 : serverUri.getPort())
+        + serverUri.getPath();
+    return config;
   }
 
   public static void setMqttHost(SetHost handler, ActualConfiguration config) throws IOException {
@@ -32,7 +37,11 @@ public class Util {
     handler.setHost(hostAndPort.host, hostAndPort.port);
   }
 
-  public static void iterateLinks(HandleLink callback, ActualConfiguration config) {
+  public static String mqttUri(String topic, ActualConfiguration config) {
+    return "mqtt://" + config.server + "/" + topic;
+  }
+
+  public static void iterateLinks(HandleLink callback, ActualConfiguration config) throws IOException {
     for (Map.Entry<String, SortedMap<String, String>> dataRobot : config.parts.entrySet()) {
       String topicPrefix = dataRobot.getKey() + "/";
       for (Map.Entry<String, String> dataLink : dataRobot.getValue().entrySet()) {
@@ -69,6 +78,6 @@ public class Util {
 
   @FunctionalInterface
   public interface HandleLink {
-    void handle(boolean isEndEffector, String topic, String name);
+    void handle(boolean isEndEffector, String topic, String name) throws IOException;
   }
 }
diff --git a/ros2rag.goal/build.gradle b/ros2rag.goal/build.gradle
index 0c537c2ba4f8d5a5f92fc572249ddcb0afa94896..3bfe45ab6c9aef96b069ff56faf8c47597f11acf 100644
--- a/ros2rag.goal/build.gradle
+++ b/ros2rag.goal/build.gradle
@@ -18,7 +18,9 @@ configurations {
     baseRuntimeClasspath
 }
 
-sourceSets.main.java.srcDir "src/gen/java"
+File genSrc = file("src/gen/java");
+sourceSets.main.java.srcDir genSrc
+idea.module.generatedSourceDirs += genSrc
 
 dependencies {
     implementation project (':ragconnect.base')
diff --git a/ros2rag.goal/src/main/java/de/tudresden/inf/st/ros2rag/goal/GoalMain.java b/ros2rag.goal/src/main/java/de/tudresden/inf/st/ros2rag/goal/GoalMain.java
index fc04dc9aeda6cc79222f1042a2d583abeeb86c6d..5dad7c9a5ef62f291fd67967cb067b2597d6239a 100644
--- a/ros2rag.goal/src/main/java/de/tudresden/inf/st/ros2rag/goal/GoalMain.java
+++ b/ros2rag.goal/src/main/java/de/tudresden/inf/st/ros2rag/goal/GoalMain.java
@@ -33,7 +33,6 @@ public class GoalMain {
     ActualConfiguration config = Util.parseConfig(configFile);
 
     model = new GoalModel();
-    Util.setMqttHost(model::MqttSetHost, config);
 
     for (DataWorkPose dataWorkPose : config.goal_poses) {
       WorkPose workPose = new WorkPose();
@@ -60,7 +59,7 @@ public class GoalMain {
     robotState.setLastUpdate(0);
     model.setRobotState(robotState);
 
-    model.MqttWaitUntilReady(2, TimeUnit.SECONDS);
+    model.ragconnectSetupMqttWaitUntilReady(2, TimeUnit.SECONDS);
 
     logger.debug("Setting dependencies");
     /*
@@ -82,15 +81,15 @@ public class GoalMain {
      */
     Util.iterateLinks((isEndEffector, topic, name) -> {
       if (isEndEffector) {
-        robotState.connectCurrentPosition(topic);
-        robotState.connectLastUpdate(topic);
+        robotState.connectCurrentPosition(Util.mqttUri(topic, config));
+        robotState.connectLastUpdate(Util.mqttUri(topic, config));
       }
     }, config);
     // next position is not initialized, so don't send it
-    model.getWorkflow().connectNextTrajectory(config.topics.trajectory, false);
-    model.getWorkflow().connectCurrentStep(config.topics.nextStep);
+    model.getWorkflow().connectNextTrajectory(Util.mqttUri(config.topics.trajectory, config), false);
+    model.getWorkflow().connectCurrentStep(Util.mqttUri(config.topics.nextStep, config));
     // initial next step is sent, as soon as this is received, the workflow starts
-    model.getWorkflow().connectReadyForThisStep(config.topics.nextStep, true);
+    model.getWorkflow().connectReadyForThisStep(Util.mqttUri(config.topics.nextStep, config), true);
 
     logStatus("Start");
     CountDownLatch exitCondition = new CountDownLatch(1);
@@ -140,7 +139,7 @@ public class GoalMain {
   private void close() {
     logger.info("Exiting ...");
     mainHandler.close();
-    model.MqttCloseConnections();
+    model.ragconnectCloseConnections();
   }
 
   public static void main(String[] args) throws IOException, InterruptedException {
diff --git a/ros2rag.safety/build.gradle b/ros2rag.safety/build.gradle
index 30d6296bdc98ad7a87f3dedbb0739fe6074120d2..4bfec22f282ed5700cd4bf05edfd4791b0f4515a 100644
--- a/ros2rag.safety/build.gradle
+++ b/ros2rag.safety/build.gradle
@@ -18,7 +18,10 @@ configurations {
     baseRuntimeClasspath
 }
 
-sourceSets.main.java.srcDir "src/gen/java"
+
+File genSrc = file("src/gen/java");
+sourceSets.main.java.srcDir genSrc
+idea.module.generatedSourceDirs += genSrc
 
 dependencies {
     implementation project (':ragconnect.base')
diff --git a/ros2rag.safety/src/main/java/de/tudresden/inf/st/ros2rag/starter/StarterMain.java b/ros2rag.safety/src/main/java/de/tudresden/inf/st/ros2rag/starter/StarterMain.java
index 7627ac005d67c1332323f95866b5f55703cfb532..bf34044f5f194c7a20e600ecd33b02da5116449c 100644
--- a/ros2rag.safety/src/main/java/de/tudresden/inf/st/ros2rag/starter/StarterMain.java
+++ b/ros2rag.safety/src/main/java/de/tudresden/inf/st/ros2rag/starter/StarterMain.java
@@ -29,7 +29,6 @@ public class StarterMain {
 
     ActualConfiguration config = Util.parseConfig(configFile);
     model = new Model();
-    Util.setMqttHost(model::MqttSetHost, config);
 
     ZoneModel zoneModel = new ZoneModel();
 
@@ -45,7 +44,7 @@ public class StarterMain {
     }
     zoneModel.addSafetyZone(safetyZone);
     model.setZoneModel(zoneModel);
-    model.MqttWaitUntilReady(2, TimeUnit.SECONDS);
+    model.ragconnectSetupMqttWaitUntilReady(2, TimeUnit.SECONDS);
 
     RobotArm robotArm = new RobotArm();
     model.setRobotArm(robotArm);
@@ -63,10 +62,10 @@ public class StarterMain {
       link.setName(name);
       link.setCurrentPosition(IntPosition.of(0, 0, 0));
       link.containingRobotArm().addDependency1(link);
-      link.connectCurrentPosition(topic);
+      link.connectCurrentPosition(Util.mqttUri(topic, config));
     }, config);
 
-    robotArm.connectAppropriateSpeed(config.topics.robotConfig, true);
+    robotArm.connectAppropriateSpeed(Util.mqttUri(config.topics.robotConfig, config), true);
 
     logStatus("Start", robotArm);
     CountDownLatch exitCondition = new CountDownLatch(1);
@@ -102,7 +101,7 @@ public class StarterMain {
   private void close() {
     logger.info("Exiting ...");
     mainHandler.close();
-    model.MqttCloseConnections();
+    model.ragconnectCloseConnections();
   }
 
   public static void main(String[] args) throws IOException, InterruptedException {