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;
11 import java.util.Optional;
12 import javax.ws.rs.core.Response;
13 import javax.ws.rs.core.Response.Status;
14 import org.opendaylight.mdsal.common.api.CommitInfo;
15 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
16 import org.opendaylight.mdsal.dom.api.DOMTransactionChain;
17 import org.opendaylight.restconf.common.context.InstanceIdentifierContext;
18 import org.opendaylight.restconf.common.context.NormalizedNodeContext;
19 import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
20 import org.opendaylight.restconf.common.errors.RestconfError;
21 import org.opendaylight.restconf.common.errors.RestconfError.ErrorTag;
22 import org.opendaylight.restconf.nb.rfc8040.rests.transactions.RestconfStrategy;
23 import org.opendaylight.restconf.nb.rfc8040.rests.transactions.RestconfTransaction;
24 import org.opendaylight.restconf.nb.rfc8040.rests.utils.RestconfDataServiceConstant.PostPutQueryParameters.Insert;
25 import org.opendaylight.restconf.nb.rfc8040.utils.parser.ParserIdentifier;
26 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
27 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
28 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodeContainer;
29 import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
30 import org.opendaylight.yangtools.yang.data.util.DataSchemaContextNode;
31 import org.opendaylight.yangtools.yang.data.util.DataSchemaContextTree;
32 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
33 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
34 import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
35 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
36 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
39 * Util class for put data to DS.
42 public final class PutDataTransactionUtil {
43 private static final String PUT_TX_TYPE = "PUT";
45 private PutDataTransactionUtil() {
49 * Check mount point and prepare variables for put data to DS. Close {@link DOMTransactionChain} if any
50 * inside of object {@link RestconfStrategy} provided as a parameter if any.
52 * @param payload data to put
53 * @param schemaContext reference to {@link EffectiveModelContext}
54 * @param strategy object that perform the actual DS operations
55 * @param point query parameter
56 * @param insert query parameter
57 * @return {@link Response}
59 public static Response putData(final NormalizedNodeContext payload, final EffectiveModelContext schemaContext,
60 final RestconfStrategy strategy, final Insert insert, final String point) {
61 final YangInstanceIdentifier path = payload.getInstanceIdentifierContext().getInstanceIdentifier();
63 final FluentFuture<Boolean> existsFuture = strategy.exists(LogicalDatastoreType.CONFIGURATION, path);
64 final FutureDataFactory<Boolean> existsResponse = new FutureDataFactory<>();
65 FutureCallbackTx.addCallback(existsFuture, PUT_TX_TYPE, existsResponse);
67 final ResponseFactory responseFactory =
68 new ResponseFactory(existsResponse.result ? Status.NO_CONTENT : Status.CREATED);
69 final FluentFuture<? extends CommitInfo> submitData = submitData(path, schemaContext, strategy,
70 payload.getData(), insert, point);
71 //This method will close transactionChain if any
72 FutureCallbackTx.addCallback(submitData, PUT_TX_TYPE, responseFactory, strategy, path);
73 return responseFactory.build();
79 * @param path path of data
80 * @param schemaContext {@link SchemaContext}
81 * @param strategy object that perform the actual DS operations
83 * @param point query parameter
84 * @param insert query parameter
85 * @return {@link FluentFuture}
87 private static FluentFuture<? extends CommitInfo> submitData(final YangInstanceIdentifier path,
88 final EffectiveModelContext schemaContext,
89 final RestconfStrategy strategy,
90 final NormalizedNode data,
91 final Insert insert, final String point) {
92 final RestconfTransaction transaction = strategy.prepareWriteExecution();
94 return makePut(path, schemaContext, transaction, data);
97 checkListAndOrderedType(schemaContext, path);
98 final NormalizedNode readData;
101 readData = readList(strategy, path.getParent());
102 if (readData == null || ((NormalizedNodeContainer<?>) readData).isEmpty()) {
103 return makePut(path, schemaContext, transaction, data);
105 transaction.remove(path.getParent());
106 transaction.replace(path, data, schemaContext);
107 transaction.replace(path.getParent(), readData, schemaContext);
108 return transaction.commit();
110 return makePut(path, schemaContext, transaction, data);
112 readData = readList(strategy, path.getParent());
113 if (readData == null || ((NormalizedNodeContainer<?>) readData).isEmpty()) {
114 return makePut(path, schemaContext, transaction, data);
116 insertWithPointPut(transaction, path, data, schemaContext, point, (NormalizedNodeContainer<?>) readData,
118 return transaction.commit();
120 readData = readList(strategy, path.getParent());
121 if (readData == null || ((NormalizedNodeContainer<?>) readData).isEmpty()) {
122 return makePut(path, schemaContext, transaction, data);
124 insertWithPointPut(transaction, path, data, schemaContext, point, (NormalizedNodeContainer<?>) readData,
126 return transaction.commit();
128 throw new RestconfDocumentedException(
129 "Used bad value of insert parameter. Possible values are first, last, before or after, "
130 + "but was: " + insert, RestconfError.ErrorType.PROTOCOL, ErrorTag.BAD_ATTRIBUTE);
134 // FIXME: this method is only called from a context where we are modifying data. This should be part of strategy,
135 // requiring an already-open transaction. It also must return a future, so it can be properly composed.
136 static NormalizedNode readList(final RestconfStrategy strategy, final YangInstanceIdentifier path) {
137 return ReadDataTransactionUtil.readDataViaTransaction(strategy, LogicalDatastoreType.CONFIGURATION, path,
141 private static void insertWithPointPut(final RestconfTransaction transaction,
142 final YangInstanceIdentifier path,
143 final NormalizedNode data,
144 final EffectiveModelContext schemaContext, final String point,
145 final NormalizedNodeContainer<?> readList, final boolean before) {
146 transaction.remove(path.getParent());
147 final InstanceIdentifierContext<?> instanceIdentifier =
148 ParserIdentifier.toInstanceIdentifier(point, schemaContext, Optional.empty());
149 int lastItemPosition = 0;
150 for (final NormalizedNode nodeChild : readList.body()) {
151 if (nodeChild.getIdentifier().equals(instanceIdentifier.getInstanceIdentifier().getLastPathArgument())) {
159 int lastInsertedPosition = 0;
160 final NormalizedNode emptySubtree = ImmutableNodes.fromInstanceId(schemaContext, path.getParent());
161 transaction.merge(YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
162 for (final NormalizedNode nodeChild : readList.body()) {
163 if (lastInsertedPosition == lastItemPosition) {
164 transaction.replace(path, data, schemaContext);
166 final YangInstanceIdentifier childPath = path.getParent().node(nodeChild.getIdentifier());
167 transaction.replace(childPath, nodeChild, schemaContext);
168 lastInsertedPosition++;
172 private static FluentFuture<? extends CommitInfo> makePut(final YangInstanceIdentifier path,
173 final SchemaContext schemaContext,
174 final RestconfTransaction transaction,
175 final NormalizedNode data) {
176 transaction.replace(path, data, schemaContext);
177 return transaction.commit();
180 public static DataSchemaNode checkListAndOrderedType(final EffectiveModelContext ctx,
181 final YangInstanceIdentifier path) {
182 final YangInstanceIdentifier parent = path.getParent();
183 final DataSchemaContextNode<?> node = DataSchemaContextTree.from(ctx).findChild(parent).orElseThrow();
184 final DataSchemaNode dataSchemaNode = node.getDataSchemaNode();
186 if (dataSchemaNode instanceof ListSchemaNode) {
187 if (!((ListSchemaNode) dataSchemaNode).isUserOrdered()) {
188 throw new RestconfDocumentedException("Insert parameter can be used only with ordered-by user list.",
189 RestconfError.ErrorType.PROTOCOL, ErrorTag.BAD_ELEMENT);
191 return dataSchemaNode;
193 if (dataSchemaNode instanceof LeafListSchemaNode) {
194 if (!((LeafListSchemaNode) dataSchemaNode).isUserOrdered()) {
195 throw new RestconfDocumentedException(
196 "Insert parameter can be used only with ordered-by user leaf-list.",
197 RestconfError.ErrorType.PROTOCOL, ErrorTag.BAD_ELEMENT);
199 return dataSchemaNode;
201 throw new RestconfDocumentedException("Insert parameter can be used only with list or leaf-list",
202 RestconfError.ErrorType.PROTOCOL, ErrorTag.BAD_ELEMENT);