Add MappingCheckedFuture 26/74826/3
authorRobert Varga <robert.varga@pantheon.tech>
Sat, 4 Aug 2018 01:02:41 +0000 (03:02 +0200)
committerRobert Varga <nite@hq.sk>
Sat, 4 Aug 2018 20:34:23 +0000 (20:34 +0000)
This class is moved here from MD-SAL, as CheckedFutures are not
used there anymore.

Change-Id: I84cda1d543a5bebdb17249943ee26ca8fe3f97bf
JIRA: MDSAL-229
Signed-off-by: Robert Varga <robert.varga@pantheon.tech>
opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/AbstractForwardedTransaction.java
opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/MappingCheckedFuture.java [new file with mode: 0644]
opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/AsyncWriteTransaction.java
opendaylight/md-sal/sal-common-api/src/test/java/org/opendaylight/controller/md/sal/common/api/MappingCheckedFutureTest.java [new file with mode: 0644]
opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/compat/LegacyDOMStoreAdapter.java
opendaylight/md-sal/sal-dom-compat/src/main/java/org/opendaylight/controller/sal/core/compat/DOMStoreReadTransactionAdapter.java
opendaylight/md-sal/sal-dom-compat/src/main/java/org/opendaylight/controller/sal/core/compat/LegacyDOMDataBrokerAdapter.java

index ad8b60517cc2c4975a17e737c0a9ffa4215037b3..86c57a3607f46df5fe9ed67f2abcfa4981eb8fb3 100644 (file)
@@ -11,11 +11,11 @@ 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.Futures;
 import com.google.common.base.Preconditions;
 import com.google.common.util.concurrent.CheckedFuture;
 import com.google.common.util.concurrent.Futures;
+import org.opendaylight.controller.md.sal.common.api.MappingCheckedFuture;
 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.dom.api.DOMDataReadTransaction;
 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.dom.api.DOMDataReadTransaction;
-import org.opendaylight.mdsal.common.api.MappingCheckedFuture;
 import org.opendaylight.yangtools.concepts.Delegator;
 import org.opendaylight.yangtools.concepts.Identifiable;
 import org.opendaylight.yangtools.yang.binding.DataObject;
 import org.opendaylight.yangtools.concepts.Delegator;
 import org.opendaylight.yangtools.concepts.Identifiable;
 import org.opendaylight.yangtools.yang.binding.DataObject;
