Fix modernization issues
[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 static com.google.common.base.Preconditions.checkState;
11 import static java.util.Objects.requireNonNull;
12
13 import com.google.common.annotations.Beta;
14 import java.util.AbstractMap.SimpleEntry;
15 import java.util.Map.Entry;
16 import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
17 import org.opendaylight.controller.sal.core.spi.data.SnapshotBackedReadTransaction.TransactionClosePrototype;
18 import org.opendaylight.controller.sal.core.spi.data.SnapshotBackedWriteTransaction.TransactionReadyPrototype;
19 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeModification;
20 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeSnapshot;
21 import org.slf4j.Logger;
22 import org.slf4j.LoggerFactory;
23
24 /**
25  * Abstract implementation of the {@link DOMStoreTransactionChain} interface relying on {@link DataTreeSnapshot}
26  * supplier and backend commit coordinator.
27  *
28  * @param <T> transaction identifier type
29  * @deprecated Use {@link org.opendaylight.mdsal.dom.spi.store.AbstractSnapshotBackedTransactionChain} instead.
30  */
31 @Deprecated
32 @Beta
33 public abstract class AbstractSnapshotBackedTransactionChain<T> extends TransactionReadyPrototype<T>
34         implements DOMStoreTransactionChain, TransactionClosePrototype<T> {
35     private abstract static class State {
36         /**
37          * Allocate a new snapshot.
38          *
39          * @return A new snapshot
40          */
41         protected abstract DataTreeSnapshot getSnapshot(Object transactionId);
42     }
43
44     private static final class Idle extends State {
45         private final AbstractSnapshotBackedTransactionChain<?> chain;
46
47         Idle(final AbstractSnapshotBackedTransactionChain<?> chain) {
48             this.chain = requireNonNull(chain);
49         }
50
51         @Override
52         protected DataTreeSnapshot getSnapshot(final Object transactionId) {
53             return chain.takeSnapshot();
54         }
55     }
56
57     /**
58      * We have a transaction out there.
59      */
60     private static final class Allocated extends State {
61         private static final AtomicReferenceFieldUpdater<Allocated, DataTreeSnapshot> SNAPSHOT_UPDATER =
62                 AtomicReferenceFieldUpdater.newUpdater(Allocated.class, DataTreeSnapshot.class, "snapshot");
63         private final DOMStoreWriteTransaction transaction;
64         private volatile DataTreeSnapshot snapshot;
65
66         Allocated(final DOMStoreWriteTransaction transaction) {
67             this.transaction = requireNonNull(transaction);
68         }
69
70         public DOMStoreWriteTransaction getTransaction() {
71             return transaction;
72         }
73
74         @Override
75         protected DataTreeSnapshot getSnapshot(final Object transactionId) {
76             final DataTreeSnapshot ret = snapshot;
77             checkState(ret != null,
78                     "Could not get snapshot for transaction %s - previous transaction %s is not ready yet",
79                     transactionId, transaction.getIdentifier());
80             return ret;
81         }
82
83         void setSnapshot(final DataTreeSnapshot snapshot) {
84             final boolean success = SNAPSHOT_UPDATER.compareAndSet(this, null, snapshot);
85             checkState(success, "Transaction %s has already been marked as ready", transaction.getIdentifier());
86         }
87     }
88
89     /**
90      * Chain is logically shut down, no further allocation allowed.
91      */
92     private static final class Shutdown extends State {
93         private final String message;
94
95         Shutdown(final String message) {
96             this.message = requireNonNull(message);
97         }
98
99         @Override
100         protected DataTreeSnapshot getSnapshot(final Object transactionId) {
101             throw new IllegalStateException(message);
102         }
103     }
104
105     @SuppressWarnings("rawtypes")
106     private static final AtomicReferenceFieldUpdater<AbstractSnapshotBackedTransactionChain, State> STATE_UPDATER =
107             AtomicReferenceFieldUpdater.newUpdater(AbstractSnapshotBackedTransactionChain.class, State.class, "state");
108     private static final Logger LOG = LoggerFactory.getLogger(AbstractSnapshotBackedTransactionChain.class);
109     private static final Shutdown CLOSED = new Shutdown("Transaction chain is closed");
110     private static final Shutdown FAILED = new Shutdown("Transaction chain has failed");
111     private final Idle idleState;
112     private volatile State state;
113
114     protected AbstractSnapshotBackedTransactionChain() {
115         idleState = new Idle(this);
116         state = idleState;
117     }
118
119     private Entry<State, DataTreeSnapshot> getSnapshot(final T transactionId) {
120         final State localState = state;
121         return new SimpleEntry<>(localState, localState.getSnapshot(transactionId));
122     }
123
124     private boolean recordTransaction(final State expected, final DOMStoreWriteTransaction transaction) {
125         final State localState = new Allocated(transaction);
126         return STATE_UPDATER.compareAndSet(this, expected, localState);
127     }
128
129     @Override
130     public final DOMStoreReadTransaction newReadOnlyTransaction() {
131         return newReadOnlyTransaction(nextTransactionIdentifier());
132     }
133
134     protected DOMStoreReadTransaction newReadOnlyTransaction(final T transactionId) {
135         final Entry<State, DataTreeSnapshot> entry = getSnapshot(transactionId);
136         return SnapshotBackedTransactions.newReadTransaction(transactionId, getDebugTransactions(), entry.getValue(),
137             this);
138     }
139
140     @Override
141     public void transactionClosed(final SnapshotBackedReadTransaction<T> tx) {
142         // Defaults to no-op
143     }
144
145     @Override
146     public final DOMStoreReadWriteTransaction newReadWriteTransaction() {
147         return newReadWriteTransaction(nextTransactionIdentifier());
148     }
149
150     protected DOMStoreReadWriteTransaction newReadWriteTransaction(final T transactionId) {
151         Entry<State, DataTreeSnapshot> entry;
152         DOMStoreReadWriteTransaction ret;
153
154         do {
155             entry = getSnapshot(transactionId);
156             ret = new SnapshotBackedReadWriteTransaction<>(transactionId, getDebugTransactions(), entry.getValue(),
157                     this);
158         } while (!recordTransaction(entry.getKey(), ret));
159
160         return ret;
161     }
162
163     @Override
164     public final DOMStoreWriteTransaction newWriteOnlyTransaction() {
165         return newWriteOnlyTransaction(nextTransactionIdentifier());
166     }
167
168     protected DOMStoreWriteTransaction newWriteOnlyTransaction(final T transactionId) {
169         Entry<State, DataTreeSnapshot> entry;
170         DOMStoreWriteTransaction ret;
171
172         do {
173             entry = getSnapshot(transactionId);
174             ret = new SnapshotBackedWriteTransaction<>(transactionId, getDebugTransactions(), entry.getValue(), this);
175         } while (!recordTransaction(entry.getKey(), ret));
176
177         return ret;
178     }
179
180     @Override
181     protected final void transactionAborted(final SnapshotBackedWriteTransaction<T> tx) {
182         final State localState = state;
183         if (localState instanceof Allocated) {
184             final Allocated allocated = (Allocated)localState;
185             if (allocated.getTransaction().equals(tx)) {
186                 final boolean success = STATE_UPDATER.compareAndSet(this, localState, idleState);
187                 if (!success) {
188                     LOG.warn(
189                         "Transaction {} aborted, but chain {} state already transitioned from {} to {}, very strange",
190                         tx, this, localState, state);
191                 }
192             }
193         }
194     }
195
196     @Override
197     protected final DOMStoreThreePhaseCommitCohort transactionReady(
198             final SnapshotBackedWriteTransaction<T> tx,
199             final DataTreeModification tree,
200             final Exception readyError) {
201
202         final State localState = state;
203
204         if (localState instanceof Allocated) {
205             final Allocated allocated = (Allocated)localState;
206             final DOMStoreWriteTransaction transaction = allocated.getTransaction();
207             checkState(tx.equals(transaction), "Mis-ordered ready transaction %s last allocated was %s", tx,
208                 transaction);
209             allocated.setSnapshot(tree);
210         } else {
211             LOG.debug("Ignoring transaction {} readiness due to state {}", tx, localState);
212         }
213
214         return createCohort(tx, tree, readyError);
215     }
216
217     @Override
218     public final void close() {
219         final State localState = state;
220
221         do {
222             checkState(!CLOSED.equals(localState), "Transaction chain %s has been closed", this);
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 }