e0dbe985a76902c81745ee0eae92fba7a5f49109
[mdsal.git] / dom / mdsal-dom-inmemory-datastore / src / main / java / org / opendaylight / mdsal / dom / store / inmemory / InmemoryDOMDataTreeShardWriteTransaction.java
1 /*
2  * Copyright (c) 2016 Cisco Systems, Inc. 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
9 package org.opendaylight.mdsal.dom.store.inmemory;
10
11 import com.google.common.base.Optional;
12 import com.google.common.base.Preconditions;
13 import com.google.common.util.concurrent.CheckedFuture;
14 import com.google.common.util.concurrent.ListenableFuture;
15 import com.google.common.util.concurrent.ListeningExecutorService;
16 import java.util.ArrayList;
17 import java.util.Iterator;
18 import java.util.Map.Entry;
19 import java.util.concurrent.atomic.AtomicLong;
20 import org.opendaylight.mdsal.common.api.ReadFailedException;
21 import org.opendaylight.mdsal.dom.api.DOMDataTreeIdentifier;
22 import org.opendaylight.mdsal.dom.api.DOMDataTreeWriteCursor;
23 import org.opendaylight.mdsal.dom.spi.store.DOMStoreThreePhaseCommitCohort;
24 import org.opendaylight.yangtools.concepts.Identifiable;
25 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
26 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
27 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
28 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTree;
29 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeModification;
30 import org.slf4j.Logger;
31 import org.slf4j.LoggerFactory;
32
33 class InmemoryDOMDataTreeShardWriteTransaction implements DOMDataTreeShardWriteTransaction, Identifiable<String> {
34
35     private static final Logger LOG = LoggerFactory.getLogger(InmemoryDOMDataTreeShardWriteTransaction.class);
36
37     private enum SimpleCursorOperation {
38         MERGE {
39             @Override
40             void applyOnLeaf(final DOMDataTreeWriteCursor cursor, final PathArgument child,
41                     final NormalizedNode<?, ?> data) {
42                 cursor.merge(child, data);
43             }
44         },
45         DELETE {
46             @Override
47             void applyOnLeaf(final DOMDataTreeWriteCursor cursor, final PathArgument child,
48                     final NormalizedNode<?, ?> data) {
49                 cursor.delete(child);
50             }
51         },
52         WRITE {
53             @Override
54             void applyOnLeaf(final DOMDataTreeWriteCursor cursor, final PathArgument child,
55                     final NormalizedNode<?, ?> data) {
56                 cursor.write(child, data);
57             }
58         };
59
60         abstract void applyOnLeaf(DOMDataTreeWriteCursor cursor, PathArgument child, NormalizedNode<?, ?> data);
61
62         void apply(final DOMDataTreeWriteCursor cursor, final YangInstanceIdentifier path,
63                 final NormalizedNode<?, ?> data) {
64             int enterCount = 0;
65             final Iterator<PathArgument> it = path.getPathArguments().iterator();
66             if (it.hasNext()) {
67                 while (true) {
68                     final PathArgument currentArg = it.next();
69                     if (!it.hasNext()) {
70                         applyOnLeaf(cursor, currentArg, data);
71                         break;
72                     }
73
74                     // We need to enter one level deeper, we are not at leaf (modified) node
75                     cursor.enter(currentArg);
76                     enterCount++;
77                 }
78             }
79
80             cursor.exit(enterCount);
81         }
82     }
83
84     private static final AtomicLong COUNTER = new AtomicLong();
85
86     private final ArrayList<DOMStoreThreePhaseCommitCohort> cohorts = new ArrayList<>();
87     private final InMemoryDOMDataTreeShardChangePublisher changePublisher;
88     private final InMemoryDOMDataTreeShardProducer producer;
89     private final ShardDataModification modification;
90     private final ListeningExecutorService executor;
91     private final DataTree rootShardDataTree;
92     private final String identifier;
93
94     private DataTreeModification rootModification = null;
95     private DOMDataTreeWriteCursor cursor;
96     private boolean finished = false;
97
98     InmemoryDOMDataTreeShardWriteTransaction(final InMemoryDOMDataTreeShardProducer producer,
99                                              final ShardDataModification root,
100                                              final DataTree rootShardDataTree,
101                                              final InMemoryDOMDataTreeShardChangePublisher changePublisher,
102                                              final ListeningExecutorService executor) {
103         this.producer = producer;
104         this.modification = Preconditions.checkNotNull(root);
105         this.rootShardDataTree = Preconditions.checkNotNull(rootShardDataTree);
106         this.changePublisher = Preconditions.checkNotNull(changePublisher);
107         this.identifier = "INMEMORY-SHARD-TX-" + COUNTER.getAndIncrement();
108         LOG.debug("Shard transaction{} created", identifier);
109         this.executor = executor;
110     }
111
112     @Override
113     public String getIdentifier() {
114         return identifier;
115     }
116
117     private DOMDataTreeWriteCursor getCursor() {
118         if (cursor == null) {
119             cursor = new ShardDataModificationCursor(modification, this);
120         }
121         return cursor;
122     }
123
124     void delete(final YangInstanceIdentifier path) {
125         final YangInstanceIdentifier relativePath = toRelative(path);
126         Preconditions.checkArgument(!YangInstanceIdentifier.EMPTY.equals(relativePath),
127                 "Deletion of shard root is not allowed");
128         SimpleCursorOperation.DELETE.apply(getCursor(), relativePath , null);
129     }
130
131     void merge(final YangInstanceIdentifier path, final NormalizedNode<?, ?> data) {
132         SimpleCursorOperation.MERGE.apply(getCursor(), toRelative(path), data);
133     }
134
135     void write(final YangInstanceIdentifier path, final NormalizedNode<?, ?> data) {
136         SimpleCursorOperation.DELETE.apply(getCursor(), toRelative(path), data);
137     }
138
139     private YangInstanceIdentifier toRelative(final YangInstanceIdentifier path) {
140         final Optional<YangInstanceIdentifier> relative =
141                 path.relativeTo(modification.getPrefix().getRootIdentifier());
142         Preconditions.checkArgument(relative.isPresent());
143         return relative.get();
144     }
145
146     public CheckedFuture<Optional<NormalizedNode<?, ?>>, ReadFailedException> read(final YangInstanceIdentifier path) {
147         throw new UnsupportedOperationException("Not implemented yet");
148     }
149
150     public CheckedFuture<Boolean, ReadFailedException> exists(final YangInstanceIdentifier path) {
151         throw new UnsupportedOperationException("Not implemented yet");
152     }
153
154     @Override
155     public void close() {
156         Preconditions.checkState(!finished, "Attempting to close an already finished transaction.");
157         modification.closeTransactions();
158         if (cursor != null) {
159             cursor.close();
160         }
161         producer.transactionAborted(this);
162         finished = true;
163     }
164
165     void cursorClosed() {
166         Preconditions.checkNotNull(cursor);
167         modification.closeCursor();
168         cursor = null;
169     }
170
171     public boolean isFinished() {
172         return finished;
173     }
174
175     @Override
176     public void ready() {
177         Preconditions.checkState(!finished, "Attempting to ready an already finished transaction.");
178         Preconditions.checkState(cursor == null, "Attempting to ready a transaction that has an open cursor.");
179         Preconditions.checkNotNull(modification, "Attempting to ready an empty transaction.");
180
181         LOG.debug("Readying open transaction on shard {}", modification.getPrefix());
182         rootModification = modification.seal();
183
184         producer.transactionReady(this, rootModification);
185         cohorts.add(new InMemoryDOMDataTreeShardThreePhaseCommitCohort(
186                 rootShardDataTree, rootModification, changePublisher));
187         for (final Entry<DOMDataTreeIdentifier, ForeignShardModificationContext> entry :
188                 modification.getChildShards().entrySet()) {
189             cohorts.add(new ForeignShardThreePhaseCommitCohort(entry.getKey(), entry.getValue()));
190         }
191         finished = true;
192     }
193
194     @Override
195     public ListenableFuture<Void> submit() {
196         LOG.debug("Submitting open transaction on shard {}", modification.getPrefix());
197
198         Preconditions.checkNotNull(cohorts);
199         Preconditions.checkState(!cohorts.isEmpty(), "Transaction was not readied yet.");
200
201         return executor.submit(new ShardSubmitCoordinationTask(modification.getPrefix(), cohorts, this));
202     }
203
204     @Override
205     public ListenableFuture<Boolean> validate() {
206         LOG.debug("CanCommit on open transaction on shard {}", modification.getPrefix());
207         return executor.submit(new ShardCanCommitCoordinationTask(modification.getPrefix(), cohorts));
208     }
209
210     @Override
211     public ListenableFuture<Void> prepare() {
212         LOG.debug("PreCommit on open transaction on shard {}", modification.getPrefix());
213         return executor.submit(new ShardPreCommitCoordinationTask(modification.getPrefix(), cohorts));
214     }
215
216     @Override
217     public ListenableFuture<Void> commit() {
218         LOG.debug("Commit open transaction on shard {}", modification.getPrefix());
219         return executor.submit(new ShardCommitCoordinationTask(modification.getPrefix(), cohorts, this));
220     }
221
222     DataTreeModification getRootModification() {
223         Preconditions.checkNotNull(rootModification, "Transaction wasn't sealed yet");
224         return rootModification;
225     }
226
227     void transactionCommited(final InmemoryDOMDataTreeShardWriteTransaction tx) {
228         producer.onTransactionCommited(tx);
229     }
230
231     @Override
232     public DOMDataTreeWriteCursor createCursor(final DOMDataTreeIdentifier prefix) {
233         Preconditions.checkState(!finished, "Transaction is finished/closed already.");
234         Preconditions.checkState(cursor == null, "Previous cursor wasn't closed");
235         final DOMDataTreeWriteCursor ret = getCursor();
236         final YangInstanceIdentifier relativePath = toRelative(prefix.getRootIdentifier());
237         ret.enter(relativePath.getPathArguments());
238         return ret;
239     }
240 }