diff --git a/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/MappingCheckedFuture.java b/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/MappingCheckedFuture.java
new file mode 100644 (file)
index 0000000..a847807
--- /dev/null
@@ -0,0 +1,95 @@
+/*
+ * Copyright (c) 2014 Brocade Communications 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.common.api;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.util.concurrent.AbstractCheckedFuture;
+import com.google.common.util.concurrent.ListenableFuture;
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import java.util.function.Function;
+import javax.annotation.Nonnull;
+
+/**
+ * An implementation of CheckedFuture that provides similar behavior for the <code>get</code> methods
+ * that the <code>checkedGet</code> methods provide.
+ *
+ * <p>For {@link CancellationException} and {@link InterruptedException}, the specified exception mapper
+ * is invoked to translate them to the checked exception type.
+ *
+ * <p>For {@link ExecutionException}, the mapper is invoked to translate the cause to the checked exception
+ * and a new ExecutionException is thrown with the translated cause.
+ *
+ * @author Thomas Pantelis
+ *
+ * @param <V> The result type returned by this Future's get method
+ * @param <X> The checked exception type
+ */
+public final class MappingCheckedFuture<V, X extends Exception> extends AbstractCheckedFuture<V, X> {
+    private final Function<Exception, X> mapper;
+
+    private MappingCheckedFuture(final ListenableFuture<V> delegate, final Function<Exception, X> mapper) {
+        super(delegate);
+        this.mapper = requireNonNull(mapper);
+    }
+
+    /**
+     * Creates a new <code>MappingCheckedFuture</code> that wraps the given {@link ListenableFuture}
+     * delegate.
+     *
+     * @param delegate the {@link ListenableFuture} to wrap
+     * @param mapper the mapping {@link Function} used to translate exceptions from the delegate
+     * @return a new <code>MappingCheckedFuture</code>
+     */
+    public static <V, X extends Exception> MappingCheckedFuture<V, X> create(
+            final ListenableFuture<V> delegate, final Function<Exception, X> mapper) {
+        return new MappingCheckedFuture<>(delegate, mapper);
+    }
+
+    @Override
+    @SuppressWarnings("checkstyle:parameterName")
+    protected X mapException(@Nonnull final Exception e) {
+        return mapper.apply(e);
+    }
+
+    private ExecutionException wrapInExecutionException(final String message, final Exception ex) {
+        return new ExecutionException(message, mapException(ex));
+    }
+
+    @Override
+    public V get() throws InterruptedException, ExecutionException {
+        try {
+            return super.get();
+        } catch (final InterruptedException e) {
+            Thread.currentThread().interrupt();
+            throw wrapInExecutionException("Operation was interrupted", e);
+        } catch (final CancellationException e) {
+            throw wrapInExecutionException("Operation was cancelled", e);
+        } catch (final ExecutionException e) {
+            throw wrapInExecutionException(e.getMessage(), e);
+        }
+    }
+
+    @Override
+    public V get(final long timeout, @Nonnull final TimeUnit unit)
+            throws InterruptedException, ExecutionException, TimeoutException {
+        try {
+            return super.get(timeout, unit);
+        } catch (final InterruptedException e) {
+            Thread.currentThread().interrupt();
+            throw wrapInExecutionException("Operation was interrupted", e);
+        } catch (final CancellationException e) {
+            throw wrapInExecutionException("Operation was cancelled", e);
+        } catch (final ExecutionException e) {
+            throw wrapInExecutionException(e.getMessage(), e);
+        }
+    }
+}
index 852c4e488617a25ef339dc4d58da6d03c4101fad..bc18e91582373783ac2499819164aa7935e7b5cb 100644 (file)
@@ -13,8 +13,8 @@ import com.google.common.util.concurrent.ListenableFuture;
 import com.google.common.util.concurrent.MoreExecutors;
 import javax.annotation.CheckReturnValue;
 import org.eclipse.jdt.annotation.NonNull;
 import com.google.common.util.concurrent.MoreExecutors;
 import javax.annotation.CheckReturnValue;
 import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.controller.md.sal.common.api.MappingCheckedFuture;
 import org.opendaylight.mdsal.common.api.CommitInfo;
 import org.opendaylight.mdsal.common.api.CommitInfo;
-import org.opendaylight.mdsal.common.api.MappingCheckedFuture;
 import org.opendaylight.yangtools.concepts.Path;
 import org.opendaylight.yangtools.util.concurrent.ExceptionMapper;
 
 import org.opendaylight.yangtools.concepts.Path;
 import org.opendaylight.yangtools.util.concurrent.ExceptionMapper;
 
@@ -384,7 +384,7 @@ public interface AsyncWriteTransaction<P extends Path<P>, D> extends AsyncTransa
     ExceptionMapper<TransactionCommitFailedException> SUBMIT_EXCEPTION_MAPPER =
         new ExceptionMapper<TransactionCommitFailedException>("submit", TransactionCommitFailedException.class) {
             @Override
     ExceptionMapper<TransactionCommitFailedException> SUBMIT_EXCEPTION_MAPPER =
         new ExceptionMapper<TransactionCommitFailedException>("submit", TransactionCommitFailedException.class) {
             @Override
-            protected TransactionCommitFailedException newWithCause(String message, Throwable cause) {
+            protected TransactionCommitFailedException newWithCause(final String message, final Throwable cause) {
                 return new TransactionCommitFailedException(message, cause);
             }
         };
                 return new TransactionCommitFailedException(message, cause);
             }
         };
