Eliminate unnecessary blocking checks
[netconf.git] / restconf / restconf-nb-rfc8040 / src / main / java / org / opendaylight / restconf / nb / rfc8040 / rests / transactions / MdsalRestconfStrategy.java
1 /*
2  * Copyright (c) 2020 PANTHEON.tech, s.r.o. and others.  All rights reserved.
3  *
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
7  */
8 package org.opendaylight.restconf.nb.rfc8040.rests.transactions;
9
10 import static java.util.Objects.requireNonNull;
11 import static org.opendaylight.restconf.nb.rfc8040.rests.utils.DeleteDataTransactionUtil.DELETE_TX_TYPE;
12 import static org.opendaylight.restconf.nb.rfc8040.rests.utils.PostDataTransactionUtil.checkItemDoesNotExists;
13
14 import com.google.common.util.concurrent.FluentFuture;
15 import com.google.common.util.concurrent.ListenableFuture;
16 import java.util.Collection;
17 import java.util.Map;
18 import java.util.Optional;
19 import org.eclipse.jdt.annotation.NonNull;
20 import org.opendaylight.mdsal.common.api.CommitInfo;
21 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
22 import org.opendaylight.mdsal.common.api.ReadFailedException;
23 import org.opendaylight.mdsal.dom.api.DOMDataBroker;
24 import org.opendaylight.mdsal.dom.api.DOMDataTreeReadTransaction;
25 import org.opendaylight.mdsal.dom.api.DOMDataTreeReadWriteTransaction;
26 import org.opendaylight.mdsal.dom.api.DOMTransactionChain;
27 import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
28 import org.opendaylight.restconf.common.errors.RestconfError;
29 import org.opendaylight.restconf.nb.rfc8040.handlers.TransactionChainHandler;
30 import org.opendaylight.restconf.nb.rfc8040.rests.utils.DeleteDataTransactionUtil;
31 import org.opendaylight.restconf.nb.rfc8040.rests.utils.TransactionUtil;
32 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
33 import org.opendaylight.yangtools.yang.data.api.schema.LeafSetNode;
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.api.schema.NormalizedNodeContainer;
37 import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
38 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
39
40 /**
41  * Implementation of RESTCONF operations using {@link DOMTransactionChain} and related concepts.
42  *
43  * @see DOMTransactionChain
44  * @see DOMDataTreeReadWriteTransaction
45  */
46 public final class MdsalRestconfStrategy extends RestconfStrategy {
47     private final DOMTransactionChain transactionChain;
48     private final TransactionChainHandler transactionChainHandler;
49
50     private DOMDataTreeReadWriteTransaction rwTx;
51
52     public MdsalRestconfStrategy(final DOMDataBroker dataBroker) {
53         this(new TransactionChainHandler(dataBroker));
54     }
55
56     public MdsalRestconfStrategy(final TransactionChainHandler transactionChainHandler) {
57         this.transactionChainHandler = requireNonNull(transactionChainHandler);
58         transactionChain = transactionChainHandler.get();
59     }
60
61     @Override
62     public void prepareReadWriteExecution() {
63         rwTx = transactionChain.newReadWriteTransaction();
64     }
65
66     @Override
67     public void cancel() {
68         if (rwTx != null) {
69             rwTx.cancel();
70         }
71         transactionChain.close();
72     }
73
74     @Override
75     public ListenableFuture<Optional<NormalizedNode<?, ?>>> read(final LogicalDatastoreType store,
76                                                                  final YangInstanceIdentifier path) {
77         if (rwTx != null) {
78             return rwTx.read(store, path);
79         } else {
80             try (DOMDataTreeReadTransaction tx = transactionChain.newReadOnlyTransaction()) {
81                 return tx.read(store, path);
82             }
83         }
84     }
85
86     @Override
87     public FluentFuture<Boolean> exists(final LogicalDatastoreType store, final YangInstanceIdentifier path) {
88         return rwTx.exists(store, path);
89     }
90
91     @Override
92     public void delete(final LogicalDatastoreType store, final YangInstanceIdentifier path) {
93         DeleteDataTransactionUtil.checkItemExists(this, LogicalDatastoreType.CONFIGURATION, path,
94             DELETE_TX_TYPE);
95         rwTx.delete(store, path);
96     }
97
98     @Override
99     public void remove(final LogicalDatastoreType store, final YangInstanceIdentifier path) {
100         rwTx.delete(store, path);
101     }
102
103     @Override
104     public void merge(final LogicalDatastoreType store, final YangInstanceIdentifier path,
105             final NormalizedNode<?, ?> data) {
106         rwTx.merge(store, path, data);
107     }
108
109     @Override
110     public void create(final LogicalDatastoreType store, final YangInstanceIdentifier path,
111                        final NormalizedNode<?, ?> data, final SchemaContext schemaContext) {
112         if (data instanceof MapNode || data instanceof LeafSetNode) {
113             final NormalizedNode<?, ?> emptySubTree = ImmutableNodes.fromInstanceId(schemaContext, path);
114             merge(LogicalDatastoreType.CONFIGURATION, YangInstanceIdentifier.create(emptySubTree.getIdentifier()),
115                 emptySubTree);
116             TransactionUtil.ensureParentsByMerge(path, schemaContext, this);
117
118             final Collection<? extends NormalizedNode<?, ?>> children =
119                 ((NormalizedNodeContainer<?, ?, ?>) data).getValue();
120             final BatchedExistenceCheck check =
121                 BatchedExistenceCheck.start(this, LogicalDatastoreType.CONFIGURATION, path, children);
122
123             for (final NormalizedNode<?, ?> child : children) {
124                 final YangInstanceIdentifier childPath = path.node(child.getIdentifier());
125                 rwTx.put(store, childPath, child);
126             }
127
128             // ... finally collect existence checks and abort the transaction if any of them failed.
129             checkExistence(path, check);
130         } else {
131             checkItemDoesNotExists(this, LogicalDatastoreType.CONFIGURATION, path);
132             TransactionUtil.ensureParentsByMerge(path, schemaContext, this);
133             rwTx.put(store, path, data);
134         }
135     }
136
137     @Override
138     public void replace(final LogicalDatastoreType store, final YangInstanceIdentifier path,
139                         final NormalizedNode<?, ?> data, final SchemaContext schemaContext) {
140         if (data instanceof MapNode || data instanceof LeafSetNode) {
141             final NormalizedNode<?, ?> emptySubtree = ImmutableNodes.fromInstanceId(schemaContext, path);
142             merge(LogicalDatastoreType.CONFIGURATION, YangInstanceIdentifier.create(emptySubtree.getIdentifier()),
143                 emptySubtree);
144             TransactionUtil.ensureParentsByMerge(path, schemaContext, this);
145
146             for (final NormalizedNode<?, ?> child : ((NormalizedNodeContainer<?, ?, ?>) data).getValue()) {
147                 final YangInstanceIdentifier childPath = path.node(child.getIdentifier());
148                 rwTx.put(store, childPath, child);
149             }
150         } else {
151             TransactionUtil.ensureParentsByMerge(path, schemaContext, this);
152             rwTx.put(store, path, data);
153         }
154     }
155
156     @Override
157     public FluentFuture<? extends @NonNull CommitInfo> commit() {
158         return rwTx.commit();
159     }
160
161     @Override
162     public DOMTransactionChain getTransactionChain() {
163         return transactionChain;
164     }
165
166     @Override
167     public TransactionChainHandler getTransactionChainHandler() {
168         return transactionChainHandler;
169     }
170
171     private static void checkExistence(final YangInstanceIdentifier path, final BatchedExistenceCheck check) {
172         final Map.Entry<YangInstanceIdentifier, ReadFailedException> failure;
173         try {
174             failure = check.getFailure();
175         } catch (InterruptedException e) {
176             throw new RestconfDocumentedException("Could not determine the existence of path " + path, e);
177         }
178
179         if (failure != null) {
180             final ReadFailedException e = failure.getValue();
181             if (e == null) {
182                 throw new RestconfDocumentedException("Data already exists",
183                     RestconfError.ErrorType.PROTOCOL, RestconfError.ErrorTag.DATA_EXISTS, failure.getKey());
184             }
185
186             throw new RestconfDocumentedException(
187                 "Could not determine the existence of path " + failure.getKey(), e, e.getErrorList());
188         }
189     }
190 }