Merge "Revert "Revert "BUG-1425: Integrated new Binding to Normalized Node codec...
[controller.git] / opendaylight / md-sal / sal-rest-connector / src / main / java / org / opendaylight / controller / sal / restconf / impl / BrokerFacade.java
index be24fd26dd416f7d69838121cb7bf4f7ae2fdb0e..f11e25c046feab69fe3c41f229dfdbadd496e18d 100644 (file)
@@ -7,18 +7,15 @@
  */
 package org.opendaylight.controller.sal.restconf.impl;
 
-import static org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType.CONFIGURATION;
-import static org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType.OPERATIONAL;
-
 import com.google.common.base.Optional;
 import com.google.common.util.concurrent.CheckedFuture;
-import com.google.common.util.concurrent.ListenableFuture;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.Future;
-import javax.ws.rs.core.Response.Status;
 import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope;
 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
 import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
+import org.opendaylight.controller.md.sal.common.impl.util.compat.DataNormalizationException;
+import org.opendaylight.controller.md.sal.common.impl.util.compat.DataNormalizationOperation;
+import org.opendaylight.controller.md.sal.common.impl.util.compat.DataNormalizer;
 import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker;
 import org.opendaylight.controller.md.sal.dom.api.DOMDataChangeListener;
 import org.opendaylight.controller.md.sal.dom.api.DOMDataReadTransaction;
@@ -34,10 +31,20 @@ import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.common.RpcResult;
 import org.opendaylight.yangtools.yang.data.api.CompositeNode;
 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;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import javax.ws.rs.core.Response.Status;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.concurrent.Future;
