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.rests.transactions.TransactionVarsWrapper;
26 import org.opendaylight.restconf.nb.rfc8040.utils.parser.ParserIdentifier;
27 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
28 import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode;
29 import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
30 import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
31 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
32 import org.opendaylight.yangtools.yang.data.api.schema.OrderedLeafSetNode;
33 import org.opendaylight.yangtools.yang.data.api.schema.OrderedMapNode;
34 import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
35 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
36 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
37 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
38 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
41 * Util class to post data to DS.
44 public final class PostDataTransactionUtil {
45 private PostDataTransactionUtil() {
50 * Check mount point and prepare variables for post data. Close {@link DOMTransactionChain} inside of object
51 * {@link TransactionVarsWrapper} provided as a parameter.
57 * @param transactionNode
58 * wrapper for transaction data
59 * @param schemaContext
60 * reference to current {@link EffectiveModelContext}
65 * @return {@link Response}
67 public static Response postData(final UriInfo uriInfo, final NormalizedNodeContext payload,
68 final TransactionVarsWrapper transactionNode, final EffectiveModelContext schemaContext,
69 final String insert, final String point) {
70 final FluentFuture<? extends CommitInfo> future = submitData(
71 payload.getInstanceIdentifierContext().getInstanceIdentifier(), payload.getData(),
72 transactionNode, schemaContext, insert, point);
73 final URI location = resolveLocation(uriInfo, transactionNode, schemaContext, payload.getData());
74 final ResponseFactory dataFactory = new ResponseFactory(Status.CREATED).location(location);
75 //This method will close transactionChain
76 FutureCallbackTx.addCallback(future, RestconfDataServiceConstant.PostData.POST_TX_TYPE, dataFactory,
77 transactionNode.getTransactionChain());
78 return dataFactory.build();
88 * @param transactionNode
89 * wrapper for data to transaction
90 * @param schemaContext
91 * schema context of data
96 * @return {@link FluentFuture}
98 private static FluentFuture<? extends CommitInfo> submitData(final YangInstanceIdentifier path,
99 final NormalizedNode<?, ?> data, final TransactionVarsWrapper transactionNode,
100 final EffectiveModelContext schemaContext, final String insert, final String point) {
101 final DOMTransactionChain transactionChain = transactionNode.getTransactionChain();
102 final DOMDataTreeReadWriteTransaction newReadWriteTransaction = transactionChain.newReadWriteTransaction();
103 if (insert == null) {
104 makePost(path, data, schemaContext, transactionChain, newReadWriteTransaction);
105 return newReadWriteTransaction.commit();
108 final DataSchemaNode schemaNode = PutDataTransactionUtil.checkListAndOrderedType(schemaContext, path);
111 if (schemaNode instanceof ListSchemaNode) {
112 final NormalizedNode<?, ?> readData = PutDataTransactionUtil.readList(path.getParent(),
113 schemaContext, transactionNode.getTransactionChainHandler(), schemaNode);
114 final OrderedMapNode readList = (OrderedMapNode) readData;
115 if (readList == null || readList.getValue().isEmpty()) {
116 makePost(path, data, schemaContext, transactionChain, newReadWriteTransaction);
117 return newReadWriteTransaction.commit();
120 newReadWriteTransaction.delete(LogicalDatastoreType.CONFIGURATION, path.getParent().getParent());
121 simplePost(newReadWriteTransaction, LogicalDatastoreType.CONFIGURATION, path, data, schemaContext,
123 makePost(path, readData, schemaContext, transactionChain, newReadWriteTransaction);
124 return newReadWriteTransaction.commit();
126 final NormalizedNode<?, ?> readData = PutDataTransactionUtil.readList(path.getParent(),
127 schemaContext, transactionNode.getTransactionChainHandler(), schemaNode);
129 final OrderedLeafSetNode<?> readLeafList = (OrderedLeafSetNode<?>) readData;
130 if (readLeafList == null || readLeafList.getValue().isEmpty()) {
131 makePost(path, data, schemaContext, transactionChain,
132 newReadWriteTransaction);
133 return newReadWriteTransaction.commit();
136 newReadWriteTransaction.delete(LogicalDatastoreType.CONFIGURATION, path.getParent().getParent());
137 simplePost(newReadWriteTransaction, LogicalDatastoreType.CONFIGURATION, path, data, schemaContext,
139 makePost(path, readData, schemaContext, transactionChain, newReadWriteTransaction);
140 return newReadWriteTransaction.commit();
143 makePost(path, data, schemaContext, transactionChain, newReadWriteTransaction);
144 return newReadWriteTransaction.commit();
146 if (schemaNode instanceof ListSchemaNode) {
147 final NormalizedNode<?, ?> readData = PutDataTransactionUtil.readList(path.getParent(),
148 schemaContext, transactionNode.getTransactionChainHandler(), schemaNode);
149 final OrderedMapNode readList = (OrderedMapNode) readData;
150 if (readList == null || readList.getValue().isEmpty()) {
151 makePost(path, data, schemaContext, transactionChain, newReadWriteTransaction);
152 return newReadWriteTransaction.commit();
155 insertWithPointListPost(newReadWriteTransaction, LogicalDatastoreType.CONFIGURATION, path,
156 data, schemaContext, point, readList, true, transactionChain);
157 return newReadWriteTransaction.commit();
159 final NormalizedNode<?, ?> readData = PutDataTransactionUtil.readList(path.getParent(),
160 schemaContext, transactionNode.getTransactionChainHandler(), schemaNode);
162 final OrderedLeafSetNode<?> readLeafList = (OrderedLeafSetNode<?>) readData;
163 if (readLeafList == null || readLeafList.getValue().isEmpty()) {
164 makePost(path, data, schemaContext, transactionChain, newReadWriteTransaction);
165 return newReadWriteTransaction.commit();
168 insertWithPointLeafListPost(newReadWriteTransaction, LogicalDatastoreType.CONFIGURATION,
169 path, data, schemaContext, point, readLeafList, true, transactionChain);
170 return newReadWriteTransaction.commit();
173 if (schemaNode instanceof ListSchemaNode) {
174 final NormalizedNode<?, ?> readData = PutDataTransactionUtil.readList(path.getParent(),
175 schemaContext, transactionNode.getTransactionChainHandler(), schemaNode);
176 final OrderedMapNode readList = (OrderedMapNode) readData;
177 if (readList == null || readList.getValue().isEmpty()) {
178 makePost(path, data, schemaContext, transactionChain, newReadWriteTransaction);
179 return newReadWriteTransaction.commit();
182 insertWithPointListPost(newReadWriteTransaction, LogicalDatastoreType.CONFIGURATION, path,
183 data, schemaContext, point, readList, false,
185 return newReadWriteTransaction.commit();
187 final NormalizedNode<?, ?> readData = PutDataTransactionUtil.readList(path.getParent(),
188 schemaContext, transactionNode.getTransactionChainHandler(), schemaNode);
190 final OrderedLeafSetNode<?> readLeafList = (OrderedLeafSetNode<?>) readData;
191 if (readLeafList == null || readLeafList.getValue().isEmpty()) {
192 makePost(path, data, schemaContext, transactionChain, newReadWriteTransaction);
193 return newReadWriteTransaction.commit();
196 insertWithPointLeafListPost(newReadWriteTransaction, LogicalDatastoreType.CONFIGURATION,
197 path, data, schemaContext, point, readLeafList, true, transactionChain);
198 return newReadWriteTransaction.commit();
201 throw new RestconfDocumentedException(
202 "Used bad value of insert parameter. Possible values are first, last, before or after, but was: "
203 + insert, RestconfError.ErrorType.PROTOCOL, RestconfError.ErrorTag.BAD_ATTRIBUTE);
207 private static void insertWithPointLeafListPost(final DOMDataTreeReadWriteTransaction rwTransaction,
208 final LogicalDatastoreType datastore, final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload,
209 final EffectiveModelContext schemaContext, final String point, final OrderedLeafSetNode<?> readLeafList,
210 final boolean before, final DOMTransactionChain transactionChain) {
211 rwTransaction.delete(datastore, path.getParent().getParent());
212 final InstanceIdentifierContext<?> instanceIdentifier =
213 ParserIdentifier.toInstanceIdentifier(point, schemaContext, Optional.empty());
214 int lastItemPosition = 0;
215 for (final LeafSetEntryNode<?> nodeChild : readLeafList.getValue()) {
216 if (nodeChild.getIdentifier().equals(instanceIdentifier.getInstanceIdentifier().getLastPathArgument())) {
224 int lastInsertedPosition = 0;
225 final NormalizedNode<?, ?> emptySubtree =
226 ImmutableNodes.fromInstanceId(schemaContext, path.getParent().getParent());
227 rwTransaction.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
228 for (final LeafSetEntryNode<?> nodeChild : readLeafList.getValue()) {
229 if (lastInsertedPosition == lastItemPosition) {
230 TransactionUtil.checkItemDoesNotExists(transactionChain, rwTransaction, datastore, path,
231 RestconfDataServiceConstant.PostData.POST_TX_TYPE);
232 rwTransaction.put(datastore, path, payload);
234 final YangInstanceIdentifier childPath = path.getParent().getParent().node(nodeChild.getIdentifier());
235 TransactionUtil.checkItemDoesNotExists(transactionChain, rwTransaction, datastore, childPath,
236 RestconfDataServiceConstant.PostData.POST_TX_TYPE);
237 rwTransaction.put(datastore, childPath, nodeChild);
238 lastInsertedPosition++;
242 private static void insertWithPointListPost(final DOMDataTreeReadWriteTransaction rwTransaction,
243 final LogicalDatastoreType datastore, final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload,
244 final EffectiveModelContext schemaContext, final String point, final MapNode readList, final boolean before,
245 final DOMTransactionChain transactionChain) {
246 rwTransaction.delete(datastore, path.getParent().getParent());
247 final InstanceIdentifierContext<?> instanceIdentifier =
248 ParserIdentifier.toInstanceIdentifier(point, schemaContext, Optional.empty());
249 int lastItemPosition = 0;
250 for (final MapEntryNode mapEntryNode : readList.getValue()) {
251 if (mapEntryNode.getIdentifier().equals(instanceIdentifier.getInstanceIdentifier().getLastPathArgument())) {
259 int lastInsertedPosition = 0;
260 final NormalizedNode<?, ?> emptySubtree =
261 ImmutableNodes.fromInstanceId(schemaContext, path.getParent().getParent());
262 rwTransaction.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
263 for (final MapEntryNode mapEntryNode : readList.getValue()) {
264 if (lastInsertedPosition == lastItemPosition) {
265 TransactionUtil.checkItemDoesNotExists(transactionChain, rwTransaction, datastore, path,
266 RestconfDataServiceConstant.PostData.POST_TX_TYPE);
267 rwTransaction.put(datastore, path, payload);
269 final YangInstanceIdentifier childPath = path.getParent().getParent().node(mapEntryNode.getIdentifier());
270 TransactionUtil.checkItemDoesNotExists(transactionChain, rwTransaction, datastore, childPath,
271 RestconfDataServiceConstant.PostData.POST_TX_TYPE);
272 rwTransaction.put(datastore, childPath, mapEntryNode);
273 lastInsertedPosition++;
277 private static void makePost(final YangInstanceIdentifier path, final NormalizedNode<?, ?> data,
278 final SchemaContext schemaContext, final DOMTransactionChain transactionChain,
279 final DOMDataTreeReadWriteTransaction transaction) {
280 if (data instanceof MapNode) {
281 boolean merge = false;
282 for (final MapEntryNode child : ((MapNode) data).getValue()) {
283 final YangInstanceIdentifier childPath = path.node(child.getIdentifier());
284 TransactionUtil.checkItemDoesNotExists(
285 transactionChain, transaction, LogicalDatastoreType.CONFIGURATION, childPath,
286 RestconfDataServiceConstant.PostData.POST_TX_TYPE);
289 TransactionUtil.ensureParentsByMerge(path, schemaContext, transaction);
290 final NormalizedNode<?, ?> emptySubTree = ImmutableNodes.fromInstanceId(schemaContext, path);
291 transaction.merge(LogicalDatastoreType.CONFIGURATION,
292 YangInstanceIdentifier.create(emptySubTree.getIdentifier()), emptySubTree);
294 transaction.put(LogicalDatastoreType.CONFIGURATION, childPath, child);
297 TransactionUtil.checkItemDoesNotExists(
298 transactionChain, transaction, LogicalDatastoreType.CONFIGURATION, path,
299 RestconfDataServiceConstant.PostData.POST_TX_TYPE);
301 TransactionUtil.ensureParentsByMerge(path, schemaContext, transaction);
302 transaction.put(LogicalDatastoreType.CONFIGURATION, path, data);
307 * Get location from {@link YangInstanceIdentifier} and {@link UriInfo}.
311 * @param transactionNode
312 * wrapper for data of transaction
313 * @param schemaContext
314 * reference to {@link SchemaContext}
315 * @return {@link URI}
317 private static URI resolveLocation(final UriInfo uriInfo, final TransactionVarsWrapper transactionNode,
318 final EffectiveModelContext schemaContext, final NormalizedNode<?, ?> data) {
319 if (uriInfo == null) {
323 YangInstanceIdentifier path = transactionNode.getInstanceIdentifier().getInstanceIdentifier();
325 if (data instanceof MapNode) {
326 final Collection<MapEntryNode> children = ((MapNode) data).getValue();
327 if (!children.isEmpty()) {
328 path = path.node(children.iterator().next().getIdentifier());
332 return uriInfo.getBaseUriBuilder()
334 .path(ParserIdentifier.stringFromYangInstanceIdentifier(path, schemaContext))
338 private static void simplePost(final DOMDataTreeReadWriteTransaction 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);