API Clarity: Documented Async Data Broker APIs. 46/8046/10
authorDevin Avery <devin.avery@brocade.com>
Mon, 16 Jun 2014 16:52:14 +0000 (18:52 +0200)
committerTony Tkacik <ttkacik@cisco.com>
Mon, 23 Jun 2014 09:23:24 +0000 (11:23 +0200)
Documented AsyncDataBroker APIs, which are
base abstract APIs from which Binding and DOM
data broker APIs are derived.

Introduced code-examples illustrating
transaction isollation and conflict detection.

Change-Id: I3c881b1fceb0b68c0cc28a1caa7780b9b6c2af3f
Signed-off-by: Tony Tkacik <ttkacik@cisco.com>
Signed-off-by: Devin Avery <devin.avery@brocade.com>
opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/AsyncDataBroker.java
opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/AsyncDataChangeEvent.java
opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/AsyncDataChangeListener.java
opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/AsyncDataTransactionFactory.java
opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/AsyncReadTransaction.java
opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/AsyncReadWriteTransaction.java
opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/AsyncTransaction.java
opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/AsyncWriteTransaction.java

index 87bbfd3..fb429e5 100644 (file)
@@ -10,6 +10,52 @@ package org.opendaylight.controller.md.sal.common.api.data;
 import org.opendaylight.yangtools.concepts.ListenerRegistration;
 import org.opendaylight.yangtools.concepts.Path;
 
