Remove common.api.TransactionChain 78/76078/9
authorRobert Varga <robert.varga@pantheon.tech>
Thu, 13 Sep 2018 22:40:22 +0000 (00:40 +0200)
committerRobert Varga <nite@hq.sk>
Tue, 2 Oct 2018 20:21:21 +0000 (20:21 +0000)
This interface needs specialization for both DOM and Binding
levels. While this was partially done, the TransactionChainListener
inteface ended up being clunky.

Disaggregate the TransactionChain concept, by completely eliminating
it from common-api. BindingTransactionChain already existed,
so rename it and define DOMTransactionChain. Support classes are
refactored as well.

Change-Id: I899f1844a2ba84fdc74ba507d3235b87f6a43887
Signed-off-by: Robert Varga <robert.varga@pantheon.tech>
38 files changed:
binding/mdsal-binding-api/src/main/java/org/opendaylight/mdsal/binding/api/BindingTransactionChain.java [deleted file]
binding/mdsal-binding-api/src/main/java/org/opendaylight/mdsal/binding/api/DataBroker.java
binding/mdsal-binding-api/src/main/java/org/opendaylight/mdsal/binding/api/TransactionChain.java [new file with mode: 0644]
binding/mdsal-binding-api/src/main/java/org/opendaylight/mdsal/binding/api/TransactionChainClosedException.java [moved from common/mdsal-common-api/src/main/java/org/opendaylight/mdsal/common/api/TransactionChainClosedException.java with 94% similarity]
binding/mdsal-binding-api/src/main/java/org/opendaylight/mdsal/binding/api/TransactionChainListener.java [moved from common/mdsal-common-api/src/main/java/org/opendaylight/mdsal/common/api/TransactionChainListener.java with 79% similarity]
binding/mdsal-binding-api/src/test/java/org/opendaylight/mdsal/binding/api/TransactionChainClosedExceptionTest.java [moved from common/mdsal-common-api/src/test/java/org/opendaylight/mdsal/common/api/TransactionChainClosedExceptionTest.java with 94% similarity]
binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/BindingDOMDataBrokerAdapter.java
binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/BindingDOMTransactionChainAdapter.java
binding/mdsal-binding-spi/src/main/java/org/opendaylight/mdsal/binding/spi/ForwardingDataBroker.java
binding2/mdsal-binding2-api/src/main/java/org/opendaylight/mdsal/binding/javav2/api/BindingTransactionChain.java [deleted file]
binding2/mdsal-binding2-api/src/main/java/org/opendaylight/mdsal/binding/javav2/api/DataBroker.java
binding2/mdsal-binding2-api/src/main/java/org/opendaylight/mdsal/binding/javav2/api/ReadTransaction.java
binding2/mdsal-binding2-api/src/main/java/org/opendaylight/mdsal/binding/javav2/api/Transaction.java [new file with mode: 0644]
binding2/mdsal-binding2-api/src/main/java/org/opendaylight/mdsal/binding/javav2/api/TransactionChain.java [new file with mode: 0644]
binding2/mdsal-binding2-api/src/main/java/org/opendaylight/mdsal/binding/javav2/api/TransactionChainClosedException.java [new file with mode: 0644]
binding2/mdsal-binding2-api/src/main/java/org/opendaylight/mdsal/binding/javav2/api/TransactionChainListener.java [new file with mode: 0644]
binding2/mdsal-binding2-api/src/main/java/org/opendaylight/mdsal/binding/javav2/api/WriteTransaction.java
binding2/mdsal-binding2-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/javav2/dom/adapter/impl/data/BindingDOMDataBrokerAdapter.java
binding2/mdsal-binding2-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/javav2/dom/adapter/impl/transaction/BindingDOMTransactionChainAdapter.java
binding2/mdsal-binding2-spi/src/main/java/org/opendaylight/mdsal/binding/javav2/spi/ForwardingDataBroker.java
common/mdsal-common-api/src/main/java/org/opendaylight/mdsal/common/api/AsyncDataTransactionFactory.java
common/mdsal-common-api/src/main/java/org/opendaylight/mdsal/common/api/AsyncWriteTransaction.java
common/mdsal-common-api/src/main/java/org/opendaylight/mdsal/common/api/TransactionChain.java [deleted file]
common/mdsal-common-api/src/main/java/org/opendaylight/mdsal/common/api/TransactionChainFactory.java [deleted file]
dom/mdsal-dom-api/src/main/java/org/opendaylight/mdsal/dom/api/DOMDataBroker.java
dom/mdsal-dom-api/src/main/java/org/opendaylight/mdsal/dom/api/DOMTransactionChain.java
dom/mdsal-dom-api/src/main/java/org/opendaylight/mdsal/dom/api/DOMTransactionChainClosedException.java [new file with mode: 0644]
dom/mdsal-dom-api/src/main/java/org/opendaylight/mdsal/dom/api/DOMTransactionChainListener.java [new file with mode: 0644]
dom/mdsal-dom-broker/src/main/java/org/opendaylight/mdsal/dom/broker/AbstractDOMDataBroker.java
dom/mdsal-dom-broker/src/main/java/org/opendaylight/mdsal/dom/broker/DOMDataBrokerTransactionChainImpl.java
dom/mdsal-dom-broker/src/main/java/org/opendaylight/mdsal/dom/broker/ShardedDOMDataBrokerAdapter.java
dom/mdsal-dom-broker/src/main/java/org/opendaylight/mdsal/dom/broker/ShardedDOMTransactionChainAdapter.java
dom/mdsal-dom-broker/src/main/java/org/opendaylight/mdsal/dom/broker/pingpong/PingPongDataBroker.java
dom/mdsal-dom-broker/src/main/java/org/opendaylight/mdsal/dom/broker/pingpong/PingPongTransactionChain.java
dom/mdsal-dom-broker/src/test/java/org/opendaylight/mdsal/dom/broker/BlockingTransactionChainListener.java
dom/mdsal-dom-broker/src/test/java/org/opendaylight/mdsal/dom/broker/ShardedDOMTransactionChainAdapterTest.java
dom/mdsal-dom-spi/src/main/java/org/opendaylight/mdsal/dom/spi/ForwardingDOMDataBroker.java
dom/mdsal-dom-spi/src/main/java/org/opendaylight/mdsal/dom/spi/store/DOMStore.java

diff --git a/binding/mdsal-binding-api/src/main/java/org/opendaylight/mdsal/binding/api/BindingTransactionChain.java b/binding/mdsal-binding-api/src/main/java/org/opendaylight/mdsal/binding/api/BindingTransactionChain.java
deleted file mode 100644 (file)
index afd2e75..0000000
+++ /dev/null
@@ -1,35 +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.binding.api;
-
-import org.opendaylight.mdsal.common.api.TransactionChain;
-import org.opendaylight.yangtools.yang.binding.DataObject;
-import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
-
-/**
- * A chain of transactions.
- *
- * <p>
- * For more information about transaction chaining and transaction chains
- * see {@link TransactionChain}.
- *
- * @see TransactionChain
- *
- */
-public interface BindingTransactionChain extends TransactionFactory,
-        TransactionChain<InstanceIdentifier<?>, DataObject> {
-
-    @Override
-    ReadTransaction newReadOnlyTransaction();
-
-    @Override
-    WriteTransaction newWriteOnlyTransaction();
-
-    @Override
-    ReadWriteTransaction newReadWriteTransaction();
-}
index 22a227e8226d66dc06703b0a40bbd9fc5758df33..5bcfbea7c831c202c49b6c50ab484c0577fc2544 100644 (file)
@@ -8,8 +8,6 @@
 package org.opendaylight.mdsal.binding.api;
 
 import org.opendaylight.mdsal.common.api.AsyncDataBroker;
-import org.opendaylight.mdsal.common.api.TransactionChainFactory;
-import org.opendaylight.mdsal.common.api.TransactionChainListener;
 import org.opendaylight.yangtools.yang.binding.DataObject;
 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
 
@@ -44,10 +42,14 @@ import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
  * <b>Implementation Note:</b> 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<InstanceIdentifier<?>, DataObject>,
-    TransactionChainFactory<InstanceIdentifier<?>, DataObject>, TransactionFactory, BindingService,
-        DataTreeChangeService {
-
-    @Override
-    BindingTransactionChain createTransactionChain(TransactionChainListener listener);
+public interface DataBroker extends AsyncDataBroker<InstanceIdentifier<?>, DataObject>, TransactionFactory,
+        BindingService, 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.
+     *
+     * @param listener Transaction chain event listener
+     * @return A new transaction chain.
+     */
+    TransactionChain createTransactionChain(TransactionChainListener listener);
 }
