Split Restconf implementations (draft02 and RFC) - move restconf
[netconf.git] / restconf / restconf-nb-bierman02 / 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 java.net.URI;
12 import javax.ws.rs.core.Response;
13 import javax.ws.rs.core.UriBuilder;
14 import javax.ws.rs.core.UriInfo;
15 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
16 import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
17 import org.opendaylight.controller.md.sal.dom.api.DOMDataReadWriteTransaction;
18 import org.opendaylight.controller.md.sal.dom.api.DOMTransactionChain;
19 import org.opendaylight.netconf.sal.restconf.impl.ControllerContext;
20 import org.opendaylight.restconf.common.context.InstanceIdentifierContext;
21 import org.opendaylight.restconf.common.context.NormalizedNodeContext;
22 import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
23 import org.opendaylight.restconf.common.references.SchemaContextRef;
24 import org.opendaylight.restconf.restful.transaction.TransactionVarsWrapper;
25 import org.opendaylight.restconf.utils.parser.ParserIdentifier;
26 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
27 import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode;
28 import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
29 import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
30 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
31 import org.opendaylight.yangtools.yang.data.api.schema.OrderedLeafSetNode;
32 import org.opendaylight.yangtools.yang.data.api.schema.OrderedMapNode;
33 import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
34 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
35 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
36 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
37 import org.slf4j.Logger;
38 import org.slf4j.LoggerFactory;
39
40 /**
41  * Util class to post data to DS.
42  *
43  */
44 public final class PostDataTransactionUtil {
45
46     private static final Logger LOG = LoggerFactory.getLogger(PostDataTransactionUtil.class);
47
48     private PostDataTransactionUtil() {
49         throw new UnsupportedOperationException("Util class.");
50     }
51
52     /**
53      * Check mount point and prepare variables for post data.
54      *
55      * @param uriInfo
56      *
57      * @param payload
58      *             data
59      * @param transactionNode
60      *             wrapper for transaction data
61      * @param schemaContextRef
62      *             reference to actual {@link SchemaContext}
63      * @param point
64      *             point
65      * @param insert
66      *             insert
67      * @return {@link CheckedFuture}
68      */
69     public static Response postData(final UriInfo uriInfo, final NormalizedNodeContext payload,
70             final TransactionVarsWrapper transactionNode, final SchemaContextRef schemaContextRef, final String insert,
71             final String point) {
72         final CheckedFuture<Void, TransactionCommitFailedException> future = submitData(
73                 payload.getInstanceIdentifierContext().getInstanceIdentifier(), payload.getData(),
74                 transactionNode, schemaContextRef.get(), insert, point);
75         final URI location = PostDataTransactionUtil.resolveLocation(uriInfo, transactionNode, schemaContextRef);
76         final ResponseFactory dataFactory = new ResponseFactory(null, 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      * @param point
93      *             query parameter
94      * @param insert
95      *             query parameter
96      * @return {@link CheckedFuture}
97      */
98     private static CheckedFuture<Void, TransactionCommitFailedException> submitData(final YangInstanceIdentifier path,
99             final NormalizedNode<?, ?> data, final TransactionVarsWrapper transactionNode,
100             final SchemaContext schemaContext, final String insert, final String point) {
101         final DOMTransactionChain domTransactionChain = transactionNode.getTransactionChain();
102         final DOMDataReadWriteTransaction newReadWriteTransaction = domTransactionChain.newReadWriteTransaction();
103         if (insert == null) {
104             makePost(path, data, schemaContext, domTransactionChain, newReadWriteTransaction);
105             return newReadWriteTransaction.submit();
106         } else {
107             final DataSchemaNode schemaNode = PutDataTransactionUtil.checkListAndOrderedType(schemaContext, path);
108             switch (insert) {
109                 case "first":
110                     if (schemaNode instanceof ListSchemaNode) {
111                         final NormalizedNode<?, ?> readData =
112                                 PutDataTransactionUtil.readList(path.getParent(), schemaContext, domTransactionChain,
113                                         schemaNode);
114                         final OrderedMapNode readList = (OrderedMapNode) readData;
115                         if ((readList == null) || readList.getValue().isEmpty()) {
116                             makePost(path, data, schemaContext, domTransactionChain, newReadWriteTransaction);
117                             return newReadWriteTransaction.submit();
118                         } else {
119                             newReadWriteTransaction.delete(LogicalDatastoreType.CONFIGURATION,
120                                     path.getParent().getParent());
121                             simplePost(newReadWriteTransaction, LogicalDatastoreType.CONFIGURATION, path, data,
122                                     schemaContext, domTransactionChain);
123                             makePost(path, readData, schemaContext, domTransactionChain,
124                                     newReadWriteTransaction);
125                             return newReadWriteTransaction.submit();
126                         }
127                     } else {
128                         final NormalizedNode<?, ?> readData = PutDataTransactionUtil
129                                         .readList(path.getParent(), schemaContext, domTransactionChain, schemaNode);
130
131                         final OrderedLeafSetNode<?> readLeafList = (OrderedLeafSetNode<?>) readData;
132                         if ((readLeafList == null) || readLeafList.getValue().isEmpty()) {
133                             makePost(path, data, schemaContext, domTransactionChain, newReadWriteTransaction);
134                             return newReadWriteTransaction.submit();
135                         } else {
136                             newReadWriteTransaction.delete(LogicalDatastoreType.CONFIGURATION,
137                                     path.getParent().getParent());
138                             simplePost(newReadWriteTransaction, LogicalDatastoreType.CONFIGURATION, path, data,
139                                     schemaContext, domTransactionChain);
140                             makePost(path, readData, schemaContext, domTransactionChain, newReadWriteTransaction);
141                             return newReadWriteTransaction.submit();
142                         }
143                     }
144                 case "last":
145                     makePost(path, data, schemaContext, domTransactionChain, newReadWriteTransaction);
146                     return newReadWriteTransaction.submit();
147                 case "before":
148                     if (schemaNode instanceof ListSchemaNode) {
149                         final NormalizedNode<?, ?> readData =
150                                 PutDataTransactionUtil.readList(path.getParent(), schemaContext, domTransactionChain,
151                                         schemaNode);
152                         final OrderedMapNode readList = (OrderedMapNode) readData;
153                         if ((readList == null) || readList.getValue().isEmpty()) {
154                             makePost(path, data, schemaContext, domTransactionChain, newReadWriteTransaction);
155                             return newReadWriteTransaction.submit();
156                         } else {
157                             insertWithPointListPost(newReadWriteTransaction, LogicalDatastoreType.CONFIGURATION, path,
158                                     data, schemaContext, point, readList, true, domTransactionChain);
159                             return newReadWriteTransaction.submit();
160                         }
161                     } else {
162                         final NormalizedNode<?, ?> readData =
163                                 PutDataTransactionUtil.readList(path.getParent(), schemaContext, domTransactionChain,
164                                         schemaNode);
165
166                         final OrderedLeafSetNode<?> readLeafList = (OrderedLeafSetNode<?>) readData;
167                         if ((readLeafList == null) || readLeafList.getValue().isEmpty()) {
168                             makePost(path, data, schemaContext, domTransactionChain, newReadWriteTransaction);
169                             return newReadWriteTransaction.submit();
170                         } else {
171                             insertWithPointLeafListPost(newReadWriteTransaction, LogicalDatastoreType.CONFIGURATION,
172                                     path, data, schemaContext, point, readLeafList, true, domTransactionChain);
173                             return newReadWriteTransaction.submit();
174                         }
175                     }
176                 case "after":
177                     if (schemaNode instanceof ListSchemaNode) {
178                         final NormalizedNode<?, ?> readData =
179                                 PutDataTransactionUtil.readList(path.getParent(), schemaContext, domTransactionChain,
180                                         schemaNode);
181                         final OrderedMapNode readList = (OrderedMapNode) readData;
182                         if ((readList == null) || readList.getValue().isEmpty()) {
183                             makePost(path, data, schemaContext, domTransactionChain, newReadWriteTransaction);
184                             return newReadWriteTransaction.submit();
185                         } else {
186                             insertWithPointListPost(newReadWriteTransaction, LogicalDatastoreType.CONFIGURATION, path,
187                                     data, schemaContext, point, readList, false, domTransactionChain);
188                             return newReadWriteTransaction.submit();
189                         }
190                     } else {
191                         final NormalizedNode<?, ?> readData =
192                                 PutDataTransactionUtil.readList(path.getParent(), schemaContext, domTransactionChain,
193                                         schemaNode);
194
195                         final OrderedLeafSetNode<?> readLeafList = (OrderedLeafSetNode<?>) readData;
196                         if ((readLeafList == null) || readLeafList.getValue().isEmpty()) {
197                             makePost(path, data, schemaContext, domTransactionChain, newReadWriteTransaction);
198                             return newReadWriteTransaction.submit();
199                         } else {
200                             insertWithPointLeafListPost(newReadWriteTransaction, LogicalDatastoreType.CONFIGURATION,
201                                     path, data, schemaContext, point, readLeafList, true, domTransactionChain);
202                             return newReadWriteTransaction.submit();
203                         }
204                     }
205                 default:
206                     throw new RestconfDocumentedException(
207                             "Used bad value of insert parameter. Possible values are first, last, before or after, "
208                                     + "but was: " + insert);
209             }
210         }
211     }
212
213     private static void insertWithPointLeafListPost(final DOMDataReadWriteTransaction rwTransaction,
214             final LogicalDatastoreType datastore, final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload,
215             final SchemaContext schemaContext, final String point, final OrderedLeafSetNode<?> readLeafList,
216             final boolean before, final DOMTransactionChain domTransactionChain) {
217         rwTransaction.delete(datastore, path.getParent().getParent());
218         final InstanceIdentifierContext<?> instanceIdentifier =
219                 ControllerContext.getInstance().toInstanceIdentifier(point);
220         int lastItemPosition = 0;
221         for (final LeafSetEntryNode<?> nodeChild : readLeafList.getValue()) {
222             if (nodeChild.getIdentifier().equals(instanceIdentifier.getInstanceIdentifier().getLastPathArgument())) {
223                 break;
224             }
225             lastItemPosition++;
226         }
227         if (!before) {
228             lastItemPosition++;
229         }
230         int lastInsertedPosition = 0;
231         final NormalizedNode<?, ?> emptySubtree =
232                 ImmutableNodes.fromInstanceId(schemaContext, path.getParent().getParent());
233         rwTransaction.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
234         for (final LeafSetEntryNode<?> nodeChild : readLeafList.getValue()) {
235             if (lastInsertedPosition == lastItemPosition) {
236                 TransactionUtil.checkItemDoesNotExists(domTransactionChain, rwTransaction, datastore, path,
237                         RestconfDataServiceConstant.PostData.POST_TX_TYPE);
238                 rwTransaction.put(datastore, path, payload);
239             }
240             final YangInstanceIdentifier childPath = path.getParent().getParent().node(nodeChild.getIdentifier());
241             TransactionUtil.checkItemDoesNotExists(domTransactionChain, rwTransaction, datastore, childPath,
242                     RestconfDataServiceConstant.PostData.POST_TX_TYPE);
243             rwTransaction.put(datastore, childPath, nodeChild);
244             lastInsertedPosition++;
245         }
246     }
247
248     private static void insertWithPointListPost(final DOMDataReadWriteTransaction rwTransaction,
249             final LogicalDatastoreType datastore, final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload,
250             final SchemaContext schemaContext, final String point, final MapNode readList, final boolean before,
251             final DOMTransactionChain domTransactionChain) {
252         rwTransaction.delete(datastore, path.getParent().getParent());
253         final InstanceIdentifierContext<?> instanceIdentifier =
254                 ControllerContext.getInstance().toInstanceIdentifier(point);
255         int lastItemPosition = 0;
256         for (final MapEntryNode mapEntryNode : readList.getValue()) {
257             if (mapEntryNode.getIdentifier().equals(instanceIdentifier.getInstanceIdentifier().getLastPathArgument())) {
258                 break;
259             }
260             lastItemPosition++;
261         }
262         if (!before) {
263             lastItemPosition++;
264         }
265         int lastInsertedPosition = 0;
266         final NormalizedNode<?, ?> emptySubtree =
267                 ImmutableNodes.fromInstanceId(schemaContext, path.getParent().getParent());
268         rwTransaction.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
269         for (final MapEntryNode mapEntryNode : readList.getValue()) {
270             if (lastInsertedPosition == lastItemPosition) {
271                 TransactionUtil.checkItemDoesNotExists(domTransactionChain, rwTransaction, datastore, path,
272                         RestconfDataServiceConstant.PostData.POST_TX_TYPE);
273                 rwTransaction.put(datastore, path, payload);
274             }
275             final YangInstanceIdentifier childPath = path.getParent().getParent().node(mapEntryNode.getIdentifier());
276             TransactionUtil.checkItemDoesNotExists(domTransactionChain, rwTransaction, datastore, childPath,
277                     RestconfDataServiceConstant.PostData.POST_TX_TYPE);
278             rwTransaction.put(datastore, childPath, mapEntryNode);
279             lastInsertedPosition++;
280         }
281     }
282
283     private static void makePost(final YangInstanceIdentifier path, final NormalizedNode<?, ?> data,
284             final SchemaContext schemaContext, final DOMTransactionChain transactionChain,
285             final DOMDataReadWriteTransaction transaction) {
286         if (data instanceof MapNode) {
287             boolean merge = false;
288             for (final MapEntryNode child : ((MapNode) data).getValue()) {
289                 final YangInstanceIdentifier childPath = path.node(child.getIdentifier());
290                 TransactionUtil.checkItemDoesNotExists(
291                         transactionChain, transaction, LogicalDatastoreType.CONFIGURATION, childPath,
292                         RestconfDataServiceConstant.PostData.POST_TX_TYPE);
293                 if (!merge) {
294                     merge = true;
295                     TransactionUtil.ensureParentsByMerge(path, schemaContext, transaction);
296                     final NormalizedNode<?, ?> emptySubTree = ImmutableNodes.fromInstanceId(schemaContext, path);
297                     transaction.merge(LogicalDatastoreType.CONFIGURATION,
298                             YangInstanceIdentifier.create(emptySubTree.getIdentifier()), emptySubTree);
299                 }
300                 transaction.put(LogicalDatastoreType.CONFIGURATION, childPath, child);
301             }
302         } else {
303             TransactionUtil.checkItemDoesNotExists(
304                     transactionChain, transaction, LogicalDatastoreType.CONFIGURATION, path,
305                     RestconfDataServiceConstant.PostData.POST_TX_TYPE);
306
307             TransactionUtil.ensureParentsByMerge(path, schemaContext, transaction);
308             transaction.put(LogicalDatastoreType.CONFIGURATION, path, data);
309         }
310     }
311
312     /**
313      * Get location from {@link YangInstanceIdentifier} and {@link UriInfo}.
314      *
315      * @param uriInfo
316      *             uri info
317      * @param transactionNode
318      *             wrapper for data of transaction
319      * @param schemaContextRef
320      *            reference to {@link SchemaContext}
321      * @return {@link URI}
322      */
323     private static URI resolveLocation(final UriInfo uriInfo, final TransactionVarsWrapper transactionNode,
324             final SchemaContextRef schemaContextRef) {
325         if (uriInfo == null) {
326             return null;
327         }
328
329         final UriBuilder uriBuilder = uriInfo.getBaseUriBuilder();
330         uriBuilder.path("data");
331         uriBuilder.path(ParserIdentifier
332                 .stringFromYangInstanceIdentifier(transactionNode.getInstanceIdentifier().getInstanceIdentifier(),
333                 schemaContextRef.get()));
334
335         return uriBuilder.build();
336     }
337
338     private static void simplePost(final DOMDataReadWriteTransaction rwTransaction,
339             final LogicalDatastoreType datastore, final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload,
340             final SchemaContext schemaContext, final DOMTransactionChain transactionChain) {
341         TransactionUtil.checkItemDoesNotExists(transactionChain, rwTransaction, datastore, path,
342                 RestconfDataServiceConstant.PostData.POST_TX_TYPE);
343         TransactionUtil.ensureParentsByMerge(path, schemaContext, rwTransaction);
344         rwTransaction.put(datastore, path, payload);
345     }
346 }