diff --git a/opendaylight/md-sal/sal-common-api/src/test/java/org/opendaylight/controller/md/sal/common/api/MappingCheckedFutureTest.java b/opendaylight/md-sal/sal-common-api/src/test/java/org/opendaylight/controller/md/sal/common/api/MappingCheckedFutureTest.java
new file mode 100644 (file)
index 0000000..bee3060
--- /dev/null
@@ -0,0 +1,240 @@
+/*
+ * Copyright (c) 2014 Brocade Communications 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.common.api;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import com.google.common.util.concurrent.CheckedFuture;
+import com.google.common.util.concurrent.SettableFuture;
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicReference;
+import org.junit.Test;
+import org.opendaylight.yangtools.util.concurrent.ExceptionMapper;
+
+/**
+ * Unit tests for MappingCheckedFuture.
+ *
+ * @author Thomas Pantelis
+ */
+public class MappingCheckedFutureTest {
+
+    interface FutureInvoker {
+        void invokeGet(CheckedFuture<?,?> future) throws Exception;
+
+        Throwable extractWrappedTestEx(Exception from);
+    }
+
+    static class TestException extends Exception {
+        private static final long serialVersionUID = 1L;
+
+        TestException(final String message, final Throwable cause) {
+            super(message, cause);
+        }
+    }
+
+    static final ExceptionMapper<TestException> MAPPER = new ExceptionMapper<TestException>(
+                                                                      "Test", TestException.class) {
+
+        @Override
+        protected TestException newWithCause(final String message, final Throwable cause) {
+            return new TestException(message, cause);
+        }
+    };
+
+    static final FutureInvoker GET = new FutureInvoker() {
+        @Override
+        public void invokeGet(final CheckedFuture<?, ?> future) throws Exception {
+            future.get();
+        }
+
+        @Override
+        public Throwable extractWrappedTestEx(final Exception from) {
+            if (from instanceof ExecutionException) {
+                return from.getCause();
+            }
+
+            return from;
+        }
+    };
+
+    static final FutureInvoker TIMED_GET = new FutureInvoker() {
+        @Override
+        public void invokeGet(final CheckedFuture<?, ?> future) throws Exception {
+            future.get(1, TimeUnit.HOURS);
+        }
+
+        @Override
+        public Throwable extractWrappedTestEx(final Exception from) {
+            if (from instanceof ExecutionException) {
+                return from.getCause();
+            }
+
+            return from;
+        }
+    };
+
+    static final FutureInvoker CHECKED_GET = new FutureInvoker() {
+        @Override
+        public void invokeGet(final CheckedFuture<?,?> future) throws Exception {
+            future.checkedGet();
+        }
+
+        @Override
+        public Throwable extractWrappedTestEx(final Exception from) {
+            return from;
+        }
+    };
+
+    static final FutureInvoker TIMED_CHECKED_GET = new FutureInvoker() {
+        @Override
+        public void invokeGet(final CheckedFuture<?,?> future) throws Exception {
+            future.checkedGet(50, TimeUnit.MILLISECONDS);
+        }
+
+        @Override
+        public Throwable extractWrappedTestEx(final Exception from) {
+            return from;
+        }
+    };
+
+    @Test
+    public void testGet() throws Exception {
+        SettableFuture<String> delegate = SettableFuture.create();
+        MappingCheckedFuture<String,TestException> future = MappingCheckedFuture.create(delegate, MAPPER);
+        delegate.set("test");
+        assertEquals("get", "test", future.get());
+    }
+
+    @Test
+    public void testGetWithExceptions() throws Exception {
+        testExecutionException(GET, new RuntimeException());
+        testExecutionException(GET, new TestException("mock", null));
+        testCancellationException(GET);
+        testInterruptedException(GET);
+    }
+
+    @Test
+    public void testTimedGet() throws Exception {
+        SettableFuture<String> delegate = SettableFuture.create();
+        MappingCheckedFuture<String,TestException> future = MappingCheckedFuture.create(delegate, MAPPER);
+        delegate.set("test");
+        assertEquals("get", "test", future.get(50, TimeUnit.MILLISECONDS));
+    }
+
+    @Test
+    public void testTimedGetWithExceptions() throws Exception {
+        testExecutionException(TIMED_GET, new RuntimeException());
+        testCancellationException(TIMED_GET);
+        testInterruptedException(TIMED_GET);
+    }
+
+    @Test
+    public void testCheckedGetWithExceptions() throws Exception {
+        testExecutionException(CHECKED_GET, new RuntimeException());
+        testCancellationException(CHECKED_GET);
+        testInterruptedException(CHECKED_GET);
+    }
+
+    @Test
+    public void testTimedCheckedWithExceptions() throws Exception {
+        testExecutionException(TIMED_CHECKED_GET, new RuntimeException());
+        testCancellationException(TIMED_CHECKED_GET);
+        testInterruptedException(TIMED_CHECKED_GET);
+    }
+
+    @SuppressWarnings("checkstyle:illegalCatch")
+    private static void testExecutionException(final FutureInvoker invoker, final Throwable cause) {
+        SettableFuture<String> delegate = SettableFuture.create();
+        MappingCheckedFuture<String, TestException> mappingFuture = MappingCheckedFuture.create(delegate, MAPPER);
+
+        delegate.setException(cause);
+
+        try {
+            invoker.invokeGet(mappingFuture);
+            fail("Expected exception thrown");
+        } catch (Exception e) {
+            Throwable expectedTestEx = invoker.extractWrappedTestEx(e);
+            assertNotNull("Expected returned exception is null", expectedTestEx);
+            assertEquals("Exception type", TestException.class, expectedTestEx.getClass());
+
+            if (cause instanceof TestException) {
+                assertNull("Expected null cause", expectedTestEx.getCause());
+            } else {
+                assertSame("TestException cause", cause, expectedTestEx.getCause());
+            }
+        }
+    }
+
+    @SuppressWarnings("checkstyle:illegalCatch")
+    private static void testCancellationException(final FutureInvoker invoker) {
+        SettableFuture<String> delegate = SettableFuture.create();
+        MappingCheckedFuture<String, TestException> mappingFuture = MappingCheckedFuture.create(delegate, MAPPER);
+
+        mappingFuture.cancel(false);
+
+        try {
+            invoker.invokeGet(mappingFuture);
+            fail("Expected exception thrown");
+        } catch (Exception e) {
+            Throwable expectedTestEx = invoker.extractWrappedTestEx(e);
+            assertNotNull("Expected returned exception is null", expectedTestEx);
+            assertEquals("Exception type", TestException.class, expectedTestEx.getClass());
+            assertEquals("TestException cause type", CancellationException.class, expectedTestEx.getCause().getClass());
+        }
+    }
+
+    @SuppressWarnings("checkstyle:illegalCatch")
+    private static void testInterruptedException(final FutureInvoker invoker) throws Exception {
+        SettableFuture<String> delegate = SettableFuture.create();
+        final MappingCheckedFuture<String, TestException> mappingFuture = MappingCheckedFuture.create(delegate, MAPPER);
+
+        final AtomicReference<AssertionError> assertError = new AtomicReference<>();
+        final CountDownLatch doneLatch = new CountDownLatch(1);
+        Thread thread = new Thread() {
+            @Override
+            public void run() {
+                try {
+                    doInvoke();
+                } catch (AssertionError e) {
+                    assertError.set(e);
+                } finally {
+                    doneLatch.countDown();
+                }
+            }
+
+            void doInvoke() {
+                try {
+                    invoker.invokeGet(mappingFuture);
+                    fail("Expected exception thrown");
+                } catch (Exception e) {
+                    Throwable expectedTestEx = invoker.extractWrappedTestEx(e);
+                    assertNotNull("Expected returned exception is null", expectedTestEx);
+                    assertEquals("Exception type", TestException.class, expectedTestEx.getClass());
+                    assertEquals("TestException cause type", InterruptedException.class,
+                                  expectedTestEx.getCause().getClass());
+                }
+            }
+        };
+        thread.start();
+
+        thread.interrupt();
+        assertTrue("get call completed", doneLatch.await(5, TimeUnit.SECONDS));
+
+        if (assertError.get() != null) {
+            throw assertError.get();
+        }
+    }
+}
index ae1c848656b5b965df1b8b1a4d264d6550acac8c..981be078212ea8a2d18e0ca98ca793bc21cc8cde 100644 (file)
@@ -16,6 +16,7 @@ import com.google.common.util.concurrent.ListenableFuture;
 import com.google.common.util.concurrent.MoreExecutors;
 import javax.annotation.Nonnull;
 import org.opendaylight.controller.cluster.datastore.DistributedDataStoreInterface;
 import com.google.common.util.concurrent.MoreExecutors;
 import javax.annotation.Nonnull;
 import org.opendaylight.controller.cluster.datastore.DistributedDataStoreInterface;
