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.restful.utils;
10 import com.google.common.base.Optional;
11 import com.google.common.collect.Maps;
12 import com.google.common.util.concurrent.CheckedFuture;
13 import java.util.List;
15 import javax.ws.rs.core.Response;
16 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
17 import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
18 import org.opendaylight.controller.md.sal.dom.api.DOMDataReadWriteTransaction;
19 import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction;
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.common.errors.RestconfError.ErrorTag;
25 import org.opendaylight.restconf.common.errors.RestconfError.ErrorType;
26 import org.opendaylight.restconf.common.references.SchemaContextRef;
27 import org.opendaylight.restconf.common.validation.RestconfValidationUtils;
28 import org.opendaylight.restconf.restful.transaction.TransactionVarsWrapper;
29 import org.opendaylight.restconf.utils.parser.ParserIdentifier;
30 import org.opendaylight.yangtools.yang.common.QName;
31 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
32 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
33 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
34 import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode;
35 import org.opendaylight.yangtools.yang.data.api.schema.LeafSetNode;
36 import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
37 import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
38 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
39 import org.opendaylight.yangtools.yang.data.api.schema.OrderedLeafSetNode;
40 import org.opendaylight.yangtools.yang.data.api.schema.OrderedMapNode;
41 import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
42 import org.opendaylight.yangtools.yang.data.util.DataSchemaContextNode;
43 import org.opendaylight.yangtools.yang.data.util.DataSchemaContextTree;
44 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
45 import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
46 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
47 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
48 import org.opendaylight.yangtools.yang.model.api.SchemaNode;
51 * Util class for put data to DS.
53 * @deprecated move to splitted module restconf-nb-rfc8040
56 public final class PutDataTransactionUtil {
59 * Valid input data with {@link SchemaNode}.
66 public static void validInputData(final SchemaNode schemaNode, final NormalizedNodeContext payload) {
67 if ((schemaNode != null) && (payload.getData() == null)) {
68 throw new RestconfDocumentedException("Input is required.", ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE);
69 } else if ((schemaNode == null) && (payload.getData() != null)) {
70 throw new RestconfDocumentedException("No input expected.", ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE);
75 * Valid top level node name.
82 public static void validTopLevelNodeName(final YangInstanceIdentifier path, final NormalizedNodeContext payload) {
83 final String payloadName = payload.getData().getNodeType().getLocalName();
86 if (!payload.getData().getNodeType().equals(RestconfDataServiceConstant.NETCONF_BASE_QNAME)) {
87 throw new RestconfDocumentedException("Instance identifier has to contain at least one path argument",
88 ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE);
91 final String identifierName = path.getLastPathArgument().getNodeType().getLocalName();
92 if (!payloadName.equals(identifierName)) {
93 throw new RestconfDocumentedException(
94 "Payload name (" + payloadName + ") is different from identifier name (" + identifierName + ")",
95 ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE);
101 * Validates whether keys in {@code payload} are equal to values of keys in
102 * {@code iiWithData} for list schema node.
104 * @throws RestconfDocumentedException
105 * if key values or key count in payload and URI isn't equal
107 public static void validateListKeysEqualityInPayloadAndUri(final NormalizedNodeContext payload) {
108 final InstanceIdentifierContext<?> iiWithData = payload.getInstanceIdentifierContext();
109 final PathArgument lastPathArgument = iiWithData.getInstanceIdentifier().getLastPathArgument();
110 final SchemaNode schemaNode = iiWithData.getSchemaNode();
111 final NormalizedNode<?, ?> data = payload.getData();
112 if (schemaNode instanceof ListSchemaNode) {
113 final List<QName> keyDefinitions = ((ListSchemaNode) schemaNode).getKeyDefinition();
114 if ((lastPathArgument instanceof NodeIdentifierWithPredicates) && (data instanceof MapEntryNode)) {
115 final Map<QName, Object> uriKeyValues = ((NodeIdentifierWithPredicates) lastPathArgument)
117 isEqualUriAndPayloadKeyValues(uriKeyValues, (MapEntryNode) data, keyDefinitions);
122 private static void isEqualUriAndPayloadKeyValues(final Map<QName, Object> uriKeyValues, final MapEntryNode payload,
123 final List<QName> keyDefinitions) {
124 final Map<QName, Object> mutableCopyUriKeyValues = Maps.newHashMap(uriKeyValues);
125 for (final QName keyDefinition : keyDefinitions) {
126 final Object uriKeyValue = mutableCopyUriKeyValues.remove(keyDefinition);
127 RestconfValidationUtils.checkDocumentedError(uriKeyValue != null, ErrorType.PROTOCOL, ErrorTag.DATA_MISSING,
128 "Missing key " + keyDefinition + " in URI.");
130 final Object dataKeyValue = payload.getIdentifier().getKeyValues().get(keyDefinition);
132 if (!uriKeyValue.equals(dataKeyValue)) {
133 final String errMsg = "The value '" + uriKeyValue + "' for key '" + keyDefinition.getLocalName()
134 + "' specified in the URI doesn't match the value '" + dataKeyValue
135 + "' specified in the message body. ";
136 throw new RestconfDocumentedException(errMsg, ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
142 * Check mount point and prepare variables for put data to DS.
146 * @param schemaCtxRef
147 * reference to {@link SchemaContext}
148 * @param transactionNode
149 * wrapper of variables for transaction
154 * @return {@link CheckedFuture}
156 public static Response putData(final NormalizedNodeContext payload, final SchemaContextRef schemaCtxRef,
157 final TransactionVarsWrapper transactionNode, final String insert, final String point) {
158 final YangInstanceIdentifier path = payload.getInstanceIdentifierContext().getInstanceIdentifier();
159 final SchemaContext schemaContext = schemaCtxRef.get();
160 final ResponseFactory responseFactory = new ResponseFactory(
161 ReadDataTransactionUtil.readData(RestconfDataServiceConstant.ReadData.CONFIG, transactionNode,
163 final CheckedFuture<Void, TransactionCommitFailedException> submitData = submitData(path, schemaContext,
164 transactionNode.getTransactionChain(), payload.getData(), insert, point);
165 FutureCallbackTx.addCallback(submitData, RestconfDataServiceConstant.PutData.PUT_TX_TYPE, responseFactory);
166 return responseFactory.build();
174 * @param schemaContext
175 * {@link SchemaContext}
176 * @param domTransactionChain
184 * @return {@link CheckedFuture}
186 private static CheckedFuture<Void, TransactionCommitFailedException> submitData(final YangInstanceIdentifier path,
187 final SchemaContext schemaContext, final DOMTransactionChain domTransactionChain,
188 final NormalizedNode<?, ?> data, final String insert, final String point) {
189 final DOMDataReadWriteTransaction newReadWriteTransaction = domTransactionChain.newReadWriteTransaction();
190 if (insert == null) {
191 return makePut(path, schemaContext, newReadWriteTransaction, data);
193 final DataSchemaNode schemaNode = checkListAndOrderedType(schemaContext, path);
196 if (schemaNode instanceof ListSchemaNode) {
197 final NormalizedNode<?, ?> readData =
198 readList(path, schemaContext, domTransactionChain, schemaNode);
199 final OrderedMapNode readList = (OrderedMapNode) readData;
200 if ((readList == null) || readList.getValue().isEmpty()) {
201 return makePut(path, schemaContext, newReadWriteTransaction, data);
203 newReadWriteTransaction.delete(LogicalDatastoreType.CONFIGURATION, path.getParent());
204 simplePut(LogicalDatastoreType.CONFIGURATION, path, newReadWriteTransaction,
205 schemaContext, data);
206 listPut(LogicalDatastoreType.CONFIGURATION, path.getParent(), newReadWriteTransaction,
207 schemaContext, readList);
208 return newReadWriteTransaction.submit();
211 final NormalizedNode<?, ?> readData =
212 readList(path, schemaContext, domTransactionChain, schemaNode);
214 final OrderedLeafSetNode<?> readLeafList = (OrderedLeafSetNode<?>) readData;
215 if ((readLeafList == null) || readLeafList.getValue().isEmpty()) {
216 return makePut(path, schemaContext, newReadWriteTransaction, data);
218 newReadWriteTransaction.delete(LogicalDatastoreType.CONFIGURATION, path.getParent());
219 simplePut(LogicalDatastoreType.CONFIGURATION, path, newReadWriteTransaction,
220 schemaContext, data);
221 listPut(LogicalDatastoreType.CONFIGURATION, path.getParent(), newReadWriteTransaction,
222 schemaContext, readLeafList);
223 return newReadWriteTransaction.submit();
227 return makePut(path, schemaContext, newReadWriteTransaction, data);
229 if (schemaNode instanceof ListSchemaNode) {
230 final NormalizedNode<?, ?> readData =
231 readList(path, schemaContext, domTransactionChain, schemaNode);
232 final OrderedMapNode readList = (OrderedMapNode) readData;
233 if ((readList == null) || readList.getValue().isEmpty()) {
234 return makePut(path, schemaContext, newReadWriteTransaction, data);
236 insertWithPointListPut(newReadWriteTransaction, LogicalDatastoreType.CONFIGURATION, path,
237 data, schemaContext, point, readList, true);
238 return newReadWriteTransaction.submit();
241 final NormalizedNode<?, ?> readData =
242 readList(path, schemaContext, domTransactionChain, schemaNode);
244 final OrderedLeafSetNode<?> readLeafList = (OrderedLeafSetNode<?>) readData;
245 if ((readLeafList == null) || readLeafList.getValue().isEmpty()) {
246 return makePut(path, schemaContext, newReadWriteTransaction, data);
248 insertWithPointLeafListPut(newReadWriteTransaction, LogicalDatastoreType.CONFIGURATION,
249 path, data, schemaContext, point, readLeafList, true);
250 return newReadWriteTransaction.submit();
254 if (schemaNode instanceof ListSchemaNode) {
255 final NormalizedNode<?, ?> readData =
256 readList(path, schemaContext, domTransactionChain, schemaNode);
257 final OrderedMapNode readList = (OrderedMapNode) readData;
258 if ((readList == null) || readList.getValue().isEmpty()) {
259 return makePut(path, schemaContext, newReadWriteTransaction, data);
261 insertWithPointListPut(newReadWriteTransaction, LogicalDatastoreType.CONFIGURATION,
262 path, data, schemaContext, point, readList, false);
263 return newReadWriteTransaction.submit();
266 final NormalizedNode<?, ?> readData =
267 readList(path, schemaContext, domTransactionChain, schemaNode);
269 final OrderedLeafSetNode<?> readLeafList = (OrderedLeafSetNode<?>) readData;
270 if ((readLeafList == null) || readLeafList.getValue().isEmpty()) {
271 return makePut(path, schemaContext, newReadWriteTransaction, data);
273 insertWithPointLeafListPut(newReadWriteTransaction, LogicalDatastoreType.CONFIGURATION,
274 path, data, schemaContext, point, readLeafList, true);
275 return newReadWriteTransaction.submit();
279 throw new RestconfDocumentedException(
280 "Used bad value of insert parameter. Possible values are first, last, before or after, "
281 + "but was: " + insert);
286 public static NormalizedNode<?, ?> readList(final YangInstanceIdentifier path, final SchemaContext schemaContext,
287 final DOMTransactionChain domTransactionChain, final DataSchemaNode schemaNode) {
288 final InstanceIdentifierContext<?> iid = new InstanceIdentifierContext<SchemaNode>(
289 path.getParent(), schemaNode, null, schemaContext);
290 final TransactionVarsWrapper transactionNode =
291 new TransactionVarsWrapper(iid, null, domTransactionChain);
292 final NormalizedNode<?, ?> readData = ReadDataTransactionUtil
293 .readData(RestconfDataServiceConstant.ReadData.CONFIG, transactionNode, schemaContext);
297 private static void insertWithPointLeafListPut(final DOMDataReadWriteTransaction rwTransaction,
298 final LogicalDatastoreType datastore, final YangInstanceIdentifier path,
299 final NormalizedNode<?, ?> data, final SchemaContext schemaContext, final String point,
300 final OrderedLeafSetNode<?> readLeafList, final boolean before) {
301 rwTransaction.delete(datastore, path.getParent());
302 final InstanceIdentifierContext<?> instanceIdentifier =
303 ParserIdentifier.toInstanceIdentifier(point, schemaContext, Optional.absent());
304 int lastItemPosition = 0;
305 for (final LeafSetEntryNode<?> nodeChild : readLeafList.getValue()) {
306 if (nodeChild.getIdentifier().equals(instanceIdentifier.getInstanceIdentifier().getLastPathArgument())) {
314 int lastInsertedPosition = 0;
315 final NormalizedNode<?, ?> emptySubtree = ImmutableNodes.fromInstanceId(schemaContext, path.getParent());
316 rwTransaction.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
317 for (final LeafSetEntryNode<?> nodeChild : readLeafList.getValue()) {
318 if (lastInsertedPosition == lastItemPosition) {
319 simplePut(datastore, path, rwTransaction, schemaContext, data);
321 final YangInstanceIdentifier childPath = path.getParent().node(nodeChild.getIdentifier());
322 rwTransaction.put(datastore, childPath, nodeChild);
323 lastInsertedPosition++;
327 private static void insertWithPointListPut(final DOMDataReadWriteTransaction writeTx,
328 final LogicalDatastoreType datastore, final YangInstanceIdentifier path,
329 final NormalizedNode<?, ?> data, final SchemaContext schemaContext, final String point,
330 final OrderedMapNode readList, final boolean before) {
331 writeTx.delete(datastore, path.getParent());
332 final InstanceIdentifierContext<?> instanceIdentifier =
333 ParserIdentifier.toInstanceIdentifier(point, schemaContext, Optional.absent());
334 int lastItemPosition = 0;
335 for (final MapEntryNode mapEntryNode : readList.getValue()) {
336 if (mapEntryNode.getIdentifier().equals(instanceIdentifier.getInstanceIdentifier().getLastPathArgument())) {
344 int lastInsertedPosition = 0;
345 final NormalizedNode<?, ?> emptySubtree = ImmutableNodes.fromInstanceId(schemaContext, path.getParent());
346 writeTx.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
347 for (final MapEntryNode mapEntryNode : readList.getValue()) {
348 if (lastInsertedPosition == lastItemPosition) {
349 simplePut(datastore, path, writeTx, schemaContext, data);
351 final YangInstanceIdentifier childPath = path.getParent().node(mapEntryNode.getIdentifier());
352 writeTx.put(datastore, childPath, mapEntryNode);
353 lastInsertedPosition++;
357 private static void listPut(final LogicalDatastoreType datastore, final YangInstanceIdentifier path,
358 final DOMDataReadWriteTransaction writeTx, final SchemaContext schemaContext,
359 final OrderedLeafSetNode<?> payload) {
360 final NormalizedNode<?, ?> emptySubtree = ImmutableNodes.fromInstanceId(schemaContext, path);
361 writeTx.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
362 TransactionUtil.ensureParentsByMerge(path, schemaContext, writeTx);
363 for (final LeafSetEntryNode<?> child : ((LeafSetNode<?>) payload).getValue()) {
364 final YangInstanceIdentifier childPath = path.node(child.getIdentifier());
365 writeTx.put(datastore, childPath, child);
369 private static void listPut(final LogicalDatastoreType datastore, final YangInstanceIdentifier path,
370 final DOMDataReadWriteTransaction writeTx, final SchemaContext schemaContext,
371 final OrderedMapNode payload) {
372 final NormalizedNode<?, ?> emptySubtree = ImmutableNodes.fromInstanceId(schemaContext, path);
373 writeTx.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
374 TransactionUtil.ensureParentsByMerge(path, schemaContext, writeTx);
375 for (final MapEntryNode child : ((MapNode) payload).getValue()) {
376 final YangInstanceIdentifier childPath = path.node(child.getIdentifier());
377 writeTx.put(datastore, childPath, child);
381 private static void simplePut(final LogicalDatastoreType configuration, final YangInstanceIdentifier path,
382 final DOMDataReadWriteTransaction writeTx, final SchemaContext schemaContext,
383 final NormalizedNode<?, ?> data) {
384 TransactionUtil.ensureParentsByMerge(path, schemaContext, writeTx);
385 writeTx.put(LogicalDatastoreType.CONFIGURATION, path, data);
388 private static CheckedFuture<Void, TransactionCommitFailedException> makePut(final YangInstanceIdentifier path,
389 final SchemaContext schemaContext, final DOMDataWriteTransaction writeTx, final NormalizedNode<?, ?> data) {
390 TransactionUtil.ensureParentsByMerge(path, schemaContext, writeTx);
391 writeTx.put(LogicalDatastoreType.CONFIGURATION, path, data);
392 return writeTx.submit();
395 public static DataSchemaNode checkListAndOrderedType(final SchemaContext ctx, final YangInstanceIdentifier path) {
396 final YangInstanceIdentifier parent = path.getParent();
397 final DataSchemaContextNode<?> node = DataSchemaContextTree.from(ctx).getChild(parent);
398 final DataSchemaNode dataSchemaNode = node.getDataSchemaNode();
400 if (dataSchemaNode instanceof ListSchemaNode) {
401 if (!((ListSchemaNode) dataSchemaNode).isUserOrdered()) {
402 throw new RestconfDocumentedException("Insert parameter can be used only with ordered-by user list.");
404 return dataSchemaNode;
406 if (dataSchemaNode instanceof LeafListSchemaNode) {
407 if (!((LeafListSchemaNode) dataSchemaNode).isUserOrdered()) {
408 throw new RestconfDocumentedException(
409 "Insert parameter can be used only with ordered-by user leaf-list.");
411 return dataSchemaNode;
413 throw new RestconfDocumentedException("Insert parameter can be used only with list or leaf-list");