From 356549664dfaad57c9213a947a964a9f87ebe676 Mon Sep 17 00:00:00 2001 From: Robert Varga Date: Wed, 14 Oct 2020 23:42:04 +0200 Subject: [PATCH] Split transaction lifecycle Change-Id: I0ec27e3720e23639b379f8ae594cb84dfb814516 Signed-off-by: Robert Varga Signed-off-by: Vladyslav Marchenko --- .../impl/RestconfDataServiceImpl.java | 17 +- .../transactions/BatchedExistenceCheck.java | 47 +++--- .../transactions/MdsalRestconfStrategy.java | 147 ++---------------- .../MdsalRestconfTransaction.java | 145 +++++++++++++++++ .../transactions/NetconfRestconfStrategy.java | 92 +---------- .../NetconfRestconfTransaction.java | 100 ++++++++++++ .../rests/transactions/RestconfStrategy.java | 88 +---------- .../transactions/RestconfTransaction.java | 89 +++++++++++ .../utils/DeleteDataTransactionUtil.java | 25 ++- .../rfc8040/rests/utils/FutureCallbackTx.java | 34 +--- .../rests/utils/PatchDataTransactionUtil.java | 72 ++++----- .../utils/PlainPatchDataTransactionUtil.java | 16 +- .../rests/utils/PostDataTransactionUtil.java | 76 +++++---- .../rests/utils/PutDataTransactionUtil.java | 61 ++++---- .../rests/utils/ReadDataTransactionUtil.java | 4 +- .../rfc8040/rests/utils/TransactionUtil.java | 8 +- .../JSONRestconfServiceRfc8040ImplTest.java | 13 +- .../impl/RestconfDataServiceImplTest.java | 10 +- .../utils/DeleteDataTransactionUtilTest.java | 8 +- .../utils/PostDataTransactionUtilTest.java | 8 +- .../utils/PutDataTransactionUtilTest.java | 15 +- 21 files changed, 544 insertions(+), 531 deletions(-) create mode 100644 restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/transactions/MdsalRestconfTransaction.java create mode 100644 restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/transactions/NetconfRestconfTransaction.java create mode 100644 restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/transactions/RestconfTransaction.java diff --git a/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/services/impl/RestconfDataServiceImpl.java b/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/services/impl/RestconfDataServiceImpl.java index f5219d9dda..79ccb8acd5 100644 --- a/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/services/impl/RestconfDataServiceImpl.java +++ b/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/services/impl/RestconfDataServiceImpl.java @@ -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); } diff --git a/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/transactions/BatchedExistenceCheck.java b/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/transactions/BatchedExistenceCheck.java index 8cfbab1182..d6d0acdd46 100644 --- a/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/transactions/BatchedExistenceCheck.java +++ b/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/transactions/BatchedExistenceCheck.java @@ -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> 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() { - @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() { + @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 getFailure() throws InterruptedException { diff --git a/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/transactions/MdsalRestconfStrategy.java b/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/transactions/MdsalRestconfStrategy.java index 6b2e7c2e28..d0fb5c3352 100644 --- a/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/transactions/MdsalRestconfStrategy.java +++ b/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/transactions/MdsalRestconfStrategy.java @@ -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>> 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 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> 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 commit() { - final FluentFuture 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 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 index 0000000000..a6bfdb3225 --- /dev/null +++ b/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/transactions/MdsalRestconfTransaction.java @@ -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 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> 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 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 commit() { + final FluentFuture ret = verifyNotNull(rwTx).commit(); + rwTx = null; + return ret; + } + + private static void checkExistence(final YangInstanceIdentifier path, final BatchedExistenceCheck check) { + final Map.Entry 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()); + } + } +} diff --git a/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/transactions/NetconfRestconfStrategy.java b/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/transactions/NetconfRestconfStrategy.java index 19a60f376d..0f9acb580e 100644 --- a/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/transactions/NetconfRestconfStrategy.java +++ b/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/transactions/NetconfRestconfStrategy.java @@ -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> 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 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 FluentFuture remapException(final ListenableFuture 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 index 0000000000..840a10d0b0 --- /dev/null +++ b/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/transactions/NetconfRestconfTransaction.java @@ -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> 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 commit() { + return FluentFuture.from(netconfService.commit(resultsFutures)); + } +} diff --git a/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/transactions/RestconfStrategy.java b/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/transactions/RestconfStrategy.java index bfecd8893a..dc1454a09d 100644 --- a/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/transactions/RestconfStrategy.java +++ b/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/transactions/RestconfStrategy.java @@ -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 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 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 index 0000000000..ef8e3dd31e --- /dev/null +++ b/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/transactions/RestconfTransaction.java @@ -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 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); +} diff --git a/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/utils/DeleteDataTransactionUtil.java b/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/utils/DeleteDataTransactionUtil.java index ba6d10b1cd..9552679356 100644 --- a/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/utils/DeleteDataTransactionUtil.java +++ b/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/utils/DeleteDataTransactionUtil.java @@ -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 future = strategy.commit(); + final FluentFuture 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 isExistsFuture, + final YangInstanceIdentifier path, final String operationType) { - final FluentFuture future = strategy.exists(store, path); final FutureDataFactory 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); diff --git a/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/utils/FutureCallbackTx.java b/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/utils/FutureCallbackTx.java index b68cd5177d..e4c88f22a6 100644 --- a/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/utils/FutureCallbackTx.java +++ b/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/utils/FutureCallbackTx.java @@ -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 void addCallback(final ListenableFuture listenableFuture, final String txType, final FutureDataFactory 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 void addCallback(final ListenableFuture listenableFuture, final String txType, - final FutureDataFactory 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(); } } } diff --git a/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/utils/PatchDataTransactionUtil.java b/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/utils/PatchDataTransactionUtil.java index 4d165dc1e7..43790a80d3 100644 --- a/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/utils/PatchDataTransactionUtil.java +++ b/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/utils/PatchDataTransactionUtil.java @@ -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 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 future = strategy.commit(); + final FluentFuture 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); } } diff --git a/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/utils/PlainPatchDataTransactionUtil.java b/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/utils/PlainPatchDataTransactionUtil.java index 88f463077c..cad30a9127 100644 --- a/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/utils/PlainPatchDataTransactionUtil.java +++ b/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/utils/PlainPatchDataTransactionUtil.java @@ -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 future = strategy.commit(); + final FluentFuture 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(); } } diff --git a/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/utils/PostDataTransactionUtil.java b/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/utils/PostDataTransactionUtil.java index d5b39fd8a9..9051937988 100644 --- a/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/utils/PostDataTransactionUtil.java +++ b/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/utils/PostDataTransactionUtil.java @@ -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>) readData, true, strategy); - return strategy.commit(); + (NormalizedNodeContainer>) 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>) readData, false, strategy); - return strategy.commit(); + (NormalizedNodeContainer>) 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> 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 future = strategy.exists(store, path); + public static void checkItemDoesNotExists(final FluentFuture isExistsFuture, + final YangInstanceIdentifier path) { final FutureDataFactory 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); diff --git a/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/utils/PutDataTransactionUtil.java b/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/utils/PutDataTransactionUtil.java index 0d42f064ba..ae29f56a8b 100644 --- a/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/utils/PutDataTransactionUtil.java +++ b/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/utils/PutDataTransactionUtil.java @@ -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 existsFuture = strategy.exists(LogicalDatastoreType.CONFIGURATION, path); + final FutureDataFactory existsResponse = new FutureDataFactory<>(); + FutureCallbackTx.addCallback(existsFuture, PUT_TX_TYPE, existsResponse); + + final ResponseFactory responseFactory = + new ResponseFactory(existsResponse.result ? Status.NO_CONTENT : Status.CREATED); final FluentFuture 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 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>) 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>) 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> 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 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, diff --git a/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/utils/ReadDataTransactionUtil.java b/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/utils/ReadDataTransactionUtil.java index bf65a4aee9..d4c022fd30 100644 --- a/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/utils/ReadDataTransactionUtil.java +++ b/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/utils/ReadDataTransactionUtil.java @@ -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>> 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); } diff --git a/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/utils/TransactionUtil.java b/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/utils/TransactionUtil.java index 7aa1d1937b..804ad07d05 100644 --- a/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/utils/TransactionUtil.java +++ b/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/utils/TransactionUtil.java @@ -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 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); } } diff --git a/restconf/restconf-nb-rfc8040/src/test/java/org/opendaylight/restconf/nb/rfc8040/rests/services/impl/JSONRestconfServiceRfc8040ImplTest.java b/restconf/restconf-nb-rfc8040/src/test/java/org/opendaylight/restconf/nb/rfc8040/rests/services/impl/JSONRestconfServiceRfc8040ImplTest.java index 521215356c..1271bb2396 100644 --- a/restconf/restconf-nb-rfc8040/src/test/java/org/opendaylight/restconf/nb/rfc8040/rests/services/impl/JSONRestconfServiceRfc8040ImplTest.java +++ b/restconf/restconf-nb-rfc8040/src/test/java/org/opendaylight/restconf/nb/rfc8040/rests/services/impl/JSONRestconfServiceRfc8040ImplTest.java @@ -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; diff --git a/restconf/restconf-nb-rfc8040/src/test/java/org/opendaylight/restconf/nb/rfc8040/rests/services/impl/RestconfDataServiceImplTest.java b/restconf/restconf-nb-rfc8040/src/test/java/org/opendaylight/restconf/nb/rfc8040/rests/services/impl/RestconfDataServiceImplTest.java index ac87d3abc0..d68b990533 100644 --- a/restconf/restconf-nb-rfc8040/src/test/java/org/opendaylight/restconf/nb/rfc8040/rests/services/impl/RestconfDataServiceImplTest.java +++ b/restconf/restconf-nb-rfc8040/src/test/java/org/opendaylight/restconf/nb/rfc8040/rests/services/impl/RestconfDataServiceImplTest.java @@ -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 iidContext = new InstanceIdentifierContext<>(this.iidBase, this.schemaNode, null, this.contextRef); final List entity = new ArrayList<>(); @@ -510,7 +510,7 @@ public class RestconfDataServiceImplTest { } @Test - public void testPatchDataDeleteNotExist() throws Exception { + public void testPatchDataDeleteNotExist() { final InstanceIdentifierContext iidContext = new InstanceIdentifierContext<>(this.iidBase, this.schemaNode, null, this.contextRef); final List entity = new ArrayList<>(); diff --git a/restconf/restconf-nb-rfc8040/src/test/java/org/opendaylight/restconf/nb/rfc8040/rests/utils/DeleteDataTransactionUtilTest.java b/restconf/restconf-nb-rfc8040/src/test/java/org/opendaylight/restconf/nb/rfc8040/rests/utils/DeleteDataTransactionUtilTest.java index 156100ad25..9dfd50a95c 100644 --- a/restconf/restconf-nb-rfc8040/src/test/java/org/opendaylight/restconf/nb/rfc8040/rests/utils/DeleteDataTransactionUtilTest.java +++ b/restconf/restconf-nb-rfc8040/src/test/java/org/opendaylight/restconf/nb/rfc8040/rests/utils/DeleteDataTransactionUtilTest.java @@ -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); diff --git a/restconf/restconf-nb-rfc8040/src/test/java/org/opendaylight/restconf/nb/rfc8040/rests/utils/PostDataTransactionUtilTest.java b/restconf/restconf-nb-rfc8040/src/test/java/org/opendaylight/restconf/nb/rfc8040/rests/utils/PostDataTransactionUtilTest.java index ee4d92a475..227860ba5f 100644 --- a/restconf/restconf-nb-rfc8040/src/test/java/org/opendaylight/restconf/nb/rfc8040/rests/utils/PostDataTransactionUtilTest.java +++ b/restconf/restconf-nb-rfc8040/src/test/java/org/opendaylight/restconf/nb/rfc8040/rests/utils/PostDataTransactionUtilTest.java @@ -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 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, diff --git a/restconf/restconf-nb-rfc8040/src/test/java/org/opendaylight/restconf/nb/rfc8040/rests/utils/PutDataTransactionUtilTest.java b/restconf/restconf-nb-rfc8040/src/test/java/org/opendaylight/restconf/nb/rfc8040/rests/utils/PutDataTransactionUtilTest.java index 6c95fff1ab..a07329d4f0 100644 --- a/restconf/restconf-nb-rfc8040/src/test/java/org/opendaylight/restconf/nb/rfc8040/rests/utils/PutDataTransactionUtilTest.java +++ b/restconf/restconf-nb-rfc8040/src/test/java/org/opendaylight/restconf/nb/rfc8040/rests/utils/PutDataTransactionUtilTest.java @@ -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()); } -- 2.36.6