Split transaction lifecycle 64/93064/9
authorRobert Varga <robert.varga@pantheon.tech>
Wed, 14 Oct 2020 21:42:04 +0000 (23:42 +0200)
committerVladyslav Marchenko <vladyslav.marchenko@pantheon.tech>
Fri, 20 Nov 2020 15:39:38 +0000 (17:39 +0200)
Change-Id: I0ec27e3720e23639b379f8ae594cb84dfb814516
Signed-off-by: Robert Varga <robert.varga@pantheon.tech>
Signed-off-by: Vladyslav Marchenko <vladyslav.marchenko@pantheon.tech>
21 files changed:
restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/services/impl/RestconfDataServiceImpl.java
restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/transactions/BatchedExistenceCheck.java
restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/transactions/MdsalRestconfStrategy.java
restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/transactions/MdsalRestconfTransaction.java [new file with mode: 0644]
restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/transactions/NetconfRestconfStrategy.java
restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/transactions/NetconfRestconfTransaction.java [new file with mode: 0644]
restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/transactions/RestconfStrategy.java
restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/transactions/RestconfTransaction.java [new file with mode: 0644]
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/PlainPatchDataTransactionUtil.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/TransactionUtil.java
restconf/restconf-nb-rfc8040/src/test/java/org/opendaylight/restconf/nb/rfc8040/rests/services/impl/JSONRestconfServiceRfc8040ImplTest.java
restconf/restconf-nb-rfc8040/src/test/java/org/opendaylight/restconf/nb/rfc8040/rests/services/impl/RestconfDataServiceImplTest.java
restconf/restconf-nb-rfc8040/src/test/java/org/opendaylight/restconf/nb/rfc8040/rests/utils/DeleteDataTransactionUtilTest.java
restconf/restconf-nb-rfc8040/src/test/java/org/opendaylight/restconf/nb/rfc8040/rests/utils/PostDataTransactionUtilTest.java
restconf/restconf-nb-rfc8040/src/test/java/org/opendaylight/restconf/nb/rfc8040/rests/utils/PutDataTransactionUtilTest.java

index f5219d9dda7e54d19632aaca198c455ccc9e7d90..79ccb8acd5bc7f237da7239718a0b4de01b9c57f 100644 (file)
@@ -53,6 +53,7 @@ import org.opendaylight.restconf.nb.rfc8040.rests.services.api.RestconfDataServi
 import org.opendaylight.restconf.nb.rfc8040.rests.services.api.RestconfStreamsSubscriptionService;
 import org.opendaylight.restconf.nb.rfc8040.rests.transactions.MdsalRestconfStrategy;
 import org.opendaylight.restconf.nb.rfc8040.rests.transactions.RestconfStrategy;
+import org.opendaylight.restconf.nb.rfc8040.rests.transactions.RestconfTransaction;
 import org.opendaylight.restconf.nb.rfc8040.rests.utils.DeleteDataTransactionUtil;
 import org.opendaylight.restconf.nb.rfc8040.rests.utils.PatchDataTransactionUtil;
 import org.opendaylight.restconf.nb.rfc8040.rests.utils.PlainPatchDataTransactionUtil;
