510958bff350245c291c5e6d6f8847a94476b73e
[netconf.git] / restconf / restconf-nb / src / main / java / org / opendaylight / restconf / nb / rfc8040 / rests / transactions / RestconfTransaction.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.ListenableFuture;
13 import java.util.ArrayList;
14 import java.util.Optional;
15 import org.eclipse.jdt.annotation.NonNull;
16 import org.eclipse.jdt.annotation.Nullable;
17 import org.opendaylight.mdsal.common.api.CommitInfo;
18 import org.opendaylight.odlparent.logging.markers.Markers;
19 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
20 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
21 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
22 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodeContainer;
23 import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
24 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
25 import org.slf4j.Logger;
26 import org.slf4j.LoggerFactory;
27
28 /**
29  * A handle to a set of operations being executed atomically on top of some backing store.
30  */
31 // FIXME: it seems the first two operations deal with lifecycle of a transaction, while others invoke various
32 //        operations. This should be handled through proper allocation indirection.
33 abstract class RestconfTransaction {
34     private static final Logger LOG = LoggerFactory.getLogger(RestconfTransaction.class);
35
36     final @NonNull EffectiveModelContext modelContext;
37
38     RestconfTransaction(final EffectiveModelContext modelContext) {
39         this.modelContext = requireNonNull(modelContext);
40     }
41
42     /**
43      * Rollback changes and unlock the datastore.
44      */
45     // FIXME: this looks synchronous, but it should not be
46     abstract void cancel();
47
48     /**
49      * Confirm previous operations.
50      *
51      * @return a FluentFuture containing the result of the commit information
52      */
53     abstract ListenableFuture<? extends @NonNull CommitInfo> commit();
54
55     /**
56      * Delete data from the datastore.
57      *
58      * @param path the data object path
59      */
60     final void delete(final YangInstanceIdentifier path) {
61         LOG.trace("Delete {}", path);
62         deleteImpl(requireNonNull(path));
63     }
64
65     abstract void deleteImpl(@NonNull YangInstanceIdentifier path);
66
67     /**
68      * Remove data from the datastore.
69      *
70      * @param path the data object path
71      */
72     final void remove(final YangInstanceIdentifier path) {
73         LOG.trace("Remove {}", path);
74         removeImpl(requireNonNull(path));
75     }
76
77     abstract void removeImpl(@NonNull YangInstanceIdentifier path);
78
79     /**
80      * Merges a piece of data with the existing data at a specified path.
81      *
82      * @param path the data object path
83      * @param data the data object to be merged to the specified path
84      */
85     final void merge(final YangInstanceIdentifier path, final NormalizedNode data) {
86         LOG.trace("Merge {}", path);
87         LOG.trace(Markers.confidential(), "Merge with {}", data.prettyTree());
88         mergeImpl(requireNonNull(path), data);
89     }
90
91     abstract void mergeImpl(@NonNull YangInstanceIdentifier path, @NonNull NormalizedNode data);
92
93     /**
94      * Stores a piece of data at the specified path.
95      *
96      * @param path    the data object path
97      * @param data    the data object to be merged to the specified path
98      */
99     final void create(final YangInstanceIdentifier path, final NormalizedNode data) {
100         LOG.trace("Create {}", path);
101         LOG.trace(Markers.confidential(), "Create as {}", data.prettyTree());
102         createImpl(requireNonNull(path), data);
103     }
104
105     abstract void createImpl(@NonNull YangInstanceIdentifier path, @NonNull NormalizedNode data);
106
107     /**
108      * Replace a piece of data at the specified path.
109      *
110      * @param path    the data object path
111      * @param data    the data object to be merged to the specified path
112      */
113     final void replace(final YangInstanceIdentifier path, final NormalizedNode data) {
114         LOG.trace("Replace {}", path);
115         LOG.trace(Markers.confidential(), "Replace with {}", data.prettyTree());
116         replaceImpl(requireNonNull(path), data);
117     }
118
119     abstract void replaceImpl(@NonNull YangInstanceIdentifier path, @NonNull NormalizedNode data);
120
121     final @Nullable NormalizedNodeContainer<?> readList(final YangInstanceIdentifier path) {
122         return (NormalizedNodeContainer<?>) TransactionUtil.syncAccess(read(path), path).orElse(null);
123     }
124
125     abstract ListenableFuture<Optional<NormalizedNode>> read(YangInstanceIdentifier path);
126
127     /**
128      * Merge parents of data.
129      *
130      * @param path    path of data
131      */
132     // FIXME: this method should only be invoked in MdsalRestconfStrategy, and even then only if we are crossing
133     //        an implicit list.
134     final void ensureParentsByMerge(final YangInstanceIdentifier path) {
135         final var normalizedPathWithoutChildArgs = new ArrayList<PathArgument>();
136         YangInstanceIdentifier rootNormalizedPath = null;
137
138         final var it = path.getPathArguments().iterator();
139
140         while (it.hasNext()) {
141             final var pathArgument = it.next();
142             if (rootNormalizedPath == null) {
143                 rootNormalizedPath = YangInstanceIdentifier.of(pathArgument);
144             }
145
146             if (it.hasNext()) {
147                 normalizedPathWithoutChildArgs.add(pathArgument);
148             }
149         }
150
151         if (normalizedPathWithoutChildArgs.isEmpty()) {
152             return;
153         }
154
155         merge(rootNormalizedPath,
156             ImmutableNodes.fromInstanceId(modelContext, YangInstanceIdentifier.of(normalizedPathWithoutChildArgs)));
157     }
158 }