+
+import static org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType.CONFIGURATION;
+import static org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType.OPERATIONAL;
+
 public class BrokerFacade {
     private final static Logger LOG = LoggerFactory.getLogger(BrokerFacade.class);
 
@@ -94,15 +101,17 @@ public class BrokerFacade {
     public CheckedFuture<Void, TransactionCommitFailedException> commitConfigurationDataPut(
             final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload) {
         checkPreconditions();
-        return putDataViaTransaction(domDataBroker.newWriteOnlyTransaction(), CONFIGURATION, path, payload);
+        DataNormalizationOperation<?> rootOp = ControllerContext.getInstance().getRootOperation();
+        return putDataViaTransaction(domDataBroker.newReadWriteTransaction(), CONFIGURATION, path, payload, rootOp);
     }
 
     public CheckedFuture<Void, TransactionCommitFailedException> commitConfigurationDataPut(
             final DOMMountPoint mountPoint, final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload) {
         final Optional<DOMDataBroker> domDataBrokerService = mountPoint.getService(DOMDataBroker.class);
         if (domDataBrokerService.isPresent()) {
-            return putDataViaTransaction(domDataBrokerService.get().newWriteOnlyTransaction(), CONFIGURATION, path,
-                    payload);
+            DataNormalizationOperation<?> rootOp = new DataNormalizer(mountPoint.getSchemaContext()).getRootOperation();
+            return putDataViaTransaction(domDataBrokerService.get().newReadWriteTransaction(), CONFIGURATION, path,
+                    payload, rootOp);
         }
         throw new RestconfDocumentedException("DOM data broker service isn't available for mount point.");
     }
@@ -111,15 +120,17 @@ public class BrokerFacade {
     public CheckedFuture<Void, TransactionCommitFailedException> commitConfigurationDataPost(
             final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload) {
         checkPreconditions();
-        return postDataViaTransaction(domDataBroker.newReadWriteTransaction(), CONFIGURATION, path, payload);
+        DataNormalizationOperation<?> rootOp = ControllerContext.getInstance().getRootOperation();
+        return postDataViaTransaction(domDataBroker.newReadWriteTransaction(), CONFIGURATION, path, payload, rootOp);
     }
 
     public CheckedFuture<Void, TransactionCommitFailedException> commitConfigurationDataPost(
             final DOMMountPoint mountPoint, final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload) {
         final Optional<DOMDataBroker> domDataBrokerService = mountPoint.getService(DOMDataBroker.class);
         if (domDataBrokerService.isPresent()) {
+            DataNormalizationOperation<?> rootOp = new DataNormalizer(mountPoint.getSchemaContext()).getRootOperation();
             return postDataViaTransaction(domDataBrokerService.get().newReadWriteTransaction(), CONFIGURATION, path,
-                    payload);
+                    payload, rootOp);
         }
         throw new RestconfDocumentedException("DOM data broker service isn't available for mount point.");
     }
@@ -165,49 +176,44 @@ public class BrokerFacade {
     private NormalizedNode<?, ?> readDataViaTransaction(final DOMDataReadTransaction transaction,
             LogicalDatastoreType datastore, YangInstanceIdentifier path) {
         LOG.trace("Read " + datastore.name() + " via Restconf: {}", path);
-        final ListenableFuture<Optional<NormalizedNode<?, ?>>> listenableFuture = transaction.read(datastore, path);
-        if (listenableFuture != null) {
-            Optional<NormalizedNode<?, ?>> optional;
-            try {
-                LOG.debug("Reading result data from transaction.");
-                optional = listenableFuture.get();
-            } catch (InterruptedException | ExecutionException e) {
-                throw new RestconfDocumentedException("Problem to get data from transaction.", e.getCause());
+        final CheckedFuture<Optional<NormalizedNode<?, ?>>, ReadFailedException> listenableFuture =
+                                                                 transaction.read(datastore, path);
 
-            }
-            if (optional != null) {
-                if (optional.isPresent()) {
-                    return optional.get();
-                }
-            }
+        try {
+            Optional<NormalizedNode<?, ?>> optional = listenableFuture.checkedGet();
+            return optional.isPresent() ? optional.get() : null;
+        } catch(ReadFailedException e) {
+            throw new RestconfDocumentedException(e.getMessage(), e, e.getErrorList());
         }
-        return null;
     }
 
     private CheckedFuture<Void, TransactionCommitFailedException> postDataViaTransaction(
             final DOMDataReadWriteTransaction rWTransaction, final LogicalDatastoreType datastore,
-            final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload) {
-        ListenableFuture<Optional<NormalizedNode<?, ?>>> futureDatastoreData = rWTransaction.read(datastore, path);
+            final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload, DataNormalizationOperation<?> root) {
+        CheckedFuture<Optional<NormalizedNode<?, ?>>, ReadFailedException> futureDatastoreData =
+                                                               rWTransaction.read(datastore, path);
         try {
-            final Optional<NormalizedNode<?, ?>> optionalDatastoreData = futureDatastoreData.get();
+            final Optional<NormalizedNode<?, ?>> optionalDatastoreData = futureDatastoreData.checkedGet();
             if (optionalDatastoreData.isPresent() && payload.equals(optionalDatastoreData.get())) {
-                String errMsg = "Post Configuration via Restconf was not executed because data already exists";
-                LOG.trace(errMsg + ":{}", path);
+                LOG.trace("Post Configuration via Restconf was not executed because data already exists :{}", path);
                 throw new RestconfDocumentedException("Data already exists for path: " + path, ErrorType.PROTOCOL,
                         ErrorTag.DATA_EXISTS);
             }
-        } catch (InterruptedException | ExecutionException e) {
-            LOG.trace("It wasn't possible to get data loaded from datastore at path " + path);
+        } catch(ReadFailedException e) {
+            LOG.warn("Error reading from datastore with path: " + path, e);
         }
+
+        ensureParentsByMerge(datastore, path, rWTransaction, root);
         rWTransaction.merge(datastore, path, payload);
         LOG.trace("Post " + datastore.name() + " via Restconf: {}", path);
         return rWTransaction.submit();
     }
 
     private CheckedFuture<Void, TransactionCommitFailedException> putDataViaTransaction(
-            final DOMDataWriteTransaction writeTransaction, final LogicalDatastoreType datastore,
-            final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload) {
+            final DOMDataReadWriteTransaction writeTransaction, final LogicalDatastoreType datastore,
+            final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload, DataNormalizationOperation<?> root) {
         LOG.trace("Put " + datastore.name() + " via Restconf: {}", path);
+        ensureParentsByMerge(datastore, path, writeTransaction, root);
         writeTransaction.put(datastore, path, payload);
         return writeTransaction.submit();
     }
@@ -223,4 +229,34 @@ public class BrokerFacade {
     public void setDomDataBroker(DOMDataBroker domDataBroker) {
         this.domDataBroker = domDataBroker;
     }
+
+    private final void ensureParentsByMerge(final LogicalDatastoreType store,
+            final YangInstanceIdentifier normalizedPath, final DOMDataReadWriteTransaction rwTx,
+            final DataNormalizationOperation<?> root) {
+        List<PathArgument> currentArguments = new ArrayList<>();
+        Iterator<PathArgument> iterator = normalizedPath.getPathArguments().iterator();
+        DataNormalizationOperation<?> currentOp = root;
+        while (iterator.hasNext()) {
+            PathArgument currentArg = iterator.next();
+            try {
+                currentOp = currentOp.getChild(currentArg);
+            } catch (DataNormalizationException e) {
+                throw new RestconfDocumentedException(
+                        String.format("Error normalizing data for path %s", normalizedPath), e);
+            }
+            currentArguments.add(currentArg);
+            YangInstanceIdentifier currentPath = YangInstanceIdentifier.create(currentArguments);
+
+            try {
+
+                boolean exists = rwTx.exists(store, currentPath).checkedGet();
+                if (!exists && iterator.hasNext()) {
+                    rwTx.merge(store, currentPath, currentOp.createDefault(currentArg));
+                }
+            } catch (ReadFailedException e) {
+                LOG.error("Failed to read pre-existing data from store {} path {}", store, currentPath, e);
+                throw new RestconfDocumentedException("Failed to read pre-existing data", e);
+            }
+        }
+    }
 }