BUG-2255: introduce PingPongDataBroker 83/12583/9
authorRobert Varga <rovarga@cisco.com>
Thu, 6 Nov 2014 19:13:59 +0000 (20:13 +0100)
committerRobert Varga <rovarga@cisco.com>
Tue, 25 Nov 2014 15:35:45 +0000 (16:35 +0100)
This is a forwarding DOMDataBroker implementation, which ensures that
there is at most one transaction pending for a particular transaction
chain. This is not useful in all scenarios, but if the users can
restrain themselves to use the supported semantics, they can use it.

Change-Id: I4a9ca12254763829f99e270d6716cd034b4b5ea9
Signed-off-by: Robert Varga <rovarga@cisco.com>
Signed-off-by: Dana Kutenicsova <dkutenic@cisco.com>
opendaylight/md-sal/md-sal-config/src/main/resources/initial/01-md-sal.xml
opendaylight/md-sal/sal-dom-broker/pom.xml
opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/config/yang/md/sal/dom/pingpong/PingpongDataBrokerModule.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/config/yang/md/sal/dom/pingpong/PingpongDataBrokerModuleFactory.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/PingPongDataBroker.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/PingPongFuture.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/PingPongTransaction.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/PingPongTransactionChain.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-broker/src/main/yang/opendaylight-pingpong-broker.yang [new file with mode: 0644]

index 1c0861ab02462cdd1290d9bf1b473b065d0ca5a8..b9159dccd14e2721b463568bc5cca7ed3ba3fcb6 100644 (file)
                         </schema-service>
                     </inmemory-operational-datastore-provider>
                 </module>
+
+                <!-- PingPong broker -->
+                <module>
+                    <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:pingpong">prefix:pingpong-data-broker</type>
+                    <name>pingpong-data-broker</name>
+                    <data-broker>
+                        <type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">dom:dom-async-data-broker</type>
+                        <name>inmemory-data-broker</name>
+                    </data-broker>
+                </module>
+                <module>
+                    <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">prefix:binding-forwarded-data-broker</type>
+                    <name>pingpong-binding-data-broker</name>
+                    <binding-forwarded-data-broker xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">
+                        <dom-async-broker>
+                            <type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">dom:dom-async-data-broker</type>
+                            <name>pingpong-broker</name>
+                        </dom-async-broker>
+                        <schema-service>
+                            <type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">dom:schema-service</type>
+                            <name>yang-schema-service</name>
+                        </schema-service>
+                        <binding-mapping-service>
+                            <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">binding:binding-dom-mapping-service</type>
+                            <name>runtime-mapping-singleton</name>
+                        </binding-mapping-service>
+                    </binding-forwarded-data-broker>
+                </module>
+
                 <!--
                      Tree-based in-memory data store. This is the data store which is currently
                      recommended for single-node deployments.
                             <name>binding-data-broker</name>
                             <provider>/modules/module[type='binding-forwarded-data-broker'][name='binding-async-data-broker']</provider>
                         </instance>
+                        <instance>
+                            <name>pingpong-binding-data-broker</name>
+                            <provider>/modules/module[type='binding-forwarded-data-broker'][name='pingpong-binding-data-broker']</provider>
+                        </instance>
                     </service>
 
                     <service>
                             <name>inmemory-data-broker</name>
                             <provider>/modules/module[type='dom-inmemory-data-broker'][name='inmemory-data-broker']</provider>
                         </instance>
+                        <instance>
+                            <name>pingpong-broker</name>
+                            <provider>/modules/module[type='pingpong-data-broker'][name='pingpong-data-broker']</provider>
+                        </instance>
                     </service>
             </services>
         </data>
         <capability>urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding?module=opendaylight-md-sal-binding&amp;revision=2013-10-28</capability>
         <capability>urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom?module=opendaylight-md-sal-dom&amp;revision=2013-10-28</capability>
         <capability>urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl?module=opendaylight-sal-binding-broker-impl&amp;revision=2013-10-28</capability>
+        <capability>urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:pingpong?module=opendaylight-pingpong-broker&amp;revision=2014-11-07</capability>
         <capability>urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:impl?module=opendaylight-sal-dom-broker-impl&amp;revision=2013-10-28</capability>
         <capability>urn:opendaylight:params:xml:ns:yang:controller:md:sal:common?module=opendaylight-md-sal-common&amp;revision=2013-10-28</capability>
         <capability>urn:opendaylight:params:xml:ns:yang:controller:md:sal:core:spi:config-dom-store?module=opendaylight-config-dom-datastore&amp;revision=2014-06-17</capability>
