Bug 499: Initial draft of in-memory datastore and data broker
[controller.git] / opendaylight / md-sal / sal-dom-broker / src / main / java / org / opendaylight / controller / md / sal / dom / store / impl / InMemoryDOMDataStore.java
1 /*
2  * Copyright (c) 2014 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 package org.opendaylight.controller.md.sal.dom.store.impl;
9
10 import static com.google.common.base.Preconditions.checkNotNull;
11 import static com.google.common.base.Preconditions.checkState;
12 import static org.opendaylight.controller.md.sal.dom.store.impl.StoreUtils.increase;
13
14 import java.util.concurrent.Callable;
15 import java.util.concurrent.atomic.AtomicLong;
16
17 import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope;
18 import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeListener;
19 import org.opendaylight.controller.md.sal.dom.store.impl.tree.NodeModification;
20 import org.opendaylight.controller.md.sal.dom.store.impl.tree.StoreMetadataNode;
21 import org.opendaylight.controller.sal.core.spi.data.DOMStore;
22 import org.opendaylight.controller.sal.core.spi.data.DOMStoreReadTransaction;
23 import org.opendaylight.controller.sal.core.spi.data.DOMStoreReadWriteTransaction;
24 import org.opendaylight.controller.sal.core.spi.data.DOMStoreThreePhaseCommitCohort;
25 import org.opendaylight.controller.sal.core.spi.data.DOMStoreWriteTransaction;
26 import org.opendaylight.yangtools.concepts.Identifiable;
27 import org.opendaylight.yangtools.concepts.ListenerRegistration;
28 import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
29 import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.PathArgument;
30 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
31 import org.opendaylight.yangtools.yang.data.impl.schema.NormalizedNodeUtils;
32 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
33 import org.opendaylight.yangtools.yang.model.api.SchemaContextListener;
34 import org.slf4j.Logger;
35 import org.slf4j.LoggerFactory;
36
37 import com.google.common.base.Optional;
38 import com.google.common.primitives.UnsignedLong;
39 import com.google.common.util.concurrent.Futures;
40 import com.google.common.util.concurrent.ListenableFuture;
41 import com.google.common.util.concurrent.ListeningExecutorService;
42
43 public class InMemoryDOMDataStore implements DOMStore, Identifiable<String>, SchemaContextListener {
44
45     private static final Logger LOG = LoggerFactory.getLogger(InMemoryDOMDataStore.class);
46
47     private final AtomicLong txCounter = new AtomicLong(0);
48
49     private DataAndMetadataSnapshot snapshot;
50     private ModificationApplyOperation operation;
51
52     private final ListeningExecutorService executor;
53     private final String name;
54
55     private SchemaContext schemaContext;
56
57     public InMemoryDOMDataStore(final String name, final ListeningExecutorService executor) {
58         this.executor = executor;
59         this.name = name;
60         this.operation = new AllwaysFailOperation();
61         this.snapshot = DataAndMetadataSnapshot.createEmpty();
62     }
63
64     @Override
65     public String getIdentifier() {
66         return name;
67     }
68
69     @Override
70     public DOMStoreReadTransaction newReadOnlyTransaction() {
71         return new SnapshotBackedReadTransaction(nextIdentifier(), snapshot);
72     }
73
74     @Override
75     public DOMStoreReadWriteTransaction newReadWriteTransaction() {
76         return new SnapshotBackedReadWriteTransaction(nextIdentifier(), snapshot, this, operation);
77     }
78
79     @Override
80     public DOMStoreWriteTransaction newWriteOnlyTransaction() {
81         return new SnaphostBackedWriteTransaction(nextIdentifier(), snapshot, this, operation);
82     }
83
84     @Override
85     public synchronized void onGlobalContextUpdated(final SchemaContext ctx) {
86         operation = SchemaAwareApplyOperationRoot.from(ctx);
87         schemaContext = ctx;
88     }
89
90     @Override
91     public <L extends AsyncDataChangeListener<InstanceIdentifier, NormalizedNode<?, ?>>> ListenerRegistration<L> registerChangeListener(
92             final InstanceIdentifier path, final L listener, final DataChangeScope scope) {
93         return null;
94     }
95
96     private synchronized DOMStoreThreePhaseCommitCohort submit(
97             final SnaphostBackedWriteTransaction snaphostBackedWriteTransaction) {
98         return new ThreePhaseCommitImpl(snaphostBackedWriteTransaction);
99     }
100
101     private Object nextIdentifier() {
102         return name + "-" + txCounter.getAndIncrement();
103     }
104
105     private static class SnapshotBackedReadTransaction implements DOMStoreReadTransaction {
106
107         private DataAndMetadataSnapshot stableSnapshot;
108         private final Object identifier;
109
110         public SnapshotBackedReadTransaction(final Object identifier, final DataAndMetadataSnapshot snapshot) {
111             this.identifier = identifier;
112             this.stableSnapshot = snapshot;
113         }
114
115         @Override
116         public Object getIdentifier() {
117             return identifier;
118         }
119
120         @Override
121         public void close() {
122             stableSnapshot = null;
123         }
124
125         @Override
126         public ListenableFuture<Optional<NormalizedNode<?, ?>>> read(final InstanceIdentifier path) {
127             checkNotNull(path, "Path must not be null.");
128             checkState(stableSnapshot != null, "Transaction is closed");
129             return Futures.immediateFuture(NormalizedNodeUtils.findNode(stableSnapshot.getDataTree(), path));
130         }
131
132         @Override
133         public String toString() {
134             return "SnapshotBackedReadTransaction [id =" + identifier + "]";
135         }
136
137     }
138
139     private static class SnaphostBackedWriteTransaction implements DOMStoreWriteTransaction {
140
141         private MutableDataTree mutableTree;
142         private final Object identifier;
143         private InMemoryDOMDataStore store;
144
145         private boolean ready = false;
146
147         public SnaphostBackedWriteTransaction(final Object identifier, final DataAndMetadataSnapshot snapshot,
148                 final InMemoryDOMDataStore store, final ModificationApplyOperation applyOper) {
149             this.identifier = identifier;
150             mutableTree = MutableDataTree.from(snapshot, applyOper);
151             this.store = store;
152         }
153
154         @Override
155         public Object getIdentifier() {
156             return identifier;
157         }
158
159         @Override
160         public void close() {
161             this.mutableTree = null;
162             this.store = null;
163         }
164
165         @Override
166         public void write(final InstanceIdentifier path, final NormalizedNode<?, ?> data) {
167             checkNotReady();
168             mutableTree.write(path, data);
169         }
170
171         @Override
172         public void delete(final InstanceIdentifier path) {
173             checkNotReady();
174             mutableTree.delete(path);
175         }
176
177         protected boolean isReady() {
178             return ready;
179         }
180
181         protected void checkNotReady() {
182             checkState(!ready, "Transaction is ready. No further modifications allowed.");
183         }
184
185         @Override
186         public synchronized DOMStoreThreePhaseCommitCohort ready() {
187             ready = true;
188             LOG.debug("Store transaction: {} : Ready",getIdentifier());
189             mutableTree.seal();
190             return store.submit(this);
191         }
192
193         protected MutableDataTree getMutatedView() {
194             return mutableTree;
195         }
196
197         @Override
198         public String toString() {
199             return "SnaphostBackedWriteTransaction [id=" + getIdentifier() + ", ready=" + isReady() + "]";
200         }
201
202     }
203
204     private static class SnapshotBackedReadWriteTransaction extends SnaphostBackedWriteTransaction implements
205             DOMStoreReadWriteTransaction {
206
207         protected SnapshotBackedReadWriteTransaction(final Object identifier, final DataAndMetadataSnapshot snapshot,
208                 final InMemoryDOMDataStore store, final ModificationApplyOperation applyOper) {
209             super(identifier, snapshot, store, applyOper);
210         }
211
212         @Override
213         public ListenableFuture<Optional<NormalizedNode<?, ?>>> read(final InstanceIdentifier path) {
214             return Futures.immediateFuture(getMutatedView().read(path));
215         }
216
217         @Override
218         public String toString() {
219             return "SnapshotBackedReadWriteTransaction [id=" + getIdentifier() + ", ready=" + isReady() + "]";
220         }
221
222     }
223
224     private class ThreePhaseCommitImpl implements DOMStoreThreePhaseCommitCohort {
225
226         private final SnaphostBackedWriteTransaction transaction;
227         private final NodeModification modification;
228
229         private DataAndMetadataSnapshot storeSnapshot;
230         private Optional<StoreMetadataNode> proposedSubtree;
231
232         public ThreePhaseCommitImpl(final SnaphostBackedWriteTransaction writeTransaction) {
233             this.transaction = writeTransaction;
234             this.modification = transaction.getMutatedView().getRootModification();
235         }
236
237         @Override
238         public ListenableFuture<Boolean> canCommit() {
239             final DataAndMetadataSnapshot snapshotCapture = snapshot;
240             final ModificationApplyOperation snapshotOperation = operation;
241
242             return executor.submit(new Callable<Boolean>() {
243
244                 @Override
245                 public Boolean call() throws Exception {
246                     boolean applicable = snapshotOperation.isApplicable(modification, Optional.of(snapshotCapture.getMetadataTree()));
247                     LOG.debug("Store Transcation: {} : canCommit : {}",transaction.getIdentifier(),applicable);
248                     return applicable;
249                 }
250             });
251         }
252
253         @Override
254         public ListenableFuture<Void> preCommit() {
255             storeSnapshot = snapshot;
256             return executor.submit(new Callable<Void>() {
257
258                 @Override
259                 public Void call() throws Exception {
260                     StoreMetadataNode metadataTree = storeSnapshot.getMetadataTree();
261                     proposedSubtree = operation.apply(modification, Optional.of(metadataTree),increase(metadataTree.getSubtreeVersion()));
262                     return null;
263                 }
264             });
265         }
266
267         @Override
268         public ListenableFuture<Void> abort() {
269             storeSnapshot = null;
270             proposedSubtree = null;
271             return Futures.<Void> immediateFuture(null);
272         }
273
274         @Override
275         public ListenableFuture<Void> commit() {
276             checkState(proposedSubtree != null);
277             checkState(storeSnapshot != null);
278             // return ImmediateFuture<>;
279             InMemoryDOMDataStore.this.commit(storeSnapshot, proposedSubtree);
280             return Futures.<Void> immediateFuture(null);
281         }
282
283     }
284
285     private synchronized void commit(final DataAndMetadataSnapshot storeSnapshot,
286             final Optional<StoreMetadataNode> proposedSubtree) {
287         //LOG.info("Updating Store snaphot.");
288         checkState(snapshot == storeSnapshot, "Store snapshot and transaction snapshot differs");
289         snapshot = DataAndMetadataSnapshot.builder().setMetadataTree(proposedSubtree.get())
290                 .setSchemaContext(schemaContext).build();
291     }
292
293     private class AllwaysFailOperation implements ModificationApplyOperation {
294
295         @Override
296         public Optional<StoreMetadataNode> apply(final NodeModification modification,
297                 final Optional<StoreMetadataNode> storeMeta,final UnsignedLong subtreeVersion) {
298             throw new IllegalStateException("Schema Context is not available.");
299         }
300
301         @Override
302         public boolean isApplicable(final NodeModification modification, final Optional<StoreMetadataNode> storeMetadata) {
303             throw new IllegalStateException("Schema Context is not available.");
304         }
305
306         @Override
307         public Optional<ModificationApplyOperation> getChild(final PathArgument child) {
308             throw new IllegalStateException("Schema Context is not available.");
309         }
310
311         @Override
312         public void verifyStructure(final NodeModification modification) throws IllegalArgumentException {
313             throw new IllegalStateException("Schema Context is not available.");
314         }
315
316     }
317 }