Allow SnapshotBackedReadTransaction customization
[controller.git] / opendaylight / md-sal / sal-dom-spi / src / main / java / org / opendaylight / controller / sal / core / spi / data / AbstractSnapshotBackedTransactionChain.java
index b7776b2a397940745501a6c87ffcdd5625d10dcc..e0dc0dc8de3aad229d00ca10c0d0f0f4ca4bd995 100644 (file)
@@ -12,6 +12,7 @@ import com.google.common.base.Preconditions;
 import java.util.AbstractMap.SimpleEntry;
 import java.util.Map.Entry;
 import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
+import org.opendaylight.controller.sal.core.spi.data.SnapshotBackedReadTransaction.TransactionClosePrototype;
 import org.opendaylight.controller.sal.core.spi.data.SnapshotBackedWriteTransaction.TransactionReadyPrototype;
 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeModification;
 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeSnapshot;
@@ -19,20 +20,21 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 /**
- * Abstract implementation of the {@link DOMStoreTransactionChain} interface relying on {@link DataTreeSnapshot} supplier
- * and backend commit coordinator.
+ * Abstract implementation of the {@link DOMStoreTransactionChain} interface relying on {@link DataTreeSnapshot}
+ * supplier and backend commit coordinator.
  *
  * @param <T> transaction identifier type
  */
 @Beta
-public abstract class AbstractSnapshotBackedTransactionChain<T> extends TransactionReadyPrototype<T> implements DOMStoreTransactionChain {
-    private static abstract class State {
+public abstract class AbstractSnapshotBackedTransactionChain<T> extends TransactionReadyPrototype<T>
+        implements DOMStoreTransactionChain, TransactionClosePrototype<T> {
+    private abstract static class State {
         /**
          * Allocate a new snapshot.
          *
          * @return A new snapshot
          */
-        protected abstract DataTreeSnapshot getSnapshot();
+        protected abstract DataTreeSnapshot getSnapshot(Object transactionId);
     }
 
     private static final class Idle extends State {
@@ -43,7 +45,7 @@ public abstract class AbstractSnapshotBackedTransactionChain<T> extends Transact
         }
 
         @Override
-        protected DataTreeSnapshot getSnapshot() {
+        protected DataTreeSnapshot getSnapshot(Object transactionId) {
             return chain.takeSnapshot();
         }
     }
@@ -66,15 +68,18 @@ public abstract class AbstractSnapshotBackedTransactionChain<T> extends Transact
         }
 
         @Override
-        protected DataTreeSnapshot getSnapshot() {
+        protected DataTreeSnapshot getSnapshot(Object transactionId) {
             final DataTreeSnapshot ret = snapshot;
-            Preconditions.checkState(ret != null, "Previous transaction %s is not ready yet", transaction.getIdentifier());
+            Preconditions.checkState(ret != null,
+                    "Could not get snapshot for transaction %s - previous transaction %s is not ready yet",
+                    transactionId, transaction.getIdentifier());
             return ret;
         }
 
         void setSnapshot(final DataTreeSnapshot snapshot) {
             final boolean success = SNAPSHOT_UPDATER.compareAndSet(this, null, snapshot);
-            Preconditions.checkState(success, "Transaction %s has already been marked as ready", transaction.getIdentifier());
+            Preconditions.checkState(success, "Transaction %s has already been marked as ready",
+                    transaction.getIdentifier());
         }
     }
 
@@ -89,7 +94,7 @@ public abstract class AbstractSnapshotBackedTransactionChain<T> extends Transact
         }
 
         @Override
-        protected DataTreeSnapshot getSnapshot() {
+        protected DataTreeSnapshot getSnapshot(Object transactionId) {
             throw new IllegalStateException(message);
         }
     }
@@ -108,31 +113,45 @@ public abstract class AbstractSnapshotBackedTransactionChain<T> extends Transact
         state = idleState;
     }
 
