Bug 5528 - Put data impl 85/39485/29
authorJakub Toth <jatoth@cisco.com>
Thu, 26 May 2016 13:50:31 +0000 (15:50 +0200)
committerJakub Toth <jatoth@cisco.com>
Tue, 28 Jun 2016 12:23:27 +0000 (12:23 +0000)
  *make FutureCallbackTx more generaly
  *factory for NormalizedNode(READ) and Response(PUT) from Future
  *fix ReadDataTransactionUtil after remake of FutureCallbackTx

Change-Id: Iec2d35271881c7ffe630cdd832510a3f0df7616a
Signed-off-by: Jakub Toth <jatoth@cisco.com>
restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/restful/services/impl/RestconfDataServiceImpl.java
restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/restful/transaction/TransactionVarsWrapper.java
restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/restful/utils/FutureCallbackTx.java
restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/restful/utils/FutureDataFactory.java
restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/restful/utils/NormalizedNodeFactory.java [new file with mode: 0644]
restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/restful/utils/PutDataTransactionUtil.java [new file with mode: 0644]
restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/restful/utils/ReadDataTransactionUtil.java
restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/restful/utils/ResponseFactory.java [new file with mode: 0644]
restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/restful/utils/RestconfDataServiceConstant.java

index cc34101c27f87befd7c969e471b78cf6a98b9fd1..68f965206324ad604fba8a0df1d2dc1b1ce1b2b0 100644 (file)
@@ -7,32 +7,40 @@
  */
 package org.opendaylight.restconf.restful.services.impl;
 
+import com.google.common.base.Optional;
 import com.google.common.base.Preconditions;
 import javax.ws.rs.core.Response;
 import javax.ws.rs.core.UriInfo;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataReadWriteTransaction;
 import org.opendaylight.controller.md.sal.dom.api.DOMMountPoint;
 import org.opendaylight.netconf.sal.restconf.impl.InstanceIdentifierContext;
 import org.opendaylight.netconf.sal.restconf.impl.NormalizedNodeContext;
 import org.opendaylight.netconf.sal.restconf.impl.PATCHContext;
 import org.opendaylight.netconf.sal.restconf.impl.PATCHStatusContext;
+import org.opendaylight.netconf.sal.restconf.impl.RestconfDocumentedException;
 import org.opendaylight.restconf.common.references.SchemaContextRef;
-import org.opendaylight.restconf.handlers.DOMMountPointServiceHandler;
 import org.opendaylight.restconf.handlers.SchemaContextHandler;
 import org.opendaylight.restconf.handlers.TransactionChainHandler;
 import org.opendaylight.restconf.restful.services.api.RestconfDataService;
 import org.opendaylight.restconf.restful.transaction.TransactionVarsWrapper;
