Create NetconfDataTreeService with base and additional operations for netconf 71/90371/30
authorVladyslav Marchenko <vladyslav.marchenko@pantheon.tech>
Wed, 10 Jun 2020 09:19:19 +0000 (12:19 +0300)
committerTomas Cere <tomas.cere@pantheon.tech>
Tue, 14 Jul 2020 11:58:17 +0000 (11:58 +0000)
Service provided by netconf mountpoints. It provides an api for all netconf operations, instead of mapping mdsal api to netconf requests. Restconf now uses the new NetconfDataTreeService when available. If no - fall back to DOMDataBroker.

JIRA: NETCONF-312
Signed-off-by: Vladyslav Marchenko <vladyslav.marchenko@pantheon.tech>
Change-Id: I354449883b54c8a3a4e7ffdc12d7d532c8120b6a

38 files changed:
artifacts/pom.xml
netconf/netconf-dom-api/pom.xml [new file with mode: 0644]
netconf/netconf-dom-api/src/main/java/org/opendaylight/netconf/dom/api/NetconfDataTreeService.java [new file with mode: 0644]
netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/impl/MasterSalFacade.java
netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/impl/SlaveSalFacade.java
netconf/pom.xml
netconf/sal-netconf-connector/pom.xml
netconf/sal-netconf-connector/src/main/java/org/opendaylight/netconf/sal/connect/netconf/sal/LockChangeListener.java
netconf/sal-netconf-connector/src/main/java/org/opendaylight/netconf/sal/connect/netconf/sal/NetconfDataTreeServiceImpl.java [new file with mode: 0644]
netconf/sal-netconf-connector/src/main/java/org/opendaylight/netconf/sal/connect/netconf/sal/NetconfDeviceSalFacade.java
netconf/sal-netconf-connector/src/main/java/org/opendaylight/netconf/sal/connect/netconf/sal/NetconfDeviceSalProvider.java
netconf/sal-netconf-connector/src/test/java/org/opendaylight/netconf/sal/connect/netconf/sal/MountInstanceTest.java
netconf/sal-netconf-connector/src/test/java/org/opendaylight/netconf/sal/connect/netconf/sal/NetconfDataTreeServiceImplTest.java [new file with mode: 0644]
netconf/sal-netconf-connector/src/test/java/org/opendaylight/netconf/sal/connect/netconf/sal/NetconfDeviceSalFacadeTest.java
netconf/sal-netconf-connector/src/test/java/org/opendaylight/netconf/sal/connect/netconf/sal/tx/TxTestUtils.java
restconf/restconf-nb-rfc8040/pom.xml
restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/jersey/providers/NormalizedNodeJsonBodyWriter.java
restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/jersey/providers/NormalizedNodeXmlBodyWriter.java
restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/services/impl/RestconfDataServiceImpl.java
restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/services/impl/SubscribeToStreamUtil.java
restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/transactions/MdsalRestconfStrategy.java [new file with mode: 0644]
restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/transactions/NetconfRestconfStrategy.java [new file with mode: 0644]
restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/transactions/RestconfStrategy.java [new file with mode: 0644]
restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/transactions/TransactionVarsWrapper.java [deleted file]
restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/utils/DeleteDataTransactionUtil.java
restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/utils/PatchDataTransactionUtil.java
restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/utils/PlainPatchDataTransactionUtil.java
restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/utils/PostDataTransactionUtil.java
restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/utils/PutDataTransactionUtil.java
restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/utils/ReadDataTransactionUtil.java
restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/utils/TransactionUtil.java
restconf/restconf-nb-rfc8040/src/test/java/org/opendaylight/restconf/nb/rfc8040/rests/services/impl/RestconfDataServiceImplTest.java
restconf/restconf-nb-rfc8040/src/test/java/org/opendaylight/restconf/nb/rfc8040/rests/utils/DeleteDataTransactionUtilTest.java
restconf/restconf-nb-rfc8040/src/test/java/org/opendaylight/restconf/nb/rfc8040/rests/utils/PatchDataTransactionUtilTest.java
restconf/restconf-nb-rfc8040/src/test/java/org/opendaylight/restconf/nb/rfc8040/rests/utils/PlainPatchDataTransactionUtilTest.java
restconf/restconf-nb-rfc8040/src/test/java/org/opendaylight/restconf/nb/rfc8040/rests/utils/PostDataTransactionUtilTest.java
restconf/restconf-nb-rfc8040/src/test/java/org/opendaylight/restconf/nb/rfc8040/rests/utils/PutDataTransactionUtilTest.java
restconf/restconf-nb-rfc8040/src/test/java/org/opendaylight/restconf/nb/rfc8040/rests/utils/ReadDataTransactionUtilTest.java

index ce8a55f9a97945916dde66a42cda074078fd777a..b4e1cff36f5f3e7962f9968fa09b4cde7d5892d0 100644 (file)
                 <artifactId>netconf-api</artifactId>
                 <version>${project.version}</version>
             </dependency>
+            <dependency>
+                <groupId>${project.groupId}</groupId>
+                <artifactId>netconf-dom-api</artifactId>
+                <version>${project.version}</version>
+            </dependency>
             <dependency>
                 <groupId>${project.groupId}</groupId>
                 <artifactId>netconf-auth</artifactId>
diff --git a/netconf/netconf-dom-api/pom.xml b/netconf/netconf-dom-api/pom.xml
new file mode 100644 (file)
index 0000000..d0b7d3e
--- /dev/null
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.opendaylight.netconf</groupId>
+        <artifactId>netconf-parent</artifactId>
+        <version>1.9.0-SNAPSHOT</version>
+        <relativePath>../../parent</relativePath>
+    </parent>
+
+    <groupId>org.opendaylight.netconf</groupId>
+    <artifactId>netconf-dom-api</artifactId>
+    <version>1.9.0-SNAPSHOT</version>
+    <name>${project.artifactId}</name>
+    <packaging>bundle</packaging>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.opendaylight.mdsal</groupId>
+            <artifactId>mdsal-dom-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.netconf</groupId>
+            <artifactId>netconf-api</artifactId>
+        </dependency>
+    </dependencies>
+</project>
diff --git a/netconf/netconf-dom-api/src/main/java/org/opendaylight/netconf/dom/api/NetconfDataTreeService.java b/netconf/netconf-dom-api/src/main/java/org/opendaylight/netconf/dom/api/NetconfDataTreeService.java
new file mode 100644 (file)
index 0000000..67f0633
--- /dev/null
@@ -0,0 +1,132 @@
+/*
+ * 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.netconf.dom.api;
+
+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.mdsal.dom.api.DOMService;
+import org.opendaylight.netconf.api.ModifyAction;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+/**
+ * Interface for base and additional operations for netconf (e.g. get, get-config, edit-config, (un)lock, commit etc).
+ * &lt;edit-config&gt; operation is extended according it's attributes (merge, replace, create, delete, remove).
+ * According to RFC-6241.
+ */
+public interface NetconfDataTreeService extends DOMService {
+
+    /**
+     * The &lt;lock&gt; operation.
+     * Allows the client to lock the entire configuration datastore system of a device.
+     *
+     * @return result of &lt;lock&gt; operation
+     */
+    List<ListenableFuture<? extends DOMRpcResult>> lock();
+
+    /**
+     * The &lt;unlock&gt; operation.
+     * Used to release a configuration lock, previously obtained with the &lt;lock&gt; operation.
+     */
+    void unlock();
+
+    /**
+     * The &lt;discard-changes&gt; operation.
+     * If device supports :candidate capability, discards any uncommitted changes by resetting
+     * the candidate configuration with the content of the running configuration.
+     */
+    void discardChanges();
+
+    /**
+     * The &lt;get&gt; operation.
+     * Retrieve running configuration and device state information.
+     *
+     * @return result of &lt;get&gt; operation
+     */
+    ListenableFuture<Optional<NormalizedNode<?, ?>>> get(YangInstanceIdentifier path);
+
+    /**
+     * The &lt;get-config&gt; operation.
+     * Retrieve all or part of a specified configuration datastore.
+     *
+     * @return result of &lt;get-config&gt; operation
+     */
+    ListenableFuture<Optional<NormalizedNode<?, ?>>> getConfig(YangInstanceIdentifier path);
+
+    /**
+     * The &lt;edit-config&gt; operation with "merge" attribute.
+     * The configuration data identified by the element containing this attribute is merged with the configuration
+     * at the corresponding level in the configuration datastore.
+     *
+     * @return result of &lt;edit-config&gt; operation
+     */
+    ListenableFuture<? extends DOMRpcResult> merge(LogicalDatastoreType store, YangInstanceIdentifier path,
+                                                   NormalizedNode<?, ?> data,
+                                                   Optional<ModifyAction> defaultOperation);
+
+    /**
+     * The &lt;edit-config&gt; operation with "replace" attribute.
+     * The configuration data identified by the element containing this attribute replaces any related configuration
+     * in the configuration datastore.
+     *
+     * @return result of &lt;edit-config&gt; operation
+     */
+    ListenableFuture<? extends DOMRpcResult> replace(LogicalDatastoreType store, YangInstanceIdentifier path,
+                                                     NormalizedNode<?, ?> data,
+                                                     Optional<ModifyAction> defaultOperation);
+
+    /**
+     * The &lt;edit-config&gt; operation with "create" attribute.
+     * The configuration data identified by the element containing this attribute is added to the configuration if
+     * and only if the configuration data does not already exist in the configuration datastore.
+     *
+     * @return result of &lt;edit-config&gt; operation
+     */
+    ListenableFuture<? extends DOMRpcResult> create(LogicalDatastoreType store, YangInstanceIdentifier path,
+                                                    NormalizedNode<?, ?> data,
+                                                    Optional<ModifyAction> defaultOperation);
+
+    /**
+     * The &lt;edit-config&gt; operation with "create" attribute.
+     * The configuration data identified by the element containing this attribute is deleted from the configuration
+     * if and only if the configuration data currently exists in the configuration datastore.
+     *
+     * @return result of &lt;edit-config&gt; operation
+     */
+    ListenableFuture<? extends DOMRpcResult> delete(LogicalDatastoreType store, YangInstanceIdentifier path);
+
+    /**
+     * The &lt;edit-config&gt; operation with "create" attribute.
+     * The configuration data identified by the element containing this attribute is deleted from the configuration
+     * if the configuration data currently exists in the configuration datastore.
+     *
+     * @return result of &lt;edit-config&gt; operation
+     */
+    ListenableFuture<? extends DOMRpcResult> remove(LogicalDatastoreType store, YangInstanceIdentifier path);
+
+    /**
+     * The &lt;commit&gt; operation.
+     * If device supports :candidate capability, commit the candidate configuration as the device's
+     * new current configuration.
+     *
+     * @return result of &lt;commit&gt; operation
+     */
+    ListenableFuture<? extends CommitInfo> commit(List<ListenableFuture<? extends DOMRpcResult>> resultsFutures);
+
+    /**
+     * Return device identifier.
+     *
+     * @return Device's identifier, must not be null.
+     */
+    @NonNull Object getDeviceId();
+}
\ No newline at end of file
index 03347e9c0654b67f2eaef76c89b3c49227532949..d80a9871c63e16bb431495e92593c9cfba07c67c 100644 (file)
@@ -142,7 +142,7 @@ class MasterSalFacade implements AutoCloseable, RemoteDeviceHandler<NetconfSessi
         final ProxyDOMDataBroker proxyDataBroker = new ProxyDOMDataBroker(id, masterActorRef, actorSystem.dispatcher(),
             actorResponseWaitTime);
         salProvider.getMountInstance().onTopologyDeviceConnected(currentMountContext.getEffectiveModelContext(),
-            proxyDataBroker, deviceRpc, notificationService, deviceAction);
+            proxyDataBroker, null, deviceRpc, notificationService, deviceAction);
     }
 
     protected DOMDataBroker newDeviceDataBroker() {
index a4d88adbbee4d50de79567dcf89a4272004acad4..1a8c1e0c6ca3d2abbdc8b69d22eba47707e53180 100644 (file)
@@ -52,7 +52,7 @@ public class SlaveSalFacade {
             actorSystem.dispatcher(), actorResponseWaitTime);
 
         salProvider.getMountInstance().onTopologyDeviceConnected(remoteSchemaContext, netconfDeviceDataBroker,
-            deviceRpc, notificationService, deviceAction);
+            null, deviceRpc, notificationService, deviceAction);
 
         LOG.info("{}: Slave mount point registered.", id);
     }
index 8fed24adb4d5af146b17bbe287bcdc59a4eb2db5..470a92b55a9394b524eebbb2f6bb43f4a6ed8103 100644 (file)
@@ -30,6 +30,7 @@
 
   <modules>
     <module>netconf-api</module>
+    <module>netconf-dom-api</module>
     <module>netconf-config</module>
     <module>netconf-impl</module>
     <module>mdsal-netconf-ssh</module>
index 91b75152382f856b1961aef60539103f92b76e12..2eda0f6d2f71043bbe71be9c8830f1b70ea0dcd9 100644 (file)
       <groupId>org.opendaylight.mdsal</groupId>
       <artifactId>mdsal-binding-api</artifactId>
     </dependency>
+    <dependency>
+      <groupId>org.opendaylight.netconf</groupId>
+      <artifactId>netconf-dom-api</artifactId>
+    </dependency>
     <dependency>
       <groupId>org.opendaylight.mdsal</groupId>
       <artifactId>mdsal-binding-runtime-spi</artifactId>
index ee3d6a08aeaab6607e3c2bafd82136aab2e84aef..d0820e97a279b648185d715ad96de14df3314d3c 100644 (file)
@@ -12,6 +12,7 @@ import org.opendaylight.mdsal.binding.api.DataObjectModification;
 import org.opendaylight.mdsal.binding.api.DataTreeChangeListener;
 import org.opendaylight.mdsal.binding.api.DataTreeModification;
 import org.opendaylight.mdsal.dom.api.DOMDataBroker;
+import org.opendaylight.netconf.dom.api.NetconfDataTreeService;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.optional.rev190614.netconf.node.fields.optional.topology.node.DatastoreLock;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -21,9 +22,12 @@ final class LockChangeListener implements DataTreeChangeListener<DatastoreLock>
     private static final Logger LOG = LoggerFactory.getLogger(LockChangeListener.class);
 
     private final NetconfDeviceDataBroker netconfDeviceDataBroker;
+    private final NetconfDataTreeServiceImpl netconfDataTreeService;
 
-    LockChangeListener(final DOMDataBroker netconfDeviceDataBrokder) {
+    LockChangeListener(final DOMDataBroker netconfDeviceDataBrokder,
+                       final NetconfDataTreeService netconfDataTreeService) {
         this.netconfDeviceDataBroker = (NetconfDeviceDataBroker)netconfDeviceDataBrokder;
+        this.netconfDataTreeService = (NetconfDataTreeServiceImpl) netconfDataTreeService;
     }
 
     @Override
@@ -39,9 +43,11 @@ final class LockChangeListener implements DataTreeChangeListener<DatastoreLock>
                                  + "the data store may interfere with data consistency.");
                     }
                     netconfDeviceDataBroker.setLockAllowed(rootNode.getDataAfter().isDatastoreLockAllowed());
+                    netconfDataTreeService.setLockAllowed(rootNode.getDataAfter().isDatastoreLockAllowed());
                     break;
                 case DELETE:
                     netconfDeviceDataBroker.setLockAllowed(true);
+                    netconfDataTreeService.setLockAllowed(true);
                     break;
                 default:
                     LOG.debug("Unsupported modification type: {}.", rootNode.getModificationType());
diff --git a/netconf/sal-netconf-connector/src/main/java/org/opendaylight/netconf/sal/connect/netconf/sal/NetconfDataTreeServiceImpl.java b/netconf/sal-netconf-connector/src/main/java/org/opendaylight/netconf/sal/connect/netconf/sal/NetconfDataTreeServiceImpl.java
new file mode 100644 (file)
index 0000000..df0c650
--- /dev/null
@@ -0,0 +1,388 @@
+/*
+ * 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.netconf.sal.connect.netconf.sal;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+import com.google.common.base.Preconditions;
+import com.google.common.util.concurrent.FutureCallback;
+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 edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Optional;
+import org.opendaylight.mdsal.common.api.CommitInfo;
+import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
+import org.opendaylight.mdsal.common.api.TransactionCommitFailedException;
+import org.opendaylight.mdsal.dom.api.DOMRpcResult;
+import org.opendaylight.mdsal.dom.api.DOMRpcService;
+import org.opendaylight.netconf.api.DocumentedException;
+import org.opendaylight.netconf.api.ModifyAction;
+import org.opendaylight.netconf.api.NetconfDocumentedException;
+import org.opendaylight.netconf.dom.api.NetconfDataTreeService;
+import org.opendaylight.netconf.sal.connect.netconf.listener.NetconfSessionPreferences;
+import org.opendaylight.netconf.sal.connect.netconf.util.NetconfBaseOps;
+import org.opendaylight.netconf.sal.connect.netconf.util.NetconfRpcFutureCallback;
+import org.opendaylight.netconf.sal.connect.util.RemoteDeviceId;
+import org.opendaylight.yangtools.rfc8528.data.api.MountPointContext;
+import org.opendaylight.yangtools.yang.common.RpcError;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class NetconfDataTreeServiceImpl implements NetconfDataTreeService {
+    private static final Logger LOG = LoggerFactory.getLogger(NetconfDataTreeServiceImpl.class);
+
+    private final RemoteDeviceId id;
+    private final NetconfBaseOps netconfOps;
+    private final boolean rollbackSupport;
+    private final boolean candidateSupported;
+    private final boolean runningWritable;
+
+    private boolean isLockAllowed = true;
+
+    public NetconfDataTreeServiceImpl(final RemoteDeviceId id, final MountPointContext mountContext,
+                                      final DOMRpcService rpc,
+                                      final NetconfSessionPreferences netconfSessionPreferences) {
+        this.id = id;
+        this.netconfOps = new NetconfBaseOps(rpc, mountContext);
+        // get specific attributes from netconf preferences and get rid of it
+        // no need to keep the entire preferences object, its quite big with all the capability QNames
+        candidateSupported = netconfSessionPreferences.isCandidateSupported();
+        runningWritable = netconfSessionPreferences.isRunningWritable();
+        rollbackSupport = netconfSessionPreferences.isRollbackSupported();
+        Preconditions.checkArgument(candidateSupported || runningWritable,
+                "Device %s has advertised neither :writable-running nor :candidate capability."
+                        + "At least one of these should be advertised. Failed to establish a session.", id.getName());
+    }
+
+    @Override
+    public synchronized List<ListenableFuture<? extends DOMRpcResult>> lock() {
+        final List<ListenableFuture<? extends DOMRpcResult>> resultsFutures = new ArrayList<>();
+        if (candidateSupported) {
+            lockCandidate(resultsFutures);
+            if (runningWritable) {
+                lockRunning(resultsFutures);
+            }
+        } else {
+            lockRunning(resultsFutures);
+        }
+        return resultsFutures;
+    }
+
+    @Override
+    public synchronized void unlock() {
+        if (candidateSupported) {
+            unlockCandidate();
+            if (runningWritable) {
+                unlockRunning();
+            }
+        } else {
+            unlockRunning();
+        }
+    }
+
+    /**
+     * This has to be non blocking since it is called from a callback on commit
+     * and its netty threadpool that is really sensitive to blocking calls.
+     */
+    @Override
+    public void discardChanges() {
+        if (candidateSupported) {
+            netconfOps.discardChanges(new NetconfRpcFutureCallback("Discarding candidate", id));
+        }
+    }
+
+    @Override
+    public ListenableFuture<Optional<NormalizedNode<?, ?>>> get(YangInstanceIdentifier path) {
+        return netconfOps.getData(new NetconfRpcFutureCallback("Data read", id), Optional.ofNullable(path));
+    }
+
+    @Override
+    public ListenableFuture<Optional<NormalizedNode<?, ?>>> getConfig(final YangInstanceIdentifier path) {
+        return netconfOps.getConfigRunningData(
+                new NetconfRpcFutureCallback("Data read", id), Optional.ofNullable(path));
+    }
+
+    @Override
+    public synchronized ListenableFuture<? extends DOMRpcResult> merge(final LogicalDatastoreType store,
+                                                                       final YangInstanceIdentifier path,
+                                                                       final NormalizedNode<?, ?> data,
+                                                                       final Optional<ModifyAction> defaultOperation) {
+        checkEditable(store);
+        final DataContainerChild<?, ?> editStructure = netconfOps.createEditConfigStrcture(Optional.ofNullable(data),
+                Optional.of(ModifyAction.MERGE), path);
+
+        return editConfig(defaultOperation, editStructure);
+    }
+
+    @Override
+    public synchronized ListenableFuture<? extends DOMRpcResult> replace(
+            final LogicalDatastoreType store,
+            final YangInstanceIdentifier path,
+            final NormalizedNode<?, ?> data,
+            final Optional<ModifyAction> defaultOperation) {
+        checkEditable(store);
+        final DataContainerChild<?, ?> editStructure = netconfOps.createEditConfigStrcture(Optional.ofNullable(data),
+                Optional.of(ModifyAction.REPLACE), path);
+
+        return editConfig(defaultOperation, editStructure);
+    }
+
+    @Override
+    public synchronized ListenableFuture<? extends DOMRpcResult> create(final LogicalDatastoreType store,
+                                                                        final YangInstanceIdentifier path,
+                                                                        final NormalizedNode<?, ?> data,
+                                                                        final Optional<ModifyAction> defaultOperation) {
+        checkEditable(store);
+        final DataContainerChild<?, ?> editStructure = netconfOps.createEditConfigStrcture(Optional.ofNullable(data),
+                Optional.of(ModifyAction.CREATE), path);
+
+        return editConfig(defaultOperation, editStructure);
+    }
+
+    @Override
+    public synchronized ListenableFuture<? extends DOMRpcResult> delete(final LogicalDatastoreType store,
+                                                                        final YangInstanceIdentifier path) {
+        final DataContainerChild<?, ?> editStructure = netconfOps.createEditConfigStrcture(Optional.empty(),
+                Optional.of(ModifyAction.DELETE), path);
+
+        return editConfig(Optional.empty(), editStructure);
+    }
+
+    @Override
+    public synchronized ListenableFuture<? extends DOMRpcResult> remove(final LogicalDatastoreType store,
+                                                                        final YangInstanceIdentifier path) {
+        final DataContainerChild<?, ?> editStructure = netconfOps.createEditConfigStrcture(Optional.empty(),
+                Optional.of(ModifyAction.REMOVE), path);
+
+        return editConfig(Optional.empty(), editStructure);
+    }
+
+    @Override
+    public ListenableFuture<? extends CommitInfo> commit(
+            List<ListenableFuture<? extends DOMRpcResult>> resultsFutures) {
+        final SettableFuture<CommitInfo> resultFuture = SettableFuture.create();
+        Futures.addCallback(performCommit(resultsFutures), new FutureCallback<>() {
+            @Override
+            public void onSuccess(final RpcResult<Void> result) {
+                if (!result.isSuccessful()) {
+                    final Collection<RpcError> errors = result.getErrors();
+                    resultFuture.setException(new TransactionCommitFailedException(
+                            String.format("Commit of transaction %s failed", this),
+                            errors.toArray(new RpcError[errors.size()])));
+                    return;
+                }
+                resultFuture.set(CommitInfo.empty());
+            }
+
+            @Override
+            public void onFailure(final Throwable failure) {
+                resultFuture.setException(new TransactionCommitFailedException(
+                        String.format("Commit of transaction %s failed", this), failure));
+            }
+        }, MoreExecutors.directExecutor());
+        return resultFuture;
+    }
+
+    @Override
+    public Object getDeviceId() {
+        return id;
+    }
+
+    void setLockAllowed(final boolean isLockAllowedOrig) {
+        this.isLockAllowed = isLockAllowedOrig;
+    }
+
+    private ListenableFuture<? extends DOMRpcResult> editConfig(final Optional<ModifyAction> defaultOperation,
+                                                                final DataContainerChild<?, ?> editStructure) {
+        if (candidateSupported) {
+            return editConfigCandidate(defaultOperation, editStructure);
+        } else {
+            return editConfigRunning(defaultOperation, editStructure);
+        }
+    }
+
+    private ListenableFuture<? extends DOMRpcResult> editConfigRunning(final Optional<ModifyAction> defaultOperation,
+                                                                       final DataContainerChild<?, ?> editStructure) {
+        final NetconfRpcFutureCallback callback = new NetconfRpcFutureCallback("Edit running", id);
+        if (defaultOperation.isPresent()) {
+            return netconfOps.editConfigRunning(callback, editStructure, defaultOperation.get(), rollbackSupport);
+        } else {
+            return netconfOps.editConfigRunning(callback, editStructure, rollbackSupport);
+        }
+    }
+
+    private ListenableFuture<? extends DOMRpcResult> editConfigCandidate(final Optional<ModifyAction> defaultOperation,
+                                                                         final DataContainerChild<?, ?> editStructure) {
+        final NetconfRpcFutureCallback callback = new NetconfRpcFutureCallback("Edit candidate", id);
+        if (defaultOperation.isPresent()) {
+            return netconfOps.editConfigCandidate(callback, editStructure, defaultOperation.get(), rollbackSupport);
+        } else {
+            return netconfOps.editConfigCandidate(callback, editStructure, rollbackSupport);
+        }
+    }
+
+    private void lockRunning(List<ListenableFuture<? extends DOMRpcResult>> resultsFutures) {
+        if (isLockAllowed) {
+            resultsFutures.add(netconfOps.lockRunning(new NetconfRpcFutureCallback("Lock running", id)));
+        } else {
+            LOG.trace("Lock is not allowed: {}", id);
+        }
+    }
+
+    private void unlockRunning() {
+        if (isLockAllowed) {
+            netconfOps.unlockRunning(new NetconfRpcFutureCallback("Unlock running", id));
+        } else {
+            LOG.trace("Unlock is not allowed: {}", id);
+        }
+    }
+
+    private void lockCandidate(List<ListenableFuture<? extends DOMRpcResult>> resultsFutures) {
+        if (isLockAllowed) {
+            resultsFutures.add(netconfOps.lockCandidate(new NetconfRpcFutureCallback("Lock candidate", id) {
+                @Override
+                public void onFailure(Throwable throwable) {
+                    super.onFailure(throwable);
+                    discardChanges();
+                }
+            }));
+        } else {
+            LOG.trace("Lock is not allowed: {}", id);
+        }
+    }
+
+    private void unlockCandidate() {
+        if (isLockAllowed) {
+            netconfOps.unlockCandidate(new NetconfRpcFutureCallback("Unlock candidate", id));
+        } else {
+            LOG.trace("Unlock is not allowed: {}", id);
+        }
+    }
+
+    private void checkEditable(final LogicalDatastoreType store) {
+        checkArgument(store == LogicalDatastoreType.CONFIGURATION,
+                "Can edit only configuration data, not %s", store);
+    }
+
+    private synchronized ListenableFuture<RpcResult<Void>> performCommit(
+            final List<ListenableFuture<? extends DOMRpcResult>> resultsFutures) {
+        resultsFutures.add(netconfOps.commit(new NetconfRpcFutureCallback("Commit", id)));
+
+        final ListenableFuture<RpcResult<Void>> txResult = resultsToTxStatus(id, resultsFutures);
+        Futures.addCallback(txResult, new FutureCallback<>() {
+            @Override
+            public void onSuccess(final RpcResult<Void> result) {
+                unlock();
+            }
+
+            @Override
+            public void onFailure(final Throwable throwable) {
+                discardChanges();
+                unlock();
+            }
+        }, MoreExecutors.directExecutor());
+
+        return txResult;
+    }
+
+    private static ListenableFuture<RpcResult<Void>> resultsToTxStatus(
+            final RemoteDeviceId id, List<ListenableFuture<? extends DOMRpcResult>> resultsFutures) {
+        final SettableFuture<RpcResult<Void>> transformed = SettableFuture.create();
+
+        Futures.addCallback(Futures.allAsList(resultsFutures), new FutureCallback<>() {
+            @Override
+            public void onSuccess(final List<DOMRpcResult> domRpcResults) {
+                if (!transformed.isDone()) {
+                    extractResult(domRpcResults, transformed, id);
+                }
+            }
+
+            @Override
+            public void onFailure(final Throwable throwable) {
+                final NetconfDocumentedException exception =
+                        new NetconfDocumentedException(
+                                id + ":RPC during tx returned an exception" + throwable.getMessage(),
+                                new Exception(throwable),
+                                DocumentedException.ErrorType.APPLICATION,
+                                DocumentedException.ErrorTag.OPERATION_FAILED,
+                                DocumentedException.ErrorSeverity.ERROR);
+                transformed.setException(exception);
+            }
+        }, MoreExecutors.directExecutor());
+
+        return transformed;
+    }
+
+    @SuppressFBWarnings(value = "UPM_UNCALLED_PRIVATE_METHOD",
+            justification = "https://github.com/spotbugs/spotbugs/issues/811")
+    private static void extractResult(final List<DOMRpcResult> domRpcResults,
+                                      final SettableFuture<RpcResult<Void>> transformed,
+                                      final RemoteDeviceId id) {
+        DocumentedException.ErrorType errType = DocumentedException.ErrorType.APPLICATION;
+        DocumentedException.ErrorSeverity errSeverity = DocumentedException.ErrorSeverity.ERROR;
+        StringBuilder msgBuilder = new StringBuilder();
+        boolean errorsEncouneterd = false;
+        String errorTag = "operation-failed";
+
+        for (final DOMRpcResult domRpcResult : domRpcResults) {
+            if (!domRpcResult.getErrors().isEmpty()) {
+                errorsEncouneterd = true;
+                final RpcError error = domRpcResult.getErrors().iterator().next();
+                final RpcError.ErrorType errorType = error.getErrorType();
+                switch (errorType) {
+                    case RPC:
+                        errType = DocumentedException.ErrorType.RPC;
+                        break;
+                    case PROTOCOL:
+                        errType = DocumentedException.ErrorType.PROTOCOL;
+                        break;
+                    case TRANSPORT:
+                        errType = DocumentedException.ErrorType.TRANSPORT;
+                        break;
+                    case APPLICATION:
+                    default:
+                        errType = DocumentedException.ErrorType.APPLICATION;
+                        break;
+                }
+                final RpcError.ErrorSeverity severity = error.getSeverity();
+                switch (severity) {
+                    case WARNING:
+                        errSeverity = DocumentedException.ErrorSeverity.WARNING;
+                        break;
+                    case ERROR:
+                    default:
+                        errSeverity = DocumentedException.ErrorSeverity.ERROR;
+                        break;
+                }
+                msgBuilder.append(error.getMessage());
+                msgBuilder.append(error.getInfo());
+                errorTag = error.getTag();
+            }
+        }
+        if (errorsEncouneterd) {
+            final NetconfDocumentedException exception = new NetconfDocumentedException(id
+                    + ":RPC during tx failed. " + msgBuilder.toString(),
+                    errType,
+                    DocumentedException.ErrorTag.from(errorTag),
+                    errSeverity);
+            transformed.setException(exception);
+            return;
+        }
+        transformed.set(RpcResultBuilder.<Void>success().build());
+    }
+}
index ab214400b6071cad5ca880ef714ee178d69ceb03..9ed9d71efdd75567bc7a84afd331cf1d3d1898af 100644 (file)
@@ -18,6 +18,7 @@ import org.opendaylight.mdsal.dom.api.DOMActionService;
 import org.opendaylight.mdsal.dom.api.DOMMountPointService;
 import org.opendaylight.mdsal.dom.api.DOMNotification;
 import org.opendaylight.mdsal.dom.api.DOMRpcService;