-    private Entry<State, DataTreeSnapshot> getSnapshot() {
+    private Entry<State, DataTreeSnapshot> getSnapshot(T transactionId) {
         final State localState = state;
-        return new SimpleEntry<>(localState, localState.getSnapshot());
+        return new SimpleEntry<>(localState, localState.getSnapshot(transactionId));
     }
 
     private boolean recordTransaction(final State expected, final DOMStoreWriteTransaction transaction) {
-        final State state = new Allocated(transaction);
-        return STATE_UPDATER.compareAndSet(this, expected, state);
+        final State localState = new Allocated(transaction);
+        return STATE_UPDATER.compareAndSet(this, expected, localState);
     }
 
     @Override
     public final DOMStoreReadTransaction newReadOnlyTransaction() {
-        final Entry<State, DataTreeSnapshot> entry = getSnapshot();
-        return SnapshotBackedTransactions.newReadTransaction(nextTransactionIdentifier(), getDebugTransactions(), entry.getValue());
+        return newReadOnlyTransaction(nextTransactionIdentifier());
+    }
+
+    protected DOMStoreReadTransaction newReadOnlyTransaction(T transactionId) {
+        final Entry<State, DataTreeSnapshot> entry = getSnapshot(transactionId);
+        return SnapshotBackedTransactions.newReadTransaction(transactionId, getDebugTransactions(), entry.getValue(),
+            this);
+    }
+
+    @Override
+    public void transactionClosed(final SnapshotBackedReadTransaction<T> tx) {
+        // Defaults to no-op
     }
 
     @Override
     public final DOMStoreReadWriteTransaction newReadWriteTransaction() {
+        return newReadWriteTransaction(nextTransactionIdentifier());
+    }
+
+    protected DOMStoreReadWriteTransaction newReadWriteTransaction(T transactionId) {
         Entry<State, DataTreeSnapshot> entry;
         DOMStoreReadWriteTransaction ret;
 
         do {
-            entry = getSnapshot();
-            ret = new SnapshotBackedReadWriteTransaction<T>(nextTransactionIdentifier(),
-                getDebugTransactions(), entry.getValue(), this);
+            entry = getSnapshot(transactionId);
+            ret = new SnapshotBackedReadWriteTransaction<>(transactionId, getDebugTransactions(), entry.getValue(),
+                    this);
         } while (!recordTransaction(entry.getKey(), ret));
 
         return ret;
@@ -140,13 +159,16 @@ public abstract class AbstractSnapshotBackedTransactionChain<T> extends Transact
 
     @Override
     public final DOMStoreWriteTransaction newWriteOnlyTransaction() {
+        return newWriteOnlyTransaction(nextTransactionIdentifier());
+    }
+
+    protected DOMStoreWriteTransaction newWriteOnlyTransaction(T transactionId) {
         Entry<State, DataTreeSnapshot> entry;
         DOMStoreWriteTransaction ret;
 
         do {
-            entry = getSnapshot();
-            ret = new SnapshotBackedWriteTransaction<T>(nextTransactionIdentifier(),
-                getDebugTransactions(), entry.getValue(), this);
+            entry = getSnapshot(transactionId);
+            ret = new SnapshotBackedWriteTransaction<>(transactionId, getDebugTransactions(), entry.getValue(), this);
         } while (!recordTransaction(entry.getKey(), ret));
 
         return ret;
@@ -160,7 +182,8 @@ public abstract class AbstractSnapshotBackedTransactionChain<T> extends Transact
             if (allocated.getTransaction().equals(tx)) {
                 final boolean success = STATE_UPDATER.compareAndSet(this, localState, idleState);
                 if (!success) {
-                    LOG.warn("Transaction {} aborted, but chain {} state already transitioned from {} to {}, very strange",
+                    LOG.warn(
+                        "Transaction {} aborted, but chain {} state already transitioned from {} to {}, very strange",
                         tx, this, localState, state);
                 }
             }
@@ -168,19 +191,24 @@ public abstract class AbstractSnapshotBackedTransactionChain<T> extends Transact
     }
 
     @Override
-    protected final DOMStoreThreePhaseCommitCohort transactionReady(final SnapshotBackedWriteTransaction<T> tx, final DataTreeModification tree) {
+    protected final DOMStoreThreePhaseCommitCohort transactionReady(
+            final SnapshotBackedWriteTransaction<T> tx,
+            final DataTreeModification tree,
+            final Exception readyError) {
+
         final State localState = state;
 
         if (localState instanceof Allocated) {
             final Allocated allocated = (Allocated)localState;
             final DOMStoreWriteTransaction transaction = allocated.getTransaction();
-            Preconditions.checkState(tx.equals(transaction), "Mis-ordered ready transaction %s last allocated was %s", tx, transaction);
+            Preconditions.checkState(tx.equals(transaction), "Mis-ordered ready transaction %s last allocated was %s",
+                    tx, transaction);
             allocated.setSnapshot(tree);
         } else {
             LOG.debug("Ignoring transaction {} readiness due to state {}", tx, localState);
         }
 
-        return createCohort(tx, tree);
+        return createCohort(tx, tree, readyError);
     }
 
     @Override
@@ -188,7 +216,7 @@ public abstract class AbstractSnapshotBackedTransactionChain<T> extends Transact
         final State localState = state;
 
         do {
-            Preconditions.checkState(!CLOSED.equals(localState), "Transaction chain {} has been closed", this);
+            Preconditions.checkState(!CLOSED.equals(localState), "Transaction chain %s has been closed", this);
 
             if (FAILED.equals(localState)) {
                 LOG.debug("Ignoring user close in failed state");
@@ -223,7 +251,8 @@ public abstract class AbstractSnapshotBackedTransactionChain<T> extends Transact
         }
 
         if (!STATE_UPDATER.compareAndSet(this, localState, idleState)) {
-            LOG.debug("Transaction chain {} has already transitioned from {} to {}, not making it idle", this, localState, state);
+            LOG.debug("Transaction chain {} has already transitioned from {} to {}, not making it idle",
+                    this, localState, state);
         }
     }
 
@@ -233,7 +262,8 @@ public abstract class AbstractSnapshotBackedTransactionChain<T> extends Transact
      * @param transaction Transaction which failed.
      * @param cause Failure cause
      */
-    protected final void onTransactionFailed(final SnapshotBackedWriteTransaction<T> transaction, final Throwable cause) {
+    protected final void onTransactionFailed(final SnapshotBackedWriteTransaction<T> transaction,
+            final Throwable cause) {
         LOG.debug("Transaction chain {} failed on transaction {}", this, transaction, cause);
         state = FAILED;
     }
@@ -264,7 +294,10 @@ public abstract class AbstractSnapshotBackedTransactionChain<T> extends Transact
      *
      * @param transaction Transaction handle
      * @param modification {@link DataTreeModification} which needs to be applied to the backend
+     * @param operationError Any previous error that could be reported through three phase commit
      * @return A {@link DOMStoreThreePhaseCommitCohort} cohort.
      */
-    protected abstract DOMStoreThreePhaseCommitCohort createCohort(final SnapshotBackedWriteTransaction<T> transaction, final DataTreeModification modification);
+    protected abstract DOMStoreThreePhaseCommitCohort createCohort(SnapshotBackedWriteTransaction<T> transaction,
+                                                                   DataTreeModification modification,
+                                                                   Exception operationError);
 }