From 729568de52cadd76df4daee7241b5c9a2e12f946 Mon Sep 17 00:00:00 2001
From: rschoene <rene.schoene@tu-dresden.de>
Date: Fri, 30 Jul 2021 18:54:18 +0200
Subject: [PATCH] WIP: working on correct connect and disconnect

- introduced new types to encapsulate behaviour: RagConnectTokenMap, RagConnectPublisher
- move lastValue (of sendDefinitions) into new publisher
- create connectToken outside of handlers
- MqttHandler: make wildcardPatterns a list
- RestHandler: update dependency requirement to Spark 3.9.3 in order to unmap existing routes upon disconnect
- testing disconnect functionality in (most) existing tests
- still sometimes (!) getting "Top of handler stack does not match at pop!" Error during connect
---
 pages/docs/adding.md                          |   4 +-
 .../main/jastadd/intermediate/Generation.jadd |  10 +-
 .../src/main/resources/MqttHandler.jadd       |  99 +++++++-----
 .../src/main/resources/RestHandler.jadd       |  30 ++--
 .../src/main/resources/handler.mustache       |  65 +++++++-
 .../src/main/resources/ragconnect.mustache    |   8 +-
 .../main/resources/receiveDefinition.mustache |  41 +++--
 .../main/resources/sendDefinition.mustache    |  68 ++++----
 ragconnect.tests/build.gradle                 |  20 ++-
 .../ragconnect/tests/AbstractMqttTest.java    |   4 +-
 .../tests/IncrementalDependencyTest.java      |  68 +++++++-
 .../ragconnect/tests/TokenValueSendTest.java  | 111 ++++++++-----
 .../org/jastadd/ragconnect/tests/ViaTest.java | 109 ++++++++++---
 .../tests/list/AbstractListTest.java          |  24 ++-
 .../tests/list/ListIncrementalTest.java       |  35 +++--
 .../ragconnect/tests/list/ListManualTest.java |  15 ++
 .../singleList/AbstractSingleListTest.java    |  66 +++++++-
 .../AbstractSingleListVariantTest.java        | 146 ++++++++++++++----
 ...ngleListVariantIncrementalVariantTest.java |   1 +
 .../tests/tree/AbstractTreeTest.java          |  25 ++-
 .../tests/tree/TreeIncrementalTest.java       |  18 ++-
 .../ragconnect/tests/tree/TreeManualTest.java |  18 ++-
 .../AbstractTreeAllowedTokensTest.java        |  34 +++-
 .../TreeAllowedTokensIncrementalTest.java     |  34 ++--
 .../TreeAllowedTokensManualTest.java          |  34 ++--
 25 files changed, 817 insertions(+), 270 deletions(-)

