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.DOMTransactionChain;
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.errors.RestconfError;
24 import org.opendaylight.restconf.common.errors.RestconfError.ErrorTag;
25 import org.opendaylight.restconf.common.errors.RestconfError.ErrorType;
26 import org.opendaylight.restconf.nb.rfc8040.rests.transactions.RestconfStrategy;
27 import org.opendaylight.restconf.nb.rfc8040.rests.utils.RestconfDataServiceConstant.PostPutQueryParameters.Insert;
28 import org.opendaylight.restconf.nb.rfc8040.utils.parser.ParserIdentifier;
29 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
30 import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode;
31 import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
32 import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
33 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
34 import org.opendaylight.yangtools.yang.data.api.schema.OrderedLeafSetNode;
35 import org.opendaylight.yangtools.yang.data.api.schema.OrderedMapNode;
36 import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
37 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
38 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
39 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
40 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
41 import org.slf4j.Logger;
42 import org.slf4j.LoggerFactory;
45 * Util class to post data to DS.
48 public final class PostDataTransactionUtil {
49 private static final Logger LOG = LoggerFactory.getLogger(PostDataTransactionUtil.class);
51 private PostDataTransactionUtil() {
56 * Check mount point and prepare variables for post data. Close {@link DOMTransactionChain} if any inside of object
57 * {@link RestconfStrategy} provided as a parameter.
59 * @param uriInfo uri info
61 * @param strategy Object that perform the actual DS operations
62 * @param schemaContext reference to actual {@link EffectiveModelContext}
64 * @param insert insert
65 * @return {@link Response}
67 public static Response postData(final UriInfo uriInfo, final NormalizedNodeContext payload,
68 final RestconfStrategy strategy,
69 final EffectiveModelContext schemaContext, final Insert insert,
71 final YangInstanceIdentifier path = payload.getInstanceIdentifierContext().getInstanceIdentifier();
72 final FluentFuture<? extends CommitInfo> future = submitData(path, payload.getData(),
73 strategy, schemaContext, insert, point);
74 final URI location = resolveLocation(uriInfo, path, schemaContext, payload.getData());
75 final ResponseFactory dataFactory = new ResponseFactory(Status.CREATED).location(location);
76 //This method will close transactionChain if any
77 FutureCallbackTx.addCallback(future, RestconfDataServiceConstant.PostData.POST_TX_TYPE, dataFactory,
78 strategy.getTransactionChain());
79 return dataFactory.build();
87 * @param strategy object that perform the actual DS operations
88 * @param schemaContext schema context of data
89 * @param point query parameter
90 * @param insert query parameter
91 * @return {@link FluentFuture}
93 private static FluentFuture<? extends CommitInfo> submitData(final YangInstanceIdentifier path,
94 final NormalizedNode<?, ?> data,
95 final RestconfStrategy strategy,
96 final EffectiveModelContext schemaContext,
97 final Insert insert, final String point) {
98 strategy.prepareReadWriteExecution();
100 makePost(path, data, schemaContext, strategy);
101 return strategy.commit();
104 final DataSchemaNode schemaNode = PutDataTransactionUtil.checkListAndOrderedType(schemaContext, path);
107 if (schemaNode instanceof ListSchemaNode) {
108 final NormalizedNode<?, ?> readData = PutDataTransactionUtil.readList(strategy, path.getParent());
109 final OrderedMapNode readList = (OrderedMapNode) readData;
110 if (readList == null || readList.getValue().isEmpty()) {
111 makePost(path, data, schemaContext, strategy);
112 return strategy.commit();
115 strategy.delete(LogicalDatastoreType.CONFIGURATION, path.getParent().getParent());
116 simplePost(LogicalDatastoreType.CONFIGURATION, path, data, schemaContext, strategy);
117 makePost(path, readData, schemaContext, strategy);
118 return strategy.commit();
120 final NormalizedNode<?, ?> readData = PutDataTransactionUtil.readList(strategy, path.getParent());
122 final OrderedLeafSetNode<?> readLeafList = (OrderedLeafSetNode<?>) readData;
123 if (readLeafList == null || readLeafList.getValue().isEmpty()) {
124 makePost(path, data, schemaContext, strategy);
125 return strategy.commit();
128 strategy.delete(LogicalDatastoreType.CONFIGURATION, path.getParent().getParent());
129 simplePost(LogicalDatastoreType.CONFIGURATION, path, data, schemaContext, strategy);
130 makePost(path, readData, schemaContext, strategy);
131 return strategy.commit();
134 makePost(path, data, schemaContext, strategy);
135 return strategy.commit();
137 if (schemaNode instanceof ListSchemaNode) {
138 final NormalizedNode<?, ?> readData = PutDataTransactionUtil.readList(strategy, path.getParent());
139 final OrderedMapNode readList = (OrderedMapNode) readData;
140 if (readList == null || readList.getValue().isEmpty()) {
141 makePost(path, data, schemaContext, strategy);
142 return strategy.commit();
145 insertWithPointListPost(LogicalDatastoreType.CONFIGURATION, path,
146 data, schemaContext, point, readList, true, strategy);
147 return strategy.commit();
149 final NormalizedNode<?, ?> readData = PutDataTransactionUtil.readList(strategy, path.getParent());
151 final OrderedLeafSetNode<?> readLeafList = (OrderedLeafSetNode<?>) readData;
152 if (readLeafList == null || readLeafList.getValue().isEmpty()) {
153 makePost(path, data, schemaContext, strategy);
154 return strategy.commit();
157 insertWithPointLeafListPost(LogicalDatastoreType.CONFIGURATION,
158 path, data, schemaContext, point, readLeafList, true, strategy);
159 return strategy.commit();
162 if (schemaNode instanceof ListSchemaNode) {
163 final NormalizedNode<?, ?> readData = PutDataTransactionUtil.readList(strategy, path.getParent());
164 final OrderedMapNode readList = (OrderedMapNode) readData;
165 if (readList == null || readList.getValue().isEmpty()) {
166 makePost(path, data, schemaContext, strategy);
167 return strategy.commit();
170 insertWithPointListPost(LogicalDatastoreType.CONFIGURATION, path,
171 data, schemaContext, point, readList, false, strategy);
172 return strategy.commit();
174 final NormalizedNode<?, ?> readData = PutDataTransactionUtil.readList(strategy, path.getParent());
175 final OrderedLeafSetNode<?> readLeafList = (OrderedLeafSetNode<?>) readData;
176 if (readLeafList == null || readLeafList.getValue().isEmpty()) {
177 makePost(path, data, schemaContext, strategy);
178 return strategy.commit();
181 insertWithPointLeafListPost(LogicalDatastoreType.CONFIGURATION,
182 path, data, schemaContext, point, readLeafList, true, strategy);
183 return strategy.commit();
186 throw new RestconfDocumentedException(
187 "Used bad value of insert parameter. Possible values are first, last, before or after, but was: "
188 + insert, RestconfError.ErrorType.PROTOCOL, RestconfError.ErrorTag.BAD_ATTRIBUTE);
192 private static void insertWithPointLeafListPost(final LogicalDatastoreType datastore,
193 final YangInstanceIdentifier path,
194 final NormalizedNode<?, ?> payload,
195 final EffectiveModelContext schemaContext, final String point,
196 final OrderedLeafSetNode<?> readLeafList,
197 final boolean before, final RestconfStrategy strategy) {
198 strategy.delete(datastore, path.getParent().getParent());
199 final InstanceIdentifierContext<?> instanceIdentifier =
200 ParserIdentifier.toInstanceIdentifier(point, schemaContext, Optional.empty());
201 int lastItemPosition = 0;
202 for (final LeafSetEntryNode<?> nodeChild : readLeafList.getValue()) {
203 if (nodeChild.getIdentifier().equals(instanceIdentifier.getInstanceIdentifier().getLastPathArgument())) {
211 int lastInsertedPosition = 0;
212 final NormalizedNode<?, ?> emptySubtree =
213 ImmutableNodes.fromInstanceId(schemaContext, path.getParent().getParent());
214 strategy.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
215 for (final LeafSetEntryNode<?> nodeChild : readLeafList.getValue()) {
216 if (lastInsertedPosition == lastItemPosition) {
217 checkItemDoesNotExists(strategy, datastore, path, RestconfDataServiceConstant.PostData.POST_TX_TYPE);
218 strategy.create(datastore, path, payload);
220 final YangInstanceIdentifier childPath = path.getParent().getParent().node(nodeChild.getIdentifier());
221 checkItemDoesNotExists(strategy, datastore, childPath, RestconfDataServiceConstant.PostData.POST_TX_TYPE);
222 strategy.create(datastore, childPath, nodeChild);
223 lastInsertedPosition++;
227 private static void insertWithPointListPost(final LogicalDatastoreType datastore, final YangInstanceIdentifier path,
228 final NormalizedNode<?, ?> payload,
229 final EffectiveModelContext schemaContext, final String point,
230 final MapNode readList, final boolean before,
231 final RestconfStrategy strategy) {
232 strategy.delete(datastore, path.getParent().getParent());
233 final InstanceIdentifierContext<?> instanceIdentifier =
234 ParserIdentifier.toInstanceIdentifier(point, schemaContext, Optional.empty());
235 int lastItemPosition = 0;
236 for (final MapEntryNode mapEntryNode : readList.getValue()) {
237 if (mapEntryNode.getIdentifier().equals(instanceIdentifier.getInstanceIdentifier().getLastPathArgument())) {
245 int lastInsertedPosition = 0;
246 final NormalizedNode<?, ?> emptySubtree =
247 ImmutableNodes.fromInstanceId(schemaContext, path.getParent().getParent());
248 strategy.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
249 for (final MapEntryNode mapEntryNode : readList.getValue()) {
250 if (lastInsertedPosition == lastItemPosition) {
251 checkItemDoesNotExists(strategy, datastore, path, RestconfDataServiceConstant.PostData.POST_TX_TYPE);
252 strategy.create(datastore, path, payload);
254 final YangInstanceIdentifier childPath = path.getParent().getParent().node(mapEntryNode.getIdentifier());
255 checkItemDoesNotExists(strategy, datastore, childPath, RestconfDataServiceConstant.PostData.POST_TX_TYPE);
256 strategy.create(datastore, childPath, mapEntryNode);
257 lastInsertedPosition++;
261 private static void makePost(final YangInstanceIdentifier path, final NormalizedNode<?, ?> data,
262 final SchemaContext schemaContext, final RestconfStrategy strategy) {
263 if (data instanceof MapNode) {
264 boolean merge = false;
265 for (final MapEntryNode child : ((MapNode) data).getValue()) {
266 final YangInstanceIdentifier childPath = path.node(child.getIdentifier());
267 checkItemDoesNotExists(strategy, LogicalDatastoreType.CONFIGURATION, childPath,
268 RestconfDataServiceConstant.PostData.POST_TX_TYPE);
271 TransactionUtil.ensureParentsByMerge(path, schemaContext, strategy);
272 final NormalizedNode<?, ?> emptySubTree = ImmutableNodes.fromInstanceId(schemaContext, path);
273 strategy.merge(LogicalDatastoreType.CONFIGURATION,
274 YangInstanceIdentifier.create(emptySubTree.getIdentifier()), emptySubTree);
276 strategy.create(LogicalDatastoreType.CONFIGURATION, childPath, child);
279 checkItemDoesNotExists(strategy, LogicalDatastoreType.CONFIGURATION, path,
280 RestconfDataServiceConstant.PostData.POST_TX_TYPE);
282 TransactionUtil.ensureParentsByMerge(path, schemaContext, strategy);
283 strategy.create(LogicalDatastoreType.CONFIGURATION, path, data);
288 * Get location from {@link YangInstanceIdentifier} and {@link UriInfo}.
290 * @param uriInfo uri info
291 * @param initialPath data path
292 * @param schemaContext reference to {@link SchemaContext}
293 * @return {@link URI}
295 private static URI resolveLocation(final UriInfo uriInfo, final YangInstanceIdentifier initialPath,
296 final EffectiveModelContext schemaContext, final NormalizedNode<?, ?> data) {
297 if (uriInfo == null) {
301 YangInstanceIdentifier path = initialPath;
302 if (data instanceof MapNode) {
303 final Collection<MapEntryNode> children = ((MapNode) data).getValue();
304 if (!children.isEmpty()) {
305 path = path.node(children.iterator().next().getIdentifier());
309 return uriInfo.getBaseUriBuilder()
311 .path(ParserIdentifier.stringFromYangInstanceIdentifier(path, schemaContext))
315 private static void simplePost(final LogicalDatastoreType datastore, final YangInstanceIdentifier path,
316 final NormalizedNode<?, ?> payload,
317 final SchemaContext schemaContext, final RestconfStrategy strategy) {
318 checkItemDoesNotExists(strategy, datastore, path, RestconfDataServiceConstant.PostData.POST_TX_TYPE);
319 TransactionUtil.ensureParentsByMerge(path, schemaContext, strategy);
320 strategy.create(datastore, path, payload);
325 * Check if items do NOT already exists at specified {@code path}. Throws {@link RestconfDocumentedException} if
326 * data already exists.
328 * @param strategy Object that perform the actual DS operations
329 * @param store Datastore
330 * @param path Path to be checked
331 * @param operationType Type of operation (READ, POST, PUT, DELETE...)
333 private static void checkItemDoesNotExists(final RestconfStrategy strategy,
334 final LogicalDatastoreType store, final YangInstanceIdentifier path,
335 final String operationType) {
336 final FluentFuture<Boolean> future = strategy.exists(store, path);
337 final FutureDataFactory<Boolean> response = new FutureDataFactory<>();
339 FutureCallbackTx.addCallback(future, operationType, response);
341 if (response.result) {
345 LOG.trace("Operation via Restconf was not executed because data at {} already exists", path);
346 throw new RestconfDocumentedException(
347 "Data already exists", ErrorType.PROTOCOL, ErrorTag.DATA_EXISTS, path);