+import org.opendaylight.controller.md.sal.common.api.MappingCheckedFuture;
 import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
 import org.opendaylight.controller.sal.core.compat.ReadFailedExceptionAdapter;
 import org.opendaylight.controller.sal.core.spi.data.DOMStore;
 import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
 import org.opendaylight.controller.sal.core.compat.ReadFailedExceptionAdapter;
 import org.opendaylight.controller.sal.core.spi.data.DOMStore;
@@ -24,7 +25,6 @@ import org.opendaylight.controller.sal.core.spi.data.DOMStoreReadWriteTransactio
 import org.opendaylight.controller.sal.core.spi.data.DOMStoreThreePhaseCommitCohort;
 import org.opendaylight.controller.sal.core.spi.data.DOMStoreTransactionChain;
 import org.opendaylight.controller.sal.core.spi.data.DOMStoreWriteTransaction;
 import org.opendaylight.controller.sal.core.spi.data.DOMStoreThreePhaseCommitCohort;
 import org.opendaylight.controller.sal.core.spi.data.DOMStoreTransactionChain;
 import org.opendaylight.controller.sal.core.spi.data.DOMStoreWriteTransaction;
-import org.opendaylight.mdsal.common.api.MappingCheckedFuture;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
 
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
 
index 0e5efa2b3bb9c86f3572a2741b730d240f4c1d9d..3c1e1bae12dc5645763fbeb5e1b0815c2f16a580 100644 (file)
@@ -14,9 +14,9 @@ import com.google.common.collect.ForwardingObject;
 import com.google.common.util.concurrent.CheckedFuture;
 import com.google.common.util.concurrent.Futures;
 import com.google.common.util.concurrent.MoreExecutors;
 import com.google.common.util.concurrent.CheckedFuture;
 import com.google.common.util.concurrent.Futures;
 import com.google.common.util.concurrent.MoreExecutors;
