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
9 package org.opendaylight.restconf.restful.utils;
11 import com.google.common.collect.ImmutableList;
12 import com.google.common.collect.Lists;
13 import com.google.common.util.concurrent.CheckedFuture;
14 import java.util.ArrayList;
15 import java.util.List;
16 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
17 import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
18 import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
19 import org.opendaylight.controller.md.sal.dom.api.DOMDataReadWriteTransaction;
20 import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction;
21 import org.opendaylight.restconf.RestConnectorProvider;
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.common.patch.PatchContext;
27 import org.opendaylight.restconf.common.patch.PatchEntity;
28 import org.opendaylight.restconf.common.patch.PatchStatusContext;
29 import org.opendaylight.restconf.common.patch.PatchStatusEntity;
30 import org.opendaylight.restconf.common.references.SchemaContextRef;
31 import org.opendaylight.restconf.restful.transaction.TransactionVarsWrapper;
32 import org.opendaylight.restconf.restful.utils.RestconfDataServiceConstant.PatchData;
33 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
34 import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
35 import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
36 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
37 import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
38 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
39 import org.slf4j.Logger;
40 import org.slf4j.LoggerFactory;
43 * PatchDataTransaction util.
45 * @deprecated move to splitted module restconf-nb-rfc8040
48 public final class PatchDataTransactionUtil {
49 private static final Logger LOG = LoggerFactory.getLogger(PatchDataTransactionUtil.class);
51 private PatchDataTransactionUtil() {
52 throw new UnsupportedOperationException("Util class.");
56 * Process edit operations of one {@link PatchContext}.
57 * @param context Patch context to be processed
58 * @param transactionNode Wrapper for transaction
59 * @param schemaContextRef Soft reference for global schema context
60 * @return {@link PatchStatusContext}
62 public static PatchStatusContext patchData(final PatchContext context, final TransactionVarsWrapper transactionNode,
63 final SchemaContextRef schemaContextRef) {
64 final List<PatchStatusEntity> editCollection = new ArrayList<>();
65 boolean noError = true;
66 final DOMDataReadWriteTransaction tx = transactionNode.getTransactionChain().newReadWriteTransaction();
68 for (final PatchEntity patchEntity : context.getData()) {
70 switch (patchEntity.getOperation()) {
73 createDataWithinTransaction(LogicalDatastoreType.CONFIGURATION,
74 patchEntity.getTargetNode(), patchEntity.getNode(), tx, schemaContextRef);
75 editCollection.add(new PatchStatusEntity(patchEntity.getEditId(), true, null));
76 } catch (final RestconfDocumentedException e) {
77 editCollection.add(new PatchStatusEntity(patchEntity.getEditId(),
78 false, Lists.newArrayList(e.getErrors())));
84 deleteDataWithinTransaction(LogicalDatastoreType.CONFIGURATION, patchEntity.getTargetNode(),
86 editCollection.add(new PatchStatusEntity(patchEntity.getEditId(), true, null));
87 } catch (final RestconfDocumentedException e) {
88 editCollection.add(new PatchStatusEntity(patchEntity.getEditId(),
89 false, Lists.newArrayList(e.getErrors())));
95 mergeDataWithinTransaction(LogicalDatastoreType.CONFIGURATION,
96 patchEntity.getTargetNode(), patchEntity.getNode(), tx, schemaContextRef);
97 editCollection.add(new PatchStatusEntity(patchEntity.getEditId(), true, null));
98 } catch (final RestconfDocumentedException e) {
99 editCollection.add(new PatchStatusEntity(patchEntity.getEditId(),
100 false, Lists.newArrayList(e.getErrors())));
106 replaceDataWithinTransaction(LogicalDatastoreType.CONFIGURATION,
107 patchEntity.getTargetNode(), patchEntity.getNode(), schemaContextRef, tx);
108 editCollection.add(new PatchStatusEntity(patchEntity.getEditId(), true, null));
109 } catch (final RestconfDocumentedException e) {
110 editCollection.add(new PatchStatusEntity(patchEntity.getEditId(),
111 false, Lists.newArrayList(e.getErrors())));
117 removeDataWithinTransaction(LogicalDatastoreType.CONFIGURATION, patchEntity.getTargetNode(),
119 editCollection.add(new PatchStatusEntity(patchEntity.getEditId(), true, null));
120 } catch (final RestconfDocumentedException e) {
121 editCollection.add(new PatchStatusEntity(patchEntity.getEditId(),
122 false, Lists.newArrayList(e.getErrors())));
127 editCollection.add(new PatchStatusEntity(patchEntity.getEditId(),
128 false, Lists.newArrayList(new RestconfError(ErrorType.PROTOCOL,
129 ErrorTag.OPERATION_NOT_SUPPORTED, "Not supported Yang Patch operation"))));
138 // if no errors then submit transaction, otherwise cancel
140 final ResponseFactory response = new ResponseFactory();
141 final CheckedFuture<Void, TransactionCommitFailedException> future = tx.submit();
144 FutureCallbackTx.addCallback(future, PatchData.PATCH_TX_TYPE, response);
145 } catch (final RestconfDocumentedException e) {
146 // if errors occurred during transaction commit then patch failed and global errors are reported
147 return new PatchStatusContext(context.getPatchId(), ImmutableList.copyOf(editCollection), false,
148 Lists.newArrayList(e.getErrors()));
151 return new PatchStatusContext(context.getPatchId(), ImmutableList.copyOf(editCollection), true, null);
154 RestConnectorProvider.resetTransactionChainForAdapaters(transactionNode.getTransactionChain());
155 return new PatchStatusContext(context.getPatchId(), ImmutableList.copyOf(editCollection),
161 * Create data within one transaction, return error if already exists.
162 * @param dataStore Datastore to write data to
163 * @param path Path for data to be created
164 * @param payload Data to be created
165 * @param rWTransaction Transaction
166 * @param schemaContextRef Soft reference for global schema context
168 private static void createDataWithinTransaction(final LogicalDatastoreType dataStore,
169 final YangInstanceIdentifier path,
170 final NormalizedNode<?, ?> payload,
171 final DOMDataReadWriteTransaction rwTransaction,
172 final SchemaContextRef schemaContextRef) {
173 LOG.trace("POST {} within Restconf Patch: {} with payload {}", dataStore.name(), path, payload);
174 createData(payload, schemaContextRef.get(), path, rwTransaction, dataStore, true);
178 * Check if data exists and remove it within one transaction.
179 * @param dataStore Datastore to delete data from
180 * @param path Path for data to be deleted
181 * @param readWriteTransaction Transaction
183 private static void deleteDataWithinTransaction(final LogicalDatastoreType dataStore,
184 final YangInstanceIdentifier path,
185 final DOMDataReadWriteTransaction readWriteTransaction) {
186 LOG.trace("Delete {} within Restconf Patch: {}", dataStore.name(), path);
187 checkItemExistsWithinTransaction(readWriteTransaction, dataStore, path);
188 readWriteTransaction.delete(dataStore, path);
192 * Merge data within one transaction.
193 * @param dataStore Datastore to merge data to
194 * @param path Path for data to be merged
195 * @param payload Data to be merged
196 * @param writeTransaction Transaction
197 * @param schemaContextRef Soft reference for global schema context
199 private static void mergeDataWithinTransaction(final LogicalDatastoreType dataStore,
200 final YangInstanceIdentifier path,
201 final NormalizedNode<?, ?> payload,
202 final DOMDataReadWriteTransaction writeTransaction,
203 final SchemaContextRef schemaContextRef) {
204 LOG.trace("Merge {} within Restconf Patch: {} with payload {}", dataStore.name(), path, payload);
205 TransactionUtil.ensureParentsByMerge(path, schemaContextRef.get(), writeTransaction);
206 writeTransaction.merge(dataStore, path, payload);
210 * Do NOT check if data exists and remove it within one transaction.
211 * @param dataStore Datastore to delete data from
212 * @param path Path for data to be deleted
213 * @param writeTransaction Transaction
215 private static void removeDataWithinTransaction(final LogicalDatastoreType dataStore,
216 final YangInstanceIdentifier path,
217 final DOMDataWriteTransaction writeTransaction) {
218 LOG.trace("Remove {} within Restconf Patch: {}", dataStore.name(), path);
219 writeTransaction.delete(dataStore, path);
223 * Create data within one transaction, replace if already exists.
224 * @param dataStore Datastore to write data to
225 * @param path Path for data to be created
226 * @param payload Data to be created
227 * @param schemaContextRef Soft reference for global schema context
228 * @param rwTransaction Transaction
230 private static void replaceDataWithinTransaction(final LogicalDatastoreType dataStore,
231 final YangInstanceIdentifier path,
232 final NormalizedNode<?, ?> payload,
233 final SchemaContextRef schemaContextRef,
234 final DOMDataReadWriteTransaction rwTransaction) {
235 LOG.trace("PUT {} within Restconf Patch: {} with payload {}", dataStore.name(), path, payload);
236 createData(payload, schemaContextRef.get(), path, rwTransaction, dataStore, false);
240 * Create data within one transaction. If {@code errorIfExists} is set to {@code true} then data will be checked
241 * for existence before created, otherwise they will be overwritten.
242 * @param payload Data to be created
243 * @param schemaContext Global schema context
244 * @param path Path for data to be created
245 * @param rwTransaction Transaction
246 * @param dataStore Datastore to write data to
247 * @param errorIfExists Enable checking for existence of data (throws error if already exists)
249 private static void createData(final NormalizedNode<?, ?> payload, final SchemaContext schemaContext,
250 final YangInstanceIdentifier path, final DOMDataReadWriteTransaction rwTransaction,
251 final LogicalDatastoreType dataStore, final boolean errorIfExists) {
252 if (payload instanceof MapNode) {
253 final NormalizedNode<?, ?> emptySubtree = ImmutableNodes.fromInstanceId(schemaContext, path);
254 rwTransaction.merge(dataStore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
255 TransactionUtil.ensureParentsByMerge(path, schemaContext, rwTransaction);
256 for (final MapEntryNode child : ((MapNode) payload).getValue()) {
257 final YangInstanceIdentifier childPath = path.node(child.getIdentifier());
260 checkItemDoesNotExistsWithinTransaction(rwTransaction, dataStore, childPath);
263 rwTransaction.put(dataStore, childPath, child);
267 checkItemDoesNotExistsWithinTransaction(rwTransaction, dataStore, path);
270 TransactionUtil.ensureParentsByMerge(path, schemaContext, rwTransaction);
271 rwTransaction.put(dataStore, path, payload);
276 * Check if items already exists at specified {@code path}. Throws {@link RestconfDocumentedException} if
277 * data does NOT already exists.
278 * @param rwTransaction Transaction
279 * @param store Datastore
280 * @param path Path to be checked
282 public static void checkItemExistsWithinTransaction(final DOMDataReadWriteTransaction rwTransaction,
283 final LogicalDatastoreType store, final YangInstanceIdentifier path) {
284 final CheckedFuture<Boolean, ReadFailedException> future = rwTransaction.exists(store, path);
285 final FutureDataFactory<Boolean> response = new FutureDataFactory<>();
287 FutureCallbackTx.addCallback(future, PatchData.PATCH_TX_TYPE, response);
289 if (!response.result) {
290 final String errMsg = "Operation via Restconf was not executed because data does not exist";
291 LOG.trace("{}:{}", errMsg, path);
292 throw new RestconfDocumentedException(
293 "Data does not exist", ErrorType.PROTOCOL, ErrorTag.DATA_MISSING, path);
298 * Check if items do NOT already exists at specified {@code path}. Throws {@link RestconfDocumentedException} if
299 * data already exists.
300 * @param rwTransaction Transaction
301 * @param store Datastore
302 * @param path Path to be checked
304 public static void checkItemDoesNotExistsWithinTransaction(final DOMDataReadWriteTransaction rwTransaction,
305 final LogicalDatastoreType store, final YangInstanceIdentifier path) {
306 final CheckedFuture<Boolean, ReadFailedException> future = rwTransaction.exists(store, path);
307 final FutureDataFactory<Boolean> response = new FutureDataFactory<>();
309 FutureCallbackTx.addCallback(future, PatchData.PATCH_TX_TYPE, response);
311 if (response.result) {
312 final String errMsg = "Operation via Restconf was not executed because data already exists";
313 LOG.trace("{}:{}", errMsg, path);
314 throw new RestconfDocumentedException(
315 "Data already exists", ErrorType.PROTOCOL, ErrorTag.DATA_EXISTS, path);