Restore binding RPC shortcut 35/74035/5
authorTom Pantelis <tompantelis@gmail.com>
Sat, 14 Jul 2018 01:42:46 +0000 (21:42 -0400)
committerRobert Varga <nite@hq.sk>
Sat, 14 Jul 2018 16:18:28 +0000 (16:18 +0000)
The binding -> binding RPC serialization shortcut was broken by
recent changes so restore it. The LazyDOMRpcResultFuture from
the binding adapter is preserved by DOMRpcRouter via intermediate
DOMRpcResult adapters.

Change-Id: I98942471f4e8430d0043f5c09cb8a38ce2a5d7b9
Signed-off-by: Tom Pantelis <tompantelis@gmail.com>
opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/RpcServiceAdapter.java
opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/sal/binding/test/util/BindingTestContext.java
opendaylight/md-sal/sal-binding-dom-it/src/test/java/org/opendaylight/controller/sal/binding/test/connect/dom/CrossBrokerRpcTest.java
opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/DOMRpcRouter.java
opendaylight/md-sal/sal-dom-compat/src/main/java/org/opendaylight/controller/sal/core/compat/AbstractDOMRpcResultFutureAdapter.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-compat/src/main/java/org/opendaylight/controller/sal/core/compat/LegacyDOMRpcResultFutureAdapter.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-compat/src/main/java/org/opendaylight/controller/sal/core/compat/MdsalDOMRpcResultFutureAdapter.java [new file with mode: 0644]

index d61c9a4b6e2f5af78e35128f7b278134dd6ad074..074341cc0c558b6224c528678dd73eac9698583b 100644 (file)
@@ -22,6 +22,7 @@ import org.opendaylight.controller.md.sal.dom.api.DOMRpcException;
 import org.opendaylight.controller.md.sal.dom.api.DOMRpcResult;
 import org.opendaylight.controller.md.sal.dom.api.DOMRpcService;
 import org.opendaylight.controller.md.sal.dom.broker.spi.rpc.RpcRoutingStrategy;
+import org.opendaylight.controller.sal.core.compat.LegacyDOMRpcResultFutureAdapter;
 import org.opendaylight.mdsal.binding.dom.adapter.BindingRpcFutureAware;
 import org.opendaylight.mdsal.binding.dom.codec.api.BindingNormalizedNodeSerializer;
 import org.opendaylight.yangtools.yang.binding.DataContainer;
