From 08fed252610910b34d5cd3110082d26f208dece3 Mon Sep 17 00:00:00 2001 From: Robert Varga Date: Mon, 1 Oct 2018 09:33:39 +0200 Subject: [PATCH] Remove AsyncDataBroker and related classes DOM and Binding APIs need to evolve separately, remove the common straightjacket they were using. Change-Id: I91f49284c61020fff4b4e262aa03c0a7eca70a06 Signed-off-by: Robert Varga --- .../mdsal/binding/api/DataBroker.java | 7 +- .../mdsal/binding/api/ReadTransaction.java | 4 +- .../binding/api/ReadWriteTransaction.java | 10 +- .../mdsal/binding/api/Transaction.java | 9 +- .../mdsal/binding/api/TransactionFactory.java | 85 ++- .../mdsal/binding/api/WriteTransaction.java | 400 +++++++++++++- .../adapter/AbstractForwardedTransaction.java | 28 +- .../mdsal/binding/javav2/api/DataBroker.java | 6 +- .../binding/javav2/api/ReadTransaction.java | 46 +- .../mdsal/binding/javav2/api/Transaction.java | 9 +- .../javav2/api/TransactionFactory.java | 70 ++- .../binding/javav2/api/WriteTransaction.java | 437 ++++++++++++++-- .../data/BindingDOMDataBrokerAdapter.java | 9 - .../BindingDOMTransactionChainAdapter.java | 9 - .../spi/AbstractForwardedTransaction.java | 26 +- .../javav2/spi/ForwardingDataBroker.java | 9 - .../mdsal/common/api/AsyncDataBroker.java | 64 --- .../api/AsyncDataTransactionFactory.java | 105 ---- .../common/api/AsyncReadTransaction.java | 62 --- .../common/api/AsyncReadWriteTransaction.java | 142 ----- .../mdsal/common/api/AsyncTransaction.java | 33 -- .../common/api/AsyncWriteTransaction.java | 469 ----------------- .../ForwardingAsyncReadWriteTransaction.java | 59 --- .../mdsal/dom/api/DOMDataBroker.java | 5 +- .../dom/api/DOMDataTreeReadTransaction.java | 5 +- .../api/DOMDataTreeReadWriteTransaction.java | 10 +- .../mdsal/dom/api/DOMDataTreeTransaction.java | 9 +- .../dom/api/DOMDataTreeWriteTransaction.java | 487 ++++++++++++++++-- .../mdsal/dom/api/DOMTransactionFactory.java | 47 +- ...tractDOMForwardedCompositeTransaction.java | 19 +- 30 files changed, 1484 insertions(+), 1196 deletions(-) delete mode 100644 common/mdsal-common-api/src/main/java/org/opendaylight/mdsal/common/api/AsyncDataBroker.java delete mode 100644 common/mdsal-common-api/src/main/java/org/opendaylight/mdsal/common/api/AsyncDataTransactionFactory.java delete mode 100644 common/mdsal-common-api/src/main/java/org/opendaylight/mdsal/common/api/AsyncReadTransaction.java delete mode 100644 common/mdsal-common-api/src/main/java/org/opendaylight/mdsal/common/api/AsyncReadWriteTransaction.java delete mode 100644 common/mdsal-common-api/src/main/java/org/opendaylight/mdsal/common/api/AsyncTransaction.java delete mode 100644 common/mdsal-common-api/src/main/java/org/opendaylight/mdsal/common/api/AsyncWriteTransaction.java delete mode 100644 common/mdsal-common-util/src/main/java/org/opendaylight/mdsal/common/util/ForwardingAsyncReadWriteTransaction.java diff --git a/binding/mdsal-binding-api/src/main/java/org/opendaylight/mdsal/binding/api/DataBroker.java b/binding/mdsal-binding-api/src/main/java/org/opendaylight/mdsal/binding/api/DataBroker.java index 5bcfbea7c8..16e2717123 100644 --- a/binding/mdsal-binding-api/src/main/java/org/opendaylight/mdsal/binding/api/DataBroker.java +++ b/binding/mdsal-binding-api/src/main/java/org/opendaylight/mdsal/binding/api/DataBroker.java @@ -7,10 +7,6 @@ */ package org.opendaylight.mdsal.binding.api; -import org.opendaylight.mdsal.common.api.AsyncDataBroker; -import org.opendaylight.yangtools.yang.binding.DataObject; -import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; - /** * Provides access to a conceptual data tree store and also provides the ability to * subscribe for changes to data under a given branch of the tree. @@ -42,8 +38,7 @@ import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; * Implementation Note: This interface is not intended to be implemented by users of MD-SAL, * but only to be consumed by them. */ -public interface DataBroker extends AsyncDataBroker, DataObject>, TransactionFactory, - BindingService, DataTreeChangeService { +public interface DataBroker extends BindingService, TransactionFactory, DataTreeChangeService { /** * Create a new transaction chain. The chain will be initialized to read from its backing datastore, with * no outstanding transaction. Listener will be registered to handle chain-level events. diff --git a/binding/mdsal-binding-api/src/main/java/org/opendaylight/mdsal/binding/api/ReadTransaction.java b/binding/mdsal-binding-api/src/main/java/org/opendaylight/mdsal/binding/api/ReadTransaction.java index 69fcba2ca0..35c97389cb 100644 --- a/binding/mdsal-binding-api/src/main/java/org/opendaylight/mdsal/binding/api/ReadTransaction.java +++ b/binding/mdsal-binding-api/src/main/java/org/opendaylight/mdsal/binding/api/ReadTransaction.java @@ -9,9 +9,9 @@ package org.opendaylight.mdsal.binding.api; import com.google.common.util.concurrent.FluentFuture; import java.util.Optional; -import org.opendaylight.mdsal.common.api.AsyncReadTransaction; import org.opendaylight.mdsal.common.api.LogicalDatastoreType; import org.opendaylight.mdsal.common.api.ReadFailedException; +import org.opendaylight.yangtools.concepts.Registration; import org.opendaylight.yangtools.yang.binding.DataObject; import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; @@ -50,7 +50,7 @@ import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; * java.util.concurrent.Executor)} or other functions from {@link com.google.common.util.concurrent.Futures} to register * more specific listeners. */ -public interface ReadTransaction extends Transaction, AsyncReadTransaction, DataObject> { +public interface ReadTransaction extends Transaction, Registration { /** * Reads data from the provided logical data store located at the provided path. * diff --git a/binding/mdsal-binding-api/src/main/java/org/opendaylight/mdsal/binding/api/ReadWriteTransaction.java b/binding/mdsal-binding-api/src/main/java/org/opendaylight/mdsal/binding/api/ReadWriteTransaction.java index 70299ec8a1..5ce5f4972a 100644 --- a/binding/mdsal-binding-api/src/main/java/org/opendaylight/mdsal/binding/api/ReadWriteTransaction.java +++ b/binding/mdsal-binding-api/src/main/java/org/opendaylight/mdsal/binding/api/ReadWriteTransaction.java @@ -8,17 +8,15 @@ package org.opendaylight.mdsal.binding.api; import com.google.common.annotations.Beta; -import org.opendaylight.mdsal.common.api.AsyncReadWriteTransaction; -import org.opendaylight.yangtools.yang.binding.DataObject; -import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; /** * A transaction that enables combined read/write capabilities. * *

- * For more information on usage and examples, please see the documentation in {@link AsyncReadWriteTransaction}. + * For more information on usage and examples, please see the documentation in {@link ReadTransaction} and + * {@link WriteTransaction} */ @Beta -public interface ReadWriteTransaction extends ReadTransaction, WriteTransaction, - AsyncReadWriteTransaction, DataObject> { +public interface ReadWriteTransaction extends ReadTransaction, WriteTransaction { + } diff --git a/binding/mdsal-binding-api/src/main/java/org/opendaylight/mdsal/binding/api/Transaction.java b/binding/mdsal-binding-api/src/main/java/org/opendaylight/mdsal/binding/api/Transaction.java index c5c1b42ae3..61e3a75d82 100644 --- a/binding/mdsal-binding-api/src/main/java/org/opendaylight/mdsal/binding/api/Transaction.java +++ b/binding/mdsal-binding-api/src/main/java/org/opendaylight/mdsal/binding/api/Transaction.java @@ -7,9 +7,7 @@ */ package org.opendaylight.mdsal.binding.api; -import org.opendaylight.mdsal.common.api.AsyncTransaction; -import org.opendaylight.yangtools.yang.binding.DataObject; -import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.opendaylight.yangtools.concepts.Identifiable; /** * A common parent for all transactions which operate on a conceptual data tree. @@ -21,7 +19,6 @@ import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; * * Implementation Note: This interface is not intended to be implemented by users of MD-SAL. */ -public interface Transaction extends AsyncTransaction, DataObject> { - @Override - Object getIdentifier(); +public interface Transaction extends Identifiable { + } diff --git a/binding/mdsal-binding-api/src/main/java/org/opendaylight/mdsal/binding/api/TransactionFactory.java b/binding/mdsal-binding-api/src/main/java/org/opendaylight/mdsal/binding/api/TransactionFactory.java index b41e7be9e6..f7314bbd26 100644 --- a/binding/mdsal-binding-api/src/main/java/org/opendaylight/mdsal/binding/api/TransactionFactory.java +++ b/binding/mdsal-binding-api/src/main/java/org/opendaylight/mdsal/binding/api/TransactionFactory.java @@ -7,17 +7,86 @@ */ package org.opendaylight.mdsal.binding.api; -import org.opendaylight.mdsal.common.api.AsyncDataTransactionFactory; -import org.opendaylight.yangtools.yang.binding.DataObject; -import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; - -public interface TransactionFactory extends AsyncDataTransactionFactory, DataObject> { - @Override +/** + * A factory which allocates new transactions to operate on the data tree. + * + *

+ * Note: This interface is not intended to be used directly, but rather via subinterfaces + * which introduces additional semantics to allocated transactions. + *

    + *
  • {@link DataBroker} + *
  • {@link TransactionChain} + *
+ * + *

+ * All operations on the data tree are performed via one of the transactions: + *

    + *
  • Read-Only - allocated using {@link #newReadOnlyTransaction()} + *
  • Write-Only - allocated using {@link #newWriteOnlyTransaction()} + *
+ * + *

+ * These transactions provides a stable isolated view of the data tree, which is guaranteed to be + * not affected by other concurrent transactions, until transaction is committed. + * + *

+ * For a detailed explanation of how transaction are isolated and how transaction-local changes are + * committed to global data tree, see {@link ReadTransaction}, {@link WriteTransaction} + * and {@link WriteTransaction#commit()}. + * + *

+ * It is strongly recommended to use the type of transaction, which provides only the minimal + * capabilities you need. This allows for optimizations at the data broker / data store level. For + * example, implementations may optimize the transaction for reading if they know ahead of time that + * you only need to read data - such as not keeping additional meta-data, which may be required for + * write transactions. + * + *

+ * Implementation Note: This interface is not intended to be implemented by users of MD-SAL, + * but only to be consumed by them. + * + * @see DataBroker +*/ +public interface TransactionFactory { + /** + * Allocates a new read-only transaction which provides an immutable snapshot of the data tree. The view of data + * tree is an immutable snapshot of current data tree state when transaction was allocated. + * + * @return A new read-only transaction + */ ReadTransaction newReadOnlyTransaction(); - @Override + /** + * Allocates new read-write transaction which provides a mutable view of the data tree. + * + *

+ * Preconditions for mutation of data tree are captured from the snapshot of data tree state, when the transaction + * is allocated. If data was changed during transaction in an incompatible way then the commit of this transaction + * will fail. See {@link ReadWriteTransaction#commit()} for more details about conflicting and non-conflicting + * changes and failure scenarios. + * + * @return new read-write transaction + */ ReadWriteTransaction newReadWriteTransaction(); - @Override + /** + * Allocates new write-only transaction based on latest state of data tree. + * + *

+ * Preconditions for mutation of data tree are captured from the snapshot of data tree state, when the transaction + * is allocated. If data was changed during transaction in an incompatible way then the commit of this transaction + * will fail. See {@link WriteTransaction#commit()} for more details about conflicting and not-conflicting changes + * and failure scenarios. + * + *

+ * Since this transaction does not provide a view of the data it SHOULD BE used only by callers who are exclusive + * writers (exporters of data) to the subtree they modify. This prevents optimistic lock failures as described in + * {@link WriteTransaction#commit()}. + * + *

+ * Exclusivity of writers to particular subtree SHOULD BE enforced by external locking mechanism. + * + * @return new write-only transaction + */ WriteTransaction newWriteOnlyTransaction(); } diff --git a/binding/mdsal-binding-api/src/main/java/org/opendaylight/mdsal/binding/api/WriteTransaction.java b/binding/mdsal-binding-api/src/main/java/org/opendaylight/mdsal/binding/api/WriteTransaction.java index c9c0d022f1..a94c989fd4 100644 --- a/binding/mdsal-binding-api/src/main/java/org/opendaylight/mdsal/binding/api/WriteTransaction.java +++ b/binding/mdsal-binding-api/src/main/java/org/opendaylight/mdsal/binding/api/WriteTransaction.java @@ -10,9 +10,11 @@ package org.opendaylight.mdsal.binding.api; import com.google.common.util.concurrent.FluentFuture; import javax.annotation.CheckReturnValue; import org.eclipse.jdt.annotation.NonNull; -import org.opendaylight.mdsal.common.api.AsyncWriteTransaction; import org.opendaylight.mdsal.common.api.CommitInfo; +import org.opendaylight.mdsal.common.api.DataValidationFailedException; import org.opendaylight.mdsal.common.api.LogicalDatastoreType; +import org.opendaylight.mdsal.common.api.OptimisticLockFailedException; +import org.opendaylight.mdsal.common.api.TransactionCommitFailedException; import org.opendaylight.yangtools.yang.binding.DataObject; import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; @@ -83,11 +85,366 @@ import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; * Implementation Note: This interface is not intended to be implemented by users of MD-SAL, but only to be * consumed by them. */ -public interface WriteTransaction extends Transaction, AsyncWriteTransaction, DataObject> { - @Override +public interface WriteTransaction extends Transaction { + /** + * Cancels the transaction. Transactions can only be cancelled if it was not yet committed. + * Invoking cancel() on failed or already canceled will have no effect, and transaction is considered cancelled. + * Invoking cancel() on finished transaction (future returned by {@link #commit()} already successfully completed) + * will always fail (return false). + * + * @return false if the task could not be cancelled, typically because it has already completed normally; + * true otherwise + */ boolean cancel(); - @Override + /** + * Commits this transaction to be asynchronously applied to update the logical data tree. The returned + * {@link FluentFuture} conveys the result of applying the data changes. + * + *

+ * This call logically seals the transaction, which prevents the client from further changing the data tree using + * this transaction. Any subsequent calls to put(LogicalDatastoreType, Path, Object), + * merge(LogicalDatastoreType, Path, Object), delete(LogicalDatastoreType, Path) will fail + * with {@link IllegalStateException}. The transaction is marked as committed and enqueued into the data store + * back-end for processing. + * + *

+ * Whether or not the commit is successful is determined by versioning of the data tree and validation of registered + * commit participants if the transaction changes the data tree. + * + *

+ * The effects of a successful commit of data depends on listeners and commit participants that are registered with + * the data broker. + * + *

Example usage:

+ *
+     *  private void doWrite(final int tries) {
+     *      WriteTransaction writeTx = dataBroker.newWriteOnlyTransaction();
+     *      MyDataObject data = ...;
+     *      InstanceIdentifier<MyDataObject> path = ...;
+     *      writeTx.put(LogicalDatastoreType.OPERATIONAL, path, data);
+     *      Futures.addCallback(writeTx.commit(), new FutureCallback<CommitInfo>() {
+     *          public void onSuccess(CommitInfo result) {
+     *              // succeeded
+     *          }
+     *          public void onFailure(Throwable t) {
+     *              if (t instanceof OptimisticLockFailedException) {
+     *                  if(( tries - 1) > 0 ) {
+     *                      // do retry
+     *                      doWrite(tries - 1);
+     *                  } else {
+     *                      // out of retries
+     *                  }
+     *              } else {
+     *                  // failed due to another type of TransactionCommitFailedException.
+     *              }
+     *          });
+     * }
+     * ...
+     * doWrite(2);
+     * 
+ * + *

Failure scenarios

+ * + *

+ * Transaction may fail because of multiple reasons, such as + *

    + *
  • + * Another transaction finished earlier and modified the same node in a non-compatible way (see below). In this + * case the returned future will fail with an {@link OptimisticLockFailedException}. It is the responsibility + * of the caller to create a new transaction and commit the same modification again in order to update data + * tree. + * + * Warning: In most cases, retrying after an OptimisticLockFailedException will result in a high + * probability of success. However, there are scenarios, albeit unusual, where any number of retries will + * not succeed. Therefore it is strongly recommended to limit the number of retries (2 or 3) to avoid + * an endless loop. + * + *
  • + *
  • Data change introduced by this transaction did not pass validation by commit handlers or data was + * incorrectly structured. Returned future will fail with a {@link DataValidationFailedException}. User + * should not retry to create new transaction with same data, since it probably will fail again. + *
  • + *
+ * + *

Change compatibility

+ * There are several sets of changes which could be considered incompatible between two transactions which are + * derived from same initial state. Rules for conflict detection applies recursively for each subtree level. + * + *

Change compatibility of leafs, leaf-list items

+ * Following table shows state changes and failures between two concurrent transactions, which are based on same + * initial state, Tx 1 completes successfully before Tx 2 is committed. + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Initial stateTx 1Tx 2Result
Emptyput(A,1)put(A,2)Tx 2 will fail, state is A=1
Emptyput(A,1)merge(A,2)A=2
Emptymerge(A,1)put(A,2)Tx 2 will fail, state is A=1
Emptymerge(A,1)merge(A,2)A=2
A=0put(A,1)put(A,2)Tx 2 will fail, A=1
A=0put(A,1)merge(A,2)A=2
A=0merge(A,1)put(A,2)Tx 2 will fail, A=1
A=0merge(A,1)merge(A,2)A=2
A=0delete(A)put(A,2)Tx 2 will fail, A does not exists
A=0delete(A)merge(A,2)A=2
+ * + *

Change compatibility of subtrees

+ * Following table shows state changes and failures between two concurrent transactions, which are based on same + * initial state, Tx 1 completes successfully before Tx 2 is committed. + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Initial stateTx 1Tx 2Result
Emptyput(TOP,[])put(TOP,[])Tx 2 will fail, state is TOP=[]
Emptyput(TOP,[])merge(TOP,[])TOP=[]
Emptyput(TOP,[FOO=1])put(TOP,[BAR=1])Tx 2 will fail, state is TOP=[FOO=1]
Emptyput(TOP,[FOO=1])merge(TOP,[BAR=1])TOP=[FOO=1,BAR=1]
Emptymerge(TOP,[FOO=1])put(TOP,[BAR=1])Tx 2 will fail, state is TOP=[FOO=1]
Emptymerge(TOP,[FOO=1])merge(TOP,[BAR=1])TOP=[FOO=1,BAR=1]
TOP=[]put(TOP,[FOO=1])put(TOP,[BAR=1])Tx 2 will fail, state is TOP=[FOO=1]
TOP=[]put(TOP,[FOO=1])merge(TOP,[BAR=1])state is TOP=[FOO=1,BAR=1]
TOP=[]merge(TOP,[FOO=1])put(TOP,[BAR=1])Tx 2 will fail, state is TOP=[FOO=1]
TOP=[]merge(TOP,[FOO=1])merge(TOP,[BAR=1])state is TOP=[FOO=1,BAR=1]
TOP=[]delete(TOP)put(TOP,[BAR=1])Tx 2 will fail, state is empty store
TOP=[]delete(TOP)merge(TOP,[BAR=1])state is TOP=[BAR=1]
TOP=[]put(TOP/FOO,1)put(TOP/BAR,1])state is TOP=[FOO=1,BAR=1]
TOP=[]put(TOP/FOO,1)merge(TOP/BAR,1)state is TOP=[FOO=1,BAR=1]
TOP=[]merge(TOP/FOO,1)put(TOP/BAR,1)state is TOP=[FOO=1,BAR=1]
TOP=[]merge(TOP/FOO,1)merge(TOP/BAR,1)state is TOP=[FOO=1,BAR=1]
TOP=[]delete(TOP)put(TOP/BAR,1)Tx 2 will fail, state is empty store
TOP=[]delete(TOP)merge(TOP/BAR,1]Tx 2 will fail, state is empty store
TOP=[FOO=1]put(TOP/FOO,2)put(TOP/BAR,1)state is TOP=[FOO=2,BAR=1]
TOP=[FOO=1]put(TOP/FOO,2)merge(TOP/BAR,1)state is TOP=[FOO=2,BAR=1]
TOP=[FOO=1]merge(TOP/FOO,2)put(TOP/BAR,1)state is TOP=[FOO=2,BAR=1]
TOP=[FOO=1]merge(TOP/FOO,2)merge(TOP/BAR,1)state is TOP=[FOO=2,BAR=1]
TOP=[FOO=1]delete(TOP/FOO)put(TOP/BAR,1)state is TOP=[BAR=1]
TOP=[FOO=1]delete(TOP/FOO)merge(TOP/BAR,1]state is TOP=[BAR=1]
+ * + * + *

Examples of failure scenarios

+ * + *

Conflict of two transactions

+ * This example illustrates two concurrent transactions, which derived from same initial state + * of data tree and proposes conflicting modifications. + * + *
+     * txA = broker.newWriteTransaction(); // allocates new transaction, data tree is empty
+     * txB = broker.newWriteTransaction(); // allocates new transaction, data tree is empty
+     * txA.put(CONFIGURATION, PATH, A);    // writes to PATH value A
+     * txB.put(CONFIGURATION, PATH, B)     // writes to PATH value B
+     * ListenableFuture futureA = txA.commit(); // transaction A is sealed and committed
+     * ListenebleFuture futureB = txB.commit(); // transaction B is sealed and committed
+     * 
+ * Commit of transaction A will be processed asynchronously and data tree will be updated to + * contain value A for PATH. Returned {@link FluentFuture} will + * successfully complete once state is applied to data tree. + * Commit of Transaction B will fail, because previous transaction also modified path in a + * concurrent way. The state introduced by transaction B will not be applied. Returned + * {@link FluentFuture} object will fail with {@link OptimisticLockFailedException} + * exception, which indicates to client that concurrent transaction prevented the committed + * transaction from being applied.
+ * + *

+ * A successful commit produces implementation-specific {@link CommitInfo} structure, which is used to communicate + * post-condition information to the caller. Such information can contain commit-id, timing information or any + * other information the implementation wishes to share. + * + * @return a FluentFuture containing the result of the commit information. The Future blocks until the commit + * operation is complete. A successful commit returns nothing. On failure, the Future will fail with a + * {@link TransactionCommitFailedException} or an exception derived from TransactionCommitFailedException. + * @throws IllegalStateException if the transaction is already committed or was canceled. + */ @CheckReturnValue @NonNull FluentFuture commit(); @@ -101,8 +458,6 @@ public interface WriteTransaction extends Transaction, AsyncWriteTransactioncreateMissingParents set to false. * *

- * For more information on usage and examples, please see the documentation in {@link AsyncWriteTransaction}. - *

* If you need to make sure that a parent object exists but you do not want modify * its pre-existing state by using put, consider using {@link #merge} instead. * @@ -115,23 +470,16 @@ public interface WriteTransaction extends Transaction, AsyncWriteTransaction void put(LogicalDatastoreType store, InstanceIdentifier path, T data); /** - * Stores a piece of data at the specified path. This acts as an add / - * replace operation, which is to say that whole subtree will be replaced by - * the specified data. - * - *

- * For more information on usage and examples, please see the documentation - * in {@link AsyncWriteTransaction}. + * Stores a piece of data at the specified path. This acts as an add / replace operation, which is to say that whole + * subtree will be replaced by the specified data. * *

- * If you need to make sure that a parent object exists but you do not want - * modify its pre-existing state by using put, consider using {@link #merge} - * instead. + * If you need to make sure that a parent object exists but you do not want modify its pre-existing state by using + * put, consider using {@link #merge} instead. * *

- * Note: Using createMissingParents with value true, may - * introduce garbage in data store, or recreate nodes, which were deleted by - * previous transaction. + * Note: Using createMissingParents with value true, may introduce garbage in data store, or recreate + * nodes, which were deleted by previous transaction. * * @param store the logical data store which should be modified * @param path the data object path @@ -154,9 +502,6 @@ public interface WriteTransaction extends Transaction, AsyncWriteTransactioncreateMissingParents set to false. * *

- * For more information on usage and examples, please see the documentation in {@link AsyncWriteTransaction}. - * - *

* If you require an explicit replace operation, use {@link #put} instead. * @param store the logical data store which should be modified * @param path the data object path @@ -171,9 +516,6 @@ public interface WriteTransaction extends Transaction, AsyncWriteTransaction - * For more information on usage and examples, please see the documentation in {@link AsyncWriteTransaction}. - * - *

* If you require an explicit replace operation, use {@link #put} instead. * * @param store the logical data store which should be modified @@ -187,7 +529,13 @@ public interface WriteTransaction extends Transaction, AsyncWriteTransaction void merge(LogicalDatastoreType store, InstanceIdentifier path, T data, boolean createMissingParents); - @Override + /** + * Removes a piece of data from specified path. This operation does not fail if the specified path does not exist. + * + * @param store Logical data store which should be modified + * @param path Data object path + * @throws IllegalStateException if the transaction was committed or canceled. + */ void delete(LogicalDatastoreType store, InstanceIdentifier path); /** diff --git a/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/AbstractForwardedTransaction.java b/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/AbstractForwardedTransaction.java index cf2b24e233..276521ca8c 100644 --- a/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/AbstractForwardedTransaction.java +++ b/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/AbstractForwardedTransaction.java @@ -7,46 +7,46 @@ */ package org.opendaylight.mdsal.binding.dom.adapter; -import com.google.common.base.Preconditions; +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkState; +import static java.util.Objects.requireNonNull; + import com.google.common.util.concurrent.FluentFuture; import com.google.common.util.concurrent.MoreExecutors; import java.util.Optional; -import org.opendaylight.mdsal.common.api.AsyncTransaction; import org.opendaylight.mdsal.common.api.LogicalDatastoreType; import org.opendaylight.mdsal.dom.api.DOMDataTreeReadTransaction; +import org.opendaylight.mdsal.dom.api.DOMDataTreeTransaction; import org.opendaylight.yangtools.concepts.Delegator; import org.opendaylight.yangtools.concepts.Identifiable; import org.opendaylight.yangtools.yang.binding.DataObject; import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; -import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; -import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; -abstract class AbstractForwardedTransaction>> - implements Delegator, Identifiable { +abstract class AbstractForwardedTransaction implements Delegator, + Identifiable { private final T delegate; private final BindingToNormalizedNodeCodec codec; AbstractForwardedTransaction(final T delegateTx, final BindingToNormalizedNodeCodec codec) { - this.delegate = Preconditions.checkNotNull(delegateTx, "Delegate must not be null"); - this.codec = Preconditions.checkNotNull(codec, "Codec must not be null"); + this.delegate = requireNonNull(delegateTx, "Delegate must not be null"); + this.codec = requireNonNull(codec, "Codec must not be null"); } @Override - public final Object getIdentifier() { + public final Object getIdentifier() { return delegate.getIdentifier(); } @Override - public final T getDelegate() { + public final T getDelegate() { return delegate; } @SuppressWarnings("unchecked") - protected final >> S getDelegateChecked( - final Class txType) { - Preconditions.checkState(txType.isInstance(delegate)); + protected final S getDelegateChecked(final Class txType) { + checkState(txType.isInstance(delegate)); return (S) delegate; } @@ -57,7 +57,7 @@ abstract class AbstractForwardedTransaction FluentFuture> doRead( final DOMDataTreeReadTransaction readTx, final LogicalDatastoreType store, final InstanceIdentifier path) { - Preconditions.checkArgument(!path.isWildcarded(), "Invalid read of wildcarded path %s", path); + checkArgument(!path.isWildcarded(), "Invalid read of wildcarded path %s", path); return readTx.read(store, codec.toYangInstanceIdentifierBlocking(path)) .transform(codec.getCodecRegistry().deserializeFunction(path)::apply, MoreExecutors.directExecutor()); diff --git a/binding2/mdsal-binding2-api/src/main/java/org/opendaylight/mdsal/binding/javav2/api/DataBroker.java b/binding2/mdsal-binding2-api/src/main/java/org/opendaylight/mdsal/binding/javav2/api/DataBroker.java index 713d130991..536de55eca 100644 --- a/binding2/mdsal-binding2-api/src/main/java/org/opendaylight/mdsal/binding/javav2/api/DataBroker.java +++ b/binding2/mdsal-binding2-api/src/main/java/org/opendaylight/mdsal/binding/javav2/api/DataBroker.java @@ -8,9 +8,6 @@ package org.opendaylight.mdsal.binding.javav2.api; import com.google.common.annotations.Beta; -import org.opendaylight.mdsal.binding.javav2.spec.base.InstanceIdentifier; -import org.opendaylight.mdsal.binding.javav2.spec.base.TreeNode; -import org.opendaylight.mdsal.common.api.AsyncDataBroker; /** * Provides access to a conceptual data tree store and also provides the ability to @@ -44,8 +41,7 @@ import org.opendaylight.mdsal.common.api.AsyncDataBroker; * but only to be consumed by them. */ @Beta -public interface DataBroker extends AsyncDataBroker, TreeNode>, BindingService, - TransactionFactory, DataTreeService { +public interface DataBroker extends BindingService, TransactionFactory, DataTreeService { /** * Create a new transaction chain. The chain will be initialized to read from its backing datastore, with * no outstanding transaction. Listener will be registered to handle chain-level events. diff --git a/binding2/mdsal-binding2-api/src/main/java/org/opendaylight/mdsal/binding/javav2/api/ReadTransaction.java b/binding2/mdsal-binding2-api/src/main/java/org/opendaylight/mdsal/binding/javav2/api/ReadTransaction.java index 34c786c7ac..21dc6850e9 100644 --- a/binding2/mdsal-binding2-api/src/main/java/org/opendaylight/mdsal/binding/javav2/api/ReadTransaction.java +++ b/binding2/mdsal-binding2-api/src/main/java/org/opendaylight/mdsal/binding/javav2/api/ReadTransaction.java @@ -5,27 +5,53 @@ * terms of the Eclipse Public License v1.0 which accompanies this distribution, * and is available at http://www.eclipse.org/legal/epl-v10.html */ - package org.opendaylight.mdsal.binding.javav2.api; import com.google.common.annotations.Beta; import java.util.function.BiConsumer; import org.opendaylight.mdsal.binding.javav2.spec.base.InstanceIdentifier; import org.opendaylight.mdsal.binding.javav2.spec.base.TreeNode; -import org.opendaylight.mdsal.common.api.AsyncReadTransaction; import org.opendaylight.mdsal.common.api.LogicalDatastoreType; import org.opendaylight.mdsal.common.api.ReadFailedException; +import org.opendaylight.yangtools.concepts.Registration; /** - * A transaction that provides a stateful read-only view of the data tree. + * A transaction that provides read access to a logical data store. + * + *

+ * View of the data tree is a stable point-in-time snapshot of the current data tree state when the + * transaction was created. It's state and underlying data tree is not affected by other + * concurrently running transactions. * *

- * For more information on usage and examples, please see the documentation in - * {@link org.opendaylight.mdsal.common.api.AsyncReadTransaction}. + * Implementation Note: This interface is not intended to be implemented by users of MD-SAL, + * but only to be consumed by them. + * + *

Transaction isolation example

+ * Lets assume initial state of data tree for PATH is A. + * + * + * txRead = broker.newReadOnlyTransaction(); // read Transaction is snapshot of data + * txWrite = broker.newReadWriteTransactoin(); // concurrent write transaction + * txRead.read(OPERATIONAL, PATH).get(); // will return Optional containing A + * txWrite = broker.put(OPERATIONAL, PATH, B); // writes B to PATH + * txRead.read(OPERATIONAL, PATH).get(); // still returns Optional containing A + * txWrite.submit().get(); // data tree is updated, PATH contains B + * txRead.read(OPERATIONAL, PATH).get(); // still returns Optional containing A + * txAfterCommit = broker.newReadOnlyTransaction(); // read Transaction is snapshot of new state + * txAfterCommit.read(OPERATIONAL, PATH).get(); // returns Optional containing B; + * + * + *

+ * Note: example contains blocking calls on future only to illustrate that action happened after other + * asynchronous action. Use of blocking call {@link com.google.common.util.concurrent.FluentFuture#get()} is + * discouraged for most uses and you should use + * {@link com.google.common.util.concurrent.FluentFuture#addCallback(com.google.common.util.concurrent.FutureCallback, + * java.util.concurrent.Executor)} or other functions from {@link com.google.common.util.concurrent.Futures} to register + * more specific listeners. */ @Beta -public interface ReadTransaction extends Transaction, AsyncReadTransaction, TreeNode> { - +public interface ReadTransaction extends Transaction, Registration { /** * Reads data from the provided logical data store located at the provided path. * @@ -43,4 +69,10 @@ public interface ReadTransaction extends Transaction, AsyncReadTransaction void read(LogicalDatastoreType store, InstanceIdentifier path, BiConsumer callback); + + /** + * Closes this transaction and releases all resources associated with it. + */ + @Override + void close(); } diff --git a/binding2/mdsal-binding2-api/src/main/java/org/opendaylight/mdsal/binding/javav2/api/Transaction.java b/binding2/mdsal-binding2-api/src/main/java/org/opendaylight/mdsal/binding/javav2/api/Transaction.java index 99b2000cd3..0cbd97581a 100644 --- a/binding2/mdsal-binding2-api/src/main/java/org/opendaylight/mdsal/binding/javav2/api/Transaction.java +++ b/binding2/mdsal-binding2-api/src/main/java/org/opendaylight/mdsal/binding/javav2/api/Transaction.java @@ -7,9 +7,7 @@ */ package org.opendaylight.mdsal.binding.javav2.api; -import org.opendaylight.mdsal.binding.javav2.spec.base.InstanceIdentifier; -import org.opendaylight.mdsal.binding.javav2.spec.base.TreeNode; -import org.opendaylight.mdsal.common.api.AsyncTransaction; +import org.opendaylight.yangtools.concepts.Identifiable; /** * A common parent for all transactions which operate on a conceptual data tree. @@ -21,7 +19,6 @@ import org.opendaylight.mdsal.common.api.AsyncTransaction; * * Implementation Note: This interface is not intended to be implemented by users of MD-SAL. */ -public interface Transaction extends AsyncTransaction, TreeNode> { - @Override - Object getIdentifier(); +public interface Transaction extends Identifiable { + } diff --git a/binding2/mdsal-binding2-api/src/main/java/org/opendaylight/mdsal/binding/javav2/api/TransactionFactory.java b/binding2/mdsal-binding2-api/src/main/java/org/opendaylight/mdsal/binding/javav2/api/TransactionFactory.java index aa9f5708d2..32eaa393d4 100644 --- a/binding2/mdsal-binding2-api/src/main/java/org/opendaylight/mdsal/binding/javav2/api/TransactionFactory.java +++ b/binding2/mdsal-binding2-api/src/main/java/org/opendaylight/mdsal/binding/javav2/api/TransactionFactory.java @@ -8,23 +8,75 @@ package org.opendaylight.mdsal.binding.javav2.api; import com.google.common.annotations.Beta; -import org.opendaylight.mdsal.binding.javav2.spec.base.InstanceIdentifier; -import org.opendaylight.mdsal.binding.javav2.spec.base.TreeNode; -import org.opendaylight.mdsal.common.api.AsyncDataTransactionFactory; /** * A factory which allocates new transactions to operate on the data tree. * *

- * For more information on usage, please see the documentation in {@link AsyncDataTransactionFactory}. + * Note: This interface is not intended to be used directly, but rather via subinterfaces + * which introduces additional semantics to allocated transactions. + *

    + *
  • {@link DataBroker} + *
  • {@link TransactionChain} + *
* - * @see AsyncDataTransactionFactory - */ + *

+ * All operations on the data tree are performed via one of the transactions: + *

    + *
  • Read-Only - allocated using {@link #newReadOnlyTransaction()} + *
  • Write-Only - allocated using {@link #newWriteOnlyTransaction()} + *
+ * + *

+ * These transactions provides a stable isolated view of the data tree, which is guaranteed to be + * not affected by other concurrent transactions, until transaction is committed. + * + *

+ * For a detailed explanation of how transaction are isolated and how transaction-local changes are + * committed to global data tree, see {@link ReadTransaction}, {@link WriteTransaction} + * and {@link WriteTransaction#commit()}. + * + *

+ * It is strongly recommended to use the type of transaction, which provides only the minimal + * capabilities you need. This allows for optimizations at the data broker / data store level. For + * example, implementations may optimize the transaction for reading if they know ahead of time that + * you only need to read data - such as not keeping additional meta-data, which may be required for + * write transactions. + * + *

+ * Implementation Note: This interface is not intended to be implemented by users of MD-SAL, + * but only to be consumed by them. + * + * @see DataBroker +*/ @Beta -public interface TransactionFactory extends AsyncDataTransactionFactory, TreeNode> { - @Override +public interface TransactionFactory { + /** + * Allocates a new read-only transaction which provides an immutable snapshot of the data tree. The view of data + * tree is an immutable snapshot of current data tree state when transaction was allocated. + * + * @return A new read-only transaction + */ ReadTransaction newReadOnlyTransaction(); - @Override + /** + * Allocates new write-only transaction based on latest state of data tree. + * + *

+ * Preconditions for mutation of data tree are captured from the snapshot of data tree state, when the transaction + * is allocated. If data was changed during transaction in an incompatible way then the commit of this transaction + * will fail. See {@link WriteTransaction#commit()} for more details about conflicting and not-conflicting changes + * and failure scenarios. + * + *

+ * Since this transaction does not provide a view of the data it SHOULD BE used only by callers who are exclusive + * writers (exporters of data) to the subtree they modify. This prevents optimistic lock failures as described in + * {@link WriteTransaction#commit()}. + * + *

+ * Exclusivity of writers to particular subtree SHOULD BE enforced by external locking mechanism. + * + * @return new write-only transaction + */ WriteTransaction newWriteOnlyTransaction(); } diff --git a/binding2/mdsal-binding2-api/src/main/java/org/opendaylight/mdsal/binding/javav2/api/WriteTransaction.java b/binding2/mdsal-binding2-api/src/main/java/org/opendaylight/mdsal/binding/javav2/api/WriteTransaction.java index 10e582ad3d..cad3ab22c1 100644 --- a/binding2/mdsal-binding2-api/src/main/java/org/opendaylight/mdsal/binding/javav2/api/WriteTransaction.java +++ b/binding2/mdsal-binding2-api/src/main/java/org/opendaylight/mdsal/binding/javav2/api/WriteTransaction.java @@ -13,9 +13,11 @@ import javax.annotation.CheckReturnValue; import org.eclipse.jdt.annotation.NonNull; import org.opendaylight.mdsal.binding.javav2.spec.base.InstanceIdentifier; import org.opendaylight.mdsal.binding.javav2.spec.base.TreeNode; -import org.opendaylight.mdsal.common.api.AsyncWriteTransaction; import org.opendaylight.mdsal.common.api.CommitInfo; +import org.opendaylight.mdsal.common.api.DataValidationFailedException; import org.opendaylight.mdsal.common.api.LogicalDatastoreType; +import org.opendaylight.mdsal.common.api.OptimisticLockFailedException; +import org.opendaylight.mdsal.common.api.TransactionCommitFailedException; /** * A transaction that provides mutation capabilities on a data tree. @@ -85,11 +87,366 @@ import org.opendaylight.mdsal.common.api.LogicalDatastoreType; * consumed by them. */ @Beta -public interface WriteTransaction extends AsyncWriteTransaction, TreeNode>, Transaction { - @Override +public interface WriteTransaction extends Transaction { + /** + * Cancels the transaction. Transactions can only be cancelled if it was not yet committed. + * Invoking cancel() on failed or already canceled will have no effect, and transaction is considered cancelled. + * Invoking cancel() on finished transaction (future returned by {@link #commit()} already successfully completed) + * will always fail (return false). + * + * @return false if the task could not be cancelled, typically because it has already completed normally; + * true otherwise + */ boolean cancel(); - @Override + /** + * Commits this transaction to be asynchronously applied to update the logical data tree. The returned + * {@link FluentFuture} conveys the result of applying the data changes. + * + *

+ * This call logically seals the transaction, which prevents the client from further changing the data tree using + * this transaction. Any subsequent calls to put(LogicalDatastoreType, Path, Object), + * merge(LogicalDatastoreType, Path, Object), delete(LogicalDatastoreType, Path) will fail + * with {@link IllegalStateException}. The transaction is marked as committed and enqueued into the data store + * back-end for processing. + * + *

+ * Whether or not the commit is successful is determined by versioning of the data tree and validation of registered + * commit participants if the transaction changes the data tree. + * + *

+ * The effects of a successful commit of data depends on listeners and commit participants that are registered with + * the data broker. + * + *

Example usage:

+ *
+     *  private void doWrite(final int tries) {
+     *      WriteTransaction writeTx = dataBroker.newWriteOnlyTransaction();
+     *      MyDataObject data = ...;
+     *      InstanceIdentifier<MyDataObject> path = ...;
+     *      writeTx.put(LogicalDatastoreType.OPERATIONAL, path, data);
+     *      Futures.addCallback(writeTx.commit(), new FutureCallback<CommitInfo>() {
+     *          public void onSuccess(CommitInfo result) {
+     *              // succeeded
+     *          }
+     *          public void onFailure(Throwable t) {
+     *              if (t instanceof OptimisticLockFailedException) {
+     *                  if(( tries - 1) > 0 ) {
+     *                      // do retry
+     *                      doWrite(tries - 1);
+     *                  } else {
+     *                      // out of retries
+     *                  }
+     *              } else {
+     *                  // failed due to another type of TransactionCommitFailedException.
+     *              }
+     *          });
+     * }
+     * ...
+     * doWrite(2);
+     * 
+ * + *

Failure scenarios

+ * + *

+ * Transaction may fail because of multiple reasons, such as + *

    + *
  • + * Another transaction finished earlier and modified the same node in a non-compatible way (see below). In this + * case the returned future will fail with an {@link OptimisticLockFailedException}. It is the responsibility + * of the caller to create a new transaction and commit the same modification again in order to update data + * tree. + * + * Warning: In most cases, retrying after an OptimisticLockFailedException will result in a high + * probability of success. However, there are scenarios, albeit unusual, where any number of retries will + * not succeed. Therefore it is strongly recommended to limit the number of retries (2 or 3) to avoid + * an endless loop. + * + *
  • + *
  • Data change introduced by this transaction did not pass validation by commit handlers or data was + * incorrectly structured. Returned future will fail with a {@link DataValidationFailedException}. User + * should not retry to create new transaction with same data, since it probably will fail again. + *
  • + *
+ * + *

Change compatibility

+ * There are several sets of changes which could be considered incompatible between two transactions which are + * derived from same initial state. Rules for conflict detection applies recursively for each subtree level. + * + *

Change compatibility of leafs, leaf-list items

+ * Following table shows state changes and failures between two concurrent transactions, which are based on same + * initial state, Tx 1 completes successfully before Tx 2 is committed. + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Initial stateTx 1Tx 2Result
Emptyput(A,1)put(A,2)Tx 2 will fail, state is A=1
Emptyput(A,1)merge(A,2)A=2
Emptymerge(A,1)put(A,2)Tx 2 will fail, state is A=1
Emptymerge(A,1)merge(A,2)A=2
A=0put(A,1)put(A,2)Tx 2 will fail, A=1
A=0put(A,1)merge(A,2)A=2
A=0merge(A,1)put(A,2)Tx 2 will fail, A=1
A=0merge(A,1)merge(A,2)A=2
A=0delete(A)put(A,2)Tx 2 will fail, A does not exists
A=0delete(A)merge(A,2)A=2
+ * + *

Change compatibility of subtrees

+ * Following table shows state changes and failures between two concurrent transactions, which are based on same + * initial state, Tx 1 completes successfully before Tx 2 is committed. + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Initial stateTx 1Tx 2Result
Emptyput(TOP,[])put(TOP,[])Tx 2 will fail, state is TOP=[]
Emptyput(TOP,[])merge(TOP,[])TOP=[]
Emptyput(TOP,[FOO=1])put(TOP,[BAR=1])Tx 2 will fail, state is TOP=[FOO=1]
Emptyput(TOP,[FOO=1])merge(TOP,[BAR=1])TOP=[FOO=1,BAR=1]
Emptymerge(TOP,[FOO=1])put(TOP,[BAR=1])Tx 2 will fail, state is TOP=[FOO=1]
Emptymerge(TOP,[FOO=1])merge(TOP,[BAR=1])TOP=[FOO=1,BAR=1]
TOP=[]put(TOP,[FOO=1])put(TOP,[BAR=1])Tx 2 will fail, state is TOP=[FOO=1]
TOP=[]put(TOP,[FOO=1])merge(TOP,[BAR=1])state is TOP=[FOO=1,BAR=1]
TOP=[]merge(TOP,[FOO=1])put(TOP,[BAR=1])Tx 2 will fail, state is TOP=[FOO=1]
TOP=[]merge(TOP,[FOO=1])merge(TOP,[BAR=1])state is TOP=[FOO=1,BAR=1]
TOP=[]delete(TOP)put(TOP,[BAR=1])Tx 2 will fail, state is empty store
TOP=[]delete(TOP)merge(TOP,[BAR=1])state is TOP=[BAR=1]
TOP=[]put(TOP/FOO,1)put(TOP/BAR,1])state is TOP=[FOO=1,BAR=1]
TOP=[]put(TOP/FOO,1)merge(TOP/BAR,1)state is TOP=[FOO=1,BAR=1]
TOP=[]merge(TOP/FOO,1)put(TOP/BAR,1)state is TOP=[FOO=1,BAR=1]
TOP=[]merge(TOP/FOO,1)merge(TOP/BAR,1)state is TOP=[FOO=1,BAR=1]
TOP=[]delete(TOP)put(TOP/BAR,1)Tx 2 will fail, state is empty store
TOP=[]delete(TOP)merge(TOP/BAR,1]Tx 2 will fail, state is empty store
TOP=[FOO=1]put(TOP/FOO,2)put(TOP/BAR,1)state is TOP=[FOO=2,BAR=1]
TOP=[FOO=1]put(TOP/FOO,2)merge(TOP/BAR,1)state is TOP=[FOO=2,BAR=1]
TOP=[FOO=1]merge(TOP/FOO,2)put(TOP/BAR,1)state is TOP=[FOO=2,BAR=1]
TOP=[FOO=1]merge(TOP/FOO,2)merge(TOP/BAR,1)state is TOP=[FOO=2,BAR=1]
TOP=[FOO=1]delete(TOP/FOO)put(TOP/BAR,1)state is TOP=[BAR=1]
TOP=[FOO=1]delete(TOP/FOO)merge(TOP/BAR,1]state is TOP=[BAR=1]
+ * + * + *

Examples of failure scenarios

+ * + *

Conflict of two transactions

+ * This example illustrates two concurrent transactions, which derived from same initial state + * of data tree and proposes conflicting modifications. + * + *
+     * txA = broker.newWriteTransaction(); // allocates new transaction, data tree is empty
+     * txB = broker.newWriteTransaction(); // allocates new transaction, data tree is empty
+     * txA.put(CONFIGURATION, PATH, A);    // writes to PATH value A
+     * txB.put(CONFIGURATION, PATH, B)     // writes to PATH value B
+     * ListenableFuture futureA = txA.commit(); // transaction A is sealed and committed
+     * ListenebleFuture futureB = txB.commit(); // transaction B is sealed and committed
+     * 
+ * Commit of transaction A will be processed asynchronously and data tree will be updated to + * contain value A for PATH. Returned {@link FluentFuture} will + * successfully complete once state is applied to data tree. + * Commit of Transaction B will fail, because previous transaction also modified path in a + * concurrent way. The state introduced by transaction B will not be applied. Returned + * {@link FluentFuture} object will fail with {@link OptimisticLockFailedException} + * exception, which indicates to client that concurrent transaction prevented the committed + * transaction from being applied.
+ * + *

+ * A successful commit produces implementation-specific {@link CommitInfo} structure, which is used to communicate + * post-condition information to the caller. Such information can contain commit-id, timing information or any + * other information the implementation wishes to share. + * + * @return a FluentFuture containing the result of the commit information. The Future blocks until the commit + * operation is complete. A successful commit returns nothing. On failure, the Future will fail with a + * {@link TransactionCommitFailedException} or an exception derived from TransactionCommitFailedException. + * @throws IllegalStateException if the transaction is already committed or was canceled. + */ @CheckReturnValue @NonNull FluentFuture commit(); @@ -103,8 +460,6 @@ public interface WriteTransaction extends AsyncWriteTransactioncreateMissingParents set to false. * *

- * For more information on usage and examples, please see the documentation in {@link AsyncWriteTransaction}. - *

* If you need to make sure that a parent object exists but you do not want modify * its pre-existing state by using put, consider using {@link #merge} instead. * @@ -121,18 +476,12 @@ public interface WriteTransaction extends AsyncWriteTransaction void put(LogicalDatastoreType store, InstanceIdentifier path, T data); /** - * Stores a piece of data at the specified path. This acts as an add / - * replace operation, which is to say that whole subtree will be replaced by - * the specified data. - * - *

- * For more information on usage and examples, please see the documentation - * in {@link AsyncWriteTransaction}. + * Stores a piece of data at the specified path. This acts as an add / replace operation, which is to say that whole + * subtree will be replaced by the specified data. * *

- * If you need to make sure that a parent object exists but you do not want - * modify its pre-existing state by using put, consider using {@link #merge} - * instead. + * If you need to make sure that a parent object exists but you do not want modify its pre-existing state by using + * put, consider using {@link #merge} instead. * *

* Note: Using createMissingParents with value true, may @@ -156,9 +505,8 @@ public interface WriteTransaction extends AsyncWriteTransaction * This method does not automatically create missing parent nodes. It is equivalent to invoking @@ -166,51 +514,42 @@ public interface WriteTransaction extends AsyncWriteTransactioncreateMissingParents set to false. * *

- * For more information on usage and examples, please see the documentation in - * {@link AsyncWriteTransaction}. - * - *

* If you require an explicit replace operation, use {@link #put} instead. - * @param store - * the logical data store which should be modified - * @param path - * the data object path - * @param data - * the data object to be merged to the specified path + * @param store the logical data store which should be modified + * @param path the data object path + * @param data the data object to be merged to the specified path * @param data tree type - * @throws IllegalStateException - * if the transaction has already been submitted + * @throws IllegalStateException if the transaction has already been submitted */ void merge(LogicalDatastoreType store, InstanceIdentifier path, T data); /** - * Merges a piece of data with the existing data at a specified path. Any - * pre-existing data which is not explicitly overwritten will be preserved. - * This means that if you store a container, its child lists will be merged. - * - *

- * For more information on usage and examples, please see the documentation - * in {@link AsyncWriteTransaction}. + * Merges a piece of data with the existing data at a specified path. Any pre-existing data which is not explicitly + * overwritten will be preserved. This means that if you store a container, its child lists will be merged. * *

* If you require an explicit replace operation, use {@link #put} instead. * - * @param store - * the logical data store which should be modified - * @param path - * the data object path - * @param data - * the data object to be merged to the specified path - * @param createMissingParents - * if {@link #CREATE_MISSING_PARENTS}, any missing parent nodes will be automatically created - * using a merge operation. + * @param store the logical data store which should be modified + * @param path the data object path + * @param data the data object to be merged to the specified path + * @param createMissingParents if {@link #CREATE_MISSING_PARENTS}, any missing parent nodes will be automatically + * created using a merge operation. * @param data tree type - * @throws IllegalStateException - * if the transaction has already been submitted + * @throws IllegalStateException if the transaction has already been submitted */ void merge(LogicalDatastoreType store, InstanceIdentifier path, T data, boolean createMissingParents); + /** + * Removes a piece of data from specified path. This operation does not fail if the specified path does not exist. + * + * @param store Logical data store which should be modified + * @param path Data object path + * @throws IllegalStateException if the transaction was committed or canceled. + */ + void delete(LogicalDatastoreType store, InstanceIdentifier path); + /** * Flag value indicating that missing parents should be created. */ diff --git a/binding2/mdsal-binding2-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/javav2/dom/adapter/impl/data/BindingDOMDataBrokerAdapter.java b/binding2/mdsal-binding2-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/javav2/dom/adapter/impl/data/BindingDOMDataBrokerAdapter.java index ba5c28b2e0..523642cccd 100644 --- a/binding2/mdsal-binding2-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/javav2/dom/adapter/impl/data/BindingDOMDataBrokerAdapter.java +++ b/binding2/mdsal-binding2-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/javav2/dom/adapter/impl/data/BindingDOMDataBrokerAdapter.java @@ -31,9 +31,6 @@ import org.opendaylight.mdsal.binding.javav2.dom.adapter.spi.AbstractForwardedDa import org.opendaylight.mdsal.binding.javav2.dom.adapter.spi.builder.BindingDOMAdapterBuilder; import org.opendaylight.mdsal.binding.javav2.dom.adapter.spi.builder.BindingDOMAdapterBuilder.Factory; import org.opendaylight.mdsal.binding.javav2.dom.codec.impl.BindingToNormalizedNodeCodec; -import org.opendaylight.mdsal.binding.javav2.spec.base.InstanceIdentifier; -import org.opendaylight.mdsal.binding.javav2.spec.base.TreeNode; -import org.opendaylight.mdsal.common.api.AsyncReadWriteTransaction; import org.opendaylight.mdsal.dom.api.DOMDataBroker; import org.opendaylight.mdsal.dom.api.DOMDataTreeService; import org.opendaylight.mdsal.dom.api.DOMService; @@ -78,12 +75,6 @@ public class BindingDOMDataBrokerAdapter extends AbstractForwardedDataBroker imp return new BindingDOMWriteTransactionAdapter<>(getDelegate().newWriteOnlyTransaction(), getCodec()); } - @Override - public AsyncReadWriteTransaction, TreeNode> newReadWriteTransaction() { - // TODO - placeholder for now - throw new UnsupportedOperationException(); - } - @Override public TransactionChain createTransactionChain(final TransactionChainListener listener) { return new BindingDOMTransactionChainAdapter(getDelegate(), getCodec(), listener); diff --git a/binding2/mdsal-binding2-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/javav2/dom/adapter/impl/transaction/BindingDOMTransactionChainAdapter.java b/binding2/mdsal-binding2-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/javav2/dom/adapter/impl/transaction/BindingDOMTransactionChainAdapter.java index e02d2f2624..3dcfe8278e 100644 --- a/binding2/mdsal-binding2-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/javav2/dom/adapter/impl/transaction/BindingDOMTransactionChainAdapter.java +++ b/binding2/mdsal-binding2-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/javav2/dom/adapter/impl/transaction/BindingDOMTransactionChainAdapter.java @@ -22,9 +22,6 @@ import org.opendaylight.mdsal.binding.javav2.api.TransactionChainClosedException import org.opendaylight.mdsal.binding.javav2.api.TransactionChainListener; import org.opendaylight.mdsal.binding.javav2.api.WriteTransaction; import org.opendaylight.mdsal.binding.javav2.dom.codec.impl.BindingToNormalizedNodeCodec; -import org.opendaylight.mdsal.binding.javav2.spec.base.InstanceIdentifier; -import org.opendaylight.mdsal.binding.javav2.spec.base.TreeNode; -import org.opendaylight.mdsal.common.api.AsyncReadWriteTransaction; import org.opendaylight.mdsal.common.api.CommitInfo; import org.opendaylight.mdsal.dom.api.DOMDataBroker; import org.opendaylight.mdsal.dom.api.DOMDataTreeTransaction; @@ -79,12 +76,6 @@ public final class BindingDOMTransactionChainAdapter implements TransactionChain }; } - @Override - public AsyncReadWriteTransaction, TreeNode> newReadWriteTransaction() { - // TODO - placeholder for now - throw new UnsupportedOperationException(); - } - private > F listenForFailure(final WriteTransaction tx, final F future) { future.addCallback(new FutureCallback() { @Override diff --git a/binding2/mdsal-binding2-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/javav2/dom/adapter/spi/AbstractForwardedTransaction.java b/binding2/mdsal-binding2-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/javav2/dom/adapter/spi/AbstractForwardedTransaction.java index 7fbb2e94bf..ce0c955bb9 100644 --- a/binding2/mdsal-binding2-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/javav2/dom/adapter/spi/AbstractForwardedTransaction.java +++ b/binding2/mdsal-binding2-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/javav2/dom/adapter/spi/AbstractForwardedTransaction.java @@ -7,8 +7,11 @@ */ package org.opendaylight.mdsal.binding.javav2.dom.adapter.spi; +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkState; +import static java.util.Objects.requireNonNull; + import com.google.common.annotations.Beta; -import com.google.common.base.Preconditions; import com.google.common.util.concurrent.FluentFuture; import com.google.common.util.concurrent.MoreExecutors; import java.util.Optional; @@ -16,31 +19,27 @@ import javax.annotation.Nonnull; import org.opendaylight.mdsal.binding.javav2.dom.codec.impl.BindingToNormalizedNodeCodec; import org.opendaylight.mdsal.binding.javav2.spec.base.InstanceIdentifier; import org.opendaylight.mdsal.binding.javav2.spec.base.TreeNode; -import org.opendaylight.mdsal.common.api.AsyncTransaction; import org.opendaylight.mdsal.common.api.LogicalDatastoreType; import org.opendaylight.mdsal.dom.api.DOMDataTreeReadTransaction; +import org.opendaylight.mdsal.dom.api.DOMDataTreeTransaction; import org.opendaylight.yangtools.concepts.Delegator; import org.opendaylight.yangtools.concepts.Identifiable; -import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; -import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; /** * Abstract class for forwards transaction and codec for serialize/deserialize DOM and Binding data. * - * @param - * - type of asynchronous transaction + * @param type of asynchronous transaction */ @Beta -public abstract class AbstractForwardedTransaction< - T extends AsyncTransaction>> +public abstract class AbstractForwardedTransaction implements Delegator, Identifiable { private final T delegate; private final BindingToNormalizedNodeCodec codec; public AbstractForwardedTransaction(final T delegateTx, final BindingToNormalizedNodeCodec codec) { - this.delegate = Preconditions.checkNotNull(delegateTx, "Delegate must not be null"); - this.codec = Preconditions.checkNotNull(codec, "Codec must not be null"); + this.delegate = requireNonNull(delegateTx, "Delegate must not be null"); + this.codec = requireNonNull(codec, "Codec must not be null"); } @Nonnull @@ -55,9 +54,8 @@ public abstract class AbstractForwardedTransaction< } @SuppressWarnings("unchecked") - protected final >> S - getDelegateChecked(final Class txType) { - Preconditions.checkState(txType.isInstance(delegate)); + protected final S getDelegateChecked(final Class txType) { + checkState(txType.isInstance(delegate)); return (S) delegate; } @@ -68,7 +66,7 @@ public abstract class AbstractForwardedTransaction< protected final FluentFuture> doRead( final DOMDataTreeReadTransaction readTx, final LogicalDatastoreType store, final InstanceIdentifier path) { - Preconditions.checkArgument(!path.isWildcarded(), "Invalid read of wildcarded path %s", path); + checkArgument(!path.isWildcarded(), "Invalid read of wildcarded path %s", path); return readTx.read(store, codec.toYangInstanceIdentifierBlocking(path)) .transform(codec.deserializeFunction(path)::apply, MoreExecutors.directExecutor()); diff --git a/binding2/mdsal-binding2-spi/src/main/java/org/opendaylight/mdsal/binding/javav2/spi/ForwardingDataBroker.java b/binding2/mdsal-binding2-spi/src/main/java/org/opendaylight/mdsal/binding/javav2/spi/ForwardingDataBroker.java index 77622fd9fc..fd66e0eec7 100644 --- a/binding2/mdsal-binding2-spi/src/main/java/org/opendaylight/mdsal/binding/javav2/spi/ForwardingDataBroker.java +++ b/binding2/mdsal-binding2-spi/src/main/java/org/opendaylight/mdsal/binding/javav2/spi/ForwardingDataBroker.java @@ -19,9 +19,6 @@ import org.opendaylight.mdsal.binding.javav2.api.ReadTransaction; import org.opendaylight.mdsal.binding.javav2.api.TransactionChain; import org.opendaylight.mdsal.binding.javav2.api.TransactionChainListener; import org.opendaylight.mdsal.binding.javav2.api.WriteTransaction; -import org.opendaylight.mdsal.binding.javav2.spec.base.InstanceIdentifier; -import org.opendaylight.mdsal.binding.javav2.spec.base.TreeNode; -import org.opendaylight.mdsal.common.api.AsyncReadWriteTransaction; import org.opendaylight.yangtools.concepts.ListenerRegistration; /** @@ -38,11 +35,6 @@ public abstract class ForwardingDataBroker extends ForwardingObject implements D return delegate().newReadOnlyTransaction(); } - @Override - public AsyncReadWriteTransaction, TreeNode> newReadWriteTransaction() { - return delegate().newReadWriteTransaction(); - } - @Override public WriteTransaction newWriteOnlyTransaction() { return delegate().newWriteOnlyTransaction(); @@ -65,5 +57,4 @@ public abstract class ForwardingDataBroker extends ForwardingObject implements D public TransactionChain createTransactionChain(final TransactionChainListener listener) { return delegate().createTransactionChain(listener); } - } diff --git a/common/mdsal-common-api/src/main/java/org/opendaylight/mdsal/common/api/AsyncDataBroker.java b/common/mdsal-common-api/src/main/java/org/opendaylight/mdsal/common/api/AsyncDataBroker.java deleted file mode 100644 index 33afe6d8ce..0000000000 --- a/common/mdsal-common-api/src/main/java/org/opendaylight/mdsal/common/api/AsyncDataBroker.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v1.0 which accompanies this distribution, - * and is available at http://www.eclipse.org/legal/epl-v10.html - */ -package org.opendaylight.mdsal.common.api; - -import org.opendaylight.yangtools.concepts.Path; - - -/** - * Base interface that provides access to a conceptual data tree store and also provides the ability - * to subscribe for changes to data under a given branch of the tree. - * - *

- * All operations on the data tree are performed via one of the transactions: - *

    - *
  • Read-Only - allocated using {@link #newReadOnlyTransaction()} - *
  • Write-Only - allocated using {@link #newWriteOnlyTransaction()} - *
- * - *

- * These transactions provide a stable isolated view of data tree, which is guaranteed to be not - * affected by other concurrent transactions, until transaction is committed. - * - *

- * For a detailed explanation of how transaction are isolated and how transaction-local changes are - * committed to global data tree, see {@link AsyncReadTransaction}, {@link AsyncWriteTransaction} - * and {@link AsyncWriteTransaction#commit()}. - * - * - *

- * It is strongly recommended to use the type of transaction, which provides only the minimal - * capabilities you need. This allows for optimizations at the data broker / data store level. For - * example, implementations may optimize the transaction for reading if they know ahead of time that - * you only need to read data - such as not keeping additional meta-data, which may be required for - * write transactions. - * - *

- * Implementation Note: This interface is not intended to be implemented by users of MD-SAL, - * but only to be consumed by them. - * - * @param

Type of path (subtree identifier), which represents location in tree - * @param Type of data (payload), which represents data payload - * @deprecated This interface is being removed. Use either {@code org.opendaylight.mdsal.binding.api.DataBroker} - * or {@code org.opendaylight.mdsal.dom.api.DOMDataBroker} instead. - */ -@Deprecated -public interface AsyncDataBroker

, D> extends - AsyncDataTransactionFactory { - - - @Override - AsyncReadTransaction newReadOnlyTransaction(); - - - @Override - AsyncWriteTransaction newWriteOnlyTransaction(); - - @Override - AsyncReadWriteTransaction newReadWriteTransaction(); -} diff --git a/common/mdsal-common-api/src/main/java/org/opendaylight/mdsal/common/api/AsyncDataTransactionFactory.java b/common/mdsal-common-api/src/main/java/org/opendaylight/mdsal/common/api/AsyncDataTransactionFactory.java deleted file mode 100644 index b6d16934e5..0000000000 --- a/common/mdsal-common-api/src/main/java/org/opendaylight/mdsal/common/api/AsyncDataTransactionFactory.java +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v1.0 which accompanies this distribution, - * and is available at http://www.eclipse.org/legal/epl-v10.html - */ -package org.opendaylight.mdsal.common.api; - -import org.opendaylight.yangtools.concepts.Path; - -/** - * A factory which allocates new transactions to operate on the data tree. - * - *

- * Note: This interface is not intended to be used directly, but rather via subinterfaces - * which introduces additional semantics to allocated transactions. - *

    - *
  • {@link AsyncDataBroker} - *
- * - *

- * All operations on the data tree are performed via one of the transactions: - *

    - *
  • Read-Only - allocated using {@link #newReadOnlyTransaction()} - *
  • Write-Only - allocated using {@link #newWriteOnlyTransaction()} - *
- * - *

- * These transactions provides a stable isolated view of the data tree, which is guaranteed to be - * not affected by other concurrent transactions, until transaction is committed. - * - *

- * For a detailed explanation of how transaction are isolated and how transaction-local changes are - * committed to global data tree, see {@link AsyncReadTransaction}, {@link AsyncWriteTransaction} - * and {@link AsyncWriteTransaction#commit()}. - * - *

- * It is strongly recommended to use the type of transaction, which provides only the minimal - * capabilities you need. This allows for optimizations at the data broker / data store level. For - * example, implementations may optimize the transaction for reading if they know ahead of time that - * you only need to read data - such as not keeping additional meta-data, which may be required for - * write transactions. - * - *

- * Implementation Note: This interface is not intended to be implemented by users of MD-SAL, - * but only to be consumed by them. - * - * @see AsyncDataBroker - * - * @param

Type of path (subtree identifier), which represents location in tree - * @param Type of data (payload), which represents data payload - * @deprecated This interface is being removed. Use either {@code org.opendaylight.mdsal.binding.api.TransactionFactory} - * or {@code org.opendaylight.mdsal.dom.api.DOMTransactionFactory} instead. - */ -@Deprecated -public interface AsyncDataTransactionFactory

, D> { - - /** - * Allocates a new read-only transaction which provides an immutable snapshot of - * the data tree. - * - *

- * The view of data tree is an immutable snapshot of current data tree state when - * transaction was allocated. - * - * @return new read-only transaction - */ - AsyncReadTransaction newReadOnlyTransaction(); - - /** - * Allocates new write-only transaction based on latest state of data tree. - * - *

- * Preconditions for mutation of data tree are captured from the snapshot of data tree state, - * when the transaction is allocated. If data was changed during transaction in an incompatible - * way then the commit of this transaction will fail. See {@link AsyncWriteTransaction#commit()} - * for more details about conflicting and not-conflicting changes and failure scenarios. - * - *

- * Since this transaction does not provide a view of the data it SHOULD BE used only by callers - * which are exclusive writers (exporters of data) to the subtree they modify. This prevents - * optimistic lock failures as described in {@link AsyncWriteTransaction#commit()}. - * - *

- * Exclusivity of writers to particular subtree SHOULD BE enforced by external locking - * mechanism. - * - * @return new write-only transaction - */ - AsyncWriteTransaction newWriteOnlyTransaction(); - - /** - * Allocates new read-write transaction which provides a mutable view of the data tree. - * - *

- * Preconditions for mutation of data tree are captured from the snapshot of data tree state, when the transaction - * is allocated. If data was changed during transaction in an incompatible way then the commit of this transaction - * will fail. See {@link AsyncWriteTransaction#commit()} for more details about conflicting and not-conflicting - * changes and failure scenarios. - * - * @return new read-write transaction - */ - AsyncReadWriteTransaction newReadWriteTransaction(); -} diff --git a/common/mdsal-common-api/src/main/java/org/opendaylight/mdsal/common/api/AsyncReadTransaction.java b/common/mdsal-common-api/src/main/java/org/opendaylight/mdsal/common/api/AsyncReadTransaction.java deleted file mode 100644 index 00d9792d08..0000000000 --- a/common/mdsal-common-api/src/main/java/org/opendaylight/mdsal/common/api/AsyncReadTransaction.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v1.0 which accompanies this distribution, - * and is available at http://www.eclipse.org/legal/epl-v10.html - */ -package org.opendaylight.mdsal.common.api; - -import org.opendaylight.yangtools.concepts.Path; - -/** - * Marker interface for stateful read view of the data tree. - * - *

- * View of the data tree is a stable point-in-time snapshot of the current data tree state when the - * transaction was created. It's state and underlying data tree is not affected by other - * concurrently running transactions. - * - *

- * Implementation Note: This interface is not intended to be implemented by users of MD-SAL, - * but only to be consumed by them. - * - *

Transaction isolation example

- * Lets assume initial state of data tree for PATH is A. - * - *
- * txRead = broker.newReadOnlyTransaction(); // read Transaction is snapshot of data
- * txWrite = broker.newReadWriteTransaction(); // concurrent write transaction
- * txRead.read(OPERATIONAL, PATH).get(); // will return Optional containing A
- * txWrite = broker.put(OPERATIONAL, PATH, B); // writes B to PATH
- * txRead.read(OPERATIONAL, PATH).get(); // still returns Optional containing A
- * txWrite.submit().get(); // data tree is updated, PATH contains B
- * txRead.read(OPERATIONAL, PATH).get(); // still returns Optional containing A
- * txAfterCommit = broker.newReadOnlyTransaction(); // read Transaction is snapshot of new state
- * txAfterCommit.read(OPERATIONAL, PATH).get(); // returns Optional containing B;
- * 
- * - *

- * Note: example contains blocking calls on future only to illustrate that action happened - * after other asynchronous action. Use of blocking call - * {@link com.google.common.util.concurrent.ListenableFuture#get()} is discouraged for most uses and - * you should use - * {@link com.google.common.util.concurrent.Futures#addCallback(com.google.common.util.concurrent.ListenableFuture, - * com.google.common.util.concurrent.FutureCallback)} - * or other functions from {@link com.google.common.util.concurrent.Futures} to register more - * specific listeners. - * - * @param

Type of path (subtree identifier), which represents location in tree - * @param Type of data (payload), which represents data payload - * @deprecated This interface is being removed. Use either {@code org.opendaylight.mdsal.binding.api.ReadTransaction} - * or {@code org.opendaylight.mdsal.dom.api.DOMDataTreeReadTransaction} instead. - */ -@Deprecated -public interface AsyncReadTransaction

, D> extends AsyncTransaction, AutoCloseable { - - /** - * Closes this transaction and releases all resources associated with it. - */ - @Override - void close(); -} diff --git a/common/mdsal-common-api/src/main/java/org/opendaylight/mdsal/common/api/AsyncReadWriteTransaction.java b/common/mdsal-common-api/src/main/java/org/opendaylight/mdsal/common/api/AsyncReadWriteTransaction.java deleted file mode 100644 index 5d63b97043..0000000000 --- a/common/mdsal-common-api/src/main/java/org/opendaylight/mdsal/common/api/AsyncReadWriteTransaction.java +++ /dev/null @@ -1,142 +0,0 @@ -/* - * Copyright (c) 2017 Brocade Communications Systems, Inc. and others. All rights reserved. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v1.0 which accompanies this distribution, - * and is available at http://www.eclipse.org/legal/epl-v10.html - */ -package org.opendaylight.mdsal.common.api; - -import org.opendaylight.yangtools.concepts.Path; - -/** - * Transaction enabling a client to have combined read/write capabilities. - * - *

- * The initial state of the write transaction is stable snapshot of current data tree - * state captured when transaction was created and it's state and underlying - * data tree are not affected by other concurrently running transactions. - * - *

- * Write transactions are isolated from other concurrent write transactions. All - * writes are local to the transaction and represents only a proposal of state - * change for data tree and it is not visible to any other concurrently running - * transactions. - * - *

- * Applications publish the changes proposed in the transaction by calling {@link #commit} - * on the transaction. This seals the transaction - * (preventing any further writes using this transaction) and commits it to be - * processed and applied to global conceptual data tree. - * - *

- * The transaction commit may fail due to a concurrent transaction modifying and committing data in - * an incompatible way. See {@link #commit()} for more concrete commit failure examples. - * - * Implementation Note: This interface is not intended to be implemented - * by users of MD-SAL, but only to be consumed by them. - * - *

Examples

- * - *

Transaction local state

- * - *

- * Let's assume initial state of data tree for PATH is A - * . - *

- * txWrite = broker.newReadWriteTransaction(); // concurrent write transaction
- *
- * txWrite.read(OPERATIONAL,PATH).get()        // will return Optional containing A
- * txWrite.put(OPERATIONAL,PATH,B);            // writes B to PATH
- * txWrite.read(OPERATIONAL,PATH).get()        // will return Optional Containing B
- *
- * txWrite.commit().get();                     // data tree is updated, PATH contains B
- *
- * tx1afterCommit = broker.newReadOnlyTransaction(); // read Transaction is snapshot of new state
- * tx1afterCommit.read(OPERATIONAL,PATH).get(); // returns Optional containing B
- * 
- * - *

- * As you could see read-write transaction provides capabilities as - * {@link AsyncWriteTransaction} but also allows for reading proposed changes as - * if they already happened. - * - *

Transaction isolation (read transaction, read-write transaction)

Let - * assume initial state of data tree for PATH is A. - * - *
- * txRead = broker.newReadOnlyTransaction();   // read Transaction is snapshot of data
- * txWrite = broker.newReadWriteTransaction(); // concurrent write transaction
- *
- * txRead.read(OPERATIONAL,PATH).get();        // will return Optional containing A
- * txWrite.read(OPERATIONAL,PATH).get()        // will return Optional containing A
- *
- * txWrite.put(OPERATIONAL,PATH,B);            // writes B to PATH
- * txWrite.read(OPERATIONAL,PATH).get()        // will return Optional Containing B
- *
- * txRead.read(OPERATIONAL,PATH).get();        // concurrent read transaction still returns
- *                                             // Optional containing A
- *
- * txWrite.commit().get();                     // data tree is updated, PATH contains B
- * txRead.read(OPERATIONAL,PATH).get();        // still returns Optional containing A
- *
- * tx1afterCommit = broker.newReadOnlyTransaction(); // read Transaction is snapshot of new state
- * tx1afterCommit.read(OPERATIONAL,PATH).get(); // returns Optional containing B
- * 
- * - *

Transaction isolation (2 concurrent read-write transactions)

Let - * assume initial state of data tree for PATH is A. - * - *
- * tx1 = broker.newReadWriteTransaction(); // read Transaction is snapshot of data
- * tx2 = broker.newReadWriteTransaction(); // concurrent write transaction
- *
- * tx1.read(OPERATIONAL,PATH).get();       // will return Optional containing A
- * tx2.read(OPERATIONAL,PATH).get()        // will return Optional containing A
- *
- * tx2.put(OPERATIONAL,PATH,B);            // writes B to PATH
- * tx2.read(OPERATIONAL,PATH).get()        // will return Optional Containing B
- *
- * tx1.read(OPERATIONAL,PATH).get();       // tx1 read-write transaction still sees Optional
- *                                         // containing A since is isolated from tx2
- * tx1.put(OPERATIONAL,PATH,C);            // writes C to PATH
- * tx1.read(OPERATIONAL,PATH).get()        // will return Optional Containing C
- *
- * tx2.read(OPERATIONAL,PATH).get()        // tx2 read-write transaction still sees Optional
- *                                         // containing B since is isolated from tx1
- *
- * tx2.commit().get();                     // data tree is updated, PATH contains B
- * tx1.read(OPERATIONAL,PATH).get();       // still returns Optional containing C since is isolated from tx2
- *
- * tx1afterCommit = broker.newReadOnlyTransaction(); // read Transaction is snapshot of new state
- * tx1afterCommit.read(OPERATIONAL,PATH).get(); // returns Optional containing B
- *
- * tx1.commit()                            // Will fail with OptimisticLockFailedException
- *                                         // which means concurrent transaction changed the same PATH
- *
- * 
- * - *

- * Note: examples contains blocking calls on future only to illustrate - * that action happened after other asynchronous action. Use of blocking call - * {@link com.google.common.util.concurrent.ListenableFuture#get()} is discouraged for most uses and you should - * use - * {@link com.google.common.util.concurrent.Futures#addCallback(com.google.common.util.concurrent.ListenableFuture, - * com.google.common.util.concurrent.FutureCallback)} - * or other functions from {@link com.google.common.util.concurrent.Futures} to - * register more specific listeners. - * - * @see AsyncReadTransaction - * @see AsyncWriteTransaction - * - * @param

Type of path (subtree identifier), which represents location in tree - * @param Type of data (payload), which represents data payload - * @deprecated This interface is being removed. Use either - * {@code org.opendaylight.mdsal.binding.api.ReadWriteTransaction} - * or {@code org.opendaylight.mdsal.dom.api.DOMDataTreeReadWriteTransaction} instead. - */ -@Deprecated -public interface AsyncReadWriteTransaction

, D> extends AsyncReadTransaction, - AsyncWriteTransaction { - -} diff --git a/common/mdsal-common-api/src/main/java/org/opendaylight/mdsal/common/api/AsyncTransaction.java b/common/mdsal-common-api/src/main/java/org/opendaylight/mdsal/common/api/AsyncTransaction.java deleted file mode 100644 index 644a319119..0000000000 --- a/common/mdsal-common-api/src/main/java/org/opendaylight/mdsal/common/api/AsyncTransaction.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v1.0 which accompanies this distribution, - * and is available at http://www.eclipse.org/legal/epl-v10.html - */ -package org.opendaylight.mdsal.common.api; - -import org.opendaylight.yangtools.concepts.Identifiable; -import org.opendaylight.yangtools.concepts.Path; - -/** - * A common parent for all transactions which operate on a conceptual data tree. - * See derived transaction types for more concrete behavior: - *

    - *
  • {@link AsyncReadTransaction} - Read capabilities, user is able to read data from data tree
  • - *
  • {@link AsyncWriteTransaction} - Write capabilities, user is able to propose changes to data tree
  • - *
- * - * Implementation Note: This interface is not intended to be implemented - * by users of MD-SAL. - * - * @param

Type of path (subtree identifier), which represents location in tree - * @param Type of data (payload), which represents data payload - * @deprecated This interface is being removed. Use either {@code org.opendaylight.mdsal.binding.api.Transaction} - * or {@code org.opendaylight.mdsal.dom.api.DOMDataTreeTransaction} instead. - */ -@Deprecated -public interface AsyncTransaction

,D> extends Identifiable { - @Override - Object getIdentifier(); -} diff --git a/common/mdsal-common-api/src/main/java/org/opendaylight/mdsal/common/api/AsyncWriteTransaction.java b/common/mdsal-common-api/src/main/java/org/opendaylight/mdsal/common/api/AsyncWriteTransaction.java deleted file mode 100644 index 2da17bd6f4..0000000000 --- a/common/mdsal-common-api/src/main/java/org/opendaylight/mdsal/common/api/AsyncWriteTransaction.java +++ /dev/null @@ -1,469 +0,0 @@ -/* - * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v1.0 which accompanies this distribution, - * and is available at http://www.eclipse.org/legal/epl-v10.html - */ -package org.opendaylight.mdsal.common.api; - -import com.google.common.util.concurrent.FluentFuture; -import com.google.common.util.concurrent.ListenableFuture; -import javax.annotation.CheckReturnValue; -import org.eclipse.jdt.annotation.NonNull; -import org.opendaylight.yangtools.concepts.Path; - -/** - * Write transaction provides mutation capabilities for a data tree. - * - *

- * Initial state of write transaction is a stable snapshot of the current data tree. - * The state is captured when the transaction is created and its state and underlying - * data tree are not affected by other concurrently running transactions. - * - *

- * Write transactions are isolated from other concurrent write transactions. All - * writes are local to the transaction and represent only a proposal of state - * change for the data tree and it is not visible to any other concurrently running - * transaction. - * - *

- * Applications make changes to the local data tree in the transaction by via the - * put, merge, and delete operations. - * - *

Put operation

- * Stores a piece of data at a specified path. This acts as an add / replace - * operation, which is to say that whole subtree will be replaced by the - * specified data. - * - *

- * Performing the following put operations: - * - *

- * 1) container { list [ a ] }
- * 2) container { list [ b ] }
- * 
- * will result in the following data being present: - * - *
- * container { list [ b ] }
- * 
- *

Merge operation

- * Merges a piece of data with the existing data at a specified path. Any pre-existing data - * which is not explicitly overwritten will be preserved. This means that if you store a container, - * its child lists will be merged. - * - *

- * Performing the following merge operations: - * - *

- * 1) container { list [ a ] }
- * 2) container { list [ b ] }
- * 
- * will result in the following data being present: - * - *
- * container { list [ a, b ] }
- * 
- * This also means that storing the container will preserve any - * augmentations which have been attached to it. - * - *

Delete operation

- * Removes a piece of data from a specified path. - * - *

- * After applying changes to the local data tree, applications publish the changes proposed in the - * transaction by calling {@link #commit} on the transaction. This seals the transaction - * (preventing any further writes using this transaction) and commits it to be - * processed and applied to global conceptual data tree. - * - *

- * The transaction commit may fail due to a concurrent transaction modifying and committing data in - * an incompatible way. See {@link #commit} for more concrete commit failure examples. - * - *

- * Implementation Note: This interface is not intended to be implemented - * by users of MD-SAL, but only to be consumed by them. - * - * @param

Type of path (subtree identifier), which represents location in tree - * @param Type of data (payload), which represents data payload - * @deprecated This interface is being removed. Use either {@code org.opendaylight.mdsal.binding.api.WriteTransaction} - * or {@code org.opendaylight.mdsal.dom.api.DOMDataTreeWriteTransaction} instead. - */ -@Deprecated -public interface AsyncWriteTransaction

, D> extends AsyncTransaction { - /** - * Cancels the transaction. - * Transactions can only be cancelled if it was not yet committed. - * Invoking cancel() on failed or already canceled will have no effect, and transaction is - * considered cancelled. - * Invoking cancel() on finished transaction (future returned by {@link #commit()} already - * successfully completed) will always fail (return false). - * - * @return false if the task could not be cancelled, typically because it has already - * completed normally; true otherwise - * - */ - boolean cancel(); - - /** - * Removes a piece of data from specified path. This operation does not fail if the specified - * path does not exist. - * - * @param store Logical data store which should be modified - * @param path Data object path - * @throws IllegalStateException if the transaction was committed or canceled. - */ - void delete(LogicalDatastoreType store, P path); - - /** - * Commits this transaction to be asynchronously applied to update the logical data tree. The returned - * {@link FluentFuture} conveys the result of applying the data changes. - * - *

- * This call logically seals the transaction, which prevents the client from further changing the data tree using - * this transaction. Any subsequent calls to put(LogicalDatastoreType, Path, Object), - * merge(LogicalDatastoreType, Path, Object), delete(LogicalDatastoreType, Path) will fail - * with {@link IllegalStateException}. The transaction is marked as committed and enqueued into the data store - * back-end for processing. - * - *

- * Whether or not the commit is successful is determined by versioning of the data tree and validation of registered - * commit participants if the transaction changes the data tree. - * - *

- * The effects of a successful commit of data depends on listeners and commit participants that are registered with - * the data broker. - * - *

Example usage:

- *
-     *  private void doWrite(final int tries) {
-     *      WriteTransaction writeTx = dataBroker.newWriteOnlyTransaction();
-     *      MyDataObject data = ...;
-     *      InstanceIdentifier<MyDataObject> path = ...;
-     *      writeTx.put(LogicalDatastoreType.OPERATIONAL, path, data);
-     *      Futures.addCallback(writeTx.commit(), new FutureCallback<CommitInfo>() {
-     *          public void onSuccess(CommitInfo result) {
-     *              // succeeded
-     *          }
-     *          public void onFailure(Throwable t) {
-     *              if(t instanceof OptimisticLockFailedException) {
-     *                  if(( tries - 1) > 0 ) {
-     *                      // do retry
-     *                      doWrite(tries - 1);
-     *                  } else {
-     *                      // out of retries
-     *                  }
-     *              } else {
-     *                  // failed due to another type of TransactionCommitFailedException.
-     *              }
-     *          });
-     * }
-     * ...
-     * doWrite(2);
-     * 
- * - *

Failure scenarios

- * - *

- * Transaction may fail because of multiple reasons, such as - *

    - *
  • - * Another transaction finished earlier and modified the same node in a non-compatible way (see below). In this - * case the returned future will fail with an {@link OptimisticLockFailedException}. It is the responsibility - * of the caller to create a new transaction and commit the same modification again in order to update data - * tree. - * - * Warning: In most cases, retrying after an OptimisticLockFailedException will result in a high - * probability of success. However, there are scenarios, albeit unusual, where any number of retries will - * not succeed. Therefore it is strongly recommended to limit the number of retries (2 or 3) to avoid - * an endless loop. - * - *
  • - *
  • Data change introduced by this transaction did not pass validation by commit handlers or data was - * incorrectly structured. Returned future will fail with a {@link DataValidationFailedException}. User - * should not retry to create new transaction with same data, since it probably will fail again. - *
  • - *
- * - *

Change compatibility

- * There are several sets of changes which could be considered incompatible between two transactions which are - * derived from same initial state. Rules for conflict detection applies recursively for each subtree level. - * - *

Change compatibility of leafs, leaf-list items

- * Following table shows state changes and failures between two concurrent transactions, which are based on same - * initial state, Tx 1 completes successfully before Tx 2 is committed. - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - *
Initial stateTx 1Tx 2Result
Emptyput(A,1)put(A,2)Tx 2 will fail, state is A=1
Emptyput(A,1)merge(A,2)A=2
Emptymerge(A,1)put(A,2)Tx 2 will fail, state is A=1
Emptymerge(A,1)merge(A,2)A=2
A=0put(A,1)put(A,2)Tx 2 will fail, A=1
A=0put(A,1)merge(A,2)A=2
A=0merge(A,1)put(A,2)Tx 2 will fail, A=1
A=0merge(A,1)merge(A,2)A=2
A=0delete(A)put(A,2)Tx 2 will fail, A does not exists
A=0delete(A)merge(A,2)A=2
- * - *

Change compatibility of subtrees

- * Following table shows state changes and failures between two concurrent transactions, which are based on same - * initial state, Tx 1 completes successfully before Tx 2 is committed. - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - *
Initial stateTx 1Tx 2Result
Emptyput(TOP,[])put(TOP,[])Tx 2 will fail, state is TOP=[]
Emptyput(TOP,[])merge(TOP,[])TOP=[]
Emptyput(TOP,[FOO=1])put(TOP,[BAR=1])Tx 2 will fail, state is TOP=[FOO=1]
Emptyput(TOP,[FOO=1])merge(TOP,[BAR=1])TOP=[FOO=1,BAR=1]
Emptymerge(TOP,[FOO=1])put(TOP,[BAR=1])Tx 2 will fail, state is TOP=[FOO=1]
Emptymerge(TOP,[FOO=1])merge(TOP,[BAR=1])TOP=[FOO=1,BAR=1]
TOP=[]put(TOP,[FOO=1])put(TOP,[BAR=1])Tx 2 will fail, state is TOP=[FOO=1]
TOP=[]put(TOP,[FOO=1])merge(TOP,[BAR=1])state is TOP=[FOO=1,BAR=1]
TOP=[]merge(TOP,[FOO=1])put(TOP,[BAR=1])Tx 2 will fail, state is TOP=[FOO=1]
TOP=[]merge(TOP,[FOO=1])merge(TOP,[BAR=1])state is TOP=[FOO=1,BAR=1]
TOP=[]delete(TOP)put(TOP,[BAR=1])Tx 2 will fail, state is empty store
TOP=[]delete(TOP)merge(TOP,[BAR=1])state is TOP=[BAR=1]
TOP=[]put(TOP/FOO,1)put(TOP/BAR,1])state is TOP=[FOO=1,BAR=1]
TOP=[]put(TOP/FOO,1)merge(TOP/BAR,1)state is TOP=[FOO=1,BAR=1]
TOP=[]merge(TOP/FOO,1)put(TOP/BAR,1)state is TOP=[FOO=1,BAR=1]
TOP=[]merge(TOP/FOO,1)merge(TOP/BAR,1)state is TOP=[FOO=1,BAR=1]
TOP=[]delete(TOP)put(TOP/BAR,1)Tx 2 will fail, state is empty store
TOP=[]delete(TOP)merge(TOP/BAR,1]Tx 2 will fail, state is empty store
TOP=[FOO=1]put(TOP/FOO,2)put(TOP/BAR,1)state is TOP=[FOO=2,BAR=1]
TOP=[FOO=1]put(TOP/FOO,2)merge(TOP/BAR,1)state is TOP=[FOO=2,BAR=1]
TOP=[FOO=1]merge(TOP/FOO,2)put(TOP/BAR,1)state is TOP=[FOO=2,BAR=1]
TOP=[FOO=1]merge(TOP/FOO,2)merge(TOP/BAR,1)state is TOP=[FOO=2,BAR=1]
TOP=[FOO=1]delete(TOP/FOO)put(TOP/BAR,1)state is TOP=[BAR=1]
TOP=[FOO=1]delete(TOP/FOO)merge(TOP/BAR,1]state is TOP=[BAR=1]
- * - * - *

Examples of failure scenarios

- * - *

Conflict of two transactions

- * This example illustrates two concurrent transactions, which derived from same initial state - * of data tree and proposes conflicting modifications. - * - *
-     * txA = broker.newWriteTransaction(); // allocates new transaction, data tree is empty
-     * txB = broker.newWriteTransaction(); // allocates new transaction, data tree is empty
-     * txA.put(CONFIGURATION, PATH, A);    // writes to PATH value A
-     * txB.put(CONFIGURATION, PATH, B)     // writes to PATH value B
-     * ListenableFuture futureA = txA.commit(); // transaction A is sealed and committed
-     * ListenebleFuture futureB = txB.commit(); // transaction B is sealed and committed
-     * 
- * Commit of transaction A will be processed asynchronously and data tree will be updated to - * contain value A for PATH. Returned {@link ListenableFuture} will - * successfully complete once state is applied to data tree. - * Commit of Transaction B will fail, because previous transaction also modified path in a - * concurrent way. The state introduced by transaction B will not be applied. Returned - * {@link ListenableFuture} object will fail with {@link OptimisticLockFailedException} - * exception, which indicates to client that concurrent transaction prevented the committed - * transaction from being applied.
- * - *

- * A successful commit produces implementation-specific {@link CommitInfo} structure, which is used to communicate - * post-condition information to the caller. Such information can contain commit-id, timing information or any - * other information the implementation wishes to share. - * - * @return a FluentFuture containing the result of the commit information. The Future blocks until the commit - * operation is complete. A successful commit returns nothing. On failure, the Future will fail with a - * {@link TransactionCommitFailedException} or an exception derived from TransactionCommitFailedException. - * @throws IllegalStateException if the transaction is already committed or was canceled. - */ - @CheckReturnValue - @NonNull FluentFuture commit(); -} diff --git a/common/mdsal-common-util/src/main/java/org/opendaylight/mdsal/common/util/ForwardingAsyncReadWriteTransaction.java b/common/mdsal-common-util/src/main/java/org/opendaylight/mdsal/common/util/ForwardingAsyncReadWriteTransaction.java deleted file mode 100644 index c39aa0c616..0000000000 --- a/common/mdsal-common-util/src/main/java/org/opendaylight/mdsal/common/util/ForwardingAsyncReadWriteTransaction.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright © 2017, 2018 Red Hat, Inc. and others. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v1.0 which accompanies this distribution, - * and is available at http://www.eclipse.org/legal/epl-v10.html - */ -package org.opendaylight.mdsal.common.util; - -import com.google.common.collect.ForwardingObject; -import com.google.common.util.concurrent.FluentFuture; -import org.opendaylight.mdsal.common.api.AsyncReadWriteTransaction; -import org.opendaylight.mdsal.common.api.CommitInfo; -import org.opendaylight.mdsal.common.api.LogicalDatastoreType; -import org.opendaylight.yangtools.concepts.Path; - -/** - * Utility {@link AsyncReadWriteTransaction} implementation which forwards all interface method - * invocation to a delegate instance. - */ -public class ForwardingAsyncReadWriteTransaction

, D> extends ForwardingObject - implements AsyncReadWriteTransaction { - - private final AsyncReadWriteTransaction delegate; - - protected ForwardingAsyncReadWriteTransaction(AsyncReadWriteTransaction delegate) { - this.delegate = delegate; - } - - @Override - protected AsyncReadWriteTransaction delegate() { - return delegate; - } - - @Override - public Object getIdentifier() { - return delegate.getIdentifier(); - } - - @Override - public boolean cancel() { - return delegate.cancel(); - } - - @Override - public FluentFuture commit() { - return delegate.commit(); - } - - @Override - public void delete(LogicalDatastoreType store, P path) { - delegate.delete(store, path); - } - - @Override - public void close() { - delegate.close(); - } -} diff --git a/dom/mdsal-dom-api/src/main/java/org/opendaylight/mdsal/dom/api/DOMDataBroker.java b/dom/mdsal-dom-api/src/main/java/org/opendaylight/mdsal/dom/api/DOMDataBroker.java index 34b4c517bd..dab3c13988 100644 --- a/dom/mdsal-dom-api/src/main/java/org/opendaylight/mdsal/dom/api/DOMDataBroker.java +++ b/dom/mdsal-dom-api/src/main/java/org/opendaylight/mdsal/dom/api/DOMDataBroker.java @@ -7,8 +7,6 @@ */ package org.opendaylight.mdsal.dom.api; -import org.opendaylight.mdsal.common.api.AsyncDataBroker; -import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; /** @@ -42,8 +40,7 @@ import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; * Implementation Note: This interface is not intended to be implemented by users of MD-SAL, * but only to be consumed by them. */ -public interface DOMDataBroker extends - AsyncDataBroker>, DOMTransactionFactory, +public interface DOMDataBroker extends DOMTransactionFactory, DOMExtensibleService { /** * Create a new transaction chain. The chain will be initialized to read from its backing datastore, with diff --git a/dom/mdsal-dom-api/src/main/java/org/opendaylight/mdsal/dom/api/DOMDataTreeReadTransaction.java b/dom/mdsal-dom-api/src/main/java/org/opendaylight/mdsal/dom/api/DOMDataTreeReadTransaction.java index 578b48e371..d36f72f512 100644 --- a/dom/mdsal-dom-api/src/main/java/org/opendaylight/mdsal/dom/api/DOMDataTreeReadTransaction.java +++ b/dom/mdsal-dom-api/src/main/java/org/opendaylight/mdsal/dom/api/DOMDataTreeReadTransaction.java @@ -9,9 +9,9 @@ package org.opendaylight.mdsal.dom.api; import com.google.common.util.concurrent.FluentFuture; import java.util.Optional; -import org.opendaylight.mdsal.common.api.AsyncReadTransaction; import org.opendaylight.mdsal.common.api.LogicalDatastoreType; import org.opendaylight.mdsal.common.api.ReadFailedException; +import org.opendaylight.yangtools.concepts.Registration; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; @@ -50,8 +50,7 @@ import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; * java.util.concurrent.Executor)} or other functions from {@link com.google.common.util.concurrent.Futures} to register * more specific listeners. */ -public interface DOMDataTreeReadTransaction extends DOMDataTreeTransaction, - AsyncReadTransaction> { +public interface DOMDataTreeReadTransaction extends DOMDataTreeTransaction, Registration { /** * Reads data from provided logical data store located at the provided path. * diff --git a/dom/mdsal-dom-api/src/main/java/org/opendaylight/mdsal/dom/api/DOMDataTreeReadWriteTransaction.java b/dom/mdsal-dom-api/src/main/java/org/opendaylight/mdsal/dom/api/DOMDataTreeReadWriteTransaction.java index 7b481c794e..941eab2dcf 100644 --- a/dom/mdsal-dom-api/src/main/java/org/opendaylight/mdsal/dom/api/DOMDataTreeReadWriteTransaction.java +++ b/dom/mdsal-dom-api/src/main/java/org/opendaylight/mdsal/dom/api/DOMDataTreeReadWriteTransaction.java @@ -7,16 +7,12 @@ */ package org.opendaylight.mdsal.dom.api; -import org.opendaylight.mdsal.common.api.AsyncReadWriteTransaction; -import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; -import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; - /** * A transaction that provides read/write access to a logical data store. * *

- * For more information on usage and examples, please see the documentation in {@link AsyncReadWriteTransaction}. + * For more information on usage and examples, please see the documentation in {@link DOMDataTreeReadTransaction} + * and {@link DOMDataTreeWriteTransaction}. */ -public interface DOMDataTreeReadWriteTransaction extends DOMDataTreeReadTransaction, DOMDataTreeWriteTransaction, - AsyncReadWriteTransaction> { +public interface DOMDataTreeReadWriteTransaction extends DOMDataTreeReadTransaction, DOMDataTreeWriteTransaction { } diff --git a/dom/mdsal-dom-api/src/main/java/org/opendaylight/mdsal/dom/api/DOMDataTreeTransaction.java b/dom/mdsal-dom-api/src/main/java/org/opendaylight/mdsal/dom/api/DOMDataTreeTransaction.java index 6cabe0266d..09d4298f6c 100644 --- a/dom/mdsal-dom-api/src/main/java/org/opendaylight/mdsal/dom/api/DOMDataTreeTransaction.java +++ b/dom/mdsal-dom-api/src/main/java/org/opendaylight/mdsal/dom/api/DOMDataTreeTransaction.java @@ -7,9 +7,7 @@ */ package org.opendaylight.mdsal.dom.api; -import org.opendaylight.mdsal.common.api.AsyncTransaction; -import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; -import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; +import org.opendaylight.yangtools.concepts.Identifiable; /** * A common parent for all transactions which operate on a conceptual data tree. @@ -21,7 +19,6 @@ import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; * * Implementation Note: This interface is not intended to be implemented by users of MD-SAL. */ -public interface DOMDataTreeTransaction extends AsyncTransaction> { - @Override - Object getIdentifier(); +public interface DOMDataTreeTransaction extends Identifiable { + } diff --git a/dom/mdsal-dom-api/src/main/java/org/opendaylight/mdsal/dom/api/DOMDataTreeWriteTransaction.java b/dom/mdsal-dom-api/src/main/java/org/opendaylight/mdsal/dom/api/DOMDataTreeWriteTransaction.java index 24f57dfeae..252c9829ff 100644 --- a/dom/mdsal-dom-api/src/main/java/org/opendaylight/mdsal/dom/api/DOMDataTreeWriteTransaction.java +++ b/dom/mdsal-dom-api/src/main/java/org/opendaylight/mdsal/dom/api/DOMDataTreeWriteTransaction.java @@ -10,71 +10,484 @@ package org.opendaylight.mdsal.dom.api; import com.google.common.util.concurrent.FluentFuture; import javax.annotation.CheckReturnValue; import org.eclipse.jdt.annotation.NonNull; -import org.opendaylight.mdsal.common.api.AsyncWriteTransaction; import org.opendaylight.mdsal.common.api.CommitInfo; +import org.opendaylight.mdsal.common.api.DataValidationFailedException; import org.opendaylight.mdsal.common.api.LogicalDatastoreType; +import org.opendaylight.mdsal.common.api.OptimisticLockFailedException; +import org.opendaylight.mdsal.common.api.TransactionCommitFailedException; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; /** - * A transaction that provides mutation capabilities on a data tree. + * Write transaction provides mutation capabilities for a data tree. * *

- * For more information on usage and examples, please see the documentation in {@link AsyncWriteTransaction}. + * Initial state of write transaction is a stable snapshot of the current data tree. + * The state is captured when the transaction is created and its state and underlying + * data tree are not affected by other concurrently running transactions. + * + *

+ * Write transactions are isolated from other concurrent write transactions. All + * writes are local to the transaction and represent only a proposal of state + * change for the data tree and it is not visible to any other concurrently running + * transaction. + * + *

+ * Applications make changes to the local data tree in the transaction by via the + * put, merge, and delete operations. + * + *

Put operation

+ * Stores a piece of data at a specified path. This acts as an add / replace + * operation, which is to say that whole subtree will be replaced by the + * specified data. + * + *

+ * Performing the following put operations: + * + *

+ * 1) container { list [ a ] }
+ * 2) container { list [ b ] }
+ * 
+ * will result in the following data being present: + * + *
+ * container { list [ b ] }
+ * 
+ *

Merge operation

+ * Merges a piece of data with the existing data at a specified path. Any pre-existing data + * which is not explicitly overwritten will be preserved. This means that if you store a container, + * its child lists will be merged. + * + *

+ * Performing the following merge operations: + * + *

+ * 1) container { list [ a ] }
+ * 2) container { list [ b ] }
+ * 
+ * will result in the following data being present: + * + *
+ * container { list [ a, b ] }
+ * 
+ * This also means that storing the container will preserve any + * augmentations which have been attached to it. + * + *

Delete operation

+ * Removes a piece of data from a specified path. + * + *

+ * After applying changes to the local data tree, applications publish the changes proposed in the + * transaction by calling {@link #commit} on the transaction. This seals the transaction + * (preventing any further writes using this transaction) and commits it to be + * processed and applied to global conceptual data tree. + * + *

+ * The transaction commit may fail due to a concurrent transaction modifying and committing data in + * an incompatible way. See {@link #commit} for more concrete commit failure examples. + * + *

+ * Implementation Note: This interface is not intended to be implemented + * by users of MD-SAL, but only to be consumed by them. */ -public interface DOMDataTreeWriteTransaction extends DOMDataTreeTransaction, - AsyncWriteTransaction> { +public interface DOMDataTreeWriteTransaction extends DOMDataTreeTransaction { /** - * Stores a piece of data at the specified path. This acts as an add / replace - * operation, which is to say that whole subtree will be replaced by the specified data. + * Stores a piece of data at the specified path. This acts as an add / replace operation, which is to say that whole + * subtree will be replaced by the specified data. * *

- * For more information on usage and examples, please see the documentation in {@link AsyncWriteTransaction}. + * If you need to make sure that a parent object exists but you do not want modify its pre-existing state by using + * put, consider using {@link #merge} instead. * - *

- * If you need to make sure that a parent object exists but you do not want modify - * its pre-existing state by using put, consider using {@link #merge} instead. - * - * @param store - * the logical data store which should be modified - * @param path - * the data object path - * @param data - * the data object to be written to the specified path - * @throws IllegalStateException - * if the transaction has already been submitted + * @param store the logical data store which should be modified + * @param path the data object path + * @param data the data object to be written to the specified path + * @throws IllegalStateException if the transaction has already been submitted */ void put(LogicalDatastoreType store, YangInstanceIdentifier path, NormalizedNode data); /** - * Merges a piece of data with the existing data at a specified path. Any pre-existing data - * which is not explicitly overwritten will be preserved. This means that if you store a container, - * its child lists will be merged. + * Merges a piece of data with the existing data at a specified path. Any pre-existing data which is not explicitly + * overwritten will be preserved. This means that if you store a container, its child lists will be merged. * *

- * For more information on usage and examples, please see the documentation in {@link AsyncWriteTransaction}. - * - *

* If you require an explicit replace operation, use {@link #put} instead. * - * @param store - * the logical data store which should be modified - * @param path - * the data object path - * @param data - * the data object to be merged to the specified path - * @throws IllegalStateException - * if the transaction has already been submitted + * @param store the logical data store which should be modified + * @param path the data object path + * @param data the data object to be merged to the specified path + * @throws IllegalStateException if the transaction has already been submitted */ void merge(LogicalDatastoreType store, YangInstanceIdentifier path, NormalizedNode data); - @Override + /** + * Removes a piece of data from specified path. This operation does not fail if the specified path does not exist. + * + * @param store Logical data store which should be modified + * @param path Data object path + * @throws IllegalStateException if the transaction was committed or canceled. + */ void delete(LogicalDatastoreType store, YangInstanceIdentifier path); - @Override + /** + * Commits this transaction to be asynchronously applied to update the logical data tree. The returned + * {@link FluentFuture} conveys the result of applying the data changes. + * + *

+ * This call logically seals the transaction, which prevents the client from further changing the data tree using + * this transaction. Any subsequent calls to put(LogicalDatastoreType, Path, Object), + * merge(LogicalDatastoreType, Path, Object), delete(LogicalDatastoreType, Path) will fail + * with {@link IllegalStateException}. The transaction is marked as committed and enqueued into the data store + * back-end for processing. + * + *

+ * Whether or not the commit is successful is determined by versioning of the data tree and validation of registered + * commit participants if the transaction changes the data tree. + * + *

+ * The effects of a successful commit of data depends on listeners and commit participants that are registered with + * the data broker. + * + *

Example usage:

+ *
+     *  private void doWrite(final int tries) {
+     *      WriteTransaction writeTx = dataBroker.newWriteOnlyTransaction();
+     *      MyDataObject data = ...;
+     *      InstanceIdentifier<MyDataObject> path = ...;
+     *      writeTx.put(LogicalDatastoreType.OPERATIONAL, path, data);
+     *      Futures.addCallback(writeTx.commit(), new FutureCallback<CommitInfo>() {
+     *          public void onSuccess(CommitInfo result) {
+     *              // succeeded
+     *          }
+     *          public void onFailure(Throwable t) {
+     *              if (t instanceof OptimisticLockFailedException) {
+     *                  if(( tries - 1) > 0 ) {
+     *                      // do retry
+     *                      doWrite(tries - 1);
+     *                  } else {
+     *                      // out of retries
+     *                  }
+     *              } else {
+     *                  // failed due to another type of TransactionCommitFailedException.
+     *              }
+     *          });
+     * }
+     * ...
+     * doWrite(2);
+     * 
+ * + *

Failure scenarios

+ * + *

+ * Transaction may fail because of multiple reasons, such as + *

    + *
  • + * Another transaction finished earlier and modified the same node in a non-compatible way (see below). In this + * case the returned future will fail with an {@link OptimisticLockFailedException}. It is the responsibility + * of the caller to create a new transaction and commit the same modification again in order to update data + * tree. + * + * Warning: In most cases, retrying after an OptimisticLockFailedException will result in a high + * probability of success. However, there are scenarios, albeit unusual, where any number of retries will + * not succeed. Therefore it is strongly recommended to limit the number of retries (2 or 3) to avoid + * an endless loop. + * + *
  • + *
  • Data change introduced by this transaction did not pass validation by commit handlers or data was + * incorrectly structured. Returned future will fail with a {@link DataValidationFailedException}. User + * should not retry to create new transaction with same data, since it probably will fail again. + *
  • + *
+ * + *

Change compatibility

+ * There are several sets of changes which could be considered incompatible between two transactions which are + * derived from same initial state. Rules for conflict detection applies recursively for each subtree level. + * + *

Change compatibility of leafs, leaf-list items

+ * Following table shows state changes and failures between two concurrent transactions, which are based on same + * initial state, Tx 1 completes successfully before Tx 2 is committed. + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Initial stateTx 1Tx 2Result
Emptyput(A,1)put(A,2)Tx 2 will fail, state is A=1
Emptyput(A,1)merge(A,2)A=2
Emptymerge(A,1)put(A,2)Tx 2 will fail, state is A=1
Emptymerge(A,1)merge(A,2)A=2
A=0put(A,1)put(A,2)Tx 2 will fail, A=1
A=0put(A,1)merge(A,2)A=2
A=0merge(A,1)put(A,2)Tx 2 will fail, A=1
A=0merge(A,1)merge(A,2)A=2
A=0delete(A)put(A,2)Tx 2 will fail, A does not exists
A=0delete(A)merge(A,2)A=2
+ * + *

Change compatibility of subtrees

+ * Following table shows state changes and failures between two concurrent transactions, which are based on same + * initial state, Tx 1 completes successfully before Tx 2 is committed. + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Initial stateTx 1Tx 2Result
Emptyput(TOP,[])put(TOP,[])Tx 2 will fail, state is TOP=[]
Emptyput(TOP,[])merge(TOP,[])TOP=[]
Emptyput(TOP,[FOO=1])put(TOP,[BAR=1])Tx 2 will fail, state is TOP=[FOO=1]
Emptyput(TOP,[FOO=1])merge(TOP,[BAR=1])TOP=[FOO=1,BAR=1]
Emptymerge(TOP,[FOO=1])put(TOP,[BAR=1])Tx 2 will fail, state is TOP=[FOO=1]
Emptymerge(TOP,[FOO=1])merge(TOP,[BAR=1])TOP=[FOO=1,BAR=1]
TOP=[]put(TOP,[FOO=1])put(TOP,[BAR=1])Tx 2 will fail, state is TOP=[FOO=1]
TOP=[]put(TOP,[FOO=1])merge(TOP,[BAR=1])state is TOP=[FOO=1,BAR=1]
TOP=[]merge(TOP,[FOO=1])put(TOP,[BAR=1])Tx 2 will fail, state is TOP=[FOO=1]
TOP=[]merge(TOP,[FOO=1])merge(TOP,[BAR=1])state is TOP=[FOO=1,BAR=1]
TOP=[]delete(TOP)put(TOP,[BAR=1])Tx 2 will fail, state is empty store
TOP=[]delete(TOP)merge(TOP,[BAR=1])state is TOP=[BAR=1]
TOP=[]put(TOP/FOO,1)put(TOP/BAR,1])state is TOP=[FOO=1,BAR=1]
TOP=[]put(TOP/FOO,1)merge(TOP/BAR,1)state is TOP=[FOO=1,BAR=1]
TOP=[]merge(TOP/FOO,1)put(TOP/BAR,1)state is TOP=[FOO=1,BAR=1]
TOP=[]merge(TOP/FOO,1)merge(TOP/BAR,1)state is TOP=[FOO=1,BAR=1]
TOP=[]delete(TOP)put(TOP/BAR,1)Tx 2 will fail, state is empty store
TOP=[]delete(TOP)merge(TOP/BAR,1]Tx 2 will fail, state is empty store
TOP=[FOO=1]put(TOP/FOO,2)put(TOP/BAR,1)state is TOP=[FOO=2,BAR=1]
TOP=[FOO=1]put(TOP/FOO,2)merge(TOP/BAR,1)state is TOP=[FOO=2,BAR=1]
TOP=[FOO=1]merge(TOP/FOO,2)put(TOP/BAR,1)state is TOP=[FOO=2,BAR=1]
TOP=[FOO=1]merge(TOP/FOO,2)merge(TOP/BAR,1)state is TOP=[FOO=2,BAR=1]
TOP=[FOO=1]delete(TOP/FOO)put(TOP/BAR,1)state is TOP=[BAR=1]
TOP=[FOO=1]delete(TOP/FOO)merge(TOP/BAR,1]state is TOP=[BAR=1]
+ * + * + *

Examples of failure scenarios

+ * + *

Conflict of two transactions

+ * This example illustrates two concurrent transactions, which derived from same initial state + * of data tree and proposes conflicting modifications. + * + *
+     * txA = broker.newWriteTransaction(); // allocates new transaction, data tree is empty
+     * txB = broker.newWriteTransaction(); // allocates new transaction, data tree is empty
+     * txA.put(CONFIGURATION, PATH, A);    // writes to PATH value A
+     * txB.put(CONFIGURATION, PATH, B)     // writes to PATH value B
+     * ListenableFuture futureA = txA.commit(); // transaction A is sealed and committed
+     * ListenebleFuture futureB = txB.commit(); // transaction B is sealed and committed
+     * 
+ * Commit of transaction A will be processed asynchronously and data tree will be updated to + * contain value A for PATH. Returned {@link FluentFuture} will + * successfully complete once state is applied to data tree. + * Commit of Transaction B will fail, because previous transaction also modified path in a + * concurrent way. The state introduced by transaction B will not be applied. Returned + * {@link FluentFuture} object will fail with {@link OptimisticLockFailedException} + * exception, which indicates to client that concurrent transaction prevented the committed + * transaction from being applied.
+ * + *

+ * A successful commit produces implementation-specific {@link CommitInfo} structure, which is used to communicate + * post-condition information to the caller. Such information can contain commit-id, timing information or any + * other information the implementation wishes to share. + * + * @return a FluentFuture containing the result of the commit information. The Future blocks until the commit + * operation is complete. A successful commit returns nothing. On failure, the Future will fail with a + * {@link TransactionCommitFailedException} or an exception derived from TransactionCommitFailedException. + * @throws IllegalStateException if the transaction is already committed or was canceled. + */ @CheckReturnValue @NonNull FluentFuture commit(); - @Override + /** + * Cancels the transaction. Transactions can only be cancelled if it was not yet committed. + * Invoking cancel() on failed or already canceled will have no effect, and transaction is considered cancelled. + * Invoking cancel() on finished transaction (future returned by {@link #commit()} already successfully completed) + * will always fail (return false). + * + * @return false if the task could not be cancelled, typically because it has already completed normally; + * true otherwise + */ boolean cancel(); } diff --git a/dom/mdsal-dom-api/src/main/java/org/opendaylight/mdsal/dom/api/DOMTransactionFactory.java b/dom/mdsal-dom-api/src/main/java/org/opendaylight/mdsal/dom/api/DOMTransactionFactory.java index 586702605a..d12ec4d9ea 100644 --- a/dom/mdsal-dom-api/src/main/java/org/opendaylight/mdsal/dom/api/DOMTransactionFactory.java +++ b/dom/mdsal-dom-api/src/main/java/org/opendaylight/mdsal/dom/api/DOMTransactionFactory.java @@ -7,10 +7,6 @@ */ package org.opendaylight.mdsal.dom.api; -import org.opendaylight.mdsal.common.api.AsyncDataTransactionFactory; -import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; -import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; - /** * A factory which allocates new transactions to operate on the data tree. * @@ -52,15 +48,46 @@ import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; * @see DOMDataBroker * @see DOMTransactionChain */ -public interface DOMTransactionFactory - extends AsyncDataTransactionFactory> { - - @Override +public interface DOMTransactionFactory { + /** + * Allocates a new read-only transaction which provides an immutable snapshot of the data tree. The view of data + * tree is an immutable snapshot of current data tree state when transaction was allocated. + * + * @return A new read-only transaction + */ DOMDataTreeReadTransaction newReadOnlyTransaction(); - @Override + /** + * Allocates new write-only transaction based on latest state of data tree. + * + *

+ * Preconditions for mutation of data tree are captured from the snapshot of data tree state, when the transaction + * is allocated. If data was changed during transaction in an incompatible way then the commit of this transaction + * will fail. See {@link DOMDataTreeWriteTransaction#commit()} for more details about conflicting and + * non-conflicting changes and failure scenarios. + * + *

+ * Since this transaction does not provide a view of the data it SHOULD BE used only by callers which are exclusive + * writers (exporters of data) to the subtree they modify. This prevents optimistic lock failures as described in + * {@link DOMDataTreeWriteTransaction#commit()}. + * + *

+ * Exclusivity of writers to particular subtree SHOULD BE enforced by external locking mechanism. + * + * @return new write-only transaction + */ DOMDataTreeWriteTransaction newWriteOnlyTransaction(); - @Override + /** + * Allocates new read-write transaction which provides a mutable view of the data tree. + * + *

+ * Preconditions for mutation of data tree are captured from the snapshot of data tree state, when the transaction + * is allocated. If data was changed during transaction in an incompatible way then the commit of this transaction + * will fail. See {@link DOMDataTreeReadWriteTransaction#commit()} for more details about conflicting and + * non-conflicting changes and failure scenarios. + * + * @return new read-write transaction + */ DOMDataTreeReadWriteTransaction newReadWriteTransaction(); } diff --git a/dom/mdsal-dom-broker/src/main/java/org/opendaylight/mdsal/dom/broker/AbstractDOMForwardedCompositeTransaction.java b/dom/mdsal-dom-broker/src/main/java/org/opendaylight/mdsal/dom/broker/AbstractDOMForwardedCompositeTransaction.java index 747ef3f4bf..0a0b8c91a3 100644 --- a/dom/mdsal-dom-broker/src/main/java/org/opendaylight/mdsal/dom/broker/AbstractDOMForwardedCompositeTransaction.java +++ b/dom/mdsal-dom-broker/src/main/java/org/opendaylight/mdsal/dom/broker/AbstractDOMForwardedCompositeTransaction.java @@ -5,16 +5,15 @@ * terms of the Eclipse Public License v1.0 which accompanies this distribution, * and is available at http://www.eclipse.org/legal/epl-v10.html */ - package org.opendaylight.mdsal.dom.broker; -import com.google.common.base.Preconditions; +import static com.google.common.base.Preconditions.checkArgument; +import static java.util.Objects.requireNonNull; + import java.util.Collection; import java.util.Map; -import org.opendaylight.mdsal.common.api.AsyncTransaction; +import org.opendaylight.mdsal.dom.api.DOMDataTreeTransaction; import org.opendaylight.mdsal.dom.spi.store.DOMStoreTransaction; -import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; -import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; /** * Composite DOM Transaction backed by {@link DOMStoreTransaction}. @@ -30,7 +29,7 @@ import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; * Subtransaction type */ abstract class AbstractDOMForwardedCompositeTransaction implements - AsyncTransaction> { + DOMDataTreeTransaction { private final Map backingTxs; private final Object identifier; @@ -44,8 +43,8 @@ abstract class AbstractDOMForwardedCompositeTransaction backingTxs) { - this.identifier = Preconditions.checkNotNull(identifier, "Identifier should not be null"); - this.backingTxs = Preconditions.checkNotNull(backingTxs, "Backing transactions should not be null"); + this.identifier = requireNonNull(identifier, "Identifier should not be null"); + this.backingTxs = requireNonNull(backingTxs, "Backing transactions should not be null"); } /** @@ -59,10 +58,10 @@ abstract class AbstractDOMForwardedCompositeTransaction