Reorganize transactionChainHandler usage. 79/84279/8
authorPeterSuna <peter.suna@pantheon.tech>
Mon, 9 Sep 2019 11:45:43 +0000 (13:45 +0200)
committerPeterSuna <peter.suna@pantheon.tech>
Fri, 13 Sep 2019 09:48:04 +0000 (11:48 +0200)
Update transactionChainHandler to conform dependecny injection pattern.
Update DOMtransactionChain to be used multiple time.

JIRA: NETCONF-352
Change-Id: I26da73992ed2772f4f84e2a09788600b808283b3
Signed-off-by: PeterSuna <peter.suna@pantheon.tech>
12 files changed:
restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/handlers/SchemaContextHandler.java
restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/handlers/TransactionChainHandler.java
restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/utils/DeleteDataTransactionUtil.java
restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/utils/FutureCallbackTx.java
restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/utils/PatchDataTransactionUtil.java
restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/utils/PostDataTransactionUtil.java
restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/utils/PutDataTransactionUtil.java
restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/utils/ReadDataTransactionUtil.java
restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/utils/SubscribeToStreamUtil.java
restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/utils/TransactionUtil.java
restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/streams/listeners/AbstractNotificationsData.java
restconf/restconf-nb-rfc8040/src/test/java/org/opendaylight/restconf/nb/rfc8040/handlers/TransactionChainHandlerTest.java [new file with mode: 0644]

index 5483dab8d4167555d1b8980072bedffd3e9a925e..26cc056ed847777f9912491147260c395a46dd0e 100644 (file)
@@ -22,6 +22,7 @@ import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
 import org.opendaylight.mdsal.common.api.TransactionCommitFailedException;
 import org.opendaylight.mdsal.dom.api.DOMDataTreeWriteTransaction;
 import org.opendaylight.mdsal.dom.api.DOMSchemaService;
+import org.opendaylight.mdsal.dom.api.DOMTransactionChain;
 import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
 import org.opendaylight.restconf.nb.rfc8040.Rfc8040.IetfYangLibrary;
 import org.opendaylight.restconf.nb.rfc8040.Rfc8040.MonitoringModule;
@@ -117,7 +118,8 @@ public class SchemaContextHandler implements SchemaContextListenerHandler, AutoC
 
     private void putData(
             final NormalizedNode<NodeIdentifier, Collection<DataContainerChild<? extends PathArgument, ?>>> normNode) {
-        final DOMDataTreeWriteTransaction wTx = this.transactionChainHandler.get().newWriteOnlyTransaction();
+        final DOMTransactionChain transactionChain = this.transactionChainHandler.get();
+        final DOMDataTreeWriteTransaction wTx = transactionChain.newWriteOnlyTransaction();
         wTx.put(LogicalDatastoreType.OPERATIONAL,
                 YangInstanceIdentifier.create(NodeIdentifier.create(normNode.getNodeType())), normNode);
         try {
@@ -136,10 +138,11 @@ public class SchemaContextHandler implements SchemaContextListenerHandler, AutoC
                  * This is workaround for bug https://bugs.opendaylight.org/show_bug.cgi?id=7728
                  */
                 LOG.warn("Ignoring that another cluster node is already putting the same data to DS.", e);
-                this.transactionChainHandler.reset();
             } else {
                 throw new RestconfDocumentedException("Problem occurred while putting data to DS.", failure);
             }
+        } finally {
+            transactionChain.close();
         }
     }
 }
index 0e2df38e5fce0038081d037e7d66cc606ea2d1de..28830dd3417e2ef9afb936ea7a9307b7bbe8b989 100644 (file)
@@ -8,6 +8,8 @@
 package org.opendaylight.restconf.nb.rfc8040.handlers;
 
 import java.util.Objects;
+import java.util.Queue;
+import java.util.concurrent.ConcurrentLinkedQueue;
 import javax.annotation.PreDestroy;
 import javax.inject.Inject;
 import javax.inject.Singleton;
@@ -31,19 +33,19 @@ public class TransactionChainHandler implements Handler<DOMTransactionChain>, Au
         public void onTransactionChainFailed(final DOMTransactionChain chain, final DOMDataTreeTransaction transaction,
                 final Throwable cause) {
             LOG.warn("TransactionChain({}) {} FAILED!", chain, transaction.getIdentifier(), cause);
-            reset();
+            transactionChainList.remove(chain);
             throw new RestconfDocumentedException("TransactionChain(" + chain + ") not committed correctly", cause);
         }
 
         @Override
         public void onTransactionChainSuccessful(final DOMTransactionChain chain) {
             LOG.trace("TransactionChain({}) SUCCESSFUL", chain);
+            transactionChainList.remove(chain);
         }
     };
 
     private final DOMDataBroker dataBroker;
-
-    private volatile DOMTransactionChain transactionChain;
+    private final Queue<DOMTransactionChain> transactionChainList;
 
     /**
      * Prepare transaction chain service for Restconf services.
@@ -51,23 +53,37 @@ public class TransactionChainHandler implements Handler<DOMTransactionChain>, Au
     @Inject
     public TransactionChainHandler(final DOMDataBroker dataBroker) {
         this.dataBroker = Objects.requireNonNull(dataBroker);
-        transactionChain = Objects.requireNonNull(dataBroker.createTransactionChain(transactionChainListener));
+        this.transactionChainList = new ConcurrentLinkedQueue<>();
     }
 
+    /**
+     * Create and return new instance of object {@link DOMTransactionChain}.
+     * After use, is important to close transactionChain by method {@link DOMTransactionChain#close()}.
+     * @return new instance of object {@link DOMTransactionChain}
+     */
     @Override
     public DOMTransactionChain get() {
-        return this.transactionChain;
-    }
-
-    public synchronized void reset() {
-        LOG.trace("Resetting TransactionChain({})", transactionChain);
-        transactionChain.close();
-        transactionChain = dataBroker.createTransactionChain(transactionChainListener);
+        final DOMTransactionChain transactionChain = dataBroker.createTransactionChain(transactionChainListener);
+        this.transactionChainList.add(transactionChain);
+        LOG.trace("Started TransactionChain({})", transactionChain);
+        return transactionChain;
     }
 
     @Override
     @PreDestroy
     public synchronized void close() {
-        transactionChain.close();
+        for (DOMTransactionChain transactionChain: this.transactionChainList) {
+            transactionChain.close();
+            LOG.trace("Closed TransactionChain({})", transactionChain);
+        }
+    }
+
+    /**
+     * Verify if {@link DOMTransactionChain} exist in {@link TransactionChainHandler} queue.
+     * @param transactionChain object to check.
+     * @return true if object still exist in {@link TransactionChainHandler}.
+     */
+    boolean verifyIfExistTransactionChain(DOMTransactionChain transactionChain) {
+        return this.transactionChainList.contains(transactionChain);
     }
 }