+import org.opendaylight.controller.md.sal.common.api.MappingCheckedFuture;
 import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
 import org.opendaylight.controller.sal.core.spi.data.DOMStoreReadTransaction;
 import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
 import org.opendaylight.controller.sal.core.spi.data.DOMStoreReadTransaction;
-import org.opendaylight.mdsal.common.api.MappingCheckedFuture;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
 
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
 
index 6ae611675e8b899c20baabc27e5fa86211b703bd..56981e9ad1f8faf9f25dde4461c343bdbde6172e 100644 (file)
@@ -22,6 +22,7 @@ import com.google.common.util.concurrent.SettableFuture;
 import java.util.Map;
 import java.util.concurrent.atomic.AtomicReference;
 import javax.annotation.Nonnull;
 import java.util.Map;
 import java.util.concurrent.atomic.AtomicReference;
 import javax.annotation.Nonnull;
+import org.opendaylight.controller.md.sal.common.api.MappingCheckedFuture;
 import org.opendaylight.controller.md.sal.common.api.data.AsyncTransaction;
 import org.opendaylight.controller.md.sal.common.api.data.DataStoreUnavailableException;
 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
 import org.opendaylight.controller.md.sal.common.api.data.AsyncTransaction;
 import org.opendaylight.controller.md.sal.common.api.data.DataStoreUnavailableException;
 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
@@ -41,7 +42,6 @@ import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeIdentifier;
 import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction;
 import org.opendaylight.controller.md.sal.dom.api.DOMTransactionChain;
 import org.opendaylight.mdsal.common.api.CommitInfo;
 import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction;
 import org.opendaylight.controller.md.sal.dom.api.DOMTransactionChain;
 import org.opendaylight.mdsal.common.api.CommitInfo;
-import org.opendaylight.mdsal.common.api.MappingCheckedFuture;
 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.DOMDataTreeReadTransaction;
 import org.opendaylight.mdsal.dom.api.DOMDataTreeReadWriteTransaction;
 import org.opendaylight.mdsal.dom.api.DOMDataTreeWriteTransaction;