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.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.netconf.sal.restconf.impl.PATCHContext;
21 import org.opendaylight.netconf.sal.restconf.impl.PATCHEditOperation;
22 import org.opendaylight.netconf.sal.restconf.impl.PATCHEntity;
23 import org.opendaylight.netconf.sal.restconf.impl.PATCHStatusContext;
24 import org.opendaylight.netconf.sal.restconf.impl.PATCHStatusEntity;
25 import org.opendaylight.netconf.sal.restconf.impl.RestconfDocumentedException;
26 import org.opendaylight.netconf.sal.restconf.impl.RestconfError;
27 import org.opendaylight.netconf.sal.restconf.impl.RestconfError.ErrorTag;
28 import org.opendaylight.netconf.sal.restconf.impl.RestconfError.ErrorType;
29 import org.opendaylight.restconf.RestConnectorProvider;
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;
42 public final class PatchDataTransactionUtil {
43 private static final Logger LOG = LoggerFactory.getLogger(PatchDataTransactionUtil.class);
45 public PatchDataTransactionUtil() {
46 throw new UnsupportedOperationException("Util class.");
50 * Process edit operations of one {@link PATCHContext}.
51 * @param context Patch context to be processed
52 * @param transactionNode Wrapper for transaction
53 * @param schemaContextRef Soft reference for global schema context
54 * @return {@link PATCHStatusContext}
56 public static PATCHStatusContext patchData(final PATCHContext context, final TransactionVarsWrapper transactionNode,
57 final SchemaContextRef schemaContextRef) {
58 final List<PATCHStatusEntity> editCollection = new ArrayList<>();
60 final DOMDataReadWriteTransaction tx = transactionNode.getTransactionChain().newReadWriteTransaction();
62 for (final PATCHEntity patchEntity : context.getData()) {
63 final PATCHEditOperation operation = PATCHEditOperation.valueOf(patchEntity.getOperation().toUpperCase());
67 if (errorCounter == 0) {
69 createDataWithinTransaction(LogicalDatastoreType.CONFIGURATION,
70 patchEntity.getTargetNode(), patchEntity.getNode(), tx, schemaContextRef);
71 editCollection.add(new PATCHStatusEntity(patchEntity.getEditId(), true, null));
72 } catch (final RestconfDocumentedException e) {
73 editCollection.add(new PATCHStatusEntity(patchEntity.getEditId(),
74 false, Lists.newArrayList(e.getErrors())));
80 if (errorCounter == 0) {
82 deleteDataWithinTransaction(LogicalDatastoreType.CONFIGURATION, patchEntity.getTargetNode(),
84 editCollection.add(new PATCHStatusEntity(patchEntity.getEditId(), true, null));
85 } catch (final RestconfDocumentedException e) {
86 editCollection.add(new PATCHStatusEntity(patchEntity.getEditId(),
87 false, Lists.newArrayList(e.getErrors())));
93 if (errorCounter == 0) {
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 if (errorCounter == 0) {
108 replaceDataWithinTransaction(LogicalDatastoreType.CONFIGURATION,
109 patchEntity.getTargetNode(), patchEntity.getNode(), schemaContextRef, tx);
110 editCollection.add(new PATCHStatusEntity(patchEntity.getEditId(), true, null));
111 } catch (final RestconfDocumentedException e) {
112 editCollection.add(new PATCHStatusEntity(patchEntity.getEditId(),
113 false, Lists.newArrayList(e.getErrors())));
119 if (errorCounter == 0) {
121 removeDataWithinTransaction(LogicalDatastoreType.CONFIGURATION, patchEntity.getTargetNode(),
123 editCollection.add(new PATCHStatusEntity(patchEntity.getEditId(), true, null));
124 } catch (final RestconfDocumentedException e) {
125 editCollection.add(new PATCHStatusEntity(patchEntity.getEditId(),
126 false, Lists.newArrayList(e.getErrors())));
132 editCollection.add(new PATCHStatusEntity(patchEntity.getEditId(),
133 false, Lists.newArrayList(new RestconfError(ErrorType.PROTOCOL,
134 ErrorTag.OPERATION_NOT_SUPPORTED, "Not supported Yang PATCH operation"))));
140 // if no errors then submit transaction, otherwise cancel
141 if (errorCounter == 0) {
142 final ResponseFactory response = new ResponseFactory();
143 final CheckedFuture<Void, TransactionCommitFailedException> future = tx.submit();
146 FutureCallbackTx.addCallback(future, PatchData.PATCH_TX_TYPE, response);
147 } catch (final RestconfDocumentedException e) {
148 // if errors occurred during transaction commit then patch failed and global errors are reported
149 return new PATCHStatusContext(context.getPatchId(), ImmutableList.copyOf(editCollection), false,
150 Lists.newArrayList(e.getErrors()));
153 return new PATCHStatusContext(context.getPatchId(), ImmutableList.copyOf(editCollection), true, null);
156 RestConnectorProvider.resetTransactionChainForAdapaters(transactionNode.getTransactionChain());
157 return new PATCHStatusContext(context.getPatchId(), ImmutableList.copyOf(editCollection), false, null);
162 * Create data within one transaction, return error if already exists.
163 * @param dataStore Datastore to write data to
164 * @param path Path for data to be created
165 * @param payload Data to be created
166 * @param rWTransaction Transaction
167 * @param schemaContextRef Soft reference for global schema context
169 private static void createDataWithinTransaction(final LogicalDatastoreType dataStore,
170 final YangInstanceIdentifier path,
171 final NormalizedNode<?, ?> payload,
172 final DOMDataReadWriteTransaction rWTransaction,
173 final SchemaContextRef schemaContextRef) {
174 LOG.trace("POST {} within Restconf PATCH: {} with payload {}", dataStore.name(), path, payload);
175 createData(payload, schemaContextRef.get(), path, rWTransaction, dataStore, true);
179 * Check if data exists and remove it within one transaction.
180 * @param dataStore Datastore to delete data from
181 * @param path Path for data to be deleted
182 * @param readWriteTransaction Transaction
184 private static void deleteDataWithinTransaction(final LogicalDatastoreType dataStore,
185 final YangInstanceIdentifier path,
186 final DOMDataReadWriteTransaction readWriteTransaction) {
187 LOG.trace("Delete {} within Restconf PATCH: {}", dataStore.name(), path);
188 TransactionUtil.checkItemExists(readWriteTransaction, dataStore, path, PatchData.PATCH_TX_TYPE);
189 readWriteTransaction.delete(dataStore, path);
193 * Merge data within one transaction.
194 * @param dataStore Datastore to merge data to
195 * @param path Path for data to be merged
196 * @param payload Data to be merged
197 * @param writeTransaction Transaction
198 * @param schemaContextRef Soft reference for global schema context
200 private static void mergeDataWithinTransaction(final LogicalDatastoreType dataStore,
201 final YangInstanceIdentifier path,
202 final NormalizedNode<?, ?> payload,
203 final DOMDataReadWriteTransaction writeTransaction,
204 final SchemaContextRef schemaContextRef) {
205 LOG.trace("Merge {} within Restconf PATCH: {} with payload {}", dataStore.name(), path, payload);
206 TransactionUtil.ensureParentsByMerge(path, schemaContextRef.get(), writeTransaction);
208 // merging is necessary only for lists otherwise we can call put method
209 if (payload instanceof MapNode) {
210 writeTransaction.merge(dataStore, path, payload);
212 writeTransaction.put(dataStore, path, payload);
217 * Do NOT check if data exists and remove it within one transaction.
218 * @param dataStore Datastore to delete data from
219 * @param path Path for data to be deleted
220 * @param writeTransaction Transaction
222 private static void removeDataWithinTransaction(final LogicalDatastoreType dataStore,
223 final YangInstanceIdentifier path,
224 final DOMDataWriteTransaction writeTransaction) {
225 LOG.trace("Remove {} within Restconf PATCH: {}", dataStore.name(), path);
226 writeTransaction.delete(dataStore, path);
230 * Create data within one transaction, replace if already exists.
231 * @param dataStore Datastore to write data to
232 * @param path Path for data to be created
233 * @param payload Data to be created
234 * @param schemaContextRef Soft reference for global schema context
235 * @param rWTransaction Transaction
237 private static void replaceDataWithinTransaction(final LogicalDatastoreType dataStore,
238 final YangInstanceIdentifier path,
239 final NormalizedNode<?, ?> payload,
240 final SchemaContextRef schemaContextRef,
241 final DOMDataReadWriteTransaction rWTransaction) {
242 LOG.trace("PUT {} within Restconf PATCH: {} with payload {}", dataStore.name(), path, payload);
243 createData(payload, schemaContextRef.get(), path, rWTransaction, dataStore, false);
247 * Create data within one transaction. If {@code errorIfExists} is set to {@code true} then data will be checked
248 * for existence before created, otherwise they will be overwritten.
249 * @param payload Data to be created
250 * @param schemaContext Global schema context
251 * @param path Path for data to be created
252 * @param rWTransaction Transaction
253 * @param dataStore Datastore to write data to
254 * @param errorIfExists Enable checking for existence of data (throws error if already exists)
256 private static void createData(final NormalizedNode<?, ?> payload, final SchemaContext schemaContext,
257 final YangInstanceIdentifier path, final DOMDataReadWriteTransaction rWTransaction,
258 final LogicalDatastoreType dataStore, final boolean errorIfExists) {
259 if (payload instanceof MapNode) {
260 final NormalizedNode<?, ?> emptySubtree = ImmutableNodes.fromInstanceId(schemaContext, path);
261 rWTransaction.merge(dataStore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
262 TransactionUtil.ensureParentsByMerge(path, schemaContext, rWTransaction);
263 for (final MapEntryNode child : ((MapNode) payload).getValue()) {
264 final YangInstanceIdentifier childPath = path.node(child.getIdentifier());
267 TransactionUtil.checkItemDoesNotExists(
268 rWTransaction, dataStore, childPath, PatchData.PATCH_TX_TYPE);
271 rWTransaction.put(dataStore, childPath, child);
275 TransactionUtil.checkItemDoesNotExists(
276 rWTransaction, dataStore, path, PatchData.PATCH_TX_TYPE);
279 TransactionUtil.ensureParentsByMerge(path, schemaContext, rWTransaction);
280 rWTransaction.put(dataStore, path, payload);