Add MdsalUtilsAsync to make transactions asyncrhonous 32/37832/8
authorAlexis de Talhouët <adetalhouet@inocybe.com>
Tue, 19 Apr 2016 16:52:45 +0000 (12:52 -0400)
committerAlexis de Talhouët <adetalhouet@inocybe.com>
Fri, 13 May 2016 16:08:33 +0000 (16:08 +0000)
Change-Id: I8bb5d82f019ad62beb4b3c944640070227f38632
Signed-off-by: Alexis de Talhouët <adetalhouet@inocybe.com>
utils/mdsal-utils/pom.xml
utils/mdsal-utils/src/main/java/org/opendaylight/ovsdb/utils/mdsal/utils/MdsalUtilsAsync.java [new file with mode: 0644]
utils/mdsal-utils/src/test/java/org/opendaylight/ovsdb/utils/mdsal/utils/MdsalUtilsAsyncTest.java [new file with mode: 0644]

index 2c49e50b32be5154565bb7dd086674ec3b357f0f..e8db8c5f0823accfe804889aed2ef2ace8b9dd85 100644 (file)
@@ -37,10 +37,6 @@ and is available at http://www.eclipse.org/legal/epl-v10.html
   </scm>
 
   <dependencies>
-    <dependency>
-      <groupId>org.opendaylight.controller</groupId>
-      <artifactId>sal-binding-api</artifactId>
-    </dependency>
     <dependency>
       <groupId>org.opendaylight.ovsdb</groupId>
       <artifactId>southbound-api</artifactId>
@@ -58,6 +54,12 @@ and is available at http://www.eclipse.org/legal/epl-v10.html
       <artifactId>mockito-all</artifactId>
       <scope>test</scope>
     </dependency>
+    <dependency>
+      <groupId>org.opendaylight.controller</groupId>
+      <artifactId>sal-binding-broker-impl</artifactId>
+      <type>test-jar</type>
+      <scope>test</scope>
+    </dependency>
   </dependencies>
 
   <build>
