5a762ebb7b2878fccaaa3addcb780c31145d2b7d
[netconf.git] / restconf / sal-rest-connector / src / main / java / org / opendaylight / restconf / restful / utils / PostDataTransactionUtil.java
1 /*
2  * Copyright (c) 2016 Cisco Systems, Inc. and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8 package org.opendaylight.restconf.restful.utils;
9
10 import com.google.common.util.concurrent.CheckedFuture;
11 import com.google.common.util.concurrent.ListenableFuture;
12 import java.net.URI;
13 import java.util.concurrent.ExecutionException;
14 import javax.ws.rs.core.Response;
15 import javax.ws.rs.core.UriBuilder;
16 import javax.ws.rs.core.UriInfo;
17 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
18 import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
19 import org.opendaylight.controller.md.sal.dom.api.DOMDataReadWriteTransaction;
20 import org.opendaylight.netconf.sal.restconf.impl.NormalizedNodeContext;
21 import org.opendaylight.netconf.sal.restconf.impl.RestconfDocumentedException;
22 import org.opendaylight.netconf.sal.restconf.impl.RestconfError.ErrorTag;
23 import org.opendaylight.netconf.sal.restconf.impl.RestconfError.ErrorType;
24 import org.opendaylight.restconf.common.references.SchemaContextRef;
25 import org.opendaylight.restconf.restful.transaction.TransactionVarsWrapper;
26 import org.opendaylight.restconf.utils.parser.ParserIdentifier;
27 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
28 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
29 import org.opendaylight.yangtools.yang.data.api.schema.AugmentationNode;
30 import org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode;
31 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
32 import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
33 import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode;
34 import org.opendaylight.yangtools.yang.data.api.schema.LeafSetNode;
35 import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
36 import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
37 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
38 import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
39 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
40 import org.slf4j.Logger;
41 import org.slf4j.LoggerFactory;
42
43 /**
44  * Util class to post data to DS
45  *
46  */
47 public final class PostDataTransactionUtil {
48
49     private static final Logger LOG = LoggerFactory.getLogger(PostDataTransactionUtil.class);
50
51     private PostDataTransactionUtil() {
52         throw new UnsupportedOperationException("Util class.");
53     }
54
55     /**
56      * Check mount point and prepare variables for post data
57      *
58      * @param uriInfo
59      *
60      * @param payload
61      *            - data
62      * @param transactionNode
63      *            - wrapper for transaction data
64      * @param schemaContextRef
65      *            - reference to actual {@link SchemaContext}
66      * @return {@link CheckedFuture}
67      */
68     public static Response postData(final UriInfo uriInfo, final NormalizedNodeContext payload,
69             final TransactionVarsWrapper transactionNode, final SchemaContextRef schemaContextRef) {
70         final CheckedFuture<Void, TransactionCommitFailedException> future = submitData(
71                 payload.getInstanceIdentifierContext().getInstanceIdentifier(), payload.getData(),
72                 transactionNode, schemaContextRef.get());
73         final URI location = PostDataTransactionUtil.resolveLocation(uriInfo, transactionNode, schemaContextRef);
74         final ResponseFactory dataFactory = new ResponseFactory(
75                 ReadDataTransactionUtil.readData(RestconfDataServiceConstant.ReadData.CONFIG, transactionNode),
76                 location);
77         FutureCallbackTx.addCallback(future, RestconfDataServiceConstant.PostData.POST_TX_TYPE, dataFactory);
78         return dataFactory.build();
79     }
80
81     /**
82      * Post data by type
83      *
84      * @param path
85      *            - path
86      * @param data
87      *            - data
88      * @param transactionNode
89      *            - wrapper for data to transaction
90      * @param schemaContext
91      *            - schema context of data
92      * @return {@link CheckedFuture}
93      */
94     private static CheckedFuture<Void, TransactionCommitFailedException> submitData(final YangInstanceIdentifier path,
95             final NormalizedNode<?, ?> data, final TransactionVarsWrapper transactionNode,
96             final SchemaContext schemaContext) {
97         final DOMDataReadWriteTransaction transaction = transactionNode.getTransaction();
98         final NormalizedNode<?, ?> node = ImmutableNodes.fromInstanceId(schemaContext, path);
99         transaction.put(LogicalDatastoreType.CONFIGURATION, YangInstanceIdentifier.create(node.getIdentifier()), node);
100         TransactionUtil.ensureParentsByMerge(path, schemaContext, transaction);
101
102         if (data instanceof MapNode) {
103             for (final MapEntryNode child : ((MapNode) data).getValue()) {
104                 putChild(child, transaction, path);
105             }
106         } else if (data instanceof AugmentationNode) {
107             for (final DataContainerChild<? extends PathArgument, ?> child : ((AugmentationNode) data).getValue()) {
108                 putChild(child, transaction, path);
109             }
110         } else if (data instanceof ChoiceNode) {
111             for (final DataContainerChild<? extends PathArgument, ?> child : ((ChoiceNode) data).getValue()) {
112                 putChild(child, transaction, path);
113             }
114         } else if (data instanceof LeafSetNode<?>) {
115             for (final LeafSetEntryNode<?> child : ((LeafSetNode<?>) data).getValue()) {
116                 putChild(child, transaction, path);
117             }
118         } else if (data instanceof ContainerNode) {
119             for (final DataContainerChild<? extends PathArgument, ?> child : ((ContainerNode) data).getValue()) {
120                 putChild(child, transaction, path);
121             }
122         }
123         return transaction.submit();
124     }
125
126     /**
127      * Prepare data for submit
128      *
129      * @param child
130      *            - data
131      * @param readWriteTx
132      *            - transaction
133      * @param path
134      *            - path to data
135      */
136     private static void putChild(final NormalizedNode<?, ?> child, final DOMDataReadWriteTransaction readWriteTx,
137             final YangInstanceIdentifier path) {
138         final YangInstanceIdentifier childPath = path.node(child.getIdentifier());
139         checkItemDesNotExits(childPath, readWriteTx);
140         readWriteTx.put(LogicalDatastoreType.CONFIGURATION, childPath, child);
141     }
142
143     /**
144      * Check if data posted to create doesn't exits.
145      *
146      * @param path
147      *            - path to data
148      * @param readWriteTx
149      *            - read write transaction
150      */
151     private static void checkItemDesNotExits(final YangInstanceIdentifier path,
152             final DOMDataReadWriteTransaction readWriteTx) {
153         final ListenableFuture<Boolean> existData = readWriteTx.exists(LogicalDatastoreType.CONFIGURATION, path);
154         try {
155             if (existData.get()) {
156                 readWriteTx.cancel();
157                 throw new RestconfDocumentedException("Data already exists for path: " + path, ErrorType.PROTOCOL,
158                         ErrorTag.DATA_EXISTS);
159             }
160         } catch (InterruptedException | ExecutionException e) {
161             LOG.warn("It wasn't possible to get data loaded from datastore at path {}", path, e);
162         }
163     }
164
165     /**
166      * Get location from {@link YangInstanceIdentifier} and {@link UriInfo}
167      *
168      * @param uriInfo
169      *            - uri info
170      * @param transactionNode
171      *            - wrapper for data of transaction
172      * @param schemaContextRef
173      *            -reference to {@link SchemaContext}
174      * @return {@link URI}
175      */
176     private static URI resolveLocation(final UriInfo uriInfo, final TransactionVarsWrapper transactionNode,
177             final SchemaContextRef schemaContextRef) {
178         if (uriInfo == null) {
179             return null;
180         }
181
182         final UriBuilder uriBuilder = uriInfo.getBaseUriBuilder();
183         uriBuilder.path("data");
184         uriBuilder.path(ParserIdentifier.stringFromYangInstanceIdentifier(transactionNode.getInstanceIdentifier().getInstanceIdentifier(),
185                 schemaContextRef.get()));
186
187         return uriBuilder.build();
188     }
189 }
190