Eliminate unnecessary blocking checks
[netconf.git] / restconf / restconf-nb-rfc8040 / src / main / java / org / opendaylight / restconf / nb / rfc8040 / rests / transactions / NetconfRestconfStrategy.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
12 import com.google.common.util.concurrent.FluentFuture;
13 import com.google.common.util.concurrent.FutureCallback;
14 import com.google.common.util.concurrent.Futures;
15 import com.google.common.util.concurrent.ListenableFuture;
16 import com.google.common.util.concurrent.MoreExecutors;
17 import com.google.common.util.concurrent.SettableFuture;
18 import java.util.List;
19 import java.util.Optional;
20 import org.eclipse.jdt.annotation.NonNull;
21 import org.opendaylight.mdsal.common.api.CommitInfo;
22 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
23 import org.opendaylight.mdsal.common.api.ReadFailedException;
24 import org.opendaylight.mdsal.dom.api.DOMRpcResult;
25 import org.opendaylight.mdsal.dom.api.DOMTransactionChain;
26 import org.opendaylight.netconf.dom.api.NetconfDataTreeService;
27 import org.opendaylight.restconf.nb.rfc8040.handlers.TransactionChainHandler;
28 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
29 import org.opendaylight.yangtools.yang.data.api.schema.LeafSetNode;
30 import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
31 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
32 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodeContainer;
33 import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
34 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
35 import org.slf4j.Logger;
36 import org.slf4j.LoggerFactory;
37
38 /**
39  * Implementation of RESTCONF operations on top of a raw NETCONF backend.
40  *
41  * @see NetconfDataTreeService
42  */
43 public final class NetconfRestconfStrategy extends RestconfStrategy {
44     private static final Logger LOG = LoggerFactory.getLogger(NetconfRestconfStrategy.class);
45
46     private final NetconfDataTreeService netconfService;
47
48     private List<ListenableFuture<? extends DOMRpcResult>> resultsFutures;
49
50     public NetconfRestconfStrategy(final NetconfDataTreeService netconfService) {
51         this.netconfService = requireNonNull(netconfService);
52     }
53
54     @Override
55     public void prepareReadWriteExecution() {
56         resultsFutures = netconfService.lock();
57     }
58
59     @Override
60     public void cancel() {
61         netconfService.discardChanges();
62         netconfService.unlock();
63     }
64
65     @Override
66     public ListenableFuture<Optional<NormalizedNode<?, ?>>> read(final LogicalDatastoreType store,
67                                                                  final YangInstanceIdentifier path) {
68         switch (store) {
69             case CONFIGURATION:
70                 return netconfService.getConfig(path);
71             case OPERATIONAL:
72                 return netconfService.get(path);
73             default:
74                 LOG.info("Unknown datastore type: {}.", store);
75                 throw new IllegalArgumentException(String.format(
76                         "%s, Cannot read data %s for %s datastore, unknown datastore type",
77                         netconfService.getDeviceId(), path, store));
78         }
79     }
80
81     @Override
82     public FluentFuture<Boolean> exists(final LogicalDatastoreType store, final YangInstanceIdentifier path) {
83         return remapException(read(store, path))
84                 .transform(optionalNode -> optionalNode != null && optionalNode.isPresent(),
85                         MoreExecutors.directExecutor());
86     }
87
88     @Override
89     public void delete(final LogicalDatastoreType store, final YangInstanceIdentifier path) {
90         resultsFutures.add(netconfService.delete(store, path));
91     }
92
93     @Override
94     public void remove(final LogicalDatastoreType store, final YangInstanceIdentifier path) {
95         resultsFutures.add(netconfService.remove(store, path));
96     }
97
98     @Override
99     public void merge(final LogicalDatastoreType store, final YangInstanceIdentifier path,
100                       final NormalizedNode<?, ?> data) {
101         resultsFutures.add(netconfService.merge(store, path, data, Optional.empty()));
102     }
103
104     @Override
105     public void create(final LogicalDatastoreType store, final YangInstanceIdentifier path,
106                        final NormalizedNode<?, ?> data, final SchemaContext schemaContext) {
107         if (data instanceof MapNode || data instanceof LeafSetNode) {
108             final NormalizedNode<?, ?> emptySubTree = ImmutableNodes.fromInstanceId(schemaContext, path);
109             merge(LogicalDatastoreType.CONFIGURATION, YangInstanceIdentifier.create(emptySubTree.getIdentifier()),
110                 emptySubTree);
111
112             for (final NormalizedNode<?, ?> child : ((NormalizedNodeContainer<?, ?, ?>) data).getValue()) {
113                 final YangInstanceIdentifier childPath = path.node(child.getIdentifier());
114                 resultsFutures.add(netconfService.create(store, childPath, child, Optional.empty()));
115             }
116         } else {
117             resultsFutures.add(netconfService.create(store, path, data, Optional.empty()));
118         }
119     }
120
121     @Override
122     public void replace(final LogicalDatastoreType store, final YangInstanceIdentifier path,
123                         final NormalizedNode<?, ?> data, final SchemaContext schemaContext) {
124         if (data instanceof MapNode || data instanceof LeafSetNode) {
125             final NormalizedNode<?, ?> emptySubTree = ImmutableNodes.fromInstanceId(schemaContext, path);
126             merge(LogicalDatastoreType.CONFIGURATION, YangInstanceIdentifier.create(emptySubTree.getIdentifier()),
127                 emptySubTree);
128
129             for (final NormalizedNode<?, ?> child : ((NormalizedNodeContainer<?, ?, ?>) data).getValue()) {
130                 final YangInstanceIdentifier childPath = path.node(child.getIdentifier());
131                 resultsFutures.add(netconfService.replace(store, childPath, child, Optional.empty()));
132             }
133         } else {
134             resultsFutures.add(netconfService.replace(store, path, data, Optional.empty()));
135         }
136     }
137
138     @Override
139     public FluentFuture<? extends @NonNull CommitInfo> commit() {
140         return FluentFuture.from(netconfService.commit(resultsFutures));
141     }
142
143     /**
144      * As we are not using any transactions here, always return null.
145      */
146     @Override
147     public DOMTransactionChain getTransactionChain() {
148         return null;
149     }
150
151     /**
152      * As we are not using any transactions here, always return null.
153      */
154     @Override
155     public TransactionChainHandler getTransactionChainHandler() {
156         return null;
157     }
158
159     private static <T> FluentFuture<T> remapException(final ListenableFuture<T> input) {
160         final SettableFuture<T> ret = SettableFuture.create();
161         Futures.addCallback(input, new FutureCallback<T>() {
162
163             @Override
164             public void onSuccess(final T result) {
165                 ret.set(result);
166             }
167
168             @Override
169             public void onFailure(final Throwable cause) {
170                 ret.setException(cause instanceof ReadFailedException ? cause
171                     : new ReadFailedException("NETCONF operation failed", cause));
172             }
173         }, MoreExecutors.directExecutor());
174         return FluentFuture.from(ret);
175     }
176 }