diff --git a/utils/mdsal-utils/src/main/java/org/opendaylight/ovsdb/utils/mdsal/utils/MdsalUtilsAsync.java b/utils/mdsal-utils/src/main/java/org/opendaylight/ovsdb/utils/mdsal/utils/MdsalUtilsAsync.java
new file mode 100644 (file)
index 0000000..358a6af
--- /dev/null
@@ -0,0 +1,221 @@
+/*
+ * Copyright (c) 2016 Inocybe Technologies 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.ovsdb.utils.mdsal.utils;
+
+import com.google.common.base.Optional;
+import com.google.common.util.concurrent.CheckedFuture;
+import com.google.common.util.concurrent.FutureCallback;
+import com.google.common.util.concurrent.Futures;
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction;
+import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
+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.TransactionCommitFailedException;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class MdsalUtilsAsync {
+
+    private static final Logger LOG = LoggerFactory.getLogger(MdsalUtilsAsync.class);
+    private final DataBroker databroker;
+
+    /**
+     * Class constructor setting the data broker.
+     *
+     * @param dataBroker the {@link org.opendaylight.controller.md.sal.binding.api.DataBroker}
+     */
+    public MdsalUtilsAsync(final DataBroker dataBroker) {
+        this.databroker = dataBroker;
+    }
+
+    /**
+     * Executes delete as a non blocking transaction and returns the future.
+     *
+     * @param store
+     *            {@link LogicalDatastoreType} which should be modified
+     * @param path
+     *            {@link InstanceIdentifier} to read from
+     * @return The {@link CheckedFuture} object to which you can assign a
+     *         callback
+     */
+    public <D extends DataObject> CheckedFuture<Void, TransactionCommitFailedException> delete(
+                                    final LogicalDatastoreType store,
+                                    final InstanceIdentifier<D> path)  {
+        final WriteTransaction transaction = databroker.newWriteOnlyTransaction();
+        transaction.delete(store, path);
+        return transaction.submit();
+    }
+
+    /**
+     * Executes delete as a non blocking transaction and assign a default callback.
+     *
+     * @param store
+     *            {@link LogicalDatastoreType} which should be modified
+     * @param path
+     *            {@link InstanceIdentifier} to read from
+     * @param operationDesc
+     *            A brief description of the operation to perform
+     */
+    public <D extends DataObject> void delete(
+                                    final LogicalDatastoreType store,
+                                    final InstanceIdentifier<D> path,
+                                    final String operationDesc)  {
+        assignDefaultCallback(delete(store, path), operationDesc);
+    }
+
+    /**
+     * Executes put as non blocking transaction and return the future.
+     *
+     * @param logicalDatastoreType
+     *            {@link LogicalDatastoreType} which should be modified
+     * @param path
+     *            {@link InstanceIdentifier} for path to read
+     * @param <D>
+     *            The data object type
+     * @return The {@link CheckedFuture} object to which you can assign a
+     *         callback
+     */
+    public <D extends DataObject> CheckedFuture<Void, TransactionCommitFailedException> put(
+                                        final LogicalDatastoreType logicalDatastoreType,
+                                        final InstanceIdentifier<D> path,
+                                        final D data)  {
+        final WriteTransaction transaction = databroker.newWriteOnlyTransaction();
+        transaction.put(logicalDatastoreType, path, data, true);
+        return transaction.submit();
+    }
+
+    /**
+     * Executes put as non blocking transaction and assign default callback.
+     *
+     * @param logicalDatastoreType
+     *            {@link LogicalDatastoreType} which should be modified
+     * @param path
+     *            {@link InstanceIdentifier} for path to read
+     * @param <D>
+     *            The data object type
+     * @param operationDesc
+     *            A brief description of the operation to perform
+     */
+    public <D extends DataObject> void put(
+                                        final LogicalDatastoreType logicalDatastoreType,
+                                        final InstanceIdentifier<D> path,
+                                        final D data,
+                                        final String operationDesc)  {
+        assignDefaultCallback(put(logicalDatastoreType, path, data), operationDesc);
+    }
+
+    /**
+     * Executes merge as non blocking transaction and return the future.
+     *
+     * @param logicalDatastoreType
+     *            {@link LogicalDatastoreType} which should be modified
+     * @param path
+     *            {@link InstanceIdentifier} for path to read
+     * @param <D>
+     *            The data object type
+     * @param withParent
+     *            Whether or not to create missing parent.
+     * @return The {@link CheckedFuture} object to which you can assign a
+     *         callback
+     */
+    public <D extends DataObject> CheckedFuture<Void, TransactionCommitFailedException> merge(
+                                        final LogicalDatastoreType logicalDatastoreType,
+                                        final InstanceIdentifier<D> path,
+                                        final D data,
+                                        final boolean withParent)  {
+        final WriteTransaction transaction = databroker.newWriteOnlyTransaction();
+        transaction.merge(logicalDatastoreType, path, data, withParent);
+        return transaction.submit();
+    }
+
+    /**
+     * Executes merge as non blocking transaction and assign default callback.
+     *
+     * @param logicalDatastoreType
+     *            {@link LogicalDatastoreType} which should be modified
+     * @param path
+     *            {@link InstanceIdentifier} for path to read
+     * @param <D>
+     *            The data object type
+     * @param operationDesc
+     *            A brief description of the operation to perform
+     * @param withParent
+     *            Whether or not to create missing parent.
+     */
+    public <D extends DataObject> void merge(
+                                        final LogicalDatastoreType logicalDatastoreType,
+                                        final InstanceIdentifier<D> path,
+                                        final D data,
+                                        final String operationDesc,
+                                        final boolean withParent)  {
+        assignDefaultCallback(merge(logicalDatastoreType, path, data, withParent), operationDesc);
+    }
+
+    /**
+     * Executes read as non blocking transaction and assign a default callback
+     * to close the transaction.
+     *
+     * @param store
+     *            {@link LogicalDatastoreType} to read
+     * @param path
+     *            {@link InstanceIdentifier} for path to read
+     * @param <D>
+     *            The data object type
+     * @return The {@link CheckedFuture} object to which you can assign a
+     *         callback
+     */
+    public <D extends DataObject> CheckedFuture<Optional<D>, ReadFailedException> read(
+                                        final LogicalDatastoreType store,
+                                        final InstanceIdentifier<D> path)  {
+        final ReadOnlyTransaction transaction = databroker.newReadOnlyTransaction();
+        final CheckedFuture<Optional<D>, ReadFailedException> future = transaction.read(store, path);
+        final FutureCallback<Optional<D>> closeTransactionCallback = new FutureCallback<Optional<D>>() {
+            @Override
+            public void onSuccess(final Optional<D> result) {
+                transaction.close();
+            }
+
+            @Override
+            public void onFailure(final Throwable t) {
+                transaction.close();
+            }
+        };
+        Futures.addCallback(future, closeTransactionCallback);
+        return future;
+    }
+
+    /**
+     * Assign a default callback to a {@link CheckedFuture}. It will either log
+     * a message at DEBUG level if the transaction succeed, or will log at ERROR
+     * level and throw an {@link IllegalStateException} if the transaction
+     * failed.
+     *
+     * @param transaction
+     *            The transaction to commit.
+     * @param operationDesc
+     *            A description of the transaction to commit.
+     */
+    void assignDefaultCallback(final CheckedFuture<Void, TransactionCommitFailedException> transactionFuture, final String operationDesc) {
+        Futures.addCallback(transactionFuture, new FutureCallback<Void>() {
+            @Override
+            public void onSuccess(final Void result) {
+                LOG.debug("Transaction({}) {} SUCCESSFUL", operationDesc);
+            }
+
+            @Override
+            public void onFailure(final Throwable t) {
+                LOG.error("Transaction({}) {} FAILED!", operationDesc, t);
+                throw new IllegalStateException("  Transaction(" + operationDesc + ") not committed correctly", t);
+            }
+        });
+    }
+}
diff --git a/utils/mdsal-utils/src/test/java/org/opendaylight/ovsdb/utils/mdsal/utils/MdsalUtilsAsyncTest.java b/utils/mdsal-utils/src/test/java/org/opendaylight/ovsdb/utils/mdsal/utils/MdsalUtilsAsyncTest.java
new file mode 100644 (file)
index 0000000..a3d3048
--- /dev/null
@@ -0,0 +1,189 @@
+/*
+ * Copyright (c) 2016 Inocybe 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.ovsdb.utils.mdsal.utils;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.fail;
+
+import com.google.common.base.Optional;
+import com.google.common.util.concurrent.CheckedFuture;
+import com.google.common.util.concurrent.FutureCallback;
+import com.google.common.util.concurrent.Futures;
+import java.util.Arrays;
+import java.util.concurrent.ExecutionException;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mockito;
+import org.mockito.runners.MockitoJUnitRunner;
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.md.sal.binding.test.AbstractDataBrokerTest;
+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.TransactionCommitFailedException;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NetworkTopology;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.TopologyId;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.Topology;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.TopologyKey;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.NodeBuilder;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.NodeKey;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.node.attributes.SupportingNode;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.node.attributes.SupportingNodeBuilder;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.node.attributes.SupportingNodeKey;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+@RunWith(MockitoJUnitRunner.class)
+public class MdsalUtilsAsyncTest extends AbstractDataBrokerTest {
+
+    private MdsalUtilsAsync mdsalUtilsAsync;
+    private DataBroker databroker;
+
+    private static final TopologyId TOPOLOGY_TEST = new TopologyId("test:1");
+
+    private static final NodeId NODE_ID = new NodeId("test");
+    private static final NodeKey NODE_KEY =  new NodeKey(NODE_ID);
+    private static final Node data = new NodeBuilder().setKey(NODE_KEY).setNodeId(NODE_ID).build();
+
+    private static final InstanceIdentifier<Node> TEST_IID = InstanceIdentifier
+            .create(NetworkTopology.class)
+            .child(Topology.class, new TopologyKey(TOPOLOGY_TEST))
+            .child(Node.class, NODE_KEY);
+
+    @Before
+    public void setUp() {
+        databroker = getDataBroker();
+        mdsalUtilsAsync = Mockito.spy(new MdsalUtilsAsync(databroker));
+    }
+
+    @Test
+    public void testDelete() {
+        final CheckedFuture<Void, TransactionCommitFailedException> fut = mdsalUtilsAsync.put(LogicalDatastoreType.CONFIGURATION, TEST_IID, data);
+        Futures.addCallback(fut, new FutureCallback<Void>() {
+
+            @Override
+            public void onSuccess(final Void result) {
+                final CheckedFuture<Void, TransactionCommitFailedException> future = mdsalUtilsAsync.delete(LogicalDatastoreType.CONFIGURATION, TEST_IID);
+                Futures.addCallback(future, new FutureCallback<Void>() {
+
+                    @Override
+                    public void onSuccess(final Void result) {
+                        assertNull(readDS());
+                    }
+
+                    @Override
+                    public void onFailure(final Throwable t) {
+                        fail(t.getMessage());
+                    }
+                });
+            }
+            @Override
+            public void onFailure(final Throwable t) {
+                fail(t.getMessage());
+            }
+        });
+    }
+
+    @Test
+    public void testPutWithoutCallback() {
+        final String operationDesc = "testPut";
+        final SupportingNode supportingNodeBuilder1 = new SupportingNodeBuilder().setKey(new SupportingNodeKey(new NodeId("id1"), TOPOLOGY_TEST)).build();
+        final SupportingNode supportingNodeBuilder2 = new SupportingNodeBuilder().setKey(new SupportingNodeKey(new NodeId("id2"), TOPOLOGY_TEST)).build();
+
+        final Node data1 = new NodeBuilder(data).setSupportingNode(Arrays.asList(supportingNodeBuilder1)).build();
+        final Node data2 = new NodeBuilder(data).setSupportingNode(Arrays.asList(supportingNodeBuilder2)).build();
+
+        mdsalUtilsAsync.put(LogicalDatastoreType.CONFIGURATION, TEST_IID, data1, operationDesc);
+        assertEquals(data1, readDS());
+
+        final CheckedFuture<Void, TransactionCommitFailedException> future = mdsalUtilsAsync.put(LogicalDatastoreType.CONFIGURATION, TEST_IID, data2);
+        Futures.addCallback(future, new FutureCallback<Void>() {
+
+            @Override
+            public void onSuccess(final Void result) {
+                assertEquals(1, readDS().getSupportingNode().size());
+            }
+
+            @Override
+            public void onFailure(final Throwable t) {
+                fail(t.getMessage());
+            }
+        });
+    }
+
+    @Test
+    public void testMerge() {
+        final String operationDesc = "testMerge";
+        final SupportingNode supportingNodeBuilder1 = new SupportingNodeBuilder().setKey(new SupportingNodeKey(new NodeId("id1"), TOPOLOGY_TEST)).build();
+        final SupportingNode supportingNodeBuilder2 = new SupportingNodeBuilder().setKey(new SupportingNodeKey(new NodeId("id2"), TOPOLOGY_TEST)).build();
+
+        final Node data1 = new NodeBuilder(data).setSupportingNode(Arrays.asList(supportingNodeBuilder1)).build();
+        final Node data2 = new NodeBuilder(data).setSupportingNode(Arrays.asList(supportingNodeBuilder2)).build();
+
+        mdsalUtilsAsync.merge(LogicalDatastoreType.CONFIGURATION, TEST_IID, data1, operationDesc, true);
+        assertEquals(data1, readDS());
+
+        final CheckedFuture<Void, TransactionCommitFailedException> future = mdsalUtilsAsync.merge(LogicalDatastoreType.CONFIGURATION, TEST_IID, data2, true);
+        Futures.addCallback(future, new FutureCallback<Void>() {
+
+            @Override
+            public void onSuccess(final Void result) {
+                assertEquals(2, readDS().getSupportingNode().size());
+            }
+
+            @Override
+            public void onFailure(final Throwable t) {
+                fail(t.getMessage());
+            }
+        });
+    }
+
+    @Test
+    public void testRead() {
+        final CheckedFuture<Void, TransactionCommitFailedException> fut = mdsalUtilsAsync.put(LogicalDatastoreType.CONFIGURATION, TEST_IID, data);
+
+        Futures.addCallback(fut, new FutureCallback<Void>() {
+
+            @Override
+            public void onSuccess(final Void result) {
+                final CheckedFuture<Optional<Node>, ReadFailedException> future = mdsalUtilsAsync.read(LogicalDatastoreType.CONFIGURATION, TEST_IID);
+                Optional<Node> optNode;
+                try {
+                    optNode = future.get();
+                    if (optNode.isPresent()) {
+                        assertEquals(data, optNode.get());
+                    } else {
+                        fail("Couldn't read node");
+                    }
+                } catch (InterruptedException | ExecutionException e) {
+                    fail(e.getMessage());
+                }
+            }
+
+            @Override
+            public void onFailure(final Throwable t) {
+                fail(t.getMessage());
+            }
+        });
+    }
+
+    private Node readDS() {
+        try {
+            final Optional<Node> result = databroker.newReadOnlyTransaction().read(LogicalDatastoreType.CONFIGURATION, TEST_IID).get();
+            if (result.isPresent()) {
+                return result.get();
+            }
+        } catch (InterruptedException | ExecutionException e) {
+            fail(e.getMessage());
+        }
+        return null;
+    }
+}