index 60581f99cb26d40a016b285b8c7ae49fb8b8fd39..79408669cc0763a7cd7415af2a6ba0ca06e17e81 100644 (file)
                             org.opendaylight.controller.sal.dom.broker.osgi,
                             org.opendaylight.controller.sal.dom.broker.util,
                             org.opendaylight.controller.config.yang.md.sal.dom.impl,
+                            org.opendaylight.controller.config.yang.md.sal.dom.pingpong,
                             org.opendaylight.controller.config.yang.md.sal.dom.statistics,\
                             org.opendaylight.controller.md.sal.dom.broker.impl,
                             org.opendaylight.controller.md.sal.dom.broker.impl.*,
                             org.opendaylight.yangtools.yang.util,
-                            org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.dom.impl.rev131028.*</Private-Package>
+                            org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.dom.impl.rev131028.*,
+                            org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.dom.pingpong.rev141107.*
+            </Private-Package>
             <Import-Package>*</Import-Package>
           </instructions>
         </configuration>
diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/config/yang/md/sal/dom/pingpong/PingpongDataBrokerModule.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/config/yang/md/sal/dom/pingpong/PingpongDataBrokerModule.java
new file mode 100644 (file)
index 0000000..1664a37
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * 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.controller.config.yang.md.sal.dom.pingpong;
+
+import org.opendaylight.controller.config.api.DependencyResolver;
+import org.opendaylight.controller.config.api.ModuleIdentifier;
+import org.opendaylight.controller.md.sal.dom.broker.impl.PingPongDataBroker;
+
+public class PingpongDataBrokerModule extends AbstractPingpongDataBrokerModule {
+    public PingpongDataBrokerModule(final ModuleIdentifier identifier, final DependencyResolver dependencyResolver) {
+        super(identifier, dependencyResolver);
+    }
+
+    public PingpongDataBrokerModule(final ModuleIdentifier identifier, final DependencyResolver dependencyResolver, final PingpongDataBrokerModule oldModule, final java.lang.AutoCloseable oldInstance) {
+        super(identifier, dependencyResolver, oldModule, oldInstance);
+    }
+
+    @Override
+    public java.lang.AutoCloseable createInstance() {
+        return new PingPongDataBroker(getDataBrokerDependency());
+    }
+}
diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/config/yang/md/sal/dom/pingpong/PingpongDataBrokerModuleFactory.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/config/yang/md/sal/dom/pingpong/PingpongDataBrokerModuleFactory.java
new file mode 100644 (file)
index 0000000..8ec17fd
--- /dev/null
@@ -0,0 +1,12 @@
+/*
+ * 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.controller.config.yang.md.sal.dom.pingpong;
+
+public class PingpongDataBrokerModuleFactory extends AbstractPingpongDataBrokerModuleFactory {
+
+}
diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/PingPongDataBroker.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/PingPongDataBroker.java
new file mode 100644 (file)
index 0000000..715a136
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * 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.controller.md.sal.dom.broker.impl;
+
+import com.google.common.base.Preconditions;
+import javax.annotation.Nonnull;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionChainListener;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker;
+import org.opendaylight.controller.md.sal.dom.spi.ForwardingDOMDataBroker;
+
+/**
+ * An implementation of a {@link DOMDataBroker}, which forwards most requests to a delegate.
+ *
+ * Its interpretation of the API contract is somewhat looser, specifically it does not
+ * guarantee transaction ordering between transactions allocated directly from the broker
+ * and its transaction chains.
+ */
+public final class PingPongDataBroker extends ForwardingDOMDataBroker implements AutoCloseable {
+    private final DOMDataBroker delegate;
+
+    /**
+     * Instantiate a new broker, backed by the the specified delegate
+     * {@link DOMDataBroker}.
+     *
+     * @param delegate Backend broker, may not be null.
+     */
+    public PingPongDataBroker(final @Nonnull DOMDataBroker delegate) {
+        this.delegate = Preconditions.checkNotNull(delegate);
+    }
+
+    @Override
+    protected DOMDataBroker delegate() {
+        return delegate;
+    }
+
+    @Override
+    public PingPongTransactionChain createTransactionChain(final TransactionChainListener listener) {
+        return new PingPongTransactionChain(delegate, listener);
+    }
+
+    @Override
+    public void close() {
+        // TODO Auto-generated method stub
+    }
+}
diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/PingPongFuture.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/PingPongFuture.java
new file mode 100644 (file)
index 0000000..1dfc607
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * 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.controller.md.sal.dom.broker.impl;
+
+import com.google.common.base.Preconditions;
+import com.google.common.util.concurrent.AbstractCheckedFuture;
+import com.google.common.util.concurrent.ListenableFuture;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
+
+/**
+ * A {@link Future} used to report the status of an future {@link java.util.concurrent.Future}.
+ */
+final class PingPongFuture extends AbstractCheckedFuture<Void, TransactionCommitFailedException> {
+    protected PingPongFuture(final ListenableFuture<Void> delegate) {
+        super(delegate);
+    }
+
+    @Override
+    protected TransactionCommitFailedException mapException(final Exception e) {
+        Preconditions.checkArgument(e instanceof TransactionCommitFailedException);
+        return (TransactionCommitFailedException) e;
+    }
+}
diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/PingPongTransaction.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/PingPongTransaction.java
new file mode 100644 (file)
index 0000000..fdb80eb
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+ * 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.controller.md.sal.dom.broker.impl;
+
+import com.google.common.base.Objects;
+import com.google.common.base.Objects.ToStringHelper;
+import com.google.common.base.Preconditions;
+import com.google.common.util.concurrent.CheckedFuture;
+import com.google.common.util.concurrent.FutureCallback;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.SettableFuture;
+import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
+import org.opendaylight.controller.md.sal.common.impl.service.AbstractDataTransaction;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataReadWriteTransaction;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+
+/**
+ * Transaction context. Tracks the relationship with the backend transaction.
+ * We never leak this class to the user and have it implement the {@link FutureCallback}
+ * interface so we have a simple way of propagating the result.
+ */
+final class PingPongTransaction implements FutureCallback<Void> {
+    private final CheckedFuture<Void, TransactionCommitFailedException> submitFuture;
+    private final ListenableFuture<RpcResult<TransactionStatus>> commitFuture;
+    private final DOMDataReadWriteTransaction delegate;
+    private final SettableFuture<Void> future;
+    private DOMDataReadWriteTransaction frontendTransaction;
+
+    PingPongTransaction(final DOMDataReadWriteTransaction delegate) {
+        this.delegate = Preconditions.checkNotNull(delegate);
+        future = SettableFuture.create();
+        submitFuture = new PingPongFuture(future);
+        commitFuture = AbstractDataTransaction.convertToLegacyCommitFuture(submitFuture);
+    }
+
+    DOMDataReadWriteTransaction getTransaction() {
+        return delegate;
+    }
+
+    DOMDataReadWriteTransaction getFrontendTransaction() {
+        return frontendTransaction;
+    }
+
+    CheckedFuture<Void, TransactionCommitFailedException> getSubmitFuture() {
+        return submitFuture;
+    }
+
+    ListenableFuture<RpcResult<TransactionStatus>> getCommitFuture() {
+        return commitFuture;
+    }
+
+    @Override
+    public void onSuccess(final Void result) {
+        future.set(result);
+    }
+
+    @Override
+    public void onFailure(final Throwable t) {
+        future.setException(t);
+    }
+
+    void recordFrontendTransaction(final DOMDataReadWriteTransaction tx) {
+        if (frontendTransaction != null) {
+            frontendTransaction = tx;
+        }
+    }
+
+    @Override
+    public String toString() {
+        return addToStringAttributes(Objects.toStringHelper(this)).toString();
+    }
+
+    protected ToStringHelper addToStringAttributes(final ToStringHelper toStringHelper) {
+        return toStringHelper.add("delegate", delegate);
+    }
+}
\ No newline at end of file
diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/PingPongTransactionChain.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/PingPongTransactionChain.java
new file mode 100644 (file)
index 0000000..83f31b9
--- /dev/null
@@ -0,0 +1,250 @@
+/*
+ * 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.controller.md.sal.dom.broker.impl;
+
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import com.google.common.util.concurrent.CheckedFuture;
+import com.google.common.util.concurrent.FutureCallback;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import javax.annotation.concurrent.GuardedBy;
+import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncTransaction;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionChain;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionChainListener;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataReadOnlyTransaction;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataReadWriteTransaction;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction;
+import org.opendaylight.controller.md.sal.dom.api.DOMTransactionChain;
+import org.opendaylight.controller.md.sal.dom.spi.ForwardingDOMDataReadWriteTransaction;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * An implementation of {@link DOMTransactionChain}, which has a very specific
+ * behavior, which some users may find surprising. If keeps the general
+ * intent of the contract, but it makes sure there are never more than two
+ * transactions allocated at any given time: one of them is being committed,
+ * and while that is happening, the other one acts as the scratch pad. Once
+ * the committing transaction completes successfully, the scratch transaction
+ * is enqueued as soon as it is ready.
+ *
+ * This mode of operation means that there is no inherent isolation between
+ * the front-end transactions and transactions cannot be reasonably cancelled.
+ *
+ * It furthermore means that the transactions returned by {@link #newReadOnlyTransaction()}
+ * counts as an outstanding transaction and the user may not allocate multiple
+ * read-only transactions at the same time.
+ */
+public final class PingPongTransactionChain implements DOMTransactionChain {
+    private static final Logger LOG = LoggerFactory.getLogger(PingPongTransactionChain.class);
+    private final DOMTransactionChain delegate;
+
+    @GuardedBy("this")
+    private PingPongTransaction bufferTransaction;
+    @GuardedBy("this")
+    private PingPongTransaction inflightTransaction;
+    @GuardedBy("this")
+    private boolean haveLocked;
+    @GuardedBy("this")
+    private boolean failed;
+
+    PingPongTransactionChain(final DOMDataBroker broker, final TransactionChainListener listener) {
+        this.delegate = broker.createTransactionChain(new TransactionChainListener() {
+            @Override
+            public void onTransactionChainFailed(final TransactionChain<?, ?> chain, final AsyncTransaction<?, ?> transaction, final Throwable cause) {
+                LOG.debug("Delegate chain {} reported failure in {}", chain, transaction, cause);
+
+                final DOMDataReadWriteTransaction frontend;
+                if (inflightTransaction == null) {
+                    LOG.warn("Transaction chain {} failed with no pending transactions", chain);
+                    frontend = null;
+                } else {
+                    frontend = inflightTransaction.getFrontendTransaction();
+                }
+
+                listener.onTransactionChainFailed(PingPongTransactionChain.this, frontend , cause);
+                delegateFailed();
+            }
+
+            @Override
+            public void onTransactionChainSuccessful(final TransactionChain<?, ?> chain) {
+                listener.onTransactionChainSuccessful(PingPongTransactionChain.this);
+            }
+        });
+    }
+
+    private synchronized void delegateFailed() {
+        failed = true;
+        if (!haveLocked) {
+            processBuffer();
+        }
+    }
+
+    private synchronized PingPongTransaction allocateTransaction() {
+        Preconditions.checkState(!haveLocked, "Attempted to start a transaction while a previous one is still outstanding");
+        Preconditions.checkState(!failed, "Attempted to use a failed chain");
+
+        if (bufferTransaction == null) {
+            bufferTransaction = new PingPongTransaction(delegate.newReadWriteTransaction());
+        }
+
+        haveLocked = true;
+        return bufferTransaction;
+    }
+
+    @GuardedBy("this")
+    private void processBuffer() {
+        final PingPongTransaction tx = bufferTransaction;
+
+        if (tx != null) {
+            if (failed) {
+                LOG.debug("Cancelling transaction {}", tx);
+                tx.getTransaction().cancel();
+                bufferTransaction = null;
+                return;
+            }
+
+            LOG.debug("Submitting transaction {}", tx);
+            final CheckedFuture<Void, ?> f = tx.getTransaction().submit();
+            bufferTransaction = null;
+            inflightTransaction = tx;
+
+            Futures.addCallback(f, new FutureCallback<Void>() {
+                @Override
+                public void onSuccess(final Void result) {
+                    transactionSuccessful(tx, result);
+                }
+
+                @Override
+                public void onFailure(final Throwable t) {
+                    transactionFailed(tx, t);
+                }
+            });
+        }
+    }
+
+    private void transactionSuccessful(final PingPongTransaction tx, final Void result) {
+        LOG.debug("Transaction {} completed successfully", tx);
+
+        synchronized (this) {
+            Preconditions.checkState(inflightTransaction == tx, "Successful transaction %s while %s was submitted", tx, inflightTransaction);
+            inflightTransaction = null;
+
+            if (!haveLocked) {
+                processBuffer();
+            }
+        }
+
+        // Can run unsynchronized
+        tx.onSuccess(result);
+    }
+
+    private void transactionFailed(final PingPongTransaction tx, final Throwable t) {
+        LOG.debug("Transaction {} failed", tx, t);
+
+        synchronized (this) {
+            Preconditions.checkState(inflightTransaction == tx, "Failed transaction %s while %s was submitted", tx, inflightTransaction);
+            inflightTransaction = null;
+        }
+
+        tx.onFailure(t);
+    }
+
+    private synchronized void readyTransaction(final PingPongTransaction tx) {
+        Preconditions.checkState(haveLocked, "Attempted to submit transaction while it is not outstanding");
+        Preconditions.checkState(bufferTransaction == tx, "Attempted to submit transaction %s while we have %s", tx, bufferTransaction);
+
+        haveLocked = false;
+        LOG.debug("Transaction {} unlocked", bufferTransaction);
+
+        if (inflightTransaction == null) {
+            processBuffer();
+        }
+    }
+
+    @Override
+    public synchronized void close() {
+        Preconditions.checkState(!haveLocked, "Attempted to close chain while a transaction is outstanding");
+        processBuffer();
+        delegate.close();
+    }
+
+    @Override
+    public DOMDataReadOnlyTransaction newReadOnlyTransaction() {
+        final PingPongTransaction tx = allocateTransaction();
+
+        return new DOMDataReadOnlyTransaction() {
+            @Override
+            public CheckedFuture<Optional<NormalizedNode<?, ?>>, ReadFailedException> read(final LogicalDatastoreType store,
+                    final YangInstanceIdentifier path) {
+                return tx.getTransaction().read(store, path);
+            }
+
+            @Override
+            public CheckedFuture<Boolean, ReadFailedException> exists(final LogicalDatastoreType store,
+                    final YangInstanceIdentifier path) {
+                return tx.getTransaction().exists(store, path);
+            }
+
+            @Override
+            public Object getIdentifier() {
+                return tx.getTransaction().getIdentifier();
+            }
+
+            @Override
+            public void close() {
+                readyTransaction(tx);
+            }
+        };
+    }
+
+    @Override
+    public DOMDataReadWriteTransaction newReadWriteTransaction() {
+        final PingPongTransaction tx = allocateTransaction();
+        final DOMDataReadWriteTransaction ret = new ForwardingDOMDataReadWriteTransaction() {
+            @Override
+            protected DOMDataReadWriteTransaction delegate() {
+                return tx.getTransaction();
+            }
+
+            @Override
+            public CheckedFuture<Void, TransactionCommitFailedException> submit() {
+                readyTransaction(tx);
+                return tx.getSubmitFuture();
+            }
+
+            @Override
+            public ListenableFuture<RpcResult<TransactionStatus>> commit() {
+                readyTransaction(tx);
+                return tx.getCommitFuture();
+            }
+
+            @Override
+            public boolean cancel() {
+                throw new UnsupportedOperationException("Transaction cancellation is not supported");
+            }
+        };
+
+        tx.recordFrontendTransaction(ret);
+        return ret;
+    }
+
+    @Override
+    public DOMDataWriteTransaction newWriteOnlyTransaction() {
+        return newReadWriteTransaction();
+    }
+}
diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/yang/opendaylight-pingpong-broker.yang b/opendaylight/md-sal/sal-dom-broker/src/main/yang/opendaylight-pingpong-broker.yang
new file mode 100644 (file)
index 0000000..0ed1bbd
--- /dev/null
@@ -0,0 +1,39 @@
+module opendaylight-pingpong-broker {
+    yang-version 1;
+    namespace "urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:pingpong";
+    prefix "pingpong";
+
+    import config { prefix config; revision-date 2013-04-05; }
+    import opendaylight-md-sal-dom {prefix sal;}
+    import opendaylight-md-sal-common {prefix common;}
+    import opendaylight-config-dom-datastore {prefix config-dom-store-spi;}
+    import opendaylight-operational-dom-datastore {prefix operational-dom-store-spi;}
+
+    description
+        "Service definition for Ping-Pong DOM broker";
+
+    revision "2014-11-07" {
+        description
+            "Initial revision";
+    }
+
+    identity pingpong-data-broker {
+        base config:module-type;
+        config:provided-service sal:dom-async-data-broker;
+    }
+
+    augment "/config:modules/config:module/config:configuration" {
+        case pingpong-data-broker {
+            when "/config:modules/config:module/config:type = 'pingpong-data-broker'";
+
+            container data-broker {
+                uses config:service-ref {
+                    refine type {
+                        mandatory true;
+                        config:required-identity sal:dom-async-data-broker;
+                    }
+                }
+            }
+        }
+    }
+}