diff --git a/binding/mdsal-binding-api/src/main/java/org/opendaylight/mdsal/binding/api/TransactionChain.java b/binding/mdsal-binding-api/src/main/java/org/opendaylight/mdsal/binding/api/TransactionChain.java
new file mode 100644 (file)
index 0000000..4383db4
--- /dev/null
@@ -0,0 +1,105 @@
+/*
+ * 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.binding.api;
+
+import org.opendaylight.yangtools.concepts.Registration;
+
+/**
+ * A chain of transactions. Transactions in a chain need to be committed in sequence and each transaction should see
+ * the effects of previous committed transactions as they occurred. A chain makes no guarantees of atomicity across
+ * the chained transactions - the transactions are committed as soon as possible in the order that they were committed.
+ * This behaviour is different from the default AsyncDataBroker, where a transaction is always created from the current
+ * global state, not taking into account any transactions previously committed by the calling thread. Due to
+ * the asynchronous nature of transaction submission this can lead to surprising results. If a thread executes
+ * the following sequence sufficiently quickly:
+ *
+ * <code>
+ * AsyncWriteTransaction t1 = broker.newWriteOnlyTransaction();
+ * t1.put(id, data);
+ * t1.commit();
+ *
+ * AsyncReadTransaction t2 = broker.newReadOnlyTransaction();
+ * Optional&lt;?&gt; maybeData = t2.read(id).get();
+ * </code>
+ * it may happen, that it sees maybeData.isPresent() == false, simply because t1 has not completed the processes
+ * of being applied and t2 is actually allocated from the previous state. This is obviously bad for users who create
+ * incremental state in the datastore and actually read what they write in subsequent transactions. Using
+ * a TransactionChain instead of a broker solves this particular problem, and leads to expected behavior: t2 will always
+ * see the data written in t1 present.
+ */
+public interface TransactionChain extends Registration, TransactionFactory {
+    /**
+     * Create a new read only transaction which will continue the chain.
+     *
+     * <p>
+     * The previous write transaction has to be either SUBMITTED ({@link WriteTransaction#commit commit} was invoked)
+     * or CANCELLED ({@link #close close} was invoked).
+     *
+     * <p>
+     * The returned read-only transaction presents an isolated view of the data if the previous write transaction was
+     * successful - in other words, this read-only transaction will see the state changes made by the previous write
+     * transaction in the chain. However, state which was introduced by other transactions outside this transaction
+     * chain after creation of the previous transaction is not visible.
+     *
+     * @return New transaction in the chain.
+     * @throws IllegalStateException if the previous transaction was not SUBMITTED or CANCELLED.
+     * @throws TransactionChainClosedException if the chain has been closed.
+     */
+    @Override
+    ReadTransaction newReadOnlyTransaction();
+
+    /**
+     * Create a new write-only transaction which will continue the chain.
+     *
+     * <p>
+     * The previous write transaction has to be either SUBMITTED ({@link WriteTransaction#commit commit} was invoked)
+     * or CANCELLED ({@link #close close} was invoked)
+     *
+     * <p>
+     * The returned write-only transaction presents an isolated view of the data if the previous write transaction was
+     * successful - in other words, this write-only transaction will see the state changes made by the previous write
+     * transaction in the chain. However, state which was introduced by other transactions outside this transaction
+     * chain after creation of the previous transaction is not visible
+     *
+     * <p>
+     * Committing this write-only transaction using {@link WriteTransaction#commit commit} will commit the state
+     * changes in this transaction to be visible to any subsequent transaction in this chain and also to any transaction
+     * outside this chain.
+     *
+     * @return New transaction in the chain.
+     * @throws IllegalStateException if the previous transaction was not SUBMITTED or CANCELLED.
+     * @throws TransactionChainClosedException if the chain has been closed.
+     */
+    @Override
+    WriteTransaction newWriteOnlyTransaction();
+
+    /**
+     * Create a new read-write transaction which will continue the chain.
+     *
+     * <p>
+     * The previous write transaction has to be either SUBMITTED ({@link WriteTransaction#commit commit} was invoked)
+     * or CANCELLED ({@link #close close} was invoked).
+     *
+     * <p>
+     * The returned read-write transaction presents an isolated view of the data if the previous write transaction was
+     * successful - in other words, this read-write transaction will see the state changes made by the previous write
+     * transaction in the chain. However, state which was introduced by other transactions outside this transaction
+     * chain after creation of the previous transaction is not visible.
+     *
+     * <p>
+     * Committing this read-write transaction using {@link WriteTransaction#commit commit} will commit the state changes
+     * in this transaction to be visible to any subsequent transaction in this chain and also to any transaction outside
+     * this chain.
+     *
+     * @return New transaction in the chain.
+     * @throws IllegalStateException if the previous transaction was not SUBMITTED or CANCELLED.
+     * @throws TransactionChainClosedException if the chain has been closed.
+     */
+    @Override
+    ReadWriteTransaction newReadWriteTransaction();
+}
similarity index 94%
rename from common/mdsal-common-api/src/main/java/org/opendaylight/mdsal/common/api/TransactionChainClosedException.java
rename to binding/mdsal-binding-api/src/main/java/org/opendaylight/mdsal/binding/api/TransactionChainClosedException.java
index 81c18eff2cdb9206e9338fc74f8dd59f6815fa7c..02b8d47108ae2292939fcaf0b261c56a420e00a8 100644 (file)
@@ -5,7 +5,7 @@
  * 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;
+package org.opendaylight.mdsal.binding.api;
 
 /**
  * Exception thrown when an attempt is made to open a new transaction in a closed
similarity index 79%
rename from common/mdsal-common-api/src/main/java/org/opendaylight/mdsal/common/api/TransactionChainListener.java
rename to binding/mdsal-binding-api/src/main/java/org/opendaylight/mdsal/binding/api/TransactionChainListener.java
index 9137c9af10abd37753733c324678dfa30bb5473e..515dcb75ae78ba3fcf978385b043de183e46d1b5 100644 (file)
@@ -5,13 +5,14 @@
  * 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;
+package org.opendaylight.mdsal.binding.api;
 
 import java.util.EventListener;
 
 /**
  * Listener for transaction chain events.
  */
+// FIXME: 4.0.0: remove this in favor of a TransactionChain destiny, available as a FluentFuture from TransactionChain
 public interface TransactionChainListener extends EventListener {
     /**
      * Invoked if when a transaction in the chain fails. All transactions submitted after the failed transaction, in the
@@ -23,7 +24,7 @@ public interface TransactionChainListener extends EventListener {
      * @param transaction Transaction which caused the chain to fail
      * @param cause The cause of transaction failure
      */
-    void onTransactionChainFailed(TransactionChain<?, ?> chain, AsyncTransaction<?, ?> transaction, Throwable cause);
+    void onTransactionChainFailed(TransactionChain chain, Transaction transaction, Throwable cause);
 
     /**
      * Invoked when a transaction chain is completed. A transaction chain is considered completed when it has been
@@ -31,6 +32,6 @@ public interface TransactionChainListener extends EventListener {
      *
      * @param chain Transaction chain which completed
      */
-    void onTransactionChainSuccessful(TransactionChain<?, ?> chain);
+    void onTransactionChainSuccessful(TransactionChain chain);
 }
 
@@ -5,7 +5,7 @@
  * 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;
+package org.opendaylight.mdsal.binding.api;
 
 import org.junit.Test;
 
index 283423a498258434cbc33873321a9883837c9a7e..44db2d773182b9366f0339e1381df673d47d6c6d 100644 (file)
@@ -10,16 +10,16 @@ package org.opendaylight.mdsal.binding.dom.adapter;
 import com.google.common.collect.ClassToInstanceMap;
 import com.google.common.collect.ImmutableSet;
 import java.util.Set;
-import org.opendaylight.mdsal.binding.api.BindingTransactionChain;
 import org.opendaylight.mdsal.binding.api.DataBroker;
 import org.opendaylight.mdsal.binding.api.DataTreeChangeListener;
 import org.opendaylight.mdsal.binding.api.DataTreeChangeService;
 import org.opendaylight.mdsal.binding.api.DataTreeIdentifier;
 import org.opendaylight.mdsal.binding.api.ReadTransaction;
 import org.opendaylight.mdsal.binding.api.ReadWriteTransaction;
+import org.opendaylight.mdsal.binding.api.TransactionChain;
+import org.opendaylight.mdsal.binding.api.TransactionChainListener;
 import org.opendaylight.mdsal.binding.api.WriteTransaction;
 import org.opendaylight.mdsal.binding.dom.adapter.BindingDOMAdapterBuilder.Factory;
-import org.opendaylight.mdsal.common.api.TransactionChainListener;
 import org.opendaylight.mdsal.dom.api.DOMDataBroker;
 import org.opendaylight.mdsal.dom.api.DOMDataTreeChangeService;
 import org.opendaylight.mdsal.dom.api.DOMService;
@@ -71,7 +71,7 @@ public class BindingDOMDataBrokerAdapter extends AbstractForwardedDataBroker imp
     }
 
     @Override
