</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&revision=2013-10-28</capability>
<capability>urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom?module=opendaylight-md-sal-dom&revision=2013-10-28</capability>
<capability>urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl?module=opendaylight-sal-binding-broker-impl&revision=2013-10-28</capability>
+ <capability>urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:pingpong?module=opendaylight-pingpong-broker&revision=2014-11-07</capability>
<capability>urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:impl?module=opendaylight-sal-dom-broker-impl&revision=2013-10-28</capability>
<capability>urn:opendaylight:params:xml:ns:yang:controller:md:sal:common?module=opendaylight-md-sal-common&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&revision=2014-06-17</capability>
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>
--- /dev/null
+/*
+ * 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());
+ }
+}
--- /dev/null
+/*
+ * 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 {
+
+}
--- /dev/null
+/*
+ * 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
+ }
+}
--- /dev/null
+/*
+ * 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;
+ }
+}
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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();
+ }
+}
--- /dev/null
+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;
+ }
+ }
+ }
+ }
+ }
+}