@@ -207,7 +208,7 @@ public class RestconfDataServiceImpl implements RestconfDataService {
 
     private void createAllYangNotificationStreams(final RestconfStrategy strategy,
             final EffectiveModelContext schemaContext, final UriInfo uriInfo) {
-        strategy.prepareReadWriteExecution();
+        final RestconfTransaction transaction = strategy.prepareWriteExecution();
         final boolean exist = checkExist(schemaContext, strategy);
 
         for (final NotificationDefinition notificationDefinition : schemaContext.getNotifications()) {
@@ -217,18 +218,18 @@ public class RestconfDataServiceImpl implements RestconfDataService {
             final NotificationListenerAdapter notifiStreamJSON =
                 CreateStreamUtil.createYangNotifiStream(notificationDefinition, schemaContext,
                     NotificationOutputType.JSON);
-            writeNotificationStreamToDatastore(schemaContext, uriInfo, strategy, exist, notifiStreamXML);
-            writeNotificationStreamToDatastore(schemaContext, uriInfo, strategy, exist, notifiStreamJSON);
+            writeNotificationStreamToDatastore(schemaContext, uriInfo, transaction, exist, notifiStreamXML);
+            writeNotificationStreamToDatastore(schemaContext, uriInfo, transaction, exist, notifiStreamJSON);
         }
         try {
-            strategy.commit().get();
+            transaction.commit().get();
         } catch (final InterruptedException | ExecutionException e) {
             throw new RestconfDocumentedException("Problem while putting data to DS.", e);
         }
     }
 
     private void writeNotificationStreamToDatastore(final EffectiveModelContext schemaContext,
-            final UriInfo uriInfo, final RestconfStrategy strategy, final boolean exist,
+            final UriInfo uriInfo, final RestconfTransaction transaction, final boolean exist,
             final NotificationListenerAdapter listener) {
         final URI uri = streamUtils.prepareUriByStreamName(uriInfo, listener.getStreamName());
         final NormalizedNode<?, ?> mapToStreams =
@@ -236,7 +237,7 @@ public class RestconfDataServiceImpl implements RestconfDataService {
                 listener.getSchemaPath().lastNodeIdentifier(), schemaContext.getNotifications(), null,
                 listener.getOutputType(), uri, SubscribeToStreamUtil.getMonitoringModule(schemaContext), exist);
         writeDataToDS(schemaContext,
-            listener.getSchemaPath().lastNodeIdentifier().getLocalName(), strategy, exist, mapToStreams);
+            listener.getSchemaPath().lastNodeIdentifier().getLocalName(), transaction, exist, mapToStreams);
     }
 
     private static boolean checkExist(final EffectiveModelContext schemaContext, final RestconfStrategy strategy) {
@@ -249,7 +250,7 @@ public class RestconfDataServiceImpl implements RestconfDataService {
     }
 
     private static void writeDataToDS(final EffectiveModelContext schemaContext, final String name,
-                                      final RestconfStrategy strategy, final boolean exist,
+                                      final RestconfTransaction transaction, final boolean exist,
                                       final NormalizedNode<?, ?> mapToStreams) {
         final String pathId;
         if (exist) {
@@ -257,7 +258,7 @@ public class RestconfDataServiceImpl implements RestconfDataService {
         } else {
             pathId = Rfc8040.MonitoringModule.PATH_TO_STREAMS;
         }
-        strategy.merge(LogicalDatastoreType.OPERATIONAL, IdentifierCodec.deserialize(pathId, schemaContext),
+        transaction.merge(LogicalDatastoreType.OPERATIONAL, IdentifierCodec.deserialize(pathId, schemaContext),
             mapToStreams);
     }
 
index 8cfbab1182b0cefb87a7cfe8f952ac0119549044..d6d0acdd4685f8c6c9420e1cacb9e630e64821be 100644 (file)
@@ -18,6 +18,8 @@ import java.util.concurrent.ExecutionException;
 import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
 import org.opendaylight.mdsal.common.api.ReadFailedException;
+import org.opendaylight.mdsal.dom.api.DOMDataTreeReadTransaction;
+import org.opendaylight.mdsal.dom.api.DOMTransactionChain;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
 
@@ -34,34 +36,35 @@ final class BatchedExistenceCheck {
         this.outstanding = total;
     }
 
-    static BatchedExistenceCheck start(final RestconfStrategy read,
+    static BatchedExistenceCheck start(final DOMTransactionChain transactionChain,
                                        final LogicalDatastoreType datastore, final YangInstanceIdentifier parentPath,
                                        final Collection<? extends NormalizedNode<?, ?>> children) {
         final BatchedExistenceCheck ret = new BatchedExistenceCheck(children.size());
-        for (NormalizedNode<?, ?> child : children) {
-            final YangInstanceIdentifier path = parentPath.node(child.getIdentifier());
-            read.exists(datastore, path).addCallback(new FutureCallback<Boolean>() {
-                @Override
-                public void onSuccess(final Boolean result) {
-                    ret.complete(path, result);
-                }
-
-                @Override
-                @SuppressFBWarnings("BC_UNCONFIRMED_CAST_OF_RETURN_VALUE")
-                public void onFailure(final Throwable throwable) {
-                    final Exception e;
-                    if (throwable instanceof Exception) {
-                        e = (Exception) throwable;
-                    } else {
-                        e = new ExecutionException(throwable);
+        try (DOMDataTreeReadTransaction tx = transactionChain.newReadOnlyTransaction()) {
+            for (NormalizedNode<?, ?> child : children) {
+                final YangInstanceIdentifier path = parentPath.node(child.getIdentifier());
+                tx.exists(datastore, path).addCallback(new FutureCallback<Boolean>() {
+                    @Override
+                    public void onSuccess(final Boolean result) {
+                        ret.complete(path, result);
                     }
 
-                    ret.complete(path, ReadFailedException.MAPPER.apply(e));
-                }
-            }, MoreExecutors.directExecutor());
-        }
+                    @Override
+                    @SuppressFBWarnings("BC_UNCONFIRMED_CAST_OF_RETURN_VALUE")
+                    public void onFailure(final Throwable throwable) {
+                        final Exception e;
+                        if (throwable instanceof Exception) {
+                            e = (Exception) throwable;
+                        } else {
+                            e = new ExecutionException(throwable);
+                        }
 
-        return ret;
+                        ret.complete(path, ReadFailedException.MAPPER.apply(e));
+                    }
+                }, MoreExecutors.directExecutor());
+            }
+            return ret;
+        }
     }
 
     Entry<YangInstanceIdentifier, ReadFailedException> getFailure() throws InterruptedException {
index 6b2e7c2e28e7b6769e98bd8c8c45dee70eff1969..d0fb5c33520a15e8dc41555e5cab00a4c6b6df82 100644 (file)
@@ -7,36 +7,19 @@
  */
 package org.opendaylight.restconf.nb.rfc8040.rests.transactions;
 
-import static com.google.common.base.Verify.verifyNotNull;
 import static java.util.Objects.requireNonNull;
-import static org.opendaylight.restconf.nb.rfc8040.rests.utils.DeleteDataTransactionUtil.DELETE_TX_TYPE;
-import static org.opendaylight.restconf.nb.rfc8040.rests.utils.PostDataTransactionUtil.checkItemDoesNotExists;
 
 import com.google.common.util.concurrent.FluentFuture;
 import com.google.common.util.concurrent.ListenableFuture;
-import java.util.Collection;
-import java.util.Map;
 import java.util.Optional;
-import org.eclipse.jdt.annotation.NonNull;
-import org.opendaylight.mdsal.common.api.CommitInfo;
 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
-import org.opendaylight.mdsal.common.api.ReadFailedException;
 import org.opendaylight.mdsal.dom.api.DOMDataBroker;
 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.errors.RestconfDocumentedException;
-import org.opendaylight.restconf.common.errors.RestconfError;
 import org.opendaylight.restconf.nb.rfc8040.handlers.TransactionChainHandler;
-import org.opendaylight.restconf.nb.rfc8040.rests.utils.DeleteDataTransactionUtil;
-import org.opendaylight.restconf.nb.rfc8040.rests.utils.TransactionUtil;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
-import org.opendaylight.yangtools.yang.data.api.schema.LeafSetNode;
-import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
-import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodeContainer;
-import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
-import org.opendaylight.yangtools.yang.model.api.SchemaContext;
 
 /**
  * Implementation of RESTCONF operations using {@link DOMTransactionChain} and related concepts.
@@ -46,149 +29,37 @@ import org.opendaylight.yangtools.yang.model.api.SchemaContext;
  */
 public final class MdsalRestconfStrategy extends RestconfStrategy {
     private final DOMTransactionChain transactionChain;
-    private final TransactionChainHandler transactionChainHandler;
-
-    private DOMDataTreeReadWriteTransaction rwTx;
 
     public MdsalRestconfStrategy(final DOMDataBroker dataBroker) {
         this(new TransactionChainHandler(dataBroker));
     }
 
     public MdsalRestconfStrategy(final TransactionChainHandler transactionChainHandler) {
-        this.transactionChainHandler = requireNonNull(transactionChainHandler);
-        transactionChain = transactionChainHandler.get();
+        transactionChain = requireNonNull(transactionChainHandler).get();
     }
 
     @Override
-    public void prepareReadWriteExecution() {
-        rwTx = transactionChain.newReadWriteTransaction();
-    }
-
-    @Override
-    public void cancel() {
-        if (rwTx != null) {
-            rwTx.cancel();
-            rwTx = null;
-        }
-        transactionChain.close();
+    public RestconfTransaction prepareWriteExecution() {
+        return new MdsalRestconfTransaction(transactionChain);
     }
 
     @Override
     public ListenableFuture<Optional<NormalizedNode<?, ?>>> read(final LogicalDatastoreType store,
                                                                  final YangInstanceIdentifier path) {
-        if (rwTx != null) {
-            return rwTx.read(store, path);
-        } else {
-            try (DOMDataTreeReadTransaction tx = transactionChain.newReadOnlyTransaction()) {
-                return tx.read(store, path);
-            }
+        try (DOMDataTreeReadTransaction tx = transactionChain.newReadOnlyTransaction()) {
+            return tx.read(store, path);
         }
     }
 
     @Override
     public FluentFuture<Boolean> exists(final LogicalDatastoreType store, final YangInstanceIdentifier path) {
-        return rwTx.exists(store, path);
-    }
-
-    @Override
-    public void delete(final LogicalDatastoreType store, final YangInstanceIdentifier path) {
-        DeleteDataTransactionUtil.checkItemExists(this, LogicalDatastoreType.CONFIGURATION, path,
-            DELETE_TX_TYPE);
-        rwTx.delete(store, path);
-    }
-
-    @Override
-    public void remove(final LogicalDatastoreType store, final YangInstanceIdentifier path) {
-        rwTx.delete(store, path);
-    }
-
-    @Override
-    public void merge(final LogicalDatastoreType store, final YangInstanceIdentifier path,
-            final NormalizedNode<?, ?> data) {
-        rwTx.merge(store, path, data);
-    }
-
-    @Override
-    public void create(final LogicalDatastoreType store, final YangInstanceIdentifier path,
-                       final NormalizedNode<?, ?> data, final SchemaContext schemaContext) {
-        if (data instanceof MapNode || data instanceof LeafSetNode) {
-            final NormalizedNode<?, ?> emptySubTree = ImmutableNodes.fromInstanceId(schemaContext, path);
-            merge(LogicalDatastoreType.CONFIGURATION, YangInstanceIdentifier.create(emptySubTree.getIdentifier()),
-                emptySubTree);
-            TransactionUtil.ensureParentsByMerge(path, schemaContext, this);
-
-            final Collection<? extends NormalizedNode<?, ?>> children =
-                ((NormalizedNodeContainer<?, ?, ?>) data).getValue();
-            final BatchedExistenceCheck check =
-                BatchedExistenceCheck.start(this, LogicalDatastoreType.CONFIGURATION, path, children);
-
-            for (final NormalizedNode<?, ?> child : children) {
-                final YangInstanceIdentifier childPath = path.node(child.getIdentifier());
-                rwTx.put(store, childPath, child);
-            }
-
-            // ... finally collect existence checks and abort the transaction if any of them failed.
-            checkExistence(path, check);
-        } else {
-            checkItemDoesNotExists(this, LogicalDatastoreType.CONFIGURATION, path);
-            TransactionUtil.ensureParentsByMerge(path, schemaContext, this);
-            rwTx.put(store, path, data);
-        }
-    }
-
-    @Override
-    public void replace(final LogicalDatastoreType store, final YangInstanceIdentifier path,
-                        final NormalizedNode<?, ?> data, final SchemaContext schemaContext) {
-        if (data instanceof MapNode || data instanceof LeafSetNode) {
-            final NormalizedNode<?, ?> emptySubtree = ImmutableNodes.fromInstanceId(schemaContext, path);
-            merge(LogicalDatastoreType.CONFIGURATION, YangInstanceIdentifier.create(emptySubtree.getIdentifier()),
-                emptySubtree);
-            TransactionUtil.ensureParentsByMerge(path, schemaContext, this);
-
-            for (final NormalizedNode<?, ?> child : ((NormalizedNodeContainer<?, ?, ?>) data).getValue()) {
-                final YangInstanceIdentifier childPath = path.node(child.getIdentifier());
-                rwTx.put(store, childPath, child);
-            }
-        } else {
-            TransactionUtil.ensureParentsByMerge(path, schemaContext, this);
-            rwTx.put(store, path, data);
+        try (DOMDataTreeReadTransaction tx = transactionChain.newReadOnlyTransaction()) {
+            return tx.exists(store, path);
         }
     }
 
     @Override
-    public FluentFuture<? extends @NonNull CommitInfo> commit() {
-        final FluentFuture<? extends @NonNull CommitInfo> ret = verifyNotNull(rwTx).commit();
-        rwTx = null;
-        return ret;
-    }
-
-    @Override
-    public DOMTransactionChain getTransactionChain() {
-        return transactionChain;
-    }
-
-    @Override
-    public TransactionChainHandler getTransactionChainHandler() {
-        return transactionChainHandler;
-    }
-
-    private static void checkExistence(final YangInstanceIdentifier path, final BatchedExistenceCheck check) {
-        final Map.Entry<YangInstanceIdentifier, ReadFailedException> failure;
-        try {
-            failure = check.getFailure();
-        } catch (InterruptedException e) {
-            throw new RestconfDocumentedException("Could not determine the existence of path " + path, e);
-        }
-
-        if (failure != null) {
-            final ReadFailedException e = failure.getValue();
-            if (e == null) {
-                throw new RestconfDocumentedException("Data already exists",
-                    RestconfError.ErrorType.PROTOCOL, RestconfError.ErrorTag.DATA_EXISTS, failure.getKey());
-            }
-
-            throw new RestconfDocumentedException(
-                "Could not determine the existence of path " + failure.getKey(), e, e.getErrorList());
-        }
+    public void close() {
+        transactionChain.close();
     }
 }
diff --git a/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/transactions/MdsalRestconfTransaction.java b/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/transactions/MdsalRestconfTransaction.java
new file mode 100644 (file)
index 0000000..a6bfdb3
--- /dev/null
@@ -0,0 +1,145 @@
+/*
+ * Copyright (c) 2020 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.rests.transactions;
+
+import static com.google.common.base.Verify.verifyNotNull;
+import static java.util.Objects.requireNonNull;
+import static org.opendaylight.restconf.nb.rfc8040.rests.utils.DeleteDataTransactionUtil.DELETE_TX_TYPE;
+import static org.opendaylight.restconf.nb.rfc8040.rests.utils.PostDataTransactionUtil.checkItemDoesNotExists;
+
+import com.google.common.util.concurrent.FluentFuture;
+import java.util.Collection;
+import java.util.Map;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.mdsal.common.api.CommitInfo;
+import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
+import org.opendaylight.mdsal.common.api.ReadFailedException;
+import org.opendaylight.mdsal.dom.api.DOMDataTreeReadWriteTransaction;
+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.nb.rfc8040.rests.utils.DeleteDataTransactionUtil;
+import org.opendaylight.restconf.nb.rfc8040.rests.utils.TransactionUtil;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafSetNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodeContainer;
+import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+final class MdsalRestconfTransaction extends RestconfTransaction {
+    private final DOMTransactionChain transactionChain;
+    private DOMDataTreeReadWriteTransaction rwTx;
+
+    MdsalRestconfTransaction(DOMTransactionChain transactionChain) {
+        this.transactionChain = requireNonNull(transactionChain);
+        this.rwTx = transactionChain.newReadWriteTransaction();
+    }
+
+    @Override
+    public void cancel() {
+        if (rwTx != null) {
+            rwTx.cancel();
+            rwTx = null;
+        }
+        transactionChain.close();
+    }
+
+    @Override
+    public void delete(final LogicalDatastoreType store, final YangInstanceIdentifier path) {
+        final FluentFuture<Boolean> isExists = verifyNotNull(rwTx).exists(store, path);
+        DeleteDataTransactionUtil.checkItemExists(isExists, path, DELETE_TX_TYPE);
+        rwTx.delete(store, path);
+    }
+
+    @Override
+    public void remove(final LogicalDatastoreType store, final YangInstanceIdentifier path) {
+        verifyNotNull(rwTx).delete(store, path);
+    }
+
+    @Override
+    public void merge(final LogicalDatastoreType store, final YangInstanceIdentifier path,
+                      final NormalizedNode<?, ?> data) {
+        verifyNotNull(rwTx).merge(store, path, data);
+    }
+
+    @Override
+    public void create(final LogicalDatastoreType store, final YangInstanceIdentifier path,
+                       final NormalizedNode<?, ?> data, final SchemaContext schemaContext) {
+        if (data instanceof MapNode || data instanceof LeafSetNode) {
+            final NormalizedNode<?, ?> emptySubTree = ImmutableNodes.fromInstanceId(schemaContext, path);
+            merge(LogicalDatastoreType.CONFIGURATION, YangInstanceIdentifier.create(emptySubTree.getIdentifier()),
+                emptySubTree);
+            TransactionUtil.ensureParentsByMerge(path, schemaContext, this);
+
+            final Collection<? extends NormalizedNode<?, ?>> children =
+                ((NormalizedNodeContainer<?, ?, ?>) data).getValue();
+            final BatchedExistenceCheck check =
+                BatchedExistenceCheck.start(transactionChain, LogicalDatastoreType.CONFIGURATION, path, children);
+
+            for (final NormalizedNode<?, ?> child : children) {
+                final YangInstanceIdentifier childPath = path.node(child.getIdentifier());
+                verifyNotNull(rwTx).put(store, childPath, child);
+            }
+            // ... finally collect existence checks and abort the transaction if any of them failed.
+            checkExistence(path, check);
+        } else {
+            final FluentFuture<Boolean> isExists = verifyNotNull(rwTx).exists(store, path);
+            checkItemDoesNotExists(isExists, path);
+            TransactionUtil.ensureParentsByMerge(path, schemaContext, this);
+            verifyNotNull(rwTx).put(store, path, data);
+        }
+    }
+
+    @Override
+    public void replace(final LogicalDatastoreType store, final YangInstanceIdentifier path,
+                        final NormalizedNode<?, ?> data, final SchemaContext schemaContext) {
+        if (data instanceof MapNode || data instanceof LeafSetNode) {
+            final NormalizedNode<?, ?> emptySubtree = ImmutableNodes.fromInstanceId(schemaContext, path);
+            merge(LogicalDatastoreType.CONFIGURATION, YangInstanceIdentifier.create(emptySubtree.getIdentifier()),
+                emptySubtree);
+            TransactionUtil.ensureParentsByMerge(path, schemaContext, this);
+
+            for (final NormalizedNode<?, ?> child : ((NormalizedNodeContainer<?, ?, ?>) data).getValue()) {
+                final YangInstanceIdentifier childPath = path.node(child.getIdentifier());
+                verifyNotNull(rwTx).put(store, childPath, child);
+            }
+        } else {
+            TransactionUtil.ensureParentsByMerge(path, schemaContext, this);
+            verifyNotNull(rwTx).put(store, path, data);
+        }
+    }
+
+    @Override
+    public FluentFuture<? extends @NonNull CommitInfo> commit() {
+        final FluentFuture<? extends @NonNull CommitInfo> ret = verifyNotNull(rwTx).commit();
+        rwTx = null;
+        return ret;
+    }
+
+    private static void checkExistence(final YangInstanceIdentifier path, final BatchedExistenceCheck check) {
+        final Map.Entry<YangInstanceIdentifier, ReadFailedException> failure;
+        try {
+            failure = check.getFailure();
+        } catch (InterruptedException e) {
+            throw new RestconfDocumentedException("Could not determine the existence of path " + path, e);
+        }
+
+        if (failure != null) {
+            final ReadFailedException e = failure.getValue();
+            if (e == null) {
+                throw new RestconfDocumentedException("Data already exists",
+                    RestconfError.ErrorType.PROTOCOL, RestconfError.ErrorTag.DATA_EXISTS, failure.getKey());
+            }
+
+            throw new RestconfDocumentedException(
+                "Could not determine the existence of path " + failure.getKey(), e, e.getErrorList());
+        }
+    }
+}
index 19a60f376d17b7bbdaa3476fdb70e6b565735097..0f9acb580e2a7789d0f4031577750ea6c236b38e 100644 (file)
@@ -15,23 +15,12 @@ import com.google.common.util.concurrent.Futures;
 import com.google.common.util.concurrent.ListenableFuture;
 import com.google.common.util.concurrent.MoreExecutors;
 import com.google.common.util.concurrent.SettableFuture;
-import java.util.List;
 import java.util.Optional;
-import org.eclipse.jdt.annotation.NonNull;
-import org.opendaylight.mdsal.common.api.CommitInfo;
 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
 import org.opendaylight.mdsal.common.api.ReadFailedException;
-import org.opendaylight.mdsal.dom.api.DOMRpcResult;
-import org.opendaylight.mdsal.dom.api.DOMTransactionChain;
 import org.opendaylight.netconf.dom.api.NetconfDataTreeService;
-import org.opendaylight.restconf.nb.rfc8040.handlers.TransactionChainHandler;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
-import org.opendaylight.yangtools.yang.data.api.schema.LeafSetNode;
-import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
-import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodeContainer;
-import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
-import org.opendaylight.yangtools.yang.model.api.SchemaContext;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -45,21 +34,13 @@ public final class NetconfRestconfStrategy extends RestconfStrategy {
 
     private final NetconfDataTreeService netconfService;
 
-    private List<ListenableFuture<? extends DOMRpcResult>> resultsFutures;
-
     public NetconfRestconfStrategy(final NetconfDataTreeService netconfService) {
         this.netconfService = requireNonNull(netconfService);
     }
 
     @Override
-    public void prepareReadWriteExecution() {
-        resultsFutures = netconfService.lock();
-    }
-
-    @Override
-    public void cancel() {
-        netconfService.discardChanges();
-        netconfService.unlock();
+    public RestconfTransaction prepareWriteExecution() {
+        return new NetconfRestconfTransaction(netconfService);
     }
 
     @Override
@@ -86,74 +67,7 @@ public final class NetconfRestconfStrategy extends RestconfStrategy {
     }
 
     @Override
-    public void delete(final LogicalDatastoreType store, final YangInstanceIdentifier path) {
-        resultsFutures.add(netconfService.delete(store, path));
-    }
-
-    @Override
-    public void remove(final LogicalDatastoreType store, final YangInstanceIdentifier path) {
-        resultsFutures.add(netconfService.remove(store, path));
-    }
-
-    @Override
-    public void merge(final LogicalDatastoreType store, final YangInstanceIdentifier path,
-                      final NormalizedNode<?, ?> data) {
-        resultsFutures.add(netconfService.merge(store, path, data, Optional.empty()));
-    }
-
-    @Override
-    public void create(final LogicalDatastoreType store, final YangInstanceIdentifier path,
-                       final NormalizedNode<?, ?> data, final SchemaContext schemaContext) {
-        if (data instanceof MapNode || data instanceof LeafSetNode) {
-            final NormalizedNode<?, ?> emptySubTree = ImmutableNodes.fromInstanceId(schemaContext, path);
-            merge(LogicalDatastoreType.CONFIGURATION, YangInstanceIdentifier.create(emptySubTree.getIdentifier()),
-                emptySubTree);
-
-            for (final NormalizedNode<?, ?> child : ((NormalizedNodeContainer<?, ?, ?>) data).getValue()) {
-                final YangInstanceIdentifier childPath = path.node(child.getIdentifier());
-                resultsFutures.add(netconfService.create(store, childPath, child, Optional.empty()));
-            }
-        } else {
-            resultsFutures.add(netconfService.create(store, path, data, Optional.empty()));
-        }
-    }
-
-    @Override
-    public void replace(final LogicalDatastoreType store, final YangInstanceIdentifier path,
-                        final NormalizedNode<?, ?> data, final SchemaContext schemaContext) {
-        if (data instanceof MapNode || data instanceof LeafSetNode) {
-            final NormalizedNode<?, ?> emptySubTree = ImmutableNodes.fromInstanceId(schemaContext, path);
-            merge(LogicalDatastoreType.CONFIGURATION, YangInstanceIdentifier.create(emptySubTree.getIdentifier()),
-                emptySubTree);
-
-            for (final NormalizedNode<?, ?> child : ((NormalizedNodeContainer<?, ?, ?>) data).getValue()) {
-                final YangInstanceIdentifier childPath = path.node(child.getIdentifier());
-                resultsFutures.add(netconfService.replace(store, childPath, child, Optional.empty()));
-            }
-        } else {
-            resultsFutures.add(netconfService.replace(store, path, data, Optional.empty()));
-        }
-    }
-
-    @Override
-    public FluentFuture<? extends @NonNull CommitInfo> commit() {
-        return FluentFuture.from(netconfService.commit(resultsFutures));
-    }
-
-    /**
-     * As we are not using any transactions here, always return null.
-     */
-    @Override
-    public DOMTransactionChain getTransactionChain() {
-        return null;
-    }
-
-    /**
-     * As we are not using any transactions here, always return null.
-     */
-    @Override
-    public TransactionChainHandler getTransactionChainHandler() {
-        return null;
+    public void close() {
     }
 
     private static <T> FluentFuture<T> remapException(final ListenableFuture<T> input) {
diff --git a/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/transactions/NetconfRestconfTransaction.java b/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/transactions/NetconfRestconfTransaction.java
new file mode 100644 (file)
index 0000000..840a10d
--- /dev/null
@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) 2020 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.rests.transactions;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.util.concurrent.FluentFuture;
+import com.google.common.util.concurrent.ListenableFuture;
+import java.util.List;
+import java.util.Optional;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.mdsal.common.api.CommitInfo;
+import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
+import org.opendaylight.mdsal.dom.api.DOMRpcResult;
+import org.opendaylight.netconf.dom.api.NetconfDataTreeService;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafSetNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodeContainer;
+import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+final class NetconfRestconfTransaction extends RestconfTransaction {
+    private final NetconfDataTreeService netconfService;
+
+    private List<ListenableFuture<? extends DOMRpcResult>> resultsFutures;
+
+    NetconfRestconfTransaction(final NetconfDataTreeService netconfService) {
+        this.netconfService = requireNonNull(netconfService);
+        this.resultsFutures = netconfService.lock();
+    }
+
+    @Override
+    public void cancel() {
+        resultsFutures = null;
+        netconfService.discardChanges();
+        netconfService.unlock();
+    }
+
+    @Override
+    public void delete(final LogicalDatastoreType store, final YangInstanceIdentifier path) {
+        resultsFutures.add(netconfService.delete(store, path));
+    }
+
+    @Override
+    public void remove(final LogicalDatastoreType store, final YangInstanceIdentifier path) {
+        resultsFutures.add(netconfService.remove(store, path));
+    }
+
+    @Override
+    public void merge(final LogicalDatastoreType store, final YangInstanceIdentifier path,
+            final NormalizedNode<?, ?> data) {
+        resultsFutures.add(netconfService.merge(store, path, data, Optional.empty()));
+    }
+
+    @Override
+    public void create(final LogicalDatastoreType store, final YangInstanceIdentifier path,
+           final NormalizedNode<?, ?> data, final SchemaContext schemaContext) {
+        if (data instanceof MapNode || data instanceof LeafSetNode) {
+            final NormalizedNode<?, ?> emptySubTree = ImmutableNodes.fromInstanceId(schemaContext, path);
+            merge(LogicalDatastoreType.CONFIGURATION, YangInstanceIdentifier.create(emptySubTree.getIdentifier()),
+                emptySubTree);
+
+            for (final NormalizedNode<?, ?> child : ((NormalizedNodeContainer<?, ?, ?>) data).getValue()) {
+                final YangInstanceIdentifier childPath = path.node(child.getIdentifier());
+                resultsFutures.add(netconfService.create(store, childPath, child, Optional.empty()));
+            }
+        } else {
+            resultsFutures.add(netconfService.create(store, path, data, Optional.empty()));
+        }
+    }
+
+    @Override
+    public void replace(final LogicalDatastoreType store, final YangInstanceIdentifier path,
+            final NormalizedNode<?, ?> data, final SchemaContext schemaContext) {
+        if (data instanceof MapNode || data instanceof LeafSetNode) {
+            final NormalizedNode<?, ?> emptySubTree = ImmutableNodes.fromInstanceId(schemaContext, path);
+            merge(LogicalDatastoreType.CONFIGURATION, YangInstanceIdentifier.create(emptySubTree.getIdentifier()),
+                emptySubTree);
+
+            for (final NormalizedNode<?, ?> child : ((NormalizedNodeContainer<?, ?, ?>) data).getValue()) {
+                final YangInstanceIdentifier childPath = path.node(child.getIdentifier());
+                resultsFutures.add(netconfService.replace(store, childPath, child, Optional.empty()));
+            }
+        } else {
+            resultsFutures.add(netconfService.replace(store, path, data, Optional.empty()));
+        }
+    }
+
+    @Override
+    public FluentFuture<? extends @NonNull CommitInfo> commit() {
+        return FluentFuture.from(netconfService.commit(resultsFutures));
+    }
+}
index bfecd8893ac7341ada18bd2dc5539b925943c77d..dc1454a09d38e36d6441cf359b1a611361bc4b7f 100644 (file)
@@ -10,18 +10,12 @@ package org.opendaylight.restconf.nb.rfc8040.rests.transactions;
 import com.google.common.util.concurrent.FluentFuture;
 import com.google.common.util.concurrent.ListenableFuture;
 import java.util.Optional;
-import org.eclipse.jdt.annotation.NonNull;
-import org.eclipse.jdt.annotation.Nullable;
-import org.opendaylight.mdsal.common.api.CommitInfo;
 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
 import org.opendaylight.mdsal.dom.api.DOMDataBroker;
 import org.opendaylight.mdsal.dom.api.DOMMountPoint;
-import org.opendaylight.mdsal.dom.api.DOMTransactionChain;
 import org.opendaylight.netconf.dom.api.NetconfDataTreeService;
-import org.opendaylight.restconf.nb.rfc8040.handlers.TransactionChainHandler;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
-import org.opendaylight.yangtools.yang.model.api.SchemaContext;
 
 /**
  * Baseline execution strategy for various RESTCONF operations.
@@ -31,7 +25,7 @@ import org.opendaylight.yangtools.yang.model.api.SchemaContext;
  */
 // FIXME: it seems the first three operations deal with lifecycle of a transaction, while others invoke various
 //        operations. This should be handled through proper allocation indirection.
-public abstract class RestconfStrategy {
+public abstract class RestconfStrategy implements AutoCloseable {
     RestconfStrategy() {
         // Hidden on purpose
     }
@@ -55,20 +49,11 @@ public abstract class RestconfStrategy {
 
     /**
      * Lock the entire datastore.
-     */
-    public abstract void prepareReadWriteExecution();
-
-    /**
-     * Confirm previous operations.
      *
-     * @return a FluentFuture containing the result of the commit information
-     */
-    public abstract FluentFuture<? extends @NonNull CommitInfo> commit();
-
-    /**
-     * Rollback changes and unlock the datastore.
+     * @return A {@link RestconfTransaction}. This transaction needs to be either committed or canceled before doing
+     *         anything else.
      */
-    public abstract void cancel();
+    public abstract RestconfTransaction prepareWriteExecution();
 
     /**
      * Read data from the datastore.
@@ -89,67 +74,6 @@ public abstract class RestconfStrategy {
      */
     public abstract FluentFuture<Boolean> exists(LogicalDatastoreType store, YangInstanceIdentifier path);
 
-    /**
-     * Delete data from the datastore.
-     *
-     * @param store the logical data store which should be modified
-     * @param path the data object path
-     */
-    public abstract void delete(LogicalDatastoreType store, YangInstanceIdentifier path);
-
-    /**
-     * Remove data from the datastore.
-     *
-     * @param store the logical data store which should be modified
-     * @param path  the data object path
-     */
-    public abstract void remove(LogicalDatastoreType store, YangInstanceIdentifier path);
-
-    /**
-     * Merges a piece of data with the existing data at a specified path.
-     *
-     * @param store the logical data store which should be modified
-     * @param path the data object path
-     * @param data the data object to be merged to the specified path
-     */
-    public abstract void merge(LogicalDatastoreType store, YangInstanceIdentifier path, NormalizedNode<?, ?> data);
-
-    /**
-     * Stores a piece of data at the specified path.
-     *
-     * @param store the logical data store which should be modified
-     * @param path  the data object path
-     * @param data  the data object to be merged to the specified path
-     * @param schemaContext  static view of compiled yang files
-     */
-    public abstract void create(LogicalDatastoreType store, YangInstanceIdentifier path, NormalizedNode<?, ?> data,
-                SchemaContext schemaContext);
-
-    /**
-     * Replace a piece of data at the specified path.
-     *
-     * @param store        the logical data store which should be modified
-     * @param path         the data object path
-     * @param data         the data object to be merged to the specified path
-     * @param schemaContext  static view of compiled yang files
-     */
-    public abstract void replace(LogicalDatastoreType store, YangInstanceIdentifier path, NormalizedNode<?, ?> data,
-                 SchemaContext schemaContext);
-
-
-    /**
-     * Get transaction chain for creating specific transaction for specific operation.
-     *
-     * @return transaction chain or null
-     */
-    // FIXME: these look like an implementation detail
-    public abstract @Nullable DOMTransactionChain getTransactionChain();
-
-    /**
-     * Get transaction chain handler for creating new transaction chain.
-     *
-     * @return {@link TransactionChainHandler} or null
-     */
-    // FIXME: these look like an implementation detail
-    public abstract @Nullable TransactionChainHandler getTransactionChainHandler();
+    @Override
+    public abstract void close();
 }
diff --git a/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/transactions/RestconfTransaction.java b/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/transactions/RestconfTransaction.java
new file mode 100644 (file)
index 0000000..ef8e3dd
--- /dev/null
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 2020 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.rests.transactions;
+
+import com.google.common.annotations.Beta;
+import com.google.common.util.concurrent.FluentFuture;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.mdsal.common.api.CommitInfo;
+import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+/**
+ * A handle to a set of operations being executed atomically on top of some backing store.
+ */
+// FIXME: it seems the first two operations deal with lifecycle of a transaction, while others invoke various
+//        operations. This should be handled through proper allocation indirection.
+@Beta
+public abstract class RestconfTransaction {
+    RestconfTransaction() {
+        // Hidden on purpose
+    }
+
+    /**
+     * Rollback changes and unlock the datastore.
+     */
+    // FIXME: this looks synchronous, but it should not be
+    public abstract void cancel();
+
+    /**
+     * Confirm previous operations.
+     *
+     * @return a FluentFuture containing the result of the commit information
+     */
+    public abstract FluentFuture<? extends @NonNull CommitInfo> commit();
+
+    /**
+     * Delete data from the datastore.
+     *
+     * @param store the logical data store which should be modified
+     * @param path the data object path
+     */
+    public abstract void delete(LogicalDatastoreType store, YangInstanceIdentifier path);
+
+    /**
+     * Remove data from the datastore.
+     *
+     * @param store the logical data store which should be modified
+     * @param path  the data object path
+     */
+    public abstract void remove(LogicalDatastoreType store, YangInstanceIdentifier path);
+
+    /**
+     * Merges a piece of data with the existing data at a specified path.
+     *
+     * @param store the logical data store which should be modified
+     * @param path the data object path
+     * @param data the data object to be merged to the specified path
+     */
+    public abstract void merge(LogicalDatastoreType store, YangInstanceIdentifier path, NormalizedNode<?, ?> data);
+
+    /**
+     * Stores a piece of data at the specified path.
+     *
+     * @param store         the logical data store which should be modified
+     * @param path          the data object path
+     * @param data          the data object to be merged to the specified path
+     * @param schemaContext static view of compiled yang files
+     */
+    public abstract void create(LogicalDatastoreType store, YangInstanceIdentifier path, NormalizedNode<?, ?> data,
+                                SchemaContext schemaContext);
+
+    /**
+     * Replace a piece of data at the specified path.
+     *
+     * @param store         the logical data store which should be modified
+     * @param path          the data object path
+     * @param data          the data object to be merged to the specified path
+     * @param schemaContext static view of compiled yang files
+     */
+    public abstract void replace(LogicalDatastoreType store, YangInstanceIdentifier path, NormalizedNode<?, ?> data,
+                                 SchemaContext schemaContext);
+}
index ba6d10b1cdf88205307fdd388f13cd9b9f8d5ae7..955267935625cab3bf49c2b247fc5ed84a2bd66f 100644 (file)
@@ -16,6 +16,7 @@ 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.rests.transactions.RestconfStrategy;
+import org.opendaylight.restconf.nb.rfc8040.rests.transactions.RestconfTransaction;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -38,18 +39,18 @@ public final class DeleteDataTransactionUtil {
      * @return {@link Response}
      */
     public static Response deleteData(final RestconfStrategy strategy, final YangInstanceIdentifier path) {
-        strategy.prepareReadWriteExecution();
+        final RestconfTransaction transaction = strategy.prepareWriteExecution();
         try {
-            strategy.delete(LogicalDatastoreType.CONFIGURATION, path);
+            transaction.delete(LogicalDatastoreType.CONFIGURATION, path);
         } catch (RestconfDocumentedException e) {
             // close transaction if any and pass exception further
-            strategy.cancel();
+            transaction.cancel();
             throw e;
         }
-        final FluentFuture<? extends CommitInfo> future = strategy.commit();
+        final FluentFuture<? extends CommitInfo> future = transaction.commit();
         final ResponseFactory response = new ResponseFactory(Status.NO_CONTENT);
         //This method will close transactionChain if any
-        FutureCallbackTx.addCallback(future, DELETE_TX_TYPE, response, strategy.getTransactionChain(), path);
+        FutureCallbackTx.addCallback(future, DELETE_TX_TYPE, response, strategy, path);
         return response.build();
     }
 
@@ -57,17 +58,15 @@ public final class DeleteDataTransactionUtil {
      * Check if items already exists at specified {@code path}. Throws {@link RestconfDocumentedException} if
      * data does NOT already exists.
      *
-     * @param strategy      Object that perform the actual DS operations
-     * @param store         Datastore
-     * @param path          Path to be checked
-     * @param operationType Type of operation (READ, POST, PUT, DELETE...)
+     * @param isExistsFuture if checked data exists
+     * @param path           Path to be checked
+     * @param operationType  Type of operation (READ, POST, PUT, DELETE...)
      */
-    public static void checkItemExists(final RestconfStrategy strategy,
-                                       final LogicalDatastoreType store, final YangInstanceIdentifier path,
+    public static void checkItemExists(final FluentFuture<Boolean> isExistsFuture,
+                                       final YangInstanceIdentifier path,
                                        final String operationType) {
-        final FluentFuture<Boolean> future = strategy.exists(store, path);
         final FutureDataFactory<Boolean> response = new FutureDataFactory<>();
-        FutureCallbackTx.addCallback(future, operationType, response);
+        FutureCallbackTx.addCallback(isExistsFuture, operationType, response);
 
         if (!response.result) {
             LOG.trace("Operation via Restconf was not executed because data at {} does not exist", path);
index b68cd5177dda5f901eaaa60e5598a740676c8667..e4c88f22a683aa27aa7a4902fc885a1fc0cfe190 100644 (file)
@@ -16,13 +16,13 @@ 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.DocumentedException;
 import org.opendaylight.netconf.api.NetconfDocumentedException;
 import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
 import org.opendaylight.restconf.common.errors.RestconfError;
+import org.opendaylight.restconf.nb.rfc8040.rests.transactions.RestconfStrategy;
 import org.opendaylight.yangtools.yang.common.RpcError;
 import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
@@ -66,38 +66,18 @@ final class FutureCallbackTx {
      *             type of operation (READ, POST, PUT, DELETE)
      * @param dataFactory
      *             factory setting result
-     * @param transactionChain
-     *             transaction chain
+     * @param strategy Strategy for various RESTCONF operations
+     * @param path unique identifier of a particular node instance in the data tree
      * @throws RestconfDocumentedException
      *             if the Future throws an exception
      */
     // FIXME: this is a *synchronous operation* and has to die
     static <T> void addCallback(final ListenableFuture<T> listenableFuture, final String txType,
                                 final FutureDataFactory<? super T> dataFactory,
-                                @Nullable final DOMTransactionChain transactionChain)
+                                @Nullable final RestconfStrategy strategy,
+                                final YangInstanceIdentifier path)
         throws RestconfDocumentedException {
-        addCallback(listenableFuture, txType, dataFactory, transactionChain, 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
-     * @param path
-     *             unique identifier of a particular node instance in the data tree.
-     * @throws RestconfDocumentedException
-     *             if the Future throws an exception
-     */
-    static <T> void addCallback(final ListenableFuture<T> listenableFuture, final String txType,
-            final FutureDataFactory<? super T> dataFactory, @Nullable final DOMTransactionChain transactionChain,
-                                YangInstanceIdentifier path) throws RestconfDocumentedException {
         try {
             final T result = listenableFuture.get();
             dataFactory.setResult(result);
@@ -153,8 +133,8 @@ final class FutureCallbackTx {
                 throw new RestconfDocumentedException("Transaction failed", e);
             }
         } finally {
-            if (transactionChain != null) {
-                transactionChain.close();
+            if (strategy != null) {
+                strategy.close();
             }
         }
     }
index 4d165dc1e7db83463c40115fe825aee94beb0e22..43790a80d34f1fb59832c02e986c3881e98c11e3 100644 (file)
@@ -25,6 +25,7 @@ import org.opendaylight.restconf.common.patch.PatchEntity;
 import org.opendaylight.restconf.common.patch.PatchStatusContext;
 import org.opendaylight.restconf.common.patch.PatchStatusEntity;
 import org.opendaylight.restconf.nb.rfc8040.rests.transactions.RestconfStrategy;
+import org.opendaylight.restconf.nb.rfc8040.rests.transactions.RestconfTransaction;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
@@ -53,7 +54,7 @@ public final class PatchDataTransactionUtil {
                                                final EffectiveModelContext schemaContext) {
         final List<PatchStatusEntity> editCollection = new ArrayList<>();
         boolean noError = true;
-        strategy.prepareReadWriteExecution();
+        final RestconfTransaction transaction = strategy.prepareWriteExecution();
 
         for (final PatchEntity patchEntity : context.getData()) {
             if (noError) {
@@ -61,7 +62,7 @@ public final class PatchDataTransactionUtil {
                     case CREATE:
                         try {
                             createDataWithinTransaction(patchEntity.getTargetNode(), patchEntity.getNode(),
-                                schemaContext, strategy);
+                                schemaContext, transaction);
                             editCollection.add(new PatchStatusEntity(patchEntity.getEditId(), true, null));
                         } catch (final RestconfDocumentedException e) {
                             editCollection.add(new PatchStatusEntity(patchEntity.getEditId(),
@@ -71,7 +72,7 @@ public final class PatchDataTransactionUtil {
                         break;
                     case DELETE:
                         try {
-                            deleteDataWithinTransaction(patchEntity.getTargetNode(), strategy);
+                            deleteDataWithinTransaction(patchEntity.getTargetNode(), transaction);
                             editCollection.add(new PatchStatusEntity(patchEntity.getEditId(), true, null));
                         } catch (final RestconfDocumentedException e) {
                             editCollection.add(new PatchStatusEntity(patchEntity.getEditId(),
@@ -82,7 +83,7 @@ public final class PatchDataTransactionUtil {
                     case MERGE:
                         try {
                             mergeDataWithinTransaction(patchEntity.getTargetNode(), patchEntity.getNode(),
-                                schemaContext, strategy);
+                                schemaContext, transaction);
                             editCollection.add(new PatchStatusEntity(patchEntity.getEditId(), true, null));
                         } catch (final RestconfDocumentedException e) {
                             editCollection.add(new PatchStatusEntity(patchEntity.getEditId(),
@@ -93,7 +94,7 @@ public final class PatchDataTransactionUtil {
                     case REPLACE:
                         try {
                             replaceDataWithinTransaction(patchEntity.getTargetNode(), patchEntity.getNode(),
-                                schemaContext, strategy);
+                                schemaContext, transaction);
                             editCollection.add(new PatchStatusEntity(patchEntity.getEditId(), true, null));
                         } catch (final RestconfDocumentedException e) {
                             editCollection.add(new PatchStatusEntity(patchEntity.getEditId(),
@@ -103,7 +104,7 @@ public final class PatchDataTransactionUtil {
                         break;
                     case REMOVE:
                         try {
-                            removeDataWithinTransaction(patchEntity.getTargetNode(), strategy);
+                            removeDataWithinTransaction(patchEntity.getTargetNode(), transaction);
                             editCollection.add(new PatchStatusEntity(patchEntity.getEditId(), true, null));
                         } catch (final RestconfDocumentedException e) {
                             editCollection.add(new PatchStatusEntity(patchEntity.getEditId(),
@@ -126,11 +127,11 @@ public final class PatchDataTransactionUtil {
         // if no errors then submit transaction, otherwise cancel
         if (noError) {
             final ResponseFactory response = new ResponseFactory(Status.OK);
-            final FluentFuture<? extends CommitInfo> future = strategy.commit();
+            final FluentFuture<? extends CommitInfo> future = transaction.commit();
 
             try {
                 //This method will close transactionChain if any
-                FutureCallbackTx.addCallback(future, PATCH_TX_TYPE, response, strategy.getTransactionChain());
+                FutureCallbackTx.addCallback(future, PATCH_TX_TYPE, response, strategy, null);
             } 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,
@@ -139,7 +140,7 @@ public final class PatchDataTransactionUtil {
 
             return new PatchStatusContext(context.getPatchId(), ImmutableList.copyOf(editCollection), true, null);
         } else {
-            strategy.cancel();
+            transaction.cancel();
             return new PatchStatusContext(context.getPatchId(), ImmutableList.copyOf(editCollection), false, null);
         }
     }
@@ -149,27 +150,27 @@ public final class PatchDataTransactionUtil {
      *
      * @param path          Path for data to be created
      * @param payload       Data to be created
-     * @param strategy      Object that perform the actual DS operations
+     * @param transaction   A handle to a set of DS operations
      */
     private static void createDataWithinTransaction(final YangInstanceIdentifier path,
                                                     final NormalizedNode<?, ?> payload,
                                                     final EffectiveModelContext schemaContext,
-                                                    final RestconfStrategy strategy) {
+                                                    final RestconfTransaction transaction) {
         LOG.trace("POST {} within Restconf Patch: {} with payload {}", LogicalDatastoreType.CONFIGURATION.name(),
             path, payload);
-        createData(payload, path, strategy, schemaContext, true);
+        transaction.create(LogicalDatastoreType.CONFIGURATION, path, payload, schemaContext);
     }
 
     /**
      * Remove data within one transaction.
      *
      * @param path     Path for data to be deleted
-     * @param strategy Object that perform the actual DS operations
+     * @param transaction   A handle to a set of DS operations
      */
     private static void deleteDataWithinTransaction(final YangInstanceIdentifier path,
-                                                    final RestconfStrategy strategy) {
+                                                    final RestconfTransaction transaction) {
         LOG.trace("Delete {} within Restconf Patch: {}", LogicalDatastoreType.CONFIGURATION.name(), path);
-        strategy.delete(LogicalDatastoreType.CONFIGURATION, path);
+        transaction.delete(LogicalDatastoreType.CONFIGURATION, path);
     }
 
     /**
@@ -177,28 +178,28 @@ public final class PatchDataTransactionUtil {
      *
      * @param path     Path for data to be merged
      * @param payload  Data to be merged
-     * @param strategy Object that perform the actual DS operations
+     * @param transaction   A handle to a set of DS operations
      */
     private static void mergeDataWithinTransaction(final YangInstanceIdentifier path,
                                                    final NormalizedNode<?, ?> payload,
                                                    final EffectiveModelContext schemaContext,
-                                                   final RestconfStrategy strategy) {
+                                                   final RestconfTransaction transaction) {
         LOG.trace("Merge {} within Restconf Patch: {} with payload {}", LogicalDatastoreType.CONFIGURATION.name(),
             path, payload);
-        TransactionUtil.ensureParentsByMerge(path, schemaContext, strategy);
-        strategy.merge(LogicalDatastoreType.CONFIGURATION, path, payload);
+        TransactionUtil.ensureParentsByMerge(path, schemaContext, transaction);
+        transaction.merge(LogicalDatastoreType.CONFIGURATION, path, payload);
     }
 
     /**
      * Do NOT check if data exists and remove it within one transaction.
      *
      * @param path     Path for data to be deleted
-     * @param strategy Object that perform the actual DS operations
+     * @param transaction   A handle to a set of DS operations
      */
     private static void removeDataWithinTransaction(final YangInstanceIdentifier path,
-                                                    final RestconfStrategy strategy) {
+                                                    final RestconfTransaction transaction) {
         LOG.trace("Remove {} within Restconf Patch: {}", LogicalDatastoreType.CONFIGURATION.name(), path);
-        strategy.remove(LogicalDatastoreType.CONFIGURATION, path);
+        transaction.remove(LogicalDatastoreType.CONFIGURATION, path);
     }
 
     /**
@@ -206,35 +207,14 @@ public final class PatchDataTransactionUtil {
      *
      * @param path          Path for data to be created
      * @param payload       Data to be created
-     * @param strategy      Object that perform the actual DS operations
+     * @param transaction   A handle to a set of DS operations
      */
     private static void replaceDataWithinTransaction(final YangInstanceIdentifier path,
                                                      final NormalizedNode<?, ?> payload,
                                                      final EffectiveModelContext schemaContext,
-                                                     final RestconfStrategy strategy) {
+                                                     final RestconfTransaction transaction) {
         LOG.trace("PUT {} within Restconf Patch: {} with payload {}",
             LogicalDatastoreType.CONFIGURATION.name(), path, payload);
-        createData(payload, path, strategy, schemaContext, false);
-    }
-
-    /**
-     * Create data within one transaction. If {@code errorIfExists} is set to {@code true} then data will be checked
-     * for existence before created, otherwise they will be overwritten.
-     *
-     * @param data          Data to be created
-     * @param path          Path for data to be created
-     * @param strategy      Object that perform the actual DS operations
-     * @param errorIfExists Enable checking for existence of data (throws error if already exists)
-     */
-    private static void createData(final NormalizedNode<?, ?> data,
-                                   final YangInstanceIdentifier path,
-                                   final RestconfStrategy strategy,
-                                   final EffectiveModelContext schemaContext,
-                                   final boolean errorIfExists) {
-        if (errorIfExists) {
-            strategy.create(LogicalDatastoreType.CONFIGURATION, path, data, schemaContext);
-        } else {
-            strategy.replace(LogicalDatastoreType.CONFIGURATION, path, data, schemaContext);
-        }
+        transaction.replace(LogicalDatastoreType.CONFIGURATION, path, payload, schemaContext);
     }
 }
index 88f463077c9975313a62c9f4463804660547c7d9..cad30a9127c6a9e4ff6773417addacf07ef1cca6 100644 (file)
@@ -17,6 +17,7 @@ import org.opendaylight.mdsal.dom.api.DOMTransactionChain;
 import org.opendaylight.restconf.common.context.NormalizedNodeContext;
 import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
 import org.opendaylight.restconf.nb.rfc8040.rests.transactions.RestconfStrategy;
+import org.opendaylight.restconf.nb.rfc8040.rests.transactions.RestconfTransaction;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
@@ -46,25 +47,24 @@ public final class PlainPatchDataTransactionUtil {
                                      final RestconfStrategy strategy,
                                      final EffectiveModelContext schemaContext) {
 
-        strategy.prepareReadWriteExecution();
+        final RestconfTransaction transaction = strategy.prepareWriteExecution();
         YangInstanceIdentifier path = payload.getInstanceIdentifierContext().getInstanceIdentifier();
         NormalizedNode<?, ?> data = payload.getData();
 
         try {
             LOG.trace("Merge CONFIGURATION within Restconf Patch: {} with payload {}", path, data);
-            TransactionUtil.ensureParentsByMerge(path, schemaContext, strategy);
-            strategy.merge(LogicalDatastoreType.CONFIGURATION, path, data);
+            TransactionUtil.ensureParentsByMerge(path, schemaContext, transaction);
+            transaction.merge(LogicalDatastoreType.CONFIGURATION, path, data);
         } catch (final RestconfDocumentedException e) {
-            strategy.cancel();
+            transaction.cancel();
             throw new IllegalArgumentException(e);
         }
 
-        final FluentFuture<? extends CommitInfo> future = strategy.commit();
+        final FluentFuture<? extends CommitInfo> future = transaction.commit();
         final ResponseFactory response = new ResponseFactory(Status.OK);
 
-        FutureCallbackTx.addCallback(future, PatchDataTransactionUtil.PATCH_TX_TYPE, response,
-                strategy.getTransactionChain()); // closes transactionChain if any, may throw
-
+        // closes transactionChain if any, may throw
+        FutureCallbackTx.addCallback(future, PatchDataTransactionUtil.PATCH_TX_TYPE, response, strategy, path);
         return response.build();
     }
 }
index d5b39fd8a90b239453740e69ca24a83c78572a6a..905193798837da4f6defbaca2262499e87c0af93 100644 (file)
@@ -24,6 +24,7 @@ import org.opendaylight.restconf.common.errors.RestconfError;
 import org.opendaylight.restconf.common.errors.RestconfError.ErrorTag;
 import org.opendaylight.restconf.common.errors.RestconfError.ErrorType;
 import org.opendaylight.restconf.nb.rfc8040.rests.transactions.RestconfStrategy;
+import org.opendaylight.restconf.nb.rfc8040.rests.transactions.RestconfTransaction;
 import org.opendaylight.restconf.nb.rfc8040.rests.utils.RestconfDataServiceConstant.PostPutQueryParameters.Insert;
 import org.opendaylight.restconf.nb.rfc8040.utils.parser.ParserIdentifier;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
@@ -72,7 +73,7 @@ public final class PostDataTransactionUtil {
         final URI location = resolveLocation(uriInfo, path, schemaContext, payload.getData());
         final ResponseFactory dataFactory = new ResponseFactory(Status.CREATED).location(location);
         //This method will close transactionChain if any
-        FutureCallbackTx.addCallback(future, POST_TX_TYPE, dataFactory, strategy.getTransactionChain(), path);
+        FutureCallbackTx.addCallback(future, POST_TX_TYPE, dataFactory, strategy, path);
         return dataFactory.build();
     }
 
@@ -92,10 +93,10 @@ public final class PostDataTransactionUtil {
                                                                  final RestconfStrategy strategy,
                                                                  final EffectiveModelContext schemaContext,
                                                                  final Insert insert, final String point) {
-        strategy.prepareReadWriteExecution();
+        final RestconfTransaction transaction = strategy.prepareWriteExecution();
         if (insert == null) {
-            makePost(path, data, schemaContext, strategy);
-            return strategy.commit();
+            makePost(path, data, schemaContext, transaction);
+            return transaction.commit();
         }
 
         PutDataTransactionUtil.checkListAndOrderedType(schemaContext, path);
@@ -104,38 +105,38 @@ public final class PostDataTransactionUtil {
             case FIRST:
                 readData = PutDataTransactionUtil.readList(strategy, path.getParent().getParent());
                 if (readData == null || ((NormalizedNodeContainer<?, ?, ?>) readData).getValue().isEmpty()) {
-                    strategy.replace(LogicalDatastoreType.CONFIGURATION, path, data, schemaContext);
-                    return strategy.commit();
+                    transaction.replace(LogicalDatastoreType.CONFIGURATION, path, data, schemaContext);
+                    return transaction.commit();
                 }
-                checkItemDoesNotExists(strategy, LogicalDatastoreType.CONFIGURATION, path);
-                strategy.remove(LogicalDatastoreType.CONFIGURATION, path.getParent().getParent());
-                strategy.replace(LogicalDatastoreType.CONFIGURATION, path, data, schemaContext);
-                strategy.replace(LogicalDatastoreType.CONFIGURATION, path.getParent().getParent(), readData,
+                checkItemDoesNotExists(strategy.exists(LogicalDatastoreType.CONFIGURATION, path), path);
+                transaction.remove(LogicalDatastoreType.CONFIGURATION, path.getParent().getParent());
+                transaction.replace(LogicalDatastoreType.CONFIGURATION, path, data, schemaContext);
+                transaction.replace(LogicalDatastoreType.CONFIGURATION, path.getParent().getParent(), readData,
                     schemaContext);
-                return strategy.commit();
+                return transaction.commit();
             case LAST:
-                makePost(path, data, schemaContext, strategy);
-                return strategy.commit();
+                makePost(path, data, schemaContext, transaction);
+                return transaction.commit();
             case BEFORE:
                 readData = PutDataTransactionUtil.readList(strategy, path.getParent().getParent());
                 if (readData == null || ((NormalizedNodeContainer<?, ?, ?>) readData).getValue().isEmpty()) {
-                    strategy.replace(LogicalDatastoreType.CONFIGURATION, path, data, schemaContext);
-                    return strategy.commit();
+                    transaction.replace(LogicalDatastoreType.CONFIGURATION, path, data, schemaContext);
+                    return transaction.commit();
                 }
-                checkItemDoesNotExists(strategy, LogicalDatastoreType.CONFIGURATION, path);
+                checkItemDoesNotExists(strategy.exists(LogicalDatastoreType.CONFIGURATION, path), path);
                 insertWithPointPost(path, data, schemaContext, point,
-                    (NormalizedNodeContainer<?, ?, NormalizedNode<?, ?>>) readData, true, strategy);
-                return strategy.commit();
+                    (NormalizedNodeContainer<?, ?, NormalizedNode<?, ?>>) readData, true, transaction);
+                return transaction.commit();
             case AFTER:
                 readData = PutDataTransactionUtil.readList(strategy, path.getParent().getParent());
                 if (readData == null || ((NormalizedNodeContainer<?, ?, ?>) readData).getValue().isEmpty()) {
-                    strategy.replace(LogicalDatastoreType.CONFIGURATION, path, data, schemaContext);
-                    return strategy.commit();
+                    transaction.replace(LogicalDatastoreType.CONFIGURATION, path, data, schemaContext);
+                    return transaction.commit();
                 }
-                checkItemDoesNotExists(strategy, LogicalDatastoreType.CONFIGURATION, path);
+                checkItemDoesNotExists(strategy.exists(LogicalDatastoreType.CONFIGURATION, path), path);
                 insertWithPointPost(path, data, schemaContext, point,
-                    (NormalizedNodeContainer<?, ?, NormalizedNode<?, ?>>) readData, false, strategy);
-                return strategy.commit();
+                    (NormalizedNodeContainer<?, ?, NormalizedNode<?, ?>>) readData, false, transaction);
+                return transaction.commit();
             default:
                 throw new RestconfDocumentedException(
                     "Used bad value of insert parameter. Possible values are first, last, before or after, but was: "
@@ -147,9 +148,9 @@ public final class PostDataTransactionUtil {
                                             final NormalizedNode<?, ?> data,
                                             final EffectiveModelContext schemaContext, final String point,
                                             final NormalizedNodeContainer<?, ?, NormalizedNode<?, ?>> readList,
-                                            final boolean before, final RestconfStrategy strategy) {
+                                            final boolean before, final RestconfTransaction transaction) {
         final YangInstanceIdentifier parent = path.getParent().getParent();
-        strategy.remove(LogicalDatastoreType.CONFIGURATION, parent);
+        transaction.remove(LogicalDatastoreType.CONFIGURATION, parent);
         final InstanceIdentifierContext<?> instanceIdentifier =
             ParserIdentifier.toInstanceIdentifier(point, schemaContext, Optional.empty());
         int lastItemPosition = 0;
@@ -164,25 +165,25 @@ public final class PostDataTransactionUtil {
         }
         int lastInsertedPosition = 0;
         final NormalizedNode<?, ?> emptySubtree = ImmutableNodes.fromInstanceId(schemaContext, parent);
-        strategy.merge(LogicalDatastoreType.CONFIGURATION,
+        transaction.merge(LogicalDatastoreType.CONFIGURATION,
             YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
         for (final NormalizedNode<?, ?> nodeChild : readList.getValue()) {
             if (lastInsertedPosition == lastItemPosition) {
-                strategy.replace(LogicalDatastoreType.CONFIGURATION, path, data, schemaContext);
+                transaction.replace(LogicalDatastoreType.CONFIGURATION, path, data, schemaContext);
             }
             final YangInstanceIdentifier childPath = parent.node(nodeChild.getIdentifier());
-            strategy.replace(LogicalDatastoreType.CONFIGURATION, childPath, nodeChild, schemaContext);
+            transaction.replace(LogicalDatastoreType.CONFIGURATION, childPath, nodeChild, schemaContext);
             lastInsertedPosition++;
         }
     }
 
     private static void makePost(final YangInstanceIdentifier path, final NormalizedNode<?, ?> data,
-                                 final SchemaContext schemaContext, final RestconfStrategy strategy) {
+                                 final SchemaContext schemaContext, final RestconfTransaction transaction) {
         try {
-            strategy.create(LogicalDatastoreType.CONFIGURATION, path, data, schemaContext);
+            transaction.create(LogicalDatastoreType.CONFIGURATION, path, data, schemaContext);
         } catch (RestconfDocumentedException e) {
             // close transaction if any and pass exception further
-            strategy.cancel();
+            transaction.cancel();
             throw e;
         }
     }
@@ -219,16 +220,13 @@ public final class PostDataTransactionUtil {
      * Check if items do NOT already exists at specified {@code path}. Throws {@link RestconfDocumentedException} if
      * data already exists.
      *
-     * @param strategy      Object that perform the actual DS operations
-     * @param store         Datastore
-     * @param path          Path to be checked
+     * @param isExistsFuture if checked data exists
+     * @param path           Path to be checked
      */
-    public static void checkItemDoesNotExists(final RestconfStrategy strategy,
-                                               final LogicalDatastoreType store, final YangInstanceIdentifier path) {
-        final FluentFuture<Boolean> future = strategy.exists(store, path);
+    public static void checkItemDoesNotExists(final FluentFuture<Boolean> isExistsFuture,
+                                              final YangInstanceIdentifier path) {
         final FutureDataFactory<Boolean> response = new FutureDataFactory<>();
-
-        FutureCallbackTx.addCallback(future, POST_TX_TYPE, response);
+        FutureCallbackTx.addCallback(isExistsFuture, POST_TX_TYPE, response);
 
         if (response.result) {
             LOG.trace("Operation via Restconf was not executed because data at {} already exists", path);
index 0d42f064ba95d4b9b140122cc81ab96cf76fb717..ae29f56a8b829852f1d2cf106839f4cfedd2bcf5 100644 (file)
@@ -24,6 +24,7 @@ import org.opendaylight.restconf.common.errors.RestconfError;
 import org.opendaylight.restconf.common.errors.RestconfError.ErrorTag;
 import org.opendaylight.restconf.common.errors.RestconfError.ErrorType;
 import org.opendaylight.restconf.nb.rfc8040.rests.transactions.RestconfStrategy;
+import org.opendaylight.restconf.nb.rfc8040.rests.transactions.RestconfTransaction;
 import org.opendaylight.restconf.nb.rfc8040.rests.utils.RestconfDataServiceConstant.PostPutQueryParameters.Insert;
 import org.opendaylight.restconf.nb.rfc8040.utils.parser.ParserIdentifier;
 import org.opendaylight.yangtools.yang.common.QName;
@@ -145,17 +146,17 @@ public final class PutDataTransactionUtil {
                                    final RestconfStrategy strategy, final Insert insert, final String point) {
         final YangInstanceIdentifier path = payload.getInstanceIdentifierContext().getInstanceIdentifier();
 
-        strategy.prepareReadWriteExecution();
         final FluentFuture<Boolean> existsFuture = strategy.exists(LogicalDatastoreType.CONFIGURATION, path);
+        final FutureDataFactory<Boolean> existsResponse = new FutureDataFactory<>();
+        FutureCallbackTx.addCallback(existsFuture, PUT_TX_TYPE, existsResponse);
+
+        final ResponseFactory responseFactory =
+            new ResponseFactory(existsResponse.result ? Status.NO_CONTENT : Status.CREATED);
         final FluentFuture<? extends CommitInfo> submitData = submitData(path, schemaContext, strategy,
-                payload.getData(), insert, point);
-        final ResponseFactory response = new ResponseFactory();
+            payload.getData(), insert, point);
         //This method will close transactionChain if any
-        FutureCallbackTx.addCallback(submitData, PUT_TX_TYPE, response, strategy.getTransactionChain());
-
-        final FutureDataFactory<Boolean> isExists = new FutureDataFactory<>();
-        FutureCallbackTx.addCallback(existsFuture, PUT_TX_TYPE, isExists);
-        return response.status(isExists.result ? Status.NO_CONTENT : Status.CREATED).build();
+        FutureCallbackTx.addCallback(submitData, PUT_TX_TYPE, responseFactory, strategy, path);
+        return responseFactory.build();
     }
 
     /**
@@ -174,8 +175,9 @@ public final class PutDataTransactionUtil {
                                                                  final RestconfStrategy strategy,
                                                                  final NormalizedNode<?, ?> data,
                                                                  final Insert insert, final String point) {
+        final RestconfTransaction transaction = strategy.prepareWriteExecution();
         if (insert == null) {
-            return makePut(path, schemaContext, strategy, data);
+            return makePut(path, schemaContext, transaction, data);
         }
 
         checkListAndOrderedType(schemaContext, path);
@@ -184,30 +186,30 @@ public final class PutDataTransactionUtil {
             case FIRST:
                 readData = readList(strategy, path.getParent());
                 if (readData == null || ((NormalizedNodeContainer<?, ?, ?>) readData).getValue().isEmpty()) {
-                    return makePut(path, schemaContext, strategy, data);
+                    return makePut(path, schemaContext, transaction, data);
                 }
-                strategy.remove(LogicalDatastoreType.CONFIGURATION, path.getParent());
-                strategy.replace(LogicalDatastoreType.CONFIGURATION, path, data, schemaContext);
-                strategy.replace(LogicalDatastoreType.CONFIGURATION, path.getParent(), readData, schemaContext);
-                return strategy.commit();
+                transaction.remove(LogicalDatastoreType.CONFIGURATION, path.getParent());
+                transaction.replace(LogicalDatastoreType.CONFIGURATION, path, data, schemaContext);
+                transaction.replace(LogicalDatastoreType.CONFIGURATION, path.getParent(), readData, schemaContext);
+                return transaction.commit();
             case LAST:
-                return makePut(path, schemaContext, strategy, data);
+                return makePut(path, schemaContext, transaction, data);
             case BEFORE:
                 readData = readList(strategy, path.getParent());
                 if (readData == null || ((NormalizedNodeContainer<?, ?, ?>) readData).getValue().isEmpty()) {
-                    return makePut(path, schemaContext, strategy, data);
+                    return makePut(path, schemaContext, transaction, data);
                 }
-                insertWithPointPut(strategy, path, data, schemaContext, point,
+                insertWithPointPut(transaction, path, data, schemaContext, point,
                     (NormalizedNodeContainer<?, ?, NormalizedNode<?, ?>>) readData, true);
-                return strategy.commit();
+                return transaction.commit();
             case AFTER:
                 readData = readList(strategy, path.getParent());
                 if (readData == null || ((NormalizedNodeContainer<?, ?, ?>) readData).getValue().isEmpty()) {
-                    return makePut(path, schemaContext, strategy, data);
+                    return makePut(path, schemaContext, transaction, data);
                 }
-                insertWithPointPut(strategy, path, data, schemaContext, point,
+                insertWithPointPut(transaction, path, data, schemaContext, point,
                     (NormalizedNodeContainer<?, ?, NormalizedNode<?, ?>>) readData, false);
-                return strategy.commit();
+                return transaction.commit();
             default:
                 throw new RestconfDocumentedException(
                         "Used bad value of insert parameter. Possible values are first, last, before or after, "
@@ -222,13 +224,13 @@ public final class PutDataTransactionUtil {
             path,false);
     }
 
-    private static void insertWithPointPut(final RestconfStrategy strategy,
+    private static void insertWithPointPut(final RestconfTransaction transaction,
                                            final YangInstanceIdentifier path,
                                            final NormalizedNode<?, ?> data,
                                            final EffectiveModelContext schemaContext, final String point,
                                            final NormalizedNodeContainer<?, ?, NormalizedNode<?, ?>> readList,
                                            final boolean before) {
-        strategy.remove(LogicalDatastoreType.CONFIGURATION, path.getParent());
+        transaction.remove(LogicalDatastoreType.CONFIGURATION, path.getParent());
         final InstanceIdentifierContext<?> instanceIdentifier =
             ParserIdentifier.toInstanceIdentifier(point, schemaContext, Optional.empty());
         int lastItemPosition = 0;
@@ -243,24 +245,25 @@ public final class PutDataTransactionUtil {
         }
         int lastInsertedPosition = 0;
         final NormalizedNode<?, ?> emptySubtree = ImmutableNodes.fromInstanceId(schemaContext, path.getParent());
-        strategy.merge(LogicalDatastoreType.CONFIGURATION, YangInstanceIdentifier.create(emptySubtree.getIdentifier()),
+        transaction.merge(LogicalDatastoreType.CONFIGURATION,
+            YangInstanceIdentifier.create(emptySubtree.getIdentifier()),
             emptySubtree);
         for (final NormalizedNode<?, ?> nodeChild : readList.getValue()) {
             if (lastInsertedPosition == lastItemPosition) {
-                strategy.replace(LogicalDatastoreType.CONFIGURATION, path, data, schemaContext);
+                transaction.replace(LogicalDatastoreType.CONFIGURATION, path, data, schemaContext);
             }
             final YangInstanceIdentifier childPath = path.getParent().node(nodeChild.getIdentifier());
-            strategy.replace(LogicalDatastoreType.CONFIGURATION, childPath, nodeChild, schemaContext);
+            transaction.replace(LogicalDatastoreType.CONFIGURATION, childPath, nodeChild, schemaContext);
             lastInsertedPosition++;
         }
     }
 
     private static FluentFuture<? extends CommitInfo> makePut(final YangInstanceIdentifier path,
                                                               final SchemaContext schemaContext,
-                                                              final RestconfStrategy strategy,
+                                                              final RestconfTransaction transaction,
                                                               final NormalizedNode<?, ?> data) {
-        strategy.replace(LogicalDatastoreType.CONFIGURATION, path, data, schemaContext);
-        return strategy.commit();
+        transaction.replace(LogicalDatastoreType.CONFIGURATION, path, data, schemaContext);
+        return transaction.commit();
     }
 
     public static DataSchemaNode checkListAndOrderedType(final EffectiveModelContext ctx,
index bf65a4aee97afb2e96c03897ddc4b674abbc6937..d4c022fd309b4990ea45e1d786a192c737033f0f 100644 (file)
@@ -213,7 +213,7 @@ public final class ReadDataTransactionUtil {
             case RestconfDataServiceConstant.ReadData.ALL:
                 return readAllData(strategy, path, withDefa, ctx);
             default:
-                strategy.cancel();
+                strategy.close();
                 throw new RestconfDocumentedException(
                         new RestconfError(RestconfError.ErrorType.PROTOCOL, RestconfError.ErrorTag.INVALID_VALUE,
                                 "Invalid content parameter: " + valueOfContent, null,
@@ -398,7 +398,7 @@ public final class ReadDataTransactionUtil {
         final ListenableFuture<Optional<NormalizedNode<?, ?>>> listenableFuture = strategy.read(store, path);
         if (closeTransactionChain) {
             //Method close transactionChain if any
-            FutureCallbackTx.addCallback(listenableFuture, READ_TYPE_TX, dataFactory, strategy.getTransactionChain());
+            FutureCallbackTx.addCallback(listenableFuture, READ_TYPE_TX, dataFactory, strategy, path);
         } else {
             FutureCallbackTx.addCallback(listenableFuture, READ_TYPE_TX, dataFactory);
         }
index 7aa1d1937badbbf64dd50dc886a13665abe4ad49..804ad07d05e2e11a739b8792d9755e876a0ecb48 100644 (file)
@@ -11,7 +11,7 @@ import java.util.ArrayList;
 import java.util.Iterator;
 import java.util.List;
 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
-import org.opendaylight.restconf.nb.rfc8040.rests.transactions.RestconfStrategy;
+import org.opendaylight.restconf.nb.rfc8040.rests.transactions.RestconfTransaction;
 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;
@@ -31,12 +31,12 @@ public final class TransactionUtil {
      *
      * @param path          path of data
      * @param schemaContext {@link SchemaContext}
-     * @param strategy      object that perform the actual DS operations
+     * @param transaction   A handle to a set of DS operations
      */
     // FIXME: this method should only be invoked in MdsalRestconfStrategy, and even then only if we are crossing
     //        an implicit list.
     public static void ensureParentsByMerge(final YangInstanceIdentifier path, final SchemaContext schemaContext,
-                                            final RestconfStrategy strategy) {
+                                            final RestconfTransaction transaction) {
         final List<PathArgument> normalizedPathWithoutChildArgs = new ArrayList<>();
         YangInstanceIdentifier rootNormalizedPath = null;
 
@@ -59,6 +59,6 @@ public final class TransactionUtil {
 
         final NormalizedNode<?, ?> parentStructure = ImmutableNodes.fromInstanceId(schemaContext,
                 YangInstanceIdentifier.create(normalizedPathWithoutChildArgs));
-        strategy.merge(LogicalDatastoreType.CONFIGURATION, rootNormalizedPath, parentStructure);
+        transaction.merge(LogicalDatastoreType.CONFIGURATION, rootNormalizedPath, parentStructure);
     }
 }
index 521215356cad887aafd42368eef281e43fab018c..1271bb2396d57fc0ce76d27d1236afa30a598d59 100644 (file)
@@ -49,6 +49,7 @@ import org.opendaylight.mdsal.dom.api.DOMActionService;
 import org.opendaylight.mdsal.dom.api.DOMDataBroker;
 import org.opendaylight.mdsal.dom.api.DOMDataTreeReadTransaction;
 import org.opendaylight.mdsal.dom.api.DOMDataTreeReadWriteTransaction;
+import org.opendaylight.mdsal.dom.api.DOMDataTreeWriteTransaction;
 import org.opendaylight.mdsal.dom.api.DOMMountPoint;
 import org.opendaylight.mdsal.dom.api.DOMMountPointService;
 import org.opendaylight.mdsal.dom.api.DOMNotificationService;
@@ -133,6 +134,9 @@ public class JSONRestconfServiceRfc8040ImplTest {
     @Mock
     private DOMDataTreeReadTransaction mockReadOnlyTx;
 
+    @Mock
+    private DOMDataTreeWriteTransaction mockWriteTx;
+
     @Mock
     private DOMMountPointService mockMountPointService;
 
@@ -167,16 +171,15 @@ public class JSONRestconfServiceRfc8040ImplTest {
 
         doReturn(immediateFluentFuture(Optional.empty())).when(mockReadOnlyTx).read(
                 eq(LogicalDatastoreType.CONFIGURATION), any(YangInstanceIdentifier.class));
-
         doNothing().when(mockReadWriteTx).put(eq(LogicalDatastoreType.CONFIGURATION), any(YangInstanceIdentifier.class),
                 any(NormalizedNode.class));
         doReturn(CommitInfo.emptyFluentFuture()).when(mockReadWriteTx).commit();
-        doReturn(immediateFalseFluentFuture()).when(mockReadWriteTx).exists(
+        doReturn(immediateFalseFluentFuture()).when(mockReadOnlyTx).exists(
                 eq(LogicalDatastoreType.CONFIGURATION), any(YangInstanceIdentifier.class));
-
+        doReturn(immediateFalseFluentFuture()).when(mockReadWriteTx).exists(
+            eq(LogicalDatastoreType.CONFIGURATION), any(YangInstanceIdentifier.class));
         doReturn(mockReadOnlyTx).when(mockTxChain).newReadOnlyTransaction();
         doReturn(mockReadWriteTx).when(mockTxChain).newReadWriteTransaction();
-
         doReturn(mockTxChain).when(mockDOMDataBroker).createTransactionChain(any());
 
         final TransactionChainHandler txChainHandler = new TransactionChainHandler(mockDOMDataBroker);
@@ -251,6 +254,7 @@ public class JSONRestconfServiceRfc8040ImplTest {
         verifyLeafNode(actualNode, TEST_LF12_QNAME, "lf12 data");
     }
 
+    @Test
     public void testPutFailure() throws IOException {
         doReturn(immediateFailedFluentFuture(new TransactionCommitFailedException("mock")))
                 .when(mockReadWriteTx).commit();
@@ -556,7 +560,6 @@ public class JSONRestconfServiceRfc8040ImplTest {
 
         doReturn(Optional.of(mockDOMDataBroker)).when(mockMountPoint).getService(DOMDataBroker.class);
         doReturn(Optional.empty()).when(mockMountPoint).getService(NetconfDataTreeService.class);
-
         doReturn(Optional.of(mockMountPoint)).when(mockMountPointService).getMountPoint(notNull());
 
         return mockMountPoint;
index ac87d3abc04f149889b75c644c04227bafcdcaab..d68b9905330c4ec4d63837ba1b2c556dbe53d0f0 100644 (file)
@@ -367,7 +367,7 @@ public class RestconfDataServiceImplTest {
                 new InstanceIdentifierContext<>(this.iidBase, this.schemaNode, null, this.contextRef);
         final NormalizedNodeContext payload = new NormalizedNodeContext(iidContext, this.buildBaseCont);
 
-        doReturn(immediateTrueFluentFuture()).when(this.readWrite)
+        doReturn(immediateTrueFluentFuture()).when(this.read)
                 .exists(LogicalDatastoreType.CONFIGURATION, this.iidBase);
         doNothing().when(this.readWrite).put(LogicalDatastoreType.CONFIGURATION, this.iidBase, payload.getData());
         final Response response = this.dataService.putData(null, payload, this.uriInfo);
@@ -385,7 +385,7 @@ public class RestconfDataServiceImplTest {
                 new InstanceIdentifierContext<>(this.iidBase, this.schemaNode, mountPoint, this.contextRef);
         final NormalizedNodeContext payload = new NormalizedNodeContext(iidContext, this.buildBaseCont);
 
-        doReturn(immediateTrueFluentFuture()).when(this.readWrite)
+        doReturn(immediateTrueFluentFuture()).when(this.read)
                 .exists(LogicalDatastoreType.CONFIGURATION, this.iidBase);
         doNothing().when(this.readWrite).put(LogicalDatastoreType.CONFIGURATION, this.iidBase, payload.getData());
         final Response response = this.dataService.putData(null, payload, this.uriInfo);
@@ -427,7 +427,7 @@ public class RestconfDataServiceImplTest {
         final YangInstanceIdentifier node =
                 payload.getInstanceIdentifierContext().getInstanceIdentifier().node(identifier);
         doReturn(immediateFalseFluentFuture())
-                .when(this.readWrite).exists(LogicalDatastoreType.CONFIGURATION, node);
+                .when(this.read).exists(LogicalDatastoreType.CONFIGURATION, node);
         doNothing().when(this.readWrite).put(LogicalDatastoreType.CONFIGURATION, node, entryNode);
         doReturn(UriBuilder.fromUri("http://localhost:8181/restconf/15/")).when(this.uriInfo).getBaseUriBuilder();
 
@@ -460,7 +460,7 @@ public class RestconfDataServiceImplTest {
     }
 
     @Test
-    public void testPatchData() throws Exception {
+    public void testPatchData() {
         final InstanceIdentifierContext<? extends SchemaNode> iidContext =
                 new InstanceIdentifierContext<>(this.iidBase, this.schemaNode, null, this.contextRef);
         final List<PatchEntity> entity = new ArrayList<>();
@@ -510,7 +510,7 @@ public class RestconfDataServiceImplTest {
     }
 
     @Test
-    public void testPatchDataDeleteNotExist() throws Exception {
+    public void testPatchDataDeleteNotExist() {
         final InstanceIdentifierContext<? extends SchemaNode> iidContext =
                 new InstanceIdentifierContext<>(this.iidBase, this.schemaNode, null, this.contextRef);
         final List<PatchEntity> entity = new ArrayList<>();
index 156100ad25b56cf5cee85fd95ccec2d62415092d..9dfd50a95c27d6e95a68720523b3809fbef124cf 100644 (file)
@@ -73,8 +73,8 @@ public class DeleteDataTransactionUtilTest {
     @Test
     public void deleteData() {
         // assert that data to delete exists
-        Mockito.when(this.transactionChain.newReadWriteTransaction().exists(LogicalDatastoreType.CONFIGURATION,
-                YangInstanceIdentifier.empty())).thenReturn(immediateTrueFluentFuture());
+        Mockito.when(readWrite.exists(LogicalDatastoreType.CONFIGURATION, YangInstanceIdentifier.empty()))
+            .thenReturn(immediateTrueFluentFuture());
         // test
         delete(new MdsalRestconfStrategy(transactionChainHandler));
         delete(new NetconfRestconfStrategy(netconfService));
@@ -86,8 +86,8 @@ public class DeleteDataTransactionUtilTest {
     @Test
     public void deleteDataNegativeTest() {
         // assert that data to delete does NOT exist
-        Mockito.when(this.transactionChain.newReadWriteTransaction().exists(LogicalDatastoreType.CONFIGURATION,
-                YangInstanceIdentifier.empty())).thenReturn(immediateFalseFluentFuture());
+        Mockito.when(readWrite.exists(LogicalDatastoreType.CONFIGURATION, YangInstanceIdentifier.empty()))
+            .thenReturn(immediateFalseFluentFuture());
         final NetconfDocumentedException exception = new NetconfDocumentedException("id",
             DocumentedException.ErrorType.RPC, DocumentedException.ErrorTag.from("data-missing"),
             DocumentedException.ErrorSeverity.ERROR);
index ee4d92a4756eae26063b0a189f12fdee5e147c35..227860ba5fee20e0f7bb5d1039bda725b6343601 100644 (file)
@@ -18,7 +18,6 @@ import static org.mockito.Mockito.verify;
 import static org.opendaylight.yangtools.util.concurrent.FluentFutures.immediateFailedFluentFuture;
 import static org.opendaylight.yangtools.util.concurrent.FluentFutures.immediateFalseFluentFuture;
 
-import java.io.UnsupportedEncodingException;
 import java.net.URLDecoder;
 import java.nio.charset.StandardCharsets;
 import java.util.Collection;
@@ -174,7 +173,7 @@ public class PostDataTransactionUtilTest {
     }
 
     @Test
-    public void testPostListData() throws UnsupportedEncodingException {
+    public void testPostListData() {
         final InstanceIdentifierContext<? extends SchemaNode> iidContext =
                 new InstanceIdentifierContext<>(this.iidList, null, null, this.schema);
         final NormalizedNodeContext payload = new NormalizedNodeContext(iidContext, this.buildList);
@@ -184,7 +183,8 @@ public class PostDataTransactionUtilTest {
         final NodeIdentifierWithPredicates identifier = entryNode.getIdentifier();
         final YangInstanceIdentifier node =
                 payload.getInstanceIdentifierContext().getInstanceIdentifier().node(identifier);
-        doReturn(immediateFalseFluentFuture()).when(this.readWrite).exists(LogicalDatastoreType.CONFIGURATION, node);
+        doReturn(read).when(this.transactionChain).newReadOnlyTransaction();
+        doReturn(immediateFalseFluentFuture()).when(this.read).exists(LogicalDatastoreType.CONFIGURATION, node);
         doNothing().when(this.readWrite).put(LogicalDatastoreType.CONFIGURATION, node, entryNode);
         doReturn(CommitInfo.emptyFluentFuture()).when(this.readWrite).commit();
         doReturn(CommitInfo.emptyFluentFuture()).when(this.netconfService).commit(Mockito.any());
@@ -194,7 +194,7 @@ public class PostDataTransactionUtilTest {
         assertEquals(201, response.getStatus());
         assertThat(URLDecoder.decode(response.getLocation().toString(), StandardCharsets.UTF_8),
             containsString(identifier.getValue(identifier.keySet().iterator().next()).toString()));
-        verify(this.readWrite).exists(LogicalDatastoreType.CONFIGURATION, node);
+        verify(this.read).exists(LogicalDatastoreType.CONFIGURATION, node);
         verify(this.readWrite).put(LogicalDatastoreType.CONFIGURATION, node, entryNode);
 
         response = PostDataTransactionUtil.postData(this.uriInfo, payload,
index 6c95fff1ab93c05cc5b4ab8cbecb22915ee48359..a07329d4f027a69ec07a2a4b459f17cc4f55dd43 100644 (file)
@@ -219,15 +219,16 @@ public class PutDataTransactionUtilTest {
         final NormalizedNodeContext payload = new NormalizedNodeContext(iidContext, this.buildBaseCont);
 
         doReturn(this.readWrite).when(this.transactionChain).newReadWriteTransaction();
+        doReturn(this.read).when(this.transactionChain).newReadOnlyTransaction();
         doReturn(immediateFalseFluentFuture())
-                .when(this.readWrite).exists(LogicalDatastoreType.CONFIGURATION, this.iid2);
+                .when(this.read).exists(LogicalDatastoreType.CONFIGURATION, this.iid2);
         doNothing().when(this.readWrite).put(LogicalDatastoreType.CONFIGURATION,
                 payload.getInstanceIdentifierContext().getInstanceIdentifier(), payload.getData());
         doReturn(CommitInfo.emptyFluentFuture()).when(this.readWrite).commit();
 
         PutDataTransactionUtil.putData(payload, this.schema, new MdsalRestconfStrategy(transactionChainHandler),
                 null, null);
-        verify(this.readWrite).exists(LogicalDatastoreType.CONFIGURATION,
+        verify(this.read).exists(LogicalDatastoreType.CONFIGURATION,
                 payload.getInstanceIdentifierContext().getInstanceIdentifier());
         verify(this.readWrite).put(LogicalDatastoreType.CONFIGURATION,
                 payload.getInstanceIdentifierContext().getInstanceIdentifier(), payload.getData());
@@ -273,15 +274,16 @@ public class PutDataTransactionUtilTest {
         final NormalizedNodeContext payload = new NormalizedNodeContext(iidContext, this.buildLeaf);
 
         doReturn(this.readWrite).when(this.transactionChain).newReadWriteTransaction();
+        doReturn(this.read).when(this.transactionChain).newReadOnlyTransaction();
         doReturn(immediateFalseFluentFuture())
-                .when(this.readWrite).exists(LogicalDatastoreType.CONFIGURATION, this.iid);
+                .when(this.read).exists(LogicalDatastoreType.CONFIGURATION, this.iid);
         doNothing().when(this.readWrite).put(LogicalDatastoreType.CONFIGURATION,
                 payload.getInstanceIdentifierContext().getInstanceIdentifier(), payload.getData());
         doReturn(CommitInfo.emptyFluentFuture()).when(this.readWrite).commit();
 
         PutDataTransactionUtil.putData(payload, this.schema, new MdsalRestconfStrategy(transactionChainHandler),
                 null, null);
-        verify(this.readWrite).exists(LogicalDatastoreType.CONFIGURATION,
+        verify(this.read).exists(LogicalDatastoreType.CONFIGURATION,
                 payload.getInstanceIdentifierContext().getInstanceIdentifier());
         verify(this.readWrite).put(LogicalDatastoreType.CONFIGURATION,
                 payload.getInstanceIdentifierContext().getInstanceIdentifier(), payload.getData());
@@ -327,14 +329,15 @@ public class PutDataTransactionUtilTest {
         final NormalizedNodeContext payload = new NormalizedNodeContext(iidContext, this.buildBaseContWithList);
 
         doReturn(this.readWrite).when(this.transactionChain).newReadWriteTransaction();
+        doReturn(this.read).when(this.transactionChain).newReadOnlyTransaction();
         doReturn(immediateFalseFluentFuture())
-                .when(this.readWrite).exists(LogicalDatastoreType.CONFIGURATION, this.iid2);
+                .when(this.read).exists(LogicalDatastoreType.CONFIGURATION, this.iid2);
         doNothing().when(this.readWrite).put(LogicalDatastoreType.CONFIGURATION,
                 payload.getInstanceIdentifierContext().getInstanceIdentifier(), payload.getData());
         doReturn(CommitInfo.emptyFluentFuture()).when(this.readWrite).commit();
         PutDataTransactionUtil.putData(payload, this.schema, new MdsalRestconfStrategy(transactionChainHandler),
                 null, null);
-        verify(this.readWrite).exists(LogicalDatastoreType.CONFIGURATION, this.iid2);
+        verify(this.read).exists(LogicalDatastoreType.CONFIGURATION, this.iid2);
         verify(this.readWrite).put(LogicalDatastoreType.CONFIGURATION, this.iid2, payload.getData());
     }