+import org.opendaylight.netconf.dom.api.NetconfDataTreeService;
 import org.opendaylight.netconf.sal.connect.api.RemoteDeviceHandler;
 import org.opendaylight.netconf.sal.connect.netconf.listener.NetconfDeviceCapabilities;
 import org.opendaylight.netconf.sal.connect.netconf.listener.NetconfSessionPreferences;
@@ -77,12 +78,14 @@ public final class NetconfDeviceSalFacade implements AutoCloseable, RemoteDevice
         final EffectiveModelContext schemaContext = mountContext.getEffectiveModelContext();
         final NetconfDeviceDataBroker netconfDeviceDataBroker =
                 new NetconfDeviceDataBroker(id, mountContext, deviceRpc, netconfSessionPreferences);
-        registerLockListener(netconfDeviceDataBroker);
+        final NetconfDataTreeService netconfService =
+                new NetconfDataTreeServiceImpl(id, mountContext, deviceRpc, netconfSessionPreferences);
+        registerLockListener(netconfDeviceDataBroker, netconfService);
         final NetconfDeviceNotificationService notificationService = new NetconfDeviceNotificationService();
 
         salProvider.getMountInstance()
-                .onTopologyDeviceConnected(schemaContext, netconfDeviceDataBroker, deviceRpc, notificationService,
-                        deviceAction);
+                .onTopologyDeviceConnected(schemaContext, netconfDeviceDataBroker, netconfService,
+                        deviceRpc, notificationService, deviceAction);
         salProvider.getTopologyDatastoreAdapter()
                 .updateDeviceData(true, netconfSessionPreferences.getNetconfDeviceCapabilities());
     }
@@ -134,10 +137,11 @@ public final class NetconfDeviceSalFacade implements AutoCloseable, RemoteDevice
         }
     }
 
