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