2 * Copyright (c) 2020 PANTHEON.tech, s.r.o. 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.transactions;
10 import static java.util.Objects.requireNonNull;
12 import com.google.common.util.concurrent.FutureCallback;
13 import com.google.common.util.concurrent.Futures;
14 import com.google.common.util.concurrent.ListenableFuture;
15 import com.google.common.util.concurrent.MoreExecutors;
16 import java.util.List;
17 import java.util.Optional;
18 import org.eclipse.jdt.annotation.NonNull;
19 import org.opendaylight.mdsal.common.api.CommitInfo;
20 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
21 import org.opendaylight.mdsal.dom.api.DOMDataBroker;
22 import org.opendaylight.mdsal.dom.api.DOMMountPoint;
23 import org.opendaylight.netconf.dom.api.NetconfDataTreeService;
24 import org.opendaylight.restconf.common.errors.RestconfFuture;
25 import org.opendaylight.restconf.common.errors.SettableRestconfFuture;
26 import org.opendaylight.restconf.nb.rfc8040.rests.utils.TransactionUtil;
27 import org.opendaylight.yangtools.yang.common.Empty;
28 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
29 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
30 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
33 * Baseline execution strategy for various RESTCONF operations.
35 * @see NetconfRestconfStrategy
36 * @see MdsalRestconfStrategy
38 // FIXME: it seems the first three operations deal with lifecycle of a transaction, while others invoke various
39 // operations. This should be handled through proper allocation indirection.
40 public abstract class RestconfStrategy {
42 * Result of a {@code PUT} request as defined in
43 * <a href="https://www.rfc-editor.org/rfc/rfc8040#section-4.5">RFC8040 section 4.5</a>. The definition makes it
44 * clear that the logical operation is {@code create-or-replace}.
46 public enum CreateOrReplaceResult {
48 * A new resource has been created.
52 * An existing resources has been replaced.
62 * Look up the appropriate strategy for a particular mount point.
64 * @param mountPoint Target mount point
65 * @return A strategy, or null if the mount point does not expose a supported interface
66 * @throws NullPointerException if {@code mountPoint} is null
68 public static Optional<RestconfStrategy> forMountPoint(final DOMMountPoint mountPoint) {
69 final Optional<RestconfStrategy> netconf = mountPoint.getService(NetconfDataTreeService.class)
70 .map(NetconfRestconfStrategy::new);
71 if (netconf.isPresent()) {
75 return mountPoint.getService(DOMDataBroker.class).map(MdsalRestconfStrategy::new);
79 * Lock the entire datastore.
81 * @return A {@link RestconfTransaction}. This transaction needs to be either committed or canceled before doing
84 public abstract RestconfTransaction prepareWriteExecution();
87 * Read data from the datastore.
89 * @param store the logical data store which should be modified
90 * @param path the data object path
91 * @return a ListenableFuture containing the result of the read
93 public abstract ListenableFuture<Optional<NormalizedNode>> read(LogicalDatastoreType store,
94 YangInstanceIdentifier path);
97 * Read data selected using fields from the datastore.
99 * @param store the logical data store which should be modified
100 * @param path the parent data object path
101 * @param fields paths to selected fields relative to parent path
102 * @return a ListenableFuture containing the result of the read
104 public abstract ListenableFuture<Optional<NormalizedNode>> read(LogicalDatastoreType store,
105 YangInstanceIdentifier path, List<YangInstanceIdentifier> fields);
108 * Check if data already exists in the datastore.
110 * @param store the logical data store which should be modified
111 * @param path the data object path
112 * @return a FluentFuture containing the result of the check
114 public abstract ListenableFuture<Boolean> exists(LogicalDatastoreType store, YangInstanceIdentifier path);
117 * Delete data from the configuration datastore. If the data does not exist, this operation will fail, as outlined
118 * in <a href="https://www.rfc-editor.org/rfc/rfc8040#section-4.7">RFC8040 section 4.7</a>
120 * @param path Path to delete
121 * @return A {@link RestconfFuture}
122 * @throws NullPointerException if {@code path} is {@code null}
124 public final @NonNull RestconfFuture<Empty> delete(final YangInstanceIdentifier path) {
125 final var ret = new SettableRestconfFuture<Empty>();
126 delete(ret, requireNonNull(path));
130 protected abstract void delete(@NonNull SettableRestconfFuture<Empty> future, @NonNull YangInstanceIdentifier path);
133 * Merge data into the configuration datastore, as outlined in
134 * <a href="https://www.rfc-editor.org/rfc/rfc8040#section-4.6.1">RFC8040 section 4.6.1</a>.
136 * @param path Path to merge
137 * @param data Data to merge
138 * @param context Corresponding EffectiveModelContext
139 * @return A {@link RestconfFuture}
140 * @throws NullPointerException if any argument is {@code null}
142 public final @NonNull RestconfFuture<Empty> merge(final YangInstanceIdentifier path, final NormalizedNode data,
143 final EffectiveModelContext context) {
144 final var ret = new SettableRestconfFuture<Empty>();
145 merge(ret, requireNonNull(path), requireNonNull(data), requireNonNull(context));
149 private void merge(final @NonNull SettableRestconfFuture<Empty> future,
150 final @NonNull YangInstanceIdentifier path, final @NonNull NormalizedNode data,
151 final @NonNull EffectiveModelContext context) {
152 final var tx = prepareWriteExecution();
153 // FIXME: this method should be further specialized to eliminate this call -- it is only needed for MD-SAL
154 TransactionUtil.ensureParentsByMerge(path, context, tx);
155 tx.merge(path, data);
156 Futures.addCallback(tx.commit(), new FutureCallback<CommitInfo>() {
158 public void onSuccess(final CommitInfo result) {
159 future.set(Empty.value());
163 public void onFailure(final Throwable cause) {
164 future.setFailure(TransactionUtil.decodeException(cause, "MERGE", path));
166 }, MoreExecutors.directExecutor());