2 * Copyright (c) 2016 Cisco Systems, Inc. and others. All rights reserved.
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
8 package org.opendaylight.restconf.nb.rfc8040.rests.utils;
10 import com.google.common.util.concurrent.FluentFuture;
12 import java.util.Collection;
13 import java.util.Optional;
14 import javax.ws.rs.core.Response;
15 import javax.ws.rs.core.Response.Status;
16 import javax.ws.rs.core.UriInfo;
17 import org.opendaylight.mdsal.common.api.CommitInfo;
18 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
19 import org.opendaylight.mdsal.dom.api.DOMDataTreeReadWriteTransaction;
20 import org.opendaylight.mdsal.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.common.errors.RestconfError;
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.EffectiveModelContext;
38 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
39 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
42 * Util class to post data to DS.
45 public final class PostDataTransactionUtil {
46 private PostDataTransactionUtil() {
51 * Check mount point and prepare variables for post data. Close {@link DOMTransactionChain} inside of object
52 * {@link TransactionVarsWrapper} provided as a parameter.
58 * @param transactionNode
59 * wrapper for transaction data
60 * @param schemaContextRef
61 * reference to actual {@link SchemaContext}
66 * @return {@link Response}
68 public static Response postData(final UriInfo uriInfo, final NormalizedNodeContext payload,
69 final TransactionVarsWrapper transactionNode, final SchemaContextRef schemaContextRef, final String insert,
71 final FluentFuture<? extends CommitInfo> future = submitData(
72 payload.getInstanceIdentifierContext().getInstanceIdentifier(), payload.getData(),
73 transactionNode, schemaContextRef.get(), insert, point);
74 final URI location = resolveLocation(uriInfo, transactionNode, schemaContextRef, payload.getData());
75 final ResponseFactory dataFactory = new ResponseFactory(Status.CREATED).location(location);
76 //This method will close transactionChain
77 FutureCallbackTx.addCallback(future, RestconfDataServiceConstant.PostData.POST_TX_TYPE, dataFactory,
78 transactionNode.getTransactionChain());
79 return dataFactory.build();
89 * @param transactionNode
90 * wrapper for data to transaction
91 * @param schemaContext
92 * schema context of data
97 * @return {@link FluentFuture}
99 private static FluentFuture<? extends CommitInfo> submitData(final YangInstanceIdentifier path,
100 final NormalizedNode<?, ?> data, final TransactionVarsWrapper transactionNode,
101 final EffectiveModelContext schemaContext, final String insert, final String point) {
102 final DOMTransactionChain transactionChain = transactionNode.getTransactionChain();
103 final DOMDataTreeReadWriteTransaction newReadWriteTransaction = transactionChain.newReadWriteTransaction();
104 if (insert == null) {
105 makePost(path, data, schemaContext, transactionChain, newReadWriteTransaction);
106 return newReadWriteTransaction.commit();
109 final DataSchemaNode schemaNode = PutDataTransactionUtil.checkListAndOrderedType(schemaContext, path);
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, transactionChain, newReadWriteTransaction);
118 return newReadWriteTransaction.commit();
121 newReadWriteTransaction.delete(LogicalDatastoreType.CONFIGURATION, path.getParent().getParent());
122 simplePost(newReadWriteTransaction, LogicalDatastoreType.CONFIGURATION, path, data, schemaContext,
124 makePost(path, readData, schemaContext, transactionChain, newReadWriteTransaction);
125 return newReadWriteTransaction.commit();
127 final NormalizedNode<?, ?> readData = PutDataTransactionUtil.readList(path.getParent(),
128 schemaContext, transactionNode.getTransactionChainHandler(), schemaNode);
130 final OrderedLeafSetNode<?> readLeafList = (OrderedLeafSetNode<?>) readData;
131 if (readLeafList == null || readLeafList.getValue().isEmpty()) {
132 makePost(path, data, schemaContext, transactionChain,
133 newReadWriteTransaction);
134 return newReadWriteTransaction.commit();
137 newReadWriteTransaction.delete(LogicalDatastoreType.CONFIGURATION, path.getParent().getParent());
138 simplePost(newReadWriteTransaction, LogicalDatastoreType.CONFIGURATION, path, data, schemaContext,
140 makePost(path, readData, schemaContext, transactionChain, newReadWriteTransaction);
141 return newReadWriteTransaction.commit();
144 makePost(path, data, schemaContext, transactionChain, newReadWriteTransaction);
145 return newReadWriteTransaction.commit();
147 if (schemaNode instanceof ListSchemaNode) {
148 final NormalizedNode<?, ?> readData = PutDataTransactionUtil.readList(path.getParent(),
149 schemaContext, transactionNode.getTransactionChainHandler(), schemaNode);
150 final OrderedMapNode readList = (OrderedMapNode) readData;
151 if (readList == null || readList.getValue().isEmpty()) {
152 makePost(path, data, schemaContext, transactionChain, newReadWriteTransaction);
153 return newReadWriteTransaction.commit();
156 insertWithPointListPost(newReadWriteTransaction, LogicalDatastoreType.CONFIGURATION, path,
157 data, schemaContext, point, readList, true, transactionChain);
158 return newReadWriteTransaction.commit();
160 final NormalizedNode<?, ?> readData = PutDataTransactionUtil.readList(path.getParent(),
161 schemaContext, transactionNode.getTransactionChainHandler(), schemaNode);
163 final OrderedLeafSetNode<?> readLeafList = (OrderedLeafSetNode<?>) readData;
164 if (readLeafList == null || readLeafList.getValue().isEmpty()) {
165 makePost(path, data, schemaContext, transactionChain, newReadWriteTransaction);
166 return newReadWriteTransaction.commit();
169 insertWithPointLeafListPost(newReadWriteTransaction, LogicalDatastoreType.CONFIGURATION,
170 path, data, schemaContext, point, readLeafList, true, transactionChain);
171 return newReadWriteTransaction.commit();
174 if (schemaNode instanceof ListSchemaNode) {
175 final NormalizedNode<?, ?> readData = PutDataTransactionUtil.readList(path.getParent(),
176 schemaContext, transactionNode.getTransactionChainHandler(), schemaNode);
177 final OrderedMapNode readList = (OrderedMapNode) readData;
178 if (readList == null || readList.getValue().isEmpty()) {
179 makePost(path, data, schemaContext, transactionChain, newReadWriteTransaction);
180 return newReadWriteTransaction.commit();
183 insertWithPointListPost(newReadWriteTransaction, LogicalDatastoreType.CONFIGURATION, path,
184 data, schemaContext, point, readList, false,
186 return newReadWriteTransaction.commit();
188 final NormalizedNode<?, ?> readData = PutDataTransactionUtil.readList(path.getParent(),
189 schemaContext, transactionNode.getTransactionChainHandler(), schemaNode);
191 final OrderedLeafSetNode<?> readLeafList = (OrderedLeafSetNode<?>) readData;
192 if (readLeafList == null || readLeafList.getValue().isEmpty()) {
193 makePost(path, data, schemaContext, transactionChain, newReadWriteTransaction);
194 return newReadWriteTransaction.commit();
197 insertWithPointLeafListPost(newReadWriteTransaction, LogicalDatastoreType.CONFIGURATION,
198 path, data, schemaContext, point, readLeafList, true, transactionChain);
199 return newReadWriteTransaction.commit();
202 throw new RestconfDocumentedException(
203 "Used bad value of insert parameter. Possible values are first, last, before or after, but was: "
204 + insert, RestconfError.ErrorType.PROTOCOL, RestconfError.ErrorTag.BAD_ATTRIBUTE);
208 private static void insertWithPointLeafListPost(final DOMDataTreeReadWriteTransaction rwTransaction,
209 final LogicalDatastoreType datastore, final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload,
210 final EffectiveModelContext schemaContext, final String point, final OrderedLeafSetNode<?> readLeafList,
211 final boolean before, final DOMTransactionChain transactionChain) {
212 rwTransaction.delete(datastore, path.getParent().getParent());
213 final InstanceIdentifierContext<?> instanceIdentifier =
214 ParserIdentifier.toInstanceIdentifier(point, schemaContext, Optional.empty());
215 int lastItemPosition = 0;
216 for (final LeafSetEntryNode<?> nodeChild : readLeafList.getValue()) {
217 if (nodeChild.getIdentifier().equals(instanceIdentifier.getInstanceIdentifier().getLastPathArgument())) {
225 int lastInsertedPosition = 0;
226 final NormalizedNode<?, ?> emptySubtree =
227 ImmutableNodes.fromInstanceId(schemaContext, path.getParent().getParent());
228 rwTransaction.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
229 for (final LeafSetEntryNode<?> nodeChild : readLeafList.getValue()) {
230 if (lastInsertedPosition == lastItemPosition) {
231 TransactionUtil.checkItemDoesNotExists(transactionChain, rwTransaction, datastore, path,
232 RestconfDataServiceConstant.PostData.POST_TX_TYPE);
233 rwTransaction.put(datastore, path, payload);
235 final YangInstanceIdentifier childPath = path.getParent().getParent().node(nodeChild.getIdentifier());
236 TransactionUtil.checkItemDoesNotExists(transactionChain, rwTransaction, datastore, childPath,
237 RestconfDataServiceConstant.PostData.POST_TX_TYPE);
238 rwTransaction.put(datastore, childPath, nodeChild);
239 lastInsertedPosition++;
243 private static void insertWithPointListPost(final DOMDataTreeReadWriteTransaction rwTransaction,
244 final LogicalDatastoreType datastore, final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload,
245 final EffectiveModelContext schemaContext, final String point, final MapNode readList, final boolean before,
246 final DOMTransactionChain transactionChain) {
247 rwTransaction.delete(datastore, path.getParent().getParent());
248 final InstanceIdentifierContext<?> instanceIdentifier =
249 ParserIdentifier.toInstanceIdentifier(point, schemaContext, Optional.empty());
250 int lastItemPosition = 0;
251 for (final MapEntryNode mapEntryNode : readList.getValue()) {
252 if (mapEntryNode.getIdentifier().equals(instanceIdentifier.getInstanceIdentifier().getLastPathArgument())) {
260 int lastInsertedPosition = 0;
261 final NormalizedNode<?, ?> emptySubtree =
262 ImmutableNodes.fromInstanceId(schemaContext, path.getParent().getParent());
263 rwTransaction.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
264 for (final MapEntryNode mapEntryNode : readList.getValue()) {
265 if (lastInsertedPosition == lastItemPosition) {
266 TransactionUtil.checkItemDoesNotExists(transactionChain, rwTransaction, datastore, path,
267 RestconfDataServiceConstant.PostData.POST_TX_TYPE);
268 rwTransaction.put(datastore, path, payload);
270 final YangInstanceIdentifier childPath = path.getParent().getParent().node(mapEntryNode.getIdentifier());
271 TransactionUtil.checkItemDoesNotExists(transactionChain, rwTransaction, datastore, childPath,
272 RestconfDataServiceConstant.PostData.POST_TX_TYPE);
273 rwTransaction.put(datastore, childPath, mapEntryNode);
274 lastInsertedPosition++;
278 private static void makePost(final YangInstanceIdentifier path, final NormalizedNode<?, ?> data,
279 final SchemaContext schemaContext, final DOMTransactionChain transactionChain,
280 final DOMDataTreeReadWriteTransaction transaction) {
281 if (data instanceof MapNode) {
282 boolean merge = false;
283 for (final MapEntryNode child : ((MapNode) data).getValue()) {
284 final YangInstanceIdentifier childPath = path.node(child.getIdentifier());
285 TransactionUtil.checkItemDoesNotExists(
286 transactionChain, transaction, LogicalDatastoreType.CONFIGURATION, childPath,
287 RestconfDataServiceConstant.PostData.POST_TX_TYPE);
290 TransactionUtil.ensureParentsByMerge(path, schemaContext, transaction);
291 final NormalizedNode<?, ?> emptySubTree = ImmutableNodes.fromInstanceId(schemaContext, path);
292 transaction.merge(LogicalDatastoreType.CONFIGURATION,
293 YangInstanceIdentifier.create(emptySubTree.getIdentifier()), emptySubTree);
295 transaction.put(LogicalDatastoreType.CONFIGURATION, childPath, child);
298 TransactionUtil.checkItemDoesNotExists(
299 transactionChain, transaction, LogicalDatastoreType.CONFIGURATION, path,
300 RestconfDataServiceConstant.PostData.POST_TX_TYPE);
302 TransactionUtil.ensureParentsByMerge(path, schemaContext, transaction);
303 transaction.put(LogicalDatastoreType.CONFIGURATION, path, data);
308 * Get location from {@link YangInstanceIdentifier} and {@link UriInfo}.
312 * @param transactionNode
313 * wrapper for data of transaction
314 * @param schemaContextRef
315 * reference to {@link SchemaContext}
316 * @return {@link URI}
318 private static URI resolveLocation(final UriInfo uriInfo, final TransactionVarsWrapper transactionNode,
319 final SchemaContextRef schemaContextRef, final NormalizedNode<?, ?> data) {
320 if (uriInfo == null) {
324 YangInstanceIdentifier path = transactionNode.getInstanceIdentifier().getInstanceIdentifier();
326 if (data instanceof MapNode) {
327 final Collection<MapEntryNode> children = ((MapNode) data).getValue();
328 if (!children.isEmpty()) {
329 path = path.node(children.iterator().next().getIdentifier());
333 return uriInfo.getBaseUriBuilder()
335 .path(ParserIdentifier.stringFromYangInstanceIdentifier(path, schemaContextRef.get()))
339 private static void simplePost(final DOMDataTreeReadWriteTransaction rwTransaction,
340 final LogicalDatastoreType datastore, final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload,
341 final SchemaContext schemaContext, final DOMTransactionChain transactionChain) {
342 TransactionUtil.checkItemDoesNotExists(transactionChain, rwTransaction, datastore, path,
343 RestconfDataServiceConstant.PostData.POST_TX_TYPE);
344 TransactionUtil.ensureParentsByMerge(path, schemaContext, rwTransaction);
345 rwTransaction.put(datastore, path, payload);