+import org.opendaylight.restconf.restful.utils.PutDataTransactionUtil;
 import org.opendaylight.restconf.restful.utils.ReadDataTransactionUtil;
 import org.opendaylight.restconf.restful.utils.RestconfDataServiceConstant;
 import org.opendaylight.restconf.utils.parser.ParserIdentifier;
 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaNode;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 /**
  * Implementation of {@link RestconfDataService}
  */
 public class RestconfDataServiceImpl implements RestconfDataService {
 
+    private final static Logger LOG = LoggerFactory.getLogger(RestconfDataServiceImpl.class);
+
     private SchemaContextHandler schemaContextHandler;
-    private DOMMountPointServiceHandler domMountPointServiceHandler;
     private TransactionChainHandler transactionChainHandler;
 
     @Override
@@ -44,8 +52,14 @@ public class RestconfDataServiceImpl implements RestconfDataService {
         final DOMMountPoint mountPoint = instanceIdentifier.getMountPoint();
         final String value = uriInfo.getQueryParameters().getFirst(RestconfDataServiceConstant.CONTENT);
 
+        DOMDataReadWriteTransaction transaction = null;
+        if (mountPoint == null) {
+            transaction = this.transactionChainHandler.get().newReadWriteTransaction();
+        } else {
+            transaction = transactionOfMountPoint(mountPoint);
+        }
         final TransactionVarsWrapper transactionNode = new TransactionVarsWrapper(instanceIdentifier, mountPoint,
-                this.transactionChainHandler.get(), this.domMountPointServiceHandler.get(), schemaContextRef.get());
+                transaction);
         final NormalizedNode<?, ?> node = ReadDataTransactionUtil.readData(value, transactionNode);
 
         return new NormalizedNodeContext(instanceIdentifier, node);
@@ -53,7 +67,30 @@ public class RestconfDataServiceImpl implements RestconfDataService {
 
     @Override
     public Response putData(final String identifier, final NormalizedNodeContext payload) {
-        throw new UnsupportedOperationException("Not yet implemented.");
+        Preconditions.checkNotNull(identifier);
+        Preconditions.checkNotNull(payload);
+
+        final InstanceIdentifierContext<? extends SchemaNode> iid = payload
+                .getInstanceIdentifierContext();
+
+        PutDataTransactionUtil.validInputData(iid.getSchemaNode(), payload);
+        PutDataTransactionUtil.validTopLevelNodeName(iid.getInstanceIdentifier(), payload);
+        PutDataTransactionUtil.validateListKeysEqualityInPayloadAndUri(payload);
+
+        final DOMMountPoint mountPoint = payload.getInstanceIdentifierContext().getMountPoint();
+        DOMDataReadWriteTransaction transaction = null;
+        SchemaContextRef ref = null;
+        if (mountPoint == null) {
+            transaction = this.transactionChainHandler.get().newReadWriteTransaction();
+            ref = new SchemaContextRef(this.schemaContextHandler.get());
+        } else {
+            transaction = transactionOfMountPoint(mountPoint);
+            ref = new SchemaContextRef(mountPoint.getSchemaContext());
+        }
+
+        final TransactionVarsWrapper transactionNode = new TransactionVarsWrapper(
+                payload.getInstanceIdentifierContext(), mountPoint, transaction);
+        return PutDataTransactionUtil.putData(payload, ref, transactionNode);
     }
 
     @Override
@@ -80,4 +117,25 @@ public class RestconfDataServiceImpl implements RestconfDataService {
     public PATCHStatusContext patchData(final PATCHContext context, final UriInfo uriInfo) {
         throw new UnsupportedOperationException("Not yet implemented.");
     }
+
+    /**
+     * Prepare transaction to read data of mount point, if these data are
+     * present.
+     * @param mountPoint
+     *
+     * @param transactionNode
+     *            - {@link TransactionVarsWrapper} - wrapper for variables
+     * @return {@link NormalizedNode}
+     */
+    private static DOMDataReadWriteTransaction transactionOfMountPoint(final DOMMountPoint mountPoint) {
+        final Optional<DOMDataBroker> domDataBrokerService = mountPoint.getService(DOMDataBroker.class);
+        if (domDataBrokerService.isPresent()) {
+            return domDataBrokerService.get().newReadWriteTransaction();
+        } else {
+            final String errMsg = "DOM data broker service isn't available for mount point "
+                    + mountPoint.getIdentifier();
+            LOG.warn(errMsg);
+            throw new RestconfDocumentedException(errMsg);
+        }
+    }
 }
index 3b65762e642b0d28fa0530dc52be8f3e5570ddd7..1789d30933d98d992258066d5ecde44971068b50 100644 (file)
@@ -8,11 +8,9 @@
 package org.opendaylight.restconf.restful.transaction;
 
 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataReadWriteTransaction;
 import org.opendaylight.controller.md.sal.dom.api.DOMMountPoint;
-import org.opendaylight.controller.md.sal.dom.api.DOMMountPointService;
-import org.opendaylight.controller.md.sal.dom.api.DOMTransactionChain;
 import org.opendaylight.netconf.sal.restconf.impl.InstanceIdentifierContext;
-import org.opendaylight.yangtools.yang.model.api.SchemaContext;
 
 /**
  * This class represent delegation wrapper for transaction variables.
@@ -23,34 +21,26 @@ public final class TransactionVarsWrapper {
     private final InstanceIdentifierContext<?> instanceIdentifier;
     private final DOMMountPoint mountPoint;
     private LogicalDatastoreType configuration = null;
-    private final DOMMountPointService domMountPointService;
-    private final SchemaContext schemaContext;
-    private final DOMTransactionChain domTransactionChain;
+    private final DOMDataReadWriteTransaction transaction;
 
     /**
      * 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).
+     * from DS - config + state).
      *
      * @param instanceIdentifier
      *            - {@link InstanceIdentifierContext} of data for transaction
      * @param mountPoint
-     *            - mount point if is presnet
-     * @param domTransactionChain
-     *            - {@link DOMTransactionChain} for transactions
-     * @param domMountPointService
-     *            - mount point service
-     * @param schemaContext
-     *            - {@link SchemaContext}
+     *            - mount point if is present
+     * @param transaction
+     *            - {@link DOMDataReadWriteTransaction} transaction for
+     *            operations
      */
     public TransactionVarsWrapper(final InstanceIdentifierContext<?> instanceIdentifier, final DOMMountPoint mountPoint,
-            final DOMTransactionChain domTransactionChain, final DOMMountPointService domMountPointService,
-            final SchemaContext schemaContext) {
+            final DOMDataReadWriteTransaction transaction) {
         this.instanceIdentifier = instanceIdentifier;
         this.mountPoint = mountPoint;
-        this.domTransactionChain = domTransactionChain;
-        this.domMountPointService = domMountPointService;
-        this.schemaContext = schemaContext;
+        this.transaction = transaction;
     }
 
     /**
@@ -92,29 +82,11 @@ public final class TransactionVarsWrapper {
     }
 
     /**
-     * Get mount point service
+     * Get specific type of transaction
      *
-     * @return {@link DOMMountPointService}
+     * @return specific type transaction
      */
-    public DOMMountPointService getDomMountPointService() {
-        return this.domMountPointService;
-    }
-
-    /**
-     * Get schema context of data
-     *
-     * @return {@link SchemaContext}
-     */
-    public SchemaContext getSchemaContext() {
-        return this.schemaContext;
-    }
-
-    /**
-     * Get transaction chain
-     *
-     * @return {@link DOMTransactionChain}
-     */
-    public DOMTransactionChain getDomTransactionChain() {
-        return this.domTransactionChain;
+    public DOMDataReadWriteTransaction getTransaction() {
+        return this.transaction;
     }
 }
index a2b95fbe0bac25ce97779f4205bcfe92173f1863..26f97df2beb405dd2d8feac770a1f0d01ce288ea 100644 (file)
@@ -7,13 +7,11 @@
  */
 package org.opendaylight.restconf.restful.utils;
 
-import com.google.common.base.Optional;
 import com.google.common.util.concurrent.CheckedFuture;
 import com.google.common.util.concurrent.FutureCallback;
 import com.google.common.util.concurrent.Futures;
 import javax.annotation.Nullable;
 import org.opendaylight.controller.md.sal.common.api.data.AsyncTransaction;
-import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
 import org.slf4j.Logger;
@@ -43,10 +41,10 @@ final class FutureCallbackTx {
      * @param dataFactory
      *            - factory setting result
      */
-    static void addCallback(final CheckedFuture<Optional<NormalizedNode<?, ?>>, ReadFailedException> listenableFuture,
+    static <T, X extends Exception> void addCallback(final CheckedFuture<T, X> listenableFuture,
             final AsyncTransaction<YangInstanceIdentifier, NormalizedNode<?, ?>> transaction, final String txType,
-            final FutureDataFactory dataFactory) {
-        Futures.addCallback(listenableFuture, new FutureCallback<Optional<NormalizedNode<?, ?>>>() {
+            final FutureDataFactory<T> dataFactory) {
+        Futures.addCallback(listenableFuture, new FutureCallback<T>() {
 
             @Override
             public void onFailure(final Throwable t) {
@@ -54,7 +52,7 @@ final class FutureCallbackTx {
             }
 
             @Override
-            public void onSuccess(final Optional<NormalizedNode<?, ?>> result) {
+            public void onSuccess(final T result) {
                 handlingLoggerAndValues(null, txType, transaction, result, dataFactory);
             }
 
@@ -79,17 +77,15 @@ final class FutureCallbackTx {
      * @param dataFactory
      *            - setter for result - in callback is onSuccess
      */
-    protected static void handlingLoggerAndValues(@Nullable final Throwable t, final String txType,
+    protected static <T> void handlingLoggerAndValues(@Nullable final Throwable t, final String txType,
             final AsyncTransaction<YangInstanceIdentifier, NormalizedNode<?, ?>> transaction,
-            final Optional<NormalizedNode<?, ?>> optionalNN, final FutureDataFactory dataFactory) {
+            final T result, final FutureDataFactory<T> dataFactory) {
         if (t != null) {
             LOG.info("Transaction({}) {} FAILED!", txType, transaction.getIdentifier(), t);
             throw new IllegalStateException("  Transaction(" + txType + ") not committed correctly", t);
         } else {
             LOG.trace("Transaction({}) {} SUCCESSFUL!", txType, transaction.getIdentifier());
-            if (optionalNN.isPresent()) {
-                dataFactory.setData(optionalNN.get());
-            }
+            dataFactory.setResult(result);
         }
     }
 }
index 42f75653fb828d4e112b0cb72d6e63f91e6562f1..bec46374d3e362baa2e5c9913edd7d33b8c2c53f 100644 (file)
@@ -7,18 +7,12 @@
  */
 package org.opendaylight.restconf.restful.utils;
 
-import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+class FutureDataFactory<T> {
 
-class FutureDataFactory {
+    protected T result;
 
-    private NormalizedNode<?, ?> normalizedNode = null;
-
-    public void setData(final NormalizedNode<?, ?> normalizedNode) {
-        this.normalizedNode = normalizedNode;
-    }
-
-    public NormalizedNode<?, ?> getData() {
-        return this.normalizedNode;
+    void setResult(final T result) {
+        this.result = result;
     }
 
 }
diff --git a/restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/restful/utils/NormalizedNodeFactory.java b/restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/restful/utils/NormalizedNodeFactory.java
new file mode 100644 (file)
index 0000000..f7cc9af
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ * 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.restful.utils;
+
+import com.google.common.base.Optional;
+import org.apache.commons.lang3.builder.Builder;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+class NormalizedNodeFactory extends FutureDataFactory<Optional<NormalizedNode<?, ?>>>
+        implements Builder<NormalizedNode<?, ?>> {
+
+    @Override
+    public NormalizedNode<?, ?> build() {
+        if (this.result.isPresent()) {
+            return this.result.get();
+        }
+        return null;
+    }
+
+}
diff --git a/restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/restful/utils/PutDataTransactionUtil.java b/restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/restful/utils/PutDataTransactionUtil.java
new file mode 100644 (file)
index 0000000..9b919f7
--- /dev/null
@@ -0,0 +1,217 @@
+/*
+ * 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.restful.utils;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.Maps;
+import com.google.common.util.concurrent.CheckedFuture;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import javax.ws.rs.core.Response;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction;
+import org.opendaylight.netconf.md.sal.rest.common.RestconfValidationUtils;
+import org.opendaylight.netconf.sal.restconf.impl.InstanceIdentifierContext;
+import org.opendaylight.netconf.sal.restconf.impl.NormalizedNodeContext;
+import org.opendaylight.netconf.sal.restconf.impl.RestconfDocumentedException;
+import org.opendaylight.netconf.sal.restconf.impl.RestconfError.ErrorTag;
+import org.opendaylight.netconf.sal.restconf.impl.RestconfError.ErrorType;
+import org.opendaylight.restconf.common.references.SchemaContextRef;
+import org.opendaylight.restconf.restful.transaction.TransactionVarsWrapper;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
+import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaNode;
+
+/**
+ * Util class for put data to DS
+ *
+ */
+public final class PutDataTransactionUtil {
+
+    /**
+     * Valid input data with {@link SchemaNode}
+     *
+     * @param schemaNode
+     *            - {@link SchemaNode}
+     * @param payload
+     *            - input data
+     */
+    public static void validInputData(final SchemaNode schemaNode, final NormalizedNodeContext payload) {
+        if ((schemaNode != null) && (payload.getData() == null)) {
+            throw new RestconfDocumentedException("Input is required.", ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE);
+        } else if ((schemaNode == null) && (payload.getData() != null)) {
+            throw new RestconfDocumentedException("No input expected.", ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE);
+        }
+    }
+
+    /**
+     * Valid top level node name
+     *
+     * @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();
+
+        if (path.isEmpty()) {
+            if (!payload.getData().getNodeType().equals(RestconfDataServiceConstant.NETCONF_BASE_QNAME)) {
+                throw new RestconfDocumentedException("Instance identifier has to contain at least one path argument",
+                        ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE);
+            }
+        } else {
+            final String identifierName = path.getLastPathArgument().getNodeType().getLocalName();
+            if (!payloadName.equals(identifierName)) {
+                throw new RestconfDocumentedException(
+                        "Payload name (" + payloadName + ") is different from identifier name (" + identifierName + ")",
+                        ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE);
+            }
+        }
+    }
+
+    /**
+     * 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
+     *
+     */
+    public static void validateListKeysEqualityInPayloadAndUri(final NormalizedNodeContext payload) {
+        final InstanceIdentifierContext<?> iiWithData = payload.getInstanceIdentifierContext();
+        final PathArgument lastPathArgument = iiWithData.getInstanceIdentifier().getLastPathArgument();
+        final SchemaNode schemaNode = iiWithData.getSchemaNode();
+        final NormalizedNode<?, ?> data = payload.getData();
+        if (schemaNode instanceof ListSchemaNode) {
+            final List<QName> keyDefinitions = ((ListSchemaNode) schemaNode).getKeyDefinition();
+            if ((lastPathArgument instanceof NodeIdentifierWithPredicates) && (data instanceof MapEntryNode)) {
+                final Map<QName, Object> uriKeyValues = ((NodeIdentifierWithPredicates) lastPathArgument)
+                        .getKeyValues();
+                isEqualUriAndPayloadKeyValues(uriKeyValues, (MapEntryNode) data, keyDefinitions);
+            }
+        }
+    }
+
+    private static void isEqualUriAndPayloadKeyValues(final Map<QName, Object> uriKeyValues, final MapEntryNode payload,
+            final List<QName> keyDefinitions) {
+        final Map<QName, Object> mutableCopyUriKeyValues = Maps.newHashMap(uriKeyValues);
+        for (final QName keyDefinition : keyDefinitions) {
+            final Object uriKeyValue = mutableCopyUriKeyValues.remove(keyDefinition);
+            RestconfValidationUtils.checkDocumentedError(uriKeyValue != null, ErrorType.PROTOCOL, ErrorTag.DATA_MISSING,
+                    "Missing key " + keyDefinition + " in URI.");
+
+            final Object dataKeyValue = payload.getIdentifier().getKeyValues().get(keyDefinition);
+
+            if (!uriKeyValue.equals(dataKeyValue)) {
+                final String errMsg = "The value '" + uriKeyValue + "' for key '" + keyDefinition.getLocalName()
+                        + "' specified in the URI doesn't match the value '" + dataKeyValue
+                        + "' specified in the message body. ";
+                throw new RestconfDocumentedException(errMsg, ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
+            }
+        }
+    }
+
+    /**
+     * Check mount point and prepare variables for put data to DS
+     *
+     * @param payload
+     *            - data to put
+     * @param schemaCtxRef
+     *            - reference to {@link SchemaContext}
+     * @param transactionNode
+     * @return {@link CheckedFuture}
+     */
+    public static Response putData(final NormalizedNodeContext payload,
+            final SchemaContextRef schemaCtxRef, final TransactionVarsWrapper transactionNode) {
+        final YangInstanceIdentifier path = payload.getInstanceIdentifierContext().getInstanceIdentifier();
+        final ResponseFactory responseFactory = new ResponseFactory(
+                ReadDataTransactionUtil.readData(RestconfDataServiceConstant.ReadData.CONFIG, transactionNode));
+        final CheckedFuture<Void, TransactionCommitFailedException> submitData = submitData(path, schemaCtxRef.get(),
+                transactionNode.getTransaction(), payload.getData());
+        FutureCallbackTx.addCallback(submitData, transactionNode.getTransaction(),
+                RestconfDataServiceConstant.PutData.PUT_TX_TYPE, responseFactory);
+        return responseFactory.build();
+    }
+
+    /**
+     * Put data to DS
+     *
+     * @param path
+     *            - path of data
+     * @param schemaContext
+     *            - {@link SchemaContext}
+     * @param writeTx
+     *            - write transaction
+     * @param data
+     *            - data
+     * @return {@link CheckedFuture}
+     */
+    private static CheckedFuture<Void, TransactionCommitFailedException> submitData(final YangInstanceIdentifier path,
+            final SchemaContext schemaContext,
+            final DOMDataWriteTransaction writeTx, final NormalizedNode<?, ?> data) {
+        ensureParentsByMerge(path, schemaContext, writeTx);
+        writeTx.put(LogicalDatastoreType.CONFIGURATION, path, data);
+        return writeTx.submit();
+    }
+
+    /**
+     * Merged parents of data
+     *
+     * @param path
+     *            - path of data
+     * @param schemaContext
+     *            - {@link SchemaContext}
+     * @param writeTx
+     *            - write transaction
+     */
+    private static void ensureParentsByMerge(final YangInstanceIdentifier path, final SchemaContext schemaContext,
+            final DOMDataWriteTransaction writeTx) {
+        final List<PathArgument> normalizedPathWithoutChildArgs = new ArrayList<>();
+        boolean hasList = false;
+        YangInstanceIdentifier rootNormalizedPath = null;
+
+        final Iterator<PathArgument> it = path.getPathArguments().iterator();
+        final Module module = schemaContext.findModuleByNamespaceAndRevision(
+                path.getLastPathArgument().getNodeType().getModule().getNamespace(),
+                path.getLastPathArgument().getNodeType().getModule().getRevision());
+
+        while (it.hasNext()) {
+            final PathArgument pathArgument = it.next();
+            if (rootNormalizedPath == null) {
+                rootNormalizedPath = YangInstanceIdentifier.create(pathArgument);
+            }
+            if (it.hasNext()) {
+                normalizedPathWithoutChildArgs.add(pathArgument);
+                if (module.getDataChildByName(pathArgument.getNodeType()) instanceof ListSchemaNode) {
+                    hasList = true;
+                }
+            }
+        }
+        if (normalizedPathWithoutChildArgs.isEmpty()) {
+            return;
+        }
+        if (hasList) {
+            Preconditions.checkArgument(rootNormalizedPath != null, "Empty path received");
+            final NormalizedNode<?, ?> parentStructure = ImmutableNodes.fromInstanceId(schemaContext,
+                    YangInstanceIdentifier.create(normalizedPathWithoutChildArgs));
+            writeTx.merge(LogicalDatastoreType.CONFIGURATION, rootNormalizedPath, parentStructure);
+        }
+    }
+}
index a6c4ef6dfcbf364804eea58c49d2db08ff3a1938..344db30a8d491f49ac739e609b580d2ec4e6b5b9 100644 (file)
@@ -10,11 +10,8 @@ package org.opendaylight.restconf.restful.utils;
 import com.google.common.base.Optional;
 import com.google.common.base.Preconditions;
 import com.google.common.util.concurrent.CheckedFuture;
-import java.util.concurrent.ExecutionException;
 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.dom.api.DOMDataBroker;
-import org.opendaylight.controller.md.sal.dom.api.DOMDataReadTransaction;
 import org.opendaylight.netconf.sal.restconf.impl.RestconfDocumentedException;
 import org.opendaylight.netconf.sal.restconf.impl.RestconfError.ErrorTag;
 import org.opendaylight.netconf.sal.restconf.impl.RestconfError.ErrorType;
@@ -35,8 +32,6 @@ import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
 import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeBuilder;
 import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 
 /**
  * Util class for read data from data store via transaction.
@@ -49,8 +44,6 @@ import org.slf4j.LoggerFactory;
  */
 public final class ReadDataTransactionUtil {
 
-    private final static Logger LOG = LoggerFactory.getLogger(ReadDataTransactionUtil.class);
-
     private ReadDataTransactionUtil() {
         throw new UnsupportedOperationException("Util class.");
     }
@@ -69,84 +62,44 @@ public final class ReadDataTransactionUtil {
             switch (valueOfContent) {
                 case RestconfDataServiceConstant.ReadData.CONFIG:
                     transactionNode.setLogicalDatastoreType(LogicalDatastoreType.CONFIGURATION);
-                    return readData(transactionNode);
+                    return readDataViaTransaction(transactionNode);
                 case RestconfDataServiceConstant.ReadData.NONCONFIG:
                     transactionNode.setLogicalDatastoreType(LogicalDatastoreType.OPERATIONAL);
-                    return readData(transactionNode);
+                    return readDataViaTransaction(transactionNode);
                 case RestconfDataServiceConstant.ReadData.ALL:
-                    return readData(transactionNode);
+                    return readDataViaTransaction(transactionNode);
                 default:
                     throw new RestconfDocumentedException("Bad querry parameter for content.", ErrorType.APPLICATION,
                             ErrorTag.INVALID_VALUE);
             }
         } else {
-            return readData(transactionNode);
-        }
-    }
-
-    /**
-     * Check mount point
-     *
-     * @param transactionNode
-     *            - {@link TransactionVarsWrapper} - wrapper for variables
-     * @return {@link NormalizedNode}
-     */
-    private static NormalizedNode<?, ?> readData(final TransactionVarsWrapper transactionNode) {
-        if (transactionNode.getMountPoint() == null) {
-            return readDataViaTransaction(transactionNode.getDomTransactionChain().newReadOnlyTransaction(),
-                    transactionNode);
-        } else {
-            return readDataOfMountPointViaTransaction(transactionNode);
+            return readDataViaTransaction(transactionNode);
         }
     }
 
     /**
      * 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)
+     * {@link TransactionVarsWrapper}, then read this type of data from DS. If
+     * don't, we have to read all data from DS (state + config)
      *
-     * @param readTransaction
-     *            - {@link DOMDataReadTransaction} to read data from DS
      * @param transactionNode
      *            - {@link TransactionVarsWrapper} - wrapper for variables
      * @return {@link NormalizedNode}
      */
-    private static NormalizedNode<?, ?> readDataViaTransaction(final DOMDataReadTransaction readTransaction,
-            final TransactionVarsWrapper transactionNode) {
+    private static NormalizedNode<?, ?> readDataViaTransaction(final TransactionVarsWrapper transactionNode) {
         if (transactionNode.getLogicalDatastoreType() != null) {
-            final CheckedFuture<Optional<NormalizedNode<?, ?>>, ReadFailedException> listenableFuture = readTransaction
-                    .read(transactionNode.getLogicalDatastoreType(),
+            final CheckedFuture<Optional<NormalizedNode<?, ?>>, ReadFailedException> listenableFuture = transactionNode
+                    .getTransaction().read(transactionNode.getLogicalDatastoreType(),
                             transactionNode.getInstanceIdentifier().getInstanceIdentifier());
-            final FutureDataFactory dataFactory = new FutureDataFactory();
-            FutureCallbackTx.addCallback(listenableFuture, readTransaction,
+            final NormalizedNodeFactory dataFactory = new NormalizedNodeFactory();
+            FutureCallbackTx.addCallback(listenableFuture, transactionNode.getTransaction(),
                     RestconfDataServiceConstant.ReadData.READ_TYPE_TX, dataFactory);
-            return dataFactory.getData();
+            return dataFactory.build();
         } else {
             return readAllData(transactionNode);
         }
     }
 
-    /**
-     * Prepare transaction to read data of mount point, if these data are
-     * present.
-     *
-     * @param transactionNode
-     *            - {@link TransactionVarsWrapper} - wrapper for variables
-     * @return {@link NormalizedNode}
-     */
-    private static NormalizedNode<?, ?> readDataOfMountPointViaTransaction(final TransactionVarsWrapper transactionNode) {
-        final Optional<DOMDataBroker> domDataBrokerService = transactionNode.getMountPoint()
-                .getService(DOMDataBroker.class);
-        if (domDataBrokerService.isPresent()) {
-            return readDataViaTransaction(domDataBrokerService.get().newReadOnlyTransaction(), transactionNode);
-        } else {
-            final String errMsg = "DOM data broker service isn't available for mount point "
-                    + transactionNode.getInstanceIdentifier().getInstanceIdentifier();
-            LOG.warn(errMsg);
-            throw new RestconfDocumentedException(errMsg);
-        }
-    }
-
     /**
      * Read config and state data, then map them.
      *
@@ -157,13 +110,13 @@ public final class ReadDataTransactionUtil {
     private static NormalizedNode<?, ?> readAllData(final TransactionVarsWrapper transactionNode) {
         // PREPARE STATE DATA NODE
         transactionNode.setLogicalDatastoreType(LogicalDatastoreType.OPERATIONAL);
-        final NormalizedNode<?, ?> stateDataNode = readData(transactionNode);
+        final NormalizedNode<?, ?> stateDataNode = readDataViaTransaction(transactionNode);
 
         // PREPARE CONFIG DATA NODE
         transactionNode.setLogicalDatastoreType(LogicalDatastoreType.CONFIGURATION);
-        final NormalizedNode<?, ?> configDataNode = readData(transactionNode);
+        final NormalizedNode<?, ?> configDataNode = readDataViaTransaction(transactionNode);
 
-        return mapNode(stateDataNode, configDataNode, transactionNode);
+        return mapNode(stateDataNode, configDataNode);
     }
 
     /**
@@ -178,8 +131,7 @@ public final class ReadDataTransactionUtil {
      * @return {@link NormalizedNode}
      */
     private static NormalizedNode<?, ?> mapNode(final NormalizedNode<?, ?> stateDataNode,
-            final NormalizedNode<?, ?> configDataNode,
-            final TransactionVarsWrapper transactionNode) {
+            final NormalizedNode<?, ?> configDataNode) {
         validPossibilityOfMergeNodes(stateDataNode, configDataNode);
         if (configDataNode instanceof RpcDefinition) {
             return prepareRpcData(configDataNode, stateDataNode);
@@ -323,35 +275,4 @@ public final class ReadDataTransactionUtil {
             throw new RestconfDocumentedException("It is not possible to merge ");
         }
     }
-
-    /**
-     * Get data from future object if these data are present.
-     *
-     * @param listenableFuture
-     *            - future of optional {@link NormalizedNode}
-     * @param transactionNode
-     *            - {@link TransactionVarsWrapper} - wrapper for variables
-     * @return {@link NormalizedNode}
-     */
-    private static NormalizedNode<?, ?> getNodeFromFuture(
-            final CheckedFuture<Optional<NormalizedNode<?, ?>>, ReadFailedException> listenableFuture,
-            final TransactionVarsWrapper transactionNode) {
-        Optional<NormalizedNode<?, ?>> optional;
-        try {
-            LOG.debug("Reading result data from transaction.");
-            optional = listenableFuture.get();
-        } catch (InterruptedException | ExecutionException e) {
-            LOG.warn("Exception by reading {} via Restconf: {}", transactionNode.getLogicalDatastoreType().name(),
-                    transactionNode.getInstanceIdentifier().getInstanceIdentifier(), e);
-            throw new RestconfDocumentedException("Problem to get data from transaction.", e.getCause());
-
-        }
-        if (optional != null) {
-            if (optional.isPresent()) {
-                return optional.get();
-            }
-        }
-        throw new RestconfDocumentedException("Normalized node is not available : "
-                + transactionNode.getInstanceIdentifier().getInstanceIdentifier());
-    }
 }
diff --git a/restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/restful/utils/ResponseFactory.java b/restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/restful/utils/ResponseFactory.java
new file mode 100644 (file)
index 0000000..a854556
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * 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.restful.utils;
+
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.Status;
+import org.apache.commons.lang3.builder.Builder;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+final class ResponseFactory extends FutureDataFactory<Void> implements Builder<Response> {
+
+    private final NormalizedNode<?, ?> readData;
+
+    ResponseFactory(final NormalizedNode<?, ?> readData) {
+        this.readData = readData;
+    }
+
+    @Override
+    public Response build() {
+        final Status status = this.readData != null ? Status.OK : Status.CREATED;
+        return Response.status(status).build();
+    }
+}
index 01cec9e3c32e39c8f85836c593557964a81299ef..ca39e5645783a607374a9f1cd2ac4bc812f5e188 100644 (file)
@@ -7,6 +7,14 @@
  */
 package org.opendaylight.restconf.restful.utils;
 
+import java.net.URI;
+import java.net.URISyntaxException;
+import org.opendaylight.netconf.sal.restconf.impl.RestconfDocumentedException;
+import org.opendaylight.netconf.sal.restconf.impl.RestconfError.ErrorTag;
+import org.opendaylight.netconf.sal.restconf.impl.RestconfError.ErrorType;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+
 /**
  * Constants for RestconfDataService
  *
@@ -14,6 +22,17 @@ package org.opendaylight.restconf.restful.utils;
 public final class RestconfDataServiceConstant {
 
     public static final String CONTENT = "content";
+    public static final QName NETCONF_BASE_QNAME;
+    static {
+        try {
+            NETCONF_BASE_QNAME = QName.create(
+                    QNameModule.create(new URI(PutData.NETCONF_BASE), null), PutData.NETCONF_BASE_PAYLOAD_NAME);
+        } catch (final URISyntaxException e) {
+            final String errMsg = "It wasn't possible to create instance of URI class with " + PutData.NETCONF_BASE
+                    + " URI";
+            throw new RestconfDocumentedException(errMsg, ErrorType.APPLICATION, ErrorTag.OPERATION_FAILED);
+        }
+    }
 
     private RestconfDataServiceConstant() {
         throw new UnsupportedOperationException("Util class.");
@@ -35,4 +54,17 @@ public final class RestconfDataServiceConstant {
         }
     }
 
+    /**
+     * Constants for data to put
+     *
+     */
+    public final class PutData {
+        public static final String NETCONF_BASE = "urn:ietf:params:xml:ns:netconf:base:1.0";
+        public static final String NETCONF_BASE_PAYLOAD_NAME = "data";
+        public static final String PUT_TX_TYPE = "PUT";
+
+        private PutData() {
+            throw new UnsupportedOperationException("Util class.");
+        }
+    }
 }