+/**
+ *
+ * Provides access to a conceptual data tree store.
+ *
+ * <p>
+ * Also provides the ability to subscribe for changes to data under a given
+ * branch of the tree.
+ *
+ * <p>
+ * All operations on data tree are performed via one of the transactions:
+ * <ul>
+ * <li>Read-Only - allocated using {@link #newReadOnlyTransaction()}
+ * <li>Write-Only - allocated using {@link #newWriteOnlyTransaction()}
+ * <li>Read-Write - allocated using {@link #newReadWriteTransaction()}
+ * </ul>
+ *
+ * <p>
+ * 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.
+ *
+ * <p>
+ * 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},
+ * {@link AsyncReadWriteTransaction} and {@link AsyncWriteTransaction#commit()}.
+ *
+ *
+ * <p>
+ * 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.
+ *
+ * <p>
+ * <b>Implementation Note:</b> This interface is not intended to be implemented
+ * by users of MD-SAL, but only to be consumed by them.
+ *
+ * @param <P>
+ *            Type of path (subtree identifier), which represents location in
+ *            tree
+ * @param <D>
+ *            Type of data (payload), which represents data payload
+ */
 public interface AsyncDataBroker<P extends Path<P>, D, L extends AsyncDataChangeListener<P, D>> extends //
         AsyncDataTransactionFactory<P, D> {
 
@@ -17,52 +63,150 @@ public interface AsyncDataBroker<P extends Path<P>, D, L extends AsyncDataChange
      *
      * Scope of Data Change
      *
+     * <p>
      * Represents scope of data change (addition, replacement, deletion).
      *
-     * The terminology for types is reused from LDAP
+     * The terminology for scope types is reused from LDAP.
+     *
+     * <h2>Examples</h2>
+     *
+     * Following is an example model with comments describing what notifications
+     * you would receive based on the scope you specify, when you are
+     * registering for changes on container a.
      *
-     * @see http://www.idevelopment.info/data/LDAP/LDAP_Resources/SEARCH_Setting_the_SCOPE_Parameter.shtml
+     * <pre>
+     * container a              // scope BASE, ONE, SUBTREE
+     *    leaf "foo"            // scope ONE, SUBTREE
+     *    container             // scope ONE, SUBTREE
+     *       leaf  "bar"        // scope SUBTREE
+     *    list list             // scope ONE, SUBTREE
+     *      list [a]            // scope SUBTREE
+     *        id "a"            // scope SUBTREE
+     *      list [b]            // scope SUBTREE
+     *        id "b"            // scope SUBTREE
+     * </pre>
+     *
+     * Following is an example model with comments describing what notifications
+     * you would receive based on the scope you specify, when you are
+     * registering for changes on list list (without specifying concrete item in
+     * the list).
+     *
+     * <pre>
+     *  list list               // scope BASE, ONE, SUBTREE
+     *      list [a]            // scope ONE, SUBTREE
+     *        id "a"            // scope SUBTREE
+     *      list [b]            // scope ONE, SUBTREE
+     *        id "b"            // scope SUBTREE
+     * </pre>
+     *
+     *
+     * @see http://www.idevelopment.info/data/LDAP/LDAP_Resources/
+     *      SEARCH_Setting_the_SCOPE_Parameter.shtml
      */
     public enum DataChangeScope {
 
-       /**
-        * Represents only a direct change of the node, such as replacement of node,
-        * addition or deletion.
-        *
-        */
-       BASE,
-       /**
-        * Represent a change (addition,replacement,deletion)
-        * of the node or one of it's direct childs.
-        *
-        */
-       ONE,
-       /**
-        * Represents a change of the node or any of it's child nodes.
-        *
-        */
-       SUBTREE
+        /**
+         * Represents only a direct change of the node, such as replacement of a
+         * node, addition or deletion.
+         *
+         */
+        BASE,
+        /**
+         * Represent a change (addition,replacement,deletion) of the node or one
+         * of its direct children.
+         *
+         * This scope is superset of {@link #BASE}.
+         *
+         */
+        ONE,
+        /**
+         * Represents a change of the node or any of or any of its child nodes,
+         * direct and nested.
+         *
+         * This scope is superset of {@link #ONE} and {@link #BASE}.
+         *
+         */
+        SUBTREE
     }
 
+    /**
+     * {@inheritDoc}
+     */
     @Override
     public AsyncReadTransaction<P, D> newReadOnlyTransaction();
 
+    /**
+     * {@inheritDoc}
+     */
     @Override
-    public AsyncReadWriteTransaction<P,D> newReadWriteTransaction();
+    public AsyncReadWriteTransaction<P, D> newReadWriteTransaction();
 
+    /**
+     * {@inheritDoc}
+     */
     @Override
     public AsyncWriteTransaction<P, D> newWriteOnlyTransaction();
 
     /**
-     * Registers {@link DataChangeListener} for Data Change callbacks
-     * which will be triggered on which will be triggered on the store
+     * Registers a {@link AsyncDataChangeListener} to receive
+     * notifications when data changes under a given path in the conceptual data
+     * tree.
+     * <p>
+     * You are able to register for notifications  for any node or subtree
+     * which can be reached via the supplied path.
+     * <p>
+     * If path type <code>P</code> allows it, you may specify paths up to the leaf nodes
+     * then it is possible to listen on leaf nodes.
+     * <p>
+     * You are able to register for data change notifications for a subtree even
+     * if it does not exist. You will receive notification once that node is
+     * created.
+     * <p>
+     * If there is any preexisting data in data tree on path for which you are
+     * registering, you will receive initial data change event, which will
+     * contain all preexisting data, marked as created.
+     *
+     * <p>
+     * You are also able to specify the scope of the changes you want to be
+     * notified.
+     * <p>
+     * Supported scopes are:
+     * <ul>
+     * <li>{@link DataChangeScope#BASE} - notification events will only be
+     * triggered when a node referenced by path is created, removed or replaced.
+     * <li>{@link DataChangeScope#ONE} - notifications events will only be
+     * triggered when a node referenced by path is created, removed or replaced,
+     * or any or any of its immediate children are created, updated or removed.
+     * <li>{@link DataChangeScope#SUBTREE} - notification events will be
+     * triggered when a node referenced by the path is created, removed
+     * or replaced or any of the children in its subtree are created, removed
+     * or replaced.
+     * </ul>
+     * See {@link DataChangeScope} for examples.
+     * <p>
+     * This method returns a {@link ListenerRegistration} object. To
+     * "unregister" your listener for changes call the "close" method on this
+     * returned object.
+     * <p>
+     * You MUST call close when you no longer need to receive notifications
+     * (such as during shutdown or for example if your bundle is shutting down).
      *
-     * @param store Logical store in which listener is registered.
-     * @param path Path (subtree identifier) on which client listener will be invoked.
-     * @param listener Instance of listener which should be invoked on
-     * @param triggeringScope Scope of change which triggers callback.
-     * @return Listener registration of the listener, call {@link ListenerRegistration#close()}
-     *         to stop delivery of change events.
+     * @param store
+     *            Logical Data Store - Logical Datastore you want to listen for
+     *            changes in. For example
+     *            {@link LogicalDatastoreType#OPERATIONAL} or
+     *            {@link LogicalDatastoreType#CONFIGURATION}
+     * @param path
+     *            Path (subtree identifier) on which client listener will be
+     *            invoked.
+     * @param listener
+     *            Instance of listener which should be invoked on
+     * @param triggeringScope
+     *            Scope of change which triggers callback.
+     * @return Listener registration object, which may be used to unregister
+     *         your listener using {@link ListenerRegistration#close()} to stop
+     *         delivery of change events.
      */
-    ListenerRegistration<L> registerDataChangeListener(LogicalDatastoreType store, P path, L listener, DataChangeScope triggeringScope);
+    ListenerRegistration<L> registerDataChangeListener(LogicalDatastoreType store, P path, L listener,
+            DataChangeScope triggeringScope);
 }
index f612e51..29c9bae 100644 (file)
@@ -13,63 +13,128 @@ import java.util.Set;
 import org.opendaylight.yangtools.concepts.Immutable;
 import org.opendaylight.yangtools.concepts.Path;
 
