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/src/main/jastadd/Analysis.jrag b/ragconnect.base/src/main/jastadd/Analysis.jrag
index 623a3f0ad8c0457d8d0606bf70dea0adfea89ea4..30159bc3149d419953be27e9a0801faa46acdde3 100644
--- a/ragconnect.base/src/main/jastadd/Analysis.jrag
+++ b/ragconnect.base/src/main/jastadd/Analysis.jrag
@@ -1,13 +1,14 @@
 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) {
+    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 +23,70 @@ aspect Analysis {
   }
 
   // --- isAlreadyDefined ---
-  syn boolean TokenEndpointDefinition.isAlreadyDefined() = lookupTokenEndpointDefinition(getToken()) != this;
+  syn boolean TokenEndpointDefinition.isAlreadyDefined() {
+    java.util.List<TokenEndpointDefinition> definitions = lookupTokenEndpointDefinitions(getToken());
+    java.util.Set<String> protocols = definitions
+      .stream()
+      .map(TokenEndpointDefinition::protocol)
+      .collect(java.util.stream.Collectors.toSet());
+    return definitions.size() > protocols.size();
+  }
   syn boolean DependencyDefinition.isAlreadyDefined() = lookupDependencyDefinition(getSource().containingTypeDecl(), getID()) != this;
+
+  // --- protocol ---
+  syn String TokenEndpointDefinition.protocol();
+  eq ReceiveFromMqttDefinition.protocol() = "mqtt";
+  eq SendToMqttDefinition.protocol() = "mqtt";
+  eq ReceiveFromRestDefinition.protocol() = "rest";
+  eq SendToRestDefinition.protocol() = "rest";
+
+  // --- usesPROTOCOL ---
+  syn boolean RagConnect.usesMqtt() = !mqttEndpointDefinitions().isEmpty();
+  syn boolean RagConnect.usesRest() = !restEndpointDefinitions().isEmpty();
+
+  // --- mqttEndpointDefinitions ---
+  coll java.util.List<TokenEndpointDefinition> RagConnect.mqttEndpointDefinitions() [new java.util.ArrayList<>()] root RagConnect;
+  ReceiveFromMqttDefinition contributes this
+    to RagConnect.mqttEndpointDefinitions()
+    for ragconnect();
+  SendToMqttDefinition contributes this
+    to RagConnect.mqttEndpointDefinitions()
+    for ragconnect();
+
+  // --- restEndpointDefinitions ---
+  coll java.util.List<TokenEndpointDefinition> RagConnect.restEndpointDefinitions() [new java.util.ArrayList<>()] root RagConnect;
+  ReceiveFromRestDefinition contributes this
+    to RagConnect.restEndpointDefinitions()
+    for ragconnect();
+  SendToRestDefinition contributes this
+    to RagConnect.restEndpointDefinitions()
+    for ragconnect();
+
+  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 cf001ac9d3b64181b58e9b1c461cba6e316d1370..fe2fcd59efd365174ca2803affebc34819cf3d7e 100644
--- a/ragconnect.base/src/main/jastadd/Errors.jrag
+++ b/ragconnect.base/src/main/jastadd/Errors.jrag
@@ -23,9 +23,9 @@ aspect Errors {
       when effectiveMappings().get(0) == null
       to RagConnect.errors();
 
-    ReceiveTokenEndpointDefinition contributes error("to-type of last mapping (" + effectiveMappings().get(effectiveMappings().size() - 1).getToType().prettyPrint() + ") does not match type of the Token (" + getToken().effectiveJavaTypeUse().prettyPrint() + ")!")
-      when !getToken().effectiveJavaTypeUse().prettyPrint().equals(
-          effectiveMappings().get(effectiveMappings().size() - 1).getToType().prettyPrint())
+    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();
 
     SendTokenEndpointDefinition contributes error("Sending target token must be an NTA token!")
diff --git a/ragconnect.base/src/main/jastadd/MustacheNodes.relast b/ragconnect.base/src/main/jastadd/MustacheNodes.relast
index 1a35853e84701475c11632e7b31a31458b8de729..73d1b318e03c68d2bd243264b3d9863548657e02 100644
--- a/ragconnect.base/src/main/jastadd/MustacheNodes.relast
+++ b/ragconnect.base/src/main/jastadd/MustacheNodes.relast
@@ -1,7 +1,7 @@
 //TypeComponentMustache ;
 //rel TypeComponentMustache.TypeComponent -> TypeComponent ;
 
-MRagConnect ::= ReceiveDefinition:MReceiveDefinition* SendDefinition:MSendDefinition* MappingDefinition:MMappingDefinition* DependencyDefinition:MDependencyDefinition* RootTypeComponent:MTypeComponent* TokenComponent:MTokenComponent*;
+MRagConnect ::= ReceiveDefinition:MReceiveDefinition* PushSendDefinition:MSendDefinition* PullSendDefinition:MSendDefinition* MappingDefinition:MMappingDefinition* DependencyDefinition:MDependencyDefinition* RootTypeComponent:MTypeComponent* TokenComponent:MTokenComponent*;
 abstract MEndpointDefinition ::= InnerMappingDefinition:MInnerMappingDefinition*;
 MReceiveDefinition : MEndpointDefinition;
 MSendDefinition : MEndpointDefinition;
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/backend/Generation.jadd b/ragconnect.base/src/main/jastadd/backend/Generation.jadd
index 512750e268fea52e0c0d46284e55fb3d66054ec1..35d1fcd6c65b37e0f6344df233310a371fbabb60 100644
--- a/ragconnect.base/src/main/jastadd/backend/Generation.jadd
+++ b/ragconnect.base/src/main/jastadd/backend/Generation.jadd
@@ -31,13 +31,18 @@ aspect AttributesForMustache {
   eq MRagConnect.getChild().mqttHandlerField() = mqttHandlerField();
   eq MRagConnect.getRootTypeComponent(int i).isFirst() = i == 0;
 
+  syn boolean MRagConnect.usesMqtt() = getRagConnect().usesMqtt();
   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 boolean MRagConnect.usesRest() = getRagConnect().usesRest();
   syn String MRagConnect.restHandlerAttribute() = getRagConnect().restHandlerAttribute();
+  syn String MRagConnect.restHandlerField() = getRagConnect().restHandlerField();
+  syn String MRagConnect.restSetPortMethod() = getRagConnect().restSetPortMethod();
+  syn String MRagConnect.restCloseMethod() = getRagConnect().restCloseMethod();
 
   // --- MEndpointDefinition ---
   syn String MEndpointDefinition.preemptiveExpectedValue();
@@ -51,6 +56,8 @@ aspect AttributesForMustache {
 
   inh String MEndpointDefinition.mqttHandlerAttribute();
 
+  syn String MEndpointDefinition.connectParameterName() = endpointDef().connectParameterName();
+  syn String MEndpointDefinition.newConnectionMethod() = endpointDef().newConnectionMethod();
   syn String MEndpointDefinition.handlerAttribute() = endpointDef().handlerAttribute();
   syn String MEndpointDefinition.connectMethod() = endpointDef().connectMethod();
   syn TokenComponent MEndpointDefinition.token() = endpointDef().getToken();
@@ -88,9 +95,12 @@ aspect AttributesForMustache {
   eq MReceiveDefinition.endpointDef() = getReceiveTokenEndpointDefinition();
   eq MReceiveDefinition.firstInputVarName() = "message";
 
+  syn String MReceiveDefinition.newConnectionMethod() = getReceiveTokenEndpointDefinition().newConnectionMethod();
+  syn String MReceiveDefinition.connectParameterName() = getReceiveTokenEndpointDefinition().connectParameterName();
+
   // --- MSendDefinition ---
   eq MSendDefinition.preemptiveExpectedValue() = lastValue();
-  eq MSendDefinition.preemptiveReturn() = "return false;";
+  eq MSendDefinition.preemptiveReturn() = getSendTokenEndpointDefinition().preemptiveReturn();
   eq MSendDefinition.endpointDef() = getSendTokenEndpointDefinition();
   eq MSendDefinition.firstInputVarName() = "get" + tokenName() + "()";
 
@@ -99,6 +109,7 @@ aspect AttributesForMustache {
   syn String MSendDefinition.updateMethod() = getSendTokenEndpointDefinition().updateMethod();
   syn String MSendDefinition.writeMethod() = getSendTokenEndpointDefinition().writeMethod();
   syn String MSendDefinition.tokenResetMethod() = getSendTokenEndpointDefinition().tokenResetMethod();
+  syn boolean MSendDefinition.isPush() = getSendTokenEndpointDefinition().isPush();
 
   // --- MMappingDefinition ---
   syn String MMappingDefinition.toType() = getMappingDefinition().getToType().prettyPrint();
@@ -112,7 +123,7 @@ aspect AttributesForMustache {
   syn String MDependencyDefinition.dependencyMethod() = getDependencyDefinition().dependencyMethod();
   syn String MDependencyDefinition.sourceParentTypeName() = getDependencyDefinition().getSource().containingTypeDecl().getName();
   syn String MDependencyDefinition.internalRelationPrefix() = getDependencyDefinition().internalRelationPrefix();
-  syn nta MEndpointDefinition MDependencyDefinition.targetEndpointDefinition() {
+  syn nta MSendDefinition MDependencyDefinition.targetEndpointDefinition() {
     return getDependencyDefinition().targetEndpointDefinition().toMustache();
   }
 
@@ -134,7 +145,12 @@ aspect AttributesForMustache {
     result.setRagConnect(this);
     for (EndpointDefinition def : getEndpointDefinitionList()) {
       if (def.isSendTokenEndpointDefinition()) {
-        result.addSendDefinition(def.asSendTokenEndpointDefinition().toMustache());
+        SendTokenEndpointDefinition sendDef = def.asSendTokenEndpointDefinition();
+        if (sendDef.isPush()) {
+          result.addPushSendDefinition(sendDef.toMustache());
+        } else {
+          result.addPullSendDefinition(sendDef.toMustache());
+        }
       } else {
         result.addReceiveDefinition(def.asReceiveTokenEndpointDefinition().toMustache());
       }
@@ -166,34 +182,18 @@ aspect AttributesForMustache {
       addInnerMappingDefinition(inner);
     }
   }
-  syn lazy MReceiveDefinition ReceiveTokenEndpointDefinition.toMustache();
-  eq ReceiveFromMqttDefinition.toMustache() {
+  syn lazy MReceiveDefinition ReceiveTokenEndpointDefinition.toMustache() {
     MReceiveDefinition result = new MReceiveDefinition();
     result.setReceiveTokenEndpointDefinition(this);
     result.addInnerMappings();
     return result;
   }
-  eq ReceiveFromRestDefinition.toMustache() {
-    MReceiveDefinition result = new MReceiveDefinition();
-    System.err.println("REST not implemented!");
-    result.setReceiveTokenEndpointDefinition(this);
-    result.addInnerMappings();
-    return result;
-  }
-  syn lazy MSendDefinition SendTokenEndpointDefinition.toMustache();
-  eq SendToMqttDefinition.toMustache() {
+  syn lazy MSendDefinition SendTokenEndpointDefinition.toMustache() {
     MSendDefinition result = new MSendDefinition();
     result.setSendTokenEndpointDefinition(this);
     result.addInnerMappings();
     return result;
   }
-  eq SendToRestDefinition.toMustache() {
-    MSendDefinition result = new MSendDefinition();
-    System.err.println("REST not implemented!");
-    result.setSendTokenEndpointDefinition(this);
-    result.addInnerMappings();
-    return result;
-  }
   syn lazy MMappingDefinition MappingDefinition.toMustache() {
     MMappingDefinition result = new MMappingDefinition();
     result.setMappingDefinition(this);
@@ -224,7 +224,7 @@ aspect AspectGeneration {
   syn String TokenComponent.internalName() = getDependencySourceDefinitionList().isEmpty() ? externalName() : "_internal_" + getName();
   syn String TokenComponent.externalName() = getName();
 
-  syn String TokenEndpointDefinition.connectMethod() = "connect" + getToken().getName();
+  syn String TokenEndpointDefinition.connectMethod() = "connect" + getToken().getName() + (lookupTokenEndpointDefinitions(getToken()).size() > 1 ? "Via" + capitalize(protocol()) : "");
   syn String SendTokenEndpointDefinition.sendTopic() = "_topic_" + getToken().getName();
   syn String SendTokenEndpointDefinition.lastValue() = "_lastValue" + getToken().getName();
   syn String SendTokenEndpointDefinition.updateMethod() = "_update_" + getToken().getName();
@@ -237,6 +237,7 @@ aspect AspectGeneration {
   syn String DependencyDefinition.internalRelationPrefix() = "_internal_" + getID();
   syn String DependencyDefinition.internalTokenName() = getSource().internalName();
 
+  // -- MQTT names --
   syn String RagConnect.mqttHandlerAttribute() = "_mqttHandler";
   syn String RagConnect.mqttHandlerField() = "_mqttHandler";
 
@@ -244,15 +245,40 @@ aspect AspectGeneration {
   syn String RagConnect.mqttWaitUntilReadyMethod() = "MqttWaitUntilReady";
   syn String RagConnect.mqttCloseMethod() = "MqttCloseConnections";
 
+  // -- REST names --
+  syn String RagConnect.restSetPortMethod() = "RestSetPort";
+  syn String RagConnect.restCloseMethod() = "RestCloseConnections";
+
   syn String RagConnect.restHandlerAttribute() = "_restHandler";
   syn String RagConnect.restHandlerField() = "_restHandler";
 
+  // -- endpoint names --
   syn String TokenEndpointDefinition.handlerAttribute();
   eq ReceiveFromMqttDefinition.handlerAttribute() = mqttHandlerAttribute();
   eq SendToMqttDefinition.handlerAttribute() = mqttHandlerAttribute();
   eq ReceiveFromRestDefinition.handlerAttribute() = restHandlerAttribute();
   eq SendToRestDefinition.handlerAttribute() = restHandlerAttribute();
 
+  syn String TokenEndpointDefinition.newConnectionMethod();
+  eq ReceiveFromMqttDefinition.newConnectionMethod() = "newConnection";
+  eq SendToMqttDefinition.newConnectionMethod() = null;
+  eq ReceiveFromRestDefinition.newConnectionMethod() = "newPUTConnection";
+  eq SendToRestDefinition.newConnectionMethod() = "newGETConnection";
+
+  syn String TokenEndpointDefinition.connectParameterName();
+  eq ReceiveFromMqttDefinition.connectParameterName() = "topic";
+  eq SendToMqttDefinition.connectParameterName() = "topic";
+  eq ReceiveFromRestDefinition.connectParameterName() = "path";
+  eq SendToRestDefinition.connectParameterName() = "path";
+
+  syn String SendTokenEndpointDefinition.preemptiveReturn();
+  eq SendToMqttDefinition.preemptiveReturn() = "return false;";
+  eq SendToRestDefinition.preemptiveReturn() = "throw e;";  // e is Exception variable
+
+  syn boolean SendTokenEndpointDefinition.isPush();
+  eq SendToMqttDefinition.isPush() = true;
+  eq SendToRestDefinition.isPush() = false;
+
   // naming copy attributes
   // --- mqttHandlerAttribute ---
   inh String EndpointDefinition.mqttHandlerAttribute();
diff --git a/ragconnect.base/src/main/jastadd/backend/Mappings.jrag b/ragconnect.base/src/main/jastadd/backend/Mappings.jrag
index d5f79046be4271f93a954061c28978aa19ba63b9..0410cc289d9e69fc27b343ac162cdd6b60bcb9d5 100644
--- a/ragconnect.base/src/main/jastadd/backend/Mappings.jrag
+++ b/ragconnect.base/src/main/jastadd/backend/Mappings.jrag
@@ -1,25 +1,7 @@
 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;
-  }
-
   private String RagConnect.baseDefaultMappingTypeNamePart(String typeName) {
-    return Character.toUpperCase(typeName.charAt(0)) + typeName.substring(1).replace("[]", "s");
+    return capitalize(typeName).replace("[]", "s");
   }
 
   private MappingDefinitionType RagConnect.baseDefaultMappingTypeFromName(String typeName) {
@@ -38,103 +20,61 @@ aspect DefaultMappings {
     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.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.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;
-  }
-
-  syn nta DefaultMappingDefinition RagConnect.defaultBytesToCharMapping() {
-    DefaultMappingDefinition result = baseDefaultMappingDefinitionFromBytes("char");
-    result.setContent("return java.nio.ByteBuffer.wrap(bytes).getChar();");
-    return result;
-  }
-
-  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();");
-    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.defaultLongToBytesMapping() {
-    DefaultMappingDefinition result = baseDefaultMappingDefinitionToBytes("long");
-    result.setContent("return java.nio.ByteBuffer.allocate(8).putLong(input).array();");
-    return result;
-  }
-
-  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.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.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.defaultStringToIntMapping() = baseDefaultMappingDefinition("String", "int", "return Integer.parseInteger(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.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.defaultIntToStringMapping() = baseDefaultMappingDefinition("int", "String", "return String.valueOf(input);");
-  syn nta DefaultMappingDefinition RagConnect.defaultShortToStringMapping() = baseDefaultMappingDefinition("int", "String", "return String.valueOf(input);");
-  syn nta DefaultMappingDefinition RagConnect.defaultLongToStringMapping() = baseDefaultMappingDefinition("int", "String", "return String.valueOf(input);");
-  syn nta DefaultMappingDefinition RagConnect.defaultFloatToStringMapping() = baseDefaultMappingDefinition("int", "String", "return String.valueOf(input);");
-  syn nta DefaultMappingDefinition RagConnect.defaultDoubleToStringMapping() = baseDefaultMappingDefinition("int", "String", "return String.valueOf(input);");
-  syn nta DefaultMappingDefinition RagConnect.defaultCharToStringMapping() = baseDefaultMappingDefinition("int", "String", "return String.valueOf(input);");
+  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 {
@@ -300,7 +240,7 @@ aspect Mappings {
   syn java.util.List<MappingDefinition> RagConnect.allMappingDefinitions() {
     java.util.List<MappingDefinition> result = new java.util.ArrayList<>();
     getMappingDefinitionList().iterator().forEachRemaining(result::add);
-    // byte[] converstion
+    // byte[] conversion
     result.add(defaultBytesToIntMapping());
     result.add(defaultBytesToShortMapping());
     result.add(defaultBytesToLongMapping());
@@ -315,7 +255,7 @@ aspect Mappings {
     result.add(defaultDoubleToBytesMapping());
     result.add(defaultCharToBytesMapping());
     result.add(defaultStringToBytesMapping());
-    // string converstion
+    // string conversion
     result.add(defaultStringToIntMapping());
     result.add(defaultStringToShortMapping());
     result.add(defaultStringToLongMapping());
diff --git a/ragconnect.base/src/main/jastadd/parser/RagConnect.parser b/ragconnect.base/src/main/jastadd/parser/RagConnect.parser
index 0f897afc1992544d7736ccee4d884b934e7f0227..56a8dc4cc7f3396e778a529e981c1aefb302e18b 100644
--- a/ragconnect.base/src/main/jastadd/parser/RagConnect.parser
+++ b/ragconnect.base/src/main/jastadd/parser/RagConnect.parser
@@ -10,6 +10,10 @@ 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
@@ -29,10 +33,10 @@ EndpointDefinition endpoint_definition
 EndpointDefinition endpoint_definition_type
   = RECEIVE token_ref           {: return new ReceiveFromMqttDefinition().setToken(token_ref); :}
   | RECEIVE token_ref VIA MQTT  {: return new ReceiveFromMqttDefinition().setToken(token_ref); :}
-  | RECEIVE token_ref VIA REST  {: return new ReceiveFromRestDefinition().setToken(token_ref); :}
+  | RECEIVE token_ref VIA REST  {: return enableAlwaysApply(new ReceiveFromRestDefinition()).setToken(token_ref); :}
   | SEND token_ref              {: return new SendToMqttDefinition().setToken(token_ref); :}
   | SEND token_ref VIA MQTT     {: return new SendToMqttDefinition().setToken(token_ref); :}
-  | SEND token_ref VIA REST     {: return new SendToRestDefinition().setToken(token_ref); :}
+  | SEND token_ref VIA REST     {: return enableAlwaysApply(new SendToRestDefinition()).setToken(token_ref); :}
 ;
 
 TokenComponent token_ref
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..5160ab6611f8a8f177555e55e511153b95a9659b 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,9 +14,7 @@ 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 {
 
@@ -62,18 +60,26 @@ public class Compiler extends AbstractCompiler {
     }
 
     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 (ragConnect.usesMqtt()) {
+      handlers.add("MqttHandler.jadd");
+    }
+    if (ragConnect.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());
@@ -90,7 +96,7 @@ public class Compiler extends AbstractCompiler {
       new Compiler().run(args);
     } catch (CompilerException e) {
       System.err.println(e.getMessage());
-      System.exit(-1);
+      System.exit(1);
     }
   }
 
@@ -162,6 +168,7 @@ public class Compiler extends AbstractCompiler {
           parseGrammar(program, filename);
           break;
         case "connect":
+        case "ragconnect":
           // process ragConnect
           RagConnect parsedRagConnect = parseRagConnect(program, filename);
           mergeRagConnectDefinitions(ragConnect, parsedRagConnect);
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..9da13bf479b6fe16be8c935b06cf1e9ad3283308 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
@@ -21,6 +21,17 @@ public class SimpleMain {
 
   // --- just testing byte[] conversion ---
   public static void testing() {
+    System.out.println("---");
+    try {
+      Class<?> clazz = Class.forName("java.util.List");
+      System.out.println("clazz.getName() = " + clazz.getName());
+      System.out.println(Integer.class.isAssignableFrom(Integer.class));
+      System.out.println(new SimpleJavaTypeUse("int").assignableTo(new SimpleJavaTypeUse("Integer")));
+    } catch (ClassNotFoundException e) {
+      e.printStackTrace();
+    }
+    System.out.println("---");
+
     byte[] bytes;
     int i = 1;
     double d = 2.3d;
@@ -102,8 +113,8 @@ public class SimpleMain {
   }
 
   public static void main(String[] args) {
-//    testing();
-    createManualAST();
+    testing();
+//    createManualAST();
   }
 
   private static void createManualAST() {
diff --git a/ragconnect.base/src/main/resources/MqttHandler.jadd b/ragconnect.base/src/main/resources/MqttHandler.jadd
index 493d8e4452e81c5dbfa45a522a6366345908cdcc..1161211ffb0b90eaf26c7d27d709d71f7a6369c8 100644
--- a/ragconnect.base/src/main/resources/MqttHandler.jadd
+++ b/ragconnect.base/src/main/resources/MqttHandler.jadd
@@ -83,7 +83,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));
diff --git a/ragconnect.base/src/main/resources/RestHandler.jadd b/ragconnect.base/src/main/resources/RestHandler.jadd
new file mode 100644
index 0000000000000000000000000000000000000000..24118f04c563fe0b44105bfe674e269c76e7466f
--- /dev/null
+++ b/ragconnect.base/src/main/resources/RestHandler.jadd
@@ -0,0 +1,99 @@
+aspect RestHandler {
+/**
+ * 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 folloing errors 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;
+  }
+
+  public 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/mappingApplication.mustache b/ragconnect.base/src/main/resources/mappingApplication.mustache
index 95ce2fe29329d87f880497b37697d50c8c0687be..cbfac221a210037dce1f9881699edad5c68776a2 100644
--- a/ragconnect.base/src/main/resources/mappingApplication.mustache
+++ b/ragconnect.base/src/main/resources/mappingApplication.mustache
@@ -1,7 +1,7 @@
 {{lastDefinitionToType}} {{resultVarPrefix}}{{lastDefinitionName}};
 try {
   {{#InnerMappingDefinitions}}
-  {{^last}}{{ToType}} {{/last}}{{resultVarPrefix}}{{methodName}} = {{methodName}}({{inputVarName}});{{!inputVarName has to be computed beforehand}}
+  {{^last}}{{ToType}} {{/last}}{{resultVarPrefix}}{{methodName}} = {{methodName}}({{inputVarName}});
   {{/InnerMappingDefinitions}}
 } catch (Exception e) {
   e.printStackTrace();
diff --git a/ragconnect.base/src/main/resources/mqtt.mustache b/ragconnect.base/src/main/resources/mqtt.mustache
index dbdb85e7954dbf50ae7e150ffc43dd4cbda0fd80..95b253a9b4b4e46fda738a66499b8e2a7cc3707d 100644
--- a/ragconnect.base/src/main/resources/mqtt.mustache
+++ b/ragconnect.base/src/main/resources/mqtt.mustache
@@ -1,5 +1,5 @@
 aspect MQTT {
-  private String {{rootNodeName}}.MqttName() { return "Ros2Rag"; }
+  private String {{rootNodeName}}.MqttName() { return "RagConnectMQTT"; }
   private MqttHandler {{rootNodeName}}.{{mqttHandlerField}} = new MqttHandler(MqttName());
   public void {{rootNodeName}}.{{mqttSetHostMethod}}(String host) throws java.io.IOException {
     {{mqttHandlerField}}.setHost(host);
@@ -20,4 +20,7 @@ aspect MQTT {
   {{#first}}inh MqttHandler ASTNode.{{mqttHandlerAttribute}}();{{/first}}
   eq {{rootNodeName}}.get{{name}}().{{mqttHandlerAttribute}}() = {{mqttHandlerField}};
   {{/getRootTypeComponents}}
+  {{^getRootTypeComponents}}
+  syn MqttHandler {{rootNodeName}}.{{mqttHandlerAttribute}}() = {{mqttHandlerField}};
+  {{/getRootTypeComponents}}
 }
diff --git a/ragconnect.base/src/main/resources/ragconnect.mustache b/ragconnect.base/src/main/resources/ragconnect.mustache
index 0b455283977cf812a47b5266ee593c6597e97cc9..24e25418d07766f0ac881683080a622687ceff8e 100644
--- a/ragconnect.base/src/main/resources/ragconnect.mustache
+++ b/ragconnect.base/src/main/resources/ragconnect.mustache
@@ -1,12 +1,21 @@
-{{> mqtt}}
+{{#usesMqtt}}
+  {{> mqtt}}
+{{/usesMqtt}}
+{{#usesRest}}
+  {{> rest}}
+{{/usesRest}}
 aspect ROS2RAG {
   {{#ReceiveDefinitions}}
     {{> receiveDefinition}}
   {{/ReceiveDefinitions}}
 
-  {{#SendDefinitions}}
-    {{> sendDefinition}}
-  {{/SendDefinitions}}
+  {{#PushSendDefinitions}}
+    {{> sendDefinitionPush}}
+  {{/PushSendDefinitions}}
+
+  {{#PullSendDefinitions}}
+    {{> sendDefinitionPull}}
+  {{/PullSendDefinitions}}
 
   {{#MappingDefinitions}}
     {{> mappingDefinition}}
diff --git a/ragconnect.base/src/main/resources/receiveDefinition.mustache b/ragconnect.base/src/main/resources/receiveDefinition.mustache
index c02f5e5f49f6a9bebf0f6a1e7498ec743024afb3..31a08c737349b6c2d16f14a0fab50065d2c01a35 100644
--- a/ragconnect.base/src/main/resources/receiveDefinition.mustache
+++ b/ragconnect.base/src/main/resources/receiveDefinition.mustache
@@ -1,8 +1,8 @@
-  public void {{parentTypeName}}.{{connectMethod}}(String topic) {
-    {{handlerAttribute}}().newConnection(topic, message -> {
+  public void {{parentTypeName}}.{{connectMethod}}(String {{connectParameterName}}) {
+    {{handlerAttribute}}().{{newConnectionMethod}}({{connectParameterName}}, message -> {
       {{> mappingApplication}}
       {{#loggingEnabledForReads}}
-      System.out.println("[Receive] " + topic + " -> {{tokenName}} = " + {{lastResult}});
+      System.out.println("[Receive] " + {{connectParameterName}} + " -> {{tokenName}} = " + {{lastResult}});
       {{/loggingEnabledForReads}}
       set{{tokenName}}({{lastResult}});
     });
diff --git a/ragconnect.base/src/main/resources/rest.mustache b/ragconnect.base/src/main/resources/rest.mustache
new file mode 100644
index 0000000000000000000000000000000000000000..fb77601c38467e0f3f1bd1f01993db410670ce16
--- /dev/null
+++ b/ragconnect.base/src/main/resources/rest.mustache
@@ -0,0 +1,18 @@
+aspect REST {
+  private String {{rootNodeName}}.RestName() { return "RagConnectREST"; }
+  private RestHandler {{rootNodeName}}.{{restHandlerField}} = new RestHandler(RestName());
+  public void {{rootNodeName}}.{{restSetPortMethod}}(int port) {
+    {{restHandlerField}}.setPort(port);
+  }
+  public void {{rootNodeName}}.{{restCloseMethod}}() {
+    {{restHandlerField}}.close();
+  }
+
+  {{#getRootTypeComponents}}
+  {{#first}}inh RestHandler ASTNode.{{restHandlerAttribute}}();{{/first}}
+  eq {{rootNodeName}}.get{{name}}().{{restHandlerAttribute}}() = {{restHandlerField}};
+  {{/getRootTypeComponents}}
+  {{^getRootTypeComponents}}
+  syn RestHandler {{rootNodeName}}.{{restHandlerAttribute}}() = {{restHandlerField}};
+  {{/getRootTypeComponents}}
+}
diff --git a/ragconnect.base/src/main/resources/sendDefinitionPull.mustache b/ragconnect.base/src/main/resources/sendDefinitionPull.mustache
new file mode 100644
index 0000000000000000000000000000000000000000..598846d86f9fc67167de7896f42c30cd0cfc3360
--- /dev/null
+++ b/ragconnect.base/src/main/resources/sendDefinitionPull.mustache
@@ -0,0 +1,7 @@
+  public void {{parentTypeName}}.{{connectMethod}}(String {{connectParameterName}}, boolean writeCurrentValue) {
+    {{handlerAttribute}}().{{newConnectionMethod}}({{connectParameterName}}, () -> {
+      {{tokenResetMethod}}();
+      {{> mappingApplication}}
+      return {{lastResult}};
+    });
+  }
diff --git a/ragconnect.base/src/main/resources/sendDefinition.mustache b/ragconnect.base/src/main/resources/sendDefinitionPush.mustache
similarity index 80%
rename from ragconnect.base/src/main/resources/sendDefinition.mustache
rename to ragconnect.base/src/main/resources/sendDefinitionPush.mustache
index 874a342512326434d365563f5c1de93a609c5029..3f75b18274d687d306f7778f9846aeca592fa86f 100644
--- a/ragconnect.base/src/main/resources/sendDefinition.mustache
+++ b/ragconnect.base/src/main/resources/sendDefinitionPush.mustache
@@ -1,8 +1,8 @@
   private String {{parentTypeName}}.{{sendTopic}} = null;
   private byte[] {{parentTypeName}}.{{lastValue}} = null;
 
-  public void {{parentTypeName}}.{{connectMethod}}(String topic, boolean writeCurrentValue) {
-    {{sendTopic}} = topic;
+  public void {{parentTypeName}}.{{connectMethod}}(String {{connectParameterName}}, boolean writeCurrentValue) {
+    {{sendTopic}} = {{connectParameterName}};
     {{updateMethod}}();
     if (writeCurrentValue) {
       {{writeMethod}}();
diff --git a/ragconnect.base/src/main/resources/tokenComponent.mustache b/ragconnect.base/src/main/resources/tokenComponent.mustache
index e6a136d1c8de08b6dce9104fd46fafa5d91b67b6..729f442aebfc67ee2bbf5e559a9946405a275260 100644
--- a/ragconnect.base/src/main/resources/tokenComponent.mustache
+++ b/ragconnect.base/src/main/resources/tokenComponent.mustache
@@ -3,9 +3,11 @@
     {{#DependencyDefinitions}}
     for ({{targetParentTypeName}} target : get{{internalRelationPrefix}}TargetList()) {
       {{#targetEndpointDefinition}}
+      {{#isPush}}
       if (target.{{updateMethod}}()) {
         target.{{writeMethod}}();
       }
+      {{/isPush}}
       {{/targetEndpointDefinition}}
     }
     {{/DependencyDefinitions}}
diff --git a/ragconnect.tests/build.gradle b/ragconnect.tests/build.gradle
index 4cb42ea9965f71777280c355df42a8cde31ab47d..96b3d74270fae3c825621c5e5d199b6559105510 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'
 }
@@ -97,7 +106,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 ---
@@ -113,8 +122,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) {
@@ -128,7 +136,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 ---
@@ -144,8 +152,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) {
@@ -160,7 +167,7 @@ task compileDefaultOnlyWriteTest(type: RelastTest) {
             'src/test/02-after-ragconnect/defaultOnlyWrite/RagConnect.jadd'
 }
 
-testClasses.dependsOn compileDefaultOnlyWriteTest
+compileTestJava.dependsOn compileDefaultOnlyWriteTest
 compileDefaultOnlyWriteTest.dependsOn preprocessDefaultOnlyWriteTest
 
 // --- Test: read1write2 ---
@@ -192,7 +199,7 @@ task compileRead1Write2Test(type: RelastTest) {
             'src/test/02-after-ragconnect/read1write2/RagConnect.jadd'
 }
 
-testClasses.dependsOn compileRead1Write2Test
+compileTestJava.dependsOn compileRead1Write2Test
 compileRead1Write2Test.dependsOn preprocessRead1Write2Test
 
 // --- Test: read2write1 ---
@@ -208,7 +215,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'
 }
 
@@ -224,7 +231,7 @@ task compileRead2Write1Test(type: RelastTest) {
             'src/test/02-after-ragconnect/read2write1/RagConnect.jadd'
 }
 
-testClasses.dependsOn compileRead2Write1Test
+compileTestJava.dependsOn compileRead2Write1Test
 compileRead2Write1Test.dependsOn preprocessRead2Write1Test
 
 // --- Test: via ---
@@ -232,6 +239,7 @@ 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'
     }
 
@@ -253,8 +261,9 @@ task compileViaTest(type: RelastTest) {
     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'
 }
 
-testClasses.dependsOn compileViaTest
+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
index 2f39f088672c170a424dd1707bbbfcd24e169c4d..938490169415b2ce8f3da325ae2a1c768030921f 100644
--- a/ragconnect.tests/src/test/01-input/via/Test.connect
+++ b/ragconnect.tests/src/test/01-input/via/Test.connect
@@ -2,27 +2,33 @@ receive A.Mqtt2MqttInput via mqtt using MarkMqttInput ;
 receive A.Rest2RestInput via rest using MarkRestInput ;
 receive A.Mqtt2RestInput via mqtt using MarkMqttInput ;
 receive A.Rest2MqttInput via rest using MarkRestInput ;
+receive A.Both2BothInput via mqtt using MarkMqttInput ;
+receive A.Both2BothInput via rest using MarkRestInput ;
 
 send A.Mqtt2MqttOutput via mqtt using MarkMqttOutput ;
 send A.Rest2RestOutput via rest using MarkRestOutput ;
 send A.Mqtt2RestOutput via rest using MarkRestOutput ;
 send A.Rest2MqttOutput via mqtt using MarkMqttOutput ;
+send A.Both2RestOutput via rest using MarkRestOutput ;
+send A.Both2MqttOutput via mqtt 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;
+  return "FromMqtt-" + s;
 :}
 MarkRestInput maps String s to String {:
-  return "FromRest" + s;
+  return "FromRest-" + s;
 :}
 
 MarkMqttOutput maps String s to String {:
-  return s + "ToMqtt";
+  return s + "-ToMqtt";
 :}
 MarkRestOutput maps String s to String {:
-  return s + "ToRest";
+  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
index 3c4313fb9b799f12d73ec1ea10279b579a5e388e..9d25387d26e651fe25a937a56a863311b4b7b69a 100644
--- a/ragconnect.tests/src/test/01-input/via/Test.jadd
+++ b/ragconnect.tests/src/test/01-input/via/Test.jadd
@@ -1,6 +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.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
index 6868ed853c2c06a60798db23c153deb200f6c647..1e707fbbc04b1204381351d0c38e826fcbcc8b80 100644
--- a/ragconnect.tests/src/test/01-input/via/Test.relast
+++ b/ragconnect.tests/src/test/01-input/via/Test.relast
@@ -1,4 +1,5 @@
 A ::= <Mqtt2MqttInput> /<Mqtt2MqttOutput>/
       <Rest2RestInput> /<Rest2RestOutput>/
       <Mqtt2RestInput> /<Mqtt2RestOutput>/
-      <Rest2MqttInput> /<Rest2MqttOutput>/;
+      <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 99%
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..8482c308f29b8381bc698efac91c69e2e94a8d9f 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;
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 99%
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..11fe3d9f240ea595d2c9c5825148c555272ba8cc 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;
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 99%
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..1a58b683d55b0401485c85caca8105914e6cf67f 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;
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 99%
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..595976330654d05ae6e7a37dd8514fe901b9164f 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;
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 99%
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..5f61867b5a254a92090a4b6d502100a356f12ddd 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;
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 97%
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..38616caf60907ff0b17da55d420180f00f1a225c 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,4 +1,4 @@
-package org.jastadd.ros2rag.tests;
+package org.jastadd.ragconnect.tests;
 
 import java.io.File;
 import java.io.IOException;
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..8f1a8394960fa4d3ab37aeaf345e4f266b157e02
--- /dev/null
+++ b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/ViaTest.java
@@ -0,0 +1,179 @@
+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.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_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 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 WebTarget senderRest2Rest;
+  private WebTarget senderRest2Mqtt;
+
+  @AfterEach
+  public void closeConnections() {
+    if (handler != null) {
+      handler.close();
+    }
+    if (model != null) {
+      model.MqttCloseConnections();
+      model.RestCloseConnections();
+    }
+  }
+
+  @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");
+
+    sendData("101", "201", "301", "401");
+
+    // check new value
+    TestUtils.waitForMqtt();
+    checkData(2, "FromMqtt-101-M2M-ToMqtt", "FromRest-201-R2R-ToRest", "FromMqtt-301-M2R-ToRest", 2, "FromRest-401-R2M-ToMqtt");
+  }
+
+  @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);
+
+    sendData("111", "211", "311", "411");
+
+    // check new value
+    TestUtils.waitForMqtt();
+    checkData(1, "FromMqtt-111-M2M-ToMqtt", "FromRest-211-R2R-ToRest", "FromMqtt-311-M2R-ToRest", 1, "FromRest-411-R2M-ToMqtt");
+  }
+
+  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 checkData(int numberOfMqtt2MqttValues, String mqtt2MqttValue, String rest2RestValue, String mqtt2RestValue, int numberOfRest2MqttValues, String rest2MqttValue) {
+    dataMqtt2Mqtt.assertEqualData(numberOfMqtt2MqttValues, mqtt2MqttValue);
+    dataRest2Mqtt.assertEqualData(numberOfRest2MqttValues, rest2MqttValue);
+    assertEquals(rest2RestValue, readRest2Rest());
+    assertEquals(mqtt2RestValue, readMqtt2Rest());
+  }
+
+  private String readRest2Rest() {
+    return dataRest2Rest.request().get().readEntity(String.class);
+  }
+
+  private String readMqtt2Rest() {
+    return dataMqtt2Rest.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");
+  }
+
+  private void setupReceiverAndConnect(boolean writeCurrentValue) throws IOException {
+    model.MqttSetHost(TestUtils.getMqttHost());
+    model.RestSetPort(REST_PORT);
+    assertTrue(model.MqttWaitUntilReady(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);
+
+    dataMqtt2Mqtt = new ReceiverData();
+    dataRest2Mqtt = 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);
+    });
+
+    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);
+    senderRest2Rest = client.target(REST_SERVER_BASE_URL + PATH_REST_2_REST_RECEIVE);
+    senderRest2Mqtt = client.target(REST_SERVER_BASE_URL + PATH_REST_2_MQTT_RECEIVE);
+
+    model.connectMqtt2MqttInput(TOPIC_MQTT_2_MQTT_RECEIVE);
+    model.connectMqtt2MqttOutput(TOPIC_MQTT_2_MQTT_SEND, writeCurrentValue);
+    model.connectMqtt2RestInput(TOPIC_MQTT_2_REST_RECEIVE);
+    model.connectMqtt2RestOutput(PATH_MQTT_2_REST_SEND, writeCurrentValue);
+    model.connectRest2MqttInput(PATH_REST_2_MQTT_RECEIVE);
+    model.connectRest2MqttOutput(TOPIC_REST_2_MQTT_SEND, writeCurrentValue);
+    model.connectRest2RestInput(PATH_REST_2_REST_RECEIVE);
+    model.connectRest2RestOutput(PATH_REST_2_REST_SEND, 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);
+    }
+  }
+}