@@ -68,6 +69,12 @@ class RpcServiceAdapter implements InvocationHandler {
         final CheckedFuture<DOMRpcResult, DOMRpcException> result = delegate.invokeRpc(schemaPath, input);
         if (result instanceof BindingRpcFutureAware) {
             return ((BindingRpcFutureAware) result).getBindingFuture();
+        } else if (result instanceof LegacyDOMRpcResultFutureAdapter) {
+            CheckedFuture<org.opendaylight.mdsal.dom.api.DOMRpcResult, org.opendaylight.mdsal.dom.api.DOMRpcException>
+                    delegateFuture = ((LegacyDOMRpcResultFutureAdapter)result).delegate();
+            if (delegateFuture instanceof BindingRpcFutureAware) {
+                return ((BindingRpcFutureAware) delegateFuture).getBindingFuture();
+            }
         }
 
         return transformFuture(schemaPath, result, codec.getCodecFactory());
index 99385b0325f0e3626495b15aa1d811c04c8b135b..a337b5312a9b1a98e680be98e2d8f2391be5b4dd 100644 (file)
@@ -11,9 +11,9 @@ import static com.google.common.base.Preconditions.checkState;
 
 import com.google.common.annotations.Beta;
 import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.ImmutableSet;
 import com.google.common.util.concurrent.ListeningExecutorService;
 import com.google.common.util.concurrent.MoreExecutors;
+import java.util.Set;
 import javassist.ClassPool;
 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
 import org.opendaylight.controller.md.sal.binding.api.MountPointService;
@@ -82,6 +82,7 @@ public class BindingTestContext implements AutoCloseable {
 
     private BindingDOMRpcProviderServiceAdapter baProviderRpc;
     private DOMRpcRouter domRouter;
+    private org.opendaylight.mdsal.dom.broker.DOMRpcRouter delegateDomRouter;
 
     private NotificationPublishService publishService;
 
@@ -91,7 +92,7 @@ public class BindingTestContext implements AutoCloseable {
 
     private DOMNotificationService domListenService;
 
-
+    private Set<YangModuleInfo> schemaModuleInfos;
 
     public DOMDataBroker getDomAsyncDataBroker() {
         return this.newDOMDataBroker;
@@ -169,11 +170,11 @@ public class BindingTestContext implements AutoCloseable {
         this.mockSchemaService.registerSchemaContextListener(this.codec);
     }
 
-    private void updateYangSchema(final ImmutableSet<YangModuleInfo> moduleInfos) {
+    private void updateYangSchema(final Set<YangModuleInfo> moduleInfos) {
         this.mockSchemaService.changeSchema(getContext(moduleInfos));
     }
 
-    private SchemaContext getContext(final ImmutableSet<YangModuleInfo> moduleInfos) {
+    private SchemaContext getContext(final Set<YangModuleInfo> moduleInfos) {
         final ModuleInfoBackedContext ctx = ModuleInfoBackedContext.create();
         ctx.addModuleInfos(moduleInfos);
         return ctx.tryToCreateSchemaContext().get();
@@ -190,7 +191,10 @@ public class BindingTestContext implements AutoCloseable {
         startBindingBroker();
 
         startForwarding();
-        if (this.startWithSchema) {
+
+        if (schemaModuleInfos != null) {
+            updateYangSchema(schemaModuleInfos);
+        } else if (this.startWithSchema) {
             loadYangSchemaFromClasspath();
         }
     }
@@ -202,9 +206,8 @@ public class BindingTestContext implements AutoCloseable {
     private void startDomBroker() {
         checkState(this.executor != null);
 
-        org.opendaylight.mdsal.dom.broker.DOMRpcRouter delegate =
-                org.opendaylight.mdsal.dom.broker.DOMRpcRouter.newInstance(mockSchemaService);
-        this.domRouter = new DOMRpcRouter(delegate, delegate);
+        delegateDomRouter = org.opendaylight.mdsal.dom.broker.DOMRpcRouter.newInstance(mockSchemaService);
+        this.domRouter = new DOMRpcRouter(delegateDomRouter, delegateDomRouter);
     }
 
     public void startBindingNotificationBroker() {
@@ -219,8 +222,7 @@ public class BindingTestContext implements AutoCloseable {
     }
 
     public void loadYangSchemaFromClasspath() {
-        final ImmutableSet<YangModuleInfo> moduleInfos = BindingReflections.loadModuleInfos();
-        updateYangSchema(moduleInfos);
+        updateYangSchema(BindingReflections.loadModuleInfos());
     }
 
     public RpcProviderRegistry getBindingRpcRegistry() {
@@ -235,6 +237,10 @@ public class BindingTestContext implements AutoCloseable {
         return this.domRouter;
     }
 
+    public org.opendaylight.mdsal.dom.broker.DOMRpcRouter getDelegateDomRouter() {
+        return delegateDomRouter;
+    }
+
     @Override
     public void close() throws Exception {
 
@@ -251,4 +257,8 @@ public class BindingTestContext implements AutoCloseable {
     public DataBroker getDataBroker() {
         return this.dataBroker;
     }
+
+    public void setSchemaModuleInfos(Set<YangModuleInfo> moduleInfos) {
+        this.schemaModuleInfos = moduleInfos;
+    }
 }
index dbb839e3ffa3a1b3ad20e8675eea52fd488a7fce..dcdb5a1b0e64cfa02ec096063547047c9642a01b 100644 (file)
@@ -10,12 +10,19 @@ package org.opendaylight.controller.sal.binding.test.connect.dom;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNotSame;
+import static org.junit.Assert.assertSame;
 import static org.junit.Assert.assertTrue;
 
+import com.google.common.collect.HashMultimap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Multimap;
 import com.google.common.util.concurrent.Futures;
 import com.google.common.util.concurrent.ListenableFuture;
 import com.google.common.util.concurrent.MoreExecutors;
+import java.util.concurrent.ExecutionException;
 import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
@@ -27,6 +34,7 @@ import org.opendaylight.controller.md.sal.dom.spi.DefaultDOMRpcResult;
 import org.opendaylight.controller.sal.binding.api.RpcProviderRegistry;
 import org.opendaylight.controller.sal.binding.test.util.BindingBrokerTestFactory;
 import org.opendaylight.controller.sal.binding.test.util.BindingTestContext;
+import org.opendaylight.mdsal.binding.dom.adapter.BindingDOMRpcProviderServiceAdapter;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.of.migration.test.model.rev150210.KnockKnockInput;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.of.migration.test.model.rev150210.KnockKnockInputBuilder;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.of.migration.test.model.rev150210.KnockKnockOutput;
@@ -38,6 +46,7 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controll
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.rpc.routing.rev140701.TestContext;
 import org.opendaylight.yangtools.yang.binding.DataObject;
 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.binding.util.BindingReflections;
 import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.common.RpcResult;
 import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
@@ -70,12 +79,13 @@ public class CrossBrokerRpcTest {
 
 
     @Before
-    public void setup() {
+    public void setup() throws Exception {
         BindingBrokerTestFactory testFactory = new BindingBrokerTestFactory();
         testFactory.setExecutor(MoreExecutors.newDirectExecutorService());
-        testFactory.setStartWithParsedSchema(true);
         testContext = testFactory.getTestContext();
 
+        testContext.setSchemaModuleInfos(ImmutableSet.of(
+                BindingReflections.getModuleInfo(OpendaylightOfMigrationTestModelService.class)));
         testContext.start();
         providerRegistry = testContext.getBindingRpcRegistry();
         provisionRegistry = testContext.getDomRpcRegistry();
@@ -87,6 +97,58 @@ public class CrossBrokerRpcTest {
 
     }
 
+    @After
+    public void teardown() throws Exception {
+        testContext.close();
+    }
+
+    @Test
+    public void testBindingRpcShortcutRegisteredViaLegacyAPI()
+            throws InterruptedException, ExecutionException, TimeoutException {
+        final ListenableFuture<RpcResult<KnockKnockOutput>> knockResult = knockResult(true, "open");
+        knockService.registerPath(TestContext.class, BA_NODE_A_ID).setKnockKnockResult(knockResult);
+
+        OpendaylightOfMigrationTestModelService baKnockInvoker =
+                providerRegistry.getRpcService(OpendaylightOfMigrationTestModelService.class);
+
+        final KnockKnockInput knockInput = knockKnock(BA_NODE_A_ID).setQuestion("Who's there?").build();
+        ListenableFuture<RpcResult<KnockKnockOutput>> future = baKnockInvoker.knockKnock(knockInput);
+
+        final RpcResult<KnockKnockOutput> rpcResult = future.get(5, TimeUnit.SECONDS);
+
+        assertEquals(knockResult.get().getResult().getClass(), rpcResult.getResult().getClass());
+        assertSame(knockResult.get().getResult(), rpcResult.getResult());
+        assertSame(knockInput, knockService.getReceivedKnocks().get(BA_NODE_A_ID).iterator().next());
+    }
+
+    @Test
+    public void testBindingRpcShortcutRegisteredViaMdsalAPI()
+            throws InterruptedException, ExecutionException, TimeoutException {
+        final ListenableFuture<RpcResult<KnockKnockOutput>> knockResult = knockResult(true, "open");
+
+        BindingDOMRpcProviderServiceAdapter mdsalServiceRegistry = new BindingDOMRpcProviderServiceAdapter(
+                testContext.getDelegateDomRouter(), testContext.getCodec());
+
+        final Multimap<InstanceIdentifier<?>, KnockKnockInput> receivedKnocks = HashMultimap.create();
+        mdsalServiceRegistry.registerRpcImplementation(OpendaylightOfMigrationTestModelService.class,
+            (OpendaylightOfMigrationTestModelService) input -> {
+                receivedKnocks.put(input.getKnockerId(), input);
+                return knockResult;
+            }, ImmutableSet.of(BA_NODE_A_ID));
+
+        OpendaylightOfMigrationTestModelService baKnockInvoker =
+                providerRegistry.getRpcService(OpendaylightOfMigrationTestModelService.class);
+
+        final KnockKnockInput knockInput = knockKnock(BA_NODE_A_ID).setQuestion("Who's there?").build();
+        Future<RpcResult<KnockKnockOutput>> future = baKnockInvoker.knockKnock(knockInput);
+
+        final RpcResult<KnockKnockOutput> rpcResult = future.get(5, TimeUnit.SECONDS);
+
+        assertEquals(knockResult.get().getResult().getClass(), rpcResult.getResult().getClass());
+        assertSame(knockResult.get().getResult(), rpcResult.getResult());
+        assertSame(knockInput, receivedKnocks.get(BA_NODE_A_ID).iterator().next());
+    }
+
     @Test
     public void bindingRoutedRpcProvider_DomInvokerTest() throws Exception {
 
@@ -134,11 +196,6 @@ public class CrossBrokerRpcTest {
         return testContext.getCodec().getCodecFactory().toNormalizedNodeRpcData(addFlowA);
     }
 
-    @After
-    public void teardown() throws Exception {
-        testContext.close();
-    }
-
     private static org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier createBINodeIdentifier(
             final TopLevelListKey listKey) {
         return org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.builder().node(Top.QNAME)
index 3d57aac6010702caea06ed5525604a6c4c135ec6..54845cf09787d7d48131ca2268452376fc208928 100644 (file)
@@ -10,9 +10,6 @@ package org.opendaylight.controller.md.sal.dom.broker.impl;
 import com.google.common.annotations.VisibleForTesting;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.util.concurrent.CheckedFuture;
-import com.google.common.util.concurrent.Futures;
-import com.google.common.util.concurrent.ListenableFuture;
-import com.google.common.util.concurrent.MoreExecutors;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.Map;
@@ -23,46 +20,21 @@ import org.opendaylight.controller.md.sal.dom.api.DOMRpcAvailabilityListener;
 import org.opendaylight.controller.md.sal.dom.api.DOMRpcException;
 import org.opendaylight.controller.md.sal.dom.api.DOMRpcIdentifier;
 import org.opendaylight.controller.md.sal.dom.api.DOMRpcImplementation;
-import org.opendaylight.controller.md.sal.dom.api.DOMRpcImplementationNotAvailableException;
 import org.opendaylight.controller.md.sal.dom.api.DOMRpcImplementationRegistration;
 import org.opendaylight.controller.md.sal.dom.api.DOMRpcProviderService;
 import org.opendaylight.controller.md.sal.dom.api.DOMRpcResult;
 import org.opendaylight.controller.md.sal.dom.api.DOMRpcService;
-import org.opendaylight.controller.md.sal.dom.api.DefaultDOMRpcException;
 import org.opendaylight.controller.md.sal.dom.spi.AbstractDOMRpcImplementationRegistration;
-import org.opendaylight.controller.md.sal.dom.spi.DefaultDOMRpcResult;
-import org.opendaylight.mdsal.common.api.MappingCheckedFuture;
+import org.opendaylight.controller.sal.core.compat.LegacyDOMRpcResultFutureAdapter;
+import org.opendaylight.controller.sal.core.compat.MdsalDOMRpcResultFutureAdapter;
 import org.opendaylight.yangtools.concepts.AbstractListenerRegistration;
 import org.opendaylight.yangtools.concepts.ListenerRegistration;
-import org.opendaylight.yangtools.util.concurrent.ExceptionMapper;
 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
 import org.opendaylight.yangtools.yang.model.api.SchemaContextListener;
 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
 
 public final class DOMRpcRouter implements AutoCloseable, DOMRpcService, DOMRpcProviderService, SchemaContextListener {
-    private static final ExceptionMapper<org.opendaylight.mdsal.dom.api.DOMRpcException> MDSAL_DOM_RPC_EX_MAPPER =
-            new ExceptionMapper<org.opendaylight.mdsal.dom.api.DOMRpcException>(
-                    "rpc", org.opendaylight.mdsal.dom.api.DOMRpcException.class) {
-        @Override
-        protected org.opendaylight.mdsal.dom.api.DOMRpcException newWithCause(String message, Throwable cause) {
-            return cause instanceof org.opendaylight.mdsal.dom.api.DOMRpcException
-                    ? (org.opendaylight.mdsal.dom.api.DOMRpcException)cause
-                            : new org.opendaylight.mdsal.dom.api.DefaultDOMRpcException("RPC failed", cause);
-        }
-    };
-
-    private static final ExceptionMapper<DOMRpcException> LEGACY_DOM_RPC_EX_MAPPER =
-            new ExceptionMapper<DOMRpcException>("rpc", DOMRpcException.class) {
-        @Override
-        protected DOMRpcException newWithCause(String message, Throwable cause) {
-            return cause instanceof DOMRpcException ? (DOMRpcException)cause
-                : cause instanceof org.opendaylight.mdsal.dom.api.DOMRpcImplementationNotAvailableException
-                    ? new DOMRpcImplementationNotAvailableException(cause.getMessage(), cause.getCause())
-                        : new DefaultDOMRpcException("RPC failed", cause);
-        }
-    };
-
     // This mapping is used to translate mdsal DOMRpcImplementations to their corresponding legacy
     // DOMRpcImplementations registered thru this interface when invoking a DOMRpcAvailabilityListener.
     private final Map<org.opendaylight.mdsal.dom.api.DOMRpcImplementation, DOMRpcImplementation> implMapping =
@@ -90,7 +62,6 @@ public final class DOMRpcRouter implements AutoCloseable, DOMRpcService, DOMRpcP
         return registerRpcImplementation(implementation, ImmutableSet.copyOf(rpcs));
     }
 
-    @SuppressWarnings({ "unchecked", "rawtypes" })
     @Override
     public synchronized <T extends DOMRpcImplementation> DOMRpcImplementationRegistration<T> registerRpcImplementation(
             final T implementation, final Set<DOMRpcIdentifier> rpcs) {
@@ -100,8 +71,7 @@ public final class DOMRpcRouter implements AutoCloseable, DOMRpcService, DOMRpcP
                 public CheckedFuture<org.opendaylight.mdsal.dom.api.DOMRpcResult,
                         org.opendaylight.mdsal.dom.api.DOMRpcException> invokeRpc(
                         org.opendaylight.mdsal.dom.api.DOMRpcIdentifier rpc, NormalizedNode<?, ?> input) {
-                    final ListenableFuture future = implementation.invokeRpc(convert(rpc), input);
-                    return MappingCheckedFuture.create(future, MDSAL_DOM_RPC_EX_MAPPER);
+                    return new MdsalDOMRpcResultFutureAdapter(implementation.invokeRpc(convert(rpc), input));
                 }
 
                 @Override
@@ -134,12 +104,6 @@ public final class DOMRpcRouter implements AutoCloseable, DOMRpcService, DOMRpcP
         return DOMRpcIdentifier.create(from.getType(), from.getContextReference());
     }
 
-    private static ListenableFuture<DOMRpcResult> convert(org.opendaylight.mdsal.dom.api.DOMRpcResult from) {
-        return from == null ? Futures.immediateFuture(null)
-                : from instanceof DOMRpcResult ? Futures.immediateFuture((DOMRpcResult)from)
-                        : Futures.immediateFuture(new DefaultDOMRpcResult(from.getResult(), from.getErrors()));
-    }
-
     private static Collection<DOMRpcIdentifier> convert(
             Collection<org.opendaylight.mdsal.dom.api.DOMRpcIdentifier> from) {
         return from.stream().map(DOMRpcRouter::convert).collect(Collectors.toList());
@@ -148,9 +112,10 @@ public final class DOMRpcRouter implements AutoCloseable, DOMRpcService, DOMRpcP
     @Override
     public CheckedFuture<DOMRpcResult, DOMRpcException> invokeRpc(final SchemaPath type,
                                                                   final NormalizedNode<?, ?> input) {
-        final ListenableFuture<DOMRpcResult> future = Futures.transformAsync(delegateRpcService.invokeRpc(type, input),
-                DOMRpcRouter::convert, MoreExecutors.directExecutor());
-        return MappingCheckedFuture.create(future, LEGACY_DOM_RPC_EX_MAPPER);
+        final CheckedFuture<org.opendaylight.mdsal.dom.api.DOMRpcResult, org.opendaylight.mdsal.dom.api.DOMRpcException>
+            future = delegateRpcService.invokeRpc(type, input);
+        return future instanceof MdsalDOMRpcResultFutureAdapter ? ((MdsalDOMRpcResultFutureAdapter)future).delegate()
+                : new LegacyDOMRpcResultFutureAdapter(future);
     }
 
     @Override
diff --git a/opendaylight/md-sal/sal-dom-compat/src/main/java/org/opendaylight/controller/sal/core/compat/AbstractDOMRpcResultFutureAdapter.java b/opendaylight/md-sal/sal-dom-compat/src/main/java/org/opendaylight/controller/sal/core/compat/AbstractDOMRpcResultFutureAdapter.java
new file mode 100644 (file)
index 0000000..376682a
--- /dev/null
@@ -0,0 +1,122 @@
+/*
+ * Copyright (c) 2018 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.controller.sal.core.compat;
+
+import com.google.common.util.concurrent.CheckedFuture;
+import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
+import java.util.Optional;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Executor;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import org.opendaylight.mdsal.dom.api.DOMRpcException;
+import org.opendaylight.mdsal.dom.api.DOMRpcResult;
+import org.opendaylight.yangtools.util.concurrent.ExceptionMapper;
+
+/**
+ * Base for a DOMRpcResult future adapter.
+ *
+ * @author Thomas Pantelis
+ */
+@SuppressWarnings("checkstyle:ClassTypeParameterName")
+public abstract class AbstractDOMRpcResultFutureAdapter<T extends DOMRpcResult, TE extends DOMRpcException,
+        F extends DOMRpcResult, FE extends DOMRpcException> implements CheckedFuture<T, TE> {
+    private final CheckedFuture<F, FE> delegate;
+    private final ExceptionMapper<TE> exMapper;
+    private volatile Optional<T> result;
+
+    AbstractDOMRpcResultFutureAdapter(CheckedFuture<F, FE> delegate, ExceptionMapper<TE> exMapper) {
+        this.delegate = delegate;
+        this.exMapper = exMapper;
+    }
+
+    protected abstract T transform(F fromResult);
+
+    public CheckedFuture<F, FE> delegate() {
+        return delegate;
+    }
+
+    @Override
+    public void addListener(Runnable listener, Executor executor) {
+        delegate.addListener(listener, executor);
+    }
+
+    @Override
+    public boolean cancel(boolean mayInterruptIfRunning) {
+        return delegate.cancel(mayInterruptIfRunning);
+    }
+
+    @Override
+    public boolean isCancelled() {
+        return delegate.isCancelled();
+    }
+
+    @Override
+    public boolean isDone() {
+        return delegate.isDone();
+    }
+
+    @Override
+    public T get() throws InterruptedException, ExecutionException {
+        if (result != null) {
+            return result.orElse(null);
+        }
+
+        try {
+            return transformIfNecessary(delegate.get());
+        } catch (ExecutionException e) {
+            throw new ExecutionException(e.getMessage(), exMapper.apply(e));
+        }
+    }
+
+    @Override
+    public T get(final long timeout, final TimeUnit unit) throws InterruptedException, ExecutionException,
+            TimeoutException {
+        if (result != null) {
+            return result.orElse(null);
+        }
+
+        try {
+            return transformIfNecessary(delegate.get(timeout, unit));
+        } catch (ExecutionException e) {
+            throw new ExecutionException(e.getMessage(), exMapper.apply(e));
+        }
+    }
+
+    @Override
+    @SuppressFBWarnings("BC_UNCONFIRMED_CAST_OF_RETURN_VALUE")
+    public T checkedGet() throws TE {
+        try {
+            return get();
+        } catch (InterruptedException | ExecutionException e) {
+            throw exMapper.apply(e);
+        }
+    }
+
+    @Override
+    @SuppressFBWarnings("BC_UNCONFIRMED_CAST_OF_RETURN_VALUE")
+    public T checkedGet(final long timeout, final TimeUnit unit) throws TimeoutException, TE {
+        try {
+            return get(timeout, unit);
+        } catch (InterruptedException | ExecutionException e) {
+            throw exMapper.apply(e);
+        }
+    }
+
+    private synchronized T transformIfNecessary(F delegateResult) {
+        if (result == null) {
+            if (delegateResult == null) {
+                result = Optional.empty();
+            } else {
+                result = Optional.of(transform(delegateResult));
+            }
+        }
+
+        return result.orElse(null);
+    }
+}
diff --git a/opendaylight/md-sal/sal-dom-compat/src/main/java/org/opendaylight/controller/sal/core/compat/LegacyDOMRpcResultFutureAdapter.java b/opendaylight/md-sal/sal-dom-compat/src/main/java/org/opendaylight/controller/sal/core/compat/LegacyDOMRpcResultFutureAdapter.java
new file mode 100644 (file)
index 0000000..3e80344
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2018 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.controller.sal.core.compat;
+
+import com.google.common.util.concurrent.CheckedFuture;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcException;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcImplementationNotAvailableException;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcResult;
+import org.opendaylight.controller.md.sal.dom.api.DefaultDOMRpcException;
+import org.opendaylight.controller.md.sal.dom.spi.DefaultDOMRpcResult;
+import org.opendaylight.yangtools.util.concurrent.ExceptionMapper;
+
+/**
+ * Adapts a {@link org.opendaylight.mdsal.dom.api.DOMRpcResult} CheckedFuture to a {@link DOMRpcResult} CheckedFuture.
+ *
+ * @author Thomas Pantelis
+ */
+public class LegacyDOMRpcResultFutureAdapter extends AbstractDOMRpcResultFutureAdapter<DOMRpcResult, DOMRpcException,
+        org.opendaylight.mdsal.dom.api.DOMRpcResult, org.opendaylight.mdsal.dom.api.DOMRpcException> {
+
+    private static final ExceptionMapper<DOMRpcException> LEGACY_DOM_RPC_EX_MAPPER =
+            new ExceptionMapper<DOMRpcException>("rpc", DOMRpcException.class) {
+        @Override
+        protected DOMRpcException newWithCause(String message, Throwable cause) {
+            return cause instanceof DOMRpcException ? (DOMRpcException)cause
+                : cause instanceof org.opendaylight.mdsal.dom.api.DOMRpcImplementationNotAvailableException
+                    ? new DOMRpcImplementationNotAvailableException(cause.getMessage(), cause.getCause())
+                        : new DefaultDOMRpcException("RPC failed", cause);
+        }
+    };
+
+    public LegacyDOMRpcResultFutureAdapter(CheckedFuture<org.opendaylight.mdsal.dom.api.DOMRpcResult,
+            org.opendaylight.mdsal.dom.api.DOMRpcException> delegate) {
+        super(delegate, LEGACY_DOM_RPC_EX_MAPPER);
+    }
+
+    @Override
+    protected DOMRpcResult transform(org.opendaylight.mdsal.dom.api.DOMRpcResult fromResult) {
+        return new DefaultDOMRpcResult(fromResult.getResult(), fromResult.getErrors());
+    }
+}
diff --git a/opendaylight/md-sal/sal-dom-compat/src/main/java/org/opendaylight/controller/sal/core/compat/MdsalDOMRpcResultFutureAdapter.java b/opendaylight/md-sal/sal-dom-compat/src/main/java/org/opendaylight/controller/sal/core/compat/MdsalDOMRpcResultFutureAdapter.java
new file mode 100644 (file)
index 0000000..bc06110
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2018 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.controller.sal.core.compat;
+
+import com.google.common.util.concurrent.CheckedFuture;
+import org.opendaylight.mdsal.dom.api.DOMRpcException;
+import org.opendaylight.mdsal.dom.api.DOMRpcResult;
+import org.opendaylight.mdsal.dom.api.DefaultDOMRpcException;
+import org.opendaylight.mdsal.dom.spi.DefaultDOMRpcResult;
+import org.opendaylight.yangtools.util.concurrent.ExceptionMapper;
+
+/**
+ * Adapts a {@link org.opendaylight.controller.md.sal.dom.api.DOMRpcResult} CheckedFuture to a
+ * {@link DOMRpcResult} CheckedFuture.
+ *
+ * @author Thomas Pantelis
+ */
+public class MdsalDOMRpcResultFutureAdapter extends AbstractDOMRpcResultFutureAdapter<
+        DOMRpcResult, DOMRpcException, org.opendaylight.controller.md.sal.dom.api.DOMRpcResult,
+            org.opendaylight.controller.md.sal.dom.api.DOMRpcException> {
+    private static final ExceptionMapper<DOMRpcException> MDSAL_DOM_RPC_EX_MAPPER =
+            new ExceptionMapper<DOMRpcException>("rpc", DOMRpcException.class) {
+        @Override
+        protected DOMRpcException newWithCause(String message, Throwable cause) {
+            return cause instanceof DOMRpcException ? (DOMRpcException) cause
+                    : new DefaultDOMRpcException("RPC failed", cause);
+        }
+    };
+
+    public MdsalDOMRpcResultFutureAdapter(CheckedFuture<org.opendaylight.controller.md.sal.dom.api.DOMRpcResult,
+            org.opendaylight.controller.md.sal.dom.api.DOMRpcException> delegate) {
+        super(delegate, MDSAL_DOM_RPC_EX_MAPPER);
+    }
+
+    @Override
+    protected DOMRpcResult transform(org.opendaylight.controller.md.sal.dom.api.DOMRpcResult fromResult) {
+        return new DefaultDOMRpcResult(fromResult.getResult(), fromResult.getErrors());
+    }
+}