-    private void registerLockListener(final NetconfDeviceDataBroker netconfDeviceDataBroker) {
+    private void registerLockListener(final NetconfDeviceDataBroker netconfDeviceDataBroker,
+                                      final NetconfDataTreeService netconfDataTreeService) {
         listenerRegistration = dataBroker.registerDataTreeChangeListener(
                 DataTreeIdentifier.create(LogicalDatastoreType.CONFIGURATION, createTopologyListPath()),
-                new LockChangeListener(netconfDeviceDataBroker));
+                new LockChangeListener(netconfDeviceDataBroker, netconfDataTreeService));
     }
 
     private InstanceIdentifier<DatastoreLock> createTopologyListPath() {
index 5dd2418603f5899444939893cc3caaf7dc5a87e6..8c77933ac3ff5f8e435599b1916fa75691ef0186 100644 (file)
@@ -23,6 +23,7 @@ import org.opendaylight.mdsal.dom.api.DOMMountPointService;
 import org.opendaylight.mdsal.dom.api.DOMNotification;
 import org.opendaylight.mdsal.dom.api.DOMNotificationService;
 import org.opendaylight.mdsal.dom.api.DOMRpcService;
+import org.opendaylight.netconf.dom.api.NetconfDataTreeService;
 import org.opendaylight.netconf.sal.connect.util.RemoteDeviceId;
 import org.opendaylight.yangtools.concepts.ObjectRegistration;
 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
@@ -120,11 +121,11 @@ public class NetconfDeviceSalProvider implements AutoCloseable {
         public void onTopologyDeviceConnected(final EffectiveModelContext initialCtx,
                 final DOMDataBroker broker, final DOMRpcService rpc,
                 final NetconfDeviceNotificationService newNotificationService) {
-            onTopologyDeviceConnected(initialCtx, broker, rpc, newNotificationService, null);
+            onTopologyDeviceConnected(initialCtx, broker, null, rpc, newNotificationService, null);
         }
 
         public synchronized void onTopologyDeviceConnected(final EffectiveModelContext initialCtx,
-                final DOMDataBroker broker, final DOMRpcService rpc,
+                final DOMDataBroker broker, final NetconfDataTreeService netconfService, final DOMRpcService rpc,
                 final NetconfDeviceNotificationService newNotificationService, final DOMActionService deviceAction) {
             requireNonNull(mountService, "Closed");
             checkState(topologyRegistration == null, "Already initialized");
@@ -133,12 +134,17 @@ public class NetconfDeviceSalProvider implements AutoCloseable {
                     mountService.createMountPoint(id.getTopologyPath());
             mountBuilder.addInitialSchemaContext(initialCtx);
 
-            mountBuilder.addService(DOMDataBroker.class, broker);
+            if (broker != null) {
+                mountBuilder.addService(DOMDataBroker.class, broker);
+            }
             mountBuilder.addService(DOMRpcService.class, rpc);
             mountBuilder.addService(DOMNotificationService.class, newNotificationService);
             if (deviceAction != null) {
                 mountBuilder.addService(DOMActionService.class, deviceAction);
             }
+            if (netconfService != null) {
+                mountBuilder.addService(NetconfDataTreeService.class, netconfService);
+            }
             this.notificationService = newNotificationService;
 
             topologyRegistration = mountBuilder.register();
index afeeb90c7493d0057fd626eb7f93755280a86197..d2f661a17cee46ebb2e5d91cfcd63d2d76f3fdba 100644 (file)
@@ -25,6 +25,7 @@ import org.opendaylight.mdsal.dom.api.DOMMountPointService;
 import org.opendaylight.mdsal.dom.api.DOMNotification;
 import org.opendaylight.mdsal.dom.api.DOMNotificationService;
 import org.opendaylight.mdsal.dom.api.DOMRpcService;
+import org.opendaylight.netconf.dom.api.NetconfDataTreeService;
 import org.opendaylight.netconf.sal.connect.util.RemoteDeviceId;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.netconf.base._1._0.rev110601.IetfNetconfService;
 import org.opendaylight.yangtools.concepts.ObjectRegistration;
@@ -43,6 +44,8 @@ public class MountInstanceTest {
     @Mock
     private DOMDataBroker broker;
     @Mock
+    private NetconfDataTreeService netconfService;
+    @Mock
     private DOMRpcService rpcService;
     @Mock
     private NetconfDeviceNotificationService notificationService;
@@ -72,7 +75,7 @@ public class MountInstanceTest {
 
 
     @Test
-    public void testOnTopologyDeviceConnected() throws Exception {
+    public void testOnTopologyDeviceConnected() {
         mountInstance.onTopologyDeviceConnected(SCHEMA_CONTEXT, broker, rpcService, notificationService);
         verify(mountPointBuilder).addInitialSchemaContext(SCHEMA_CONTEXT);
         verify(mountPointBuilder).addService(DOMDataBroker.class, broker);
@@ -80,6 +83,16 @@ public class MountInstanceTest {
         verify(mountPointBuilder).addService(DOMNotificationService.class, notificationService);
     }
 
+    @Test
+    public void testOnTopologyDeviceConnectedWithNetconfService() {
+        mountInstance.onTopologyDeviceConnected(SCHEMA_CONTEXT, null, netconfService, rpcService,
+                notificationService, null);
+        verify(mountPointBuilder).addInitialSchemaContext(SCHEMA_CONTEXT);
+        verify(mountPointBuilder).addService(NetconfDataTreeService.class, netconfService);
+        verify(mountPointBuilder).addService(DOMRpcService.class, rpcService);
+        verify(mountPointBuilder).addService(DOMNotificationService.class, notificationService);
+    }
+
     @Test
     public void testOnTopologyDeviceDisconnected() throws Exception {
         mountInstance.onTopologyDeviceConnected(SCHEMA_CONTEXT, broker, rpcService, notificationService);
diff --git a/netconf/sal-netconf-connector/src/test/java/org/opendaylight/netconf/sal/connect/netconf/sal/NetconfDataTreeServiceImplTest.java b/netconf/sal-netconf-connector/src/test/java/org/opendaylight/netconf/sal/connect/netconf/sal/NetconfDataTreeServiceImplTest.java
new file mode 100644 (file)
index 0000000..ca2e3b7
--- /dev/null
@@ -0,0 +1,179 @@
+/*
+ * 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.netconf.sal.connect.netconf.sal;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.isNull;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.verify;
+import static org.opendaylight.netconf.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_COMMIT_QNAME;
+import static org.opendaylight.netconf.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_DISCARD_CHANGES_QNAME;
+import static org.opendaylight.netconf.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_GET_CONFIG_QNAME;
+import static org.opendaylight.netconf.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_GET_QNAME;
+import static org.opendaylight.netconf.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_LOCK_QNAME;
+import static org.opendaylight.netconf.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_UNLOCK_QNAME;
+import static org.opendaylight.netconf.sal.connect.netconf.util.NetconfMessageTransformUtil.toPath;
+
+import com.google.common.util.concurrent.ListenableFuture;
+import java.net.InetSocketAddress;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Optional;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnitRunner;
+import org.opendaylight.binding.runtime.spi.BindingRuntimeHelpers;
+import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
+import org.opendaylight.mdsal.dom.api.DOMRpcResult;
+import org.opendaylight.mdsal.dom.api.DOMRpcService;
+import org.opendaylight.mdsal.dom.spi.DefaultDOMRpcResult;
+import org.opendaylight.netconf.api.NetconfMessage;
+import org.opendaylight.netconf.sal.connect.netconf.AbstractTestModelTest;
+import org.opendaylight.netconf.sal.connect.netconf.listener.NetconfSessionPreferences;
+import org.opendaylight.netconf.sal.connect.netconf.sal.tx.TxTestUtils;
+import org.opendaylight.netconf.sal.connect.netconf.schema.mapping.NetconfMessageTransformer;
+import org.opendaylight.netconf.sal.connect.netconf.util.NetconfMessageTransformUtil;
+import org.opendaylight.netconf.sal.connect.util.RemoteDeviceId;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.netconf.base._1._0.rev110601.IetfNetconfService;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.NetconfState;
+import org.opendaylight.yangtools.rcf8528.data.util.EmptyMountPointContext;
+import org.opendaylight.yangtools.util.concurrent.FluentFutures;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+
+@RunWith(MockitoJUnitRunner.StrictStubs.class)
+public class NetconfDataTreeServiceImplTest extends AbstractTestModelTest {
+    @Mock
+    private DOMRpcService rpcService;
+    private NetconfDataTreeServiceImpl netconService;
+    private NetconfMessageTransformer netconfMessageTransformer;
+    ArgumentCaptor<ContainerNode> captor = ArgumentCaptor.forClass(ContainerNode.class);
+
+    @Before
+    public void setUp() {
+        doReturn(FluentFutures.immediateFluentFuture(new DefaultDOMRpcResult())).when(rpcService)
+                .invokeRpc(any(), any());
+        netconService = getNetconService();
+        final EffectiveModelContext model = BindingRuntimeHelpers.createEffectiveModel(IetfNetconfService.class,
+                NetconfState.class);
+        netconfMessageTransformer = new NetconfMessageTransformer(new EmptyMountPointContext(model), true,
+                BASE_SCHEMAS.getBaseSchema());
+    }
+
+    @Test
+    public void lock() {
+        netconService.lock();
+        verify(rpcService).invokeRpc(eq(toPath(NETCONF_LOCK_QNAME)), any(ContainerNode.class));
+    }
+
+    @Test
+    public void unlock() {
+        netconService.unlock();
+        verify(rpcService).invokeRpc(eq(toPath(NETCONF_UNLOCK_QNAME)), any(ContainerNode.class));
+    }
+
+    @Test
+    public void discardChanges() {
+        doReturn(FluentFutures.immediateFluentFuture(new DefaultDOMRpcResult())).when(rpcService)
+                .invokeRpc(any(SchemaPath.class), isNull());
+        netconService.discardChanges();
+        verify(rpcService).invokeRpc(eq(toPath(NETCONF_DISCARD_CHANGES_QNAME)), isNull());
+    }
+
+    @Test
+    public void get() {
+        netconService.get(null);
+        verify(rpcService).invokeRpc(eq(toPath(NETCONF_GET_QNAME)), any(ContainerNode.class));
+    }
+
+    @Test
+    public void getConfig() {
+        netconService.getConfig(null);
+        verify(rpcService).invokeRpc(eq(toPath(NETCONF_GET_CONFIG_QNAME)), any(ContainerNode.class));
+    }
+
+    @Test
+    public void merge() {
+        netconService.merge(LogicalDatastoreType.CONFIGURATION, TxTestUtils.getLeafId(), TxTestUtils.getLeafNode(),
+                Optional.empty());
+        verify(rpcService).invokeRpc(eq(SchemaPath.create(true, NetconfMessageTransformUtil.NETCONF_EDIT_CONFIG_QNAME)),
+                captor.capture());
+
+        final NetconfMessage netconfMessage = netconfMessageTransformer.toRpcRequest(
+                toPath(NetconfMessageTransformUtil.NETCONF_EDIT_CONFIG_QNAME), captor.getValue());
+        Assert.assertTrue(netconfMessage.toString().contains("operation=\"merge\""));
+    }
+
+    @Test
+    public void replace() {
+        netconService.replace(LogicalDatastoreType.CONFIGURATION, TxTestUtils.getLeafId(), TxTestUtils.getLeafNode(),
+                Optional.empty());
+        verify(rpcService).invokeRpc(
+                eq(SchemaPath.create(true, NetconfMessageTransformUtil.NETCONF_EDIT_CONFIG_QNAME)), captor.capture());
+
+        final NetconfMessage netconfMessage = netconfMessageTransformer.toRpcRequest(
+                toPath(NetconfMessageTransformUtil.NETCONF_EDIT_CONFIG_QNAME), captor.getValue());
+        Assert.assertTrue(netconfMessage.toString().contains("operation=\"replace\""));
+    }
+
+    @Test
+    public void create() {
+        netconService.create(LogicalDatastoreType.CONFIGURATION, TxTestUtils.getLeafId(), TxTestUtils.getLeafNode(),
+                Optional.empty());
+        verify(rpcService).invokeRpc(
+                eq(SchemaPath.create(true, NetconfMessageTransformUtil.NETCONF_EDIT_CONFIG_QNAME)), captor.capture());
+
+        final NetconfMessage netconfMessage = netconfMessageTransformer.toRpcRequest(
+                toPath(NetconfMessageTransformUtil.NETCONF_EDIT_CONFIG_QNAME), captor.getValue());
+        Assert.assertTrue(netconfMessage.toString().contains("operation=\"create\""));
+    }
+
+    @Test
+    public void delete() {
+        netconService.delete(LogicalDatastoreType.CONFIGURATION, TxTestUtils.getLeafId().getParent());
+        verify(rpcService).invokeRpc(
+                eq(SchemaPath.create(true, NetconfMessageTransformUtil.NETCONF_EDIT_CONFIG_QNAME)), captor.capture());
+
+        final NetconfMessage netconfMessage = netconfMessageTransformer.toRpcRequest(
+                toPath(NetconfMessageTransformUtil.NETCONF_EDIT_CONFIG_QNAME), captor.getValue());
+        Assert.assertTrue(netconfMessage.toString().contains("operation=\"delete\""));
+    }
+
+    @Test
+    public void remove() {
+        netconService.remove(LogicalDatastoreType.CONFIGURATION, TxTestUtils.getLeafId().getParent());
+        verify(rpcService).invokeRpc(
+                eq(SchemaPath.create(true, NetconfMessageTransformUtil.NETCONF_EDIT_CONFIG_QNAME)), captor.capture());
+
+        final NetconfMessage netconfMessage = netconfMessageTransformer.toRpcRequest(
+                toPath(NetconfMessageTransformUtil.NETCONF_EDIT_CONFIG_QNAME), captor.getValue());
+        Assert.assertTrue(netconfMessage.toString().contains("operation=\"remove\""));
+    }
+
+    @Test
+    public void commit() {
+        List<ListenableFuture<? extends DOMRpcResult>> resultsFutures = new ArrayList<>();
+        netconService.commit(resultsFutures);
+        verify(rpcService).invokeRpc(eq(toPath(NETCONF_COMMIT_QNAME)), any(ContainerNode.class));
+    }
+
+    private NetconfDataTreeServiceImpl getNetconService() {
+        NetconfSessionPreferences prefs = NetconfSessionPreferences.fromStrings(
+                Collections.singletonList(NetconfMessageTransformUtil.NETCONF_CANDIDATE_URI.toString()));
+        final RemoteDeviceId id =
+                new RemoteDeviceId("device-1", InetSocketAddress.createUnresolved("localhost", 17830));
+        return new NetconfDataTreeServiceImpl(id, new EmptyMountPointContext(SCHEMA_CONTEXT), rpcService, prefs);
+    }
+}
\ No newline at end of file
index d2e2bec004cb5457bb50f4364ac8fe22d38a93df..3ba465071849ca3dbf924987b75db47e6fc8379c 100644 (file)
@@ -28,6 +28,7 @@ import org.opendaylight.mdsal.binding.api.DataBroker;
 import org.opendaylight.mdsal.dom.api.DOMDataBroker;
 import org.opendaylight.mdsal.dom.api.DOMNotification;
 import org.opendaylight.mdsal.dom.api.DOMRpcService;
+import org.opendaylight.netconf.dom.api.NetconfDataTreeService;
 import org.opendaylight.netconf.sal.connect.netconf.listener.NetconfDeviceCapabilities;
 import org.opendaylight.netconf.sal.connect.netconf.listener.NetconfSessionPreferences;
 import org.opendaylight.netconf.sal.connect.netconf.util.NetconfMessageTransformUtil;
@@ -100,8 +101,8 @@ public class NetconfDeviceSalFacadeTest {
             null);
 
         verify(mountInstance, times(1)).onTopologyDeviceConnected(eq(schemaContext),
-                any(DOMDataBroker.class), eq(deviceRpc), any(NetconfDeviceNotificationService.class),
-                isNull());
+                any(DOMDataBroker.class), any(NetconfDataTreeService.class), eq(deviceRpc),
+                any(NetconfDeviceNotificationService.class), isNull());
         verify(netconfDeviceTopologyAdapter,
                 times(1)).updateDeviceData(true, netconfSessionPreferences.getNetconfDeviceCapabilities());
     }
index 5e53287cde91137cd6b304a5ba9efa7ce06acc19..498812a46a7735fef217c4305a7d792a3e49bfc0 100644 (file)
@@ -14,7 +14,7 @@ import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
 import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
 import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
 
-final class TxTestUtils {
+public final class TxTestUtils {
 
     private static final QName Q_NAME_1 = QName.create("test:namespace", "2013-07-22", "c");
     private static final QName Q_NAME_2 = QName.create(Q_NAME_1, "a");
@@ -29,7 +29,7 @@ final class TxTestUtils {
                 .build();
     }
 
-    static YangInstanceIdentifier getLeafId() {
+    public static YangInstanceIdentifier getLeafId() {
         return YangInstanceIdentifier.builder()
                 .node(Q_NAME_1)
                 .node(Q_NAME_2)
@@ -42,11 +42,10 @@ final class TxTestUtils {
                 .build();
     }
 
-    static LeafNode<String> getLeafNode() {
+    public static LeafNode<String> getLeafNode() {
         return Builders.<String>leafBuilder()
                 .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(Q_NAME_2))
                 .withValue("data")
                 .build();
     }
-
 }
index 3fc2d9cf60abbbc73cd9dc74152440c6736ac005..a2a188a53df40785a89ed0c8c7bcae7f4e87c5b8 100644 (file)
       <groupId>org.opendaylight.netconf</groupId>
       <artifactId>netconf-api</artifactId>
     </dependency>
+    <dependency>
+      <groupId>org.opendaylight.netconf</groupId>
+      <artifactId>netconf-dom-api</artifactId>
+    </dependency>
 
     <dependency>
       <groupId>org.opendaylight.yangtools</groupId>
index 67fea4db2f9cb68f8d4943f2e71c051f46dfe1d1..ec3df98aea5ea04074ccd2fc45c45e5c04fbbf01 100644 (file)
@@ -17,6 +17,7 @@ import java.lang.reflect.Type;
 import java.net.URI;
 import java.nio.charset.StandardCharsets;
 import java.util.List;
+import java.util.Map;
 import java.util.Set;
 import javax.ws.rs.Produces;
 import javax.ws.rs.WebApplicationException;
@@ -95,6 +96,12 @@ public class NormalizedNodeJsonBodyWriter implements MessageBodyWriter<Normalize
             jsonWriter.endObject();
             jsonWriter.flush();
         }
+
+        if (httpHeaders != null) {
+            for (final Map.Entry<String, Object> entry : context.getNewHeaders().entrySet()) {
+                httpHeaders.add(entry.getKey(), entry.getValue());
+            }
+        }
     }
 
     private static void writeNormalizedNode(final JsonWriter jsonWriter,
index 9d849ec565001c49b76a495bc53c00cfea3f3ab1..a89caa90c96a20e3698c5d79ad466f66320f44b9 100644 (file)
@@ -14,6 +14,7 @@ import java.lang.annotation.Annotation;
 import java.lang.reflect.Type;
 import java.nio.charset.StandardCharsets;
 import java.util.List;
+import java.util.Map;
 import java.util.Set;
 import javanet.staxutils.IndentingXMLStreamWriter;
 import javax.ws.rs.Produces;
@@ -84,6 +85,11 @@ public class NormalizedNodeXmlBodyWriter implements MessageBodyWriter<Normalized
         if (context.getData() == null) {
             return;
         }
+        if (httpHeaders != null) {
+            for (final Map.Entry<String, Object> entry : context.getNewHeaders().entrySet()) {
+                httpHeaders.add(entry.getKey(), entry.getValue());
+            }
+        }
 
         XMLStreamWriter xmlWriter;
         try {
index 4bb24bc7d6fae28d1f4fcd5c201dba5df41cc8d9..a9edb4a3283ccca7eb165320a032f6d15bfb0462 100644 (file)
@@ -24,16 +24,18 @@ import java.time.format.DateTimeFormatter;
 import java.util.List;
 import java.util.Map.Entry;
 import java.util.Optional;
+import java.util.concurrent.ExecutionException;
 import javax.ws.rs.Path;
 import javax.ws.rs.WebApplicationException;
 import javax.ws.rs.core.Response;
 import javax.ws.rs.core.UriInfo;
 import org.eclipse.jdt.annotation.NonNull;
 import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
 import org.opendaylight.mdsal.dom.api.DOMActionResult;
 import org.opendaylight.mdsal.dom.api.DOMDataBroker;
-import org.opendaylight.mdsal.dom.api.DOMDataTreeReadWriteTransaction;
 import org.opendaylight.mdsal.dom.api.DOMMountPoint;
+import org.opendaylight.netconf.dom.api.NetconfDataTreeService;
 import org.opendaylight.restconf.common.context.InstanceIdentifierContext;
 import org.opendaylight.restconf.common.context.NormalizedNodeContext;
 import org.opendaylight.restconf.common.context.WriterParameters;
@@ -43,13 +45,16 @@ import org.opendaylight.restconf.common.errors.RestconfError.ErrorTag;
 import org.opendaylight.restconf.common.errors.RestconfError.ErrorType;
 import org.opendaylight.restconf.common.patch.PatchContext;
 import org.opendaylight.restconf.common.patch.PatchStatusContext;
+import org.opendaylight.restconf.nb.rfc8040.Rfc8040;
 import org.opendaylight.restconf.nb.rfc8040.handlers.ActionServiceHandler;
 import org.opendaylight.restconf.nb.rfc8040.handlers.DOMMountPointServiceHandler;
 import org.opendaylight.restconf.nb.rfc8040.handlers.SchemaContextHandler;
 import org.opendaylight.restconf.nb.rfc8040.handlers.TransactionChainHandler;
 import org.opendaylight.restconf.nb.rfc8040.rests.services.api.RestconfDataService;
 import org.opendaylight.restconf.nb.rfc8040.rests.services.api.RestconfStreamsSubscriptionService;
-import org.opendaylight.restconf.nb.rfc8040.rests.transactions.TransactionVarsWrapper;
+import org.opendaylight.restconf.nb.rfc8040.rests.transactions.MdsalRestconfStrategy;
+import org.opendaylight.restconf.nb.rfc8040.rests.transactions.NetconfRestconfStrategy;
+import org.opendaylight.restconf.nb.rfc8040.rests.transactions.RestconfStrategy;
 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;
@@ -60,6 +65,7 @@ import org.opendaylight.restconf.nb.rfc8040.rests.utils.RestconfDataServiceConst
 import org.opendaylight.restconf.nb.rfc8040.rests.utils.RestconfInvokeOperationsUtil;
 import org.opendaylight.restconf.nb.rfc8040.streams.listeners.NotificationListenerAdapter;
 import org.opendaylight.restconf.nb.rfc8040.utils.mapping.RestconfMappingNodeUtil;
+import org.opendaylight.restconf.nb.rfc8040.utils.parser.IdentifierCodec;
 import org.opendaylight.restconf.nb.rfc8040.utils.parser.ParserIdentifier;
 import org.opendaylight.yang.gen.v1.urn.sal.restconf.event.subscription.rev140708.NotificationOutputTypeGrouping.NotificationOutputType;
 import org.opendaylight.yangtools.concepts.Immutable;
@@ -142,10 +148,9 @@ public class RestconfDataServiceImpl implements RestconfDataService {
         final WriterParameters parameters = ReadDataTransactionUtil.parseUriParameters(instanceIdentifier, uriInfo);
 
         final DOMMountPoint mountPoint = instanceIdentifier.getMountPoint();
-        final TransactionVarsWrapper transactionNode = new TransactionVarsWrapper(
-                instanceIdentifier, mountPoint, getTransactionChainHandler(mountPoint));
+        final RestconfStrategy strategy = getRestconfStrategy(instanceIdentifier, mountPoint);
         final NormalizedNode<?, ?> node = readData(identifier, parameters.getContent(),
-                transactionNode, parameters.getWithDefault(), schemaContextRef, uriInfo);
+                strategy, parameters.getWithDefault(), schemaContextRef, uriInfo);
         if (identifier != null && identifier.contains(STREAM_PATH) && identifier.contains(STREAM_ACCESS_PATH_PART)
                 && identifier.contains(STREAM_LOCATION_PATH_PART)) {
             final String value = (String) node.getValue();
@@ -173,62 +178,83 @@ public class RestconfDataServiceImpl implements RestconfDataService {
         return Response.status(200).entity(new NormalizedNodeContext(instanceIdentifier, node, parameters)).build();
     }
 
-
     /**
      * Read specific type of data from data store via transaction and if identifier read data from
      * streams then put streams from actual schema context to datastore.
      *
-     * @param identifier
-     *             identifier of data to read
-     * @param content
-     *             type of data to read (config, state, all)
-     * @param transactionNode
-     *             {@link TransactionVarsWrapper} - wrapper for variables
-     * @param withDefa
-     *             vaule of with-defaults parameter
-     * @param schemaContext
-     *             schema context
-     * @param uriInfo
-     *             uri info
+     * @param identifier    identifier of data to read
+     * @param content       type of data to read (config, state, all)
+     * @param strategy      {@link RestconfStrategy} - object that perform the actual DS operations
+     * @param withDefa      vaule of with-defaults parameter
+     * @param schemaContext schema context
+     * @param uriInfo       uri info
      * @return {@link NormalizedNode}
      */
-    private static NormalizedNode<?, ?> readData(final String identifier, final String content,
-                                                final TransactionVarsWrapper transactionNode, final String withDefa,
+    public static NormalizedNode<?, ?> readData(final String identifier, final String content,
+                                                final RestconfStrategy strategy, final String withDefa,
                                                 final EffectiveModelContext schemaContext, final UriInfo uriInfo) {
         if (identifier != null && identifier.contains(STREAMS_PATH) && !identifier.contains(STREAM_PATH_PART)) {
-            createAllYangNotificationStreams(transactionNode, schemaContext, uriInfo);
+            createAllYangNotificationStreams(strategy, schemaContext, uriInfo);
         }
-        return ReadDataTransactionUtil.readData(content, transactionNode, withDefa, schemaContext);
+        return ReadDataTransactionUtil.readData(content, strategy, withDefa, schemaContext);
     }
 
-    private static void createAllYangNotificationStreams(final TransactionVarsWrapper transactionNode,
-            final EffectiveModelContext schemaContext, final UriInfo uriInfo) {
-        final DOMDataTreeReadWriteTransaction wTx = transactionNode.getTransactionChain().newReadWriteTransaction();
-        final boolean exist = SubscribeToStreamUtil.checkExist(schemaContext, wTx);
+    private static void createAllYangNotificationStreams(final RestconfStrategy strategy,
+                                                         final EffectiveModelContext schemaContext,
+                                                         final UriInfo uriInfo) {
+        strategy.prepareReadWriteExecution();
+        final boolean exist = checkExist(schemaContext, strategy);
 
         for (final NotificationDefinition notificationDefinition : schemaContext.getNotifications()) {
             final NotificationListenerAdapter notifiStreamXML =
-                    CreateStreamUtil.createYangNotifiStream(notificationDefinition, schemaContext,
-                            NotificationOutputType.XML);
+                CreateStreamUtil.createYangNotifiStream(notificationDefinition, schemaContext,
+                    NotificationOutputType.XML);
             final NotificationListenerAdapter notifiStreamJSON =
-                    CreateStreamUtil.createYangNotifiStream(notificationDefinition, schemaContext,
-                            NotificationOutputType.JSON);
-            writeNotificationStreamToDatastore(schemaContext, uriInfo, wTx, exist, notifiStreamXML);
-            writeNotificationStreamToDatastore(schemaContext, uriInfo, wTx, exist, notifiStreamJSON);
+                CreateStreamUtil.createYangNotifiStream(notificationDefinition, schemaContext,
+                    NotificationOutputType.JSON);
+            writeNotificationStreamToDatastore(schemaContext, uriInfo, strategy, exist, notifiStreamXML);
+            writeNotificationStreamToDatastore(schemaContext, uriInfo, strategy, exist, notifiStreamJSON);
+        }
+        try {
+            strategy.commit().get();
+        } catch (final InterruptedException | ExecutionException e) {
+            throw new RestconfDocumentedException("Problem while putting data to DS.", e);
         }
-        SubscribeToStreamUtil.submitData(wTx);
     }
 
     private static void writeNotificationStreamToDatastore(final EffectiveModelContext schemaContext,
-            final UriInfo uriInfo, final DOMDataTreeReadWriteTransaction readWriteTransaction, final boolean exist,
-            final NotificationListenerAdapter listener) {
+                                                           final UriInfo uriInfo, final RestconfStrategy strategy,
+                                                           final boolean exist,
+                                                           final NotificationListenerAdapter listener) {
         final URI uri = SubscribeToStreamUtil.prepareUriByStreamName(uriInfo, listener.getStreamName());
         final NormalizedNode<?, ?> mapToStreams =
-                RestconfMappingNodeUtil.mapYangNotificationStreamByIetfRestconfMonitoring(
-                    listener.getSchemaPath().getLastComponent(), schemaContext.getNotifications(), null,
-                    listener.getOutputType(), uri, SubscribeToStreamUtil.getMonitoringModule(schemaContext), exist);
-        SubscribeToStreamUtil.writeDataToDS(schemaContext,
-                listener.getSchemaPath().getLastComponent().getLocalName(), readWriteTransaction, exist, mapToStreams);
+            RestconfMappingNodeUtil.mapYangNotificationStreamByIetfRestconfMonitoring(
+                listener.getSchemaPath().getLastComponent(), schemaContext.getNotifications(), null,
+                listener.getOutputType(), uri, SubscribeToStreamUtil.getMonitoringModule(schemaContext), exist);
+        writeDataToDS(schemaContext,
+            listener.getSchemaPath().getLastComponent().getLocalName(), strategy, exist, mapToStreams);
+    }
+
+    private static boolean checkExist(final EffectiveModelContext schemaContext, final RestconfStrategy strategy) {
+        try {
+            return strategy.exists(LogicalDatastoreType.OPERATIONAL,
+                IdentifierCodec.deserialize(Rfc8040.MonitoringModule.PATH_TO_STREAMS, schemaContext)).get();
+        } catch (final InterruptedException | ExecutionException exception) {
+            throw new RestconfDocumentedException("Problem while checking data if exists", exception);
+        }
+    }
+
+    private static void writeDataToDS(final EffectiveModelContext schemaContext, final String name,
+                                      final RestconfStrategy strategy, final boolean exist,
+                                      final NormalizedNode mapToStreams) {
+        String pathId;
+        if (exist) {
+            pathId = Rfc8040.MonitoringModule.PATH_TO_STREAM_WITHOUT_KEY + name;
+        } else {
+            pathId = Rfc8040.MonitoringModule.PATH_TO_STREAMS;
+        }
+        strategy.merge(LogicalDatastoreType.OPERATIONAL,
+            IdentifierCodec.deserialize(pathId, schemaContext), mapToStreams);
     }
 
     @Override
@@ -245,19 +271,12 @@ public class RestconfDataServiceImpl implements RestconfDataService {
         PutDataTransactionUtil.validateListKeysEqualityInPayloadAndUri(payload);
 
         final DOMMountPoint mountPoint = payload.getInstanceIdentifierContext().getMountPoint();
-        final TransactionChainHandler localTransactionChainHandler;
-        final EffectiveModelContext ref;
-        if (mountPoint == null) {
-            localTransactionChainHandler = this.transactionChainHandler;
-            ref = this.schemaContextHandler.get();
-        } else {
-            localTransactionChainHandler = transactionChainOfMountPoint(mountPoint);
-            ref = mountPoint.getEffectiveModelContext();
-        }
+        final EffectiveModelContext ref = mountPoint == null
+                ? this.schemaContextHandler.get()
+                : mountPoint.getEffectiveModelContext();
 
-        final TransactionVarsWrapper transactionNode = new TransactionVarsWrapper(
-                payload.getInstanceIdentifierContext(), mountPoint, localTransactionChainHandler);
-        return PutDataTransactionUtil.putData(payload, ref, transactionNode, checkedParms.insert, checkedParms.point);
+        final RestconfStrategy strategy = getRestconfStrategy(payload.getInstanceIdentifierContext(), mountPoint);
+        return PutDataTransactionUtil.putData(payload, ref, strategy, checkedParms.insert, checkedParms.point);
     }
 
     private static QueryParams checkQueryParameters(final UriInfo uriInfo) {
@@ -321,11 +340,10 @@ public class RestconfDataServiceImpl implements RestconfDataService {
         }
 
         final QueryParams checkedParms = checkQueryParameters(uriInfo);
-
         final DOMMountPoint mountPoint = payload.getInstanceIdentifierContext().getMountPoint();
-        final TransactionVarsWrapper transactionNode = new TransactionVarsWrapper(
-                payload.getInstanceIdentifierContext(), mountPoint, getTransactionChainHandler(mountPoint));
-        return PostDataTransactionUtil.postData(uriInfo, payload, transactionNode,
+        final RestconfStrategy strategy = getRestconfStrategy(payload.getInstanceIdentifierContext(),
+                payload.getInstanceIdentifierContext().getMountPoint());
+        return PostDataTransactionUtil.postData(uriInfo, payload, strategy,
                 getSchemaContext(mountPoint), checkedParms.insert, checkedParms.point);
     }
 
@@ -335,16 +353,8 @@ public class RestconfDataServiceImpl implements RestconfDataService {
                 identifier, this.schemaContextHandler.get(), Optional.of(this.mountPointServiceHandler.get()));
 
         final DOMMountPoint mountPoint = instanceIdentifier.getMountPoint();
-        final TransactionChainHandler localTransactionChainHandler;
-        if (mountPoint == null) {
-            localTransactionChainHandler = this.transactionChainHandler;
-        } else {
-            localTransactionChainHandler = transactionChainOfMountPoint(mountPoint);
-        }
-
-        final TransactionVarsWrapper transactionNode = new TransactionVarsWrapper(instanceIdentifier, mountPoint,
-                localTransactionChainHandler);
-        return DeleteDataTransactionUtil.deleteData(transactionNode);
+        final RestconfStrategy strategy = getRestconfStrategy(instanceIdentifier, mountPoint);
+        return DeleteDataTransactionUtil.deleteData(strategy);
     }
 
     @Override
@@ -355,9 +365,8 @@ public class RestconfDataServiceImpl implements RestconfDataService {
     @Override
     public PatchStatusContext patchData(final PatchContext context, final UriInfo uriInfo) {
         final DOMMountPoint mountPoint = requireNonNull(context).getInstanceIdentifierContext().getMountPoint();
-        final TransactionVarsWrapper transactionNode = new TransactionVarsWrapper(
-                context.getInstanceIdentifierContext(), mountPoint, getTransactionChainHandler(mountPoint));
-        return PatchDataTransactionUtil.patchData(context, transactionNode, getSchemaContext(mountPoint));
+        final RestconfStrategy strategy = getRestconfStrategy(context.getInstanceIdentifierContext(), mountPoint);
+        return PatchDataTransactionUtil.patchData(context, strategy, getSchemaContext(mountPoint));
     }
 
     @Override
@@ -372,34 +381,35 @@ public class RestconfDataServiceImpl implements RestconfDataService {
         PutDataTransactionUtil.validateListKeysEqualityInPayloadAndUri(payload);
 
         final DOMMountPoint mountPoint = payload.getInstanceIdentifierContext().getMountPoint();
-        final TransactionChainHandler localTransactionChainHandler;
-        final EffectiveModelContext ref;
-        if (mountPoint == null) {
-            localTransactionChainHandler = this.transactionChainHandler;
-            ref = this.schemaContextHandler.get();
-        } else {
-            localTransactionChainHandler = transactionChainOfMountPoint(mountPoint);
-            ref = mountPoint.getEffectiveModelContext();
-        }
-
-        final TransactionVarsWrapper transactionNode = new TransactionVarsWrapper(
-                payload.getInstanceIdentifierContext(), mountPoint, localTransactionChainHandler);
-
-        return PlainPatchDataTransactionUtil.patchData(payload, transactionNode, ref);
-    }
+        final EffectiveModelContext ref = mountPoint == null
+                ? this.schemaContextHandler.get()
+                : mountPoint.getEffectiveModelContext();
+        final RestconfStrategy strategy = getRestconfStrategy(payload.getInstanceIdentifierContext(), mountPoint);
 
-    private TransactionChainHandler getTransactionChainHandler(final DOMMountPoint mountPoint) {
-        return mountPoint == null ? transactionChainHandler : transactionChainOfMountPoint(mountPoint);
+        return PlainPatchDataTransactionUtil.patchData(payload, strategy, ref);
     }
 
     private EffectiveModelContext getSchemaContext(final DOMMountPoint mountPoint) {
         return mountPoint == null ? schemaContextHandler.get() : mountPoint.getEffectiveModelContext();
     }
 
+    public synchronized RestconfStrategy getRestconfStrategy(final InstanceIdentifierContext<?> instanceIdentifier,
+                                                final DOMMountPoint mountPoint) {
+        if (mountPoint != null) {
+            final Optional<NetconfDataTreeService> service = mountPoint.getService(NetconfDataTreeService.class);
+            if (service.isPresent()) {
+                return new NetconfRestconfStrategy(service.get(), instanceIdentifier);
+            }
+        }
+        final TransactionChainHandler transactionChain = mountPoint == null
+                ? transactionChainHandler : transactionChainOfMountPoint(mountPoint);
+        return new MdsalRestconfStrategy(instanceIdentifier, transactionChain);
+    }
+
     /**
      * Prepare transaction chain to access data of mount point.
-     * @param mountPoint
-     *            mount point reference
+     *
+     * @param mountPoint mount point reference
      * @return {@link TransactionChainHandler}
      */
     private static TransactionChainHandler transactionChainOfMountPoint(final @NonNull DOMMountPoint mountPoint) {
@@ -416,10 +426,8 @@ public class RestconfDataServiceImpl implements RestconfDataService {
     /**
      * Invoke Action operation.
      *
-     * @param payload
-     *             {@link NormalizedNodeContext} - the body of the operation
-     * @param uriInfo
-     *             URI info
+     * @param payload {@link NormalizedNodeContext} - the body of the operation
+     * @param uriInfo URI info
      * @return {@link NormalizedNodeContext} wrapped in {@link Response}
      */
     public Response invokeAction(final NormalizedNodeContext payload, final UriInfo uriInfo) {
index 9eab73c316754501a0d307966d687ee33c00bbb5..b6897539254c7d387ffb1be36b469d0145c8aeac 100644 (file)
@@ -328,7 +328,6 @@ final class SubscribeToStreamUtil {
 
     static boolean checkExist(final SchemaContext schemaContext,
                               final DOMDataTreeReadOperations readWriteTransaction) {
-        boolean exist;
         try {
             return readWriteTransaction.exists(LogicalDatastoreType.OPERATIONAL,
                     IdentifierCodec.deserialize(MonitoringModule.PATH_TO_STREAMS, schemaContext)).get();
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
new file mode 100644 (file)
index 0000000..f05f675
--- /dev/null
@@ -0,0 +1,115 @@
+/*
+ * 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.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.DOMDataTreeReadTransaction;
+import org.opendaylight.mdsal.dom.api.DOMDataTreeReadWriteTransaction;
+import org.opendaylight.mdsal.dom.api.DOMTransactionChain;
+import org.opendaylight.restconf.common.context.InstanceIdentifierContext;
+import org.opendaylight.restconf.nb.rfc8040.handlers.TransactionChainHandler;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+/**
+ * Strategy that allow to communicate with netconf devices in terms of md-sal transactions.
+ *
+ * @see DOMTransactionChain
+ * @see DOMDataTreeReadWriteTransaction
+ */
+public class MdsalRestconfStrategy implements RestconfStrategy {
+    private final InstanceIdentifierContext<?> instanceIdentifier;
+    private final DOMTransactionChain transactionChain;
+    private DOMDataTreeReadWriteTransaction rwTx;
+    private final TransactionChainHandler transactionChainHandler;
+
+    public MdsalRestconfStrategy(final InstanceIdentifierContext<?> instanceIdentifier,
+                                 final TransactionChainHandler transactionChainHandler) {
+        this.instanceIdentifier = requireNonNull(instanceIdentifier);
+        this.transactionChainHandler = requireNonNull(transactionChainHandler);
+        transactionChain = transactionChainHandler.get();
+    }
+
+    @Override
+    public void prepareReadWriteExecution() {
+        rwTx = transactionChain.newReadWriteTransaction();
+    }
+
+    @Override
+    public void cancel() {
+        if (rwTx != null) {
+            rwTx.cancel();
+        }
+        transactionChain.close();
+    }
+
+    @Override
+    public ListenableFuture<Optional<NormalizedNode<?, ?>>> read(final LogicalDatastoreType store,
+                                                                 final YangInstanceIdentifier path) {
+        try (DOMDataTreeReadTransaction tx = transactionChain.newReadOnlyTransaction()) {
+            return tx.read(store, path);
+        }
+    }
+
+    @Override
+    public FluentFuture<Boolean> exists(LogicalDatastoreType store, YangInstanceIdentifier path) {
+        return rwTx.exists(store, path);
+    }
+
+    @Override
+    public void delete(LogicalDatastoreType store, final YangInstanceIdentifier path) {
+        rwTx.delete(store, path);
+    }
+
+    @Override
+    public void merge(LogicalDatastoreType store, YangInstanceIdentifier path, NormalizedNode<?, ?> data) {
+        rwTx.merge(store, path, data);
+    }
+
+    @Override
+    public void create(LogicalDatastoreType store, YangInstanceIdentifier path, NormalizedNode<?, ?> data) {
+        rwTx.put(store, path, data);
+    }
+
+    @Override
+    public void replace(LogicalDatastoreType store, YangInstanceIdentifier path, NormalizedNode<?, ?> data) {
+        create(store, path, data);
+    }
+
+    @Override
+    public FluentFuture<? extends @NonNull CommitInfo> commit() {
+        return rwTx.commit();
+    }
+
+    @Override
+    public DOMTransactionChain getTransactionChain() {
+        return transactionChain;
+    }
+
+    @Override
+    public InstanceIdentifierContext<?> getInstanceIdentifier() {
+        return instanceIdentifier;
+    }
+
+    @Override
+    public TransactionChainHandler getTransactionChainHandler() {
+        return transactionChainHandler;
+    }
+
+    @Override
+    public RestconfStrategy buildStrategy(final InstanceIdentifierContext<?> instanceIdentifierContext) {
+        return new MdsalRestconfStrategy(instanceIdentifierContext, this.transactionChainHandler);
+    }
+}
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
new file mode 100644 (file)
index 0000000..7231706
--- /dev/null
@@ -0,0 +1,156 @@
+/*
+ * 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.FutureCallback;
+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.common.context.InstanceIdentifierContext;
+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.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Strategy that allow to communicate with netconf devices using pure netconf operations.
+ */
+public class NetconfRestconfStrategy implements RestconfStrategy {
+    private static final Logger LOG = LoggerFactory.getLogger(NetconfRestconfStrategy.class);
+
+    private final NetconfDataTreeService netconfService;
+    private final InstanceIdentifierContext<?> instanceIdentifier;
+
+    private List<ListenableFuture<? extends DOMRpcResult>> resultsFutures;
+
+    public NetconfRestconfStrategy(final NetconfDataTreeService netconfService,
+                                   final InstanceIdentifierContext<?> instanceIdentifier) {
+        this.netconfService = requireNonNull(netconfService);
+        this.instanceIdentifier = requireNonNull(instanceIdentifier);
+    }
+
+    @Override
+    public void prepareReadWriteExecution() {
+        resultsFutures = netconfService.lock();
+    }
+
+    @Override
+    public void cancel() {
+        netconfService.discardChanges();
+        netconfService.unlock();
+    }
+
+    @Override
+    public ListenableFuture<Optional<NormalizedNode<?, ?>>> read(final LogicalDatastoreType store,
+                                                                 final YangInstanceIdentifier path) {
+        switch (store) {
+            case CONFIGURATION:
+                return netconfService.getConfig(path);
+            case OPERATIONAL:
+                return netconfService.get(path);
+            default:
+                LOG.info("Unknown datastore type: {}.", store);
+                throw new IllegalArgumentException(String.format(
+                        "%s, Cannot read data %s for %s datastore, unknown datastore type",
+                        netconfService.getDeviceId(), path, store));
+        }
+    }
+
+    @Override
+    public FluentFuture<Boolean> exists(final LogicalDatastoreType store, final YangInstanceIdentifier path) {
+        return remapException(read(store, path))
+                .transform(optionalNode -> optionalNode != null && optionalNode.isPresent(),
+                        MoreExecutors.directExecutor());
+    }
+
+    @Override
+    public void delete(final LogicalDatastoreType store, final YangInstanceIdentifier path) {
+        resultsFutures.add(netconfService.delete(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) {
+        resultsFutures.add(netconfService.create(store, path, data, Optional.empty()));
+    }
+
+    @Override
+    public void replace(final LogicalDatastoreType store, final YangInstanceIdentifier path,
+                        final NormalizedNode<?, ?> data) {
+        resultsFutures.add(netconfService.replace(store, path, data, Optional.empty()));
+    }
+
+    @Override
+    public FluentFuture<? extends @NonNull CommitInfo> commit() {
+        return FluentFuture.from(netconfService.commit(resultsFutures));
+    }
+
+    /**
+     * As we are not using any transactions here, always return null.
+     */
+    @Override
+    public DOMTransactionChain getTransactionChain() {
+        return null;
+    }
+
+    @Override
+    public InstanceIdentifierContext<?> getInstanceIdentifier() {
+        return instanceIdentifier;
+    }
+
+    /**
+     * As we are not using any transactions here, always return null.
+     */
+    @Override
+    public TransactionChainHandler getTransactionChainHandler() {
+        return null;
+    }
+
+    @Override
+    public RestconfStrategy buildStrategy(final InstanceIdentifierContext<?> instanceIdentifierContext) {
+        return new NetconfRestconfStrategy(this.netconfService, instanceIdentifierContext);
+    }
+
+    private static <T> FluentFuture<T> remapException(final ListenableFuture<T> input) {
+        final SettableFuture<T> ret = SettableFuture.create();
+        Futures.addCallback(input, new FutureCallback<T>() {
+
+            @Override
+            public void onSuccess(final T result) {
+                ret.set(result);
+            }
+
+            @Override
+            public void onFailure(final Throwable cause) {
+                ret.setException(cause instanceof ReadFailedException ? cause
+                        : new ReadFailedException("NETCONF operation failed", cause));
+            }
+        }, MoreExecutors.directExecutor());
+        return FluentFuture.from(ret);
+    }
+}
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
new file mode 100644 (file)
index 0000000..71732d1
--- /dev/null
@@ -0,0 +1,129 @@
+/*
+ * 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.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.DOMTransactionChain;
+import org.opendaylight.restconf.common.context.InstanceIdentifierContext;
+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;
+
+/**
+ * This interface allows interact with netconf operations in different ways.
+ *
+ * @see NetconfRestconfStrategy
+ * @see MdsalRestconfStrategy
+ */
+public interface RestconfStrategy {
+
+    /**
+     * Lock the entire datastore.
+     */
+    void prepareReadWriteExecution();
+
+    /**
+     * Rollback changes and unlock the datastore.
+     */
+    void cancel();
+
+    /**
+     * Read data from the datastore.
+     *
+     * @param store the logical data store which should be modified
+     * @param path the data object path
+     * @return a ListenableFuture containing the result of the read
+     */
+    ListenableFuture<Optional<NormalizedNode<?, ?>>> read(LogicalDatastoreType store, YangInstanceIdentifier path);
+
+    /**
+     * Check if data already exists in the datastore.
+     *
+     * @param store the logical data store which should be modified
+     * @param path the data object path
+     * @return a FluentFuture containing the result of the check
+     */
+    FluentFuture<Boolean> exists(LogicalDatastoreType store, YangInstanceIdentifier path);
+
+    /**
+     * Delete data from the datastore.
+     *
+     * @param store the logical data store which should be modified
+     * @param path the data object path
+     */
+    void delete(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
+     */
+    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
+     */
+    void create(LogicalDatastoreType store, YangInstanceIdentifier path, NormalizedNode<?, ?> data);
+
+    /**
+     * 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
+     */
+    void replace(LogicalDatastoreType store, YangInstanceIdentifier path, NormalizedNode<?, ?> data);
+
+    /**
+     * Confirm previous operations.
+     *
+     * @return a FluentFuture containing the result of the commit information
+     */
+    FluentFuture<? extends @NonNull CommitInfo> commit();
+
+    /**
+     * Get transaction chain for creating specific transaction for specific operation.
+     *
+     * @return transaction chain or null
+     */
+    @Nullable DOMTransactionChain getTransactionChain();
+
+    /**
+     * Get instance identifier of data.
+     *
+     * @return {@link InstanceIdentifierContext}
+     */
+    InstanceIdentifierContext<?> getInstanceIdentifier();
+
+    /**
+     * Get transaction chain handler for creating new transaction chain.
+     *
+     * @return {@link TransactionChainHandler} or null
+     */
+    @Nullable TransactionChainHandler getTransactionChainHandler();
+
+    /**
+     * Create a new and same type strategy for communication with netconf interface with
+     * a new {@link InstanceIdentifierContext}.
+     *
+     * @return {@link RestconfStrategy}
+     */
+    RestconfStrategy buildStrategy(InstanceIdentifierContext<?> instanceIdentifierContext);
+}
diff --git a/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/transactions/TransactionVarsWrapper.java b/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/transactions/TransactionVarsWrapper.java
deleted file mode 100644 (file)
index 706879f..0000000
+++ /dev/null
@@ -1,99 +0,0 @@
-/*
- * Copyright (c) 2016 Cisco Systems, Inc. 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 org.opendaylight.mdsal.common.api.LogicalDatastoreType;
-import org.opendaylight.mdsal.dom.api.DOMMountPoint;
-import org.opendaylight.mdsal.dom.api.DOMTransactionChain;
-import org.opendaylight.restconf.common.context.InstanceIdentifierContext;
-import org.opendaylight.restconf.nb.rfc8040.handlers.TransactionChainHandler;
-
-/**
- * This class represent delegation wrapper for transaction variables.
- *
- */
-public final class TransactionVarsWrapper {
-
-    private final InstanceIdentifierContext<?> instanceIdentifier;
-    private final DOMMountPoint mountPoint;
-    private LogicalDatastoreType configuration = null;
-    private final DOMTransactionChain transactionChain;
-    private final TransactionChainHandler transactionChainHandler;
-
-    /**
-     * Set base type of variables, which ones we need for transaction.
-     * {@link LogicalDatastoreType} is default set to null (to read all data
-     * from DS - config + state).
-     *
-     * @param instanceIdentifier
-     *             {@link InstanceIdentifierContext} of data for transaction
-     * @param mountPoint
-     *             mount point if is present
-     * @param transactionChainHandler
-     *             used to obtain the transaction chain for creating specific type of transaction
-     *             in specific operation
-     */
-    public TransactionVarsWrapper(final InstanceIdentifierContext<?> instanceIdentifier, final DOMMountPoint mountPoint,
-            final TransactionChainHandler transactionChainHandler) {
-        this.instanceIdentifier = instanceIdentifier;
-        this.mountPoint = mountPoint;
-        this.transactionChainHandler = transactionChainHandler;
-        transactionChain = transactionChainHandler.get();
-    }
-
-    /**
-     * Get instance identifier of data.
-     *
-     * @return {@link InstanceIdentifierContext}
-     */
-    public InstanceIdentifierContext<?> getInstanceIdentifier() {
-        return this.instanceIdentifier;
-    }
-
-    /**
-     * Get mount point.
-     *
-     * @return {@link DOMMountPoint}
-     */
-    public DOMMountPoint getMountPoint() {
-        return this.mountPoint;
-    }
-
-    /**
-     * Set {@link LogicalDatastoreType} of data for transaction.
-     *
-     * @param datastoreType
-     *             {@link LogicalDatastoreType}
-     */
-    public void setLogicalDatastoreType(final LogicalDatastoreType datastoreType) {
-        this.configuration = datastoreType;
-    }
-
-    /**
-     * Get type of data.
-     *
-     * @return {@link LogicalDatastoreType}
-     */
-    public LogicalDatastoreType getLogicalDatastoreType() {
-        return this.configuration;
-    }
-
-    /**
-     * Get transaction chain for creating specific transaction for specific
-     * operation.
-     *
-     * @return transaction chain
-     */
-    public DOMTransactionChain getTransactionChain() {
-        return this.transactionChain;
-    }
-
-    public TransactionChainHandler getTransactionChainHandler() {
-        return transactionChainHandler;
-    }
-}
index 201166185b5b440849d68ed3bf9ac2aa340aec59..9544a07f3a592c2cfa69384d002856f199d702d2 100644 (file)
@@ -12,14 +12,11 @@ import javax.ws.rs.core.Response;
 import javax.ws.rs.core.Response.Status;
 import org.opendaylight.mdsal.common.api.CommitInfo;
 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
-import org.opendaylight.mdsal.dom.api.DOMDataTreeReadWriteTransaction;
-import org.opendaylight.mdsal.dom.api.DOMTransactionChain;
-import org.opendaylight.restconf.nb.rfc8040.rests.transactions.TransactionVarsWrapper;
+import org.opendaylight.restconf.nb.rfc8040.rests.transactions.RestconfStrategy;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
 
 /**
  * Util class for delete specific data in config DS.
- *
  */
 public final class DeleteDataTransactionUtil {
 
@@ -30,36 +27,20 @@ public final class DeleteDataTransactionUtil {
     /**
      * Delete data from DS via transaction.
      *
-     * @param transactionNode
-     *             Wrapper for data of transaction
+     * @param strategy object that perform the actual DS operations
      * @return {@link Response}
      */
-    public static Response deleteData(final TransactionVarsWrapper transactionNode) {
-        final DOMTransactionChain transactionChain = transactionNode.getTransactionChainHandler().get();
-        final FluentFuture<? extends CommitInfo> future = submitData(transactionChain,
-                transactionNode.getInstanceIdentifier().getInstanceIdentifier());
+    public static Response deleteData(final RestconfStrategy strategy) {
+        strategy.prepareReadWriteExecution();
+        final YangInstanceIdentifier path = strategy.getInstanceIdentifier().getInstanceIdentifier();
+        TransactionUtil.checkItemExists(strategy, LogicalDatastoreType.CONFIGURATION, path,
+                RestconfDataServiceConstant.DeleteData.DELETE_TX_TYPE);
+        strategy.delete(LogicalDatastoreType.CONFIGURATION, path);
+        final FluentFuture<? extends CommitInfo> future = strategy.commit();
         final ResponseFactory response = new ResponseFactory(Status.NO_CONTENT);
-        //This method will close transactionChain
+        //This method will close transactionChain if any
         FutureCallbackTx.addCallback(future, RestconfDataServiceConstant.DeleteData.DELETE_TX_TYPE, response,
-                transactionChain);
+                strategy.getTransactionChain());
         return response.build();
     }
-
-    /**
-     * Delete data via transaction. Return error if data to delete does not exist.
-     *
-     * @param transactionChain
-     *             transaction chain
-     * @param path
-     *             path of data to delete
-     * @return {@link FluentFuture}
-     */
-    private static FluentFuture<? extends CommitInfo> submitData(
-            final DOMTransactionChain transactionChain, final YangInstanceIdentifier path) {
-        final DOMDataTreeReadWriteTransaction readWriteTx = transactionChain.newReadWriteTransaction();
-        TransactionUtil.checkItemExists(transactionChain, readWriteTx, LogicalDatastoreType.CONFIGURATION, path,
-                RestconfDataServiceConstant.DeleteData.DELETE_TX_TYPE);
-        readWriteTx.delete(LogicalDatastoreType.CONFIGURATION, path);
-        return readWriteTx.commit();
-    }
 }
index ebfb9964b6357e907b09c35222ca8fc930a7315f..e047dab994d211c7d3126a4211f031e1196a5782 100644 (file)
@@ -15,9 +15,6 @@ import java.util.List;
 import javax.ws.rs.core.Response.Status;
 import org.opendaylight.mdsal.common.api.CommitInfo;
 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
-import org.opendaylight.mdsal.dom.api.DOMDataTreeReadOperations;
-import org.opendaylight.mdsal.dom.api.DOMDataTreeReadWriteTransaction;
-import org.opendaylight.mdsal.dom.api.DOMDataTreeWriteTransaction;
 import org.opendaylight.mdsal.dom.api.DOMTransactionChain;
 import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
 import org.opendaylight.restconf.common.errors.RestconfError;
@@ -27,7 +24,7 @@ import org.opendaylight.restconf.common.patch.PatchContext;
 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.TransactionVarsWrapper;
+import org.opendaylight.restconf.nb.rfc8040.rests.transactions.RestconfStrategy;
 import org.opendaylight.restconf.nb.rfc8040.rests.utils.RestconfDataServiceConstant.PatchData;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
 import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
@@ -46,19 +43,19 @@ public final class PatchDataTransactionUtil {
     }
 
     /**
-     * Process edit operations of one {@link PatchContext}. Close {@link DOMTransactionChain} inside of object
-     * {@link TransactionVarsWrapper} provided as a parameter.
-     * @param context Patch context to be processed
-     * @param transactionNode Wrapper for transaction
+     * Process edit operations of one {@link PatchContext}. Close {@link DOMTransactionChain} if any inside of object
+     * {@link RestconfStrategy} provided as a parameter.
+     *
+     * @param context       Patch context to be processed
+     * @param strategy      object that perform the actual DS operations
      * @param schemaContext Global schema context
      * @return {@link PatchStatusContext}
      */
-    public static PatchStatusContext patchData(final PatchContext context, final TransactionVarsWrapper transactionNode,
+    public static PatchStatusContext patchData(final PatchContext context, final RestconfStrategy strategy,
                                                final EffectiveModelContext schemaContext) {
         final List<PatchStatusEntity> editCollection = new ArrayList<>();
         boolean noError = true;
-        final DOMTransactionChain transactionChain = transactionNode.getTransactionChain();
-        final DOMDataTreeReadWriteTransaction tx = transactionChain.newReadWriteTransaction();
+        strategy.prepareReadWriteExecution();
 
         for (final PatchEntity patchEntity : context.getData()) {
             if (noError) {
@@ -66,7 +63,7 @@ public final class PatchDataTransactionUtil {
                     case CREATE:
                         try {
                             createDataWithinTransaction(LogicalDatastoreType.CONFIGURATION,
-                                    patchEntity.getTargetNode(), patchEntity.getNode(), tx, schemaContext);
+                                    patchEntity.getTargetNode(), patchEntity.getNode(), strategy, schemaContext);
                             editCollection.add(new PatchStatusEntity(patchEntity.getEditId(), true, null));
                         } catch (final RestconfDocumentedException e) {
                             editCollection.add(new PatchStatusEntity(patchEntity.getEditId(),
@@ -77,7 +74,7 @@ public final class PatchDataTransactionUtil {
                     case DELETE:
                         try {
                             deleteDataWithinTransaction(LogicalDatastoreType.CONFIGURATION, patchEntity.getTargetNode(),
-                                    tx);
+                                    strategy);
                             editCollection.add(new PatchStatusEntity(patchEntity.getEditId(), true, null));
                         } catch (final RestconfDocumentedException e) {
                             editCollection.add(new PatchStatusEntity(patchEntity.getEditId(),
@@ -88,7 +85,7 @@ public final class PatchDataTransactionUtil {
                     case MERGE:
                         try {
                             mergeDataWithinTransaction(LogicalDatastoreType.CONFIGURATION,
-                                    patchEntity.getTargetNode(), patchEntity.getNode(), tx, schemaContext);
+                                    patchEntity.getTargetNode(), patchEntity.getNode(), strategy, schemaContext);
                             editCollection.add(new PatchStatusEntity(patchEntity.getEditId(), true, null));
                         } catch (final RestconfDocumentedException e) {
                             editCollection.add(new PatchStatusEntity(patchEntity.getEditId(),
@@ -99,7 +96,7 @@ public final class PatchDataTransactionUtil {
                     case REPLACE:
                         try {
                             replaceDataWithinTransaction(LogicalDatastoreType.CONFIGURATION,
-                                    patchEntity.getTargetNode(), patchEntity.getNode(), schemaContext, tx);
+                                    patchEntity.getTargetNode(), patchEntity.getNode(), schemaContext, strategy);
                             editCollection.add(new PatchStatusEntity(patchEntity.getEditId(), true, null));
                         } catch (final RestconfDocumentedException e) {
                             editCollection.add(new PatchStatusEntity(patchEntity.getEditId(),
@@ -110,7 +107,7 @@ public final class PatchDataTransactionUtil {
                     case REMOVE:
                         try {
                             removeDataWithinTransaction(LogicalDatastoreType.CONFIGURATION, patchEntity.getTargetNode(),
-                                    tx);
+                                    strategy);
                             editCollection.add(new PatchStatusEntity(patchEntity.getEditId(), true, null));
                         } catch (final RestconfDocumentedException e) {
                             editCollection.add(new PatchStatusEntity(patchEntity.getEditId(),
@@ -133,21 +130,21 @@ public final class PatchDataTransactionUtil {
         // if no errors then submit transaction, otherwise cancel
         if (noError) {
             final ResponseFactory response = new ResponseFactory(Status.OK);
-            final FluentFuture<? extends CommitInfo> future = tx.commit();
+            final FluentFuture<? extends CommitInfo> future = strategy.commit();
 
             try {
-                //This method will close transactionChain
-                FutureCallbackTx.addCallback(future, PatchData.PATCH_TX_TYPE, response, transactionChain);
+                //This method will close transactionChain if any
+                FutureCallbackTx.addCallback(future, PatchData.PATCH_TX_TYPE, response, strategy.getTransactionChain());
             } 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,
                         Lists.newArrayList(e.getErrors()));
             }
 
-            return new PatchStatusContext(context.getPatchId(), ImmutableList.copyOf(editCollection), true, null);
+            return new PatchStatusContext(context.getPatchId(), ImmutableList.copyOf(editCollection),
+                    true, null);
         } else {
-            tx.cancel();
-            transactionChain.close();
+            strategy.cancel();
             return new PatchStatusContext(context.getPatchId(), ImmutableList.copyOf(editCollection),
                     false, null);
         }
@@ -155,130 +152,146 @@ public final class PatchDataTransactionUtil {
 
     /**
      * Create data within one transaction, return error if already exists.
-     * @param dataStore Datastore to write data to
-     * @param path Path for data to be created
-     * @param payload Data to be created
-     * @param rwTransaction Transaction
+     *
+     * @param dataStore     Datastore to write data to
+     * @param path          Path for data to be created
+     * @param payload       Data to be created
+     * @param strategy      Object that perform the actual DS operations
      * @param schemaContext Global schema context
      */
     private static void createDataWithinTransaction(final LogicalDatastoreType dataStore,
                                                     final YangInstanceIdentifier path,
                                                     final NormalizedNode<?, ?> payload,
-                                                    final DOMDataTreeReadWriteTransaction rwTransaction,
+                                                    final RestconfStrategy strategy,
                                                     final EffectiveModelContext schemaContext) {
         LOG.trace("POST {} within Restconf Patch: {} with payload {}", dataStore.name(), path, payload);
-        createData(payload, schemaContext, path, rwTransaction, dataStore, true);
+        createData(payload, schemaContext, path, strategy, dataStore, true);
     }
 
     /**
      * Check if data exists and remove it within one transaction.
-     * @param dataStore Datastore to delete data from
-     * @param path Path for data to be deleted
-     * @param readWriteTransaction Transaction
+     *
+     * @param dataStore            Datastore to delete data from
+     * @param path                 Path for data to be deleted
+     * @param strategy             Object that perform the actual DS operations
      */
     private static void deleteDataWithinTransaction(final LogicalDatastoreType dataStore,
                                                     final YangInstanceIdentifier path,
-                                                    final DOMDataTreeReadWriteTransaction readWriteTransaction) {
+                                                    final RestconfStrategy strategy) {
         LOG.trace("Delete {} within Restconf Patch: {}", dataStore.name(), path);
-        checkItemExistsWithinTransaction(readWriteTransaction, dataStore, path);
-        readWriteTransaction.delete(dataStore, path);
+        checkItemExistsWithinTransaction(strategy, dataStore, path);
+        strategy.delete(dataStore, path);
     }
 
     /**
      * Merge data within one transaction.
-     * @param dataStore Datastore to merge data to
-     * @param path Path for data to be merged
-     * @param payload Data to be merged
-     * @param writeTransaction Transaction
+     *
+     * @param dataStore     Datastore to merge data to
+     * @param path          Path for data to be merged
+     * @param payload       Data to be merged
+     * @param strategy      Object that perform the actual DS operations
      * @param schemaContext Global schema context
      */
     private static void mergeDataWithinTransaction(final LogicalDatastoreType dataStore,
                                                    final YangInstanceIdentifier path,
                                                    final NormalizedNode<?, ?> payload,
-                                                   final DOMDataTreeWriteTransaction writeTransaction,
+                                                   final RestconfStrategy strategy,
                                                    final EffectiveModelContext schemaContext) {
         LOG.trace("Merge {} within Restconf Patch: {} with payload {}", dataStore.name(), path, payload);
-        TransactionUtil.ensureParentsByMerge(path, schemaContext, writeTransaction);
-        writeTransaction.merge(dataStore, path, payload);
+        TransactionUtil.ensureParentsByMerge(path, schemaContext, strategy);
+        strategy.merge(dataStore, path, payload);
     }
 
     /**
      * Do NOT check if data exists and remove it within one transaction.
-     * @param dataStore Datastore to delete data from
-     * @param path Path for data to be deleted
-     * @param writeTransaction Transaction
+     *
+     * @param dataStore        Datastore to delete data from
+     * @param path             Path for data to be deleted
+     * @param strategy         Object that perform the actual DS operations
      */
     private static void removeDataWithinTransaction(final LogicalDatastoreType dataStore,
                                                     final YangInstanceIdentifier path,
-                                                    final DOMDataTreeWriteTransaction writeTransaction) {
+                                                    final RestconfStrategy strategy) {
         LOG.trace("Remove {} within Restconf Patch: {}", dataStore.name(), path);
-        writeTransaction.delete(dataStore, path);
+        strategy.delete(dataStore, path);
     }
 
     /**
      * Create data within one transaction, replace if already exists.
-     * @param dataStore Datastore to write data to
-     * @param path Path for data to be created
-     * @param payload Data to be created
-     * @param path Path for data to be created
-     * @param rwTransaction Transaction
+     *
+     * @param dataStore     Datastore to write data to
+     * @param path          Path for data to be created
+     * @param payload       Data to be created
+     * @param schemaContext Global schema context
+     * @param strategy      Object that perform the actual DS operations
      */
     private static void replaceDataWithinTransaction(final LogicalDatastoreType dataStore,
                                                      final YangInstanceIdentifier path,
                                                      final NormalizedNode<?, ?> payload,
                                                      final EffectiveModelContext schemaContext,
-                                                     final DOMDataTreeReadWriteTransaction rwTransaction) {
+                                                     final RestconfStrategy strategy) {
         LOG.trace("PUT {} within Restconf Patch: {} with payload {}", dataStore.name(), path, payload);
-        createData(payload, schemaContext, path, rwTransaction, dataStore, false);
+        createData(payload, schemaContext, path, strategy, dataStore, 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 payload Data to be created
+     *
+     * @param payload       Data to be created
      * @param schemaContext Global schema context
-     * @param path Path for data to be created
-     * @param rwTransaction Transaction
-     * @param dataStore Datastore to write data to
+     * @param path          Path for data to be created
+     * @param strategy      Object that perform the actual DS operations
+     * @param dataStore     Datastore to write data to
      * @param errorIfExists Enable checking for existence of data (throws error if already exists)
      */
     private static void createData(final NormalizedNode<?, ?> payload, final EffectiveModelContext schemaContext,
                                    final YangInstanceIdentifier path,
-                                   final DOMDataTreeReadWriteTransaction rwTransaction,
+                                   final RestconfStrategy strategy,
                                    final LogicalDatastoreType dataStore, final boolean errorIfExists) {
         if (payload instanceof MapNode) {
             final NormalizedNode<?, ?> emptySubtree = ImmutableNodes.fromInstanceId(schemaContext, path);
-            rwTransaction.merge(dataStore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
-            TransactionUtil.ensureParentsByMerge(path, schemaContext, rwTransaction);
+            strategy.merge(dataStore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
+            TransactionUtil.ensureParentsByMerge(path, schemaContext, strategy);
             for (final MapEntryNode child : ((MapNode) payload).getValue()) {
                 final YangInstanceIdentifier childPath = path.node(child.getIdentifier());
 
                 if (errorIfExists) {
-                    checkItemDoesNotExistsWithinTransaction(rwTransaction, dataStore, childPath);
+                    checkItemDoesNotExistsWithinTransaction(strategy, dataStore, childPath);
                 }
 
-                rwTransaction.put(dataStore, childPath, child);
+                if (errorIfExists) {
+                    strategy.create(dataStore, childPath, child);
+                } else {
+                    strategy.replace(dataStore, childPath, child);
+                }
             }
         } else {
             if (errorIfExists) {
-                checkItemDoesNotExistsWithinTransaction(rwTransaction, dataStore, path);
+                checkItemDoesNotExistsWithinTransaction(strategy, dataStore, path);
             }
 
-            TransactionUtil.ensureParentsByMerge(path, schemaContext, rwTransaction);
-            rwTransaction.put(dataStore, path, payload);
+            TransactionUtil.ensureParentsByMerge(path, schemaContext, strategy);
+            if (errorIfExists) {
+                strategy.create(dataStore, path, payload);
+            } else {
+                strategy.replace(dataStore, path, payload);
+            }
         }
     }
 
     /**
      * Check if items already exists at specified {@code path}. Throws {@link RestconfDocumentedException} if
      * data does NOT already exists.
-     * @param rwTransaction Transaction
-     * @param store Datastore
-     * @param path Path to be checked
+     *
+     * @param strategy      Object that perform the actual DS operations
+     * @param store         Datastore
+     * @param path          Path to be checked
      */
-    public static void checkItemExistsWithinTransaction(final DOMDataTreeReadOperations rwTransaction,
-                                                final LogicalDatastoreType store, final YangInstanceIdentifier path) {
-        final FluentFuture<Boolean> future = rwTransaction.exists(store, path);
+    public static void checkItemExistsWithinTransaction(final RestconfStrategy strategy,
+                                                        final LogicalDatastoreType store,
+                                                        final YangInstanceIdentifier path) {
+        final FluentFuture<Boolean> future = strategy.exists(store, path);
         final FutureDataFactory<Boolean> response = new FutureDataFactory<>();
 
         FutureCallbackTx.addCallback(future, PatchData.PATCH_TX_TYPE, response);
@@ -286,20 +299,22 @@ public final class PatchDataTransactionUtil {
         if (!response.result) {
             LOG.trace("Operation via Restconf was not executed because data at {} does not exist", path);
             throw new RestconfDocumentedException("Data does not exist", ErrorType.PROTOCOL, ErrorTag.DATA_MISSING,
-                path);
+                    path);
         }
     }
 
     /**
      * Check if items do NOT already exists at specified {@code path}. Throws {@link RestconfDocumentedException} if
      * data already exists.
-     * @param rwTransaction Transaction
-     * @param store Datastore
-     * @param path Path to be checked
+     *
+     * @param strategy      Object that perform the actual DS operations
+     * @param store         Datastore
+     * @param path          Path to be checked
      */
-    public static void checkItemDoesNotExistsWithinTransaction(final DOMDataTreeReadOperations rwTransaction,
-                                               final LogicalDatastoreType store, final YangInstanceIdentifier path) {
-        final FluentFuture<Boolean> future = rwTransaction.exists(store, path);
+    public static void checkItemDoesNotExistsWithinTransaction(final RestconfStrategy strategy,
+                                                               final LogicalDatastoreType store,
+                                                               final YangInstanceIdentifier path) {
+        final FluentFuture<Boolean> future = strategy.exists(store, path);
         final FutureDataFactory<Boolean> response = new FutureDataFactory<>();
 
         FutureCallbackTx.addCallback(future, PatchData.PATCH_TX_TYPE, response);
@@ -307,7 +322,7 @@ public final class PatchDataTransactionUtil {
         if (response.result) {
             LOG.trace("Operation via Restconf was not executed because data at {} already exists", path);
             throw new RestconfDocumentedException("Data already exists", ErrorType.PROTOCOL, ErrorTag.DATA_EXISTS,
-                path);
+                    path);
         }
     }
 }
index 95e36e59522faa4b706b3f42b22f83d8a2784041..371b155f1b3475e10585210c7a7e2130d8f80863 100644 (file)
@@ -13,12 +13,10 @@ import javax.ws.rs.core.Response;
 import javax.ws.rs.core.Response.Status;
 import org.opendaylight.mdsal.common.api.CommitInfo;
 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
-import org.opendaylight.mdsal.dom.api.DOMDataTreeReadWriteTransaction;
-import org.opendaylight.mdsal.dom.api.DOMDataTreeWriteTransaction;
 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.TransactionVarsWrapper;
+import org.opendaylight.restconf.nb.rfc8040.rests.transactions.RestconfStrategy;
 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;
@@ -36,62 +34,54 @@ public final class PlainPatchDataTransactionUtil {
     }
 
     /**
-     * Prepare variables for put data to DS. Close {@link DOMTransactionChain} inside of object
-     * {@link TransactionVarsWrapper} provided as a parameter.
+     * Prepare variables for put data to DS. Close {@link DOMTransactionChain} if any inside of object
+     * {@link RestconfStrategy} provided as a parameter if any.
      *
-     * @param payload
-     *             data to put
-     * @param schemaContext
-     *             reference to {@link EffectiveModelContext}
-     * @param transactionNode
-     *             wrapper of variables for transaction
+     * @param payload       data to put
+     * @param schemaContext reference to {@link EffectiveModelContext}
+     * @param strategy      object that perform the actual DS operations
      * @return {@link Response}
      */
     public static Response patchData(final NormalizedNodeContext payload,
-                                     final TransactionVarsWrapper transactionNode,
+                                     final RestconfStrategy strategy,
                                      final EffectiveModelContext schemaContext) {
 
-        final DOMTransactionChain transactionChain = transactionNode.getTransactionChain();
-        final DOMDataTreeReadWriteTransaction tx = transactionChain.newReadWriteTransaction();
-
+        strategy.prepareReadWriteExecution();
         YangInstanceIdentifier path = payload.getInstanceIdentifierContext().getInstanceIdentifier();
         NormalizedNode<?, ?> data = payload.getData();
 
         try {
-            mergeDataWithinTransaction(LogicalDatastoreType.CONFIGURATION, path, data, tx, schemaContext);
+            mergeDataWithinTransaction(LogicalDatastoreType.CONFIGURATION, path, data, strategy, schemaContext);
         } catch (final RestconfDocumentedException e) {
-            tx.cancel();
-            transactionChain.close();
-
+            strategy.cancel();
             throw new IllegalArgumentException(e);
         }
 
-        final FluentFuture<? extends CommitInfo> future = tx.commit();
+        final FluentFuture<? extends CommitInfo> future = strategy.commit();
         final ResponseFactory response = new ResponseFactory(Status.OK);
 
-        FutureCallbackTx.addCallback(future,
-                RestconfDataServiceConstant.PatchData.PATCH_TX_TYPE,
-                response,
-                transactionChain); // closes transactionChain, may throw
+        FutureCallbackTx.addCallback(future, RestconfDataServiceConstant.PatchData.PATCH_TX_TYPE, response,
+                strategy.getTransactionChain()); // closes transactionChain if any, may throw
 
         return response.build();
     }
 
     /**
      * Merge data within one transaction.
-     * @param dataStore Datastore to merge data to
-     * @param path Path for data to be merged
-     * @param payload Data to be merged
-     * @param writeTransaction Transaction
+     *
+     * @param dataStore     Datastore to merge data to
+     * @param path          Path for data to be merged
+     * @param payload       Data to be merged
+     * @param strategy      Object that perform the actual DS operations
      * @param schemaContext global schema context
      */
     private static void mergeDataWithinTransaction(final LogicalDatastoreType dataStore,
                                                    final YangInstanceIdentifier path,
                                                    final NormalizedNode<?, ?> payload,
-                                                   final DOMDataTreeWriteTransaction writeTransaction,
+                                                   final RestconfStrategy strategy,
                                                    final EffectiveModelContext schemaContext) {
         LOG.trace("Merge {} within Restconf Patch: {} with payload {}", dataStore.name(), path, payload);
-        TransactionUtil.ensureParentsByMerge(path, schemaContext, writeTransaction);
-        writeTransaction.merge(dataStore, path, payload);
+        TransactionUtil.ensureParentsByMerge(path, schemaContext, strategy);
+        strategy.merge(dataStore, path, payload);
     }
 }
index 1e79b9a36f16af144d8c0f45a5cab653d5a307d7..295640d01a6f78a515d32feaadad1ed1a91f67fa 100644 (file)
@@ -16,13 +16,12 @@ import javax.ws.rs.core.Response.Status;
 import javax.ws.rs.core.UriInfo;
 import org.opendaylight.mdsal.common.api.CommitInfo;
 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
-import org.opendaylight.mdsal.dom.api.DOMDataTreeReadWriteTransaction;
 import org.opendaylight.mdsal.dom.api.DOMTransactionChain;
 import org.opendaylight.restconf.common.context.InstanceIdentifierContext;
 import org.opendaylight.restconf.common.context.NormalizedNodeContext;
 import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
 import org.opendaylight.restconf.common.errors.RestconfError;
-import org.opendaylight.restconf.nb.rfc8040.rests.transactions.TransactionVarsWrapper;
+import org.opendaylight.restconf.nb.rfc8040.rests.transactions.RestconfStrategy;
 import org.opendaylight.restconf.nb.rfc8040.utils.parser.ParserIdentifier;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
 import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode;
@@ -47,62 +46,53 @@ public final class PostDataTransactionUtil {
     }
 
     /**
-     * Check mount point and prepare variables for post data. Close {@link DOMTransactionChain} inside of object
-     * {@link TransactionVarsWrapper} provided as a parameter.
+     * Check mount point and prepare variables for post data. Close {@link DOMTransactionChain} if any inside of object
+     * {@link RestconfStrategy} provided as a parameter.
      *
-     * @param uriInfo
-     *
-     * @param payload
-     *             data
-     * @param transactionNode
-     *             wrapper for transaction data
-     * @param schemaContext
-     *             reference to current {@link EffectiveModelContext}
-     * @param point
-     *             point
-     * @param insert
-     *             insert
+     * @param uriInfo       uri info
+     * @param payload       data
+     * @param strategy      Object that perform the actual DS operations
+     * @param schemaContext reference to actual {@link EffectiveModelContext}
+     * @param point         point
+     * @param insert        insert
      * @return {@link Response}
      */
     public static Response postData(final UriInfo uriInfo, final NormalizedNodeContext payload,
-            final TransactionVarsWrapper transactionNode, final EffectiveModelContext schemaContext,
-            final String insert, final String point) {
+                                    final RestconfStrategy strategy,
+                                    final EffectiveModelContext schemaContext, final String insert,
+                                    final String point) {
         final FluentFuture<? extends CommitInfo> future = submitData(
                 payload.getInstanceIdentifierContext().getInstanceIdentifier(), payload.getData(),
-                transactionNode, schemaContext, insert, point);
-        final URI location = resolveLocation(uriInfo, transactionNode, schemaContext, payload.getData());
+                strategy, schemaContext, insert, point);
+        final URI location = resolveLocation(uriInfo, strategy.getInstanceIdentifier(),
+                schemaContext, payload.getData());
         final ResponseFactory dataFactory = new ResponseFactory(Status.CREATED).location(location);
-        //This method will close transactionChain
+        //This method will close transactionChain if any
         FutureCallbackTx.addCallback(future, RestconfDataServiceConstant.PostData.POST_TX_TYPE, dataFactory,
-                transactionNode.getTransactionChain());
+                strategy.getTransactionChain());
         return dataFactory.build();
     }
 
     /**
      * Post data by type.
      *
-     * @param path
-     *             path
-     * @param data
-     *             data
-     * @param transactionNode
-     *             wrapper for data to transaction
-     * @param schemaContext
-     *             schema context of data
-     * @param point
-     *             query parameter
-     * @param insert
-     *             query parameter
+     * @param path          path
+     * @param data          data
+     * @param strategy      object that perform the actual DS operations
+     * @param schemaContext schema context of data
+     * @param point         query parameter
+     * @param insert        query parameter
      * @return {@link FluentFuture}
      */
     private static FluentFuture<? extends CommitInfo> submitData(final YangInstanceIdentifier path,
-            final NormalizedNode<?, ?> data, final TransactionVarsWrapper transactionNode,
-            final EffectiveModelContext schemaContext, final String insert, final String point) {
-        final DOMTransactionChain transactionChain = transactionNode.getTransactionChain();
-        final DOMDataTreeReadWriteTransaction newReadWriteTransaction = transactionChain.newReadWriteTransaction();
+                                                                 final NormalizedNode<?, ?> data,
+                                                                 final RestconfStrategy strategy,
+                                                                 final EffectiveModelContext schemaContext,
+                                                                 final String insert, final String point) {
+        strategy.prepareReadWriteExecution();
         if (insert == null) {
-            makePost(path, data, schemaContext, transactionChain, newReadWriteTransaction);
-            return newReadWriteTransaction.commit();
+            makePost(path, data, schemaContext, strategy);
+            return strategy.commit();
         }
 
         final DataSchemaNode schemaNode = PutDataTransactionUtil.checkListAndOrderedType(schemaContext, path);
@@ -110,92 +100,88 @@ public final class PostDataTransactionUtil {
             case "first":
                 if (schemaNode instanceof ListSchemaNode) {
                     final NormalizedNode<?, ?> readData = PutDataTransactionUtil.readList(path.getParent(),
-                        schemaContext, transactionNode.getTransactionChainHandler(), schemaNode);
+                            schemaContext, strategy, schemaNode);
                     final OrderedMapNode readList = (OrderedMapNode) readData;
                     if (readList == null || readList.getValue().isEmpty()) {
-                        makePost(path, data, schemaContext, transactionChain, newReadWriteTransaction);
-                        return newReadWriteTransaction.commit();
+                        makePost(path, data, schemaContext, strategy);
+                        return strategy.commit();
                     }
 
-                    newReadWriteTransaction.delete(LogicalDatastoreType.CONFIGURATION, path.getParent().getParent());
-                    simplePost(newReadWriteTransaction, LogicalDatastoreType.CONFIGURATION, path, data, schemaContext,
-                        transactionChain);
-                    makePost(path, readData, schemaContext, transactionChain, newReadWriteTransaction);
-                    return newReadWriteTransaction.commit();
+                    strategy.delete(LogicalDatastoreType.CONFIGURATION, path.getParent().getParent());
+                    simplePost(LogicalDatastoreType.CONFIGURATION, path, data, schemaContext, strategy);
+                    makePost(path, readData, schemaContext, strategy);
+                    return strategy.commit();
                 } else {
                     final NormalizedNode<?, ?> readData = PutDataTransactionUtil.readList(path.getParent(),
-                        schemaContext, transactionNode.getTransactionChainHandler(), schemaNode);
+                            schemaContext, strategy, schemaNode);
 
                     final OrderedLeafSetNode<?> readLeafList = (OrderedLeafSetNode<?>) readData;
                     if (readLeafList == null || readLeafList.getValue().isEmpty()) {
-                        makePost(path, data, schemaContext, transactionChain,
-                            newReadWriteTransaction);
-                        return newReadWriteTransaction.commit();
+                        makePost(path, data, schemaContext, strategy);
+                        return strategy.commit();
                     }
 
-                    newReadWriteTransaction.delete(LogicalDatastoreType.CONFIGURATION, path.getParent().getParent());
-                    simplePost(newReadWriteTransaction, LogicalDatastoreType.CONFIGURATION, path, data, schemaContext,
-                        transactionChain);
-                    makePost(path, readData, schemaContext, transactionChain, newReadWriteTransaction);
-                    return newReadWriteTransaction.commit();
+                    strategy.delete(LogicalDatastoreType.CONFIGURATION, path.getParent().getParent());
+                    simplePost(LogicalDatastoreType.CONFIGURATION, path, data, schemaContext, strategy);
+                    makePost(path, readData, schemaContext, strategy);
+                    return strategy.commit();
                 }
             case "last":
-                makePost(path, data, schemaContext, transactionChain, newReadWriteTransaction);
-                return newReadWriteTransaction.commit();
+                makePost(path, data, schemaContext, strategy);
+                return strategy.commit();
             case "before":
                 if (schemaNode instanceof ListSchemaNode) {
                     final NormalizedNode<?, ?> readData = PutDataTransactionUtil.readList(path.getParent(),
-                        schemaContext, transactionNode.getTransactionChainHandler(), schemaNode);
+                            schemaContext, strategy, schemaNode);
                     final OrderedMapNode readList = (OrderedMapNode) readData;
                     if (readList == null || readList.getValue().isEmpty()) {
-                        makePost(path, data, schemaContext, transactionChain, newReadWriteTransaction);
-                        return newReadWriteTransaction.commit();
+                        makePost(path, data, schemaContext, strategy);
+                        return strategy.commit();
                     }
 
-                    insertWithPointListPost(newReadWriteTransaction, LogicalDatastoreType.CONFIGURATION, path,
-                        data, schemaContext, point, readList, true, transactionChain);
-                    return newReadWriteTransaction.commit();
+                    insertWithPointListPost(LogicalDatastoreType.CONFIGURATION, path,
+                            data, schemaContext, point, readList, true, strategy);
+                    return strategy.commit();
                 } else {
                     final NormalizedNode<?, ?> readData = PutDataTransactionUtil.readList(path.getParent(),
-                        schemaContext, transactionNode.getTransactionChainHandler(), schemaNode);
+                            schemaContext, strategy, schemaNode);
 
                     final OrderedLeafSetNode<?> readLeafList = (OrderedLeafSetNode<?>) readData;
                     if (readLeafList == null || readLeafList.getValue().isEmpty()) {
-                        makePost(path, data, schemaContext, transactionChain, newReadWriteTransaction);
-                        return newReadWriteTransaction.commit();
+                        makePost(path, data, schemaContext, strategy);
+                        return strategy.commit();
                     }
 
-                    insertWithPointLeafListPost(newReadWriteTransaction, LogicalDatastoreType.CONFIGURATION,
-                        path, data, schemaContext, point, readLeafList, true, transactionChain);
-                    return newReadWriteTransaction.commit();
+                    insertWithPointLeafListPost(LogicalDatastoreType.CONFIGURATION,
+                            path, data, schemaContext, point, readLeafList, true, strategy);
+                    return strategy.commit();
                 }
             case "after":
                 if (schemaNode instanceof ListSchemaNode) {
                     final NormalizedNode<?, ?> readData = PutDataTransactionUtil.readList(path.getParent(),
-                        schemaContext, transactionNode.getTransactionChainHandler(), schemaNode);
+                            schemaContext, strategy, schemaNode);
                     final OrderedMapNode readList = (OrderedMapNode) readData;
                     if (readList == null || readList.getValue().isEmpty()) {
-                        makePost(path, data, schemaContext, transactionChain, newReadWriteTransaction);
-                        return newReadWriteTransaction.commit();
+                        makePost(path, data, schemaContext, strategy);
+                        return strategy.commit();
                     }
 
-                    insertWithPointListPost(newReadWriteTransaction, LogicalDatastoreType.CONFIGURATION, path,
-                        data, schemaContext, point, readList, false,
-                        transactionChain);
-                    return newReadWriteTransaction.commit();
+                    insertWithPointListPost(LogicalDatastoreType.CONFIGURATION, path,
+                            data, schemaContext, point, readList, false, strategy);
+                    return strategy.commit();
                 } else {
                     final NormalizedNode<?, ?> readData = PutDataTransactionUtil.readList(path.getParent(),
-                        schemaContext, transactionNode.getTransactionChainHandler(), schemaNode);
+                            schemaContext, strategy, schemaNode);
 
                     final OrderedLeafSetNode<?> readLeafList = (OrderedLeafSetNode<?>) readData;
                     if (readLeafList == null || readLeafList.getValue().isEmpty()) {
-                        makePost(path, data, schemaContext, transactionChain, newReadWriteTransaction);
-                        return newReadWriteTransaction.commit();
+                        makePost(path, data, schemaContext, strategy);
+                        return strategy.commit();
                     }
 
-                    insertWithPointLeafListPost(newReadWriteTransaction, LogicalDatastoreType.CONFIGURATION,
-                        path, data, schemaContext, point, readLeafList, true, transactionChain);
-                    return newReadWriteTransaction.commit();
+                    insertWithPointLeafListPost(LogicalDatastoreType.CONFIGURATION,
+                            path, data, schemaContext, point, readLeafList, true, strategy);
+                    return strategy.commit();
                 }
             default:
                 throw new RestconfDocumentedException(
@@ -204,11 +190,13 @@ public final class PostDataTransactionUtil {
         }
     }
 
-    private static void insertWithPointLeafListPost(final DOMDataTreeReadWriteTransaction rwTransaction,
-            final LogicalDatastoreType datastore, final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload,
-            final EffectiveModelContext schemaContext, final String point, final OrderedLeafSetNode<?> readLeafList,
-            final boolean before, final DOMTransactionChain transactionChain) {
-        rwTransaction.delete(datastore, path.getParent().getParent());
+    private static void insertWithPointLeafListPost(final LogicalDatastoreType datastore,
+                                                    final YangInstanceIdentifier path,
+                                                    final NormalizedNode<?, ?> payload,
+                                                    final EffectiveModelContext schemaContext, final String point,
+                                                    final OrderedLeafSetNode<?> readLeafList,
+                                                    final boolean before, final RestconfStrategy strategy) {
+        strategy.delete(datastore, path.getParent().getParent());
         final InstanceIdentifierContext<?> instanceIdentifier =
                 ParserIdentifier.toInstanceIdentifier(point, schemaContext, Optional.empty());
         int lastItemPosition = 0;
@@ -224,26 +212,27 @@ public final class PostDataTransactionUtil {
         int lastInsertedPosition = 0;
         final NormalizedNode<?, ?> emptySubtree =
                 ImmutableNodes.fromInstanceId(schemaContext, path.getParent().getParent());
-        rwTransaction.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
+        strategy.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
         for (final LeafSetEntryNode<?> nodeChild : readLeafList.getValue()) {
             if (lastInsertedPosition == lastItemPosition) {
-                TransactionUtil.checkItemDoesNotExists(transactionChain, rwTransaction, datastore, path,
+                TransactionUtil.checkItemDoesNotExists(strategy, datastore, path,
                         RestconfDataServiceConstant.PostData.POST_TX_TYPE);
-                rwTransaction.put(datastore, path, payload);
+                strategy.create(datastore, path, payload);
             }
             final YangInstanceIdentifier childPath = path.getParent().getParent().node(nodeChild.getIdentifier());
-            TransactionUtil.checkItemDoesNotExists(transactionChain, rwTransaction, datastore, childPath,
+            TransactionUtil.checkItemDoesNotExists(strategy, datastore, childPath,
                     RestconfDataServiceConstant.PostData.POST_TX_TYPE);
-            rwTransaction.put(datastore, childPath, nodeChild);
+            strategy.create(datastore, childPath, nodeChild);
             lastInsertedPosition++;
         }
     }
 
-    private static void insertWithPointListPost(final DOMDataTreeReadWriteTransaction rwTransaction,
-            final LogicalDatastoreType datastore, final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload,
-            final EffectiveModelContext schemaContext, final String point, final MapNode readList, final boolean before,
-            final DOMTransactionChain transactionChain) {
-        rwTransaction.delete(datastore, path.getParent().getParent());
+    private static void insertWithPointListPost(final LogicalDatastoreType datastore, final YangInstanceIdentifier path,
+                                                final NormalizedNode<?, ?> payload,
+                                                final EffectiveModelContext schemaContext, final String point,
+                                                final MapNode readList, final boolean before,
+                                                final RestconfStrategy strategy) {
+        strategy.delete(datastore, path.getParent().getParent());
         final InstanceIdentifierContext<?> instanceIdentifier =
                 ParserIdentifier.toInstanceIdentifier(point, schemaContext, Optional.empty());
         int lastItemPosition = 0;
@@ -259,68 +248,62 @@ public final class PostDataTransactionUtil {
         int lastInsertedPosition = 0;
         final NormalizedNode<?, ?> emptySubtree =
                 ImmutableNodes.fromInstanceId(schemaContext, path.getParent().getParent());
-        rwTransaction.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
+        strategy.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
         for (final MapEntryNode mapEntryNode : readList.getValue()) {
             if (lastInsertedPosition == lastItemPosition) {
-                TransactionUtil.checkItemDoesNotExists(transactionChain, rwTransaction, datastore, path,
+                TransactionUtil.checkItemDoesNotExists(strategy, datastore, path,
                         RestconfDataServiceConstant.PostData.POST_TX_TYPE);
-                rwTransaction.put(datastore, path, payload);
+                strategy.create(datastore, path, payload);
             }
             final YangInstanceIdentifier childPath = path.getParent().getParent().node(mapEntryNode.getIdentifier());
-            TransactionUtil.checkItemDoesNotExists(transactionChain, rwTransaction, datastore, childPath,
+            TransactionUtil.checkItemDoesNotExists(strategy, datastore, childPath,
                     RestconfDataServiceConstant.PostData.POST_TX_TYPE);
-            rwTransaction.put(datastore, childPath, mapEntryNode);
+            strategy.create(datastore, childPath, mapEntryNode);
             lastInsertedPosition++;
         }
     }
 
     private static void makePost(final YangInstanceIdentifier path, final NormalizedNode<?, ?> data,
-            final SchemaContext schemaContext, final DOMTransactionChain transactionChain,
-            final DOMDataTreeReadWriteTransaction transaction) {
+                                 final SchemaContext schemaContext, final RestconfStrategy strategy) {
         if (data instanceof MapNode) {
             boolean merge = false;
             for (final MapEntryNode child : ((MapNode) data).getValue()) {
                 final YangInstanceIdentifier childPath = path.node(child.getIdentifier());
-                TransactionUtil.checkItemDoesNotExists(
-                        transactionChain, transaction, LogicalDatastoreType.CONFIGURATION, childPath,
+                TransactionUtil.checkItemDoesNotExists(strategy, LogicalDatastoreType.CONFIGURATION, childPath,
                         RestconfDataServiceConstant.PostData.POST_TX_TYPE);
                 if (!merge) {
                     merge = true;
-                    TransactionUtil.ensureParentsByMerge(path, schemaContext, transaction);
+                    TransactionUtil.ensureParentsByMerge(path, schemaContext, strategy);
                     final NormalizedNode<?, ?> emptySubTree = ImmutableNodes.fromInstanceId(schemaContext, path);
-                    transaction.merge(LogicalDatastoreType.CONFIGURATION,
+                    strategy.merge(LogicalDatastoreType.CONFIGURATION,
                             YangInstanceIdentifier.create(emptySubTree.getIdentifier()), emptySubTree);
                 }
-                transaction.put(LogicalDatastoreType.CONFIGURATION, childPath, child);
+                strategy.create(LogicalDatastoreType.CONFIGURATION, childPath, child);
             }
         } else {
-            TransactionUtil.checkItemDoesNotExists(
-                    transactionChain, transaction, LogicalDatastoreType.CONFIGURATION, path,
+            TransactionUtil.checkItemDoesNotExists(strategy, LogicalDatastoreType.CONFIGURATION, path,
                     RestconfDataServiceConstant.PostData.POST_TX_TYPE);
 
-            TransactionUtil.ensureParentsByMerge(path, schemaContext, transaction);
-            transaction.put(LogicalDatastoreType.CONFIGURATION, path, data);
+            TransactionUtil.ensureParentsByMerge(path, schemaContext, strategy);
+            strategy.create(LogicalDatastoreType.CONFIGURATION, path, data);
         }
     }
 
     /**
      * Get location from {@link YangInstanceIdentifier} and {@link UriInfo}.
      *
-     * @param uriInfo
-     *             uri info
-     * @param transactionNode
-     *             wrapper for data of transaction
-     * @param schemaContext
-     *            reference to {@link SchemaContext}
+     * @param uriInfo                uri info
+     * @param yangInstanceIdentifier reference to {@link InstanceIdentifierContext}
+     * @param schemaContext          reference to {@link SchemaContext}
      * @return {@link URI}
      */
-    private static URI resolveLocation(final UriInfo uriInfo, final TransactionVarsWrapper transactionNode,
-            final EffectiveModelContext schemaContext, final NormalizedNode<?, ?> data) {
+    private static URI resolveLocation(final UriInfo uriInfo, final InstanceIdentifierContext<?> yangInstanceIdentifier,
+                                       final EffectiveModelContext schemaContext, final NormalizedNode<?, ?> data) {
         if (uriInfo == null) {
             return null;
         }
 
-        YangInstanceIdentifier path = transactionNode.getInstanceIdentifier().getInstanceIdentifier();
+        YangInstanceIdentifier path = yangInstanceIdentifier.getInstanceIdentifier();
 
         if (data instanceof MapNode) {
             final Collection<MapEntryNode> children = ((MapNode) data).getValue();
@@ -335,12 +318,12 @@ public final class PostDataTransactionUtil {
                 .build();
     }
 
-    private static void simplePost(final DOMDataTreeReadWriteTransaction rwTransaction,
-            final LogicalDatastoreType datastore, final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload,
-            final SchemaContext schemaContext, final DOMTransactionChain transactionChain) {
-        TransactionUtil.checkItemDoesNotExists(transactionChain, rwTransaction, datastore, path,
+    private static void simplePost(final LogicalDatastoreType datastore, final YangInstanceIdentifier path,
+                                   final NormalizedNode<?, ?> payload,
+                                   final SchemaContext schemaContext, final RestconfStrategy strategy) {
+        TransactionUtil.checkItemDoesNotExists(strategy, datastore, path,
                 RestconfDataServiceConstant.PostData.POST_TX_TYPE);
-        TransactionUtil.ensureParentsByMerge(path, schemaContext, rwTransaction);
-        rwTransaction.put(datastore, path, payload);
+        TransactionUtil.ensureParentsByMerge(path, schemaContext, strategy);
+        strategy.create(datastore, path, payload);
     }
 }
index c9f7d6379d11bdfe774700d9692c14f11b9761a6..b9f20b79df08a9d84b039ff5f36132ad5b4597c9 100644 (file)
@@ -16,8 +16,6 @@ import javax.ws.rs.core.Response;
 import javax.ws.rs.core.Response.Status;
 import org.opendaylight.mdsal.common.api.CommitInfo;
 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
-import org.opendaylight.mdsal.dom.api.DOMDataTreeReadWriteTransaction;
-import org.opendaylight.mdsal.dom.api.DOMDataTreeWriteTransaction;
 import org.opendaylight.mdsal.dom.api.DOMTransactionChain;
 import org.opendaylight.restconf.common.context.InstanceIdentifierContext;
 import org.opendaylight.restconf.common.context.NormalizedNodeContext;
@@ -25,8 +23,7 @@ import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
 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.handlers.TransactionChainHandler;
-import org.opendaylight.restconf.nb.rfc8040.rests.transactions.TransactionVarsWrapper;
+import org.opendaylight.restconf.nb.rfc8040.rests.transactions.RestconfStrategy;
 import org.opendaylight.restconf.nb.rfc8040.utils.parser.ParserIdentifier;
 import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
@@ -55,16 +52,13 @@ import org.opendaylight.yangtools.yang.model.api.SchemaNode;
 public final class PutDataTransactionUtil {
 
     private PutDataTransactionUtil() {
-
     }
 
     /**
      * Valid input data with {@link SchemaNode}.
      *
-     * @param schemaNode
-     *             {@link SchemaNode}
-     * @param payload
-     *             input data
+     * @param schemaNode {@link SchemaNode}
+     * @param payload    input data
      */
     public static void validInputData(final SchemaNode schemaNode, final NormalizedNodeContext payload) {
         if (schemaNode != null && payload.getData() == null) {
@@ -77,10 +71,8 @@ public final class PutDataTransactionUtil {
     /**
      * Valid top level node name.
      *
-     * @param path
-     *             path of node
-     * @param payload
-     *             data
+     * @param path    path of node
+     * @param payload data
      */
     public static void validTopLevelNodeName(final YangInstanceIdentifier path, final NormalizedNodeContext payload) {
         final String payloadName = payload.getData().getNodeType().getLocalName();
@@ -104,8 +96,7 @@ public final class PutDataTransactionUtil {
      * Validates whether keys in {@code payload} are equal to values of keys in
      * {@code iiWithData} for list schema node.
      *
-     * @throws RestconfDocumentedException
-     *             if key values or key count in payload and URI isn't equal
+     * @throws RestconfDocumentedException if key values or key count in payload and URI isn't equal
      */
     public static void validateListKeysEqualityInPayloadAndUri(final NormalizedNodeContext payload) {
         final InstanceIdentifierContext<?> iiWithData = payload.getInstanceIdentifierContext();
@@ -122,12 +113,12 @@ public final class PutDataTransactionUtil {
     }
 
     private static void isEqualUriAndPayloadKeyValues(final Map<QName, Object> uriKeyValues, final MapEntryNode payload,
-            final List<QName> keyDefinitions) {
+                                                      final List<QName> keyDefinitions) {
         final Map<QName, Object> mutableCopyUriKeyValues = new HashMap<>(uriKeyValues);
         for (final QName keyDefinition : keyDefinitions) {
             final Object uriKeyValue = RestconfDocumentedException.throwIfNull(
-                mutableCopyUriKeyValues.remove(keyDefinition), ErrorType.PROTOCOL, ErrorTag.DATA_MISSING,
-                "Missing key %s in URI.", keyDefinition);
+                    mutableCopyUriKeyValues.remove(keyDefinition), ErrorType.PROTOCOL, ErrorTag.DATA_MISSING,
+                    "Missing key %s in URI.", keyDefinition);
 
             final Object dataKeyValue = payload.getIdentifier().getValue(keyDefinition);
 
@@ -141,151 +132,131 @@ public final class PutDataTransactionUtil {
     }
 
     /**
-     * Check mount point and prepare variables for put data to DS. Close {@link DOMTransactionChain} inside of object
-     * {@link TransactionVarsWrapper} provided as a parameter.
+     * Check mount point and prepare variables for put data to DS. Close {@link DOMTransactionChain} if any
+     * inside of object {@link RestconfStrategy} provided as a parameter if any.
      *
-     * @param payload
-     *             data to put
-     * @param schemaContext
-     *             reference to {@link EffectiveModelContext}
-     * @param transactionNode
-     *             wrapper of variables for transaction
-     * @param point
-     *             query parameter
-     * @param insert
-     *             query parameter
+     * @param payload       data to put
+     * @param schemaContext reference to {@link EffectiveModelContext}
+     * @param strategy      object that perform the actual DS operations
+     * @param point         query parameter
+     * @param insert        query parameter
      * @return {@link Response}
      */
     public static Response putData(final NormalizedNodeContext payload, final EffectiveModelContext schemaContext,
-                               final TransactionVarsWrapper transactionNode, final String insert, final String point) {
+                                   final RestconfStrategy strategy, final String insert, final String point) {
         final YangInstanceIdentifier path = payload.getInstanceIdentifierContext().getInstanceIdentifier();
 
-        final DOMDataTreeReadWriteTransaction readWriteTransaction =
-                transactionNode.getTransactionChain().newReadWriteTransaction();
-
-        final FluentFuture<Boolean> existsFuture = readWriteTransaction.exists(LogicalDatastoreType.CONFIGURATION,
-            path);
+        strategy.prepareReadWriteExecution();
+        final FluentFuture<Boolean> existsFuture = strategy.exists(LogicalDatastoreType.CONFIGURATION, path);
         final FutureDataFactory<Boolean> existsResponse = new FutureDataFactory<>();
         FutureCallbackTx.addCallback(existsFuture, RestconfDataServiceConstant.PutData.PUT_TX_TYPE, existsResponse);
 
         final ResponseFactory responseFactory =
                 new ResponseFactory(existsResponse.result ? Status.NO_CONTENT : Status.CREATED);
-        final FluentFuture<? extends CommitInfo> submitData = submitData(path, schemaContext,
-                transactionNode.getTransactionChainHandler(), readWriteTransaction, payload.getData(), insert, point);
-        //This method will close transactionChain
+        final FluentFuture<? extends CommitInfo> submitData = submitData(path, schemaContext, strategy,
+                payload.getData(), insert, point, existsResponse.result);
+        //This method will close transactionChain if any
         FutureCallbackTx.addCallback(submitData, RestconfDataServiceConstant.PutData.PUT_TX_TYPE, responseFactory,
-                transactionNode.getTransactionChain());
+                strategy.getTransactionChain());
         return responseFactory.build();
     }
 
     /**
      * Put data to DS.
      *
-     * @param path
-     *             path of data
-     * @param schemaContext
-     *             {@link SchemaContext}
-     * @param transactionChainHandler
-     *             write transaction
-     * @param data
-     *             data
-     * @param point
-     *             query parameter
-     * @param insert
-     *             query parameter
+     * @param path          path of data
+     * @param schemaContext {@link SchemaContext}
+     * @param strategy      object that perform the actual DS operations
+     * @param data          data
+     * @param point         query parameter
+     * @param insert        query parameter
      * @return {@link FluentFuture}
      */
-    private static FluentFuture<? extends CommitInfo> submitData(final YangInstanceIdentifier path,
-            final EffectiveModelContext schemaContext, final TransactionChainHandler transactionChainHandler,
-            final DOMDataTreeReadWriteTransaction readWriteTransaction,
-            final NormalizedNode<?, ?> data, final String insert, final String point) {
+    private static FluentFuture<? extends CommitInfo> submitData(
+            final YangInstanceIdentifier path,
+            final EffectiveModelContext schemaContext,
+            final RestconfStrategy strategy,
+            final NormalizedNode<?, ?> data, final String insert, final String point, final boolean exists) {
         if (insert == null) {
-            return makePut(path, schemaContext, readWriteTransaction, data);
+            return makePut(path, schemaContext, strategy, data, exists);
         }
 
         final DataSchemaNode schemaNode = checkListAndOrderedType(schemaContext, path);
         switch (insert) {
             case "first":
                 if (schemaNode instanceof ListSchemaNode) {
-                    final NormalizedNode<?, ?> readData =
-                            readList(path, schemaContext, transactionChainHandler, schemaNode);
+                    final NormalizedNode<?, ?> readData = readList(path, schemaContext, strategy, schemaNode);
                     final OrderedMapNode readList = (OrderedMapNode) readData;
                     if (readList == null || readList.getValue().isEmpty()) {
-                        return makePut(path, schemaContext, readWriteTransaction, data);
+                        return makePut(path, schemaContext, strategy, data, exists);
                     } else {
-                        readWriteTransaction.delete(LogicalDatastoreType.CONFIGURATION, path.getParent());
-                        simplePut(LogicalDatastoreType.CONFIGURATION, path, readWriteTransaction,
-                            schemaContext, data);
-                        listPut(LogicalDatastoreType.CONFIGURATION, path.getParent(), readWriteTransaction,
-                            schemaContext, readList);
-                        return readWriteTransaction.commit();
+                        strategy.delete(LogicalDatastoreType.CONFIGURATION, path.getParent());
+                        simplePut(LogicalDatastoreType.CONFIGURATION, path, strategy, schemaContext, data, exists);
+                        listPut(LogicalDatastoreType.CONFIGURATION, path.getParent(), strategy,
+                                schemaContext, readList, exists);
+                        return strategy.commit();
                     }
                 } else {
-                    final NormalizedNode<?, ?> readData =
-                            readList(path, schemaContext, transactionChainHandler, schemaNode);
+                    final NormalizedNode<?, ?> readData = readList(path, schemaContext, strategy, schemaNode);
 
                     final OrderedLeafSetNode<?> readLeafList = (OrderedLeafSetNode<?>) readData;
                     if (readLeafList == null || readLeafList.getValue().isEmpty()) {
-                        return makePut(path, schemaContext, readWriteTransaction, data);
+                        return makePut(path, schemaContext, strategy, data, exists);
                     } else {
-                        readWriteTransaction.delete(LogicalDatastoreType.CONFIGURATION, path.getParent());
-                        simplePut(LogicalDatastoreType.CONFIGURATION, path, readWriteTransaction,
-                            schemaContext, data);
-                        listPut(LogicalDatastoreType.CONFIGURATION, path.getParent(), readWriteTransaction,
-                            schemaContext, readLeafList);
-                        return readWriteTransaction.commit();
+                        strategy.delete(LogicalDatastoreType.CONFIGURATION, path.getParent());
+                        simplePut(LogicalDatastoreType.CONFIGURATION, path, strategy,
+                                schemaContext, data, exists);
+                        listPut(LogicalDatastoreType.CONFIGURATION, path.getParent(), strategy,
+                                schemaContext, readLeafList, exists);
+                        return strategy.commit();
                     }
                 }
             case "last":
-                return makePut(path, schemaContext, readWriteTransaction, data);
+                return makePut(path, schemaContext, strategy, data, exists);
             case "before":
                 if (schemaNode instanceof ListSchemaNode) {
-                    final NormalizedNode<?, ?> readData =
-                            readList(path, schemaContext, transactionChainHandler, schemaNode);
+                    final NormalizedNode<?, ?> readData = readList(path, schemaContext, strategy, schemaNode);
                     final OrderedMapNode readList = (OrderedMapNode) readData;
                     if (readList == null || readList.getValue().isEmpty()) {
-                        return makePut(path, schemaContext, readWriteTransaction, data);
+                        return makePut(path, schemaContext, strategy, data, exists);
                     } else {
-                        insertWithPointListPut(readWriteTransaction, LogicalDatastoreType.CONFIGURATION, path,
-                            data, schemaContext, point, readList, true);
-                        return readWriteTransaction.commit();
+                        insertWithPointListPut(strategy, LogicalDatastoreType.CONFIGURATION, path,
+                                data, schemaContext, point, readList, true, exists);
+                        return strategy.commit();
                     }
                 } else {
-                    final NormalizedNode<?, ?> readData =
-                            readList(path, schemaContext, transactionChainHandler, schemaNode);
+                    final NormalizedNode<?, ?> readData = readList(path, schemaContext, strategy, schemaNode);
 
                     final OrderedLeafSetNode<?> readLeafList = (OrderedLeafSetNode<?>) readData;
                     if (readLeafList == null || readLeafList.getValue().isEmpty()) {
-                        return makePut(path, schemaContext, readWriteTransaction, data);
+                        return makePut(path, schemaContext, strategy, data, exists);
                     } else {
-                        insertWithPointLeafListPut(readWriteTransaction, LogicalDatastoreType.CONFIGURATION,
-                            path, data, schemaContext, point, readLeafList, true);
-                        return readWriteTransaction.commit();
+                        insertWithPointLeafListPut(strategy, LogicalDatastoreType.CONFIGURATION,
+                                path, data, schemaContext, point, readLeafList, true, exists);
+                        return strategy.commit();
                     }
                 }
             case "after":
                 if (schemaNode instanceof ListSchemaNode) {
-                    final NormalizedNode<?, ?> readData =
-                            readList(path, schemaContext, transactionChainHandler, schemaNode);
+                    final NormalizedNode<?, ?> readData = readList(path, schemaContext, strategy, schemaNode);
                     final OrderedMapNode readList = (OrderedMapNode) readData;
                     if (readList == null || readList.getValue().isEmpty()) {
-                        return makePut(path, schemaContext, readWriteTransaction, data);
+                        return makePut(path, schemaContext, strategy, data, exists);
                     } else {
-                        insertWithPointListPut(readWriteTransaction, LogicalDatastoreType.CONFIGURATION,
-                            path, data, schemaContext, point, readList, false);
-                        return readWriteTransaction.commit();
+                        insertWithPointListPut(strategy, LogicalDatastoreType.CONFIGURATION,
+                                path, data, schemaContext, point, readList, false, exists);
+                        return strategy.commit();
                     }
                 } else {
-                    final NormalizedNode<?, ?> readData =
-                            readList(path, schemaContext, transactionChainHandler, schemaNode);
+                    final NormalizedNode<?, ?> readData = readList(path, schemaContext, strategy, schemaNode);
 
                     final OrderedLeafSetNode<?> readLeafList = (OrderedLeafSetNode<?>) readData;
                     if (readLeafList == null || readLeafList.getValue().isEmpty()) {
-                        return makePut(path, schemaContext, readWriteTransaction, data);
+                        return makePut(path, schemaContext, strategy, data, exists);
                     } else {
-                        insertWithPointLeafListPut(readWriteTransaction, LogicalDatastoreType.CONFIGURATION,
-                            path, data, schemaContext, point, readLeafList, true);
-                        return readWriteTransaction.commit();
+                        insertWithPointLeafListPut(strategy, LogicalDatastoreType.CONFIGURATION,
+                                path, data, schemaContext, point, readLeafList, true, exists);
+                        return strategy.commit();
                     }
                 }
             default:
@@ -296,21 +267,24 @@ public final class PutDataTransactionUtil {
     }
 
     public static NormalizedNode<?, ?> readList(final YangInstanceIdentifier path,
-            final EffectiveModelContext schemaContext, final TransactionChainHandler transactionChainHandler,
-            final DataSchemaNode schemaNode) {
+                                                final EffectiveModelContext schemaContext,
+                                                final RestconfStrategy strategy,
+                                                final DataSchemaNode schemaNode) {
         final InstanceIdentifierContext<?> iid = new InstanceIdentifierContext<SchemaNode>(
                 path.getParent(), schemaNode, null, schemaContext);
-        final TransactionVarsWrapper transactionNode = new TransactionVarsWrapper(iid, null, transactionChainHandler);
-        final NormalizedNode<?, ?> readData = ReadDataTransactionUtil
-                .readData(RestconfDataServiceConstant.ReadData.CONFIG, transactionNode, schemaContext);
-        return readData;
+        final RestconfStrategy restconfStrategy = strategy.buildStrategy(iid);
+        return ReadDataTransactionUtil.readData(
+                RestconfDataServiceConstant.ReadData.CONFIG, restconfStrategy, schemaContext);
     }
 
-    private static void insertWithPointLeafListPut(final DOMDataTreeReadWriteTransaction rwTransaction,
-            final LogicalDatastoreType datastore, final YangInstanceIdentifier path,
-            final NormalizedNode<?, ?> data, final EffectiveModelContext schemaContext, final String point,
-            final OrderedLeafSetNode<?> readLeafList, final boolean before) {
-        rwTransaction.delete(datastore, path.getParent());
+    private static void insertWithPointLeafListPut(final RestconfStrategy strategy,
+                                                   final LogicalDatastoreType datastore,
+                                                   final YangInstanceIdentifier path,
+                                                   final NormalizedNode<?, ?> data,
+                                                   final EffectiveModelContext schemaContext, final String point,
+                                                   final OrderedLeafSetNode<?> readLeafList, final boolean before,
+                                                   final boolean exists) {
+        strategy.delete(datastore, path.getParent());
         final InstanceIdentifierContext<?> instanceIdentifier =
                 ParserIdentifier.toInstanceIdentifier(point, schemaContext, Optional.empty());
         int lastItemPosition = 0;
@@ -325,22 +299,28 @@ public final class PutDataTransactionUtil {
         }
         int lastInsertedPosition = 0;
         final NormalizedNode<?, ?> emptySubtree = ImmutableNodes.fromInstanceId(schemaContext, path.getParent());
-        rwTransaction.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
+        strategy.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
         for (final LeafSetEntryNode<?> nodeChild : readLeafList.getValue()) {
             if (lastInsertedPosition == lastItemPosition) {
-                simplePut(datastore, path, rwTransaction, schemaContext, data);
+                simplePut(datastore, path, strategy, schemaContext, data, exists);
             }
             final YangInstanceIdentifier childPath = path.getParent().node(nodeChild.getIdentifier());
-            rwTransaction.put(datastore, childPath, nodeChild);
+            if (exists) {
+                strategy.replace(datastore, childPath, nodeChild);
+            } else {
+                strategy.create(datastore, childPath, nodeChild);
+            }
             lastInsertedPosition++;
         }
     }
 
-    private static void insertWithPointListPut(final DOMDataTreeReadWriteTransaction writeTx,
-            final LogicalDatastoreType datastore, final YangInstanceIdentifier path,
-            final NormalizedNode<?, ?> data, final EffectiveModelContext schemaContext, final String point,
-            final OrderedMapNode readList, final boolean before) {
-        writeTx.delete(datastore, path.getParent());
+    private static void insertWithPointListPut(final RestconfStrategy strategy,
+                                               final LogicalDatastoreType datastore, final YangInstanceIdentifier path,
+                                               final NormalizedNode<?, ?> data,
+                                               final EffectiveModelContext schemaContext, final String point,
+                                               final OrderedMapNode readList, final boolean before,
+                                               final boolean exists) {
+        strategy.delete(datastore, path.getParent());
         final InstanceIdentifierContext<?> instanceIdentifier =
                 ParserIdentifier.toInstanceIdentifier(point, schemaContext, Optional.empty());
         int lastItemPosition = 0;
@@ -355,54 +335,76 @@ public final class PutDataTransactionUtil {
         }
         int lastInsertedPosition = 0;
         final NormalizedNode<?, ?> emptySubtree = ImmutableNodes.fromInstanceId(schemaContext, path.getParent());
-        writeTx.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
+        strategy.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
         for (final MapEntryNode mapEntryNode : readList.getValue()) {
             if (lastInsertedPosition == lastItemPosition) {
-                simplePut(datastore, path, writeTx, schemaContext, data);
+                simplePut(datastore, path, strategy, schemaContext, data, exists);
             }
             final YangInstanceIdentifier childPath = path.getParent().node(mapEntryNode.getIdentifier());
-            writeTx.put(datastore, childPath, mapEntryNode);
+            if (exists) {
+                strategy.replace(datastore, childPath, mapEntryNode);
+            } else {
+                strategy.create(datastore, childPath, mapEntryNode);
+            }
             lastInsertedPosition++;
         }
     }
 
     private static void listPut(final LogicalDatastoreType datastore, final YangInstanceIdentifier path,
-            final DOMDataTreeWriteTransaction writeTx, final SchemaContext schemaContext,
-            final OrderedLeafSetNode<?> payload) {
+                                final RestconfStrategy strategy, final SchemaContext schemaContext,
+                                final OrderedLeafSetNode<?> payload, final boolean exists) {
         final NormalizedNode<?, ?> emptySubtree = ImmutableNodes.fromInstanceId(schemaContext, path);
-        writeTx.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
-        TransactionUtil.ensureParentsByMerge(path, schemaContext, writeTx);
+        strategy.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
+        TransactionUtil.ensureParentsByMerge(path, schemaContext, strategy);
         for (final LeafSetEntryNode<?> child : ((LeafSetNode<?>) payload).getValue()) {
             final YangInstanceIdentifier childPath = path.node(child.getIdentifier());
-            writeTx.put(datastore, childPath, child);
+            if (exists) {
+                strategy.replace(datastore, childPath, child);
+            } else {
+                strategy.create(datastore, childPath, child);
+            }
         }
     }
 
     private static void listPut(final LogicalDatastoreType datastore, final YangInstanceIdentifier path,
-            final DOMDataTreeWriteTransaction writeTx, final SchemaContext schemaContext,
-            final OrderedMapNode payload) {
+                                final RestconfStrategy strategy, final SchemaContext schemaContext,
+                                final OrderedMapNode payload, final boolean exists) {
         final NormalizedNode<?, ?> emptySubtree = ImmutableNodes.fromInstanceId(schemaContext, path);
-        writeTx.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
-        TransactionUtil.ensureParentsByMerge(path, schemaContext, writeTx);
+        strategy.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
+        TransactionUtil.ensureParentsByMerge(path, schemaContext, strategy);
         for (final MapEntryNode child : payload.getValue()) {
             final YangInstanceIdentifier childPath = path.node(child.getIdentifier());
-            writeTx.put(datastore, childPath, child);
+            if (exists) {
+                strategy.replace(datastore, childPath, child);
+            } else {
+                strategy.create(datastore, childPath, child);
+            }
         }
     }
 
     private static void simplePut(final LogicalDatastoreType configuration, final YangInstanceIdentifier path,
-            final DOMDataTreeWriteTransaction writeTx, final SchemaContext schemaContext,
-            final NormalizedNode<?, ?> data) {
-        TransactionUtil.ensureParentsByMerge(path, schemaContext, writeTx);
-        writeTx.put(LogicalDatastoreType.CONFIGURATION, path, data);
+                                  final RestconfStrategy strategy, final SchemaContext schemaContext,
+                                  final NormalizedNode<?, ?> data, final boolean exists) {
+        TransactionUtil.ensureParentsByMerge(path, schemaContext, strategy);
+        if (exists) {
+            strategy.replace(LogicalDatastoreType.CONFIGURATION, path, data);
+        } else {
+            strategy.create(LogicalDatastoreType.CONFIGURATION, path, data);
+        }
     }
 
     private static FluentFuture<? extends CommitInfo> makePut(final YangInstanceIdentifier path,
-            final SchemaContext schemaContext, final DOMDataTreeWriteTransaction writeTx,
-            final NormalizedNode<?, ?> data) {
-        TransactionUtil.ensureParentsByMerge(path, schemaContext, writeTx);
-        writeTx.put(LogicalDatastoreType.CONFIGURATION, path, data);
-        return writeTx.commit();
+                                                              final SchemaContext schemaContext,
+                                                              final RestconfStrategy strategy,
+                                                              final NormalizedNode<?, ?> data,
+                                                              final boolean exists) {
+        TransactionUtil.ensureParentsByMerge(path, schemaContext, strategy);
+        if (exists) {
+            strategy.replace(LogicalDatastoreType.CONFIGURATION, path, data);
+        } else {
+            strategy.create(LogicalDatastoreType.CONFIGURATION, path, data);
+        }
+        return strategy.commit();
     }
 
     public static DataSchemaNode checkListAndOrderedType(final SchemaContext ctx, final YangInstanceIdentifier path) {
index 8ba6802833e3789e9221f8d718ff3bb9427abb47..a9cdb236a0320a1aae258dce889c20adec9ef375 100644 (file)
@@ -8,7 +8,7 @@
 package org.opendaylight.restconf.nb.rfc8040.rests.utils;
 
 import com.google.common.primitives.Ints;
-import com.google.common.util.concurrent.FluentFuture;
+import com.google.common.util.concurrent.ListenableFuture;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
@@ -20,14 +20,13 @@ import javax.ws.rs.core.UriInfo;
 import org.eclipse.jdt.annotation.NonNull;
 import org.eclipse.jdt.annotation.Nullable;
 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
-import org.opendaylight.mdsal.dom.api.DOMDataTreeReadTransaction;
 import org.opendaylight.mdsal.dom.api.DOMTransactionChain;
 import org.opendaylight.restconf.common.context.InstanceIdentifierContext;
 import org.opendaylight.restconf.common.context.WriterParameters;
 import org.opendaylight.restconf.common.context.WriterParameters.WriterParametersBuilder;
 import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
 import org.opendaylight.restconf.common.errors.RestconfError;
-import org.opendaylight.restconf.nb.rfc8040.rests.transactions.TransactionVarsWrapper;
+import org.opendaylight.restconf.nb.rfc8040.rests.transactions.RestconfStrategy;
 import org.opendaylight.restconf.nb.rfc8040.utils.parser.ParserFieldsParameter;
 import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.common.QNameModule;
@@ -83,10 +82,8 @@ public final class ReadDataTransactionUtil {
     /**
      * Parse parameters from URI request and check their types and values.
      *
-     * @param identifier
-     *             {@link InstanceIdentifierContext}
-     * @param uriInfo
-     *             URI info
+     * @param identifier {@link InstanceIdentifierContext}
+     * @param uriInfo    URI info
      * @return {@link WriterParameters}
      */
     public static WriterParameters parseUriParameters(final InstanceIdentifierContext<?> identifier,
@@ -173,60 +170,50 @@ public final class ReadDataTransactionUtil {
                     builder.setWithDefault(withDefaults.get(0));
             }
         }
-
         return builder.build();
     }
 
     /**
      * Read specific type of data from data store via transaction.
      *
-     * @param valueOfContent
-     *            type of data to read (config, state, all)
-     * @param transactionNode
-     *            {@link TransactionVarsWrapper} - wrapper for variables
-     * @param schemaContext
-     *            schema context
+     * @param valueOfContent type of data to read (config, state, all)
+     * @param strategy       {@link RestconfStrategy} - wrapper for variables
+     * @param schemaContext  schema context
      * @return {@link NormalizedNode}
      */
     public static @Nullable NormalizedNode<?, ?> readData(final @NonNull String valueOfContent,
-            final @NonNull TransactionVarsWrapper transactionNode, final SchemaContext schemaContext) {
-        return readData(valueOfContent, transactionNode, null, schemaContext);
+            final @NonNull RestconfStrategy strategy, final SchemaContext schemaContext) {
+        return readData(valueOfContent, strategy, null, schemaContext);
     }
 
     /**
-     * Read specific type of data from data store via transaction. Close {@link DOMTransactionChain} inside of object
-     * {@link TransactionVarsWrapper} provided as a parameter.
+     * Read specific type of data from data store via transaction. Close {@link DOMTransactionChain} if any
+     * inside of object {@link RestconfStrategy} provided as a parameter.
      *
-     * @param valueOfContent
-     *            type of data to read (config, state, all)
-     * @param transactionNode
-     *            {@link TransactionVarsWrapper} - wrapper for variables
-     * @param withDefa
-     *            value of with-defaults parameter
-     * @param ctx
-     *            schema context
+     * @param valueOfContent type of data to read (config, state, all)
+     * @param strategy       {@link RestconfStrategy} - object that perform the actual DS operations
+     * @param withDefa       vaule of with-defaults parameter
+     * @param ctx            schema context
      * @return {@link NormalizedNode}
      */
     public static @Nullable NormalizedNode<?, ?> readData(final @NonNull String valueOfContent,
-            final @NonNull TransactionVarsWrapper transactionNode, final String withDefa, final SchemaContext ctx) {
+                                                          final @NonNull RestconfStrategy strategy,
+                                                          final String withDefa, final SchemaContext ctx) {
         switch (valueOfContent) {
             case RestconfDataServiceConstant.ReadData.CONFIG:
-                transactionNode.setLogicalDatastoreType(LogicalDatastoreType.CONFIGURATION);
                 if (withDefa == null) {
-                    return readDataViaTransaction(transactionNode);
+                    return readDataViaTransaction(strategy, LogicalDatastoreType.CONFIGURATION, true);
                 } else {
-                    return prepareDataByParamWithDef(readDataViaTransaction(transactionNode),
-                            transactionNode.getInstanceIdentifier().getInstanceIdentifier(), withDefa, ctx);
+                    return prepareDataByParamWithDef(
+                            readDataViaTransaction(strategy, LogicalDatastoreType.CONFIGURATION, true),
+                            strategy.getInstanceIdentifier().getInstanceIdentifier(), withDefa, ctx);
                 }
             case RestconfDataServiceConstant.ReadData.NONCONFIG:
-                transactionNode.setLogicalDatastoreType(LogicalDatastoreType.OPERATIONAL);
-                return readDataViaTransaction(transactionNode);
-
+                return readDataViaTransaction(strategy, LogicalDatastoreType.OPERATIONAL, true);
             case RestconfDataServiceConstant.ReadData.ALL:
-                return readAllData(transactionNode, withDefa, ctx);
-
+                return readAllData(strategy, withDefa, ctx);
             default:
-                transactionNode.getTransactionChain().close();
+                strategy.cancel();
                 throw new RestconfDocumentedException(
                         new RestconfError(RestconfError.ErrorType.PROTOCOL, RestconfError.ErrorTag.INVALID_VALUE,
                                 "Invalid content parameter: " + valueOfContent, null,
@@ -358,79 +345,57 @@ public final class ReadDataTransactionUtil {
     }
 
     /**
-     * If is set specific {@link LogicalDatastoreType} in
-     * {@link TransactionVarsWrapper}, then read this type of data from DS. If
-     * don't, we have to read all data from DS (state + config).
-     * This method will close {@link org.opendaylight.mdsal.dom.api.DOMTransactionChain} inside of
-     * {@link TransactionVarsWrapper}.
-     *
-     * @param transactionNode
-     *             {@link TransactionVarsWrapper} - wrapper for variables
-     * @return {@link NormalizedNode}
-     */
-    private static @Nullable NormalizedNode<?, ?> readDataViaTransaction(
-            final @NonNull TransactionVarsWrapper transactionNode) {
-        return readDataViaTransaction(transactionNode, true);
-    }
-
-
-    /**
-     * If is set specific {@link LogicalDatastoreType} in
-     * {@link TransactionVarsWrapper}, then read this type of data from DS. If
-     * don't, we have to read all data from DS (state + config)
+     * If is set specific {@link LogicalDatastoreType} in {@link RestconfStrategy}, then read this type of data from DS.
+     * If don't, we have to read all data from DS (state + config)
      *
-     * @param transactionNode
-     *             {@link TransactionVarsWrapper} - wrapper for variables
-     * @param closeTransactionChain
-     *             If is set to true, after transaction it will close transactionChain in {@link TransactionVarsWrapper}
+     * @param strategy              {@link RestconfStrategy} - object that perform the actual DS operations
+     * @param closeTransactionChain If is set to true, after transaction it will close transactionChain
+     *                              in {@link RestconfStrategy} if any
      * @return {@link NormalizedNode}
      */
     private static @Nullable NormalizedNode<?, ?> readDataViaTransaction(
-            final @NonNull TransactionVarsWrapper transactionNode, final boolean closeTransactionChain) {
+            final @NonNull RestconfStrategy strategy,
+            final LogicalDatastoreType store,
+            final boolean closeTransactionChain) {
         final NormalizedNodeFactory dataFactory = new NormalizedNodeFactory();
-        try (DOMDataTreeReadTransaction tx = transactionNode.getTransactionChain().newReadOnlyTransaction()) {
-            final FluentFuture<Optional<NormalizedNode<?, ?>>> listenableFuture = tx.read(
-                transactionNode.getLogicalDatastoreType(),
-                transactionNode.getInstanceIdentifier().getInstanceIdentifier());
-            if (closeTransactionChain) {
-                //Method close transactionChain inside of TransactionVarsWrapper, if is provide as a parameter.
-                FutureCallbackTx.addCallback(listenableFuture, RestconfDataServiceConstant.ReadData.READ_TYPE_TX,
-                        dataFactory, transactionNode.getTransactionChain());
-            } else {
-                FutureCallbackTx.addCallback(listenableFuture, RestconfDataServiceConstant.ReadData.READ_TYPE_TX,
-                        dataFactory);
-            }
+        final ListenableFuture<Optional<NormalizedNode<?, ?>>> listenableFuture = strategy.read(
+                store, strategy.getInstanceIdentifier().getInstanceIdentifier());
+        if (closeTransactionChain) {
+            //Method close transactionChain if any
+            FutureCallbackTx.addCallback(listenableFuture, RestconfDataServiceConstant.ReadData.READ_TYPE_TX,
+                    dataFactory, strategy.getTransactionChain());
+        } else {
+            FutureCallbackTx.addCallback(listenableFuture, RestconfDataServiceConstant.ReadData.READ_TYPE_TX,
+                    dataFactory);
         }
         return dataFactory.build();
     }
 
     /**
      * Read config and state data, then map them. Close {@link DOMTransactionChain} inside of object
-     * {@link TransactionVarsWrapper} provided as a parameter.
+     * {@link RestconfStrategy} provided as a parameter if any.
      *
-     * @param transactionNode
-     *            {@link TransactionVarsWrapper} - wrapper for variables
-     * @param withDefa
-     *            with-defaults parameter
-     * @param ctx
-     *            schema context
+     * @param strategy {@link RestconfStrategy} - object that perform the actual DS operations
+     * @param withDefa with-defaults parameter
+     * @param ctx      schema context
      * @return {@link NormalizedNode}
      */
-    private static @Nullable NormalizedNode<?, ?> readAllData(final @NonNull TransactionVarsWrapper transactionNode,
+    private static @Nullable NormalizedNode<?, ?> readAllData(final @NonNull RestconfStrategy strategy,
             final String withDefa, final SchemaContext ctx) {
         // PREPARE STATE DATA NODE
-        transactionNode.setLogicalDatastoreType(LogicalDatastoreType.OPERATIONAL);
-        final NormalizedNode<?, ?> stateDataNode = readDataViaTransaction(transactionNode, false);
+        final NormalizedNode<?, ?> stateDataNode = readDataViaTransaction(
+                strategy, LogicalDatastoreType.OPERATIONAL, false);
 
         // PREPARE CONFIG DATA NODE
-        transactionNode.setLogicalDatastoreType(LogicalDatastoreType.CONFIGURATION);
         final NormalizedNode<?, ?> configDataNode;
-        //Here will be closed transactionChain
+        //Here will be closed transactionChain if any
         if (withDefa == null) {
-            configDataNode = readDataViaTransaction(transactionNode);
+            configDataNode = readDataViaTransaction(
+                    strategy, LogicalDatastoreType.CONFIGURATION, true);
         } else {
-            configDataNode = prepareDataByParamWithDef(readDataViaTransaction(transactionNode),
-                    transactionNode.getInstanceIdentifier().getInstanceIdentifier(), withDefa, ctx);
+            configDataNode = prepareDataByParamWithDef(
+                    readDataViaTransaction(strategy, LogicalDatastoreType.CONFIGURATION, true),
+                    strategy.getInstanceIdentifier().getInstanceIdentifier(), withDefa, ctx);
         }
 
         // if no data exists
@@ -455,10 +420,8 @@ public final class ReadDataTransactionUtil {
     /**
      * Merge state and config data into a single NormalizedNode.
      *
-     * @param stateDataNode
-     *             data node of state data
-     * @param configDataNode
-     *             data node of config data
+     * @param stateDataNode  data node of state data
+     * @param configDataNode data node of config data
      * @return {@link NormalizedNode}
      */
     private static @NonNull NormalizedNode<?, ?> mergeStateAndConfigData(
@@ -474,10 +437,8 @@ public final class ReadDataTransactionUtil {
     /**
      * Validates whether the two NormalizedNodes can be merged.
      *
-     * @param stateDataNode
-     *             data node of state data
-     * @param configDataNode
-     *             data node of config data
+     * @param stateDataNode  data node of state data
+     * @param configDataNode data node of config data
      */
     private static void validateNodeMerge(final @NonNull NormalizedNode<?, ?> stateDataNode,
                                           final @NonNull NormalizedNode<?, ?> configDataNode) {
@@ -491,10 +452,8 @@ public final class ReadDataTransactionUtil {
     /**
      * Prepare and map data for rpc.
      *
-     * @param configDataNode
-     *             data node of config data
-     * @param stateDataNode
-     *             data node of state data
+     * @param configDataNode data node of config data
+     * @param stateDataNode  data node of state data
      * @return {@link NormalizedNode}
      */
     private static @NonNull NormalizedNode<?, ?> prepareRpcData(final @NonNull NormalizedNode<?, ?> configDataNode,
@@ -514,10 +473,8 @@ public final class ReadDataTransactionUtil {
     /**
      * Map node to map entry builder.
      *
-     * @param dataNode
-     *             data node
-     * @param mapEntryBuilder
-     *             builder for mapping data
+     * @param dataNode        data node
+     * @param mapEntryBuilder builder for mapping data
      */
     private static void mapRpcDataNode(final @NonNull NormalizedNode<?, ?> dataNode,
             final @NonNull DataContainerNodeBuilder<NodeIdentifierWithPredicates, MapEntryNode> mapEntryBuilder) {
@@ -527,10 +484,8 @@ public final class ReadDataTransactionUtil {
     /**
      * Prepare and map all data from DS.
      *
-     * @param configDataNode
-     *             data node of config data
-     * @param stateDataNode
-     *             data node of state data
+     * @param configDataNode data node of config data
+     * @param stateDataNode  data node of state data
      * @return {@link NormalizedNode}
      */
     @SuppressWarnings("unchecked")
@@ -627,12 +582,9 @@ public final class ReadDataTransactionUtil {
     /**
      * Map value from container node to builder.
      *
-     * @param configData
-     *             collection of config data nodes
-     * @param stateData
-     *             collection of state data nodes
-     * @param builder
-     *             builder
+     * @param configData collection of config data nodes
+     * @param stateData  collection of state data nodes
+     * @param builder    builder
      */
     private static <T extends NormalizedNode<? extends PathArgument, ?>> void mapValueToBuilder(
             final @NonNull Collection<T> configData, final @NonNull Collection<T> stateData,
@@ -653,12 +605,9 @@ public final class ReadDataTransactionUtil {
      * Map data with different identifiers to builder. Data with different identifiers can be just added
      * as childs to parent node.
      *
-     * @param configMap
-     *             map of config data nodes
-     * @param stateMap
-     *             map of state data nodes
-     * @param builder
-     *           - builder
+     * @param configMap map of config data nodes
+     * @param stateMap  map of state data nodes
+     * @param builder   - builder
      */
     private static <T extends NormalizedNode<? extends PathArgument, ?>> void mapDataToBuilder(
             final @NonNull Map<PathArgument, T> configMap, final @NonNull Map<PathArgument, T> stateMap,
@@ -673,12 +622,9 @@ public final class ReadDataTransactionUtil {
      * Map data with the same identifiers to builder. Data with the same identifiers cannot be just added but we need to
      * go one level down with {@code prepareData} method.
      *
-     * @param configMap
-     *             immutable config data
-     * @param stateMap
-     *             immutable state data
-     * @param builder
-     *           - builder
+     * @param configMap immutable config data
+     * @param stateMap  immutable state data
+     * @param builder   - builder
      */
     @SuppressWarnings("unchecked")
     private static <T extends NormalizedNode<? extends PathArgument, ?>> void mergeDataToBuilder(
index 0e4261c46a3af32962ac4319ad051c8981ae5ea8..ffe95f53663af8e0c6fdaed28b2b2201e51a0a8c 100644 (file)
@@ -7,18 +7,15 @@
  */
 package org.opendaylight.restconf.nb.rfc8040.rests.utils;
 
-import com.google.common.base.Preconditions;
 import com.google.common.util.concurrent.FluentFuture;
 import java.util.ArrayList;
 import java.util.Iterator;
 import java.util.List;
 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
-import org.opendaylight.mdsal.dom.api.DOMDataTreeReadWriteTransaction;
-import org.opendaylight.mdsal.dom.api.DOMDataTreeWriteTransaction;
-import org.opendaylight.mdsal.dom.api.DOMTransactionChain;
 import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
 import org.opendaylight.restconf.common.errors.RestconfError.ErrorTag;
 import org.opendaylight.restconf.common.errors.RestconfError.ErrorType;
+import org.opendaylight.restconf.nb.rfc8040.rests.transactions.RestconfStrategy;
 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;
@@ -42,15 +39,12 @@ public final class TransactionUtil {
     /**
      * Merged parents of data.
      *
-     * @param path
-     *             path of data
-     * @param schemaContext
-     *             {@link SchemaContext}
-     * @param writeTx
-     *             write transaction
+     * @param path          path of data
+     * @param schemaContext {@link SchemaContext}
+     * @param strategy      object that perform the actual DS operations
      */
     public static void ensureParentsByMerge(final YangInstanceIdentifier path, final SchemaContext schemaContext,
-            final DOMDataTreeWriteTransaction writeTx) {
+                                            final RestconfStrategy strategy) {
         final List<PathArgument> normalizedPathWithoutChildArgs = new ArrayList<>();
         YangInstanceIdentifier rootNormalizedPath = null;
 
@@ -71,35 +65,31 @@ public final class TransactionUtil {
             return;
         }
 
-        Preconditions.checkArgument(rootNormalizedPath != null, "Empty path received");
-
         final NormalizedNode<?, ?> parentStructure = ImmutableNodes.fromInstanceId(schemaContext,
                 YangInstanceIdentifier.create(normalizedPathWithoutChildArgs));
-        writeTx.merge(LogicalDatastoreType.CONFIGURATION, rootNormalizedPath, parentStructure);
+        strategy.merge(LogicalDatastoreType.CONFIGURATION, rootNormalizedPath, parentStructure);
     }
 
     /**
      * Check if items already exists at specified {@code path}. Throws {@link RestconfDocumentedException} if
      * data does NOT already exists.
-     * @param transactionChain Transaction chain
-     * @param rwTransaction Transaction
-     * @param store Datastore
-     * @param path Path to be checked
+     *
+     * @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...)
      */
-    public static void checkItemExists(final DOMTransactionChain transactionChain,
-                                       final DOMDataTreeReadWriteTransaction rwTransaction,
+    public static void checkItemExists(final RestconfStrategy strategy,
                                        final LogicalDatastoreType store, final YangInstanceIdentifier path,
                                        final String operationType) {
-        final FluentFuture<Boolean> future = rwTransaction.exists(store, path);
+        final FluentFuture<Boolean> future = strategy.exists(store, path);
         final FutureDataFactory<Boolean> response = new FutureDataFactory<>();
 
         FutureCallbackTx.addCallback(future, operationType, response);
 
         if (!response.result) {
             // close transaction
-            rwTransaction.cancel();
-            transactionChain.close();
+            strategy.cancel();
             // throw error
             LOG.trace("Operation via Restconf was not executed because data at {} does not exist", path);
             throw new RestconfDocumentedException(
@@ -110,25 +100,23 @@ public final class TransactionUtil {
     /**
      * Check if items do NOT already exists at specified {@code path}. Throws {@link RestconfDocumentedException} if
      * data already exists.
-     * @param transactionChain Transaction chain
-     * @param rwTransaction Transaction
-     * @param store Datastore
-     * @param path Path to be checked
+     *
+     * @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...)
      */
-    public static void checkItemDoesNotExists(final DOMTransactionChain transactionChain,
-                                              final DOMDataTreeReadWriteTransaction rwTransaction,
+    public static void checkItemDoesNotExists(final RestconfStrategy strategy,
                                               final LogicalDatastoreType store, final YangInstanceIdentifier path,
                                               final String operationType) {
-        final FluentFuture<Boolean> future = rwTransaction.exists(store, path);
+        final FluentFuture<Boolean> future = strategy.exists(store, path);
         final FutureDataFactory<Boolean> response = new FutureDataFactory<>();
 
         FutureCallbackTx.addCallback(future, operationType, response);
 
         if (response.result) {
             // close transaction
-            rwTransaction.cancel();
-            transactionChain.close();
+            strategy.cancel();
             // throw error
             LOG.trace("Operation via Restconf was not executed because data at {} already exists", path);
             throw new RestconfDocumentedException(
index c99d2b7fb843cfa45199385b5649460d2b2e61f1..c0dc6fada2e6680ee67b6079d52df3da203452ab 100644 (file)
@@ -51,6 +51,7 @@ import org.opendaylight.mdsal.dom.api.DOMMountPointService;
 import org.opendaylight.mdsal.dom.api.DOMSchemaService;
 import org.opendaylight.mdsal.dom.api.DOMTransactionChain;
 import org.opendaylight.mdsal.dom.api.DOMTransactionChainListener;
+import org.opendaylight.netconf.dom.api.NetconfDataTreeService;
 import org.opendaylight.restconf.common.context.InstanceIdentifierContext;
 import org.opendaylight.restconf.common.context.NormalizedNodeContext;
 import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
@@ -63,6 +64,9 @@ import org.opendaylight.restconf.nb.rfc8040.handlers.DOMMountPointServiceHandler
 import org.opendaylight.restconf.nb.rfc8040.handlers.SchemaContextHandler;
 import org.opendaylight.restconf.nb.rfc8040.handlers.TransactionChainHandler;
 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.NetconfRestconfStrategy;
+import org.opendaylight.restconf.nb.rfc8040.rests.transactions.RestconfStrategy;
 import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
@@ -119,6 +123,8 @@ public class RestconfDataServiceImplTest {
     @Mock
     private DOMDataBroker mountDataBroker;
     @Mock
+    private NetconfDataTreeService netconfService;
+    @Mock
     private ActionServiceHandler actionServiceHandler;
     @Mock
     private DOMTransactionChain mountTransactionChain;
@@ -544,4 +550,17 @@ public class RestconfDataServiceImplTest {
         final String errorMessage = status.getEditCollection().get(2).getEditErrors().get(0).getErrorMessage();
         assertEquals("Data does not exist", errorMessage);
     }
+
+    @Test
+    public void testGetRestconfStrategy() {
+        final InstanceIdentifierContext<? extends SchemaNode> iidContext = new InstanceIdentifierContext<>(
+                this.iidBase, this.schemaNode, this.mountPoint, this.contextRef);
+
+        RestconfStrategy restconfStrategy = this.dataService.getRestconfStrategy(iidContext, this.mountPoint);
+        assertTrue(restconfStrategy instanceof MdsalRestconfStrategy);
+
+        doReturn(Optional.of(this.netconfService)).when(this.mountPoint).getService(NetconfDataTreeService.class);
+        restconfStrategy = this.dataService.getRestconfStrategy(iidContext, this.mountPoint);
+        assertTrue(restconfStrategy instanceof NetconfRestconfStrategy);
+    }
 }
index b0fce39c4853728dc5d666f7a6c514814aef3d57..bc60219e4d09fe01003f3a20e9ad288e5cda9088 100644 (file)
@@ -9,9 +9,12 @@ package org.opendaylight.restconf.nb.rfc8040.rests.utils;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.fail;
+import static org.mockito.Mockito.mock;
 import static org.opendaylight.yangtools.util.concurrent.FluentFutures.immediateFalseFluentFuture;
+import static org.opendaylight.yangtools.util.concurrent.FluentFutures.immediateFluentFuture;
 import static org.opendaylight.yangtools.util.concurrent.FluentFutures.immediateTrueFluentFuture;
 
+import java.util.Optional;
 import javax.ws.rs.core.Response;
 import javax.ws.rs.core.Response.Status;
 import org.junit.Before;
@@ -24,13 +27,17 @@ import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
 import org.opendaylight.mdsal.dom.api.DOMDataBroker;
 import org.opendaylight.mdsal.dom.api.DOMDataTreeReadWriteTransaction;
 import org.opendaylight.mdsal.dom.api.DOMTransactionChain;
+import org.opendaylight.netconf.dom.api.NetconfDataTreeService;
 import org.opendaylight.restconf.common.context.InstanceIdentifierContext;
 import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
 import org.opendaylight.restconf.common.errors.RestconfError.ErrorTag;
 import org.opendaylight.restconf.common.errors.RestconfError.ErrorType;
 import org.opendaylight.restconf.nb.rfc8040.handlers.TransactionChainHandler;
-import org.opendaylight.restconf.nb.rfc8040.rests.transactions.TransactionVarsWrapper;
+import org.opendaylight.restconf.nb.rfc8040.rests.transactions.MdsalRestconfStrategy;
+import org.opendaylight.restconf.nb.rfc8040.rests.transactions.NetconfRestconfStrategy;
+import org.opendaylight.restconf.nb.rfc8040.rests.transactions.RestconfStrategy;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
 
 public class DeleteDataTransactionUtilTest {
     @Mock
@@ -41,14 +48,17 @@ public class DeleteDataTransactionUtilTest {
     private DOMDataTreeReadWriteTransaction readWrite;
     @Mock
     private DOMDataBroker mockDataBroker;
+    @Mock
+    private NetconfDataTreeService netconfService;
 
     private TransactionChainHandler transactionChainHandler;
 
     @Before
-    public void init() throws Exception {
+    public void init() {
         MockitoAnnotations.initMocks(this);
         Mockito.when(this.transactionChain.newReadWriteTransaction()).thenReturn(this.readWrite);
         Mockito.doReturn(CommitInfo.emptyFluentFuture()).when(this.readWrite).commit();
+        Mockito.doReturn(CommitInfo.emptyFluentFuture()).when(this.netconfService).commit(Mockito.any());
         Mockito.when(this.context.getInstanceIdentifier()).thenReturn(YangInstanceIdentifier.empty());
 
         Mockito.doReturn(transactionChain).when(mockDataBroker).createTransactionChain(Mockito.any());
@@ -59,32 +69,41 @@ public class DeleteDataTransactionUtilTest {
      * Test of successful DELETE operation.
      */
     @Test
-    public void deleteData() throws Exception {
+    public void deleteData() {
         // assert that data to delete exists
         Mockito.when(this.transactionChain.newReadWriteTransaction().exists(LogicalDatastoreType.CONFIGURATION,
                 YangInstanceIdentifier.empty())).thenReturn(immediateTrueFluentFuture());
-
+        Mockito.when(this.netconfService.getConfig(YangInstanceIdentifier.empty()))
+                .thenReturn(immediateFluentFuture(Optional.of(mock(NormalizedNode.class))));
         // test
-        final Response response = DeleteDataTransactionUtil.deleteData(
-                new TransactionVarsWrapper(this.context, null, transactionChainHandler));
-
-        // assert success
-        assertEquals("Not expected response received", Status.NO_CONTENT.getStatusCode(), response.getStatus());
+        delete(new MdsalRestconfStrategy(this.context, transactionChainHandler));
+        delete(new NetconfRestconfStrategy(netconfService, this.context));
     }
 
     /**
      * Negative test for DELETE operation when data to delete does not exist. Error DATA_MISSING is expected.
      */
     @Test
-    public void deleteDataNegativeTest() throws Exception {
+    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(this.netconfService.getConfig(YangInstanceIdentifier.empty()))
+                .thenReturn(immediateFluentFuture(Optional.empty()));
         // test and assert error
+        deleteFail(new MdsalRestconfStrategy(this.context, transactionChainHandler));
+        deleteFail(new NetconfRestconfStrategy(netconfService, this.context));
+    }
+
+    private void delete(final RestconfStrategy strategy) {
+        final Response response = DeleteDataTransactionUtil.deleteData(strategy);
+        // assert success
+        assertEquals("Not expected response received", Status.NO_CONTENT.getStatusCode(), response.getStatus());
+    }
+
+    private void deleteFail(final RestconfStrategy strategy) {
         try {
-            DeleteDataTransactionUtil.deleteData(new TransactionVarsWrapper(this.context, null,
-                    transactionChainHandler));
+            DeleteDataTransactionUtil.deleteData(strategy);
             fail("Delete operation should fail due to missing data");
         } catch (final RestconfDocumentedException e) {
             assertEquals(ErrorType.PROTOCOL, e.getErrors().get(0).getErrorType());
index 2de9591b8bd1c645d404932c17cab69cdcc72fbd..0d32951f30beb768af6dc3eda367035133766ed4 100644 (file)
@@ -12,6 +12,7 @@ import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
 import static org.mockito.MockitoAnnotations.initMocks;
 import static org.opendaylight.restconf.common.patch.PatchEditOperation.CREATE;
 import static org.opendaylight.restconf.common.patch.PatchEditOperation.DELETE;
@@ -19,18 +20,22 @@ import static org.opendaylight.restconf.common.patch.PatchEditOperation.MERGE;
 import static org.opendaylight.restconf.common.patch.PatchEditOperation.REMOVE;
 import static org.opendaylight.restconf.common.patch.PatchEditOperation.REPLACE;
 import static org.opendaylight.yangtools.util.concurrent.FluentFutures.immediateFalseFluentFuture;
+import static org.opendaylight.yangtools.util.concurrent.FluentFutures.immediateFluentFuture;
 import static org.opendaylight.yangtools.util.concurrent.FluentFutures.immediateTrueFluentFuture;
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Optional;
 import org.junit.Before;
 import org.junit.Test;
 import org.mockito.Mock;
+import org.mockito.Mockito;
 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.DOMDataTreeReadWriteTransaction;
 import org.opendaylight.mdsal.dom.api.DOMTransactionChain;
+import org.opendaylight.netconf.dom.api.NetconfDataTreeService;
 import org.opendaylight.restconf.common.context.InstanceIdentifierContext;
 import org.opendaylight.restconf.common.errors.RestconfError;
 import org.opendaylight.restconf.common.patch.PatchContext;
@@ -39,7 +44,9 @@ import org.opendaylight.restconf.common.patch.PatchStatusContext;
 import org.opendaylight.restconf.common.patch.PatchStatusEntity;
 import org.opendaylight.restconf.nb.rfc8040.TestRestconfUtils;
 import org.opendaylight.restconf.nb.rfc8040.handlers.TransactionChainHandler;
-import org.opendaylight.restconf.nb.rfc8040.rests.transactions.TransactionVarsWrapper;
+import org.opendaylight.restconf.nb.rfc8040.rests.transactions.MdsalRestconfStrategy;
+import org.opendaylight.restconf.nb.rfc8040.rests.transactions.NetconfRestconfStrategy;
+import org.opendaylight.restconf.nb.rfc8040.rests.transactions.RestconfStrategy;
 import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
@@ -48,23 +55,22 @@ import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
 import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
 import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
 import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
 import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
 import org.opendaylight.yangtools.yang.model.api.SchemaNode;
 import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils;
 
 public class PatchDataTransactionUtilTest {
-
     private static final String PATH_FOR_NEW_SCHEMA_CONTEXT = "/jukebox";
-
     @Mock
     private DOMTransactionChain transactionChain;
-
     @Mock
     private DOMDataTreeReadWriteTransaction rwTransaction;
-
     @Mock
     private DOMDataBroker mockDataBroker;
+    @Mock
+    private NetconfDataTreeService netconfService;
 
     private TransactionChainHandler transactionChainHandler;
     private EffectiveModelContext refSchemaCtx;
@@ -165,6 +171,7 @@ public class PatchDataTransactionUtilTest {
         /* Mocks */
         doReturn(this.rwTransaction).when(this.transactionChain).newReadWriteTransaction();
         doReturn(CommitInfo.emptyFluentFuture()).when(this.rwTransaction).commit();
+        doReturn(CommitInfo.emptyFluentFuture()).when(this.netconfService).commit(Mockito.any());
     }
 
     @Test
@@ -185,22 +192,21 @@ public class PatchDataTransactionUtilTest {
         final InstanceIdentifierContext<? extends SchemaNode> iidContext =
                 new InstanceIdentifierContext<>(this.instanceIdMerge, null, null, this.refSchemaCtx);
         final PatchContext patchContext = new PatchContext(iidContext, entities, "patchRMRm");
-        final TransactionVarsWrapper wrapper = new TransactionVarsWrapper(iidContext, null, transactionChainHandler);
-        final PatchStatusContext patchStatusContext =
-                PatchDataTransactionUtil.patchData(patchContext, wrapper, this.refSchemaCtx);
 
-        for (final PatchStatusEntity entity : patchStatusContext.getEditCollection()) {
-            assertTrue(entity.isOk());
-        }
-        assertTrue(patchStatusContext.isOk());
+        patch(patchContext, new MdsalRestconfStrategy(iidContext, transactionChainHandler), false);
+        patch(patchContext, new NetconfRestconfStrategy(netconfService, iidContext), false);
     }
 
     @Test
-    public void testPatchDataCreateAndDelete() throws Exception {
+    public void testPatchDataCreateAndDelete() {
         doReturn(immediateFalseFluentFuture()).when(this.rwTransaction).exists(LogicalDatastoreType.CONFIGURATION,
             this.instanceIdContainer);
+        Mockito.when(this.netconfService.getConfig(this.instanceIdContainer))
+                .thenReturn(immediateFluentFuture(Optional.empty()));
         doReturn(immediateTrueFluentFuture()).when(this.rwTransaction).exists(LogicalDatastoreType.CONFIGURATION,
             this.targetNodeForCreateAndDelete);
+        Mockito.when(this.netconfService.getConfig(this.targetNodeForCreateAndDelete))
+                .thenReturn(immediateFluentFuture(Optional.of(mock(NormalizedNode.class))));
 
         final PatchEntity entityCreate =
                 new PatchEntity("edit1", CREATE, this.instanceIdContainer, this.buildBaseContainerForTests);
@@ -214,20 +220,16 @@ public class PatchDataTransactionUtilTest {
         final InstanceIdentifierContext<? extends SchemaNode> iidContext =
                 new InstanceIdentifierContext<>(this.instanceIdCreateAndDelete, null, null, this.refSchemaCtx);
         final PatchContext patchContext = new PatchContext(iidContext, entities, "patchCD");
-        final TransactionVarsWrapper wrapper = new TransactionVarsWrapper(iidContext, null, transactionChainHandler);
-        final PatchStatusContext patchStatusContext =
-                PatchDataTransactionUtil.patchData(patchContext, wrapper, this.refSchemaCtx);
-
-        for (final PatchStatusEntity entity : patchStatusContext.getEditCollection()) {
-            assertTrue("Edit " + entity.getEditId() + " failed", entity.isOk());
-        }
-        assertTrue(patchStatusContext.isOk());
+        patch(patchContext, new MdsalRestconfStrategy(iidContext, transactionChainHandler), true);
+        patch(patchContext, new NetconfRestconfStrategy(netconfService, iidContext), true);
     }
 
     @Test
     public void deleteNonexistentDataTest() {
         doReturn(immediateFalseFluentFuture()).when(this.rwTransaction).exists(LogicalDatastoreType.CONFIGURATION,
             this.targetNodeForCreateAndDelete);
+        Mockito.when(this.netconfService.getConfig(this.targetNodeForCreateAndDelete))
+                .thenReturn(immediateFluentFuture(Optional.empty()));
 
         final PatchEntity entityDelete = new PatchEntity("edit", DELETE, this.targetNodeForCreateAndDelete);
         final List<PatchEntity> entities = new ArrayList<>();
@@ -237,19 +239,12 @@ public class PatchDataTransactionUtilTest {
         final InstanceIdentifierContext<? extends SchemaNode> iidContext =
                 new InstanceIdentifierContext<>(this.instanceIdCreateAndDelete, null, null, this.refSchemaCtx);
         final PatchContext patchContext = new PatchContext(iidContext, entities, "patchD");
-        final TransactionVarsWrapper wrapper = new TransactionVarsWrapper(iidContext, null, transactionChainHandler);
-        final PatchStatusContext patchStatusContext =
-                PatchDataTransactionUtil.patchData(patchContext, wrapper, this.refSchemaCtx);
-
-        assertFalse(patchStatusContext.isOk());
-        assertEquals(RestconfError.ErrorType.PROTOCOL,
-                patchStatusContext.getEditCollection().get(0).getEditErrors().get(0).getErrorType());
-        assertEquals(RestconfError.ErrorTag.DATA_MISSING,
-                patchStatusContext.getEditCollection().get(0).getEditErrors().get(0).getErrorTag());
+        delete(patchContext, new MdsalRestconfStrategy(iidContext, transactionChainHandler));
+        delete(patchContext, new NetconfRestconfStrategy(netconfService, iidContext));
     }
 
     @Test
-    public void testPatchMergePutContainer() throws Exception {
+    public void testPatchMergePutContainer() {
         doReturn(immediateFalseFluentFuture()).doReturn(immediateTrueFluentFuture())
                 .when(this.rwTransaction).exists(LogicalDatastoreType.CONFIGURATION, this.targetNodeForCreateAndDelete);
 
@@ -262,13 +257,32 @@ public class PatchDataTransactionUtilTest {
         final InstanceIdentifierContext<? extends SchemaNode> iidContext =
                 new InstanceIdentifierContext<>(this.instanceIdCreateAndDelete, null, null, this.refSchemaCtx);
         final PatchContext patchContext = new PatchContext(iidContext, entities, "patchM");
-        final TransactionVarsWrapper wrapper = new TransactionVarsWrapper(iidContext, null, transactionChainHandler);
-        final PatchStatusContext patchStatusContext =
-                PatchDataTransactionUtil.patchData(patchContext, wrapper, this.refSchemaCtx);
+        patch(patchContext, new MdsalRestconfStrategy(iidContext, transactionChainHandler), false);
+        patch(patchContext, new NetconfRestconfStrategy(netconfService, iidContext), false);
+    }
 
+    private void patch(final PatchContext patchContext, final RestconfStrategy strategy,
+                       final boolean failed) {
+        final PatchStatusContext patchStatusContext =
+                PatchDataTransactionUtil.patchData(patchContext, strategy, this.refSchemaCtx);
         for (final PatchStatusEntity entity : patchStatusContext.getEditCollection()) {
-            assertTrue(entity.isOk());
+            if (failed) {
+                assertTrue("Edit " + entity.getEditId() + " failed", entity.isOk());
+            } else {
+                assertTrue(entity.isOk());
+            }
         }
         assertTrue(patchStatusContext.isOk());
     }
+
+    private void delete(PatchContext patchContext, RestconfStrategy strategy) {
+        final PatchStatusContext patchStatusContext =
+                PatchDataTransactionUtil.patchData(patchContext, strategy, this.refSchemaCtx);
+
+        assertFalse(patchStatusContext.isOk());
+        assertEquals(RestconfError.ErrorType.PROTOCOL,
+                patchStatusContext.getEditCollection().get(0).getEditErrors().get(0).getErrorType());
+        assertEquals(RestconfError.ErrorTag.DATA_MISSING,
+                patchStatusContext.getEditCollection().get(0).getEditErrors().get(0).getErrorTag());
+    }
 }
index 802c7fe5ea5e9bd5e3373eeff40667bef144742e..d80cfe764c4f348dc132bbe67643d8ef940186ab 100644 (file)
@@ -13,6 +13,7 @@ import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.verify;
 import static org.opendaylight.yangtools.util.concurrent.FluentFutures.immediateFalseFluentFuture;
 
+import java.util.Optional;
 import org.junit.Before;
 import org.junit.Test;
 import org.mockito.Mock;
@@ -25,11 +26,13 @@ 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.DOMTransactionChain;
+import org.opendaylight.netconf.dom.api.NetconfDataTreeService;
 import org.opendaylight.restconf.common.context.InstanceIdentifierContext;
 import org.opendaylight.restconf.common.context.NormalizedNodeContext;
 import org.opendaylight.restconf.nb.rfc8040.TestRestconfUtils;
 import org.opendaylight.restconf.nb.rfc8040.handlers.TransactionChainHandler;
-import org.opendaylight.restconf.nb.rfc8040.rests.transactions.TransactionVarsWrapper;
+import org.opendaylight.restconf.nb.rfc8040.rests.transactions.MdsalRestconfStrategy;
+import org.opendaylight.restconf.nb.rfc8040.rests.transactions.NetconfRestconfStrategy;
 import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
@@ -45,9 +48,7 @@ import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
 import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils;
 
 public class PlainPatchDataTransactionUtilTest {
-
     private static final String PATH_FOR_NEW_SCHEMA_CONTEXT = "/jukebox";
-
     @Mock
     private DOMTransactionChain transactionChain;
     @Mock
@@ -58,6 +59,8 @@ public class PlainPatchDataTransactionUtilTest {
     private DOMDataTreeWriteTransaction write;
     @Mock
     private DOMDataBroker mockDataBroker;
+    @Mock
+    private NetconfDataTreeService netconfService;
 
     private TransactionChainHandler transactionChainHandler;
     private LeafNode<?> leafGap;
@@ -169,13 +172,19 @@ public class PlainPatchDataTransactionUtilTest {
         doNothing().when(this.readWrite).put(LogicalDatastoreType.CONFIGURATION,
                 payload.getInstanceIdentifierContext().getInstanceIdentifier(), payload.getData());
         doReturn(CommitInfo.emptyFluentFuture()).when(this.readWrite).commit();
+        doReturn(CommitInfo.emptyFluentFuture()).when(this.netconfService).commit(Mockito.any());
 
         PlainPatchDataTransactionUtil.patchData(payload,
-                new TransactionVarsWrapper(payload.getInstanceIdentifierContext(), null, transactionChainHandler),
+                new MdsalRestconfStrategy(iidContext, transactionChainHandler),
                 this.schema);
-
         verify(this.readWrite).merge(LogicalDatastoreType.CONFIGURATION,
                 payload.getInstanceIdentifierContext().getInstanceIdentifier(), payload.getData());
+
+        PlainPatchDataTransactionUtil.patchData(payload,
+                new NetconfRestconfStrategy(netconfService, iidContext),
+                this.schema);
+        verify(this.netconfService).merge(LogicalDatastoreType.CONFIGURATION,
+                payload.getInstanceIdentifierContext().getInstanceIdentifier(), payload.getData(), Optional.empty());
     }
 
     @Test
@@ -192,13 +201,17 @@ public class PlainPatchDataTransactionUtilTest {
         doNothing().when(this.readWrite).put(LogicalDatastoreType.CONFIGURATION,
                 payload.getInstanceIdentifierContext().getInstanceIdentifier(), payload.getData());
         doReturn(CommitInfo.emptyFluentFuture()).when(this.readWrite).commit();
+        doReturn(CommitInfo.emptyFluentFuture()).when(this.netconfService).commit(Mockito.any());
 
-        PlainPatchDataTransactionUtil.patchData(payload,
-                new TransactionVarsWrapper(payload.getInstanceIdentifierContext(), null, transactionChainHandler),
+        PlainPatchDataTransactionUtil.patchData(payload, new MdsalRestconfStrategy(iidContext, transactionChainHandler),
                 this.schema);
-
         verify(this.readWrite).merge(LogicalDatastoreType.CONFIGURATION,
                 payload.getInstanceIdentifierContext().getInstanceIdentifier(), payload.getData());
+
+        PlainPatchDataTransactionUtil.patchData(payload, new NetconfRestconfStrategy(netconfService, iidContext),
+                this.schema);
+        verify(this.netconfService).merge(LogicalDatastoreType.CONFIGURATION,
+                payload.getInstanceIdentifierContext().getInstanceIdentifier(), payload.getData(), Optional.empty());
     }
 
     @Test
@@ -215,11 +228,16 @@ public class PlainPatchDataTransactionUtilTest {
         doNothing().when(this.readWrite).put(LogicalDatastoreType.CONFIGURATION,
                 payload.getInstanceIdentifierContext().getInstanceIdentifier(), payload.getData());
         doReturn(CommitInfo.emptyFluentFuture()).when(this.readWrite).commit();
+        doReturn(CommitInfo.emptyFluentFuture()).when(this.netconfService).commit(Mockito.any());
 
         PlainPatchDataTransactionUtil.patchData(payload,
-                new TransactionVarsWrapper(payload.getInstanceIdentifierContext(), null, transactionChainHandler),
+                new MdsalRestconfStrategy(iidContext, transactionChainHandler),
                 this.schema);
-
         verify(this.readWrite).merge(LogicalDatastoreType.CONFIGURATION, this.iidJukebox, payload.getData());
+
+        PlainPatchDataTransactionUtil.patchData(payload, new NetconfRestconfStrategy(netconfService, iidContext),
+                this.schema);
+        verify(this.netconfService).merge(LogicalDatastoreType.CONFIGURATION, this.iidJukebox, payload.getData(),
+                Optional.empty());
     }
 }
index 7bd431caa4c958f83d22cc211476186f9b27aa15..fe22aefdab8f2cc3cb856458002b154c06c55413 100644 (file)
@@ -17,10 +17,12 @@ import static org.mockito.Mockito.doReturn;
 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 static org.opendaylight.yangtools.util.concurrent.FluentFutures.immediateFluentFuture;
 
 import java.io.UnsupportedEncodingException;
 import java.net.URLDecoder;
 import java.util.Collection;
+import java.util.Optional;
 import javax.ws.rs.core.Response;
 import javax.ws.rs.core.UriBuilder;
 import javax.ws.rs.core.UriInfo;
@@ -34,14 +36,15 @@ import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
 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.DOMTransactionChain;
+import org.opendaylight.netconf.dom.api.NetconfDataTreeService;
 import org.opendaylight.restconf.common.context.InstanceIdentifierContext;
 import org.opendaylight.restconf.common.context.NormalizedNodeContext;
 import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
 import org.opendaylight.restconf.nb.rfc8040.TestRestconfUtils;
 import org.opendaylight.restconf.nb.rfc8040.handlers.TransactionChainHandler;
-import org.opendaylight.restconf.nb.rfc8040.rests.transactions.TransactionVarsWrapper;
+import org.opendaylight.restconf.nb.rfc8040.rests.transactions.MdsalRestconfStrategy;
+import org.opendaylight.restconf.nb.rfc8040.rests.transactions.NetconfRestconfStrategy;
 import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
@@ -67,13 +70,11 @@ public class PostDataTransactionUtilTest {
     @Mock
     private DOMDataTreeReadTransaction read;
     @Mock
-    private DOMDataTreeWriteTransaction write;
-    @Mock
     private UriInfo uriInfo;
     @Mock
-    private UriBuilder uriBuilder;
-    @Mock
     private DOMDataBroker mockDataBroker;
+    @Mock
+    private NetconfDataTreeService netconfService;
 
     private TransactionChainHandler transactionChainHandler;
     private ContainerNode buildBaseCont;
@@ -150,6 +151,7 @@ public class PostDataTransactionUtilTest {
 
         doReturn(immediateFalseFluentFuture()).when(this.readWrite).exists(LogicalDatastoreType.CONFIGURATION,
             this.iid2);
+        doReturn(immediateFluentFuture(Optional.empty())).when(this.netconfService).getConfig(this.iid2);
         final NodeIdentifier identifier =
                 ((ContainerNode) ((Collection<?>) payload.getData().getValue()).iterator().next()).getIdentifier();
         final YangInstanceIdentifier node =
@@ -157,14 +159,21 @@ public class PostDataTransactionUtilTest {
         doReturn(immediateFalseFluentFuture()).when(this.readWrite).exists(LogicalDatastoreType.CONFIGURATION, node);
         doNothing().when(this.readWrite).put(LogicalDatastoreType.CONFIGURATION, node, payload.getData());
         doReturn(CommitInfo.emptyFluentFuture()).when(this.readWrite).commit();
-        final TransactionVarsWrapper wrapper =
-                new TransactionVarsWrapper(payload.getInstanceIdentifierContext(), null, transactionChainHandler);
-        final Response response =
-                PostDataTransactionUtil.postData(this.uriInfo, payload, wrapper, this.schema, null, null);
+        doReturn(CommitInfo.emptyFluentFuture()).when(this.netconfService).commit(Mockito.any());
+
+        Response response = PostDataTransactionUtil.postData(this.uriInfo, payload,
+                        new MdsalRestconfStrategy(iidContext, transactionChainHandler), this.schema, null, null);
         assertEquals(201, response.getStatus());
         verify(this.readWrite).exists(LogicalDatastoreType.CONFIGURATION, this.iid2);
         verify(this.readWrite).put(LogicalDatastoreType.CONFIGURATION,
                 payload.getInstanceIdentifierContext().getInstanceIdentifier(), payload.getData());
+
+        response = PostDataTransactionUtil.postData(this.uriInfo, payload,
+                new NetconfRestconfStrategy(netconfService, iidContext), this.schema, null, null);
+        assertEquals(201, response.getStatus());
+        verify(this.netconfService).getConfig(this.iid2);
+        verify(this.netconfService).create(LogicalDatastoreType.CONFIGURATION,
+                payload.getInstanceIdentifierContext().getInstanceIdentifier(), payload.getData(), Optional.empty());
     }
 
     @Test
@@ -178,17 +187,27 @@ public class PostDataTransactionUtilTest {
         final YangInstanceIdentifier node =
                 payload.getInstanceIdentifierContext().getInstanceIdentifier().node(identifier);
         doReturn(immediateFalseFluentFuture()).when(this.readWrite).exists(LogicalDatastoreType.CONFIGURATION, node);
+        doReturn(immediateFluentFuture(Optional.empty())).when(this.netconfService).getConfig(node);
         doNothing().when(this.readWrite).put(LogicalDatastoreType.CONFIGURATION, node, payload.getData());
         doReturn(CommitInfo.emptyFluentFuture()).when(this.readWrite).commit();
-        final TransactionVarsWrapper wrapper =
-                new TransactionVarsWrapper(payload.getInstanceIdentifierContext(), null, transactionChainHandler);
-        final Response response =
-                PostDataTransactionUtil.postData(this.uriInfo, payload, wrapper, this.schema, null, null);
+        doReturn(CommitInfo.emptyFluentFuture()).when(this.netconfService).commit(Mockito.any());
+
+        Response response = PostDataTransactionUtil.postData(this.uriInfo, payload,
+                        new MdsalRestconfStrategy(iidContext, transactionChainHandler), this.schema, null, null);
         assertEquals(201, response.getStatus());
         assertThat(URLDecoder.decode(response.getLocation().toString(), "UTF-8"),
             containsString(identifier.getValue(identifier.keySet().iterator().next()).toString()));
         verify(this.readWrite).exists(LogicalDatastoreType.CONFIGURATION, node);
         verify(this.readWrite).put(LogicalDatastoreType.CONFIGURATION, node, data.getValue().iterator().next());
+
+        response = PostDataTransactionUtil.postData(this.uriInfo, payload,
+                new NetconfRestconfStrategy(netconfService, iidContext), this.schema, null, null);
+        assertEquals(201, response.getStatus());
+        assertThat(URLDecoder.decode(response.getLocation().toString(), "UTF-8"),
+                containsString(identifier.getValue(identifier.keySet().iterator().next()).toString()));
+        verify(this.netconfService).getConfig(node);
+        verify(this.netconfService).create(LogicalDatastoreType.CONFIGURATION, node, data.getValue().iterator().next(),
+                Optional.empty());
     }
 
     @Test
@@ -199,19 +218,21 @@ public class PostDataTransactionUtilTest {
 
         doReturn(immediateFalseFluentFuture()).when(this.readWrite).exists(LogicalDatastoreType.CONFIGURATION,
             this.iid2);
+        doReturn(immediateFluentFuture(Optional.empty())).when(this.netconfService).getConfig(this.iid2);
         final NodeIdentifier identifier =
                 ((ContainerNode) ((Collection<?>) payload.getData().getValue()).iterator().next()).getIdentifier();
         final YangInstanceIdentifier node =
                 payload.getInstanceIdentifierContext().getInstanceIdentifier().node(identifier);
         doReturn(immediateFalseFluentFuture()).when(this.readWrite).exists(LogicalDatastoreType.CONFIGURATION, node);
+        doReturn(immediateFluentFuture(Optional.empty())).when(this.netconfService).getConfig(node);
         doNothing().when(this.readWrite).put(LogicalDatastoreType.CONFIGURATION, node, payload.getData());
         final DOMException domException = new DOMException((short) 414, "Post request failed");
         doReturn(immediateFailedFluentFuture(domException)).when(this.readWrite).commit();
-        final TransactionVarsWrapper wrapper =
-                new TransactionVarsWrapper(payload.getInstanceIdentifierContext(), null, transactionChainHandler);
+        doReturn(immediateFailedFluentFuture(domException)).when(this.netconfService).commit(Mockito.any());
 
         try {
-            PostDataTransactionUtil.postData(this.uriInfo, payload, wrapper, this.schema, null, null);
+            PostDataTransactionUtil.postData(this.uriInfo, payload,
+                    new MdsalRestconfStrategy(iidContext, transactionChainHandler), this.schema, null, null);
             fail("Expected RestconfDocumentedException");
         } catch (final RestconfDocumentedException e) {
             assertEquals(1, e.getErrors().size());
@@ -221,5 +242,18 @@ public class PostDataTransactionUtilTest {
         verify(this.readWrite).exists(LogicalDatastoreType.CONFIGURATION, this.iid2);
         verify(this.readWrite).put(LogicalDatastoreType.CONFIGURATION,
                 payload.getInstanceIdentifierContext().getInstanceIdentifier(), payload.getData());
+
+        try {
+            PostDataTransactionUtil.postData(this.uriInfo, payload,
+                    new NetconfRestconfStrategy(netconfService, iidContext), this.schema, null, null);
+            fail("Expected RestconfDocumentedException");
+        } catch (final RestconfDocumentedException e) {
+            assertEquals(1, e.getErrors().size());
+            assertTrue(e.getErrors().get(0).getErrorInfo().contains(domException.getMessage()));
+        }
+
+        verify(this.netconfService).getConfig(this.iid2);
+        verify(this.netconfService).create(LogicalDatastoreType.CONFIGURATION,
+                payload.getInstanceIdentifierContext().getInstanceIdentifier(), payload.getData(), Optional.empty());
     }
 }
index 3f2afde7c90722d7c93299905aa2afc13cbaf05b..f5ca8f3a735ea6d7c4130224b85c4165a04b3ca9 100644 (file)
@@ -9,9 +9,12 @@ package org.opendaylight.restconf.nb.rfc8040.rests.utils;
 
 import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
 import static org.opendaylight.yangtools.util.concurrent.FluentFutures.immediateFalseFluentFuture;
+import static org.opendaylight.yangtools.util.concurrent.FluentFutures.immediateFluentFuture;
 
+import java.util.Optional;
 import org.junit.Before;
 import org.junit.Test;
 import org.mockito.Mock;
@@ -24,12 +27,14 @@ 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.DOMTransactionChain;
+import org.opendaylight.netconf.dom.api.NetconfDataTreeService;
 import org.opendaylight.restconf.common.context.InstanceIdentifierContext;
 import org.opendaylight.restconf.common.context.NormalizedNodeContext;
 import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
 import org.opendaylight.restconf.nb.rfc8040.TestRestconfUtils;
 import org.opendaylight.restconf.nb.rfc8040.handlers.TransactionChainHandler;
-import org.opendaylight.restconf.nb.rfc8040.rests.transactions.TransactionVarsWrapper;
+import org.opendaylight.restconf.nb.rfc8040.rests.transactions.MdsalRestconfStrategy;
+import org.opendaylight.restconf.nb.rfc8040.rests.transactions.NetconfRestconfStrategy;
 import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
@@ -38,6 +43,7 @@ import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
 import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
 import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
 import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
 import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
 import org.opendaylight.yangtools.yang.data.util.DataSchemaContextTree;
 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
@@ -45,9 +51,7 @@ import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
 import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils;
 
 public class PutDataTransactionUtilTest {
-
     private static final String PATH_FOR_NEW_SCHEMA_CONTEXT = "/jukebox";
-
     @Mock
     private DOMTransactionChain transactionChain;
     @Mock
@@ -58,6 +62,8 @@ public class PutDataTransactionUtilTest {
     private DOMDataTreeWriteTransaction write;
     @Mock
     private DOMDataBroker mockDataBroker;
+    @Mock
+    private NetconfDataTreeService netconfService;
 
     private TransactionChainHandler transactionChainHandler;
     private LeafNode<?> buildLeaf;
@@ -161,7 +167,7 @@ public class PutDataTransactionUtilTest {
     }
 
     @Test
-    public void testValidInputData() throws Exception {
+    public void testValidInputData() {
         final InstanceIdentifierContext<DataSchemaNode> iidContext =
                 new InstanceIdentifierContext<>(this.iid, this.schemaNode, null, this.schema);
         final NormalizedNodeContext payload = new NormalizedNodeContext(iidContext, this.buildLeaf);
@@ -169,7 +175,7 @@ public class PutDataTransactionUtilTest {
     }
 
     @Test
-    public void testValidTopLevelNodeName() throws Exception {
+    public void testValidTopLevelNodeName() {
         InstanceIdentifierContext<DataSchemaNode> iidContext =
                 new InstanceIdentifierContext<>(this.iid, this.schemaNode, null, this.schema);
         NormalizedNodeContext payload = new NormalizedNodeContext(iidContext, this.buildLeaf);
@@ -181,7 +187,7 @@ public class PutDataTransactionUtilTest {
     }
 
     @Test(expected = RestconfDocumentedException.class)
-    public void testValidTopLevelNodeNamePathEmpty() throws Exception {
+    public void testValidTopLevelNodeNamePathEmpty() {
         final InstanceIdentifierContext<DataSchemaNode> iidContext =
                 new InstanceIdentifierContext<>(this.iid, this.schemaNode, null, this.schema);
         final NormalizedNodeContext payload = new NormalizedNodeContext(iidContext, this.buildLeaf);
@@ -189,7 +195,7 @@ public class PutDataTransactionUtilTest {
     }
 
     @Test(expected = RestconfDocumentedException.class)
-    public void testValidTopLevelNodeNameWrongTopIdentifier() throws Exception {
+    public void testValidTopLevelNodeNameWrongTopIdentifier() {
         final InstanceIdentifierContext<DataSchemaNode> iidContext =
                 new InstanceIdentifierContext<>(this.iid, this.schemaNode, null, this.schema);
         final NormalizedNodeContext payload = new NormalizedNodeContext(iidContext, this.buildLeaf);
@@ -197,7 +203,7 @@ public class PutDataTransactionUtilTest {
     }
 
     @Test
-    public void testValidateListKeysEqualityInPayloadAndUri() throws Exception {
+    public void testValidateListKeysEqualityInPayloadAndUri() {
         final InstanceIdentifierContext<DataSchemaNode> iidContext =
                 new InstanceIdentifierContext<>(this.iid3, this.schemaNode3, null, this.schema);
         final NormalizedNodeContext payload = new NormalizedNodeContext(iidContext, this.buildListEntry);
@@ -205,7 +211,7 @@ public class PutDataTransactionUtilTest {
     }
 
     @Test
-    public void testPutContainerData() throws Exception {
+    public void testPutContainerData() {
         final InstanceIdentifierContext<DataSchemaNode> iidContext =
                 new InstanceIdentifierContext<>(this.iid2, this.schemaNode2, null, this.schema);
         final NormalizedNodeContext payload = new NormalizedNodeContext(iidContext, this.buildBaseCont);
@@ -220,8 +226,7 @@ public class PutDataTransactionUtilTest {
         doReturn(CommitInfo.emptyFluentFuture()).when(this.readWrite).commit();
 
         PutDataTransactionUtil.putData(payload, this.schema,
-                new TransactionVarsWrapper(payload.getInstanceIdentifierContext(), null, transactionChainHandler), null,
-                null);
+                new MdsalRestconfStrategy(iidContext, transactionChainHandler), null, null);
         verify(this.readWrite).exists(LogicalDatastoreType.CONFIGURATION,
                 payload.getInstanceIdentifierContext().getInstanceIdentifier());
         verify(this.readWrite).put(LogicalDatastoreType.CONFIGURATION,
@@ -229,7 +234,40 @@ public class PutDataTransactionUtilTest {
     }
 
     @Test
-    public void testPutleafData() throws Exception {
+    public void testPutCreateContainerData() {
+        final InstanceIdentifierContext<DataSchemaNode> iidContext =
+                new InstanceIdentifierContext<>(this.iid2, this.schemaNode2, null, this.schema);
+        final NormalizedNodeContext payload = new NormalizedNodeContext(iidContext, this.buildBaseCont);
+
+        doReturn(immediateFluentFuture(Optional.empty())).when(this.netconfService).getConfig(this.iid2);
+        doReturn(CommitInfo.emptyFluentFuture()).when(this.netconfService).commit(Mockito.any());
+
+        PutDataTransactionUtil.putData(payload, this.schema,
+                new NetconfRestconfStrategy(netconfService, iidContext), null, null);
+        verify(this.netconfService).getConfig(payload.getInstanceIdentifierContext().getInstanceIdentifier());
+        verify(this.netconfService).create(LogicalDatastoreType.CONFIGURATION,
+                payload.getInstanceIdentifierContext().getInstanceIdentifier(), payload.getData(), Optional.empty());
+    }
+
+    @Test
+    public void testPutReplaceContainerData() {
+        final InstanceIdentifierContext<DataSchemaNode> iidContext =
+                new InstanceIdentifierContext<>(this.iid2, this.schemaNode2, null, this.schema);
+        final NormalizedNodeContext payload = new NormalizedNodeContext(iidContext, this.buildBaseCont);
+
+        doReturn(immediateFluentFuture(Optional.of(mock(NormalizedNode.class))))
+                .when(this.netconfService).getConfig(this.iid2);
+        doReturn(CommitInfo.emptyFluentFuture()).when(this.netconfService).commit(Mockito.any());
+
+        PutDataTransactionUtil.putData(payload, this.schema,
+                new NetconfRestconfStrategy(netconfService, iidContext), null, null);
+        verify(this.netconfService).getConfig(payload.getInstanceIdentifierContext().getInstanceIdentifier());
+        verify(this.netconfService).replace(LogicalDatastoreType.CONFIGURATION,
+                payload.getInstanceIdentifierContext().getInstanceIdentifier(), payload.getData(), Optional.empty());
+    }
+
+    @Test
+    public void testPutLeafData() {
         final InstanceIdentifierContext<DataSchemaNode> iidContext =
                 new InstanceIdentifierContext<>(this.iid, this.schemaNode, null, this.schema);
         final NormalizedNodeContext payload = new NormalizedNodeContext(iidContext, this.buildLeaf);
@@ -244,8 +282,7 @@ public class PutDataTransactionUtilTest {
         doReturn(CommitInfo.emptyFluentFuture()).when(this.readWrite).commit();
 
         PutDataTransactionUtil.putData(payload, this.schema,
-                new TransactionVarsWrapper(payload.getInstanceIdentifierContext(), null, transactionChainHandler), null,
-                null);
+                new MdsalRestconfStrategy(iidContext, transactionChainHandler), null, null);
         verify(this.readWrite).exists(LogicalDatastoreType.CONFIGURATION,
                 payload.getInstanceIdentifierContext().getInstanceIdentifier());
         verify(this.readWrite).put(LogicalDatastoreType.CONFIGURATION,
@@ -253,7 +290,40 @@ public class PutDataTransactionUtilTest {
     }
 
     @Test
-    public void testPutListData() throws Exception {
+    public void testPutCreateLeafData() {
+        final InstanceIdentifierContext<DataSchemaNode> iidContext =
+                new InstanceIdentifierContext<>(this.iid, this.schemaNode, null, this.schema);
+        final NormalizedNodeContext payload = new NormalizedNodeContext(iidContext, this.buildLeaf);
+
+        doReturn(immediateFluentFuture(Optional.empty())).when(this.netconfService).getConfig(this.iid);
+        doReturn(CommitInfo.emptyFluentFuture()).when(this.netconfService).commit(Mockito.any());
+
+        PutDataTransactionUtil.putData(payload, this.schema,
+                new NetconfRestconfStrategy(netconfService, iidContext), null, null);
+        verify(this.netconfService).getConfig(payload.getInstanceIdentifierContext().getInstanceIdentifier());
+        verify(this.netconfService).create(LogicalDatastoreType.CONFIGURATION,
+                payload.getInstanceIdentifierContext().getInstanceIdentifier(), payload.getData(), Optional.empty());
+    }
+
+    @Test
+    public void testPutReplaceLeafData() {
+        final InstanceIdentifierContext<DataSchemaNode> iidContext =
+                new InstanceIdentifierContext<>(this.iid, this.schemaNode, null, this.schema);
+        final NormalizedNodeContext payload = new NormalizedNodeContext(iidContext, this.buildLeaf);
+
+        doReturn(immediateFluentFuture(Optional.of(mock(NormalizedNode.class))))
+                .when(this.netconfService).getConfig(this.iid);
+        doReturn(CommitInfo.emptyFluentFuture()).when(this.netconfService).commit(Mockito.any());
+
+        PutDataTransactionUtil.putData(payload, this.schema,
+                new NetconfRestconfStrategy(netconfService, iidContext), null, null);
+        verify(this.netconfService).getConfig(payload.getInstanceIdentifierContext().getInstanceIdentifier());
+        verify(this.netconfService).replace(LogicalDatastoreType.CONFIGURATION,
+                payload.getInstanceIdentifierContext().getInstanceIdentifier(), payload.getData(), Optional.empty());
+    }
+
+    @Test
+    public void testPutListData() {
         final InstanceIdentifierContext<DataSchemaNode> iidContext =
                 new InstanceIdentifierContext<>(this.iid2, this.schemaNode2, null, this.schema);
         final NormalizedNodeContext payload = new NormalizedNodeContext(iidContext, this.buildBaseContWithList);
@@ -267,9 +337,42 @@ public class PutDataTransactionUtilTest {
                 payload.getInstanceIdentifierContext().getInstanceIdentifier(), payload.getData());
         doReturn(CommitInfo.emptyFluentFuture()).when(this.readWrite).commit();
         PutDataTransactionUtil.putData(payload, this.schema,
-                new TransactionVarsWrapper(payload.getInstanceIdentifierContext(), null, transactionChainHandler), null,
-                null);
+                new MdsalRestconfStrategy(iidContext, transactionChainHandler), null, null);
         verify(this.readWrite).exists(LogicalDatastoreType.CONFIGURATION, this.iid2);
         verify(this.readWrite).put(LogicalDatastoreType.CONFIGURATION, this.iid2, payload.getData());
     }
+
+    @Test
+    public void testPutCreateListData() {
+        final InstanceIdentifierContext<DataSchemaNode> iidContext =
+                new InstanceIdentifierContext<>(this.iid2, this.schemaNode2, null, this.schema);
+        final NormalizedNodeContext payload = new NormalizedNodeContext(iidContext, this.buildBaseContWithList);
+
+        doReturn(immediateFluentFuture(Optional.empty())).when(this.netconfService)
+                .getConfig(this.iid2);
+        doReturn(CommitInfo.emptyFluentFuture()).when(this.netconfService).commit(Mockito.any());
+
+        PutDataTransactionUtil.putData(payload, this.schema,
+                new NetconfRestconfStrategy(netconfService, iidContext), null, null);
+        verify(this.netconfService).getConfig(this.iid2);
+        verify(this.netconfService).create(LogicalDatastoreType.CONFIGURATION, this.iid2,
+                payload.getData(), Optional.empty());
+    }
+
+    @Test
+    public void testPutReplaceListData() {
+        final InstanceIdentifierContext<DataSchemaNode> iidContext =
+                new InstanceIdentifierContext<>(this.iid2, this.schemaNode2, null, this.schema);
+        final NormalizedNodeContext payload = new NormalizedNodeContext(iidContext, this.buildBaseContWithList);
+
+        doReturn(immediateFluentFuture(Optional.of(mock(NormalizedNode.class)))).when(this.netconfService)
+                .getConfig(this.iid2);
+        doReturn(CommitInfo.emptyFluentFuture()).when(this.netconfService).commit(Mockito.any());
+
+        PutDataTransactionUtil.putData(payload, this.schema,
+                new NetconfRestconfStrategy(netconfService, iidContext), null, null);
+        verify(this.netconfService).getConfig(this.iid2);
+        verify(this.netconfService).replace(LogicalDatastoreType.CONFIGURATION, this.iid2,
+                payload.getData(), Optional.empty());
+    }
 }
index 995073f8605142cef2143150fa8219c2deafe66e..41103fd54af240c1add2044ba67b745f5e5571cb 100644 (file)
@@ -31,13 +31,16 @@ import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
 import org.opendaylight.mdsal.dom.api.DOMDataBroker;
 import org.opendaylight.mdsal.dom.api.DOMDataTreeReadTransaction;
 import org.opendaylight.mdsal.dom.api.DOMTransactionChain;
+import org.opendaylight.netconf.dom.api.NetconfDataTreeService;
 import org.opendaylight.restconf.common.context.InstanceIdentifierContext;
 import org.opendaylight.restconf.common.context.WriterParameters;
 import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
 import org.opendaylight.restconf.common.errors.RestconfError.ErrorTag;
 import org.opendaylight.restconf.common.errors.RestconfError.ErrorType;
 import org.opendaylight.restconf.nb.rfc8040.handlers.TransactionChainHandler;
-import org.opendaylight.restconf.nb.rfc8040.rests.transactions.TransactionVarsWrapper;
+import org.opendaylight.restconf.nb.rfc8040.rests.transactions.MdsalRestconfStrategy;
+import org.opendaylight.restconf.nb.rfc8040.rests.transactions.NetconfRestconfStrategy;
+import org.opendaylight.restconf.nb.rfc8040.rests.transactions.RestconfStrategy;
 import org.opendaylight.restconf.nb.rfc8040.rests.utils.RestconfDataServiceConstant.ReadData;
 import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
@@ -58,7 +61,10 @@ public class ReadDataTransactionUtilTest {
     private static final YangInstanceIdentifier.NodeIdentifier NODE_IDENTIFIER = new YangInstanceIdentifier
             .NodeIdentifier(QName.create("ns", "2016-02-28", "container"));
 
-    private TransactionVarsWrapper wrapper;
+    private RestconfStrategy mdsalStrategy;
+    private RestconfStrategy netconfStrategy;
+    @Mock
+    private NetconfDataTreeService netconfService;
     @Mock
     private DOMTransactionChain transactionChain;
     @Mock
@@ -88,17 +94,22 @@ public class ReadDataTransactionUtilTest {
 
         DOMDataBroker mockDataBroker = Mockito.mock(DOMDataBroker.class);
         Mockito.doReturn(transactionChain).when(mockDataBroker).createTransactionChain(Mockito.any());
-        wrapper = new TransactionVarsWrapper(this.context, null, new TransactionChainHandler(mockDataBroker));
+        mdsalStrategy = new MdsalRestconfStrategy(this.context, new TransactionChainHandler(mockDataBroker));
+        netconfStrategy = new NetconfRestconfStrategy(this.netconfService, this.context);
     }
 
     @Test
     public void readDataConfigTest() {
         doReturn(immediateFluentFuture(Optional.of(DATA.data3))).when(read)
                 .read(LogicalDatastoreType.CONFIGURATION, DATA.path);
+        doReturn(immediateFluentFuture(Optional.of(DATA.data3))).when(this.netconfService).getConfig(DATA.path);
         doReturn(DATA.path).when(context).getInstanceIdentifier();
         final String valueOfContent = RestconfDataServiceConstant.ReadData.CONFIG;
-        final NormalizedNode<?, ?> normalizedNode =
-                ReadDataTransactionUtil.readData(valueOfContent, wrapper, schemaContext);
+        NormalizedNode<?, ?> normalizedNode =
+                ReadDataTransactionUtil.readData(valueOfContent, mdsalStrategy, schemaContext);
+        assertEquals(DATA.data3, normalizedNode);
+
+        normalizedNode = ReadDataTransactionUtil.readData(valueOfContent, netconfStrategy, schemaContext);
         assertEquals(DATA.data3, normalizedNode);
     }
 
@@ -108,10 +119,16 @@ public class ReadDataTransactionUtilTest {
                 .read(LogicalDatastoreType.CONFIGURATION, DATA.path);
         doReturn(immediateFluentFuture(Optional.empty())).when(read)
                 .read(LogicalDatastoreType.OPERATIONAL, DATA.path);
+        doReturn(immediateFluentFuture(Optional.of(DATA.data3))).when(this.netconfService).getConfig(DATA.path);
+        doReturn(immediateFluentFuture(Optional.empty())).when(this.netconfService).get(DATA.path);
         doReturn(DATA.path).when(context).getInstanceIdentifier();
         final String valueOfContent = RestconfDataServiceConstant.ReadData.ALL;
-        final NormalizedNode<?, ?> normalizedNode =
-                ReadDataTransactionUtil.readData(valueOfContent, wrapper, schemaContext);
+        NormalizedNode<?, ?> normalizedNode =
+                ReadDataTransactionUtil.readData(valueOfContent, mdsalStrategy, schemaContext);
+        assertEquals(DATA.data3, normalizedNode);
+
+        normalizedNode =
+                ReadDataTransactionUtil.readData(valueOfContent, netconfStrategy, schemaContext);
         assertEquals(DATA.data3, normalizedNode);
     }
 
@@ -121,10 +138,16 @@ public class ReadDataTransactionUtilTest {
                 .read(LogicalDatastoreType.OPERATIONAL, DATA.path2);
         doReturn(immediateFluentFuture(Optional.empty())).when(read)
                 .read(LogicalDatastoreType.CONFIGURATION, DATA.path2);
+        doReturn(immediateFluentFuture(Optional.of(DATA.data2))).when(this.netconfService).get(DATA.path2);
+        doReturn(immediateFluentFuture(Optional.empty())).when(this.netconfService).getConfig(DATA.path2);
         doReturn(DATA.path2).when(context).getInstanceIdentifier();
         final String valueOfContent = RestconfDataServiceConstant.ReadData.ALL;
-        final NormalizedNode<?, ?> normalizedNode =
-                ReadDataTransactionUtil.readData(valueOfContent, wrapper, schemaContext);
+        NormalizedNode<?, ?> normalizedNode =
+                ReadDataTransactionUtil.readData(valueOfContent, mdsalStrategy, schemaContext);
+        assertEquals(DATA.data2, normalizedNode);
+
+        normalizedNode =
+                ReadDataTransactionUtil.readData(valueOfContent, netconfStrategy, schemaContext);
         assertEquals(DATA.data2, normalizedNode);
     }
 
@@ -132,10 +155,15 @@ public class ReadDataTransactionUtilTest {
     public void readDataNonConfigTest() {
         doReturn(immediateFluentFuture(Optional.of(DATA.data2))).when(read)
                 .read(LogicalDatastoreType.OPERATIONAL, DATA.path2);
+        doReturn(immediateFluentFuture(Optional.of(DATA.data2))).when(this.netconfService).get(DATA.path2);
         doReturn(DATA.path2).when(context).getInstanceIdentifier();
         final String valueOfContent = RestconfDataServiceConstant.ReadData.NONCONFIG;
-        final NormalizedNode<?, ?> normalizedNode =
-                ReadDataTransactionUtil.readData(valueOfContent, wrapper, schemaContext);
+        NormalizedNode<?, ?> normalizedNode =
+                ReadDataTransactionUtil.readData(valueOfContent, mdsalStrategy, schemaContext);
+        assertEquals(DATA.data2, normalizedNode);
+
+        normalizedNode =
+                ReadDataTransactionUtil.readData(valueOfContent, netconfStrategy, schemaContext);
         assertEquals(DATA.data2, normalizedNode);
     }
 
@@ -145,16 +173,22 @@ public class ReadDataTransactionUtilTest {
                 .read(LogicalDatastoreType.CONFIGURATION, DATA.path);
         doReturn(immediateFluentFuture(Optional.of(DATA.data4))).when(read)
                 .read(LogicalDatastoreType.OPERATIONAL, DATA.path);
+        doReturn(immediateFluentFuture(Optional.of(DATA.data3))).when(this.netconfService).getConfig(DATA.path);
+        doReturn(immediateFluentFuture(Optional.of(DATA.data4))).when(this.netconfService).get(DATA.path);
         doReturn(DATA.path).when(context).getInstanceIdentifier();
         final String valueOfContent = RestconfDataServiceConstant.ReadData.ALL;
-        final NormalizedNode<?, ?> normalizedNode =
-                ReadDataTransactionUtil.readData(valueOfContent, wrapper, schemaContext);
         final ContainerNode checkingData = Builders
                 .containerBuilder()
                 .withNodeIdentifier(NODE_IDENTIFIER)
                 .withChild(DATA.contentLeaf)
                 .withChild(DATA.contentLeaf2)
                 .build();
+        NormalizedNode<?, ?> normalizedNode =
+                ReadDataTransactionUtil.readData(valueOfContent, mdsalStrategy, schemaContext);
+        assertEquals(checkingData, normalizedNode);
+
+        normalizedNode =
+                ReadDataTransactionUtil.readData(valueOfContent, netconfStrategy, schemaContext);
         assertEquals(checkingData, normalizedNode);
     }
 
@@ -164,15 +198,21 @@ public class ReadDataTransactionUtilTest {
                 .read(LogicalDatastoreType.CONFIGURATION, DATA.path);
         doReturn(immediateFluentFuture(Optional.of(DATA.data4))).when(read)
                 .read(LogicalDatastoreType.OPERATIONAL, DATA.path);
+        doReturn(immediateFluentFuture(Optional.of(DATA.data3))).when(this.netconfService).getConfig(DATA.path);
+        doReturn(immediateFluentFuture(Optional.of(DATA.data4))).when(this.netconfService).get(DATA.path);
         doReturn(DATA.path).when(context).getInstanceIdentifier();
-        final NormalizedNode<?, ?> normalizedNode = ReadDataTransactionUtil.readData(
-                RestconfDataServiceConstant.ReadData.ALL, wrapper, schemaContext);
         final ContainerNode checkingData = Builders
                 .containerBuilder()
                 .withNodeIdentifier(NODE_IDENTIFIER)
                 .withChild(DATA.contentLeaf)
                 .withChild(DATA.contentLeaf2)
                 .build();
+        NormalizedNode<?, ?> normalizedNode = ReadDataTransactionUtil.readData(
+                RestconfDataServiceConstant.ReadData.ALL, mdsalStrategy, schemaContext);
+        assertEquals(checkingData, normalizedNode);
+
+        normalizedNode = ReadDataTransactionUtil.readData(
+                RestconfDataServiceConstant.ReadData.ALL, netconfStrategy, schemaContext);
         assertEquals(checkingData, normalizedNode);
     }
 
@@ -182,15 +222,21 @@ public class ReadDataTransactionUtilTest {
                 .read(LogicalDatastoreType.OPERATIONAL, DATA.path3);
         doReturn(immediateFluentFuture(Optional.of(DATA.listData2))).when(read)
                 .read(LogicalDatastoreType.CONFIGURATION, DATA.path3);
+        doReturn(immediateFluentFuture(Optional.of(DATA.listData))).when(this.netconfService).get(DATA.path3);
+        doReturn(immediateFluentFuture(Optional.of(DATA.listData2))).when(this.netconfService).getConfig(DATA.path3);
         doReturn(DATA.path3).when(context).getInstanceIdentifier();
         final String valueOfContent = RestconfDataServiceConstant.ReadData.ALL;
-        final NormalizedNode<?, ?> normalizedNode =
-                ReadDataTransactionUtil.readData(valueOfContent, wrapper, schemaContext);
         final MapNode checkingData = Builders
                 .mapBuilder()
                 .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(QName.create("ns", "2016-02-28", "list")))
                 .withChild(DATA.checkData)
                 .build();
+        NormalizedNode<?, ?> normalizedNode =
+                ReadDataTransactionUtil.readData(valueOfContent, mdsalStrategy, schemaContext);
+        assertEquals(checkingData, normalizedNode);
+
+        normalizedNode =
+                ReadDataTransactionUtil.readData(valueOfContent, netconfStrategy, schemaContext);
         assertEquals(checkingData, normalizedNode);
     }
 
@@ -200,14 +246,19 @@ public class ReadDataTransactionUtilTest {
                 .read(LogicalDatastoreType.OPERATIONAL, DATA.path3);
         doReturn(immediateFluentFuture(Optional.of(DATA.orderedMapNode2))).when(read)
                 .read(LogicalDatastoreType.CONFIGURATION, DATA.path3);
+        doReturn(immediateFluentFuture(Optional.of(DATA.orderedMapNode1))).when(this.netconfService).get(DATA.path3);
+        doReturn(immediateFluentFuture(Optional.of(DATA.orderedMapNode2))).when(this.netconfService)
+                .getConfig(DATA.path3);
         doReturn(DATA.path3).when(context).getInstanceIdentifier();
-
-        final NormalizedNode<?, ?> normalizedNode =
-                ReadDataTransactionUtil.readData(RestconfDataServiceConstant.ReadData.ALL, wrapper, schemaContext);
-
         final MapNode expectedData = Builders.orderedMapBuilder()
                 .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(DATA.listQname)).withChild(DATA.checkData)
                 .build();
+        NormalizedNode<?, ?> normalizedNode = ReadDataTransactionUtil
+                .readData(RestconfDataServiceConstant.ReadData.ALL, mdsalStrategy, schemaContext);
+        assertEquals(expectedData, normalizedNode);
+
+        normalizedNode = ReadDataTransactionUtil
+                .readData(RestconfDataServiceConstant.ReadData.ALL, netconfStrategy, schemaContext);
         assertEquals(expectedData, normalizedNode);
     }
 
@@ -217,17 +268,22 @@ public class ReadDataTransactionUtilTest {
                 .read(LogicalDatastoreType.OPERATIONAL, DATA.path3);
         doReturn(immediateFluentFuture(Optional.of(DATA.unkeyedListNode2))).when(read)
                 .read(LogicalDatastoreType.CONFIGURATION, DATA.path3);
+        doReturn(immediateFluentFuture(Optional.of(DATA.unkeyedListNode1))).when(this.netconfService).get(DATA.path3);
+        doReturn(immediateFluentFuture(Optional.of(DATA.unkeyedListNode2))).when(this.netconfService)
+                .getConfig(DATA.path3);
         doReturn(DATA.path3).when(context).getInstanceIdentifier();
-
-        final NormalizedNode<?, ?> normalizedNode =
-                ReadDataTransactionUtil.readData(RestconfDataServiceConstant.ReadData.ALL, wrapper, schemaContext);
-
         final UnkeyedListNode expectedData = Builders.unkeyedListBuilder()
                 .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(DATA.listQname))
                 .withChild(Builders.unkeyedListEntryBuilder().withNodeIdentifier(
                         new YangInstanceIdentifier.NodeIdentifier(DATA.listQname))
                         .withChild(DATA.unkeyedListEntryNode1.getValue().iterator().next())
                         .withChild(DATA.unkeyedListEntryNode2.getValue().iterator().next()).build()).build();
+        NormalizedNode<?, ?> normalizedNode = ReadDataTransactionUtil
+                .readData(RestconfDataServiceConstant.ReadData.ALL, mdsalStrategy, schemaContext);
+        assertEquals(expectedData, normalizedNode);
+
+        normalizedNode = ReadDataTransactionUtil
+                .readData(RestconfDataServiceConstant.ReadData.ALL, netconfStrategy, schemaContext);
         assertEquals(expectedData, normalizedNode);
     }
 
@@ -237,15 +293,21 @@ public class ReadDataTransactionUtilTest {
                 .read(LogicalDatastoreType.OPERATIONAL, DATA.leafSetNodePath);
         doReturn(immediateFluentFuture(Optional.of(DATA.leafSetNode2))).when(read)
                 .read(LogicalDatastoreType.CONFIGURATION, DATA.leafSetNodePath);
+        doReturn(immediateFluentFuture(Optional.of(DATA.leafSetNode1))).when(this.netconfService)
+                .get(DATA.leafSetNodePath);
+        doReturn(immediateFluentFuture(Optional.of(DATA.leafSetNode2))).when(this.netconfService)
+                .getConfig(DATA.leafSetNodePath);
         doReturn(DATA.leafSetNodePath).when(context).getInstanceIdentifier();
-
-        final NormalizedNode<?, ?> normalizedNode =
-                ReadDataTransactionUtil.readData(RestconfDataServiceConstant.ReadData.ALL, wrapper, schemaContext);
-
         final LeafSetNode<String> expectedData = Builders.<String>leafSetBuilder().withNodeIdentifier(
                 new YangInstanceIdentifier.NodeIdentifier(DATA.leafListQname)).withValue(
                         ImmutableList.<LeafSetEntryNode<String>>builder().addAll(DATA.leafSetNode1.getValue())
                         .addAll(DATA.leafSetNode2.getValue()).build()).build();
+        NormalizedNode<?, ?> normalizedNode = ReadDataTransactionUtil
+                .readData(RestconfDataServiceConstant.ReadData.ALL, mdsalStrategy, schemaContext);
+        assertEquals(expectedData, normalizedNode);
+
+        normalizedNode = ReadDataTransactionUtil
+                .readData(RestconfDataServiceConstant.ReadData.ALL, netconfStrategy, schemaContext);
         assertEquals(expectedData, normalizedNode);
     }
 
@@ -255,15 +317,21 @@ public class ReadDataTransactionUtilTest {
                 .read(LogicalDatastoreType.OPERATIONAL, DATA.leafSetNodePath);
         doReturn(immediateFluentFuture(Optional.of(DATA.orderedLeafSetNode2))).when(read)
                 .read(LogicalDatastoreType.CONFIGURATION, DATA.leafSetNodePath);
+        doReturn(immediateFluentFuture(Optional.of(DATA.orderedLeafSetNode1))).when(this.netconfService)
+                .get(DATA.leafSetNodePath);
+        doReturn(immediateFluentFuture(Optional.of(DATA.orderedLeafSetNode2))).when(this.netconfService)
+                .getConfig(DATA.leafSetNodePath);
         doReturn(DATA.leafSetNodePath).when(context).getInstanceIdentifier();
-
-        final NormalizedNode<?, ?> normalizedNode =
-                ReadDataTransactionUtil.readData(RestconfDataServiceConstant.ReadData.ALL, wrapper, schemaContext);
-
         final LeafSetNode<String> expectedData = Builders.<String>orderedLeafSetBuilder().withNodeIdentifier(
                 new YangInstanceIdentifier.NodeIdentifier(DATA.leafListQname)).withValue(
                         ImmutableList.<LeafSetEntryNode<String>>builder().addAll(DATA.orderedLeafSetNode1.getValue())
                         .addAll(DATA.orderedLeafSetNode2.getValue()).build()).build();
+        NormalizedNode<?, ?> normalizedNode = ReadDataTransactionUtil
+                .readData(RestconfDataServiceConstant.ReadData.ALL, mdsalStrategy, schemaContext);
+        assertEquals(expectedData, normalizedNode);
+
+        normalizedNode = ReadDataTransactionUtil
+                .readData(RestconfDataServiceConstant.ReadData.ALL, netconfStrategy, schemaContext);
         assertEquals(expectedData, normalizedNode);
     }
 
@@ -271,18 +339,27 @@ public class ReadDataTransactionUtilTest {
     public void readDataWrongPathOrNoContentTest() {
         doReturn(immediateFluentFuture(Optional.empty())).when(read)
                 .read(LogicalDatastoreType.CONFIGURATION, DATA.path2);
+        doReturn(immediateFluentFuture(Optional.empty())).when(this.netconfService).getConfig(DATA.path2);
         doReturn(DATA.path2).when(context).getInstanceIdentifier();
         final String valueOfContent = RestconfDataServiceConstant.ReadData.CONFIG;
-        final NormalizedNode<?, ?> normalizedNode =
-                ReadDataTransactionUtil.readData(valueOfContent, wrapper, schemaContext);
+        NormalizedNode<?, ?> normalizedNode =
+                ReadDataTransactionUtil.readData(valueOfContent, mdsalStrategy, schemaContext);
+        assertNull(normalizedNode);
+
+        normalizedNode =
+                ReadDataTransactionUtil.readData(valueOfContent, netconfStrategy, schemaContext);
         assertNull(normalizedNode);
     }
 
     @Test(expected = RestconfDocumentedException.class)
     public void readDataFailTest() {
         final String valueOfContent = RestconfDataServiceConstant.ReadData.READ_TYPE_TX;
-        final NormalizedNode<?, ?> normalizedNode = ReadDataTransactionUtil.readData(
-                valueOfContent, wrapper, schemaContext);
+        NormalizedNode<?, ?> normalizedNode = ReadDataTransactionUtil.readData(
+                valueOfContent, mdsalStrategy, schemaContext);
+        assertNull(normalizedNode);
+
+        normalizedNode = ReadDataTransactionUtil.readData(
+                valueOfContent, netconfStrategy, schemaContext);
         assertNull(normalizedNode);
     }