diff --git a/pages/docs/adding.md b/pages/docs/adding.md
index ffbeda2..f777e32 100644
--- a/pages/docs/adding.md
+++ b/pages/docs/adding.md
@@ -125,6 +125,6 @@ RagConnect itself does not introduce dependencies.
 However, depending on the selected protocols (see [compiler options](using#compiler-options)), additional dependencies are required.
 
 | Protocol | Dependency (Gradle format) | Remarks |
-|-|-|-|
+|---|---|---|
 | `mqtt` | `group: 'org.fusesource.mqtt-client', name: 'mqtt-client', version: '1.15'` | Mqtt is selected by default, so this dependency therefore is required "by default". Might work with other versions as well. |
-| `rest` | `group: 'com.sparkjava', name: 'spark-core', version: '2.9.2'` | Might work with other versions as well. For debugging, it is beneficial to include an implementation for [SLF4J](http://www.slf4j.org/). |
+| `rest` | `group: 'com.sparkjava', name: 'spark-core', version: '2.9.3'` | Might work with newer versions as well. For debugging, it is beneficial to include an implementation for [SLF4J](http://www.slf4j.org/). |
diff --git a/ragconnect.base/src/main/jastadd/intermediate/Generation.jadd b/ragconnect.base/src/main/jastadd/intermediate/Generation.jadd
index 7e4bde9..6924449 100644
--- a/ragconnect.base/src/main/jastadd/intermediate/Generation.jadd
+++ b/ragconnect.base/src/main/jastadd/intermediate/Generation.jadd
@@ -64,7 +64,7 @@ aspect AttributesForMustache {
   syn boolean MEndpointDefinition.isTypeEndpointDefinition() = endpointDef().isTypeEndpointDefinition();
 
   syn String MEndpointDefinition.disconnectMethod() {
-    // if both (send and receive) are defined for the token, ensure methods with different names
+    // if both (send and receive) are defined for an endpoint, ensure methods with different names
     String extra;
     if (endpointDef().isTokenEndpointDefinition()) {
       extra = endpointDef().asTokenEndpointDefinition().lookupTokenEndpointDefinitions(token()).size() > 1 ? uniqueSuffix() : "";
@@ -114,6 +114,8 @@ aspect AttributesForMustache {
     }
     return preemptiveExpectedValue() + " != null ? " + preemptiveExpectedValue() + ".equals(" + lastResult() + ") : " + lastResult() + " == null";
   }
+  syn String MEndpointDefinition.sender() = null; // only for M*SendDefinitions
+  syn String MEndpointDefinition.lastValue() = sender() + ".lastValue"; // only for M*SendDefinitions
 
   // --- MTokenEndpointDefinition ---
   eq MTokenEndpointDefinition.getterMethod() = "get" + tokenName();
@@ -151,8 +153,7 @@ aspect AttributesForMustache {
   eq MTokenSendDefinition.updateMethod() = "_update_" + tokenName();
   eq MTokenSendDefinition.writeMethod() = "_writeLastValue_" + tokenName();
 
-  syn String MTokenSendDefinition.sender() = "_sender_" + tokenName();
-  syn String MTokenSendDefinition.lastValue() = "_lastValue" + tokenName();
+  eq MTokenSendDefinition.sender() = "_sender_" + tokenName();
   syn String MTokenSendDefinition.tokenResetMethod() = getterMethod() + "_reset";
   syn boolean MTokenSendDefinition.shouldSendValue() = endpointDef().asTokenEndpointDefinition().shouldSendValue();
 
@@ -175,8 +176,7 @@ aspect AttributesForMustache {
   eq MTypeSendDefinition.updateMethod() = "_update_" + typeName();
   eq MTypeSendDefinition.writeMethod() = "_writeLastValue_" + typeName();
 
-  syn String MTypeSendDefinition.sender() = "_sender_" + typeName();
-  syn String MTypeSendDefinition.lastValue() = "_lastValue" + typeName();
+  eq MTypeSendDefinition.sender() = "_sender_" + typeName();
   syn String MTypeSendDefinition.tokenResetMethod() = getterMethod() + "_reset";
   syn boolean MTypeSendDefinition.shouldSendValue() = endpointDef().asTypeEndpointDefinition().shouldSendValue();
 
diff --git a/ragconnect.base/src/main/resources/MqttHandler.jadd b/ragconnect.base/src/main/resources/MqttHandler.jadd
index a8f065e..e017f78 100644
--- a/ragconnect.base/src/main/resources/MqttHandler.jadd
+++ b/ragconnect.base/src/main/resources/MqttHandler.jadd
@@ -1,10 +1,7 @@
-import java.util.List;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-import java.util.function.BiConsumer;aspect MqttHandler {
+aspect MqttHandler {
 public class MqttServerHandler {
   private final java.util.Map<String, MqttHandler> handlers = new java.util.HashMap<>();
-  private final java.util.Map<ConnectToken, java.util.function.BiConsumer<String, byte[]>> tokensForRemoval = new java.util.HashMap<>();
+  private final java.util.Map<RagConnectToken, java.util.function.BiConsumer<String, byte[]>> tokensForRemoval = new java.util.HashMap<>();
   private long time;
   private java.util.concurrent.TimeUnit unit;
   private String name;
@@ -39,14 +36,12 @@ public class MqttServerHandler {
     return handler;
   }
 
-  public ConnectToken newConnection(java.net.URI uri, java.util.function.BiConsumer<String, byte[]> callback) throws java.io.IOException {
-    ConnectToken connectToken = new ConnectToken(uri);
-    resolveHandler(uri).newConnection(extractTopic(uri), callback);
+  public boolean newConnection(RagConnectToken connectToken, java.util.function.BiConsumer<String, byte[]> callback) throws java.io.IOException {
     tokensForRemoval.put(connectToken, callback);
-    return connectToken;
+    return resolveHandler(connectToken.uri).newConnection(extractTopic(connectToken.uri), callback);
   }
 
-  public boolean disconnect(ConnectToken connectToken) throws java.io.IOException {
+  public boolean disconnect(RagConnectToken connectToken) throws java.io.IOException {
     MqttHandler handler = resolveHandler(connectToken.uri);
     return handler != null ? handler.disconnect(extractTopic(connectToken.uri), tokensForRemoval.get(connectToken)) : false;
   }
@@ -89,6 +84,10 @@ public class MqttServerHandler {
  * @author rschoene - Initial contribution
  */
 public class MqttHandler {
+  private class PatternCallbackListPair {
+    java.util.regex.Pattern pattern;
+    java.util.List<java.util.function.BiConsumer<String, byte[]>> callbacks;
+  }
   private static final int DEFAULT_PORT = 1883;
 
   private final org.apache.logging.log4j.Logger logger;
@@ -104,7 +103,7 @@ public class MqttHandler {
   private org.fusesource.mqtt.client.QoS qos;
   /** Dispatch knowledge */
   private final java.util.Map<String, java.util.List<java.util.function.BiConsumer<String, byte[]>>> normalCallbacks;
-  private final java.util.Map<java.util.regex.Pattern, java.util.List<java.util.function.BiConsumer<String, byte[]>>> wildcardCallbacks;
+  private final java.util.List<PatternCallbackListPair> wildcardCallbacks;
 
   public MqttHandler() {
     this("RagConnect");
@@ -114,7 +113,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.normalCallbacks = new java.util.HashMap<>();
-    this.wildcardCallbacks = new java.util.HashMap<>();
+    this.wildcardCallbacks = new java.util.ArrayList<>();
     this.readyLatch = new java.util.concurrent.CountDownLatch(1);
     this.qos = org.fusesource.mqtt.client.QoS.AT_LEAST_ONCE;
   }
@@ -244,9 +243,9 @@ public class MqttHandler {
     if (normalCallbackList != null) {
       result.addAll(normalCallbackList);
     }
-    wildcardCallbacks.forEach((topicPattern, callback) -> {
-      if (topicPattern.matcher(topicString).matches()) {
-        result.addAll(callback);
+    wildcardCallbacks.forEach(pair -> {
+      if (pair.pattern.matcher(topicString).matches()) {
+        result.addAll(pair.callbacks);
       }
     });
     return result;
@@ -295,20 +294,29 @@ public class MqttHandler {
     logger.debug("new connection for {}", topic);
     final boolean needSubscribe;
     if (isWildcardTopic(topic)) {
-      String regexForTopic = topic.replace("*", "[^/]*").replace("#", ".*");
-      java.util.regex.Pattern pattern = java.util.regex.Pattern.compile(regexForTopic);
-      wildcardCallbacks.computeIfAbsent(pattern, p -> new java.util.ArrayList<>())
-                       .add(callback);
-      needSubscribe = true;
+      String regex = regexForWildcardTopic(topic);
+      PatternCallbackListPair pairToAddTo = null;
+      for (PatternCallbackListPair pair : wildcardCallbacks) {
+        if (pair.pattern.pattern().equals(regex)) {
+          pairToAddTo = pair;
+          break;
+        }
+      }
+      if (pairToAddTo == null) {
+        pairToAddTo = new PatternCallbackListPair();
+        pairToAddTo.pattern = java.util.regex.Pattern.compile(regex);
+        pairToAddTo.callbacks = new ArrayList<>();
+        wildcardCallbacks.add(pairToAddTo);
+      }
+      needSubscribe = pairToAddTo.callbacks.isEmpty();
+      pairToAddTo.callbacks.add(callback);
     } else { // normal topic
       java.util.List<java.util.function.BiConsumer<String, byte[]>> callbacksForTopic = normalCallbacks.get(topic);
-      if (callbacksForTopic == null || callbacksForTopic.isEmpty()) {
+      if (callbacksForTopic == null) {
         callbacksForTopic = new java.util.ArrayList<>();
         normalCallbacks.put(topic, callbacksForTopic);
-        needSubscribe = true;
-      } else {
-        needSubscribe = false;
       }
+      needSubscribe = callbacksForTopic.isEmpty();
       callbacksForTopic.add(callback);
     }
     if (needSubscribe) {
@@ -347,40 +355,49 @@ public class MqttHandler {
     return topic.contains("*") || topic.contains("#");
   }
 
+  private String regexForWildcardTopic(String topic) {
+    return topic.replace("*", "[^/]*").replace("#", ".*");
+  }
+
   public boolean disconnect(String topic, java.util.function.BiConsumer<String, byte[]> callback) {
     boolean needUnsubscribe = false;
     java.util.concurrent.atomic.AtomicReference<Boolean> success = new java.util.concurrent.atomic.AtomicReference<>(true);
 
-    boolean foundTopicInCallbacks = false;
+    final String topicToUnsubscribe;
 
     // check if wildcard is to be removed
     if (isWildcardTopic(topic)) {
-      java.util.regex.Pattern wildcardPatternToRemove = null;
-      for (java.util.Map.Entry<java.util.regex.Pattern, java.util.List<java.util.function.BiConsumer<String, byte[]>>> entry : wildcardCallbacks.entrySet()) {
-        if (entry.getKey().pattern().equals(topic)) {
-          foundTopicInCallbacks = true;
+      boolean topicRegistered = false;
+      String topicRegex = regexForWildcardTopic(topic);
+      for (PatternCallbackListPair pair : wildcardCallbacks) {
+        if (pair.pattern.pattern().equals(topicRegex)) {
+          topicRegistered = true;
           // if still successful, update with whether callback could be removed
-          success.compareAndSet(true, (entry.getValue().remove(callback)));
-          if (entry.getValue().isEmpty()) {
-            wildcardPatternToRemove = entry.getKey();
+          success.compareAndSet(true, pair.callbacks.remove(callback));
+          // if no more callbacks left, unsubscribe and remove from list
+          if (pair.callbacks.isEmpty()) {
             needUnsubscribe = true;
+            wildcardCallbacks.remove(pair.pattern);
           }
           break;
         }
       }
-      ;
-      if (wildcardPatternToRemove != null) {
-        wildcardCallbacks.remove(wildcardPatternToRemove);
-      }
+      topicToUnsubscribe = topicRegistered ? topicRegex : null;
     } else if (normalCallbacks.containsKey(topic)) {
-      foundTopicInCallbacks = true;
-      // if still successful, update with whether callback could be removed
+      topicToUnsubscribe = topic;
       var normalCallbackList = normalCallbacks.get(topic);
+      // if still successful, update with whether callback could be removed
       success.compareAndSet(true, normalCallbackList.remove(callback));
-      needUnsubscribe |= normalCallbackList.isEmpty();
+      // if no more callbacks left, unsubscribe and remove from list
+      if (normalCallbackList.isEmpty()) {
+        needUnsubscribe = true;
+        normalCallbacks.remove(topic);
+      }
+    } else {
+      topicToUnsubscribe = null;
     }
 
-    if (!foundTopicInCallbacks) {
+    if (topicToUnsubscribe == null) {
       logger.warn("Disconnect for not connected topic '{}'", topic);
       return false;
     }
@@ -389,7 +406,7 @@ public class MqttHandler {
       java.util.concurrent.CountDownLatch operationFinished = new java.util.concurrent.CountDownLatch(1);
       // no callbacks anymore for this topic, unsubscribe from mqtt
       connection.getDispatchQueue().execute(() -> {
-        org.fusesource.hawtbuf.UTF8Buffer topicBuffer = org.fusesource.hawtbuf.Buffer.utf8(topic);
+        org.fusesource.hawtbuf.UTF8Buffer topicBuffer = org.fusesource.hawtbuf.Buffer.utf8(topicToUnsubscribe);
         org.fusesource.hawtbuf.UTF8Buffer[] topicArray = new org.fusesource.hawtbuf.UTF8Buffer[]{topicBuffer};
         connection.unsubscribe(topicArray, new org.fusesource.mqtt.client.Callback<>() {
           @Override
diff --git a/ragconnect.base/src/main/resources/RestHandler.jadd b/ragconnect.base/src/main/resources/RestHandler.jadd
index b69bf71..9187afd 100644
--- a/ragconnect.base/src/main/resources/RestHandler.jadd
+++ b/ragconnect.base/src/main/resources/RestHandler.jadd
@@ -2,7 +2,7 @@ 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 final java.util.Map<ConnectToken, Object> tokensForRemoval = new java.util.HashMap<>();
+  private final java.util.Map<RagConnectToken, Object> tokensForRemoval = new java.util.HashMap<>();
   private String name;
 
   public RestServerHandler() {
@@ -25,21 +25,19 @@ public class RestServerHandler {
     return handler;
   }
 
-  public ConnectToken newPUTConnection(java.net.URI uri, java.util.function.Consumer<String> callback) {
-    ConnectToken connectToken = new ConnectToken(uri);
-    resolveHandler(uri).newPUTConnection(uri.getPath(), callback);
+  public boolean newPUTConnection(RagConnectToken connectToken, java.util.function.Consumer<String> callback) {
     tokensForRemoval.put(connectToken, callback);
-    return connectToken;
+    resolveHandler(connectToken.uri).newPUTConnection(connectToken.uri.getPath(), callback);
+    return true;
   }
 
-  public ConnectToken newGETConnection(java.net.URI uri, SupplierWithException<String> supplier) {
-    ConnectToken connectToken = new ConnectToken(uri);
-    resolveHandler(uri).newGETConnection(uri.getPath(), supplier);
+  public boolean newGETConnection(RagConnectToken connectToken, SupplierWithException<String> supplier) {
     tokensForRemoval.put(connectToken, supplier);
-    return connectToken;
+    resolveHandler(connectToken.uri).newGETConnection(connectToken.uri.getPath(), supplier);
+    return true;
   }
 
-  public boolean disconnect(ConnectToken connectToken) {
+  public boolean disconnect(RagConnectToken connectToken) {
     RestHandler handler = resolveHandler(connectToken.uri);
     return handler != null ? handler.disconnect(connectToken.uri.getPath(), tokensForRemoval.get(connectToken)) : false;
   }
@@ -127,9 +125,15 @@ public class RestHandler {
   }
 
   public boolean disconnect(String path, Object callbackOrSupplier) {
-    // only one will succeed (or false will be returned)
-    return callbacks.getOrDefault(path, java.util.Collections.emptyList()).remove(callbackOrSupplier) ||
-           suppliers.remove(path, callbackOrSupplier);
+    if (callbacks.getOrDefault(path, java.util.Collections.emptyList()).remove(callbackOrSupplier)) {
+      return true;
+    }
+    if (suppliers.remove(path, callbackOrSupplier)) {
+      // unmap the route
+      return spark.Spark.unmap(path);
+    }
+    System.err.println("Disconnect for not connected path '" + path + "'!");
+    return false;
   }
 
   private String makeError(spark.Response response, int statusCode, String message) {
diff --git a/ragconnect.base/src/main/resources/handler.mustache b/ragconnect.base/src/main/resources/handler.mustache
index 41e4238..fe8d945 100644
--- a/ragconnect.base/src/main/resources/handler.mustache
+++ b/ragconnect.base/src/main/resources/handler.mustache
@@ -14,15 +14,74 @@ aspect RagConnectHandler {
     {{#InUse}}{{FieldName}}.close();{{/InUse}}
     {{/Handlers}}
   }
-  class ConnectToken {
+  class RagConnectToken {
     static java.util.concurrent.atomic.AtomicLong counter = new java.util.concurrent.atomic.AtomicLong(0);
     final long id;
     final java.net.URI uri;
-    public ConnectToken(java.net.URI uri) {
+    final String entityName;
+    public RagConnectToken(java.net.URI uri, String entityName) {
       this.id = counter.incrementAndGet();
       this.uri = uri;
+      this.entityName = entityName;
     }
+  }
+  class RagConnectTokenMap {
+    java.util.Map<ASTNode, java.util.List<RagConnectToken>> connectTokensSend = new java.util.HashMap<>();
+    java.util.Map<ASTNode, java.util.List<RagConnectToken>> connectTokensReceive = new java.util.HashMap<>();
+    void add(ASTNode node, boolean isReceive, RagConnectToken token) {
+      java.util.Map<ASTNode, java.util.List<RagConnectToken>> mapOfTokens = (isReceive ? connectTokensReceive : connectTokensSend);
+      mapOfTokens.computeIfAbsent(node, n -> new java.util.ArrayList<>()).add(token);
+    }
+    java.util.List<RagConnectToken> removeAll(ASTNode node, boolean isReceive, java.net.URI uri, String entityName) {
+      java.util.List<RagConnectToken> listOfTokens = (isReceive ? connectTokensReceive : connectTokensSend).get(node);
+      if (listOfTokens == null) {
+        return java.util.Collections.emptyList();
+      }
+      java.util.List<RagConnectToken> tokensToRemove = listOfTokens.stream()
+          .filter(token -> token.uri.equals(uri) && token.entityName.equals(entityName))
+          .collect(java.util.stream.Collectors.toList());
+      listOfTokens.removeAll(tokensToRemove);
+      return tokensToRemove;
+    }
+  }
+  static RagConnectTokenMap ASTNode.connectTokenMap = new RagConnectTokenMap();
+
+  interface RagConnectDisconnectHandlerMethod {
+    boolean call(RagConnectToken token) throws java.io.IOException;
+  }
 
+  class RagConnectPublisher {
+    java.util.List<Runnable> senders = new java.util.ArrayList<>();
+    java.util.Map<RagConnectToken, Runnable> tokenToSender;
+    byte[] lastValue;
+
+    void add(Runnable sender, RagConnectToken connectToken) {
+      if (tokenToSender == null) {
+        tokenToSender = new java.util.HashMap<>();
+      }
+      senders.add(sender);
+      tokenToSender.put(connectToken, sender);
+    }
+
+    boolean remove(RagConnectToken token) {
+      if (tokenToSender == null) {
+        System.err.println("Removing sender before first addition for " + token.entityName + " at " + token.uri);
+        return false;
+      }
+      Runnable sender = tokenToSender.remove(token);
+      if (sender == null) {
+        System.err.println("Could not find connected sender for " + token.entityName + " at " + token.uri);
+        return false;
+      }
+      boolean success = senders.remove(sender);
+      if (senders.isEmpty()) {
+        lastValue = null;
+      }
+      return success;
+    }
+
+    void run() {
+      senders.forEach(Runnable::run);
+    }
   }
-  static java.util.Map<ASTNode, java.util.Map<java.net.URI, ConnectToken>> ASTNode.connectTokens = new java.util.HashMap<>();
 }
diff --git a/ragconnect.base/src/main/resources/ragconnect.mustache b/ragconnect.base/src/main/resources/ragconnect.mustache
index c08529a..dd509aa 100644
--- a/ragconnect.base/src/main/resources/ragconnect.mustache
+++ b/ragconnect.base/src/main/resources/ragconnect.mustache
@@ -58,12 +58,12 @@ aspect RagConnectObserver {
   class RagConnectObserver implements ASTState.Trace.Receiver {
 
     class RagConnectObserverEntry {
-      final ConnectToken connectToken;
+      final RagConnectToken connectToken;
       final ASTNode node;
       final String attributeString;
       final Runnable attributeCall;
 
-      RagConnectObserverEntry(ConnectToken connectToken, ASTNode node, String attributeString, Runnable attributeCall) {
+      RagConnectObserverEntry(RagConnectToken connectToken, ASTNode node, String attributeString, Runnable attributeCall) {
         this.connectToken = connectToken;
         this.node = node;
         this.attributeString = attributeString;
@@ -99,13 +99,13 @@ aspect RagConnectObserver {
       node.trace().setReceiver(this);
     }
 
-    void add(ConnectToken connectToken, ASTNode node, String attributeString, Runnable attributeCall) {
+    void add(RagConnectToken connectToken, ASTNode node, String attributeString, Runnable attributeCall) {
       {{#loggingEnabledForIncremental}}
       System.out.println("** observer add: " + node + " on " + attributeString);
       {{/loggingEnabledForIncremental}}
       observedNodes.add(new RagConnectObserverEntry(connectToken, node, attributeString, attributeCall));
     }
-    void remove(ConnectToken connectToken) {
+    void remove(RagConnectToken connectToken) {
       observedNodes.removeIf(entry -> entry.connectToken.equals(connectToken));
     }
     @Override
diff --git a/ragconnect.base/src/main/resources/receiveDefinition.mustache b/ragconnect.base/src/main/resources/receiveDefinition.mustache
index 4ae8a05..707725a 100644
--- a/ragconnect.base/src/main/resources/receiveDefinition.mustache
+++ b/ragconnect.base/src/main/resources/receiveDefinition.mustache
@@ -99,47 +99,56 @@ public boolean {{parentTypeName}}.{{connectMethod}}(String {{connectParameterNam
 private boolean {{parentTypeName}}.{{internalConnectMethod}}(String {{connectParameterName}},
     java.util.function.BiConsumer<String, byte[]> consumer) throws java.io.IOException {
   {{>handleUri}}
-  ConnectToken connectToken;
+  RagConnectToken connectToken = new RagConnectToken(uri, "{{entityName}}");
+  boolean success;
   switch (scheme) {
   {{#usesMqtt}}
     case "mqtt":
-      connectToken = {{mqttHandlerAttribute}}().newConnection(uri, consumer);
-      if (connectToken == null) {
-        return false;
-      }
+      success = {{mqttHandlerAttribute}}().newConnection(connectToken, consumer);
       break;
   {{/usesMqtt}}
   {{#usesRest}}
     case "rest":
-      connectToken = {{restHandlerAttribute}}().newPUTConnection(uri, input -> {
+      success = {{restHandlerAttribute}}().newPUTConnection(connectToken, input -> {
         // TODO wildcard-topic not supported yet
         consumer.accept("", input.getBytes());
       });
-      if (connectToken == null) {
-        return false;
-      }
       break;
   {{/usesRest}}
     default:
       System.err.println("Unknown protocol '" + scheme + "'.");
-      return false;
+      success = false;
+  }
+  if (success) {
+    connectTokenMap.add(this, true, connectToken);
   }
-  connectTokens.computeIfAbsent(this, astNode -> new java.util.HashMap<java.net.URI, ConnectToken>())
-               .put(uri, connectToken);
-  return true;
+  return success;
 }
 
 public boolean {{parentTypeName}}.{{disconnectMethod}}(String {{connectParameterName}}) throws java.io.IOException {
   {{>handleUri}}
+  java.util.List<RagConnectToken> connectTokens = connectTokenMap.removeAll(this, true, uri, "{{entityName}}");
+  if (connectTokens.isEmpty()) {
+    System.err.println("Disconnect called without connection for receiving " + this + ".{{entityName}} to '" + {{connectParameterName}} + "'!");
+    return false;
+  }
+  RagConnectDisconnectHandlerMethod disconnectingMethod;
   switch (scheme) {
   {{#usesMqtt}}
-    case "mqtt": return {{mqttHandlerAttribute}}().disconnect(connectTokens.get(this).get(uri));
+    case "mqtt": disconnectingMethod = {{mqttHandlerAttribute}}()::disconnect;
+    break;
   {{/usesMqtt}}
   {{#usesRest}}
-    case "rest": return {{restHandlerAttribute}}().disconnect(connectTokens.get(this).get(uri));
+    case "rest": disconnectingMethod = {{restHandlerAttribute}}()::disconnect;
+    break;
   {{/usesRest}}
     default:
-      System.err.println("Unknown protocol '" + scheme + "'.");
+      System.err.println("Unknown protocol '" + scheme + "' in '" + {{connectParameterName}} + "' for disconnecting {{parentTypeName}}.{{entityName}}");
       return false;
   }
+  boolean success = true;
+  for (RagConnectToken connectToken : connectTokens) {
+    success &= disconnectingMethod.call(connectToken);
+  }
+  return success;
 }
diff --git a/ragconnect.base/src/main/resources/sendDefinition.mustache b/ragconnect.base/src/main/resources/sendDefinition.mustache
index 6fd97bf..5cee6a4 100644
--- a/ragconnect.base/src/main/resources/sendDefinition.mustache
+++ b/ragconnect.base/src/main/resources/sendDefinition.mustache
@@ -1,85 +1,83 @@
-private Runnable {{parentTypeName}}.{{sender}} = null;
-private byte[] {{parentTypeName}}.{{lastValue}} = null;
+private RagConnectPublisher {{parentTypeName}}.{{sender}} = new RagConnectPublisher();
 
 public boolean {{parentTypeName}}.{{connectMethod}}(String {{connectParameterName}}, boolean writeCurrentValue) throws java.io.IOException {
   {{>handleUri}}
-  ConnectToken connectToken;
-  if (connectTokens.computeIfAbsent(this, astNode -> new java.util.HashMap<java.net.URI, ConnectToken>())
-                   .get(uri) != null) {
-    System.err.println("Already connected for " + uri + " on " + this + "!");
-    return true;
-  }
+  RagConnectToken connectToken = new RagConnectToken(uri, "{{entityName}}");
+  boolean success;
   switch (scheme) {
   {{#usesMqtt}}
     case "mqtt":
       final MqttHandler handler = {{mqttHandlerAttribute}}().resolveHandler(uri);
       final String topic = {{mqttHandlerAttribute}}().extractTopic(uri);
-      {{sender}} = () -> {
+      {{sender}}.add(() -> {
         {{#loggingEnabledForWrites}}
         System.out.println("[Send] {{entityName}} = " + {{getterMethod}}() + " -> " + {{connectParameterName}});
         {{/loggingEnabledForWrites}}
         handler.publish(topic, {{lastValue}});
-      };
+        }, connectToken);
       {{updateMethod}}();
       if (writeCurrentValue) {
         {{writeMethod}}();
       }
-      connectToken = new ConnectToken(uri);
+      success = true;
       break;
   {{/usesMqtt}}
   {{#usesRest}}
     case "rest":
-      connectToken = {{restHandlerAttribute}}().newGETConnection(uri, () -> {
+      success = {{restHandlerAttribute}}().newGETConnection(connectToken, () -> {
         {{updateMethod}}();
         return new String({{lastValue}});
       });
-      if (connectToken == null) {
-        return false;
-      }
       break;
   {{/usesRest}}
     default:
       System.err.println("Unknown protocol '" + scheme + "'.");
-      return false;
+      success = false;
   }
-  connectTokens.computeIfAbsent(this, astNode -> new java.util.HashMap<java.net.URI, ConnectToken>())
-               .put(uri, connectToken);
-  {{#incrementalOptionActive}}
-  _ragConnectObserver().add(connectToken, this, "{{getterMethod}}", () -> {
-    if (this.{{updateMethod}}()) {
-      this.{{writeMethod}}();
-    }
-  });
-  {{/incrementalOptionActive}}
-  return true;
+  if (success) {
+    connectTokenMap.add(this, false, connectToken);
+    {{#incrementalOptionActive}}
+    _ragConnectObserver().add(connectToken, this, "{{getterMethod}}", () -> {
+      if (this.{{updateMethod}}()) {
+        this.{{writeMethod}}();
+      }
+    });
+    {{/incrementalOptionActive}}
+  }
+  return success;
 }
 
 public boolean {{parentTypeName}}.{{disconnectMethod}}(String {{connectParameterName}}) throws java.io.IOException {
   {{>handleUri}}
-  ConnectToken connectToken = connectTokens.get(this).remove(uri);
-  if (connectToken == null) {
-    System.err.println("Disconnect without connect for " + uri + " on " + this + "!");
+  java.util.List<RagConnectToken> connectTokens = connectTokenMap.removeAll(this, false, uri, "{{entityName}}");
+  if (connectTokens.isEmpty()) {
+    System.err.println("Disconnect called without connection for sending " + this + ".{{entityName}} to '" + {{connectParameterName}} + "'!");
+    return false;
   }
   {{#incrementalOptionActive}}
-  _ragConnectObserver().remove(connectToken);
+  connectTokens.forEach(token -> _ragConnectObserver().remove(token));
   {{/incrementalOptionActive}}
+  RagConnectDisconnectHandlerMethod disconnectingMethod;
   switch (scheme) {
   {{#usesMqtt}}
     case "mqtt":
-      {{sender}} = null;
-      {{lastValue}} = null;
+      disconnectingMethod = {{sender}}::remove;
       break;
   {{/usesMqtt}}
   {{#usesRest}}
     case "rest":
-      {{restHandlerAttribute}}().disconnect(connectToken);
+      disconnectingMethod = {{restHandlerAttribute}}()::disconnect;
       break;
   {{/usesRest}}
     default:
-      System.err.println("Unknown protocol '" + scheme + "'.");
+      System.err.println("Unknown protocol '" + scheme + "' in '" + {{connectParameterName}} + "' for disconnecting {{parentTypeName}}.{{entityName}}");
       return false;
   }
-  return true;
+  boolean success = true;
+  for (RagConnectToken connectToken : connectTokens) {
+    success &= disconnectingMethod.call(connectToken);
+  }
+  return success;
 }
 
 protected boolean {{parentTypeName}}.{{updateMethod}}() {
diff --git a/ragconnect.tests/build.gradle b/ragconnect.tests/build.gradle
index 61470f5..ee6da58 100644
--- a/ragconnect.tests/build.gradle
+++ b/ragconnect.tests/build.gradle
@@ -51,7 +51,7 @@ dependencies {
     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: 'com.sparkjava', name: 'spark-core', version: '2.9.3'
     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'
@@ -550,15 +550,21 @@ task compileSingleListVariantIncremental(type: RagConnectTest, dependsOn: ':ragc
     }
 }
 
-//task cleanCurrentManualTest(type: Delete) {
+task cleanCurrentManualTest(type: Delete) {
 //    delete "src/test/02-after-ragconnect/singleListVariant"
 //    delete "src/test/03-after-relast/singleListVariant"
 //    delete "src/test/java-gen/singleListVariant/ast"
-//}
-//task cleanCurrentIncrementalTest(type: Delete) {
+    delete "src/test/02-after-ragconnect/singleList"
+    delete "src/test/03-after-relast/singleList"
+    delete "src/test/java-gen/singleList/ast"
+}
+task cleanCurrentIncrementalTest(type: Delete) {
 //    delete "src/test/02-after-ragconnect/singleListVariantInc"
 //    delete "src/test/03-after-relast/singleListVariantInc"
 //    delete "src/test/java-gen/singleListVariantInc/ast"
-//}
-//compileSingleListVariantManual.dependsOn cleanCurrentManualTest
-//compileSingleListVariantIncremental.dependsOn cleanCurrentIncrementalTest
+    delete "src/test/02-after-ragconnect/singleListInc"
+    delete "src/test/03-after-relast/singleListInc"
+    delete "src/test/java-gen/singleListInc/ast"
+}
+compileSingleListManual.dependsOn cleanCurrentManualTest
+compileSingleListIncremental.dependsOn cleanCurrentIncrementalTest
diff --git a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/AbstractMqttTest.java b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/AbstractMqttTest.java
index 330c950..db48fdf 100644
--- a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/AbstractMqttTest.java
+++ b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/AbstractMqttTest.java
@@ -55,7 +55,7 @@ public abstract class AbstractMqttTest {
    * Actual test code for communication when sending initial value.
    * @throws InterruptedException because of TestUtils.waitForMqtt()
    */
-  protected abstract void communicateSendInitialValue() throws InterruptedException;
+  protected abstract void communicateSendInitialValue() throws IOException, InterruptedException;
 
   @Tag("mqtt")
   @Test
@@ -70,7 +70,7 @@ public abstract class AbstractMqttTest {
    * Actual test code for communication without sending any value upon connecting.
    * @throws InterruptedException because of TestUtils.waitForMqtt()
    */
-  protected abstract void communicateOnlyUpdatedValue() throws InterruptedException;
+  protected abstract void communicateOnlyUpdatedValue() throws IOException, InterruptedException;
 
   /**
    * Create the model, and set required default values.
diff --git a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/IncrementalDependencyTest.java b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/IncrementalDependencyTest.java
index 580c4fc..1323177 100644
--- a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/IncrementalDependencyTest.java
+++ b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/IncrementalDependencyTest.java
@@ -9,8 +9,7 @@ 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;
+import static org.junit.jupiter.api.Assertions.*;
 
 /**
  * Testcase "Incremental Dependency".
@@ -80,7 +79,7 @@ public class IncrementalDependencyTest extends AbstractMqttTest {
   }
 
   @Override
-  protected void communicateSendInitialValue() throws InterruptedException {
+  protected void communicateSendInitialValue() throws InterruptedException, IOException {
     // check initial value
     TestUtils.waitForMqtt();
     checkData(1, "aStart",
@@ -104,10 +103,34 @@ public class IncrementalDependencyTest extends AbstractMqttTest {
     checkData(3, "a201",
         "b201Postfix",
         "b201Postfix");
+
+    // send and check new value (b1 should not change)
+    assertTrue(b1.disconnectOutputOnB(mqttUri(TOPIC_OUT_B1)));
+    sendData("301");
+    checkData(4, "a301",
+        3, "b201Postfix",
+        4, "b301Postfix");
+
+    // disconnecting again should yield false
+    assertFalse(b1.disconnectOutputOnB(mqttUri(TOPIC_OUT_B1)));
+
+    // send and check new value (b1 and b2 should not change)
+    assertTrue(b2.disconnectOutputOnB(mqttUri(TOPIC_OUT_B2)));
+    sendData("401");
+    checkData(5, "a401",
+        3, "b201Postfix",
+        4, "b301Postfix");
+
+    // send and check new value (nothing should not change)
+    assertTrue(model.disconnectOutputOnA(mqttUri(TOPIC_OUT_A)));
+    sendData("501");
+    checkData(5, "a401",
+        3, "b201Postfix",
+        4, "b301Postfix");
   }
 
   @Override
-  protected void communicateOnlyUpdatedValue() throws InterruptedException {
+  protected void communicateOnlyUpdatedValue() throws InterruptedException, IOException {
     // check initial value
     TestUtils.waitForMqtt();
     checkData(0, null,
@@ -132,6 +155,29 @@ public class IncrementalDependencyTest extends AbstractMqttTest {
         "b202Postfix",
         "b202Postfix");
 
+    // send and check new value (b1 should not change)
+    assertTrue(b1.disconnectOutputOnB(mqttUri(TOPIC_OUT_B1)));
+    sendData("302");
+    checkData(3, "a302",
+        2, "b202Postfix",
+        3, "b302Postfix");
+
+    // disconnecting again should yield false
+    assertFalse(b1.disconnectOutputOnB(mqttUri(TOPIC_OUT_B1)));
+
+    // send and check new value (b1 and b2 should not change)
+    assertTrue(b2.disconnectOutputOnB(mqttUri(TOPIC_OUT_B2)));
+    sendData("402");
+    checkData(4, "a402",
+        2, "b202Postfix",
+        3, "b302Postfix");
+
+    // send and check new value (nothing should not change)
+    assertTrue(model.disconnectOutputOnA(mqttUri(TOPIC_OUT_A)));
+    sendData("502");
+    checkData(4, "a402",
+        2, "b202Postfix",
+        3, "b302Postfix");
   }
 
   @Override
@@ -151,9 +197,17 @@ public class IncrementalDependencyTest extends AbstractMqttTest {
 
   private void checkData(int expectedNumberOfValues, String expectedLastAValue,
                          String expectedLastB1Value, String expectedLastB2Value) {
-    dataA.assertEqualData(expectedNumberOfValues, expectedLastAValue);
-    dataB1.assertEqualData(expectedNumberOfValues, expectedLastB1Value);
-    dataB2.assertEqualData(expectedNumberOfValues, expectedLastB2Value);
+    checkData(expectedNumberOfValues, expectedLastAValue,
+        expectedNumberOfValues, expectedLastB1Value,
+        expectedNumberOfValues, expectedLastB2Value);
+  }
+
+  private void checkData(int expectedNumberOfAValues, String expectedLastAValue,
+                         int expectedNumberOfB1Values, String expectedLastB1Value,
+                         int expectedNumberOfB2Values, String expectedLastB2Value) {
+    dataA.assertEqualData(expectedNumberOfAValues, expectedLastAValue);
+    dataB1.assertEqualData(expectedNumberOfB1Values, expectedLastB1Value);
+    dataB2.assertEqualData(expectedNumberOfB2Values, expectedLastB2Value);
   }
 
   private static class ReceiverData {
diff --git a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/TokenValueSendTest.java b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/TokenValueSendTest.java
index 6d99149..da4d703 100644
--- a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/TokenValueSendTest.java
+++ b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/TokenValueSendTest.java
@@ -6,8 +6,7 @@ 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;
+import static org.junit.jupiter.api.Assertions.*;
 
 /**
  * Test case "tokenValueSend".
@@ -15,6 +14,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
  * @author rschoene - Initial contribution
  */
 public class TokenValueSendTest extends AbstractMqttTest {
+  // TODO split into incremental and manual test
 
   private static final String TOPIC_SEND_ONE = "one/value/out";
 
@@ -96,9 +96,8 @@ public class TokenValueSendTest extends AbstractMqttTest {
   }
 
   @Override
-  protected void communicateSendInitialValue() throws InterruptedException {
+  protected void communicateSendInitialValue() throws InterruptedException, IOException {
     // check initial value
-    TestUtils.waitForMqtt();
     checkData(1, "Start-Post",
         1, "Start-Post",
         1, "Start-Post",
@@ -106,9 +105,6 @@ public class TokenValueSendTest extends AbstractMqttTest {
 
     // send new value
     sendData("200", "300");
-
-    // check new value
-    TestUtils.waitForMqtt();
     checkData(1, "Start-Post",
         2, "Pre-200-Post",
         2, "Pre-300-Post",
@@ -116,9 +112,6 @@ public class TokenValueSendTest extends AbstractMqttTest {
 
     // set new value
     setData("101", "201", "301");
-
-    // check new value
-    TestUtils.waitForMqtt();
     checkData(2, "101-Post",
         3, "201-Post",
         3, "301-Post",
@@ -126,9 +119,6 @@ public class TokenValueSendTest extends AbstractMqttTest {
 
     // send the same values (will not be sent again)
     setData("101", "201", "301");
-
-    // check new value
-    TestUtils.waitForMqtt();
     checkData(2, "101-Post",
         3, "201-Post",
         3, "301-Post",
@@ -136,9 +126,6 @@ public class TokenValueSendTest extends AbstractMqttTest {
 
     // send values with prefixes imitating receiving
     setData("102", "Pre-202", "Pre-302");
-
-    // check new value
-    TestUtils.waitForMqtt();
     checkData(3, "102-Post",
         4, "Pre-202-Post",
         4, "Pre-302-Post",
@@ -146,19 +133,49 @@ public class TokenValueSendTest extends AbstractMqttTest {
 
     // send the same values (will not be sent again, because previously prefixed)
     sendData("202", "302");
-
-    // check new value
-    TestUtils.waitForMqtt();
     checkData(3, "102-Post",
         4, "Pre-202-Post",
         4, "Pre-302-Post",
         4, "Pre-302-T-Post");
+
+    // new values for two and three, but two will not send updated value
+    assertTrue(two.disconnectSendValue(mqttUri(TOPIC_SEND_TWO)));
+    sendData("203", "303");
+    checkData(3, "102-Post",
+        4, "Pre-202-Post",
+        5, "Pre-303-Post",
+        5, "Pre-303-T-Post");
+    assertEquals("Pre-203", two.getValue());
+
+    // can not disconnect again, and also not for different topic
+    assertFalse(two.disconnectSendValue(mqttUri(TOPIC_SEND_TWO)));
+    assertFalse(two.disconnectSendValue(mqttUri(TOPIC_RECEIVE_TWO)));
+
+    // new values for two and three, but two will neither receive nor send updated value
+    assertTrue(two.disconnectReceiveValue(mqttUri(TOPIC_RECEIVE_TWO)));
+    sendData("204", "304");
+    checkData(3, "102-Post",
+        4, "Pre-202-Post",
+        6, "Pre-304-Post",
+        6, "Pre-304-T-Post");
+    assertEquals("Pre-203", two.getValue());
+
+    // new values for three, but it will not receive updated value, and, thus, not send it either
+    assertTrue(three.disconnectReceiveValue(mqttUri(TOPIC_RECEIVE_THREE_VALUE)));
+    sendData("204", "305");
+    checkData(3, "102-Post",
+        4, "Pre-202-Post",
+        6, "Pre-304-Post",
+        6, "Pre-304-T-Post");
+    assertEquals("Pre-203", two.getValue());
+
+    // disconnect send is possible
+    assertTrue(three.disconnectSendValue(mqttUri(TOPIC_SEND_THREE_VALUE)));
   }
 
   @Override
-  protected void communicateOnlyUpdatedValue() throws InterruptedException {
+  protected void communicateOnlyUpdatedValue() throws InterruptedException, IOException {
     // check initial value
-    TestUtils.waitForMqtt();
     checkData(0, null,
         0, null,
         0, null,
@@ -166,9 +183,6 @@ public class TokenValueSendTest extends AbstractMqttTest {
 
     // send new value
     sendData("210", "310");
-
-    // check new value
-    TestUtils.waitForMqtt();
     checkData(0, null,
         1, "Pre-210-Post",
         1, "Pre-310-Post",
@@ -176,9 +190,6 @@ public class TokenValueSendTest extends AbstractMqttTest {
 
     // set new value
     setData("111", "211", "311");
-
-    // check new value
-    TestUtils.waitForMqtt();
     checkData(1, "111-Post",
         2, "211-Post",
         2, "311-Post",
@@ -186,9 +197,6 @@ public class TokenValueSendTest extends AbstractMqttTest {
 
     // send the same values (will not be sent again)
     setData("111", "211", "311");
-
-    // check new value
-    TestUtils.waitForMqtt();
     checkData(1, "111-Post",
         2, "211-Post",
         2, "311-Post",
@@ -196,9 +204,6 @@ public class TokenValueSendTest extends AbstractMqttTest {
 
     // send values with prefixes imitating receiving
     setData("112", "Pre-212", "Pre-312");
-
-    // check new value
-    TestUtils.waitForMqtt();
     checkData(2, "112-Post",
         3, "Pre-212-Post",
         3, "Pre-312-Post",
@@ -206,13 +211,44 @@ public class TokenValueSendTest extends AbstractMqttTest {
 
     // send the same values (will not be sent again, because previously prefixed)
     sendData("212", "312");
-
-    // check new value
-    TestUtils.waitForMqtt();
     checkData(2, "112-Post",
         3, "Pre-212-Post",
         3, "Pre-312-Post",
         3, "Pre-312-T-Post");
+
+    // new values for two and three, but two will not send updated value
+    assertTrue(two.disconnectSendValue(mqttUri(TOPIC_SEND_TWO)));
+    sendData("213", "313");
+    checkData(2, "112-Post",
+        3, "Pre-212-Post",
+        4, "Pre-313-Post",
+        4, "Pre-313-T-Post");
+    assertEquals("Pre-213", two.getValue());
+
+    // can not disconnect again, and also not for different topic
+    assertFalse(two.disconnectSendValue(mqttUri(TOPIC_SEND_TWO)));
+    assertFalse(two.disconnectSendValue(mqttUri(TOPIC_RECEIVE_TWO)));
+
+    // new values for two and three, but two will neither receive nor send updated value
+    assertTrue(two.disconnectReceiveValue(mqttUri(TOPIC_RECEIVE_TWO)));
+    sendData("214", "314");
+    checkData(2, "112-Post",
+        3, "Pre-212-Post",
+        5, "Pre-314-Post",
+        5, "Pre-314-T-Post");
+    assertEquals("Pre-213", two.getValue());
+
+    // new values for three, but it will not receive updated value, and, thus, not send it either
+    assertTrue(three.disconnectReceiveValue(mqttUri(TOPIC_RECEIVE_THREE_VALUE)));
+    sendData("214", "315");
+    checkData(2, "112-Post",
+        3, "Pre-212-Post",
+        5, "Pre-314-Post",
+        5, "Pre-314-T-Post");
+    assertEquals("Pre-213", two.getValue());
+
+    // disconnect send is possible
+    assertTrue(three.disconnectSendValue(mqttUri(TOPIC_SEND_THREE_VALUE)));
   }
 
   @Override
@@ -240,7 +276,8 @@ public class TokenValueSendTest extends AbstractMqttTest {
                          int numberOfTwoValues, String lastTwoStringValue,
                          int numberOfThreeValues, String lastThreeStringValue,
                          int numberOfOtherValues, String lastOtherStringValue
-  ) {
+  ) throws InterruptedException {
+    TestUtils.waitForMqtt();
     dataOne.assertEqualData(numberOfOneValues, lastOneStringValue);
     dataTwo.assertEqualData(numberOfTwoValues, lastTwoStringValue);
     dataThree.assertEqualData(numberOfThreeValues, lastThreeStringValue);
diff --git a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/ViaTest.java b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/ViaTest.java
index 7ab46f7..ded2990 100644
--- a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/ViaTest.java
+++ b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/ViaTest.java
@@ -43,6 +43,8 @@ public class ViaTest extends AbstractMqttTest {
 
   private static final String REST_SERVER_BASE_URL = "http://localhost:" + REST_PORT + "/";
 
+  private static final String NOT_MAPPED = "<html><body><h2>404 Not found</h2></body></html>";
+
   private MqttHandler handler;
   private A model;
   private ReceiverData dataMqtt2Mqtt;
@@ -121,9 +123,8 @@ public class ViaTest extends AbstractMqttTest {
   }
 
   @Override
-  protected void communicateSendInitialValue() throws InterruptedException {
+  protected void communicateSendInitialValue() throws InterruptedException, IOException {
     // check initial value
-    TestUtils.waitForMqtt();
     checkData(1, "100-M2M-ToMqtt",
         "200-R2R-ToRest",
         "300-M2R-ToRest",
@@ -135,7 +136,6 @@ public class ViaTest extends AbstractMqttTest {
     sendDataForBoth("501", true);
 
     // check new value
-    TestUtils.waitForMqtt();
     checkData(2, "FromMqtt-101-M2M-ToMqtt",
         "FromRest-201-R2R-ToRest",
         "FromMqtt-301-M2R-ToRest",
@@ -147,7 +147,6 @@ public class ViaTest extends AbstractMqttTest {
     sendDataForBoth("502", false);
 
     // check this value
-    TestUtils.waitForMqtt();
     checkData(2, "FromMqtt-101-M2M-ToMqtt",
         "FromRest-201-R2R-ToRest",
         "FromMqtt-301-M2R-ToRest",
@@ -159,7 +158,6 @@ public class ViaTest extends AbstractMqttTest {
     sendDataForBoth("502", true);
 
     // check this value
-    TestUtils.waitForMqtt();
     checkData(2, "FromMqtt-101-M2M-ToMqtt",
         "FromRest-201-R2R-ToRest",
         "FromMqtt-301-M2R-ToRest",
@@ -171,7 +169,6 @@ public class ViaTest extends AbstractMqttTest {
     sendData("102", "202", "302", "402");
 
     // check this value
-    TestUtils.waitForMqtt();
     checkData(3, "FromMqtt-102-M2M-ToMqtt",
         "FromRest-202-R2R-ToRest",
         "FromMqtt-302-M2R-ToRest",
@@ -183,19 +180,57 @@ public class ViaTest extends AbstractMqttTest {
     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 503 over mqtt while disconnected should not change anything
+    assertTrue(model.disconnectBoth2BothInput(mqttUri(TOPIC_BOTH_MQTT_RECEIVE)));
+    sendDataForBoth("503", true);
+    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 504 over rest while still connected should update
+    sendDataForBoth("504", false);
+    checkData(3, "FromMqtt-102-M2M-ToMqtt",
+        "FromRest-202-R2R-ToRest",
+        "FromMqtt-302-M2R-ToRest",
+        3, "FromRest-402-R2M-ToMqtt",
+        4, "504-B2M-ToMqtt",
+        "504-B2R-ToRest");
+
+    // send 505 over rest while also disconnected should not change anything
+    assertTrue(model.disconnectBoth2BothInput(restUri(PATH_BOTH_REST_RECEIVE, REST_PORT)));
+    sendDataForBoth("505", false);
+    checkData(3, "FromMqtt-102-M2M-ToMqtt",
+        "FromRest-202-R2R-ToRest",
+        "FromMqtt-302-M2R-ToRest",
+        3, "FromRest-402-R2M-ToMqtt",
+        4, "504-B2M-ToMqtt",
+        "504-B2R-ToRest");
+
+    // send new values. value over rest while sender disconnected does not provide a value anymore
+    assertTrue(model.disconnectRest2RestOutput(restUri(PATH_REST_2_REST_SEND, REST_PORT)));
+    assertTrue(model.disconnectMqtt2RestOutput(restUri(PATH_MQTT_2_REST_SEND, REST_PORT)));
+    sendData("103", "203", "303", "403");
+    checkData(4, "FromMqtt-103-M2M-ToMqtt",
+        NOT_MAPPED,
+        NOT_MAPPED,
+        4, "FromRest-403-R2M-ToMqtt",
+        4, "504-B2M-ToMqtt",
+        "504-B2R-ToRest");
   }
 
   @Override
-  protected void communicateOnlyUpdatedValue() throws InterruptedException {
+  protected void communicateOnlyUpdatedValue() throws InterruptedException, IOException {
     // check initial value
-    TestUtils.waitForMqtt();
     checkData(0, null,
         "200-R2R-ToRest",
         "300-M2R-ToRest",
@@ -205,9 +240,6 @@ public class ViaTest extends AbstractMqttTest {
 
     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",
@@ -217,9 +249,6 @@ public class ViaTest extends AbstractMqttTest {
 
     // 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",
@@ -229,9 +258,6 @@ public class ViaTest extends AbstractMqttTest {
 
     // 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",
@@ -241,9 +267,6 @@ public class ViaTest extends AbstractMqttTest {
 
     // 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",
@@ -253,15 +276,52 @@ public class ViaTest extends AbstractMqttTest {
 
     // send same values again for other things
     sendData("112", "212", "312", "412");
+    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");
 
-    // check this value
-    TestUtils.waitForMqtt();
+    // send 503 over mqtt while disconnected should not change anything
+    assertTrue(model.disconnectBoth2BothInput(mqttUri(TOPIC_BOTH_MQTT_RECEIVE)));
+    sendDataForBoth("513", true);
     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 514 over rest while still connected should update
+    sendDataForBoth("514", false);
+    checkData(2, "FromMqtt-112-M2M-ToMqtt",
+        "FromRest-212-R2R-ToRest",
+        "FromMqtt-312-M2R-ToRest",
+        2, "FromRest-412-R2M-ToMqtt",
+        3, "514-B2M-ToMqtt",
+        "514-B2R-ToRest");
+
+    // send 515 over rest while also disconnected should not change anything
+    assertTrue(model.disconnectBoth2BothInput(restUri(PATH_BOTH_REST_RECEIVE, REST_PORT)));
+    sendDataForBoth("515", false);
+    checkData(2, "FromMqtt-112-M2M-ToMqtt",
+        "FromRest-212-R2R-ToRest",
+        "FromMqtt-312-M2R-ToRest",
+        2, "FromRest-412-R2M-ToMqtt",
+        3, "514-B2M-ToMqtt",
+        "514-B2R-ToRest");
+
+    // send new values. value over rest while sender disconnected does not provide a value anymore
+    assertTrue(model.disconnectRest2RestOutput(restUri(PATH_REST_2_REST_SEND, REST_PORT)));
+    assertTrue(model.disconnectMqtt2RestOutput(restUri(PATH_MQTT_2_REST_SEND, REST_PORT)));
+    sendData("113", "213", "313", "413");
+    checkData(3, "FromMqtt-113-M2M-ToMqtt",
+        NOT_MAPPED,
+        NOT_MAPPED,
+        3, "FromRest-413-R2M-ToMqtt",
+        3, "514-B2M-ToMqtt",
+        "514-B2R-ToRest");
   }
 
   @Override
@@ -289,7 +349,8 @@ public class ViaTest extends AbstractMqttTest {
     }
   }
 
-  private void checkData(int numberOfMqtt2MqttValues, String mqtt2MqttValue, String rest2RestValue, String mqtt2RestValue, int numberOfRest2MqttValues, String rest2MqttValue, int numberOfBoth2MqttValues, String both2MqttValue, String both2RestValue) {
+  private void checkData(int numberOfMqtt2MqttValues, String mqtt2MqttValue, String rest2RestValue, String mqtt2RestValue, int numberOfRest2MqttValues, String rest2MqttValue, int numberOfBoth2MqttValues, String both2MqttValue, String both2RestValue) throws InterruptedException {
+    TestUtils.waitForMqtt();
     dataMqtt2Mqtt.assertEqualData(numberOfMqtt2MqttValues, mqtt2MqttValue);
     dataRest2Mqtt.assertEqualData(numberOfRest2MqttValues, rest2MqttValue);
     dataBoth2Mqtt.assertEqualData(numberOfBoth2MqttValues, both2MqttValue);
diff --git a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/list/AbstractListTest.java b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/list/AbstractListTest.java
index 18a6e59..4017399 100644
--- a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/list/AbstractListTest.java
+++ b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/list/AbstractListTest.java
@@ -77,7 +77,7 @@ public abstract class AbstractListTest extends AbstractMqttTest {
   }
 
   @Override
-  protected void communicateSendInitialValue() throws InterruptedException {
+  protected void communicateSendInitialValue() throws InterruptedException, IOException {
     checkTree(1, list(), list(0), list(), list(0));
 
     setInput(1);
@@ -91,10 +91,18 @@ public abstract class AbstractListTest extends AbstractMqttTest {
 
     setInput(3);
     checkTree(4, list(1, 2, 3), list(3), list(1, 1, 2, 1, 2, 3), list(0, 1, 2, 3));
+
+    disconnectReceive();
+    setInput(4);
+    checkTree(5, list(1, 2, 3), list(3), list(1, 1, 2, 1, 2, 3), list(0, 1, 2, 3));
+
+    disconnectSend();
+    setInput(5);
+    checkTree(5, list(1, 2, 3), list(3), list(1, 1, 2, 1, 2, 3), list(0, 1, 2, 3));
   }
 
   @Override
-  protected void communicateOnlyUpdatedValue() throws InterruptedException {
+  protected void communicateOnlyUpdatedValue() throws InterruptedException, IOException {
     checkTree(0, list(), list(), list(), list());
 
     setInput(1);
@@ -108,8 +116,20 @@ public abstract class AbstractListTest extends AbstractMqttTest {
 
     setInput(3);
     checkTree(3, list(1, 2, 3), list(3), list(1, 1, 2, 1, 2, 3), list(1, 2, 3));
+
+    disconnectReceive();
+    setInput(4);
+    checkTree(4, list(1, 2, 3), list(3), list(1, 1, 2, 1, 2, 3), list(1, 2, 3));
+
+    disconnectSend();
+    setInput(5);
+    checkTree(4, list(1, 2, 3), list(3), list(1, 1, 2, 1, 2, 3), list(1, 2, 3));
   }
 
+  protected abstract void disconnectReceive() throws IOException;
+
+  protected abstract void disconnectSend() throws IOException;
+
   protected abstract void setInput(int input);
 
   private void checkTree(int expectedTransmissions, IntList normalA, IntList fromSingleA, IntList withAddA, IntList withAddFromSingleA) throws InterruptedException {
diff --git a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/list/ListIncrementalTest.java b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/list/ListIncrementalTest.java
index e5fbae0..ac8b941 100644
--- a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/list/ListIncrementalTest.java
+++ b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/list/ListIncrementalTest.java
@@ -62,16 +62,6 @@ public class ListIncrementalTest extends AbstractListTest {
     assertTrue(senderRoot.connectSingleAList(mqttUri(TOPIC_SINGLE_A), writeCurrentValue));
   }
 
-  @Override
-  protected void closeConnections() {
-    if (handler != null) {
-      handler.close();
-    }
-    if (model != null) {
-      model.ragconnectCloseConnections();
-    }
-  }
-
   @Override
   protected void setInput(int input) {
     senderRoot.setInput(input);
@@ -82,4 +72,29 @@ public class ListIncrementalTest extends AbstractListTest {
     assertEquals(1, senderRoot.getNumSingleA(), "size of single NTA");
     assertEquals(input, senderRoot.getSingleA(0).getID(), "ID value of single A");
   }
+
+  @Override
+  protected void disconnectReceive() throws IOException {
+    ReceiverRoot receiverRootWithLocalType = (ReceiverRoot) receiverRoot;
+    assertTrue(receiverRootWithLocalType.disconnectAList(mqttUri(TOPIC_A)));
+    assertTrue(receiverRootWithLocalType.disconnectFromSingleAList(mqttUri(TOPIC_SINGLE_A)));
+    assertTrue(receiverRootWithLocalType.disconnectWithAddFromAList(mqttUri(TOPIC_A)));
+    assertTrue(receiverRootWithLocalType.disconnectWithAddFromSingleAList(mqttUri(TOPIC_SINGLE_A)));
+  }
+
+  @Override
+  protected void disconnectSend() throws IOException {
+    assertTrue(senderRoot.disconnectAList(mqttUri(TOPIC_A)));
+    assertTrue(senderRoot.disconnectSingleAList(mqttUri(TOPIC_SINGLE_A)));
+  }
+
+  @Override
+  protected void closeConnections() {
+    if (handler != null) {
+      handler.close();
+    }
+    if (model != null) {
+      model.ragconnectCloseConnections();
+    }
+  }
 }
diff --git a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/list/ListManualTest.java b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/list/ListManualTest.java
index f3e6eee..978359f 100644
--- a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/list/ListManualTest.java
+++ b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/list/ListManualTest.java
@@ -72,6 +72,21 @@ public class ListManualTest extends AbstractListTest {
     assertEquals(1, senderRoot.getNumSingleA(), "size of single NTA");
   }
 
+  @Override
+  protected void disconnectReceive() throws IOException {
+    ReceiverRoot receiverRootWithLocalType = (ReceiverRoot) receiverRoot;
+    assertTrue(receiverRootWithLocalType.disconnectAList(mqttUri(TOPIC_A)));
+    assertTrue(receiverRootWithLocalType.disconnectFromSingleAList(mqttUri(TOPIC_SINGLE_A)));
+    assertTrue(receiverRootWithLocalType.disconnectWithAddFromAList(mqttUri(TOPIC_A)));
+    assertTrue(receiverRootWithLocalType.disconnectWithAddFromSingleAList(mqttUri(TOPIC_SINGLE_A)));
+  }
+
+  @Override
+  protected void disconnectSend() throws IOException {
+    assertTrue(senderRoot.disconnectAList(mqttUri(TOPIC_A)));
+    assertTrue(senderRoot.disconnectSingleAList(mqttUri(TOPIC_SINGLE_A)));
+  }
+
   @Override
   protected void closeConnections() {
     if (handler != null) {
diff --git a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/singleList/AbstractSingleListTest.java b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/singleList/AbstractSingleListTest.java
index 3c2779f..7878e67 100644
--- a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/singleList/AbstractSingleListTest.java
+++ b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/singleList/AbstractSingleListTest.java
@@ -30,6 +30,7 @@ import static org.junit.jupiter.api.Assertions.*;
  */
 @Tag("List")
 @Tag("SingleList")
+@Tag("New")
 public abstract class AbstractSingleListTest extends AbstractMqttTest {
 
   public interface TestWrapperJastAddList<T> extends Iterable<T> {
@@ -52,6 +53,11 @@ public abstract class AbstractSingleListTest extends AbstractMqttTest {
     @SuppressWarnings("unused") boolean connectUsingWildcardA(String mqttUri, int index) throws IOException;
     boolean connectWithAddA(String mqttUri) throws IOException;
     boolean connectUsingWildcardWithAddA(String mqttUri) throws IOException;
+
+    boolean disconnectA(String mqttUri) throws IOException;
+    boolean disconnectUsingWildcardA(String mqttUri) throws IOException;
+    boolean disconnectWithAddA(String mqttUri) throws IOException;
+    boolean disconnectUsingWildcardWithAddA(String mqttUri) throws IOException;
   }
   @SuppressWarnings("UnusedReturnValue")
   public interface TestWrapperSenderRoot {
@@ -61,6 +67,12 @@ public abstract class AbstractSingleListTest extends AbstractMqttTest {
     boolean connectA4(String mqttUri, boolean writeCurrentValue) throws IOException;
     boolean connectInOutput(String mqttUri, boolean writeCurrentValue) throws IOException;
 
+    boolean disconnectA1(String mqttUri) throws IOException;
+    boolean disconnectA2(String mqttUri) throws IOException;
+    boolean disconnectA3(String mqttUri) throws IOException;
+    boolean disconnectA4(String mqttUri) throws IOException;
+    boolean disconnectInOutput(String mqttUri) throws IOException;
+
     TestWrapperSenderRoot setInput1(int input);
     TestWrapperSenderRoot setInput2(int input);
     TestWrapperSenderRoot setInput3(int input);
@@ -170,7 +182,7 @@ public abstract class AbstractSingleListTest extends AbstractMqttTest {
   abstract protected void setupReceiverAndConnectPart() throws IOException;
 
   @Override
-  protected void communicateSendInitialValue() throws InterruptedException {
+  protected void communicateSendInitialValue() throws InterruptedException, IOException {
     checkTree(5, list(1, 2, 3, 4, 0), list(4, 3, 2, 1, 0), // normal: (normal / wildcard)
                                      list(4, 3, 2, 1, 0), list(4, 3, 2, 1, 0)); // withAdd: (normal / wildcard)
 
@@ -198,10 +210,22 @@ public abstract class AbstractSingleListTest extends AbstractMqttTest {
     setInput(3, 4);
     checkTree(9, list(3, 2, 7, 4, 5), list(4, 7, 2, 3, 5), // normal: (normal / wildcard)
                                      list(4, 3, 2, 1, 0, 2, 3, 5, 7), list(4, 3, 2, 1, 0, 2, 3, 5, 7)); // withAdd: (normal / wildcard)
+
+    // A2 will be send, but not received
+    disconnectReceive();
+    setInput(2, 5);
+    checkTree(10, list(3, 2, 7, 4, 5), list(4, 7, 2, 3, 5), // normal: (normal / wildcard)
+        list(4, 3, 2, 1, 0, 2, 3, 5, 7), list(4, 3, 2, 1, 0, 2, 3, 5, 7)); // withAdd: (normal / wildcard)
+
+    // A2 will not be send
+    disconnectSend();
+    setInput(2, 7);
+    checkTree(10, list(3, 2, 7, 4, 5), list(4, 7, 2, 3, 5), // normal: (normal / wildcard)
+        list(4, 3, 2, 1, 0, 2, 3, 5, 7), list(4, 3, 2, 1, 0, 2, 3, 5, 7)); // withAdd: (normal / wildcard)
   }
 
   @Override
-  protected void communicateOnlyUpdatedValue() throws InterruptedException {
+  protected void communicateOnlyUpdatedValue() throws InterruptedException, IOException {
     checkTree(0, list(0, 0, 0, 0, 0), list(), // normal
                                      list(), list()); // withAdd
 
@@ -229,6 +253,44 @@ public abstract class AbstractSingleListTest extends AbstractMqttTest {
     setInput(3, 4);
     checkTree(4, list(3, 0, 7, 0, 5), list(3,5,7), // normal
                                      list(2, 3, 5, 7), list(2, 3, 5, 7)); // withAdd
+
+    // A2 will be send, but not received
+    disconnectReceive();
+    setInput(2, 5);
+    checkTree(5, list(3, 0, 7, 0, 5), list(3,5,7), // normal
+        list(2, 3, 5, 7), list(2, 3, 5, 7)); // withAdd
+
+    // A2 will not be send
+    disconnectSend();
+    setInput(2, 7);
+    checkTree(5, list(3, 0, 7, 0, 5), list(3,5,7), // normal
+        list(2, 3, 5, 7), list(2, 3, 5, 7)); // withAdd
+  }
+
+  protected void disconnectReceive() throws IOException {
+    assertTrue(receiverRoot.disconnectA(mqttUri(TOPIC_A_1)));
+    assertTrue(receiverRoot.disconnectA(mqttUri(TOPIC_A_2)));
+    assertTrue(receiverRoot.disconnectA(mqttUri(TOPIC_A_3)));
+    assertTrue(receiverRoot.disconnectA(mqttUri(TOPIC_A_4)));
+    assertTrue(receiverRoot.disconnectA(mqttUri(TOPIC_A_5_INOUT)));
+
+    assertTrue(receiverRoot.disconnectWithAddA(mqttUri(TOPIC_A_1)));
+    assertTrue(receiverRoot.disconnectWithAddA(mqttUri(TOPIC_A_2)));
+    assertTrue(receiverRoot.disconnectWithAddA(mqttUri(TOPIC_A_3)));
+    assertTrue(receiverRoot.disconnectWithAddA(mqttUri(TOPIC_A_4)));
+    assertTrue(receiverRoot.disconnectWithAddA(mqttUri(TOPIC_A_5_INOUT)));
+
+    // receive: wildcard subscription
+    assertTrue(receiverRoot.disconnectUsingWildcardA(mqttUri(TOPIC_A_WILDCARD)));
+    assertTrue(receiverRoot.disconnectUsingWildcardWithAddA(mqttUri(TOPIC_A_WILDCARD)));
+  }
+
+  protected void disconnectSend() throws IOException {
+    assertTrue(senderRoot.disconnectA4(mqttUri(TOPIC_A_4)));
+    assertTrue(senderRoot.disconnectA3(mqttUri(TOPIC_A_3)));
+    assertTrue(senderRoot.disconnectA2(mqttUri(TOPIC_A_2)));
+    assertTrue(senderRoot.disconnectA1(mqttUri(TOPIC_A_1)));
+    assertTrue(senderRoot.disconnectInOutput(mqttUri(TOPIC_A_5_INOUT)));
   }
 
   protected void setInput(int index, int input) {
diff --git a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/singleListVariant/AbstractSingleListVariantTest.java b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/singleListVariant/AbstractSingleListVariantTest.java
index 465ad8a..920a4a4 100644
--- a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/singleListVariant/AbstractSingleListVariantTest.java
+++ b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/singleListVariant/AbstractSingleListVariantTest.java
@@ -52,38 +52,67 @@ public abstract class AbstractSingleListVariantTest extends AbstractMqttTest {
     TestWrapperJastAddList<? extends TestWrapperT_OneOfEach> getOneOfEachWithAddList();
     TestWrapperJastAddList<? extends TestWrapperT_Abstract> getAbstractWithAddList();
 
-    boolean connectT_Empty(String mqttUri) throws IOException;
-    boolean connectT_Token(String mqttUri) throws IOException;
-    boolean connectT_OneChild(String mqttUri) throws IOException;
-    boolean connectT_OneOpt(String mqttUri) throws IOException;
-    boolean connectT_OneList(String mqttUri) throws IOException;
-    boolean connectT_TwoChildren(String mqttUri) throws IOException;
-    boolean connectT_OneOfEach(String mqttUri) throws IOException;
-    boolean connectT_Abstract(String mqttUri) throws IOException;
-
-    boolean connectMyEmpty(String mqttUri) throws IOException;
-
-    boolean connectEmptyWithAdd(String mqttUri) throws IOException;
-    boolean connectTokenWithAdd(String mqttUri) throws IOException;
-    boolean connectOneChildWithAdd(String mqttUri) throws IOException;
-    boolean connectOneOptWithAdd(String mqttUri) throws IOException;
-    boolean connectOneListWithAdd(String mqttUri) throws IOException;
-    boolean connectTwoChildrenWithAdd(String mqttUri) throws IOException;
-    boolean connectOneOfEachWithAdd(String mqttUri) throws IOException;
-    boolean connectAbstractWithAdd(String mqttUri) throws IOException;
+    boolean connectT_Empty(String uriString) throws IOException;
+    boolean connectT_Token(String uriString) throws IOException;
+    boolean connectT_OneChild(String uriString) throws IOException;
+    boolean connectT_OneOpt(String uriString) throws IOException;
+    boolean connectT_OneList(String uriString) throws IOException;
+    boolean connectT_TwoChildren(String uriString) throws IOException;
+    boolean connectT_OneOfEach(String uriString) throws IOException;
+    boolean connectT_Abstract(String uriString) throws IOException;
+
+    boolean connectMyEmpty(String uriString) throws IOException;
+
+    boolean connectEmptyWithAdd(String uriString) throws IOException;
+    boolean connectTokenWithAdd(String uriString) throws IOException;
+    boolean connectOneChildWithAdd(String uriString) throws IOException;
+    boolean connectOneOptWithAdd(String uriString) throws IOException;
+    boolean connectOneListWithAdd(String uriString) throws IOException;
+    boolean connectTwoChildrenWithAdd(String uriString) throws IOException;
+    boolean connectOneOfEachWithAdd(String uriString) throws IOException;
+    boolean connectAbstractWithAdd(String uriString) throws IOException;
+
+    boolean disconnectT_Empty(String uriString) throws IOException;
+    boolean disconnectT_Token(String uriString) throws IOException;
+    boolean disconnectT_OneChild(String uriString) throws IOException;
+    boolean disconnectT_OneOpt(String uriString) throws IOException;
+    boolean disconnectT_OneList(String uriString) throws IOException;
+    boolean disconnectT_TwoChildren(String uriString) throws IOException;
+    boolean disconnectT_OneOfEach(String uriString) throws IOException;
+    boolean disconnectT_Abstract(String uriString) throws IOException;
+
+    boolean disconnectMyEmpty(String uriString) throws IOException;
+
+    boolean disconnectEmptyWithAdd(String uriString) throws IOException;
+    boolean disconnectTokenWithAdd(String uriString) throws IOException;
+    boolean disconnectOneChildWithAdd(String uriString) throws IOException;
+    boolean disconnectOneOptWithAdd(String uriString) throws IOException;
+    boolean disconnectOneListWithAdd(String uriString) throws IOException;
+    boolean disconnectTwoChildrenWithAdd(String uriString) throws IOException;
+    boolean disconnectOneOfEachWithAdd(String uriString) throws IOException;
+    boolean disconnectAbstractWithAdd(String uriString) throws IOException;
   }
-  @SuppressWarnings("UnusedReturnValue")
   public interface TestWrapperSenderRoot {
-    boolean connectT_Empty(String mqttUri, boolean writeCurrentValue) throws IOException;
-    boolean connectT_Token(String mqttUri, boolean writeCurrentValue) throws IOException;
-    boolean connectT_OneChild(String mqttUri, boolean writeCurrentValue) throws IOException;
-    boolean connectT_OneOpt(String mqttUri, boolean writeCurrentValue) throws IOException;
-    boolean connectT_OneList(String mqttUri, boolean writeCurrentValue) throws IOException;
-    boolean connectT_TwoChildren(String mqttUri, boolean writeCurrentValue) throws IOException;
-    boolean connectT_OneOfEach(String mqttUri, boolean writeCurrentValue) throws IOException;
-    boolean connectT_Abstract(String mqttUri, boolean writeCurrentValue) throws IOException;
+    boolean connectT_Empty(String uriString, boolean writeCurrentValue) throws IOException;
+    boolean connectT_Token(String uriString, boolean writeCurrentValue) throws IOException;
+    boolean connectT_OneChild(String uriString, boolean writeCurrentValue) throws IOException;
+    boolean connectT_OneOpt(String uriString, boolean writeCurrentValue) throws IOException;
+    boolean connectT_OneList(String uriString, boolean writeCurrentValue) throws IOException;
+    boolean connectT_TwoChildren(String uriString, boolean writeCurrentValue) throws IOException;
+    boolean connectT_OneOfEach(String uriString, boolean writeCurrentValue) throws IOException;
+    boolean connectT_Abstract(String uriString, boolean writeCurrentValue) throws IOException;
+
+    boolean disconnectT_Empty(String uriString) throws IOException;
+    boolean disconnectT_Token(String uriString) throws IOException;
+    boolean disconnectT_OneChild(String uriString) throws IOException;
+    boolean disconnectT_OneOpt(String uriString) throws IOException;
+    boolean disconnectT_OneList(String uriString) throws IOException;
+    boolean disconnectT_TwoChildren(String uriString) throws IOException;
+    boolean disconnectT_OneOfEach(String uriString) throws IOException;
+    boolean disconnectT_Abstract(String uriString) throws IOException;
 
     TestWrapperSenderRoot setInput(int input);
+    @SuppressWarnings("UnusedReturnValue")
     TestWrapperSenderRoot setShouldSetOptAndList(boolean shouldSetOptAndList);
     TestWrapperT_Empty getT_Empty();
     TestWrapperT_OneOpt getT_OneOpt();
@@ -198,7 +227,7 @@ public abstract class AbstractSingleListVariantTest extends AbstractMqttTest {
   abstract protected void setupReceiverAndConnectPart() throws IOException;
 
   @Override
-  protected void communicateSendInitialValue() throws InterruptedException {
+  protected void communicateSendInitialValue() throws IOException, InterruptedException {
     // transmissions: 8 * 1 = 8
     checkTree(8, list(-0), list(0), list(-0));
 
@@ -225,10 +254,20 @@ public abstract class AbstractSingleListVariantTest extends AbstractMqttTest {
     setInput(5);
     // transmissions: 27 + 8 = 35
     checkTree(35, list(5), list(0, 1, 2, 5), list(-0, -1, 1, 2, 5));
+
+    disconnectReceive();
+    setInput(6); // does not affect receive
+    // transmissions: 35 + 8 = 43
+    checkTree(43, list(5), list(0, 1, 2, 5), list(-0, -1, 1, 2, 5));
+
+    disconnectSend();
+    setInput(7); // not sent
+    // transmissions: 43
+    checkTree(43, list(5), list(0, 1, 2, 5), list(-0, -1, 1, 2, 5));
   }
 
   @Override
-  protected void communicateOnlyUpdatedValue() throws InterruptedException {
+  protected void communicateOnlyUpdatedValue() throws IOException, InterruptedException {
     // transmissions: 0
     checkTree(0, list(), list(), list());
 
@@ -255,6 +294,53 @@ public abstract class AbstractSingleListVariantTest extends AbstractMqttTest {
     setInput(5);
     // transmissions: 19 + 8 = 27
     checkTree(27, list(5), list(1, 2, 5), list(-1, 1, 2, 5));
+
+    disconnectReceive();
+    setInput(6); // does not affect receive
+    // transmissions: 27 + 8 = 35
+    checkTree(35, list(5), list(1, 2, 5), list(-1, 1, 2, 5));
+
+    disconnectSend();
+    setInput(7); // not sent
+    // transmissions: 35
+    checkTree(35, list(5), list(1, 2, 5), list(-1, 1, 2, 5));
+  }
+
+  private void disconnectReceive() throws IOException {
+    // receive: unnamed
+    assertTrue(receiverRoot.disconnectT_Empty(mqttUri(TOPIC_T_Empty)));
+    assertTrue(receiverRoot.disconnectT_Token(mqttUri(TOPIC_T_Token)));
+    assertTrue(receiverRoot.disconnectT_OneChild(mqttUri(TOPIC_T_OneChild)));
+    assertTrue(receiverRoot.disconnectT_OneOpt(mqttUri(TOPIC_T_OneOpt)));
+    assertTrue(receiverRoot.disconnectT_OneList(mqttUri(TOPIC_T_OneList)));
+    assertTrue(receiverRoot.disconnectT_TwoChildren(mqttUri(TOPIC_T_TwoChildren)));
+    assertTrue(receiverRoot.disconnectT_OneOfEach(mqttUri(TOPIC_T_OneOfEach)));
+    assertTrue(receiverRoot.disconnectT_Abstract(mqttUri(TOPIC_T_Abstract)));
+
+    // receive: named
+    assertTrue(receiverRoot.disconnectMyEmpty(mqttUri(TOPIC_T_Empty)));
+
+    // receive: with add
+    assertTrue(receiverRoot.disconnectEmptyWithAdd(mqttUri(TOPIC_T_Empty)));
+    assertTrue(receiverRoot.disconnectTokenWithAdd(mqttUri(TOPIC_T_Token)));
+    assertTrue(receiverRoot.disconnectOneChildWithAdd(mqttUri(TOPIC_T_OneChild)));
+    assertTrue(receiverRoot.disconnectOneOptWithAdd(mqttUri(TOPIC_T_OneOpt)));
+    assertTrue(receiverRoot.disconnectOneListWithAdd(mqttUri(TOPIC_T_OneList)));
+    assertTrue(receiverRoot.disconnectTwoChildrenWithAdd(mqttUri(TOPIC_T_TwoChildren)));
+    assertTrue(receiverRoot.disconnectOneOfEachWithAdd(mqttUri(TOPIC_T_OneOfEach)));
+    assertTrue(receiverRoot.disconnectAbstractWithAdd(mqttUri(TOPIC_T_Abstract)));
+  }
+
+  private void disconnectSend() throws IOException {
+    // send
+    assertTrue(senderRoot.disconnectT_Empty(mqttUri(TOPIC_T_Empty)));
+    assertTrue(senderRoot.disconnectT_Token(mqttUri(TOPIC_T_Token)));
+    assertTrue(senderRoot.disconnectT_OneChild(mqttUri(TOPIC_T_OneChild)));
+    assertTrue(senderRoot.disconnectT_OneOpt(mqttUri(TOPIC_T_OneOpt)));
+    assertTrue(senderRoot.disconnectT_OneList(mqttUri(TOPIC_T_OneList)));
+    assertTrue(senderRoot.disconnectT_TwoChildren(mqttUri(TOPIC_T_TwoChildren)));
+    assertTrue(senderRoot.disconnectT_OneOfEach(mqttUri(TOPIC_T_OneOfEach)));
+    assertTrue(senderRoot.disconnectT_Abstract(mqttUri(TOPIC_T_Abstract)));
   }
 
   protected void setInput(int input) {
diff --git a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/singleListVariant/SingleListVariantIncrementalVariantTest.java b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/singleListVariant/SingleListVariantIncrementalVariantTest.java
index 9a55da3..b3d23e3 100644
--- a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/singleListVariant/SingleListVariantIncrementalVariantTest.java
+++ b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/singleListVariant/SingleListVariantIncrementalVariantTest.java
@@ -16,6 +16,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
  * @author rschoene - Initial contribution
  */
 @Tag("Incremental")
+@Tag("New")
 public class SingleListVariantIncrementalVariantTest extends AbstractSingleListVariantTest {
 
   private Root model;
diff --git a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/tree/AbstractTreeTest.java b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/tree/AbstractTreeTest.java
index ebcccfe..22cc2c0 100644
--- a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/tree/AbstractTreeTest.java
+++ b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/tree/AbstractTreeTest.java
@@ -24,6 +24,7 @@ public abstract class AbstractTreeTest extends AbstractMqttTest {
   public interface TestWrapperReceiverRoot {
     TestWrapperAlfa getAlfa();
     boolean connectAlfa(String mqttUri) throws IOException;
+    boolean disconnectAlfa(String mqttUri) throws IOException;
   }
   public interface TestWrapperAlfa {
     TestWrapperBravo getBravo(int i);
@@ -68,7 +69,7 @@ public abstract class AbstractTreeTest extends AbstractMqttTest {
   }
 
   @Override
-  protected void communicateSendInitialValue() throws InterruptedException {
+  protected void communicateSendInitialValue() throws InterruptedException, IOException {
     checkTree(1, 0);
 
     setInput(1);
@@ -79,10 +80,18 @@ public abstract class AbstractTreeTest extends AbstractMqttTest {
 
     setInput(2);
     checkTree(3, 2);
+
+    disconnectReceive();
+    setInput(1);
+    checkTree(4, 2);
+
+    disconnectSend();
+    setInput(2);
+    checkTree(4, 2);
   }
 
   @Override
-  protected void communicateOnlyUpdatedValue() throws InterruptedException {
+  protected void communicateOnlyUpdatedValue() throws InterruptedException, IOException {
     checkTree(0, null);
 
     setInput(1);
@@ -93,8 +102,20 @@ public abstract class AbstractTreeTest extends AbstractMqttTest {
 
     setInput(2);
     checkTree(2, 2);
+
+    disconnectReceive();
+    setInput(1);
+    checkTree(3, 2);
+
+    disconnectSend();
+    setInput(2);
+    checkTree(3, 2);
   }
 
+  protected abstract void disconnectReceive() throws IOException;
+
+  protected abstract void disconnectSend() throws IOException;
+
   protected abstract void setInput(int input);
 
   protected void checkTree(int expectedCount, Integer expectedInput) throws InterruptedException {
diff --git a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/tree/TreeIncrementalTest.java b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/tree/TreeIncrementalTest.java
index 6722e15..96cbadc 100644
--- a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/tree/TreeIncrementalTest.java
+++ b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/tree/TreeIncrementalTest.java
@@ -60,6 +60,20 @@ public class TreeIncrementalTest extends AbstractTreeTest {
     assertTrue(senderRoot.connectAlfa(mqttUri(TOPIC_ALFA), writeCurrentValue));
   }
 
+  protected void setInput(int input) {
+    senderRoot.setInput(input);
+  }
+
+  @Override
+  protected void disconnectReceive() throws IOException {
+    assertTrue(receiverRoot.disconnectAlfa(mqttUri(TOPIC_ALFA)));
+  }
+
+  @Override
+  protected void disconnectSend() throws IOException {
+    assertTrue(senderRoot.disconnectAlfa(mqttUri(TOPIC_ALFA)));
+  }
+
   @Override
   protected void closeConnections() {
     if (handler != null) {
@@ -69,8 +83,4 @@ public class TreeIncrementalTest extends AbstractTreeTest {
       model.ragconnectCloseConnections();
     }
   }
-
-  protected void setInput(int input) {
-    senderRoot.setInput(input);
-  }
 }
diff --git a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/tree/TreeManualTest.java b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/tree/TreeManualTest.java
index d60e8fa..e557c94 100644
--- a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/tree/TreeManualTest.java
+++ b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/tree/TreeManualTest.java
@@ -62,6 +62,20 @@ public class TreeManualTest extends AbstractTreeTest {
     assertTrue(senderRoot.connectAlfa(mqttUri(TOPIC_ALFA), writeCurrentValue));
   }
 
+  protected void setInput(int input) {
+    senderRoot.setInput(input);
+  }
+
+  @Override
+  protected void disconnectReceive() throws IOException {
+    assertTrue(receiverRoot.disconnectAlfa(mqttUri(TOPIC_ALFA)));
+  }
+
+  @Override
+  protected void disconnectSend() throws IOException {
+    assertTrue(senderRoot.disconnectAlfa(mqttUri(TOPIC_ALFA)));
+  }
+
   @Override
   protected void closeConnections() {
     if (handler != null) {
@@ -71,8 +85,4 @@ public class TreeManualTest extends AbstractTreeTest {
       model.ragconnectCloseConnections();
     }
   }
-
-  protected void setInput(int input) {
-    senderRoot.setInput(input);
-  }
 }
diff --git a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/treeAllowedTokens/AbstractTreeAllowedTokensTest.java b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/treeAllowedTokens/AbstractTreeAllowedTokensTest.java
index 12b2c99..7c29538 100644
--- a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/treeAllowedTokens/AbstractTreeAllowedTokensTest.java
+++ b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/treeAllowedTokens/AbstractTreeAllowedTokensTest.java
@@ -36,6 +36,8 @@ public abstract class AbstractTreeAllowedTokensTest extends AbstractMqttTest {
     TestWrapperAlfa getAlfaPrimitive();
     boolean connectAlfa(String mqttUri) throws IOException;
     boolean connectAlfaPrimitive(String mqttUri) throws IOException;
+    boolean disconnectAlfa(String mqttUri) throws IOException;
+    boolean disconnectAlfaPrimitive(String mqttUri) throws IOException;
   }
   public interface TestWrapperAlfa {
     boolean getBooleanValue();
@@ -51,7 +53,7 @@ public abstract class AbstractTreeAllowedTokensTest extends AbstractMqttTest {
   }
 
   @Override
-  protected void communicateSendInitialValue() throws InterruptedException {
+  protected void communicateSendInitialValue() throws InterruptedException, IOException {
     checkTree(1, false, 0, INSTANT_A, 0);
     checkPrimitiveTree(1, INSTANT_A);
 
@@ -99,10 +101,22 @@ public abstract class AbstractTreeAllowedTokensTest extends AbstractMqttTest {
     sendInput3(5.1);
     checkTree(6, true, 4, INSTANT_B, 5.1);
     checkPrimitiveTree(2, INSTANT_B);
+
+    // sendInput3(7) -> send, but not receive
+    disconnectReceive();
+    sendInput3(7);
+    checkTree(7, true, 4, INSTANT_B, 5.1);
+    checkPrimitiveTree(2, INSTANT_B);
+
+    // sendInput3(8) -> not sent
+    disconnectSend();
+    sendInput3(8);
+    checkTree(7, true, 4, INSTANT_B, 5.1);
+    checkPrimitiveTree(2, INSTANT_B);
   }
 
   @Override
-  protected void communicateOnlyUpdatedValue() throws InterruptedException {
+  protected void communicateOnlyUpdatedValue() throws InterruptedException, IOException {
     checkTree(0, false, null, null, 0);
     checkPrimitiveTree(0, null);
 
@@ -150,8 +164,24 @@ public abstract class AbstractTreeAllowedTokensTest extends AbstractMqttTest {
     sendInput3(15.1);
     checkTree(5, true, 14, INSTANT_C, 15.1);
     checkPrimitiveTree(1, INSTANT_C);
+
+    // sendInput3(7) -> send, but not receive
+    disconnectReceive();
+    sendInput3(7);
+    checkTree(6, true, 14, INSTANT_C, 15.1);
+    checkPrimitiveTree(1, INSTANT_C);
+
+    // sendInput3(8) -> not sent
+    disconnectSend();
+    sendInput3(8);
+    checkTree(6, true, 14, INSTANT_C, 15.1);
+    checkPrimitiveTree(1, INSTANT_C);
   }
 
+  protected abstract void disconnectReceive() throws IOException;
+
+  protected abstract void disconnectSend() throws IOException;
+
   protected void sendInput1WhenFalse(int value) {
     publisher.publish(TOPIC_INPUT1FALSE, TestUtils.DefaultMappings.IntToBytes(value));
   }
diff --git a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/treeAllowedTokens/TreeAllowedTokensIncrementalTest.java b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/treeAllowedTokens/TreeAllowedTokensIncrementalTest.java
index e848ca4..8515650 100644
--- a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/treeAllowedTokens/TreeAllowedTokensIncrementalTest.java
+++ b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/treeAllowedTokens/TreeAllowedTokensIncrementalTest.java
@@ -71,6 +71,31 @@ public class TreeAllowedTokensIncrementalTest extends AbstractTreeAllowedTokensT
     assertTrue(senderRoot.connectAlfaPrimitive(mqttUri(TOPIC_ALFA_PRIMITIVE), writeCurrentValue));
   }
 
+  protected void setFlag(boolean value) {
+    senderRoot.setFlag(value);
+  }
+
+  @Override
+  protected void checkMyEnum(TestWrapperAlfa alfa, boolean expectedBooleanValue) {
+    assertEquals(expectedBooleanValue ? MyEnum.TRUE : MyEnum.FALSE, ((Alfa) alfa).getEnumValue());
+  }
+
+  @Override
+  protected void disconnectReceive() throws IOException {
+    assertTrue(receiverRoot.disconnectAlfa(mqttUri(TOPIC_ALFA)));
+    assertTrue(receiverRoot.disconnectAlfaPrimitive(mqttUri(TOPIC_ALFA_PRIMITIVE)));
+  }
+
+  @Override
+  protected void disconnectSend() throws IOException {
+    assertTrue(senderRoot.disconnectInput1WhenFlagIsFalse(mqttUri(TOPIC_INPUT1FALSE)));
+    assertTrue(senderRoot.disconnectInput1WhenFlagIsTrue(mqttUri(TOPIC_INPUT1TRUE)));
+    assertTrue(senderRoot.disconnectInput2(mqttUri(TOPIC_INPUT2)));
+    assertTrue(senderRoot.disconnectInput3(mqttUri(TOPIC_INPUT3)));
+    assertTrue(senderRoot.disconnectAlfa(mqttUri(TOPIC_ALFA)));
+    assertTrue(senderRoot.disconnectAlfaPrimitive(mqttUri(TOPIC_ALFA_PRIMITIVE)));
+  }
+
   @Override
   protected void closeConnections() {
     if (handler != null) {
@@ -80,13 +105,4 @@ public class TreeAllowedTokensIncrementalTest extends AbstractTreeAllowedTokensT
       model.ragconnectCloseConnections();
     }
   }
-
-  protected void setFlag(boolean value) {
-    senderRoot.setFlag(value);
-  }
-
-  @Override
-  protected void checkMyEnum(TestWrapperAlfa alfa, boolean expectedBooleanValue) {
-    assertEquals(expectedBooleanValue ? MyEnum.TRUE : MyEnum.FALSE, ((Alfa) alfa).getEnumValue());
-  }
 }
diff --git a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/treeAllowedTokens/TreeAllowedTokensManualTest.java b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/treeAllowedTokens/TreeAllowedTokensManualTest.java
index 3903834..8563e0b 100644
--- a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/treeAllowedTokens/TreeAllowedTokensManualTest.java
+++ b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/treeAllowedTokens/TreeAllowedTokensManualTest.java
@@ -73,6 +73,31 @@ public class TreeAllowedTokensManualTest extends AbstractTreeAllowedTokensTest {
     assertTrue(senderRoot.connectAlfaPrimitive(mqttUri(TOPIC_ALFA_PRIMITIVE), writeCurrentValue));
   }
 
+  protected void setFlag(boolean value) {
+    senderRoot.setFlag(value);
+  }
+
+  @Override
+  protected void checkMyEnum(TestWrapperAlfa alfa, boolean expectedBooleanValue) {
+    assertEquals(expectedBooleanValue ? MyEnum.TRUE : MyEnum.FALSE, ((Alfa) alfa).getEnumValue());
+  }
+
+  @Override
+  protected void disconnectReceive() throws IOException {
+    assertTrue(receiverRoot.disconnectAlfa(mqttUri(TOPIC_ALFA)));
+    assertTrue(receiverRoot.disconnectAlfaPrimitive(mqttUri(TOPIC_ALFA_PRIMITIVE)));
+  }
+
+  @Override
+  protected void disconnectSend() throws IOException {
+    assertTrue(senderRoot.disconnectInput1WhenFlagIsFalse(mqttUri(TOPIC_INPUT1FALSE)));
+    assertTrue(senderRoot.disconnectInput1WhenFlagIsTrue(mqttUri(TOPIC_INPUT1TRUE)));
+    assertTrue(senderRoot.disconnectInput2(mqttUri(TOPIC_INPUT2)));
+    assertTrue(senderRoot.disconnectInput3(mqttUri(TOPIC_INPUT3)));
+    assertTrue(senderRoot.disconnectAlfa(mqttUri(TOPIC_ALFA)));
+    assertTrue(senderRoot.disconnectAlfaPrimitive(mqttUri(TOPIC_ALFA_PRIMITIVE)));
+  }
+
   @Override
   protected void closeConnections() {
     if (handler != null) {
@@ -82,13 +107,4 @@ public class TreeAllowedTokensManualTest extends AbstractTreeAllowedTokensTest {
       model.ragconnectCloseConnections();
     }
   }
-
-  protected void setFlag(boolean value) {
-    senderRoot.setFlag(value);
-  }
-
-  @Override
-  protected void checkMyEnum(TestWrapperAlfa alfa, boolean expectedBooleanValue) {
-    assertEquals(expectedBooleanValue ? MyEnum.TRUE : MyEnum.FALSE, ((Alfa) alfa).getEnumValue());
-  }
 }
-- 
GitLab