index 54e3964bd9de4afa66baea0a046785da944b3ce6..201166185b5b440849d68ed3bf9ac2aa340aec59 100644 (file)
@@ -13,7 +13,7 @@ import javax.ws.rs.core.Response.Status;
 import org.opendaylight.mdsal.common.api.CommitInfo;
 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
 import org.opendaylight.mdsal.dom.api.DOMDataTreeReadWriteTransaction;
-import org.opendaylight.restconf.nb.rfc8040.handlers.TransactionChainHandler;
+import org.opendaylight.mdsal.dom.api.DOMTransactionChain;
 import org.opendaylight.restconf.nb.rfc8040.rests.transactions.TransactionVarsWrapper;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
 
@@ -35,28 +35,29 @@ public final class DeleteDataTransactionUtil {
      * @return {@link Response}
      */
     public static Response deleteData(final TransactionVarsWrapper transactionNode) {
-        final FluentFuture<? extends CommitInfo> future = submitData(transactionNode.getTransactionChainHandler(),
+        final DOMTransactionChain transactionChain = transactionNode.getTransactionChainHandler().get();
+        final FluentFuture<? extends CommitInfo> future = submitData(transactionChain,
                 transactionNode.getInstanceIdentifier().getInstanceIdentifier());
         final ResponseFactory response = new ResponseFactory(Status.NO_CONTENT);
-        FutureCallbackTx.addCallback(future, RestconfDataServiceConstant.DeleteData.DELETE_TX_TYPE, response);
+        //This method will close transactionChain
+        FutureCallbackTx.addCallback(future, RestconfDataServiceConstant.DeleteData.DELETE_TX_TYPE, response,
+                transactionChain);
         return response.build();
     }
 
     /**
      * Delete data via transaction. Return error if data to delete does not exist.
      *
-     * @param transactionChainHandler
-     *             transaction chain handler
-     * @param readWriteTx
-     *             read and write transaction
+     * @param transactionChain
+     *             transaction chain
      * @param path
      *             path of data to delete
      * @return {@link FluentFuture}
      */
     private static FluentFuture<? extends CommitInfo> submitData(
-            final TransactionChainHandler transactionChainHandler, final YangInstanceIdentifier path) {
-        final DOMDataTreeReadWriteTransaction readWriteTx = transactionChainHandler.get().newReadWriteTransaction();
-        TransactionUtil.checkItemExists(transactionChainHandler, readWriteTx, LogicalDatastoreType.CONFIGURATION, path,
+            final DOMTransactionChain transactionChain, final YangInstanceIdentifier path) {
+        final DOMDataTreeReadWriteTransaction readWriteTx = transactionChain.newReadWriteTransaction();
+        TransactionUtil.checkItemExists(transactionChain, readWriteTx, LogicalDatastoreType.CONFIGURATION, path,
                 RestconfDataServiceConstant.DeleteData.DELETE_TX_TYPE);
         readWriteTx.delete(LogicalDatastoreType.CONFIGURATION, path);
         return readWriteTx.commit();
index 58178b6c41efcde7af175f4d08fdf0e7f5816776..bd6d94132e8eea1f67972e3c841a413193af4ef6 100644 (file)
@@ -12,9 +12,11 @@ import com.google.common.collect.ImmutableList;
 import com.google.common.util.concurrent.ListenableFuture;
 import java.util.List;
 import java.util.concurrent.ExecutionException;
+import org.eclipse.jdt.annotation.Nullable;
 import org.opendaylight.mdsal.common.api.TransactionCommitFailedException;
 import org.opendaylight.mdsal.dom.api.DOMActionException;
 import org.opendaylight.mdsal.dom.api.DOMRpcException;
+import org.opendaylight.mdsal.dom.api.DOMTransactionChain;
 import org.opendaylight.mdsal.dom.spi.DefaultDOMRpcResult;
 import org.opendaylight.mdsal.dom.spi.SimpleDOMActionResult;
 import org.opendaylight.netconf.api.NetconfDocumentedException;
@@ -50,7 +52,28 @@ final class FutureCallbackTx {
      */
     @SuppressWarnings("checkstyle:IllegalCatch")
     static <T> void addCallback(final ListenableFuture<T> listenableFuture, final String txType,
-            final FutureDataFactory<? super T> dataFactory) throws RestconfDocumentedException {
+                                final FutureDataFactory<? super T> dataFactory) throws RestconfDocumentedException {
+        addCallback(listenableFuture,txType,dataFactory,null);
+    }
+
+    /**
+     * Add callback to the future object and close transaction chain.
+     *
+     * @param listenableFuture
+     *             future object
+     * @param txType
+     *             type of operation (READ, POST, PUT, DELETE)
+     * @param dataFactory
+     *             factory setting result
+     * @param transactionChain
+     *             transaction chain
+     * @throws RestconfDocumentedException
+     *             if the Future throws an exception
+     */
+    @SuppressWarnings("checkstyle:IllegalCatch")
+    static <T> void addCallback(final ListenableFuture<T> listenableFuture, final String txType,
+            final FutureDataFactory<? super T> dataFactory, @Nullable final DOMTransactionChain transactionChain)
+            throws RestconfDocumentedException {
 
         try {
             final T result = listenableFuture.get();
@@ -92,6 +115,10 @@ final class FutureCallbackTx {
             } else {
                 throw new RestconfDocumentedException("Transaction failed", e);
             }
+        } finally {
+            if (transactionChain != null) {
+                transactionChain.close();
+            }
         }
     }
 }
index 6a47e7051bed4e3c83c922c969724e7615c01f99..d92f143a1458c20d58731e5f56250185229c8a30 100644 (file)
@@ -18,6 +18,7 @@ import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
 import org.opendaylight.mdsal.dom.api.DOMDataTreeReadOperations;
 import org.opendaylight.mdsal.dom.api.DOMDataTreeReadWriteTransaction;
 import org.opendaylight.mdsal.dom.api.DOMDataTreeWriteTransaction;
+import org.opendaylight.mdsal.dom.api.DOMTransactionChain;
 import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
 import org.opendaylight.restconf.common.errors.RestconfError;
 import org.opendaylight.restconf.common.errors.RestconfError.ErrorTag;
@@ -46,7 +47,8 @@ public final class PatchDataTransactionUtil {
     }
 
     /**
-     * Process edit operations of one {@link PatchContext}.
+     * Process edit operations of one {@link PatchContext}. Close {@link DOMTransactionChain} inside of object
+     * {@link TransactionVarsWrapper} provided as a parameter.
      * @param context Patch context to be processed
      * @param transactionNode Wrapper for transaction
      * @param schemaContextRef Soft reference for global schema context
@@ -56,7 +58,8 @@ public final class PatchDataTransactionUtil {
                                                final SchemaContextRef schemaContextRef) {
         final List<PatchStatusEntity> editCollection = new ArrayList<>();
         boolean noError = true;
-        final DOMDataTreeReadWriteTransaction tx = transactionNode.getTransactionChain().newReadWriteTransaction();
+        final DOMTransactionChain transactionChain = transactionNode.getTransactionChain();
+        final DOMDataTreeReadWriteTransaction tx = transactionChain.newReadWriteTransaction();
 
         for (final PatchEntity patchEntity : context.getData()) {
             if (noError) {
@@ -134,7 +137,8 @@ public final class PatchDataTransactionUtil {
             final FluentFuture<? extends CommitInfo> future = tx.commit();
 
             try {
-                FutureCallbackTx.addCallback(future, PatchData.PATCH_TX_TYPE, response);
+                //This method will close transactionChain
+                FutureCallbackTx.addCallback(future, PatchData.PATCH_TX_TYPE, response, transactionChain);
             } catch (final RestconfDocumentedException e) {
                 // if errors occurred during transaction commit then patch failed and global errors are reported
                 return new PatchStatusContext(context.getPatchId(), ImmutableList.copyOf(editCollection), false,
@@ -144,7 +148,7 @@ public final class PatchDataTransactionUtil {
             return new PatchStatusContext(context.getPatchId(), ImmutableList.copyOf(editCollection), true, null);
         } else {
             tx.cancel();
-            transactionNode.getTransactionChainHandler().reset();
+            transactionChain.close();
             return new PatchStatusContext(context.getPatchId(), ImmutableList.copyOf(editCollection),
                     false, null);
         }
@@ -155,7 +159,7 @@ public final class PatchDataTransactionUtil {
      * @param dataStore Datastore to write data to
      * @param path Path for data to be created
      * @param payload Data to be created
-     * @param rWTransaction Transaction
+     * @param rwTransaction Transaction
      * @param schemaContextRef Soft reference for global schema context
      */
     private static void createDataWithinTransaction(final LogicalDatastoreType dataStore,
index 937a6361a0b31e583c63c98398083006f8958997..f1f395ba40e9948e1a0d6c9891655880382f1ad7 100644 (file)
@@ -22,7 +22,6 @@ import org.opendaylight.restconf.common.context.InstanceIdentifierContext;
 import org.opendaylight.restconf.common.context.NormalizedNodeContext;
 import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
 import org.opendaylight.restconf.common.errors.RestconfError;
-import org.opendaylight.restconf.nb.rfc8040.handlers.TransactionChainHandler;
 import org.opendaylight.restconf.nb.rfc8040.references.SchemaContextRef;
 import org.opendaylight.restconf.nb.rfc8040.rests.transactions.TransactionVarsWrapper;
 import org.opendaylight.restconf.nb.rfc8040.utils.parser.ParserIdentifier;
@@ -48,7 +47,8 @@ public final class PostDataTransactionUtil {
     }
 
     /**
-     * Check mount point and prepare variables for post data.
+     * Check mount point and prepare variables for post data. Close {@link DOMTransactionChain} inside of object
+     * {@link TransactionVarsWrapper} provided as a parameter.
      *
      * @param uriInfo
      *
@@ -72,7 +72,9 @@ public final class PostDataTransactionUtil {
                 transactionNode, schemaContextRef.get(), insert, point);
         final URI location = PostDataTransactionUtil.resolveLocation(uriInfo, transactionNode, schemaContextRef);
         final ResponseFactory dataFactory = new ResponseFactory(Status.CREATED).location(location);
-        FutureCallbackTx.addCallback(future, RestconfDataServiceConstant.PostData.POST_TX_TYPE, dataFactory);
+        //This method will close transactionChain
+        FutureCallbackTx.addCallback(future, RestconfDataServiceConstant.PostData.POST_TX_TYPE, dataFactory,
+                transactionNode.getTransactionChain());
         return dataFactory.build();
     }
 
@@ -96,10 +98,10 @@ public final class PostDataTransactionUtil {
     private static FluentFuture<? extends CommitInfo> submitData(final YangInstanceIdentifier path,
             final NormalizedNode<?, ?> data, final TransactionVarsWrapper transactionNode,
             final SchemaContext schemaContext, final String insert, final String point) {
-        final DOMTransactionChain domTransactionChain = transactionNode.getTransactionChain();
-        final DOMDataTreeReadWriteTransaction newReadWriteTransaction = domTransactionChain.newReadWriteTransaction();
+        final DOMTransactionChain transactionChain = transactionNode.getTransactionChain();
+        final DOMDataTreeReadWriteTransaction newReadWriteTransaction = transactionChain.newReadWriteTransaction();
         if (insert == null) {
-            makePost(path, data, schemaContext, transactionNode.getTransactionChainHandler(), newReadWriteTransaction);
+            makePost(path, data, schemaContext, transactionChain, newReadWriteTransaction);
             return newReadWriteTransaction.commit();
         } else {
             final DataSchemaNode schemaNode = PutDataTransactionUtil.checkListAndOrderedType(schemaContext, path);
@@ -110,15 +112,15 @@ public final class PostDataTransactionUtil {
                             schemaContext, transactionNode.getTransactionChainHandler(), schemaNode);
                         final OrderedMapNode readList = (OrderedMapNode) readData;
                         if (readList == null || readList.getValue().isEmpty()) {
-                            makePost(path, data, schemaContext, transactionNode.getTransactionChainHandler(),
+                            makePost(path, data, schemaContext, transactionChain,
                                     newReadWriteTransaction);
                             return newReadWriteTransaction.commit();
                         } else {
                             newReadWriteTransaction.delete(LogicalDatastoreType.CONFIGURATION,
                                     path.getParent().getParent());
                             simplePost(newReadWriteTransaction, LogicalDatastoreType.CONFIGURATION, path, data,
-                                    schemaContext, transactionNode.getTransactionChainHandler());
-                            makePost(path, readData, schemaContext, transactionNode.getTransactionChainHandler(),
+                                    schemaContext, transactionChain);
+                            makePost(path, readData, schemaContext, transactionChain,
                                     newReadWriteTransaction);
                             return newReadWriteTransaction.commit();
                         }
@@ -128,21 +130,21 @@ public final class PostDataTransactionUtil {
 
                         final OrderedLeafSetNode<?> readLeafList = (OrderedLeafSetNode<?>) readData;
                         if (readLeafList == null || readLeafList.getValue().isEmpty()) {
-                            makePost(path, data, schemaContext, transactionNode.getTransactionChainHandler(),
+                            makePost(path, data, schemaContext, transactionChain,
                                     newReadWriteTransaction);
                             return newReadWriteTransaction.commit();
                         } else {
                             newReadWriteTransaction.delete(LogicalDatastoreType.CONFIGURATION,
                                     path.getParent().getParent());
                             simplePost(newReadWriteTransaction, LogicalDatastoreType.CONFIGURATION, path, data,
-                                    schemaContext, transactionNode.getTransactionChainHandler());
-                            makePost(path, readData, schemaContext, transactionNode.getTransactionChainHandler(),
+                                    schemaContext, transactionChain);
+                            makePost(path, readData, schemaContext, transactionChain,
                                     newReadWriteTransaction);
                             return newReadWriteTransaction.commit();
                         }
                     }
                 case "last":
-                    makePost(path, data, schemaContext, transactionNode.getTransactionChainHandler(),
+                    makePost(path, data, schemaContext, transactionChain,
                             newReadWriteTransaction);
                     return newReadWriteTransaction.commit();
                 case "before":
@@ -151,13 +153,13 @@ public final class PostDataTransactionUtil {
                             schemaContext, transactionNode.getTransactionChainHandler(), schemaNode);
                         final OrderedMapNode readList = (OrderedMapNode) readData;
                         if (readList == null || readList.getValue().isEmpty()) {
-                            makePost(path, data, schemaContext, transactionNode.getTransactionChainHandler(),
+                            makePost(path, data, schemaContext, transactionChain,
                                     newReadWriteTransaction);
                             return newReadWriteTransaction.commit();
                         } else {
                             insertWithPointListPost(newReadWriteTransaction, LogicalDatastoreType.CONFIGURATION, path,
                                     data, schemaContext, point, readList, true,
-                                    transactionNode.getTransactionChainHandler());
+                                    transactionChain);
                             return newReadWriteTransaction.commit();
                         }
                     } else {
@@ -166,13 +168,13 @@ public final class PostDataTransactionUtil {
 
                         final OrderedLeafSetNode<?> readLeafList = (OrderedLeafSetNode<?>) readData;
                         if (readLeafList == null || readLeafList.getValue().isEmpty()) {
-                            makePost(path, data, schemaContext, transactionNode.getTransactionChainHandler(),
+                            makePost(path, data, schemaContext, transactionChain,
                                     newReadWriteTransaction);
                             return newReadWriteTransaction.commit();
                         } else {
                             insertWithPointLeafListPost(newReadWriteTransaction, LogicalDatastoreType.CONFIGURATION,
                                     path, data, schemaContext, point, readLeafList, true,
-                                    transactionNode.getTransactionChainHandler());
+                                    transactionChain);
                             return newReadWriteTransaction.commit();
                         }
                     }
@@ -182,13 +184,13 @@ public final class PostDataTransactionUtil {
                             schemaContext, transactionNode.getTransactionChainHandler(), schemaNode);
                         final OrderedMapNode readList = (OrderedMapNode) readData;
                         if (readList == null || readList.getValue().isEmpty()) {
-                            makePost(path, data, schemaContext, transactionNode.getTransactionChainHandler(),
+                            makePost(path, data, schemaContext, transactionChain,
                                     newReadWriteTransaction);
                             return newReadWriteTransaction.commit();
                         } else {
                             insertWithPointListPost(newReadWriteTransaction, LogicalDatastoreType.CONFIGURATION, path,
                                     data, schemaContext, point, readList, false,
-                                    transactionNode.getTransactionChainHandler());
+                                    transactionChain);
                             return newReadWriteTransaction.commit();
                         }
                     } else {
@@ -197,13 +199,13 @@ public final class PostDataTransactionUtil {
 
                         final OrderedLeafSetNode<?> readLeafList = (OrderedLeafSetNode<?>) readData;
                         if (readLeafList == null || readLeafList.getValue().isEmpty()) {
-                            makePost(path, data, schemaContext, transactionNode.getTransactionChainHandler(),
+                            makePost(path, data, schemaContext, transactionChain,
                                     newReadWriteTransaction);
                             return newReadWriteTransaction.commit();
                         } else {
                             insertWithPointLeafListPost(newReadWriteTransaction, LogicalDatastoreType.CONFIGURATION,
                                     path, data, schemaContext, point, readLeafList, true,
-                                    transactionNode.getTransactionChainHandler());
+                                    transactionChain);
                             return newReadWriteTransaction.commit();
                         }
                     }
@@ -219,7 +221,7 @@ public final class PostDataTransactionUtil {
     private static void insertWithPointLeafListPost(final DOMDataTreeReadWriteTransaction rwTransaction,
             final LogicalDatastoreType datastore, final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload,
             final SchemaContext schemaContext, final String point, final OrderedLeafSetNode<?> readLeafList,
-            final boolean before, final TransactionChainHandler transactionChainHandler) {
+            final boolean before, final DOMTransactionChain transactionChain) {
         rwTransaction.delete(datastore, path.getParent().getParent());
         final InstanceIdentifierContext<?> instanceIdentifier =
                 ParserIdentifier.toInstanceIdentifier(point, schemaContext, Optional.empty());
@@ -239,12 +241,12 @@ public final class PostDataTransactionUtil {
         rwTransaction.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
         for (final LeafSetEntryNode<?> nodeChild : readLeafList.getValue()) {
             if (lastInsertedPosition == lastItemPosition) {
-                TransactionUtil.checkItemDoesNotExists(transactionChainHandler, rwTransaction, datastore, path,
+                TransactionUtil.checkItemDoesNotExists(transactionChain, rwTransaction, datastore, path,
                         RestconfDataServiceConstant.PostData.POST_TX_TYPE);
                 rwTransaction.put(datastore, path, payload);
             }
             final YangInstanceIdentifier childPath = path.getParent().getParent().node(nodeChild.getIdentifier());
-            TransactionUtil.checkItemDoesNotExists(transactionChainHandler, rwTransaction, datastore, childPath,
+            TransactionUtil.checkItemDoesNotExists(transactionChain, rwTransaction, datastore, childPath,
                     RestconfDataServiceConstant.PostData.POST_TX_TYPE);
             rwTransaction.put(datastore, childPath, nodeChild);
             lastInsertedPosition++;
@@ -254,7 +256,7 @@ public final class PostDataTransactionUtil {
     private static void insertWithPointListPost(final DOMDataTreeReadWriteTransaction rwTransaction,
             final LogicalDatastoreType datastore, final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload,
             final SchemaContext schemaContext, final String point, final MapNode readList, final boolean before,
-            final TransactionChainHandler transactionChainHandler) {
+            final DOMTransactionChain transactionChain) {
         rwTransaction.delete(datastore, path.getParent().getParent());
         final InstanceIdentifierContext<?> instanceIdentifier =
                 ParserIdentifier.toInstanceIdentifier(point, schemaContext, Optional.empty());
@@ -274,12 +276,12 @@ public final class PostDataTransactionUtil {
         rwTransaction.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
         for (final MapEntryNode mapEntryNode : readList.getValue()) {
             if (lastInsertedPosition == lastItemPosition) {
-                TransactionUtil.checkItemDoesNotExists(transactionChainHandler, rwTransaction, datastore, path,
+                TransactionUtil.checkItemDoesNotExists(transactionChain, rwTransaction, datastore, path,
                         RestconfDataServiceConstant.PostData.POST_TX_TYPE);
                 rwTransaction.put(datastore, path, payload);
             }
             final YangInstanceIdentifier childPath = path.getParent().getParent().node(mapEntryNode.getIdentifier());
-            TransactionUtil.checkItemDoesNotExists(transactionChainHandler, rwTransaction, datastore, childPath,
+            TransactionUtil.checkItemDoesNotExists(transactionChain, rwTransaction, datastore, childPath,
                     RestconfDataServiceConstant.PostData.POST_TX_TYPE);
             rwTransaction.put(datastore, childPath, mapEntryNode);
             lastInsertedPosition++;
@@ -287,14 +289,14 @@ public final class PostDataTransactionUtil {
     }
 
     private static void makePost(final YangInstanceIdentifier path, final NormalizedNode<?, ?> data,
-            final SchemaContext schemaContext, final TransactionChainHandler transactionChainHandler,
+            final SchemaContext schemaContext, final DOMTransactionChain transactionChain,
             final DOMDataTreeReadWriteTransaction transaction) {
         if (data instanceof MapNode) {
             boolean merge = false;
             for (final MapEntryNode child : ((MapNode) data).getValue()) {
                 final YangInstanceIdentifier childPath = path.node(child.getIdentifier());
                 TransactionUtil.checkItemDoesNotExists(
-                        transactionChainHandler, transaction, LogicalDatastoreType.CONFIGURATION, childPath,
+                        transactionChain, transaction, LogicalDatastoreType.CONFIGURATION, childPath,
                         RestconfDataServiceConstant.PostData.POST_TX_TYPE);
                 if (!merge) {
                     merge = true;
@@ -307,7 +309,7 @@ public final class PostDataTransactionUtil {
             }
         } else {
             TransactionUtil.checkItemDoesNotExists(
-                    transactionChainHandler, transaction, LogicalDatastoreType.CONFIGURATION, path,
+                    transactionChain, transaction, LogicalDatastoreType.CONFIGURATION, path,
                     RestconfDataServiceConstant.PostData.POST_TX_TYPE);
 
             TransactionUtil.ensureParentsByMerge(path, schemaContext, transaction);
@@ -343,8 +345,8 @@ public final class PostDataTransactionUtil {
 
     private static void simplePost(final DOMDataTreeReadWriteTransaction rwTransaction,
             final LogicalDatastoreType datastore, final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload,
-            final SchemaContext schemaContext, final TransactionChainHandler transactionChainHandler) {
-        TransactionUtil.checkItemDoesNotExists(transactionChainHandler, rwTransaction, datastore, path,
+            final SchemaContext schemaContext, final DOMTransactionChain transactionChain) {
+        TransactionUtil.checkItemDoesNotExists(transactionChain, rwTransaction, datastore, path,
                 RestconfDataServiceConstant.PostData.POST_TX_TYPE);
         TransactionUtil.ensureParentsByMerge(path, schemaContext, rwTransaction);
         rwTransaction.put(datastore, path, payload);
index 97f04909473ae89e41df6d192d8a7574155c9a16..72ee864ad3e2d25fc79a6102c519c68b5d1a0001 100644 (file)
@@ -18,6 +18,7 @@ import org.opendaylight.mdsal.common.api.CommitInfo;
 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
 import org.opendaylight.mdsal.dom.api.DOMDataTreeReadWriteTransaction;
 import org.opendaylight.mdsal.dom.api.DOMDataTreeWriteTransaction;
+import org.opendaylight.mdsal.dom.api.DOMTransactionChain;
 import org.opendaylight.restconf.common.context.InstanceIdentifierContext;
 import org.opendaylight.restconf.common.context.NormalizedNodeContext;
 import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
@@ -140,7 +141,8 @@ public final class PutDataTransactionUtil {
     }
 
     /**
-     * Check mount point and prepare variables for put data to DS.
+     * Check mount point and prepare variables for put data to DS. Close {@link DOMTransactionChain} inside of object
+     * {@link TransactionVarsWrapper} provided as a parameter.
      *
      * @param payload
      *             data to put
@@ -171,7 +173,9 @@ public final class PutDataTransactionUtil {
                 new ResponseFactory(existsResponse.result ? Status.NO_CONTENT : Status.CREATED);
         final FluentFuture<? extends CommitInfo> submitData = submitData(path, schemaContext,
                 transactionNode.getTransactionChainHandler(), readWriteTransaction, payload.getData(), insert, point);
-        FutureCallbackTx.addCallback(submitData, RestconfDataServiceConstant.PutData.PUT_TX_TYPE, responseFactory);
+        //This method will close transactionChain
+        FutureCallbackTx.addCallback(submitData, RestconfDataServiceConstant.PutData.PUT_TX_TYPE, responseFactory,
+                transactionNode.getTransactionChain());
         return responseFactory.build();
     }
 
index 509562bb284fa8df2b2cc26ac859880212f1d869..9f2c74dcb859ab668c463684b5c5c530825d00a7 100644 (file)
@@ -26,6 +26,7 @@ import org.eclipse.jdt.annotation.Nullable;
 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
 import org.opendaylight.mdsal.dom.api.DOMDataTreeReadTransaction;
 import org.opendaylight.mdsal.dom.api.DOMDataTreeReadWriteTransaction;
+import org.opendaylight.mdsal.dom.api.DOMTransactionChain;
 import org.opendaylight.restconf.common.context.InstanceIdentifierContext;
 import org.opendaylight.restconf.common.context.WriterParameters;
 import org.opendaylight.restconf.common.context.WriterParameters.WriterParametersBuilder;
@@ -203,7 +204,8 @@ public final class ReadDataTransactionUtil {
     }
 
     /**
-     * Read specific type of data from data store via transaction.
+     * Read specific type of data from data store via transaction. Close {@link DOMTransactionChain} inside of object
+     * {@link TransactionVarsWrapper} provided as a parameter.
      *
      * @param valueOfContent
      *            type of data to read (config, state, all)
@@ -234,6 +236,7 @@ public final class ReadDataTransactionUtil {
                 return readAllData(transactionNode, withDefa, ctx);
 
             default:
+                transactionNode.getTransactionChain().close();
                 throw new RestconfDocumentedException(
                         new RestconfError(RestconfError.ErrorType.PROTOCOL, RestconfError.ErrorTag.INVALID_VALUE,
                                 "Invalid content parameter: " + valueOfContent, null,
@@ -425,7 +428,9 @@ public final class ReadDataTransactionUtil {
     /**
      * If is set specific {@link LogicalDatastoreType} in
      * {@link TransactionVarsWrapper}, then read this type of data from DS. If
-     * don't, we have to read all data from DS (state + config)
+     * don't, we have to read all data from DS (state + config).
+     * This method will close {@link org.opendaylight.mdsal.dom.api.DOMTransactionChain} inside of
+     * {@link TransactionVarsWrapper}.
      *
      * @param transactionNode
      *             {@link TransactionVarsWrapper} - wrapper for variables
@@ -433,19 +438,43 @@ public final class ReadDataTransactionUtil {
      */
     private static @Nullable NormalizedNode<?, ?> readDataViaTransaction(
             final @NonNull TransactionVarsWrapper transactionNode) {
+        return readDataViaTransaction(transactionNode, true);
+    }
+
+
+    /**
+     * If is set specific {@link LogicalDatastoreType} in
+     * {@link TransactionVarsWrapper}, then read this type of data from DS. If
+     * don't, we have to read all data from DS (state + config)
+     *
+     * @param transactionNode
+     *             {@link TransactionVarsWrapper} - wrapper for variables
+     * @param closeTransactionChain
+     *             If is set to true, after transaction it will close transactionChain in {@link TransactionVarsWrapper}
+     * @return {@link NormalizedNode}
+     */
+    private static @Nullable NormalizedNode<?, ?> readDataViaTransaction(
+            final @NonNull TransactionVarsWrapper transactionNode, final boolean closeTransactionChain) {
         final NormalizedNodeFactory dataFactory = new NormalizedNodeFactory();
         try (DOMDataTreeReadTransaction tx = transactionNode.getTransactionChain().newReadOnlyTransaction()) {
             final FluentFuture<Optional<NormalizedNode<?, ?>>> listenableFuture = tx.read(
                 transactionNode.getLogicalDatastoreType(),
                 transactionNode.getInstanceIdentifier().getInstanceIdentifier());
-            FutureCallbackTx.addCallback(listenableFuture, RestconfDataServiceConstant.ReadData.READ_TYPE_TX,
-                dataFactory);
+            if (closeTransactionChain) {
+                //Method close transactionChain inside of TransactionVarsWrapper, if is provide as a parameter.
+                FutureCallbackTx.addCallback(listenableFuture, RestconfDataServiceConstant.ReadData.READ_TYPE_TX,
+                        dataFactory, transactionNode.getTransactionChain());
+            } else {
+                FutureCallbackTx.addCallback(listenableFuture, RestconfDataServiceConstant.ReadData.READ_TYPE_TX,
+                        dataFactory);
+            }
         }
         return dataFactory.build();
     }
 
     /**
-     * Read config and state data, then map them.
+     * Read config and state data, then map them. Close {@link DOMTransactionChain} inside of object
+     * {@link TransactionVarsWrapper} provided as a parameter.
      *
      * @param transactionNode
      *            {@link TransactionVarsWrapper} - wrapper for variables
@@ -459,11 +488,12 @@ public final class ReadDataTransactionUtil {
             final String withDefa, final SchemaContext ctx) {
         // PREPARE STATE DATA NODE
         transactionNode.setLogicalDatastoreType(LogicalDatastoreType.OPERATIONAL);
-        final NormalizedNode<?, ?> stateDataNode = readDataViaTransaction(transactionNode);
+        final NormalizedNode<?, ?> stateDataNode = readDataViaTransaction(transactionNode, false);
 
         // PREPARE CONFIG DATA NODE
         transactionNode.setLogicalDatastoreType(LogicalDatastoreType.CONFIGURATION);
         final NormalizedNode<?, ?> configDataNode;
+        //Here will be closed transactionChain
         if (withDefa == null) {
             configDataNode = readDataViaTransaction(transactionNode);
         } else {
index 3a31dd976f7b8172936e4054b00a55e15973d310..acfc6437c05d9d2d86ccc209c00f120d7afd253b 100644 (file)
@@ -32,6 +32,7 @@ import org.opendaylight.mdsal.dom.api.DOMDataTreeIdentifier;
 import org.opendaylight.mdsal.dom.api.DOMDataTreeReadOperations;
 import org.opendaylight.mdsal.dom.api.DOMDataTreeReadWriteTransaction;
 import org.opendaylight.mdsal.dom.api.DOMNotificationListener;
+import org.opendaylight.mdsal.dom.api.DOMTransactionChain;
 import org.opendaylight.restconf.common.context.InstanceIdentifierContext;
 import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
 import org.opendaylight.restconf.common.errors.RestconfError.ErrorTag;
@@ -110,10 +111,8 @@ public final class SubscribeToStreamUtil {
                     ErrorTag.UNKNOWN_ELEMENT);
         }
 
-        final DOMDataTreeReadWriteTransaction writeTransaction = handlersHolder
-                .getTransactionChainHandler()
-                .get()
-                .newReadWriteTransaction();
+        final DOMTransactionChain transactionChain = handlersHolder.getTransactionChainHandler().get();
+        final DOMDataTreeReadWriteTransaction writeTransaction = transactionChain.newReadWriteTransaction();
         final SchemaContext schemaContext = handlersHolder.getSchemaHandler().get();
         final boolean exist = checkExist(schemaContext, writeTransaction);
         final URI uri = prepareUriByStreamName(uriInfo, streamName);
@@ -136,6 +135,7 @@ public final class SubscribeToStreamUtil {
                 notificationListenerAdapter.get().getSchemaPath().getLastComponent().getLocalName(), writeTransaction,
                 exist, mapToStreams);
         submitData(writeTransaction);
+        transactionChain.close();
         return uri;
     }
 
@@ -208,8 +208,8 @@ public final class SubscribeToStreamUtil {
         registration(datastoreType, listener.get(), handlersHolder.getDomDataBrokerHandler().get());
 
         final URI uri = prepareUriByStreamName(uriInfo, streamName);
-        final DOMDataTreeReadWriteTransaction writeTransaction
-                = handlersHolder.getTransactionChainHandler().get().newReadWriteTransaction();
+        final DOMTransactionChain transactionChain = handlersHolder.getTransactionChainHandler().get();
+        final DOMDataTreeReadWriteTransaction writeTransaction = transactionChain.newReadWriteTransaction();
         final SchemaContext schemaContext = handlersHolder.getSchemaHandler().get();
         final boolean exist = checkExist(schemaContext, writeTransaction);
 
@@ -220,6 +220,7 @@ public final class SubscribeToStreamUtil {
         writeDataToDS(schemaContext, listener.get().getPath().getLastPathArgument().getNodeType().getLocalName(),
                 writeTransaction, exist, mapToStreams);
         submitData(writeTransaction);
+        transactionChain.close();
         return uri;
     }
 
index 3cfddd693505c3c80dd8b2c4509c8c257f60d850..0e4261c46a3af32962ac4319ad051c8981ae5ea8 100644 (file)
@@ -15,10 +15,10 @@ import java.util.List;
 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
 import org.opendaylight.mdsal.dom.api.DOMDataTreeReadWriteTransaction;
 import org.opendaylight.mdsal.dom.api.DOMDataTreeWriteTransaction;
+import org.opendaylight.mdsal.dom.api.DOMTransactionChain;
 import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
 import org.opendaylight.restconf.common.errors.RestconfError.ErrorTag;
 import org.opendaylight.restconf.common.errors.RestconfError.ErrorType;
-import org.opendaylight.restconf.nb.rfc8040.handlers.TransactionChainHandler;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
@@ -81,13 +81,13 @@ public final class TransactionUtil {
     /**
      * Check if items already exists at specified {@code path}. Throws {@link RestconfDocumentedException} if
      * data does NOT already exists.
-     * @param transactionChainHandler Transaction chain handler
+     * @param transactionChain Transaction chain
      * @param rwTransaction Transaction
      * @param store Datastore
      * @param path Path to be checked
      * @param operationType Type of operation (READ, POST, PUT, DELETE...)
      */
-    public static void checkItemExists(final TransactionChainHandler transactionChainHandler,
+    public static void checkItemExists(final DOMTransactionChain transactionChain,
                                        final DOMDataTreeReadWriteTransaction rwTransaction,
                                        final LogicalDatastoreType store, final YangInstanceIdentifier path,
                                        final String operationType) {
@@ -97,10 +97,9 @@ public final class TransactionUtil {
         FutureCallbackTx.addCallback(future, operationType, response);
 
         if (!response.result) {
-            // close transaction and reset transaction chain
+            // close transaction
             rwTransaction.cancel();
-            transactionChainHandler.reset();
-
+            transactionChain.close();
             // throw error
             LOG.trace("Operation via Restconf was not executed because data at {} does not exist", path);
             throw new RestconfDocumentedException(
@@ -111,13 +110,13 @@ public final class TransactionUtil {
     /**
      * Check if items do NOT already exists at specified {@code path}. Throws {@link RestconfDocumentedException} if
      * data already exists.
-     * @param transactionChainHandler Transaction chain handler
+     * @param transactionChain Transaction chain
      * @param rwTransaction Transaction
      * @param store Datastore
      * @param path Path to be checked
      * @param operationType Type of operation (READ, POST, PUT, DELETE...)
      */
-    public static void checkItemDoesNotExists(final TransactionChainHandler transactionChainHandler,
+    public static void checkItemDoesNotExists(final DOMTransactionChain transactionChain,
                                               final DOMDataTreeReadWriteTransaction rwTransaction,
                                               final LogicalDatastoreType store, final YangInstanceIdentifier path,
                                               final String operationType) {
@@ -127,10 +126,9 @@ public final class TransactionUtil {
         FutureCallbackTx.addCallback(future, operationType, response);
 
         if (response.result) {
-            // close transaction and reset transaction chain
+            // close transaction
             rwTransaction.cancel();
-            transactionChainHandler.reset();
-
+            transactionChain.close();
             // throw error
             LOG.trace("Operation via Restconf was not executed because data at {} already exists", path);
             throw new RestconfDocumentedException(
index da575164f9559def2a5fa6f27facfe491f685e7b..d2c2322c4e7bfb4e072b526cd523c6c639b23eac 100644 (file)
@@ -26,6 +26,7 @@ import javax.xml.transform.dom.DOMSource;
 import javax.xml.transform.stream.StreamResult;
 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
 import org.opendaylight.mdsal.dom.api.DOMDataTreeWriteTransaction;
+import org.opendaylight.mdsal.dom.api.DOMTransactionChain;
 import org.opendaylight.restconf.nb.rfc8040.Rfc8040.MonitoringModule;
 import org.opendaylight.restconf.nb.rfc8040.handlers.SchemaContextHandler;
 import org.opendaylight.restconf.nb.rfc8040.handlers.TransactionChainHandler;
@@ -77,10 +78,12 @@ abstract class AbstractNotificationsData {
      * Delete data in DS.
      */
     protected void deleteDataInDS() throws Exception {
-        final DOMDataTreeWriteTransaction wTx = this.transactionChainHandler.get().newWriteOnlyTransaction();
+        final DOMTransactionChain transactionChain = this.transactionChainHandler.get();
+        final DOMDataTreeWriteTransaction wTx = transactionChain.newWriteOnlyTransaction();
         wTx.delete(LogicalDatastoreType.OPERATIONAL, IdentifierCodec
                 .deserialize(MonitoringModule.PATH_TO_STREAM_WITHOUT_KEY + this.localName, this.schemaHandler.get()));
         wTx.commit().get();
+        transactionChain.close();
     }
 
     /**
diff --git a/restconf/restconf-nb-rfc8040/src/test/java/org/opendaylight/restconf/nb/rfc8040/handlers/TransactionChainHandlerTest.java b/restconf/restconf-nb-rfc8040/src/test/java/org/opendaylight/restconf/nb/rfc8040/handlers/TransactionChainHandlerTest.java
new file mode 100644 (file)
index 0000000..7c7d727
--- /dev/null
@@ -0,0 +1,118 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech, s.r.o. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.restconf.nb.rfc8040.handlers;
+
+import com.google.common.collect.ClassToInstanceMap;
+import org.eclipse.jdt.annotation.NonNull;
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Test;
+import org.mockito.Mockito;
+import org.opendaylight.mdsal.dom.api.DOMDataBroker;
+import org.opendaylight.mdsal.dom.api.DOMDataBrokerExtension;
+import org.opendaylight.mdsal.dom.api.DOMDataTreeReadTransaction;
+import org.opendaylight.mdsal.dom.api.DOMDataTreeReadWriteTransaction;
+import org.opendaylight.mdsal.dom.api.DOMDataTreeTransaction;
+import org.opendaylight.mdsal.dom.api.DOMDataTreeWriteTransaction;
+import org.opendaylight.mdsal.dom.api.DOMTransactionChain;
+import org.opendaylight.mdsal.dom.api.DOMTransactionChainListener;
+import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
+
+public class TransactionChainHandlerTest {
+
+    private TransactionChainHandler transactionChainHandler;
+    private static final String EXCEPTION_MESSAGE = "(TEST) Unsupported Method";
+
+    @After
+    public void shutdown() {
+        this.transactionChainHandler.close();
+    }
+
+    @Test
+    public void transactionChainTest() {
+        this.transactionChainHandler = new TransactionChainHandler(new DataBrokerLocal());
+        final DOMTransactionChain chain1 = this.transactionChainHandler.get();
+        final DOMTransactionChain chain2 = this.transactionChainHandler.get();
+        Assert.assertNotNull(chain1);
+        Assert.assertNotNull(chain2);
+        Assert.assertNotEquals(chain1, chain2);
+        chain1.close();
+        Assert.assertFalse(this.transactionChainHandler.verifyIfExistTransactionChain(chain1));
+
+        try {
+            chain2.newReadOnlyTransaction();
+        } catch (RestconfDocumentedException e) {
+            Assert.assertEquals(e.getCause().getLocalizedMessage(), EXCEPTION_MESSAGE);
+        }
+        Assert.assertFalse(this.transactionChainHandler.verifyIfExistTransactionChain(chain2));
+    }
+
+    private final class TxChainLocal implements DOMTransactionChain {
+        DOMTransactionChainListener listener;
+
+        TxChainLocal(DOMTransactionChainListener listener) {
+            this.listener = listener;
+        }
+
+        @Override
+        public DOMDataTreeReadTransaction newReadOnlyTransaction() {
+            final DOMDataTreeTransaction domDataTreeTransaction = Mockito.mock(DOMDataTreeTransaction.class);
+            listener.onTransactionChainFailed(this,
+                    domDataTreeTransaction, new Throwable(EXCEPTION_MESSAGE));
+            return null;
+        }
+
+        @Override
+        public DOMDataTreeWriteTransaction newWriteOnlyTransaction() {
+            return null;
+        }
+
+        @Override
+        public DOMDataTreeReadWriteTransaction newReadWriteTransaction() {
+            return null;
+        }
+
+        @Override
+        public void close() {
+            listener.onTransactionChainSuccessful(this);
+        }
+    }
+
+    private final class DataBrokerLocal implements DOMDataBroker {
+
+        @Override
+        public @NonNull DOMTransactionChain createTransactionChain(DOMTransactionChainListener listener) {
+            return new TxChainLocal(listener);
+        }
+
+        @Override
+        public @NonNull DOMTransactionChain createMergingTransactionChain(DOMTransactionChainListener listener) {
+            return null;
+        }
+
+        @Override
+        public @NonNull ClassToInstanceMap<DOMDataBrokerExtension> getExtensions() {
+            return null;
+        }
+
+        @Override
+        public DOMDataTreeReadTransaction newReadOnlyTransaction() {
+            return null;
+        }
+
+        @Override
+        public DOMDataTreeWriteTransaction newWriteOnlyTransaction() {
+            return null;
+        }
+
+        @Override
+        public DOMDataTreeReadWriteTransaction newReadWriteTransaction() {
+            return null;
+        }
+    }
+}