-    public BindingTransactionChain createTransactionChain(final TransactionChainListener listener) {
+    public TransactionChain createTransactionChain(final TransactionChainListener listener) {
         return new BindingDOMTransactionChainAdapter(getDelegate(), getCodec(), listener);
     }
 
index e0c4b28174f5b0c77210b71aa92329a4dc6d76b5..2bc9007d682978806321786be4e19136ade4011c 100644 (file)
@@ -7,31 +7,35 @@
  */
 package org.opendaylight.mdsal.binding.dom.adapter;
 
-import com.google.common.base.Preconditions;
+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.FutureCallback;
 import com.google.common.util.concurrent.Futures;
 import com.google.common.util.concurrent.ListenableFuture;
 import com.google.common.util.concurrent.MoreExecutors;
+import java.util.function.Supplier;
 import org.eclipse.jdt.annotation.NonNull;
-import org.opendaylight.mdsal.binding.api.BindingTransactionChain;
 import org.opendaylight.mdsal.binding.api.ReadTransaction;
 import org.opendaylight.mdsal.binding.api.ReadWriteTransaction;
+import org.opendaylight.mdsal.binding.api.TransactionChain;
+import org.opendaylight.mdsal.binding.api.TransactionChainClosedException;
+import org.opendaylight.mdsal.binding.api.TransactionChainListener;
 import org.opendaylight.mdsal.binding.api.WriteTransaction;
-import org.opendaylight.mdsal.common.api.AsyncTransaction;
 import org.opendaylight.mdsal.common.api.CommitInfo;
-import org.opendaylight.mdsal.common.api.TransactionChain;
-import org.opendaylight.mdsal.common.api.TransactionChainListener;
 import org.opendaylight.mdsal.dom.api.DOMDataBroker;
-import org.opendaylight.mdsal.dom.api.DOMDataTreeReadTransaction;
 import org.opendaylight.mdsal.dom.api.DOMDataTreeReadWriteTransaction;
+import org.opendaylight.mdsal.dom.api.DOMDataTreeTransaction;
 import org.opendaylight.mdsal.dom.api.DOMDataTreeWriteTransaction;
 import org.opendaylight.mdsal.dom.api.DOMTransactionChain;
+import org.opendaylight.mdsal.dom.api.DOMTransactionChainClosedException;
+import org.opendaylight.mdsal.dom.api.DOMTransactionChainListener;
 import org.opendaylight.yangtools.concepts.Delegator;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-final class BindingDOMTransactionChainAdapter implements BindingTransactionChain, Delegator<DOMTransactionChain> {
+final class BindingDOMTransactionChainAdapter implements TransactionChain, Delegator<DOMTransactionChain> {
 
     private static final Logger LOG = LoggerFactory.getLogger(BindingDOMTransactionChainAdapter.class);
 
@@ -42,7 +46,7 @@ final class BindingDOMTransactionChainAdapter implements BindingTransactionChain
 
     BindingDOMTransactionChainAdapter(final DOMDataBroker chainFactory,
             final BindingToNormalizedNodeCodec codec, final TransactionChainListener listener) {
-        Preconditions.checkNotNull(chainFactory, "DOM Transaction chain factory must not be null");
+        requireNonNull(chainFactory, "DOM Transaction chain factory must not be null");
         this.domListener = new DelegateChainListener();
         this.bindingListener = listener;
         this.delegate = chainFactory.createTransactionChain(domListener);
@@ -56,28 +60,24 @@ final class BindingDOMTransactionChainAdapter implements BindingTransactionChain
 
     @Override
     public ReadTransaction newReadOnlyTransaction() {
-        final DOMDataTreeReadTransaction delegateTx = delegate.newReadOnlyTransaction();
-        return new BindingDOMReadTransactionAdapter(delegateTx, codec);
+        return new BindingDOMReadTransactionAdapter(createTransaction(delegate::newReadOnlyTransaction), codec);
     }
 
     @Override
     public WriteTransaction newWriteOnlyTransaction() {
-        final DOMDataTreeWriteTransaction delegateTx = delegate.newWriteOnlyTransaction();
+        final DOMDataTreeWriteTransaction delegateTx = createTransaction(delegate::newWriteOnlyTransaction);
         return new BindingDOMWriteTransactionAdapter<DOMDataTreeWriteTransaction>(delegateTx, codec) {
-
             @Override
             public @NonNull FluentFuture<? extends @NonNull CommitInfo> commit() {
                 return listenForFailure(this, super.commit());
             }
-
         };
     }
 
     @Override
     public ReadWriteTransaction newReadWriteTransaction() {
-        final DOMDataTreeReadWriteTransaction delegateTx = delegate.newReadWriteTransaction();
+        final DOMDataTreeReadWriteTransaction delegateTx = createTransaction(delegate::newReadWriteTransaction);
         return new BindingDOMReadWriteTransactionAdapter(delegateTx, codec) {
-
             @Override
             public @NonNull FluentFuture<? extends @NonNull CommitInfo> commit() {
                 return listenForFailure(this, super.commit());
@@ -117,13 +117,19 @@ final class BindingDOMTransactionChainAdapter implements BindingTransactionChain
         delegate.close();
     }
 
-    private final class DelegateChainListener implements TransactionChainListener {
+    private static <T> T createTransaction(final Supplier<T> supplier) {
+        try {
+            return supplier.get();
+        } catch (DOMTransactionChainClosedException e) {
+            throw new TransactionChainClosedException("Transaction chain already closed", e);
+        }
+    }
 
+    private final class DelegateChainListener implements DOMTransactionChainListener {
         @Override
-        public void onTransactionChainFailed(final TransactionChain<?, ?> chain,
-                final AsyncTransaction<?, ?> transaction, final Throwable cause) {
-            Preconditions.checkState(delegate.equals(chain),
-                    "Illegal state - listener for %s was invoked for incorrect chain %s.", delegate, chain);
+        public void onTransactionChainFailed(final DOMTransactionChain chain, final DOMDataTreeTransaction transaction,
+                final Throwable cause) {
+            checkState(delegate.equals(chain), "Listener for %s was invoked for incorrect chain %s.", delegate, chain);
             /*
              * Intentionally NOOP, callback for failure, since we
              * are also listening on each transaction future for failure,
@@ -137,9 +143,8 @@ final class BindingDOMTransactionChainAdapter implements BindingTransactionChain
         }
 
         @Override
-        public void onTransactionChainSuccessful(final TransactionChain<?, ?> chain) {
-            Preconditions.checkState(delegate.equals(chain),
-                    "Illegal state - listener for %s was invoked for incorrect chain %s.", delegate, chain);
+        public void onTransactionChainSuccessful(final DOMTransactionChain chain) {
+            checkState(delegate.equals(chain), "Listener for %s was invoked for incorrect chain %s.", delegate, chain);
             bindingListener.onTransactionChainSuccessful(BindingDOMTransactionChainAdapter.this);
         }
     }
index 7f8467f1a5e8d357cac508d056ccf2da2a27368c..9dd676b8cfb0c617f6c86332ce9ef9d87fd2f113 100644 (file)
@@ -9,14 +9,14 @@ package org.opendaylight.mdsal.binding.spi;
 
 import com.google.common.collect.ForwardingObject;
 import javax.annotation.Nonnull;
-import org.opendaylight.mdsal.binding.api.BindingTransactionChain;
 import org.opendaylight.mdsal.binding.api.DataBroker;
 import org.opendaylight.mdsal.binding.api.DataTreeChangeListener;
 import org.opendaylight.mdsal.binding.api.DataTreeIdentifier;
 import org.opendaylight.mdsal.binding.api.ReadTransaction;
 import org.opendaylight.mdsal.binding.api.ReadWriteTransaction;
+import org.opendaylight.mdsal.binding.api.TransactionChain;
+import org.opendaylight.mdsal.binding.api.TransactionChainListener;
 import org.opendaylight.mdsal.binding.api.WriteTransaction;
-import org.opendaylight.mdsal.common.api.TransactionChainListener;
 import org.opendaylight.yangtools.concepts.ListenerRegistration;
 import org.opendaylight.yangtools.yang.binding.DataObject;
 
@@ -46,12 +46,12 @@ public abstract class ForwardingDataBroker extends ForwardingObject implements D
 
     @Override
     public <T extends DataObject, L extends DataTreeChangeListener<T>> ListenerRegistration<L>
-            registerDataTreeChangeListener(DataTreeIdentifier<T> treeId, L listener) {
+            registerDataTreeChangeListener(final DataTreeIdentifier<T> treeId, final L listener) {
         return delegate().registerDataTreeChangeListener(treeId, listener);
     }
 
     @Override
-    public BindingTransactionChain createTransactionChain(TransactionChainListener listener) {
+    public TransactionChain createTransactionChain(final TransactionChainListener listener) {
         return delegate().createTransactionChain(listener);
     }
 
diff --git a/binding2/mdsal-binding2-api/src/main/java/org/opendaylight/mdsal/binding/javav2/api/BindingTransactionChain.java b/binding2/mdsal-binding2-api/src/main/java/org/opendaylight/mdsal/binding/javav2/api/BindingTransactionChain.java
deleted file mode 100644 (file)
index 9399f24..0000000
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Copyright (c) 2017 Pantheon Technologies s.r.o. 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.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.TransactionChain;
-
-/**
- * A chain of transactions.
- *
- * <p>
- * For more information about transaction chaining and transaction chains
- * see {@link TransactionChain}.
- *
- * @see TransactionChain
- *
- */
-@Beta
-public interface BindingTransactionChain extends TransactionFactory, TransactionChain<InstanceIdentifier<?>, TreeNode> {
-
-    @Override
-    ReadTransaction newReadOnlyTransaction();
-
-    @Override
-    WriteTransaction newWriteOnlyTransaction();
-}
index 3f480ddfef4e637624cdc1515aeb791b824e0bf9..713d13099189c73d63cee6d3191b31134a2ea5ef 100644 (file)
@@ -11,23 +11,47 @@ 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;
-import org.opendaylight.mdsal.common.api.TransactionChainFactory;
-import org.opendaylight.mdsal.common.api.TransactionChainListener;
 
 /**
  * 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.
  *
  * <p>
- * For more information on usage, please see the documentation in {@link AsyncDataBroker}.
+ * 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()}
+ * </ul>
  *
- * @see AsyncDataBroker
- * @see TransactionChainFactory
+ * <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 ReadTransaction}, {@link WriteTransaction}
+ * and {@link WriteTransaction#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.
  */
 @Beta
 public interface DataBroker extends AsyncDataBroker<InstanceIdentifier<?>, TreeNode>, BindingService,
-        TransactionFactory, DataTreeService, TransactionChainFactory<InstanceIdentifier<?>, TreeNode> {
-
-    @Override
-    BindingTransactionChain createTransactionChain(TransactionChainListener listener);
+        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.
+     *
+     * @param listener Transaction chain event listener
+     * @return A new transaction chain.
+     */
+    TransactionChain createTransactionChain(TransactionChainListener listener);
 }
index ec79e93a217a0a754ea41b270bd5a4f4558c9c2c..34c786c7ac0dc94708b580132fb55ef2f923db23 100644 (file)
@@ -24,7 +24,7 @@ import org.opendaylight.mdsal.common.api.ReadFailedException;
  *  {@link org.opendaylight.mdsal.common.api.AsyncReadTransaction}.
  */
 @Beta
-public interface ReadTransaction extends AsyncReadTransaction<InstanceIdentifier<?>, TreeNode> {
+public interface ReadTransaction extends Transaction, AsyncReadTransaction<InstanceIdentifier<?>, TreeNode> {
 
     /**
      * Reads data from the provided logical data store located at the provided path.
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
new file mode 100644 (file)
index 0000000..99b2000
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2018 Pantheon Technologies, s.r.o. 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.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;
+
+/**
+ * A common parent for all transactions which operate on a conceptual data tree.
+ * See derived transaction types for more concrete behavior:
+ * <ul>
+ * <li>{@link ReadTransaction} - Read capabilities, user is able to read data from data tree</li>
+ * <li>{@link WriteTransaction} - Write capabilities, user is able to propose changes to data tree</li>
+ * </ul>
+ *
+ * <b>Implementation Note:</b> This interface is not intended to be implemented by users of MD-SAL.
+ */
+public interface Transaction extends AsyncTransaction<InstanceIdentifier<?>, TreeNode> {
+    @Override
+    Object getIdentifier();
+}
diff --git a/binding2/mdsal-binding2-api/src/main/java/org/opendaylight/mdsal/binding/javav2/api/TransactionChain.java b/binding2/mdsal-binding2-api/src/main/java/org/opendaylight/mdsal/binding/javav2/api/TransactionChain.java
new file mode 100644 (file)
index 0000000..7c94a9a
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies s.r.o. 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.binding.javav2.api;
+
+import com.google.common.annotations.Beta;
+import org.opendaylight.yangtools.concepts.Registration;
+
+/**
+ * A chain of transactions. Transactions in a chain need to be committed in sequence and each transaction should see
+ * the effects of previous committed transactions as they occurred. A chain makes no guarantees of atomicity across
+ * the chained transactions - the transactions are committed as soon as possible in the order that they were committed.
+ * This behaviour is different from the default AsyncDataBroker, where a transaction is always created from the current
+ * global state, not taking into account any transactions previously committed by the calling thread. Due to
+ * the asynchronous nature of transaction submission this can lead to surprising results. If a thread executes
+ * the following sequence sufficiently quickly:
+ *
+ * <code>
+ * AsyncWriteTransaction t1 = broker.newWriteOnlyTransaction();
+ * t1.put(id, data);
+ * t1.commit();
+ *
+ * AsyncReadTransaction t2 = broker.newReadOnlyTransaction();
+ * Optional&lt;?&gt; maybeData = t2.read(id).get();
+ * </code>
+ * it may happen, that it sees maybeData.isPresent() == false, simply because t1 has not completed the processes
+ * of being applied and t2 is actually allocated from the previous state. This is obviously bad for users who create
+ * incremental state in the datastore and actually read what they write in subsequent transactions. Using
+ * a TransactionChain instead of a broker solves this particular problem, and leads to expected behavior: t2 will always
+ * see the data written in t1 present.
+ */
+@Beta
+public interface TransactionChain extends Registration, TransactionFactory {
+    /**
+     * Create a new read only transaction which will continue the chain.
+     *
+     * <p>
+     * The previous write transaction has to be either SUBMITTED ({@link WriteTransaction#commit commit} was invoked)
+     * or CANCELLED ({@link #close close} was invoked).
+     *
+     * <p>
+     * The returned read-only transaction presents an isolated view of the data if the previous write transaction was
+     * successful - in other words, this read-only transaction will see the state changes made by the previous write
+     * transaction in the chain. However, state which was introduced by other transactions outside this transaction
+     * chain after creation of the previous transaction is not visible.
+     *
+     * @return New transaction in the chain.
+     * @throws IllegalStateException if the previous transaction was not SUBMITTED or CANCELLED.
+     * @throws TransactionChainClosedException if the chain has been closed.
+     */
+    @Override
+    ReadTransaction newReadOnlyTransaction();
+
+    /**
+     * Create a new write-only transaction which will continue the chain.
+     *
+     * <p>
+     * The previous write transaction has to be either SUBMITTED ({@link WriteTransaction#commit commit} was invoked)
+     * or CANCELLED ({@link #close close} was invoked)
+     *
+     * <p>
+     * The returned write-only transaction presents an isolated view of the data if the previous write transaction was
+     * successful - in other words, this write-only transaction will see the state changes made by the previous write
+     * transaction in the chain. However, state which was introduced by other transactions outside this transaction
+     * chain after creation of the previous transaction is not visible
+     *
+     * <p>
+     * Committing this write-only transaction using {@link WriteTransaction#commit commit} will commit the state
+     * changes in this transaction to be visible to any subsequent transaction in this chain and also to any transaction
+     * outside this chain.
+     *
+     * @return New transaction in the chain.
+     * @throws IllegalStateException if the previous transaction was not SUBMITTED or CANCELLED.
+     * @throws TransactionChainClosedException if the chain has been closed.
+     */
+    @Override
+    WriteTransaction newWriteOnlyTransaction();
+}
diff --git a/binding2/mdsal-binding2-api/src/main/java/org/opendaylight/mdsal/binding/javav2/api/TransactionChainClosedException.java b/binding2/mdsal-binding2-api/src/main/java/org/opendaylight/mdsal/binding/javav2/api/TransactionChainClosedException.java
new file mode 100644 (file)
index 0000000..eeffac5
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2018 Pantheon Technologies, s.r.o. 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.binding.javav2.api;
+
+/**
+ * Exception thrown when an attempt is made to open a new transaction in a closed chain.
+ */
+public final class TransactionChainClosedException extends IllegalStateException {
+    private static final long serialVersionUID = 1L;
+
+    public TransactionChainClosedException(final String message) {
+        super(message);
+    }
+
+    public TransactionChainClosedException(final String message, final Throwable cause) {
+        super(message, cause);
+    }
+}
diff --git a/binding2/mdsal-binding2-api/src/main/java/org/opendaylight/mdsal/binding/javav2/api/TransactionChainListener.java b/binding2/mdsal-binding2-api/src/main/java/org/opendaylight/mdsal/binding/javav2/api/TransactionChainListener.java
new file mode 100644 (file)
index 0000000..e516213
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * 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.binding.javav2.api;
+
+import java.util.EventListener;
+
+/**
+ * Listener for transaction chain events.
+ */
+// FIXME: 4.0.0: remove this in favor of a TransactionChain destiny, available as a FluentFuture from TransactionChain
+public interface TransactionChainListener extends EventListener {
+    /**
+     * Invoked if when a transaction in the chain fails. All transactions submitted after the failed transaction, in the
+     * chain, are automatically cancelled by the time this notification is invoked. Open transactions need to be closed
+     * or cancelled.
+     * Implementations should invoke chain.close() to close the chain.
+     *
+     * @param chain Transaction chain which failed
+     * @param transaction Transaction which caused the chain to fail
+     * @param cause The cause of transaction failure
+     */
+    void onTransactionChainFailed(TransactionChain chain, Transaction transaction, Throwable cause);
+
+    /**
+     * Invoked when a transaction chain is completed. A transaction chain is considered completed when it has been
+     * closed and all its instructions have completed successfully.
+     *
+     * @param chain Transaction chain which completed
+     */
+    void onTransactionChainSuccessful(TransactionChain chain);
+}
+
index d3782daf6bc6d75c6428d2ca83442928b4430c2f..10e582ad3da2b0320cc282108c588c8b4ef5bec1 100644 (file)
@@ -5,7 +5,6 @@
  * 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;
@@ -22,10 +21,71 @@ import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
  * A transaction that provides mutation capabilities on a data tree.
  *
  * <p>
- * 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.
+ *
+ * <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 make changes to the local data tree in the transaction by via the <b>put</b>, <b>merge</b>,
+ * and <b>delete</b> operations.
+ *
+ * <h2>Put operation</h2>
+ * 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.
+ *
+ * <p>
+ * 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>
+ * <h2>Merge operation</h2>
+ * 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.
+ *
+ * <p>
+ * 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.
+ *
+ * <h2>Delete operation</h2>
+ * Removes a piece of data from a specified path.
+ *
+ * <p>
+ * 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.
+ *
+ * <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.
  */
 @Beta
-public interface WriteTransaction extends AsyncWriteTransaction<InstanceIdentifier<?>, TreeNode> {
+public interface WriteTransaction extends AsyncWriteTransaction<InstanceIdentifier<?>, TreeNode>, Transaction {
     @Override
     boolean cancel();
 
index 1857c67c4f4c77e3e5132c60983f7a246dce0df2..ba5c28b2e0438614f37a94c85dd92303005f9afa 100644 (file)
@@ -13,7 +13,6 @@ import com.google.common.collect.ImmutableSet;
 import java.util.Collection;
 import java.util.Set;
 import javax.annotation.Nonnull;
-import org.opendaylight.mdsal.binding.javav2.api.BindingTransactionChain;
 import org.opendaylight.mdsal.binding.javav2.api.DataBroker;
 import org.opendaylight.mdsal.binding.javav2.api.DataTreeIdentifier;
 import org.opendaylight.mdsal.binding.javav2.api.DataTreeListener;
@@ -21,6 +20,8 @@ import org.opendaylight.mdsal.binding.javav2.api.DataTreeLoopException;
 import org.opendaylight.mdsal.binding.javav2.api.DataTreeProducer;
 import org.opendaylight.mdsal.binding.javav2.api.DataTreeService;
 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.dom.adapter.impl.data.tree.BindingDOMDataTreeServiceAdapter;
 import org.opendaylight.mdsal.binding.javav2.dom.adapter.impl.transaction.BindingDOMReadTransactionAdapter;
@@ -33,7 +34,6 @@ import org.opendaylight.mdsal.binding.javav2.dom.codec.impl.BindingToNormalizedN
 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.TransactionChainListener;
 import org.opendaylight.mdsal.dom.api.DOMDataBroker;
 import org.opendaylight.mdsal.dom.api.DOMDataTreeService;
 import org.opendaylight.mdsal.dom.api.DOMService;
@@ -85,7 +85,7 @@ public class BindingDOMDataBrokerAdapter extends AbstractForwardedDataBroker imp
     }
 
     @Override
-    public BindingTransactionChain createTransactionChain(final TransactionChainListener listener) {
+    public TransactionChain createTransactionChain(final TransactionChainListener listener) {
         return new BindingDOMTransactionChainAdapter(getDelegate(), getCodec(), listener);
     }
 
index b4516ac7feac5ed47b022f08ce1f354f030e7ec6..e02d2f2624440a86377a01ec52fcff13e36fef4c 100644 (file)
@@ -7,27 +7,31 @@
  */
 package org.opendaylight.mdsal.binding.javav2.dom.adapter.impl.transaction;
 
+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.FutureCallback;
 import com.google.common.util.concurrent.MoreExecutors;
+import java.util.function.Supplier;
 import org.eclipse.jdt.annotation.NonNull;
-import org.opendaylight.mdsal.binding.javav2.api.BindingTransactionChain;
 import org.opendaylight.mdsal.binding.javav2.api.ReadTransaction;
+import org.opendaylight.mdsal.binding.javav2.api.TransactionChain;
+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.AsyncTransaction;
 import org.opendaylight.mdsal.common.api.CommitInfo;
-import org.opendaylight.mdsal.common.api.TransactionChain;
-import org.opendaylight.mdsal.common.api.TransactionChainListener;
 import org.opendaylight.mdsal.dom.api.DOMDataBroker;
-import org.opendaylight.mdsal.dom.api.DOMDataTreeReadTransaction;
+import org.opendaylight.mdsal.dom.api.DOMDataTreeTransaction;
 import org.opendaylight.mdsal.dom.api.DOMDataTreeWriteTransaction;
 import org.opendaylight.mdsal.dom.api.DOMTransactionChain;
+import org.opendaylight.mdsal.dom.api.DOMTransactionChainClosedException;
+import org.opendaylight.mdsal.dom.api.DOMTransactionChainListener;
 import org.opendaylight.yangtools.concepts.Delegator;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -36,8 +40,7 @@ import org.slf4j.LoggerFactory;
  * Transaction chain adapter.
  */
 @Beta
-public final class BindingDOMTransactionChainAdapter
-        implements BindingTransactionChain, Delegator<DOMTransactionChain> {
+public final class BindingDOMTransactionChainAdapter implements TransactionChain, Delegator<DOMTransactionChain> {
 
     private static final Logger LOG = LoggerFactory.getLogger(BindingDOMTransactionChainAdapter.class);
 
@@ -47,7 +50,7 @@ public final class BindingDOMTransactionChainAdapter
 
     public BindingDOMTransactionChainAdapter(final DOMDataBroker chainFactory, final BindingToNormalizedNodeCodec codec,
             final TransactionChainListener listener) {
-        Preconditions.checkNotNull(chainFactory, "DOM Transaction chain factory must not be null");
+        requireNonNull(chainFactory, "DOM Transaction chain factory must not be null");
         this.bindingListener = listener;
         this.delegate = chainFactory.createTransactionChain(new DelegateChainListener());
         this.codec = codec;
@@ -60,13 +63,12 @@ public final class BindingDOMTransactionChainAdapter
 
     @Override
     public ReadTransaction newReadOnlyTransaction() {
-        final DOMDataTreeReadTransaction delegateTx = delegate.newReadOnlyTransaction();
-        return new BindingDOMReadTransactionAdapter(delegateTx, codec);
+        return new BindingDOMReadTransactionAdapter(createTransaction(delegate::newReadOnlyTransaction), codec);
     }
 
     @Override
     public WriteTransaction newWriteOnlyTransaction() {
-        final DOMDataTreeWriteTransaction delegateTx = delegate.newWriteOnlyTransaction();
+        final DOMDataTreeWriteTransaction delegateTx = createTransaction(delegate::newWriteOnlyTransaction);
         return new BindingDOMWriteTransactionAdapter<DOMDataTreeWriteTransaction>(delegateTx, codec) {
 
             @Override
@@ -113,13 +115,19 @@ public final class BindingDOMTransactionChainAdapter
         delegate.close();
     }
 
-    private final class DelegateChainListener implements TransactionChainListener {
+    private static <T> T createTransaction(final Supplier<T> supplier) {
+        try {
+            return supplier.get();
+        } catch (DOMTransactionChainClosedException e) {
+            throw new TransactionChainClosedException("Transaction chain already closed", e);
+        }
+    }
 
+    private final class DelegateChainListener implements DOMTransactionChainListener {
         @Override
-        public void onTransactionChainFailed(final TransactionChain<?, ?> chain,
-                final AsyncTransaction<?, ?> transaction, final Throwable cause) {
-            Preconditions.checkState(delegate.equals(chain),
-                    "Illegal state - listener for %s was invoked for incorrect chain %s.", delegate, chain);
+        public void onTransactionChainFailed(final DOMTransactionChain chain,
+                final DOMDataTreeTransaction transaction, final Throwable cause) {
+            checkState(delegate.equals(chain), "Listener for %s was invoked for incorrect chain %s.", delegate, chain);
             /*
              * Intentionally NOOP, callback for failure, since we are also listening on each transaction
              * future for failure, in order to have reference to Binding Transaction (which was seen by client
@@ -131,9 +139,8 @@ public final class BindingDOMTransactionChainAdapter
         }
 
         @Override
-        public void onTransactionChainSuccessful(final TransactionChain<?, ?> chain) {
-            Preconditions.checkState(delegate.equals(chain),
-                    "Illegal state - listener for %s was invoked for incorrect chain %s.", delegate, chain);
+        public void onTransactionChainSuccessful(final DOMTransactionChain chain) {
+            checkState(delegate.equals(chain), "Listener for %s was invoked for incorrect chain %s.", delegate, chain);
             bindingListener.onTransactionChainSuccessful(BindingDOMTransactionChainAdapter.this);
         }
     }
index bd832811e145909a2af96fd1ad5652db2e266436..77622fd9fc2cc27aeef7fa0d7d73ad94ad012ebf 100644 (file)
@@ -10,18 +10,18 @@ package org.opendaylight.mdsal.binding.javav2.spi;
 import com.google.common.collect.ForwardingObject;
 import java.util.Collection;
 import javax.annotation.Nonnull;
-import org.opendaylight.mdsal.binding.javav2.api.BindingTransactionChain;
 import org.opendaylight.mdsal.binding.javav2.api.DataBroker;
 import org.opendaylight.mdsal.binding.javav2.api.DataTreeIdentifier;
 import org.opendaylight.mdsal.binding.javav2.api.DataTreeListener;
 import org.opendaylight.mdsal.binding.javav2.api.DataTreeLoopException;
 import org.opendaylight.mdsal.binding.javav2.api.DataTreeProducer;
 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.mdsal.common.api.TransactionChainListener;
 import org.opendaylight.yangtools.concepts.ListenerRegistration;
 
 /**
@@ -50,19 +50,19 @@ public abstract class ForwardingDataBroker extends ForwardingObject implements D
 
     @Nonnull
     @Override
-    public <T extends DataTreeListener> ListenerRegistration<T> registerListener(@Nonnull T listener,
-            @Nonnull Collection<DataTreeIdentifier<?>> subtrees, boolean allowRxMerges,
-            @Nonnull Collection<DataTreeProducer> producers) throws DataTreeLoopException {
+    public <T extends DataTreeListener> ListenerRegistration<T> registerListener(@Nonnull final T listener,
+            @Nonnull final Collection<DataTreeIdentifier<?>> subtrees, final boolean allowRxMerges,
+            @Nonnull final Collection<DataTreeProducer> producers) throws DataTreeLoopException {
         return delegate().registerListener(listener, subtrees, allowRxMerges, producers);
     }
 
     @Override
-    public DataTreeProducer createProducer(Collection<DataTreeIdentifier<?>> subtrees) {
+    public DataTreeProducer createProducer(final Collection<DataTreeIdentifier<?>> subtrees) {
         return delegate().createProducer(subtrees);
     }
 
     @Override
-    public BindingTransactionChain createTransactionChain(TransactionChainListener listener) {
+    public TransactionChain createTransactionChain(final TransactionChainListener listener) {
         return delegate().createTransactionChain(listener);
     }
 
index ed3dee913147e2d66e23d2f1893a585dba1d408e..b6d16934e5a47ab10104f1d70cf15bc4b14d6291 100644 (file)
@@ -17,7 +17,6 @@ import org.opendaylight.yangtools.concepts.Path;
  * which introduces additional semantics to allocated transactions.
  * <ul>
  * <li> {@link AsyncDataBroker}
- * <li> {@link TransactionChain}
  * </ul>
  *
  * <p>
@@ -48,7 +47,6 @@ import org.opendaylight.yangtools.concepts.Path;
  * 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
index c9456a0eb0aa949fedd66869830acdb70ed3261c..2da17bd6f42831e3cfb4b455338688fd2b79428a 100644 (file)
@@ -135,7 +135,6 @@ public interface AsyncWriteTransaction<P extends Path<P>, D> extends AsyncTransa
      * The effects of a successful commit of data depends on listeners and commit participants that are registered with
      * the data broker.
      *
-     * <p>
      * <h3>Example usage:</h3>
      * <pre>
      *  private void doWrite(final int tries) {
diff --git a/common/mdsal-common-api/src/main/java/org/opendaylight/mdsal/common/api/TransactionChain.java b/common/mdsal-common-api/src/main/java/org/opendaylight/mdsal/common/api/TransactionChain.java
deleted file mode 100644 (file)
index d68edc1..0000000
+++ /dev/null
@@ -1,127 +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 chain of transactions. Transactions in a chain need to be committed in
- * sequence and each transaction should see the effects of previous committed transactions
- * as they occurred. A chain makes no guarantees of atomicity across the chained transactions -
- * the transactions are committed as soon as possible in the order that they were committed.
- * This behaviour is different from the default AsyncDataBroker, where a
- * transaction is always created from the current global state, not taking into
- * account any transactions previously committed by the calling thread. Due to
- * the asynchronous nature of transaction submission this can lead to surprising
- * results. If a thread executes the following sequence sufficiently quickly:
- *
- * <pre><code>
- * AsyncWriteTransaction t1 = broker.newWriteOnlyTransaction();
- * t1.put(id, data);
- * t1.commit();
- *
- * AsyncReadTransaction t2 = broker.newReadOnlyTransaction();
- * Optional&lt;?&gt; maybeData = t2.read(id).get();
- * </code></pre>
- * it may happen, that it sees maybeData.isPresent() == false, simply because
- * t1 has not completed the processes of being applied and t2 is actually
- * allocated from the previous state. This is obviously bad for users who create
- * incremental state in the datastore and actually read what they write in
- * subsequent transactions.
- * Using a TransactionChain instead of a broker solves this particular problem,
- * and leads to expected behavior: t2 will always see the data written in t1
- * present.
- */
-public interface TransactionChain<P extends Path<P>, D> extends AutoCloseable,
-        AsyncDataTransactionFactory<P, D> {
-
-    /**
-     * Create a new read only transaction which will continue the chain.
-     *
-     * <p>
-     * The previous write transaction has to be either SUBMITTED
-     * ({@link AsyncWriteTransaction#commit commit} was invoked) or CANCELLED
-     * ({@link #close close} was invoked).
-     *
-     * <p>
-     * The returned read-only transaction presents an isolated view of the data if the previous
-     * write transaction was successful - in other words, this read-only transaction will see the
-     * state changes made by the previous write transaction in the chain. However, state which
-     * was introduced by other transactions outside this transaction chain after creation of
-     * the previous transaction is not visible.
-     *
-     * @return New transaction in the chain.
-     * @throws IllegalStateException
-     *             if the previous transaction was not SUBMITTED or CANCELLED.
-     * @throws TransactionChainClosedException
-     *             if the chain has been closed.
-     */
-    @Override
-    AsyncReadTransaction<P, D> newReadOnlyTransaction();
-
-    /**
-     * Create a new write-only transaction which will continue the chain.
-     *
-     * <p>
-     * The previous write transaction has to be either SUBMITTED
-     * ({@link AsyncWriteTransaction#commit commit} was invoked) or CANCELLED
-     * ({@link #close close} was invoked)
-     *
-     * <p>
-     * The returned write-only transaction presents an isolated view of the data if the previous
-     * write transaction was successful - in other words, this write-only transaction will see the
-     * state changes made by the previous write transaction in the chain. However, state which
-     * was introduced by other transactions outside this transaction chain after creation of
-     * the previous transaction is not visible
-     *
-     * <p>
-     * Committing this write-only transaction using {@link AsyncWriteTransaction#commit commit}
-     * will commit the state changes in this transaction to be visible to any subsequent
-     * transaction in this chain and also to any transaction outside this chain.
-     *
-     * @return New transaction in the chain.
-     * @throws IllegalStateException
-     *             if the previous transaction was not SUBMITTED or CANCELLED.
-     * @throws TransactionChainClosedException
-     *             if the chain has been closed.
-     */
-    @Override
-    AsyncWriteTransaction<P, D> newWriteOnlyTransaction();
-
-    /**
-     * Create a new read-write transaction which will continue the chain.
-     *
-     * <p>
-     * The previous write transaction has to be either SUBMITTED
-     * ({@link AsyncWriteTransaction#commit commit} was invoked) or CANCELLED
-     * ({@link #close close} was invoked).
-     *
-     * <p>
-     * The returned read-write transaction presents an isolated view of the data if the previous
-     * write transaction was successful - in other words, this read-write transaction will see the
-     * state changes made by the previous write transaction in the chain. However, state which
-     * was introduced by other transactions outside this transaction chain after creation of
-     * the previous transaction is not visible.
-     *
-     * <p>
-     * Committing this read-write transaction using {@link AsyncWriteTransaction#commit commit}
-     * will commit the state changes in this transaction to be visible to any subsequent
-     * transaction in this chain and also to any transaction outside this chain.
-     *
-     * @return New transaction in the chain.
-     * @throws IllegalStateException
-     *             if the previous transaction was not SUBMITTED or CANCELLED.
-     * @throws TransactionChainClosedException
-     *             if the chain has been closed.
-     */
-    @Override
-    AsyncReadWriteTransaction<P, D> newReadWriteTransaction();
-
-    @Override
-    void close();
-}
diff --git a/common/mdsal-common-api/src/main/java/org/opendaylight/mdsal/common/api/TransactionChainFactory.java b/common/mdsal-common-api/src/main/java/org/opendaylight/mdsal/common/api/TransactionChainFactory.java
deleted file mode 100644 (file)
index b6ee7b8..0000000
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright (c) 2013 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;
-
-/**
- * Interface for creating transaction chains.
- *
- * @deprecated This interface is deprecated and is integrated into AsyncDataBroker replacements.
- */
-@Deprecated
-public interface TransactionChainFactory<P extends Path<P>, D> {
-
-    /**
-     * 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.
-     *
-     * @param listener Transaction chain event listener
-     * @return A new transaction chain.
-     */
-    TransactionChain<P, D> createTransactionChain(TransactionChainListener listener);
-}
-
index 962c54e3539fc7730f2630b40f8ef36902280911..34b4c517bd3ce8fe287f185e4f0d64b442a6d230 100644 (file)
@@ -8,8 +8,6 @@
 package org.opendaylight.mdsal.dom.api;
 
 import org.opendaylight.mdsal.common.api.AsyncDataBroker;
-import org.opendaylight.mdsal.common.api.TransactionChainFactory;
-import org.opendaylight.mdsal.common.api.TransactionChainListener;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
 
@@ -46,9 +44,13 @@ import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
  */
 public interface DOMDataBroker extends
         AsyncDataBroker<YangInstanceIdentifier, NormalizedNode<?, ?>>, DOMTransactionFactory,
-        TransactionChainFactory<YangInstanceIdentifier, NormalizedNode<?, ?>>,
         DOMExtensibleService<DOMDataBroker, DOMDataBrokerExtension> {
-
-    @Override
-    DOMTransactionChain createTransactionChain(TransactionChainListener listener);
+    /**
+     * 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.
+     *
+     * @param listener Transaction chain event listener
+     * @return A new transaction chain.
+     */
+    DOMTransactionChain createTransactionChain(DOMTransactionChainListener listener);
 }
index c8d2264755945731280e57f6a05734773ce0619d..f811a6078bf1d3ecbd45f3d533d53f70083cf889 100644 (file)
  */
 package org.opendaylight.mdsal.dom.api;
 
-import org.opendaylight.mdsal.common.api.TransactionChain;
-import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
-import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.concepts.Registration;
 
 /**
- * A chain of DOM Data transactions.
- * Transactions in a chain need to be committed in sequence and each
- * transaction should see the effects of previous transactions as if they happened. A chain
- * makes no guarantees of atomicity, in fact transactions are committed as soon as possible.
+ * A chain of transactions. Transactions in a chain need to be committed in sequence and each transaction should see
+ * the effects of previous committed transactions as they occurred. A chain makes no guarantees of atomicity across
+ * the chained transactions - the transactions are committed as soon as possible in the order that they were committed.
+ * This behaviour is different from the default AsyncDataBroker, where a transaction is always created from the current
+ * global state, not taking into account any transactions previously committed by the calling thread. Due to
+ * the asynchronous nature of transaction submission this can lead to surprising results. If a thread executes
+ * the following sequence sufficiently quickly:
  *
- * <p>
- * This interface is type capture of {@link TransactionChain} for DOM Data Contracts.
+ * <code>
+ * DOMWriteTransaction t1 = broker.newWriteOnlyTransaction();
+ * t1.put(id, data);
+ * t1.commit();
+ *
+ * DOMReadTransaction t2 = broker.newReadOnlyTransaction();
+ * Optional&lt;?&gt; maybeData = t2.read(id).get();
+ * </code>
+ * it may happen, that it sees maybeData.isPresent() == false, simply because t1 has not completed the processes
+ * of being applied and t2 is actually allocated from the previous state. This is obviously bad for users who create
+ * incremental state in the datastore and actually read what they write in subsequent transactions.
+ * Using a TransactionChain instead of a broker solves this particular problem, and leads to expected behavior: t2 will
+ * always see the data written in t1
+ * present.
  */
-public interface DOMTransactionChain extends TransactionChain<YangInstanceIdentifier, NormalizedNode<?, ?>> {
-
+public interface DOMTransactionChain extends Registration, DOMTransactionFactory {
+    /**
+     * Create a new read only transaction which will continue the chain.
+     *
+     * <p>
+     * The previous write transaction has to be either SUBMITTED ({@link DOMDataTreeWriteTransaction#commit commit} was
+     * invoked) or CANCELLED ({@link #close close} was invoked).
+     *
+     * <p>
+     * The returned read-only transaction presents an isolated view of the data if the previous write transaction was
+     * successful - in other words, this read-only transaction will see the state changes made by the previous write
+     * transaction in the chain. However, state which was introduced by other transactions outside this transaction
+     * chain after creation of the previous transaction is not visible.
+     *
+     * @return New transaction in the chain.
+     * @throws IllegalStateException if the previous transaction was not SUBMITTED or CANCELLED.
+     * @throws DOMTransactionChainClosedException if the chain has been closed.
+     */
     @Override
     DOMDataTreeReadTransaction newReadOnlyTransaction();
 
+    /**
+     * Create a new write-only transaction which will continue the chain.
+     *
+     * <p>
+     * The previous write transaction has to be either SUBMITTED ({@link DOMDataTreeWriteTransaction#commit commit} was
+     * invoked) or CANCELLED ({@link #close close} was invoked)
+     *
+     * <p>
+     * The returned write-only transaction presents an isolated view of the data if the previous write transaction was
+     * successful - in other words, this write-only transaction will see the state changes made by the previous write
+     * transaction in the chain. However, state which was introduced by other transactions outside this transaction
+     * chain after creation of the previous transaction is not visible
+     *
+     * <p>
+     * Committing this write-only transaction using {@link DOMDataTreeWriteTransaction#commit commit} will commit
+     * the state changes in this transaction to be visible to any subsequent transaction in this chain and also to any
+     * transaction outside this chain.
+     *
+     * @return New transaction in the chain.
+     * @throws IllegalStateException if the previous transaction was not SUBMITTED or CANCELLED.
+     * @throws DOMTransactionChainClosedException if the chain has been closed.
+     */
     @Override
     DOMDataTreeWriteTransaction newWriteOnlyTransaction();
 
+    /**
+     * Create a new read-write transaction which will continue the chain.
+     *
+     * <p>
+     * The previous write transaction has to be either SUBMITTED ({@link DOMDataTreeWriteTransaction#commit commit} was
+     * invoked) or CANCELLED ({@link #close close} was invoked).
+     *
+     * <p>
+     * The returned read-write transaction presents an isolated view of the data if the previous write transaction was
+     * successful - in other words, this read-write transaction will see the state changes made by the previous write
+     * transaction in the chain. However, state which was introduced by other transactions outside this transaction
+     * chain after creation of the previous transaction is not visible.
+     *
+     * <p>
+     * Committing this read-write transaction using {@link DOMDataTreeReadWriteTransaction#commit commit} will commit
+     * the state changes in this transaction to be visible to any subsequent transaction in this chain and also to any
+     * transaction outside this chain.
+     *
+     * @return New transaction in the chain.
+     * @throws IllegalStateException if the previous transaction was not SUBMITTED or CANCELLED.
+     * @throws DOMTransactionChainClosedException if the chain has been closed.
+     */
     @Override
     DOMDataTreeReadWriteTransaction newReadWriteTransaction();
 }
diff --git a/dom/mdsal-dom-api/src/main/java/org/opendaylight/mdsal/dom/api/DOMTransactionChainClosedException.java b/dom/mdsal-dom-api/src/main/java/org/opendaylight/mdsal/dom/api/DOMTransactionChainClosedException.java
new file mode 100644 (file)
index 0000000..c918e17
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2018 Pantheon Technologies, s.r.o. 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.dom.api;
+
+/**
+ * Exception thrown when an attempt is made to open a new transaction in a closed chain.
+ */
+public final class DOMTransactionChainClosedException extends IllegalStateException {
+    private static final long serialVersionUID = 1L;
+
+    public DOMTransactionChainClosedException(final String message) {
+        super(message);
+    }
+
+    public DOMTransactionChainClosedException(final String message, final Throwable cause) {
+        super(message, cause);
+    }
+}
diff --git a/dom/mdsal-dom-api/src/main/java/org/opendaylight/mdsal/dom/api/DOMTransactionChainListener.java b/dom/mdsal-dom-api/src/main/java/org/opendaylight/mdsal/dom/api/DOMTransactionChainListener.java
new file mode 100644 (file)
index 0000000..246352a
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2018 Pantheon Technologies, s.r.o. 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.dom.api;
+
+import java.util.EventListener;
+
+/**
+ * Listener for transaction chain events.
+ */
+// FIXME: 4.0.0: remove this in favor of a TransactionChain destiny, available as a FluentFuture from
+//               DOMTransactionChain
+public interface DOMTransactionChainListener extends EventListener {
+    /**
+     * Invoked if when a transaction in the chain fails. All transactions submitted after the failed transaction, in the
+     * chain, are automatically cancelled by the time this notification is invoked. Open transactions need to be closed
+     * or cancelled.
+     * Implementations should invoke chain.close() to close the chain.
+     *
+     * @param chain Transaction chain which failed
+     * @param transaction Transaction which caused the chain to fail
+     * @param cause The cause of transaction failure
+     */
+    void onTransactionChainFailed(DOMTransactionChain chain, DOMDataTreeTransaction transaction, Throwable cause);
+
+    /**
+     * Invoked when a transaction chain is completed. A transaction chain is considered completed when it has been
+     * closed and all its instructions have completed successfully.
+     *
+     * @param chain Transaction chain which completed
+     */
+    void onTransactionChainSuccessful(DOMTransactionChain chain);
+}
+
index 1ef0c5ceefc2755396dffdc88e79057f6265639a..95ffcfc5a7dba6b9be1fe5e081da1569f10fb1f2 100644 (file)
@@ -15,13 +15,13 @@ import java.util.Map;
 import java.util.Map.Entry;
 import java.util.concurrent.atomic.AtomicLong;
 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
-import org.opendaylight.mdsal.common.api.TransactionChainListener;
 import org.opendaylight.mdsal.dom.api.DOMDataBroker;
 import org.opendaylight.mdsal.dom.api.DOMDataBrokerExtension;
 import org.opendaylight.mdsal.dom.api.DOMDataTreeChangeListener;
 import org.opendaylight.mdsal.dom.api.DOMDataTreeChangeService;
 import org.opendaylight.mdsal.dom.api.DOMDataTreeIdentifier;
 import org.opendaylight.mdsal.dom.api.DOMTransactionChain;
+import org.opendaylight.mdsal.dom.api.DOMTransactionChainListener;
 import org.opendaylight.mdsal.dom.spi.store.DOMStore;
 import org.opendaylight.mdsal.dom.spi.store.DOMStoreTransactionChain;
 import org.opendaylight.mdsal.dom.spi.store.DOMStoreTreeChangePublisher;
@@ -98,7 +98,7 @@ public abstract class AbstractDOMDataBroker extends AbstractDOMForwardedTransact
     }
 
     @Override
-    public DOMTransactionChain createTransactionChain(final TransactionChainListener listener) {
+    public DOMTransactionChain createTransactionChain(final DOMTransactionChainListener listener) {
         checkNotClosed();
 
         final Map<LogicalDatastoreType, DOMStoreTransactionChain> backingChains =
index 32c98be7971d624c2418b15a114aeb8acf4bb019..2ae90b997f57bbaa66e9cd00e0ce1c5c7d38e16c 100644 (file)
@@ -19,9 +19,9 @@ import java.util.concurrent.atomic.AtomicLong;
 import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
 import org.opendaylight.mdsal.common.api.CommitInfo;
 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
-import org.opendaylight.mdsal.common.api.TransactionChainListener;
 import org.opendaylight.mdsal.dom.api.DOMDataTreeWriteTransaction;
 import org.opendaylight.mdsal.dom.api.DOMTransactionChain;
+import org.opendaylight.mdsal.dom.api.DOMTransactionChainListener;
 import org.opendaylight.mdsal.dom.spi.store.DOMStoreThreePhaseCommitCohort;
 import org.opendaylight.mdsal.dom.spi.store.DOMStoreTransactionChain;
 import org.slf4j.Logger;
@@ -49,7 +49,7 @@ final class DOMDataBrokerTransactionChainImpl extends AbstractDOMForwardedTransa
     private static final Logger LOG = LoggerFactory.getLogger(DOMDataBrokerTransactionChainImpl.class);
     private final AtomicLong txNum = new AtomicLong();
     private final AbstractDOMDataBroker broker;
-    private final TransactionChainListener listener;
+    private final DOMTransactionChainListener listener;
     private final long chainId;
 
     private volatile State state = State.RUNNING;
@@ -73,7 +73,7 @@ final class DOMDataBrokerTransactionChainImpl extends AbstractDOMForwardedTransa
      */
     DOMDataBrokerTransactionChainImpl(final long chainId,
             final Map<LogicalDatastoreType, DOMStoreTransactionChain> chains,
-            final AbstractDOMDataBroker broker, final TransactionChainListener listener) {
+            final AbstractDOMDataBroker broker, final DOMTransactionChainListener listener) {
         super(chains);
         this.chainId = chainId;
         this.broker = Preconditions.checkNotNull(broker);
index 4c9b669598a341eae3edece0318b963f6d0a1f0b..35e6de9dcd9e531ba077d24e8fbbe67775b39f29 100644 (file)
@@ -10,7 +10,6 @@ package org.opendaylight.mdsal.dom.broker;
 import com.google.common.collect.ClassToInstanceMap;
 import com.google.common.collect.ImmutableClassToInstanceMap;
 import java.util.concurrent.atomic.AtomicLong;
-import org.opendaylight.mdsal.common.api.TransactionChainListener;
 import org.opendaylight.mdsal.dom.api.DOMDataBroker;
 import org.opendaylight.mdsal.dom.api.DOMDataBrokerExtension;
 import org.opendaylight.mdsal.dom.api.DOMDataTreeReadTransaction;
@@ -18,6 +17,7 @@ import org.opendaylight.mdsal.dom.api.DOMDataTreeReadWriteTransaction;
 import org.opendaylight.mdsal.dom.api.DOMDataTreeService;
 import org.opendaylight.mdsal.dom.api.DOMDataTreeWriteTransaction;
 import org.opendaylight.mdsal.dom.api.DOMTransactionChain;
+import org.opendaylight.mdsal.dom.api.DOMTransactionChainListener;
 
 public class ShardedDOMDataBrokerAdapter implements DOMDataBroker {
 
@@ -50,7 +50,7 @@ public class ShardedDOMDataBrokerAdapter implements DOMDataBroker {
     }
 
     @Override
-    public DOMTransactionChain createTransactionChain(final TransactionChainListener listener) {
+    public DOMTransactionChain createTransactionChain(final DOMTransactionChainListener listener) {
         return new ShardedDOMTransactionChainAdapter(newChainIdentifier(), service, listener);
     }
 
index e10c1e07c902fe9d1e55faa58fe7957f7ea81256..684e74d5ef81bf06f2c62df1367ea4581b204344 100644 (file)
@@ -20,10 +20,8 @@ import java.util.Map;
 import java.util.concurrent.atomic.AtomicLong;
 import javax.annotation.Nonnull;
 import javax.annotation.Nullable;
-import org.opendaylight.mdsal.common.api.AsyncTransaction;
 import org.opendaylight.mdsal.common.api.CommitInfo;
 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
-import org.opendaylight.mdsal.common.api.TransactionChainListener;
 import org.opendaylight.mdsal.dom.api.DOMDataTreeCursorAwareTransaction;
 import org.opendaylight.mdsal.dom.api.DOMDataTreeIdentifier;
 import org.opendaylight.mdsal.dom.api.DOMDataTreeListener;
@@ -34,8 +32,10 @@ import org.opendaylight.mdsal.dom.api.DOMDataTreeReadTransaction;
 import org.opendaylight.mdsal.dom.api.DOMDataTreeReadWriteTransaction;
 import org.opendaylight.mdsal.dom.api.DOMDataTreeService;
 import org.opendaylight.mdsal.dom.api.DOMDataTreeServiceExtension;
+import org.opendaylight.mdsal.dom.api.DOMDataTreeTransaction;
 import org.opendaylight.mdsal.dom.api.DOMDataTreeWriteTransaction;
 import org.opendaylight.mdsal.dom.api.DOMTransactionChain;
+import org.opendaylight.mdsal.dom.api.DOMTransactionChainListener;
 import org.opendaylight.yangtools.concepts.ListenerRegistration;
 
 public class ShardedDOMTransactionChainAdapter implements DOMTransactionChain {
@@ -43,7 +43,7 @@ public class ShardedDOMTransactionChainAdapter implements DOMTransactionChain {
     private final DOMDataTreeService dataTreeService;
     private final Object txChainIdentifier;
     private final AtomicLong txNum = new AtomicLong();
-    private final TransactionChainListener txChainListener;
+    private final DOMTransactionChainListener txChainListener;
     private final CachedDataTreeService cachedDataTreeService;
     private TransactionChainWriteTransaction writeTx;
     private TransactionChainReadTransaction readTx;
@@ -51,8 +51,7 @@ public class ShardedDOMTransactionChainAdapter implements DOMTransactionChain {
     private boolean finished = false;
 
     public ShardedDOMTransactionChainAdapter(final Object txChainIdentifier,
-                                             final DOMDataTreeService dataTreeService,
-                                             final TransactionChainListener txChainListener) {
+            final DOMDataTreeService dataTreeService, final DOMTransactionChainListener txChainListener) {
         Preconditions.checkNotNull(dataTreeService);
         Preconditions.checkNotNull(txChainIdentifier);
         this.dataTreeService = dataTreeService;
@@ -150,7 +149,7 @@ public class ShardedDOMTransactionChainAdapter implements DOMTransactionChain {
         Preconditions.checkState(!finished);
     }
 
-    public void transactionFailed(final AsyncTransaction<?, ?> tx, final Throwable cause) {
+    public void transactionFailed(final DOMDataTreeTransaction tx, final Throwable cause) {
         txChainListener.onTransactionChainFailed(this, tx, cause);
         if (writeTx != null) {
             writeTx.cancel();
index 093b3cb69c8a3448addaae527160000f53fff4ef..656d6ab14d886b377c021e275c7225b02d13e355 100644 (file)
@@ -9,11 +9,11 @@ package org.opendaylight.mdsal.dom.broker.pingpong;
 
 import com.google.common.base.Preconditions;
 import javax.annotation.Nonnull;
-import org.opendaylight.mdsal.common.api.TransactionChainListener;
 import org.opendaylight.mdsal.dom.api.DOMDataBroker;
 import org.opendaylight.mdsal.dom.api.DOMDataTreeChangeListener;
 import org.opendaylight.mdsal.dom.api.DOMDataTreeChangeService;
 import org.opendaylight.mdsal.dom.api.DOMDataTreeIdentifier;
+import org.opendaylight.mdsal.dom.api.DOMTransactionChainListener;
 import org.opendaylight.mdsal.dom.spi.ForwardingDOMDataBroker;
 import org.opendaylight.yangtools.concepts.ListenerRegistration;
 
@@ -45,7 +45,7 @@ public final class PingPongDataBroker extends ForwardingDOMDataBroker implements
     }
 
     @Override
-    public PingPongTransactionChain createTransactionChain(final TransactionChainListener listener) {
+    public PingPongTransactionChain createTransactionChain(final DOMTransactionChainListener listener) {
         return new PingPongTransactionChain(delegate, listener);
     }
 
index b11133a9faf7ee7f81755e4e4e7965bd6cb9a7a4..9db7bbc5b548ab316cb3b5c66a5e296a3b000660 100644 (file)
@@ -19,16 +19,15 @@ import java.util.concurrent.CancellationException;
 import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
 import javax.annotation.Nonnull;
 import javax.annotation.concurrent.GuardedBy;
-import org.opendaylight.mdsal.common.api.AsyncTransaction;
 import org.opendaylight.mdsal.common.api.CommitInfo;
 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
-import org.opendaylight.mdsal.common.api.TransactionChain;
-import org.opendaylight.mdsal.common.api.TransactionChainListener;
 import org.opendaylight.mdsal.dom.api.DOMDataBroker;
 import org.opendaylight.mdsal.dom.api.DOMDataTreeReadTransaction;
 import org.opendaylight.mdsal.dom.api.DOMDataTreeReadWriteTransaction;
+import org.opendaylight.mdsal.dom.api.DOMDataTreeTransaction;
 import org.opendaylight.mdsal.dom.api.DOMDataTreeWriteTransaction;
 import org.opendaylight.mdsal.dom.api.DOMTransactionChain;
+import org.opendaylight.mdsal.dom.api.DOMTransactionChainListener;
 import org.opendaylight.mdsal.dom.spi.ForwardingDOMDataReadWriteTransaction;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
@@ -55,7 +54,7 @@ import org.slf4j.LoggerFactory;
  */
 public final class PingPongTransactionChain implements DOMTransactionChain {
     private static final Logger LOG = LoggerFactory.getLogger(PingPongTransactionChain.class);
-    private final TransactionChainListener listener;
+    private final DOMTransactionChainListener listener;
     private final DOMTransactionChain delegate;
 
     @GuardedBy("this")
@@ -94,24 +93,24 @@ public final class PingPongTransactionChain implements DOMTransactionChain {
             .newUpdater(PingPongTransactionChain.class, PingPongTransaction.class, "inflightTx");
     private volatile PingPongTransaction inflightTx;
 
-    PingPongTransactionChain(final DOMDataBroker broker, final TransactionChainListener listener) {
+    PingPongTransactionChain(final DOMDataBroker broker, final DOMTransactionChainListener listener) {
         this.listener = Preconditions.checkNotNull(listener);
-        this.delegate = broker.createTransactionChain(new TransactionChainListener() {
+        this.delegate = broker.createTransactionChain(new DOMTransactionChainListener() {
             @Override
-            public void onTransactionChainFailed(final TransactionChain<?, ?> chain,
-                                                 final AsyncTransaction<?, ?> transaction, final Throwable cause) {
+            public void onTransactionChainFailed(final DOMTransactionChain chain,
+                    final DOMDataTreeTransaction transaction, final Throwable cause) {
                 LOG.debug("Transaction chain {} reported failure in {}", chain, transaction, cause);
                 delegateFailed(chain, cause);
             }
 
             @Override
-            public void onTransactionChainSuccessful(final TransactionChain<?, ?> chain) {
+            public void onTransactionChainSuccessful(final DOMTransactionChain chain) {
                 delegateSuccessful(chain);
             }
         });
     }
 
-    void delegateSuccessful(final TransactionChain<?, ?> chain) {
+    void delegateSuccessful(final DOMTransactionChain chain) {
         final Entry<PingPongTransaction, Throwable> canceled;
         synchronized (this) {
             // This looks weird, but we need not hold the lock while invoking callbacks
@@ -133,7 +132,7 @@ public final class PingPongTransactionChain implements DOMTransactionChain {
         tx.onFailure(cause);
     }
 
-    void delegateFailed(final TransactionChain<?, ?> chain, final Throwable cause) {
+    void delegateFailed(final DOMTransactionChain chain, final Throwable cause) {
 
         final DOMDataTreeReadWriteTransaction frontend;
         final PingPongTransaction tx = inflightTx;
index 1529d3073415f1578b8fc5a9050ad690f5882379..f7a620c704203edef99535066a89743dae2a43d6 100644 (file)
@@ -8,9 +8,9 @@
 package org.opendaylight.mdsal.dom.broker;
 
 import com.google.common.util.concurrent.SettableFuture;
-import org.opendaylight.mdsal.common.api.AsyncTransaction;
-import org.opendaylight.mdsal.common.api.TransactionChain;
-import org.opendaylight.mdsal.common.api.TransactionChainListener;
+import org.opendaylight.mdsal.dom.api.DOMDataTreeTransaction;
+import org.opendaylight.mdsal.dom.api.DOMTransactionChain;
+import org.opendaylight.mdsal.dom.api.DOMTransactionChainListener;
 
 /**
  * Simple implementation of {@link TransactionChainListener} for testing.
@@ -21,19 +21,19 @@ import org.opendaylight.mdsal.common.api.TransactionChainListener;
  * transaction chain event is retrieved.
  *
  */
-class BlockingTransactionChainListener implements TransactionChainListener {
+class BlockingTransactionChainListener implements DOMTransactionChainListener {
 
     private final SettableFuture<Throwable> failFuture = SettableFuture.create();
     private final SettableFuture<Void> successFuture = SettableFuture.create();
 
     @Override
-    public void onTransactionChainFailed(final TransactionChain<?, ?> chain, final AsyncTransaction<?, ?> transaction,
+    public void onTransactionChainFailed(final DOMTransactionChain chain, final DOMDataTreeTransaction transaction,
             final Throwable cause) {
         failFuture.set(cause);
     }
 
     @Override
-    public void onTransactionChainSuccessful(final TransactionChain<?, ?> chain) {
+    public void onTransactionChainSuccessful(final DOMTransactionChain chain) {
         successFuture.set(null);
     }
 
index 3101280ea2dbdc0ccc96e0745f464ff789498708..5e91d17cf214c895716525455c68567de7e032c2 100644 (file)
@@ -19,13 +19,13 @@ import static org.opendaylight.mdsal.common.api.LogicalDatastoreType.OPERATIONAL
 
 import org.junit.Test;
 import org.opendaylight.mdsal.common.api.CommitInfo;
-import org.opendaylight.mdsal.common.api.TransactionChainListener;
 import org.opendaylight.mdsal.dom.api.DOMDataTreeCursorAwareTransaction;
 import org.opendaylight.mdsal.dom.api.DOMDataTreeProducer;
 import org.opendaylight.mdsal.dom.api.DOMDataTreeReadTransaction;
 import org.opendaylight.mdsal.dom.api.DOMDataTreeService;
 import org.opendaylight.mdsal.dom.api.DOMDataTreeWriteCursor;
 import org.opendaylight.mdsal.dom.api.DOMDataTreeWriteTransaction;
+import org.opendaylight.mdsal.dom.api.DOMTransactionChainListener;
 import org.opendaylight.mdsal.dom.broker.util.TestModel;
 import org.opendaylight.yangtools.util.concurrent.FluentFutures;
 import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
@@ -45,7 +45,7 @@ public class ShardedDOMTransactionChainAdapterTest {
         doNothing().when(producer).close();
 
 
-        TransactionChainListener chainListener = new BlockingTransactionChainListener();
+        DOMTransactionChainListener chainListener = new BlockingTransactionChainListener();
         ShardedDOMTransactionChainAdapter transactionChainAdapter =
                 new ShardedDOMTransactionChainAdapter(identifier, dataTreeService, chainListener);
 
index 5575cb15abbd8ccc07d9aa81fbbe620ae2bb67a2..57543ff24473620e7cbf73064d2e81144d788c71 100644 (file)
@@ -10,13 +10,13 @@ package org.opendaylight.mdsal.dom.spi;
 import com.google.common.collect.ClassToInstanceMap;
 import com.google.common.collect.ForwardingObject;
 import javax.annotation.Nonnull;
-import org.opendaylight.mdsal.common.api.TransactionChainListener;
 import org.opendaylight.mdsal.dom.api.DOMDataBroker;
 import org.opendaylight.mdsal.dom.api.DOMDataBrokerExtension;
 import org.opendaylight.mdsal.dom.api.DOMDataTreeReadTransaction;
 import org.opendaylight.mdsal.dom.api.DOMDataTreeReadWriteTransaction;
 import org.opendaylight.mdsal.dom.api.DOMDataTreeWriteTransaction;
 import org.opendaylight.mdsal.dom.api.DOMTransactionChain;
+import org.opendaylight.mdsal.dom.api.DOMTransactionChainListener;
 
 /**
  * Utility {@link DOMDataBroker} implementation which forwards all interface
@@ -44,7 +44,7 @@ public abstract class ForwardingDOMDataBroker extends ForwardingObject implement
     }
 
     @Override
-    public DOMTransactionChain createTransactionChain(final TransactionChainListener listener) {
+    public DOMTransactionChain createTransactionChain(final DOMTransactionChainListener listener) {
         return delegate().createTransactionChain(listener);
     }
 
index d63bf0288775a69f1722506673e58dfef0e81d91..cef74d212dff4155fa684d842726850c8996cf76 100644 (file)
@@ -13,17 +13,12 @@ import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
  * DOM Data Store
  *
  * <p>
- * DOM Data Store provides transactional tree-like storage for YANG-modeled
- * entities described by YANG schema and represented by {@link NormalizedNode}.
- * Read and write access to stored data is provided only via transactions
- *
- * <p>
- * created using {@link #newReadOnlyTransaction()},
- * {@link #newWriteOnlyTransaction()} and {@link #newReadWriteTransaction()}, or
- * by creating {@link org.opendaylight.mdsal.common.api.TransactionChain}.
+ * DOM Data Store provides transactional tree-like storage for YANG-modeled entities described by YANG schema
+ * and represented by {@link NormalizedNode}. Read and write access to stored data is provided only via transactions
+ * created using {@link #newReadOnlyTransaction()}, {@link #newWriteOnlyTransaction()} and
+ * {@link #newReadWriteTransaction()}, or via {@link DOMStoreTransactionChain}.
  */
 public interface DOMStore extends DOMStoreTransactionFactory {
-
     /**
      * Creates new transaction chain.
      *
@@ -38,5 +33,4 @@ public interface DOMStore extends DOMStoreTransactionFactory {
      * @return Newly created transaction chain.
      */
     DOMStoreTransactionChain createTransactionChain();
-
 }