-public interface AsyncDataChangeEvent<P extends Path<P>,D> extends Immutable {
+/**
+ *
+ * An event which contains a capture of changes in a data subtree
+ *
+ * <p>
+ * Represents a notification indicating that some data at or under a particular
+ * path has changed. The notification contains a capture of the changes in the data
+ * subtree. This event is triggered by successful application of modifications
+ * from a transaction on the global data tree. Use the
+ * {@link AsyncDataBroker#registerDataChangeListener(LogicalDatastoreType, Path, AsyncDataChangeListener, AsyncDataBroker.DataChangeScope)}
+ * method to register a listener for data change events.
+ *
+ * <p>
+ * A listener will only receive notifications for changes to data under the path
+ * they register for. See
+ * {@link AsyncDataBroker#registerDataChangeListener(LogicalDatastoreType, Path, AsyncDataChangeListener, AsyncDataBroker.DataChangeScope)}
+ * to learn more about registration scopes.
+ *
+ * <p>
+ * The entire subtree under the path will be provided via instance methods of Data
+ * Change Event even if just a leaf node changes.
+ *
+ * <p>
+ * <b>Implementation Note:</b> This interface is not intended to be implemented
+ * by users of MD-SAL, but only to be consumed by them.
+ *
+ * @param <P>
+ *            Type of path (subtree identifier), which represents location in
+ *            tree
+ * @param <D>
+ *            Type of data (payload), which represents data payload
+ */
+public interface AsyncDataChangeEvent<P extends Path<P>, D> extends Immutable {
     /**
-     * Returns a immutable map of paths and newly created objects
+     * Returns a map of paths and newly created objects, which were introduced by
+     * this change into conceptual data tree, if no new objects were introduced
+     * this map will be empty.
+     *<p>
+     * This map contains all data tree nodes (and paths to them) which were created
+     * and are in  the scope of listener registration. The data tree nodes
+     * contain their whole subtree with their current state.
      *
      * @return map of paths and newly created objects
      */
     Map<P, D> getCreatedData();
 
     /**
-     * Returns a immutable map of paths and respective updated objects after update.
-     *
-     * Original state of the object is in
-     * {@link #getOriginalData()}
+     * Returns a map of paths and objects which were updated by this change in the
+     * conceptual data tree if no existing objects were updated
+     * this map will be empty.
+     *<p>
+     * This map contains all data tree nodes (and paths to them) which were updated
+     * and are in the scope of listener registration. The data tree nodes
+     * contain their whole subtree with their current state.
+     *<p>
+     * A Node is considered updated if it contents were replaced or one of its
+     * children was created, removed or updated.
+     *<p>
+     * Original state of the updated data tree nodes is in
+     * {@link #getOriginalData()} stored with same path.
      *
      * @return map of paths and newly created objects
      */
     Map<P, D> getUpdatedData();
 
     /**
-     * Returns a immutable set of removed paths.
-     *
-     * Original state of the object is in
-     * {@link #getOriginalData()}
+     * Returns an immutable set of removed paths.
+     *<p>
+     * This set contains the paths to the data tree nodes which are in the scope
+     * of the listener registration that have been removed.
+     *<p>
+     * Original state of the removed data tree nodes is in
+     * {@link #getOriginalData()} stored with same path.
      *
      * @return set of removed paths
      */
     Set<P> getRemovedPaths();
 
     /**
-     * Return a immutable map of paths and original state of updated and removed objects.
+     * Returns an immutable map of updated or removed paths and their original
+     * states prior to this change.
      *
-     * This map is populated if at changed path was previous object, and captures
-     * state of previous object.
+     *<p>
+     * This map contains the original version of the data tree nodes (and paths
+     * to them), which are in the scope of the listener registration.
      *
      * @return map of paths and original state of updated and removed objects.
      */
     Map<P, ? extends D> getOriginalData();
 
     /**
-     * Returns a  immutable stable view of data state, which
-     * captures state of data store before the reported change.
+     * Returns an immutable stable view of data state, which captures the state of
+     * data store before the reported change.
      *
+     *<p>
+     * The view is rooted at the point where the listener, to which the event is
+     * being delivered, was registered.
+     *<p>
+     * If listener used a wildcarded path (if supported by path type) during
+     * registration for change listeners this method returns null, and original
+     * state can be accessed only via {@link #getOriginalData()}
      *
-     * The view is rooted at the point where the listener, to which the event is being delivered, was registered.
-     *
-     * @return Stable view of data before the change happened, rooted at the listener registration path.
+     * @return Stable view of data before the change happened, rooted at the
+     *         listener registration path.
      *
      */
     D getOriginalSubtree();
 
     /**
-     * Returns a immutable stable view of data, which captures state of data store
-     * after the reported change.
-     *
-     * The view is rooted at the point where the listener, to which the event is being delivered, was registered.
+     * Returns an immutable stable view of data, which captures the state of data
+     * store after the reported change.
+     *<p>
+     * The view is rooted at the point where the listener, to which the event is
+     * being delivered, was registered.
+     *<p>
+     * If listener used a wildcarded path (if supported by path type) during
+     * registration for change listeners this method returns null, and state
+     * can be accessed only via {@link #getCreatedData()},
+     * {@link #getUpdatedData()}, {@link #getRemovedPaths()}
      *
-     * @return Stable view of data after the change happened, rooted at the listener registration path.
+     * @return Stable view of data after the change happened, rooted at the
+     *         listener registration path.
      */
     D getUpdatedSubtree();
 }
index 49f07bc..dca5200 100644 (file)
@@ -11,13 +11,42 @@ import java.util.EventListener;
 
 import org.opendaylight.yangtools.concepts.Path;
 
+/**
+ * Listener of data change events on particular subtree.
+ *
+ * <p>
+ * User-supplied implementations of this listener interface MUST register via
+ * {@link AsyncDataBroker#registerDataChangeListener(LogicalDatastoreType, Path, AsyncDataChangeListener, AsyncDataBroker.DataChangeScope)}
+ * in order to start receiving data change events, which capture state changes
+ * in a subtree.
+ *
+ * <p>
+ * <b>Implementation Note:</b> This interface is intended to be implemented
+ * by users of MD-SAL.
+ *
+ * @param <P>
+ *            Type of path (subtree identifier), which represents location in
+ *            tree
+ * @param <D>
+ *            Type of data (payload), which represents data payload
+ */
 public interface AsyncDataChangeListener<P extends Path<P>, D> extends EventListener {
     /**
-     * Note that this method may be invoked from a shared thread pool, so
+     *
+     * Invoked when there is data change for the particular path, which was used to
+     * register this listener.
+     * <p>
+     * This method may be also invoked during registration of the listener if
+     * there is any preexisting data in the conceptual data tree for supplied path.
+     * This initial event will contain all preexisting data as created.
+     *
+     * <p>
+     * <b>Note</b> that this method may be invoked from a shared thread pool, so
      * implementations SHOULD NOT perform CPU-intensive operations and they
      * definitely MUST NOT invoke any potentially blocking operations.
      *
-     * @param change Data Change Event being delivered.
+     * @param change
+     *            Data Change Event being delivered.
      */
     void onDataChanged(AsyncDataChangeEvent<P, D> change);
 }
index 732fed0..cedd883 100644 (file)
@@ -9,12 +9,110 @@ package org.opendaylight.controller.md.sal.common.api.data;
 
 import org.opendaylight.yangtools.concepts.Path;
 
+/**
+ * A factory which allocates new transactions to operate on the data
+ * tree.
+ *
+ * <p>
+ * <b>Note:</b> This interface is not intended to be used directly, but rather
+ * via subinterfaces which introduces additional semantics to allocated
+ * transactions.
+ * <ul>
+ * <li> {@link AsyncDataBroker}
+ * <li> {@link TransactionChain}
+ * </ul>
+ *
+ * <p>
+ * All operations on the data tree are performed via one of the transactions:
+ * <ul>
+ * <li>Read-Only - allocated using {@link #newReadOnlyTransaction()}
+ * <li>Write-Only - allocated using {@link #newWriteOnlyTransaction()}
+ * <li>Read-Write - allocated using {@link #newReadWriteTransaction()}
+ * </ul>
+ *
+ * <p>
+ * 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.
+ *
+ * <p>
+ * 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},
+ * {@link AsyncReadWriteTransaction} and {@link AsyncWriteTransaction#commit()}.
+ *
+ * <p>
+ * 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.
+ *<p>
+ * <b>Implementation Note:</b> This interface is not intended to be implemented
+ * by users of MD-SAL, but only to be consumed by them.
+ *
+ * @see AsyncDataBroker
+ * @see TransactionChain
+ *
+ * @param <P>
+ *            Type of path (subtree identifier), which represents location in
+ *            tree
+ * @param <D>
+ *            Type of data (payload), which represents data payload
+ */
 public interface AsyncDataTransactionFactory<P extends Path<P>, D> {
 
+    /**
+     * Allocates a new read-only transaction which provides an immutable snapshot of
+     * the data tree.
+     *<p>
+     * The view of data tree is an immutable snapshot of current data tree state when
+     * transaction was allocated.
+     *
+     * @return new read-only transaction
+     */
     AsyncReadTransaction<P, D> newReadOnlyTransaction();
 
+    /**
+     * Allocates new read-write transaction which provides a mutable view of the data
+     * tree.
+     *
+     * <p>
+     * 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<P, D> newReadWriteTransaction();
 
-    AsyncWriteTransaction<P,D> newWriteOnlyTransaction();
+    /**
+     * Allocates new write-only transaction based on latest state of data
+     * tree.
+     *
+     * <p>
+     * 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.
+     *
+     * <p>
+     * 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()}.
+     * <p>
+     * Exclusivity of writers to particular subtree SHOULD BE enforced by
+     * external locking mechanism.
+     *
+     * @return new write-only transaction
+     */
+    AsyncWriteTransaction<P, D> newWriteOnlyTransaction();
 
 }
index 7744f71..6cf5a5b 100644 (file)
@@ -12,12 +12,61 @@ import org.opendaylight.yangtools.concepts.Path;
 import com.google.common.base.Optional;
 import com.google.common.util.concurrent.ListenableFuture;
 
+/**
+ *
+ * Provides a stateful read-only view of the data tree.
+ *
+ * <p>
+ * 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.
+ *
+ * <p>
+ * <b>Implementation Note:</b> This interface is not intended to be implemented
+ * by users of MD-SAL, but only to be consumed by them.
+ *
+ * <h2>Transaction isolation example</h2> Lest assume initial state of data tree
+ * for <code>PATH</code> is <code>A</code>.
+ *
+ * <pre>
+ * 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.commit().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;
+ * </pre>
+ *
+ * <p>
+ * <b>Note:</b> example contains blocking calls on future only to illustrate
+ * that action happened after other asynchronous action. Use of blocking call
+ * {@link ListenableFuture#get()} is discouraged for most uses and you should
+ * use
+ * {@link com.google.common.util.concurrent.Futures#addCallback(ListenableFuture, com.google.common.util.concurrent.FutureCallback)}
+ * or other functions from {@link com.google.common.util.concurrent.Futures} to
+ * register more specific listeners.
+ *
+ * @param <P>
+ *            Type of path (subtree identifier), which represents location in
+ *            tree
+ * @param <D>
+ *            Type of data (payload), which represents data payload
+ */
 public interface AsyncReadTransaction<P extends Path<P>, D> extends AsyncTransaction<P, D> {
 
     /**
      *
-     * Reads data from provided logical data store located at provided path
-     *
+     * Reads data from provided logical data store located at the provided path.
+     *<p>
+     * If the target is a subtree, then the whole subtree is read (and will be
+     * accessible from the returned data object).
      *
      * @param store
      *            Logical data store from which read should occur.
@@ -26,10 +75,11 @@ public interface AsyncReadTransaction<P extends Path<P>, D> extends AsyncTransac
      *            read
      * @return Listenable Future which contains read result
      *         <ul>
-     *         <li>If data at supplied path exists the {@link Future#get()}
-     *         returns Optional object containing data
+     *         <li>If data at supplied path exists the
+     *         {@link ListeblaFuture#get()} returns Optional object containing
+     *         data once read is done.
      *         <li>If data at supplied path does not exists the
-     *         {@link Future#get()} returns {@link Optional#absent()}.
+     *         {@link ListenbleFuture#get()} returns {@link Optional#absent()}.
      *         </ul>
      */
     ListenableFuture<Optional<D>> read(LogicalDatastoreType store, P path);
index ce740bf..3410136 100644 (file)
@@ -10,12 +10,126 @@ package org.opendaylight.controller.md.sal.common.api.data;
 import org.opendaylight.yangtools.concepts.Path;
 
 /**
- * Transaction enabling client to have combined transaction,
- * which provides read and write capabilities.
+ * Transaction enabling a client to have a combined read/write capabilities.
  *
+ * <p>
+ * 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.
  *
- * @param <P> Path Type
- * @param <D> Data Type
+ * <p>
+ * 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.
+ *
+ * <p>
+ * 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 submits it to be
+ * processed and applied to global conceptual data tree.
+ *
+ * <p>
+ * 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.
+ *
+ * <b>Implementation Note:</b> This interface is not intended to be implemented
+ * by users of MD-SAL, but only to be consumed by them.
+ *
+ * <h2>Examples</h2>
+ *
+ * <h3>Transaction local state</h3>
+ *
+ * Let assume initial state of data tree for <code>PATH</code> is <code>A</code>
+ * .
+ *
+ * <pre>
+ * 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
+ * </pre>
+ *
+ * As you could see read-write transaction provides capabilities as
+ * {@link AsyncWriteTransaction} but also allows for reading proposed changes as
+ * if they already happened.
+ *
+ * <h3>Transaction isolation (read transaction, read-write transaction)</h3> Let
+ * assume initial state of data tree for <code>PATH</code> is <code>A</code>.
+ *
+ * <pre>
+ * 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
+ * </pre>
+ *
+ * <h3>Transaction isolation (2 concurrent read-write transactions)</h3> Let
+ * assume initial state of data tree for <code>PATH</code> is <code>A</code>.
+ *
+ * <pre>
+ * 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
+ *
+ * </pre>
+ *
+ * <p>
+ * <b>Note:</b> 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.
+ *
+ *
+ * @param <P>
+ *            Type of path (subtree identifier), which represents location in
+ *            tree
+ * @param <D>
+ *            Type of data (payload), which represents data payload
  */
 public interface AsyncReadWriteTransaction<P extends Path<P>, D> extends AsyncReadTransaction<P, D>,
         AsyncWriteTransaction<P, D> {
index 23ca275..c7cc915 100644 (file)
@@ -13,7 +13,17 @@ import org.opendaylight.yangtools.concepts.Path;
 
 /**
  *
- * @author
+ * A common parent for all transactions which operate on a conceptual data tree.
+ *
+ * See derived transaction types for more concrete behavior:
+ * <ul>
+ * <li>{@link AsyncReadTransaction} - Read capabilities, user is able to read data from data tree</li>
+ * <li>{@link AsyncWriteTransaction} - Write capabilities, user is able to propose changes to data tree</li>
+ * <li>{@link AsyncReadWriteTransaction} - Read and Write capabilities, user is able to read state and to propose changes of state.</li>
+ * </ul>
+ *
+ * <b>Implementation Note:</b> This interface is not intended to be implemented
+ * by users of MD-SAL.
  *
  * @param <P> Type of path (subtree identifier), which represents location in tree
  * @param <D> Type of data (payload), which represents data payload
index 82c48d2..e2734ea 100644 (file)
@@ -13,56 +13,118 @@ import org.opendaylight.yangtools.yang.common.RpcResult;
 
 import com.google.common.util.concurrent.ListenableFuture;
 
-public interface AsyncWriteTransaction<P extends Path<P>, D>  extends AsyncTransaction<P, D> {
+/**
+ * Write transaction provides mutation capabilities for a data tree.
+ *
+ * <p>
+ * 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.
+ * <p>
+ * 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.
+ * <p>
+ * 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 submits it to be
+ * processed and applied to global conceptual data tree.
+ * <p>
+ * 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.
+ *
+ *
+ * <p>
+ * <b>Implementation Note:</b> This interface is not intended to be implemented
+ * by users of MD-SAL, but only to be consumed by them.
+ *
+ * @param <P>
+ *            Type of path (subtree identifier), which represents location in
+ *            tree
+ * @param <D>
+ *            Type of data (payload), which represents data payload
+ */
+public interface AsyncWriteTransaction<P extends Path<P>, D> extends AsyncTransaction<P, D> {
     /**
-     * Cancels transaction.
+     * Cancels the transaction.
      *
-     * Transaction could be only cancelled if it's status
-     * is {@link TransactionStatus#NEW} or {@link TransactionStatus#SUBMITED}
+     * Transactions can only be cancelled if it's status is
+     * {@link TransactionStatus#NEW} or {@link TransactionStatus#SUBMITED}
      *
-     * Invoking cancel() on {@link TransactionStatus#FAILED} or {@link TransactionStatus#CANCELED}
-     * will have no effect.
+     * Invoking cancel() on {@link TransactionStatus#FAILED} or
+     * {@link TransactionStatus#CANCELED} will have no effect.
      *
-     * @throws IllegalStateException If transaction status is {@link TransactionStatus#COMMITED}
+     * @throws IllegalStateException
+     *             If transaction status is {@link TransactionStatus#COMMITED}
      *
      */
     public void cancel();
 
     /**
-     * Store a piece of data at specified path. This acts as a add / replace operation,
-     * which is to say that whole subtree will be replaced by specified path.
+     * Store a piece of data at specified path. This acts as an add / replace
+     * operation, which is to say that whole subtree will be replaced by
+     * specified path. Performing the following put operations:
+     *
+     * <pre>
+     * 1) container { list [ a ] }
+     * 2) container { list [ b ] }
+     * </pre>
+     *
+     * will result in the following data being present:
+     *
+     * <pre>
+     * container { list [ b ] }
+     * </pre>
      *
-     * If you need add or merge of current object with specified use {@link #merge(LogicalDatastoreType, Path, Object)}
      *
-     * @param store Logical data store which should be modified
-     * @param path Data object path
-     * @param data Data object to be written to specified path
-     * @throws IllegalStateException if the transaction is no longer {@link TransactionStatus#NEW}
+     * If you need to make sure that a parent object exists, but you do not want modify
+     * its preexisting state by using put, consider using
+     * {@link #merge(LogicalDatastoreType, Path, Object)}
+     *
+     * @param store
+     *            Logical data store which should be modified
+     * @param path
+     *            Data object path
+     * @param data
+     *            Data object to be written to specified path
+     * @throws IllegalStateException
+     *             if the transaction is no longer {@link TransactionStatus#NEW}
      */
     public void put(LogicalDatastoreType store, P path, D data);
 
     /**
-     * Store a piece of data at specified path. This acts as a merge operation,
+     * Store a piece of data at the specified path. This acts as a merge operation,
      * which is to say that 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 put operations:
+     * its child lists will be merged. Performing the following merge
+     * operations:
      *
+     * <pre>
      * 1) container { list [ a ] }
      * 2) container { list [ b ] }
+     * </pre>
      *
      * will result in the following data being present:
      *
+     * <pre>
      * container { list [ a, b ] }
+     * </pre>
      *
-     * This also means that storing the container will preserve any augmentations
-     * which have been attached to it.
-     *
-     * If you require an explicit replace operation, use {@link #put(LogicalDatastoreType, Path, Object)} instead.
+     * This also means that storing the container will preserve any
+     * augmentations which have been attached to it.
+     *<p>
+     * If you require an explicit replace operation, use
+     * {@link #put(LogicalDatastoreType, Path, Object)} instead.
      *
-     * @param store Logical data store which should be modified
-     * @param path Data object path
-     * @param data Data object to be written to specified path
-     * @throws IllegalStateException if the transaction is no longer {@link TransactionStatus#NEW}
+     * @param store
+     *            Logical data store which should be modified
+     * @param path
+     *            Data object path
+     * @param data
+     *            Data object to be written to specified path
+     * @throws IllegalStateException
+     *             if the transaction is no longer {@link TransactionStatus#NEW}
      */
     public void merge(LogicalDatastoreType store, P path, D data);
 
@@ -70,9 +132,12 @@ public interface AsyncWriteTransaction<P extends Path<P>, D>  extends AsyncTrans
      * Remove 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 is no longer {@link TransactionStatus#NEW}
+     * @param store
+     *            Logical data store which should be modified
+     * @param path
+     *            Data object path
+     * @throws IllegalStateException
+     *             if the transaction is no longer {@link TransactionStatus#NEW}
      */
     public void delete(LogicalDatastoreType store, P path);
 
@@ -80,41 +145,169 @@ public interface AsyncWriteTransaction<P extends Path<P>, D>  extends AsyncTrans
      *
      * Closes transaction and resources allocated to the transaction.
      *
-     * This call does not change Transaction status. Client SHOULD
-     * explicitly {@link #commit()} or {@link #cancel()} transaction.
+     * This call does not change Transaction status. Client SHOULD explicitly
+     * {@link #commit()} or {@link #cancel()} transaction.
      *
-     * @throws IllegalStateException if the transaction has not been
-     *         updated by invoking {@link #commit()} or {@link #cancel()}.
+     * @throws IllegalStateException
+     *             if the transaction has not been updated by invoking
+     *             {@link #commit()} or {@link #cancel()}.
      */
     @Override
     public void close();
 
     /**
-     * Initiates a commit of modification. This call logically seals the
-     * transaction, preventing any the client from interacting with the
-     * data stores. The transaction is marked as {@link TransactionStatus#SUBMITED}
-     * and enqueued into the data store backed for processing.
+     * Submits transaction to be applied to update logical data tree.
+     * <p>
+     * This call logically seals the transaction, which prevents the client from
+     * further changing data tree using this transaction. Any subsequent calls to
+     * {@link #put(LogicalDatastoreType, Path, Object)},
+     * {@link #merge(LogicalDatastoreType, Path, Object)} or
+     * {@link #delete(LogicalDatastoreType, Path)} will fail with
+     * {@link IllegalStateException}.
+     *
+     * The transaction is marked as {@link TransactionStatus#SUBMITED} and
+     * enqueued into the data store backed for processing.
      *
      * <p>
-     * The successful commit changes the state of the system and may affect
-     * several components.
+     * Whether or not the commit is successful is determined by versioning
+     * of data tree and validation of registered commit participants
+     * {@link AsyncConfigurationCommitHandler}
+     * if transaction changes {@link LogicalDatastoreType#CONFIGURATION} data tree.
+     *<p>
+     * The effects of successful commit of data depends on
+     * other data change listeners {@link AsyncDataChangeListener} and
+     * {@link AsyncConfigurationCommitHandler}, which was registered to the
+     * same {@link AsyncDataBroker}, to which this transaction belongs.
      *
+     * <h2>Failure scenarios</h2>
      * <p>
-     * The effects of successful commit of data are described in the
-     * specifications and YANG models describing the Provider components of
-     * controller. It is assumed that Consumer has an understanding of this
-     * changes.
-     *
-     * @see DataCommitHandler for further information how two-phase commit is
-     *      processed.
-     * @param store Identifier of the store, where commit should occur.
+     * Transaction may fail because of multiple reasons, such as
+     * <ul>
+     * <li>Another transaction finished earlier and modified the same node in
+     * non-compatible way (see below). In this case the returned future will fail with
+     * {@link OptimisticLockFailedException}. It is the responsibility of the
+     * caller to create a new transaction and submit the same modification again in
+     * order to update data tree.</li>
+     * <li>Data change introduced by this transaction did not pass validation by
+     * commit handlers or data was incorrectly structured. Returned future will
+     * fail with {@link DataValidationFailedException}. User should not retry to
+     * create new transaction with same data, since it probably will fail again.
+     * </li>
+     * </ul>
+     *
+     * <h3>Change compatibility</h3>
+     *
+     * 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.
+     *
+     * <h4>Change compatibility of leafs, leaf-list items</h4>
+     *
+     * 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 submitted.
+     *
+     * <table>
+     * <tr><th>Initial state</th><th>Tx 1</th><th>Tx 2</th><th>Result</th></tr>
+     * <tr><td>Empty</td><td>put(A,1)</td><td>put(A,2)</td><td>Tx 2 will fail, state is A=1</td></tr>
+     * <tr><td>Empty</td><td>put(A,1)</td><td>merge(A,2)</td><td>A=2</td></tr>
+     *
+     * <tr><td>Empty</td><td>merge(A,1)</td><td>put(A,2)</td><td>Tx 2 will fail, state is A=1</td></tr>
+     * <tr><td>Empty</td><td>merge(A,1)</td><td>merge(A,2)</td><td>A=2</td></tr>
+     *
+     *
+     * <tr><td>A=0</td><td>put(A,1)</td><td>put(A,2)</td><td>Tx 2 will fail, A=1</td></tr>
+     * <tr><td>A=0</td><td>put(A,1)</td><td>merge(A,2)</td><td>A=2</td></tr>
+     * <tr><td>A=0</td><td>merge(A,1)</td><td>put(A,2)</td><td>Tx 2 will fail, A=1</td></tr>
+     * <tr><td>A=0</td><td>merge(A,1)</td><td>merge(A,2)</td><td>A=2</td></tr>
+     *
+     * <tr><td>A=0</td><td>delete(A)</td><td>put(A,2)</td><td>Tx 2 will fail, A does not exists</td></tr>
+     * <tr><td>A=0</td><td>delete(A)</td><td>merge(A,2)</td><td>A=2</td></tr>
+     * </table>
+     *
+     * <h4>Change compatibility of subtrees</h4>
+     *
+     * 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 submitted.
+     *
+     * <table>
+     * <tr><th>Initial state</th><th>Tx 1</th><th>Tx 2</th><th>Result</th></tr>
+     *
+     * <tr><td>Empty</td><td>put(TOP,[])</td><td>put(TOP,[])</td><td>Tx 2 will fail, state is TOP=[]</td></tr>
+     * <tr><td>Empty</td><td>put(TOP,[])</td><td>merge(TOP,[])</td><td>TOP=[]</td></tr>
+     *
+     * <tr><td>Empty</td><td>put(TOP,[FOO=1])</td><td>put(TOP,[BAR=1])</td><td>Tx 2 will fail, state is TOP=[FOO=1]</td></tr>
+     * <tr><td>Empty</td><td>put(TOP,[FOO=1])</td><td>merge(TOP,[BAR=1])</td><td>TOP=[FOO=1,BAR=1]</td></tr>
+     *
+     * <tr><td>Empty</td><td>merge(TOP,[FOO=1])</td><td>put(TOP,[BAR=1])</td><td>Tx 2 will fail, state is TOP=[FOO=1]</td></tr>
+     * <tr><td>Empty</td><td>merge(TOP,[FOO=1])</td><td>merge(TOP,[BAR=1])</td><td>TOP=[FOO=1,BAR=1]</td></tr>
+     *
+     * <tr><td>TOP=[]</td><td>put(TOP,[FOO=1])</td><td>put(TOP,[BAR=1])</td><td>Tx 2 will fail, state is TOP=[FOO=1]</td></tr>
+     * <tr><td>TOP=[]</td><td>put(TOP,[FOO=1])</td><td>merge(TOP,[BAR=1])</td><td>state is TOP=[FOO=1,BAR=1]</td></tr>
+     * <tr><td>TOP=[]</td><td>merge(TOP,[FOO=1])</td><td>put(TOP,[BAR=1])</td><td>Tx 2 will fail, state is TOP=[FOO=1]</td></tr>
+     * <tr><td>TOP=[]</td><td>merge(TOP,[FOO=1])</td><td>merge(TOP,[BAR=1])</td><td>state is TOP=[FOO=1,BAR=1]</td></tr>
+     * <tr><td>TOP=[]</td><td>delete(TOP)</td><td>put(TOP,[BAR=1])</td><td>Tx 2 will fail, state is empty store</td></tr>
+     * <tr><td>TOP=[]</td><td>delete(TOP)</td><td>merge(TOP,[BAR=1])</td><td>state is TOP=[BAR=1]</td></tr>
+     *
+     * <tr><td>TOP=[]</td><td>put(TOP/FOO,1)</td><td>put(TOP/BAR,1])</td><td>state is TOP=[FOO=1,BAR=1]</td></tr>
+     * <tr><td>TOP=[]</td><td>put(TOP/FOO,1)</td><td>merge(TOP/BAR,1)</td><td>state is TOP=[FOO=1,BAR=1]</td></tr>
+     * <tr><td>TOP=[]</td><td>merge(TOP/FOO,1)</td><td>put(TOP/BAR,1)</td><td>state is TOP=[FOO=1,BAR=1]</td></tr>
+     * <tr><td>TOP=[]</td><td>merge(TOP/FOO,1)</td><td>merge(TOP/BAR,1)</td><td>state is TOP=[FOO=1,BAR=1]</td></tr>
+     * <tr><td>TOP=[]</td><td>delete(TOP)</td><td>put(TOP/BAR,1)</td><td>Tx 2 will fail, state is empty store</td></tr>
+     * <tr><td>TOP=[]</td><td>delete(TOP)</td><td>merge(TOP/BAR,1]</td><td>Tx 2 will fail, state is empty store</td></tr>
+     *
+     * <tr><td>TOP=[FOO=1]</td><td>put(TOP/FOO,2)</td><td>put(TOP/BAR,1)</td><td>state is TOP=[FOO=2,BAR=1]</td></tr>
+     * <tr><td>TOP=[FOO=1]</td><td>put(TOP/FOO,2)</td><td>merge(TOP/BAR,1)</td><td>state is TOP=[FOO=2,BAR=1]</td></tr>
+     * <tr><td>TOP=[FOO=1]</td><td>merge(TOP/FOO,2)</td><td>put(TOP/BAR,1)</td><td>state is TOP=[FOO=2,BAR=1]</td></tr>
+     * <tr><td>TOP=[FOO=1]</td><td>merge(TOP/FOO,2)</td><td>merge(TOP/BAR,1)</td><td>state is TOP=[FOO=2,BAR=1]</td></tr>
+     * <tr><td>TOP=[FOO=1]</td><td>delete(TOP/FOO)</td><td>put(TOP/BAR,1)</td><td>state is TOP=[BAR=1]</td></tr>
+     * <tr><td>TOP=[FOO=1]</td><td>delete(TOP/FOO)</td><td>merge(TOP/BAR,1]</td><td>state is TOP=[BAR=1]</td></tr>
+     * </table>
+     *
+     *
+     * <h3>Examples of failure scenarios</h3>
+     *
+     * <h4>Conflict of two transactions</h4>
+     *
+     * This example illustrates two concurrent transactions, which derived from
+     * same initial state of data tree and proposes conflicting modifications.
+     *
+     * <pre>
+     * 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
+     * </pre>
+     *
+     * Commit of transaction A will be processed asynchronously and data tree
+     * will be updated to contain value <code>A</code> for <code>PATH</code>.
+     * 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 submitted transaction from being
+     * applied.
+     *
      * @return Result of the Commit, containing success information or list of
      *         encountered errors, if commit was not successful. The Future
      *         blocks until {@link TransactionStatus#COMMITED} is reached.
-     *         Future will fail with {@link TransactionCommitFailedException}
-     *         if Commit of this transaction failed.
+     *         Future will fail with {@link TransactionCommitFailedException} if
+     *         Commit of this transaction failed. TODO: Usability: Consider
+     *         change from ListenableFuture to
+     *         {@link com.google.common.util.concurrent.CheckedFuture} which
+     *         will throw {@link TransactionCommitFailedException}.
      *
-     * @throws IllegalStateException if the transaction is not {@link TransactionStatus#NEW}
+     * @throws IllegalStateException
+     *             if the transaction is not {@link TransactionStatus#NEW}
      */
     public ListenableFuture<RpcResult<TransactionStatus>> commit();
 

©2013 OpenDaylight, A Linux Foundation Collaborative Project. All Rights Reserved.
OpenDaylight is a registered trademark of The OpenDaylight Project, Inc.
Linux Foundation and OpenDaylight are registered trademarks of the Linux Foundation.
Linux is a registered trademark of Linus Torvalds.