2 * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
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
8 package org.opendaylight.controller.sal.core.spi.data;
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;
23 * Abstract implementation of the {@link DOMStoreTransactionChain} interface relying on {@link DataTreeSnapshot}
24 * supplier and backend commit coordinator.
26 * @param <T> transaction identifier type
27 * @deprecated Use {@link org.opendaylight.mdsal.dom.spi.store.AbstractSnapshotBackedTransactionChain} instead.
31 public abstract class AbstractSnapshotBackedTransactionChain<T> extends TransactionReadyPrototype<T>
32 implements DOMStoreTransactionChain, TransactionClosePrototype<T> {
33 private abstract static class State {
35 * Allocate a new snapshot.
37 * @return A new snapshot
39 protected abstract DataTreeSnapshot getSnapshot(Object transactionId);
42 private static final class Idle extends State {
43 private final AbstractSnapshotBackedTransactionChain<?> chain;
45 Idle(final AbstractSnapshotBackedTransactionChain<?> chain) {
46 this.chain = Preconditions.checkNotNull(chain);
50 protected DataTreeSnapshot getSnapshot(Object transactionId) {
51 return chain.takeSnapshot();
56 * We have a transaction out there.
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;
64 Allocated(final DOMStoreWriteTransaction transaction) {
65 this.transaction = Preconditions.checkNotNull(transaction);
68 public DOMStoreWriteTransaction getTransaction() {
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());
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());
89 * Chain is logically shut down, no further allocation allowed.
91 private static final class Shutdown extends State {
92 private final String message;
94 Shutdown(final String message) {
95 this.message = Preconditions.checkNotNull(message);
99 protected DataTreeSnapshot getSnapshot(Object transactionId) {
100 throw new IllegalStateException(message);
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;
113 protected AbstractSnapshotBackedTransactionChain() {
114 idleState = new Idle(this);
118 private Entry<State, DataTreeSnapshot> getSnapshot(T transactionId) {
119 final State localState = state;
120 return new SimpleEntry<>(localState, localState.getSnapshot(transactionId));
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);
129 public final DOMStoreReadTransaction newReadOnlyTransaction() {
130 return newReadOnlyTransaction(nextTransactionIdentifier());
133 protected DOMStoreReadTransaction newReadOnlyTransaction(T transactionId) {
134 final Entry<State, DataTreeSnapshot> entry = getSnapshot(transactionId);
135 return SnapshotBackedTransactions.newReadTransaction(transactionId, getDebugTransactions(), entry.getValue(),
140 public void transactionClosed(final SnapshotBackedReadTransaction<T> tx) {
145 public final DOMStoreReadWriteTransaction newReadWriteTransaction() {
146 return newReadWriteTransaction(nextTransactionIdentifier());
149 protected DOMStoreReadWriteTransaction newReadWriteTransaction(T transactionId) {
150 Entry<State, DataTreeSnapshot> entry;
151 DOMStoreReadWriteTransaction ret;
154 entry = getSnapshot(transactionId);
155 ret = new SnapshotBackedReadWriteTransaction<>(transactionId, getDebugTransactions(), entry.getValue(),
157 } while (!recordTransaction(entry.getKey(), ret));
163 public final DOMStoreWriteTransaction newWriteOnlyTransaction() {
164 return newWriteOnlyTransaction(nextTransactionIdentifier());
167 protected DOMStoreWriteTransaction newWriteOnlyTransaction(T transactionId) {
168 Entry<State, DataTreeSnapshot> entry;
169 DOMStoreWriteTransaction ret;
172 entry = getSnapshot(transactionId);
173 ret = new SnapshotBackedWriteTransaction<>(transactionId, getDebugTransactions(), entry.getValue(), this);
174 } while (!recordTransaction(entry.getKey(), ret));
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);
188 "Transaction {} aborted, but chain {} state already transitioned from {} to {}, very strange",
189 tx, this, localState, state);
196 protected final DOMStoreThreePhaseCommitCohort transactionReady(
197 final SnapshotBackedWriteTransaction<T> tx,
198 final DataTreeModification tree,
199 final Exception readyError) {
201 final State localState = state;
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",
208 allocated.setSnapshot(tree);
210 LOG.debug("Ignoring transaction {} readiness due to state {}", tx, localState);
213 return createCohort(tx, tree, readyError);
217 public final void close() {
218 final State localState = state;
221 Preconditions.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");
227 } while (!STATE_UPDATER.compareAndSet(this, localState, CLOSED));
231 * Notify the base logic that a previously-submitted transaction has been committed successfully.
233 * @param transaction Transaction which completed successfully.
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;
241 if (!(localState instanceof Allocated)) {
242 // This can legally happen if the chain is shut down before the transaction was committed
244 LOG.debug("Ignoring successful transaction {} in state {}", transaction, localState);
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);
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);
262 * Notify the base logic that a previously-submitted transaction has failed.
264 * @param transaction Transaction which failed.
265 * @param cause Failure cause
267 protected final void onTransactionFailed(final SnapshotBackedWriteTransaction<T> transaction,
268 final Throwable cause) {
269 LOG.debug("Transaction chain {} failed on transaction {}", this, transaction, cause);
274 * Return the next transaction identifier.
276 * @return transaction identifier.
278 protected abstract T nextTransactionIdentifier();
281 * Inquire as to whether transactions should record their allocation context.
283 * @return True if allocation context should be recorded.
285 protected abstract boolean getDebugTransactions();
288 * Take a fresh {@link DataTreeSnapshot} from the backend.
290 * @return A new snapshot.
292 protected abstract DataTreeSnapshot takeSnapshot();
295 * Create a cohort for driving the transaction through the commit process.
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.
302 protected abstract DOMStoreThreePhaseCommitCohort createCohort(SnapshotBackedWriteTransaction<T> transaction,
303 DataTreeModification modification,
304 Exception operationError);