Deprecate all MD-SAL APIs
[controller.git] / opendaylight / md-sal / sal-dom-spi / src / main / java / org / opendaylight / controller / sal / core / spi / data / AbstractSnapshotBackedTransactionChain.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.sal.core.spi.data;
9
10 import com.google.common.annotations.Beta;
11 import com.google.common.base.Preconditions;
12 import java.util.AbstractMap.SimpleEntry;
13 import java.util.Map.Entry;
14 import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
15 import org.opendaylight.controller.sal.core.spi.data.SnapshotBackedReadTransaction.TransactionClosePrototype;
16 import org.opendaylight.controller.sal.core.spi.data.SnapshotBackedWriteTransaction.TransactionReadyPrototype;
17 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeModification;
18 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeSnapshot;
19 import org.slf4j.Logger;
20 import org.slf4j.LoggerFactory;
21
22 /**
23  * Abstract implementation of the {@link DOMStoreTransactionChain} interface relying on {@link DataTreeSnapshot}
24  * supplier and backend commit coordinator.
25  *
26  * @param <T> transaction identifier type
27  * @deprecated Use {@link org.opendaylight.mdsal.dom.spi.store.AbstractSnapshotBackedTransactionChain} instead.
28  */
29 @Deprecated
30 @Beta
31 public abstract class AbstractSnapshotBackedTransactionChain<T> extends TransactionReadyPrototype<T>
32         implements DOMStoreTransactionChain, TransactionClosePrototype<T> {
33     private abstract static class State {
34         /**
35          * Allocate a new snapshot.
36          *
37          * @return A new snapshot
38          */
39         protected abstract DataTreeSnapshot getSnapshot(Object transactionId);
40     }
41
42     private static final class Idle extends State {
43         private final AbstractSnapshotBackedTransactionChain<?> chain;
44
45         Idle(final AbstractSnapshotBackedTransactionChain<?> chain) {
46             this.chain = Preconditions.checkNotNull(chain);
47         }
48
49         @Override
50         protected DataTreeSnapshot getSnapshot(Object transactionId) {
51             return chain.takeSnapshot();
52         }
53     }
54
55     /**
56      * We have a transaction out there.
57      */
58     private static final class Allocated extends State {
59         private static final AtomicReferenceFieldUpdater<Allocated, DataTreeSnapshot> SNAPSHOT_UPDATER =
60                 AtomicReferenceFieldUpdater.newUpdater(Allocated.class, DataTreeSnapshot.class, "snapshot");
61         private final DOMStoreWriteTransaction transaction;
62         private volatile DataTreeSnapshot snapshot;
63
64         Allocated(final DOMStoreWriteTransaction transaction) {
65             this.transaction = Preconditions.checkNotNull(transaction);
66         }
67
68         public DOMStoreWriteTransaction getTransaction() {
69             return transaction;
70         }
71
72         @Override
73         protected DataTreeSnapshot getSnapshot(Object transactionId) {
74             final DataTreeSnapshot ret = snapshot;
75             Preconditions.checkState(ret != null,
76                     "Could not get snapshot for transaction %s - previous transaction %s is not ready yet",
77                     transactionId, transaction.getIdentifier());
78             return ret;
79         }
80
81         void setSnapshot(final DataTreeSnapshot snapshot) {
82             final boolean success = SNAPSHOT_UPDATER.compareAndSet(this, null, snapshot);
83             Preconditions.checkState(success, "Transaction %s has already been marked as ready",
84                     transaction.getIdentifier());
85         }
86     }
87
88     /**
89      * Chain is logically shut down, no further allocation allowed.
90      */
91     private static final class Shutdown extends State {
92         private final String message;
93
94         Shutdown(final String message) {
95             this.message = Preconditions.checkNotNull(message);
96         }
97
98         @Override
99         protected DataTreeSnapshot getSnapshot(Object transactionId) {
100             throw new IllegalStateException(message);
101         }
102     }
103
104     @SuppressWarnings("rawtypes")
105     private static final AtomicReferenceFieldUpdater<AbstractSnapshotBackedTransactionChain, State> STATE_UPDATER =
106             AtomicReferenceFieldUpdater.newUpdater(AbstractSnapshotBackedTransactionChain.class, State.class, "state");
107     private static final Logger LOG = LoggerFactory.getLogger(AbstractSnapshotBackedTransactionChain.class);
108     private static final Shutdown CLOSED = new Shutdown("Transaction chain is closed");
109     private static final Shutdown FAILED = new Shutdown("Transaction chain has failed");
110     private final Idle idleState;
111     private volatile State state;
112
113     protected AbstractSnapshotBackedTransactionChain() {
114         idleState = new Idle(this);
115         state = idleState;
116     }
117
118     private Entry<State, DataTreeSnapshot> getSnapshot(T transactionId) {
119         final State localState = state;
120         return new SimpleEntry<>(localState, localState.getSnapshot(transactionId));
121     }
122
123     private boolean recordTransaction(final State expected, final DOMStoreWriteTransaction transaction) {
124         final State localState = new Allocated(transaction);
125         return STATE_UPDATER.compareAndSet(this, expected, localState);
126     }
127
128     @Override
129     public final DOMStoreReadTransaction newReadOnlyTransaction() {
130         return newReadOnlyTransaction(nextTransactionIdentifier());
131     }
132
133     protected DOMStoreReadTransaction newReadOnlyTransaction(T transactionId) {
134         final Entry<State, DataTreeSnapshot> entry = getSnapshot(transactionId);
135         return SnapshotBackedTransactions.newReadTransaction(transactionId, getDebugTransactions(), entry.getValue(),
136             this);
137     }
138
139     @Override
140     public void transactionClosed(final SnapshotBackedReadTransaction<T> tx) {
141         // Defaults to no-op
142     }
143
144     @Override
145     public final DOMStoreReadWriteTransaction newReadWriteTransaction() {
146         return newReadWriteTransaction(nextTransactionIdentifier());
147     }
148
149     protected DOMStoreReadWriteTransaction newReadWriteTransaction(T transactionId) {
150         Entry<State, DataTreeSnapshot> entry;
151         DOMStoreReadWriteTransaction ret;
152
153         do {
154             entry = getSnapshot(transactionId);
155             ret = new SnapshotBackedReadWriteTransaction<>(transactionId, getDebugTransactions(), entry.getValue(),
156                     this);
157         } while (!recordTransaction(entry.getKey(), ret));
158
159         return ret;
160     }
161
162     @Override
163     public final DOMStoreWriteTransaction newWriteOnlyTransaction() {
164         return newWriteOnlyTransaction(nextTransactionIdentifier());
165     }
166
167     protected DOMStoreWriteTransaction newWriteOnlyTransaction(T transactionId) {
168         Entry<State, DataTreeSnapshot> entry;
169         DOMStoreWriteTransaction ret;
170
171         do {
172             entry = getSnapshot(transactionId);
173             ret = new SnapshotBackedWriteTransaction<>(transactionId, getDebugTransactions(), entry.getValue(), this);
174         } while (!recordTransaction(entry.getKey(), ret));
175
176         return ret;
177     }
178
179     @Override
180     protected final void transactionAborted(final SnapshotBackedWriteTransaction<T> tx) {
181         final State localState = state;
182         if (localState instanceof Allocated) {
183             final Allocated allocated = (Allocated)localState;
184             if (allocated.getTransaction().equals(tx)) {
185                 final boolean success = STATE_UPDATER.compareAndSet(this, localState, idleState);
186                 if (!success) {
187                     LOG.warn(
188                         "Transaction {} aborted, but chain {} state already transitioned from {} to {}, very strange",
189                         tx, this, localState, state);
190                 }
191             }
192         }
193     }
194
195     @Override
196     protected final DOMStoreThreePhaseCommitCohort transactionReady(
197             final SnapshotBackedWriteTransaction<T> tx,
198             final DataTreeModification tree,
199             final Exception readyError) {
200
201         final State localState = state;
202
203         if (localState instanceof Allocated) {
204             final Allocated allocated = (Allocated)localState;
205             final DOMStoreWriteTransaction transaction = allocated.getTransaction();
206             Preconditions.checkState(tx.equals(transaction), "Mis-ordered ready transaction %s last allocated was %s",
207                     tx, transaction);
208             allocated.setSnapshot(tree);
209         } else {
210             LOG.debug("Ignoring transaction {} readiness due to state {}", tx, localState);
211         }
212
213         return createCohort(tx, tree, readyError);
214     }
215
216     @Override
217     public final void close() {
218         final State localState = state;
219
220         do {
221             Preconditions.checkState(!CLOSED.equals(localState), "Transaction chain %s has been closed", this);
222
223             if (FAILED.equals(localState)) {
224                 LOG.debug("Ignoring user close in failed state");
225                 return;
226             }
227         } while (!STATE_UPDATER.compareAndSet(this, localState, CLOSED));
228     }
229
230     /**
231      * Notify the base logic that a previously-submitted transaction has been committed successfully.
232      *
233      * @param transaction Transaction which completed successfully.
234      */
235     protected final void onTransactionCommited(final SnapshotBackedWriteTransaction<T> transaction) {
236         // If the committed transaction was the one we allocated last,
237         // we clear it and the ready snapshot, so the next transaction
238         // allocated refers to the data tree directly.
239         final State localState = state;
240
241         if (!(localState instanceof Allocated)) {
242             // This can legally happen if the chain is shut down before the transaction was committed
243             // by the backend.
244             LOG.debug("Ignoring successful transaction {} in state {}", transaction, localState);
245             return;
246         }
247
248         final Allocated allocated = (Allocated)localState;
249         final DOMStoreWriteTransaction tx = allocated.getTransaction();
250         if (!tx.equals(transaction)) {
251             LOG.debug("Ignoring non-latest successful transaction {} in state {}", transaction, allocated);
252             return;
253         }
254
255         if (!STATE_UPDATER.compareAndSet(this, localState, idleState)) {
256             LOG.debug("Transaction chain {} has already transitioned from {} to {}, not making it idle",
257                     this, localState, state);
258         }
259     }
260
261     /**
262      * Notify the base logic that a previously-submitted transaction has failed.
263      *
264      * @param transaction Transaction which failed.
265      * @param cause Failure cause
266      */
267     protected final void onTransactionFailed(final SnapshotBackedWriteTransaction<T> transaction,
268             final Throwable cause) {
269         LOG.debug("Transaction chain {} failed on transaction {}", this, transaction, cause);
270         state = FAILED;
271     }
272
273     /**
274      * Return the next transaction identifier.
275      *
276      * @return transaction identifier.
277      */
278     protected abstract T nextTransactionIdentifier();
279
280     /**
281      * Inquire as to whether transactions should record their allocation context.
282      *
283      * @return True if allocation context should be recorded.
284      */
285     protected abstract boolean getDebugTransactions();
286
287     /**
288      * Take a fresh {@link DataTreeSnapshot} from the backend.
289      *
290      * @return A new snapshot.
291      */
292     protected abstract DataTreeSnapshot takeSnapshot();
293
294     /**
295      * Create a cohort for driving the transaction through the commit process.
296      *
297      * @param transaction Transaction handle
298      * @param modification {@link DataTreeModification} which needs to be applied to the backend
299      * @param operationError Any previous error that could be reported through three phase commit
300      * @return A {@link DOMStoreThreePhaseCommitCohort} cohort.
301      */
302     protected abstract DOMStoreThreePhaseCommitCohort createCohort(SnapshotBackedWriteTransaction<T> transaction,
303                                                                    DataTreeModification modification,
304                                                                    Exception operationError);
305 }