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.collect.ImmutableList;
11 import com.google.common.collect.Lists;
12 import com.google.common.util.concurrent.FluentFuture;
13 import java.util.ArrayList;
14 import java.util.List;
15 import javax.ws.rs.core.Response.Status;
16 import org.opendaylight.mdsal.common.api.CommitInfo;
17 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
18 import org.opendaylight.mdsal.dom.api.DOMDataTreeReadTransaction;
19 import org.opendaylight.mdsal.dom.api.DOMDataTreeReadWriteTransaction;
20 import org.opendaylight.mdsal.dom.api.DOMDataTreeWriteTransaction;
21 import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
22 import org.opendaylight.restconf.common.errors.RestconfError;
23 import org.opendaylight.restconf.common.errors.RestconfError.ErrorTag;
24 import org.opendaylight.restconf.common.errors.RestconfError.ErrorType;
25 import org.opendaylight.restconf.common.patch.PatchContext;
26 import org.opendaylight.restconf.common.patch.PatchEntity;
27 import org.opendaylight.restconf.common.patch.PatchStatusContext;
28 import org.opendaylight.restconf.common.patch.PatchStatusEntity;
29 import org.opendaylight.restconf.nb.rfc8040.references.SchemaContextRef;
30 import org.opendaylight.restconf.nb.rfc8040.rests.transactions.TransactionVarsWrapper;
31 import org.opendaylight.restconf.nb.rfc8040.rests.utils.RestconfDataServiceConstant.PatchData;
32 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
33 import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
34 import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
35 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
36 import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
37 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
38 import org.slf4j.Logger;
39 import org.slf4j.LoggerFactory;
41 public final class PatchDataTransactionUtil {
42 private static final Logger LOG = LoggerFactory.getLogger(PatchDataTransactionUtil.class);
44 private PatchDataTransactionUtil() {
45 throw new UnsupportedOperationException("Util class.");
49 * Process edit operations of one {@link PatchContext}.
50 * @param context Patch context to be processed
51 * @param transactionNode Wrapper for transaction
52 * @param schemaContextRef Soft reference for global schema context
53 * @return {@link PatchStatusContext}
55 public static PatchStatusContext patchData(final PatchContext context, final TransactionVarsWrapper transactionNode,
56 final SchemaContextRef schemaContextRef) {
57 final List<PatchStatusEntity> editCollection = new ArrayList<>();
58 boolean noError = true;
59 final DOMDataTreeReadWriteTransaction tx = transactionNode.getTransactionChain().newReadWriteTransaction();
61 for (final PatchEntity patchEntity : context.getData()) {
63 switch (patchEntity.getOperation()) {
66 createDataWithinTransaction(LogicalDatastoreType.CONFIGURATION,
67 patchEntity.getTargetNode(), patchEntity.getNode(), tx, schemaContextRef);
68 editCollection.add(new PatchStatusEntity(patchEntity.getEditId(), true, null));
69 } catch (final RestconfDocumentedException e) {
70 editCollection.add(new PatchStatusEntity(patchEntity.getEditId(),
71 false, Lists.newArrayList(e.getErrors())));
77 deleteDataWithinTransaction(LogicalDatastoreType.CONFIGURATION, patchEntity.getTargetNode(),
79 editCollection.add(new PatchStatusEntity(patchEntity.getEditId(), true, null));
80 } catch (final RestconfDocumentedException e) {
81 editCollection.add(new PatchStatusEntity(patchEntity.getEditId(),
82 false, Lists.newArrayList(e.getErrors())));
88 mergeDataWithinTransaction(LogicalDatastoreType.CONFIGURATION,
89 patchEntity.getTargetNode(), patchEntity.getNode(), tx, schemaContextRef);
90 editCollection.add(new PatchStatusEntity(patchEntity.getEditId(), true, null));
91 } catch (final RestconfDocumentedException e) {
92 editCollection.add(new PatchStatusEntity(patchEntity.getEditId(),
93 false, Lists.newArrayList(e.getErrors())));
99 replaceDataWithinTransaction(LogicalDatastoreType.CONFIGURATION,
100 patchEntity.getTargetNode(), patchEntity.getNode(), schemaContextRef, tx);
101 editCollection.add(new PatchStatusEntity(patchEntity.getEditId(), true, null));
102 } catch (final RestconfDocumentedException e) {
103 editCollection.add(new PatchStatusEntity(patchEntity.getEditId(),
104 false, Lists.newArrayList(e.getErrors())));
110 removeDataWithinTransaction(LogicalDatastoreType.CONFIGURATION, patchEntity.getTargetNode(),
112 editCollection.add(new PatchStatusEntity(patchEntity.getEditId(), true, null));
113 } catch (final RestconfDocumentedException e) {
114 editCollection.add(new PatchStatusEntity(patchEntity.getEditId(),
115 false, Lists.newArrayList(e.getErrors())));
120 editCollection.add(new PatchStatusEntity(patchEntity.getEditId(),
121 false, Lists.newArrayList(new RestconfError(ErrorType.PROTOCOL,
122 ErrorTag.OPERATION_NOT_SUPPORTED, "Not supported Yang Patch operation"))));
131 // if no errors then submit transaction, otherwise cancel
133 final ResponseFactory response = new ResponseFactory(Status.OK);
134 final FluentFuture<? extends CommitInfo> future = tx.commit();
137 FutureCallbackTx.addCallback(future, PatchData.PATCH_TX_TYPE, response);
138 } catch (final RestconfDocumentedException e) {
139 // if errors occurred during transaction commit then patch failed and global errors are reported
140 return new PatchStatusContext(context.getPatchId(), ImmutableList.copyOf(editCollection), false,
141 Lists.newArrayList(e.getErrors()));
144 return new PatchStatusContext(context.getPatchId(), ImmutableList.copyOf(editCollection), true, null);
147 transactionNode.getTransactionChainHandler().reset();
148 return new PatchStatusContext(context.getPatchId(), ImmutableList.copyOf(editCollection),
154 * Create data within one transaction, return error if already exists.
155 * @param dataStore Datastore to write data to
156 * @param path Path for data to be created
157 * @param payload Data to be created
158 * @param rWTransaction Transaction
159 * @param schemaContextRef Soft reference for global schema context
161 private static void createDataWithinTransaction(final LogicalDatastoreType dataStore,
162 final YangInstanceIdentifier path,
163 final NormalizedNode<?, ?> payload,
164 final DOMDataTreeReadWriteTransaction rwTransaction,
165 final SchemaContextRef schemaContextRef) {
166 LOG.trace("POST {} within Restconf Patch: {} with payload {}", dataStore.name(), path, payload);
167 createData(payload, schemaContextRef.get(), path, rwTransaction, dataStore, true);
171 * Check if data exists and remove it within one transaction.
172 * @param dataStore Datastore to delete data from
173 * @param path Path for data to be deleted
174 * @param readWriteTransaction Transaction
176 private static void deleteDataWithinTransaction(final LogicalDatastoreType dataStore,
177 final YangInstanceIdentifier path,
178 final DOMDataTreeReadWriteTransaction readWriteTransaction) {
179 LOG.trace("Delete {} within Restconf Patch: {}", dataStore.name(), path);
180 checkItemExistsWithinTransaction(readWriteTransaction, dataStore, path);
181 readWriteTransaction.delete(dataStore, path);
185 * Merge data within one transaction.
186 * @param dataStore Datastore to merge data to
187 * @param path Path for data to be merged
188 * @param payload Data to be merged
189 * @param writeTransaction Transaction
190 * @param schemaContextRef Soft reference for global schema context
192 private static void mergeDataWithinTransaction(final LogicalDatastoreType dataStore,
193 final YangInstanceIdentifier path,
194 final NormalizedNode<?, ?> payload,
195 final DOMDataTreeWriteTransaction writeTransaction,
196 final SchemaContextRef schemaContextRef) {
197 LOG.trace("Merge {} within Restconf Patch: {} with payload {}", dataStore.name(), path, payload);
198 TransactionUtil.ensureParentsByMerge(path, schemaContextRef.get(), writeTransaction);
199 writeTransaction.merge(dataStore, path, payload);
203 * Do NOT check if data exists and remove it within one transaction.
204 * @param dataStore Datastore to delete data from
205 * @param path Path for data to be deleted
206 * @param writeTransaction Transaction
208 private static void removeDataWithinTransaction(final LogicalDatastoreType dataStore,
209 final YangInstanceIdentifier path,
210 final DOMDataTreeWriteTransaction writeTransaction) {
211 LOG.trace("Remove {} within Restconf Patch: {}", dataStore.name(), path);
212 writeTransaction.delete(dataStore, path);
216 * Create data within one transaction, replace if already exists.
217 * @param dataStore Datastore to write data to
218 * @param path Path for data to be created
219 * @param payload Data to be created
220 * @param schemaContextRef Soft reference for global schema context
221 * @param rwTransaction Transaction
223 private static void replaceDataWithinTransaction(final LogicalDatastoreType dataStore,
224 final YangInstanceIdentifier path,
225 final NormalizedNode<?, ?> payload,
226 final SchemaContextRef schemaContextRef,
227 final DOMDataTreeReadWriteTransaction rwTransaction) {
228 LOG.trace("PUT {} within Restconf Patch: {} with payload {}", dataStore.name(), path, payload);
229 createData(payload, schemaContextRef.get(), path, rwTransaction, dataStore, false);
233 * Create data within one transaction. If {@code errorIfExists} is set to {@code true} then data will be checked
234 * for existence before created, otherwise they will be overwritten.
235 * @param payload Data to be created
236 * @param schemaContext Global schema context
237 * @param path Path for data to be created
238 * @param rwTransaction Transaction
239 * @param dataStore Datastore to write data to
240 * @param errorIfExists Enable checking for existence of data (throws error if already exists)
242 private static void createData(final NormalizedNode<?, ?> payload, final SchemaContext schemaContext,
243 final YangInstanceIdentifier path,
244 final DOMDataTreeReadWriteTransaction rwTransaction,
245 final LogicalDatastoreType dataStore, final boolean errorIfExists) {
246 if (payload instanceof MapNode) {
247 final NormalizedNode<?, ?> emptySubtree = ImmutableNodes.fromInstanceId(schemaContext, path);
248 rwTransaction.merge(dataStore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
249 TransactionUtil.ensureParentsByMerge(path, schemaContext, rwTransaction);
250 for (final MapEntryNode child : ((MapNode) payload).getValue()) {
251 final YangInstanceIdentifier childPath = path.node(child.getIdentifier());
254 checkItemDoesNotExistsWithinTransaction(rwTransaction, dataStore, childPath);
257 rwTransaction.put(dataStore, childPath, child);
261 checkItemDoesNotExistsWithinTransaction(rwTransaction, dataStore, path);
264 TransactionUtil.ensureParentsByMerge(path, schemaContext, rwTransaction);
265 rwTransaction.put(dataStore, path, payload);
270 * Check if items already exists at specified {@code path}. Throws {@link RestconfDocumentedException} if
271 * data does NOT already exists.
272 * @param rwTransaction Transaction
273 * @param store Datastore
274 * @param path Path to be checked
276 public static void checkItemExistsWithinTransaction(final DOMDataTreeReadTransaction rwTransaction,
277 final LogicalDatastoreType store, final YangInstanceIdentifier path) {
278 final FluentFuture<Boolean> future = rwTransaction.exists(store, path);
279 final FutureDataFactory<Boolean> response = new FutureDataFactory<>();
281 FutureCallbackTx.addCallback(future, PatchData.PATCH_TX_TYPE, response);
283 if (!response.result) {
284 LOG.trace("Operation via Restconf was not executed because data at {} does not exist", path);
285 throw new RestconfDocumentedException("Data does not exist", ErrorType.PROTOCOL, ErrorTag.DATA_MISSING,
291 * Check if items do NOT already exists at specified {@code path}. Throws {@link RestconfDocumentedException} if
292 * data already exists.
293 * @param rwTransaction Transaction
294 * @param store Datastore
295 * @param path Path to be checked
297 public static void checkItemDoesNotExistsWithinTransaction(final DOMDataTreeReadTransaction rwTransaction,
298 final LogicalDatastoreType store, final YangInstanceIdentifier path) {
299 final FluentFuture<Boolean> future = rwTransaction.exists(store, path);
300 final FutureDataFactory<Boolean> response = new FutureDataFactory<>();
302 FutureCallbackTx.addCallback(future, PatchData.PATCH_TX_TYPE, response);
304 if (response.result) {
305 LOG.trace("Operation via Restconf was not executed because data at {} already exists", path);
306 throw new RestconfDocumentedException("Data already exists", ErrorType.PROTOCOL, ErrorTag.DATA_EXISTS,