Eliminate RpcMethodInvoker 68/103368/2
authorRobert Varga <robert.varga@pantheon.tech>
Thu, 24 Nov 2022 21:18:24 +0000 (22:18 +0100)
committerRobert Varga <robert.varga@pantheon.tech>
Thu, 24 Nov 2022 23:24:06 +0000 (00:24 +0100)
RpcMethodInvoker is a useless indirection around a MethodHandle with
a known signature. Eliminate this indirection and cache all
MethodHandles for a particular Rpc, passing each of them the its
individual LegacyDOMRpcImplementationAdapter.

This allows LegacyDOMRpcImplementationAdapter to fuse the delegate
object into its internal MethodHandle -- hence we do not need to track
the delegate separately.

JIRA: MDSAL-86
Change-Id: I9b89d353fc61e313c82e997ffeddbbb473bbd07f
Signed-off-by: Robert Varga <robert.varga@pantheon.tech>
binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/BindingDOMRpcProviderServiceAdapter.java
binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/CurrentAdapterSerializer.java
binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/LegacyDOMRpcImplementationAdapter.java
binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/invoke/RpcMethodInvoker.java [deleted file]
binding/mdsal-binding-dom-adapter/src/test/java/org/opendaylight/mdsal/binding/dom/adapter/BindingNormalizedCodecTest.java
binding/mdsal-binding-dom-adapter/src/test/java/org/opendaylight/mdsal/binding/dom/adapter/invoke/RpcMethodInvokerTest.java [deleted file]

index d9f5eabc0b80d560415668505d1fd2a98c973c7b..a6092dfc9983b3e4cb7b87f09a737bb230a3b41d 100644 (file)
@@ -9,18 +9,14 @@ package org.opendaylight.mdsal.binding.dom.adapter;
 
 import com.google.common.annotations.VisibleForTesting;
 import com.google.common.collect.ClassToInstanceMap;
-import com.google.common.collect.ImmutableBiMap;
 import com.google.common.collect.ImmutableClassToInstanceMap;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
-import java.lang.reflect.Method;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.HashSet;
 import java.util.Set;
 import org.opendaylight.mdsal.binding.api.RpcProviderService;
-import org.opendaylight.mdsal.binding.dom.adapter.invoke.RpcMethodInvoker;
-import org.opendaylight.mdsal.binding.spec.naming.BindingMapping;
 import org.opendaylight.mdsal.binding.spec.reflect.BindingReflections;
 import org.opendaylight.mdsal.dom.api.DOMRpcIdentifier;
 import org.opendaylight.mdsal.dom.api.DOMRpcImplementation;
@@ -31,14 +27,12 @@ import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
 import org.opendaylight.yangtools.yang.binding.Rpc;
 import org.opendaylight.yangtools.yang.binding.RpcService;
 import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.YangConstants;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 
 @VisibleForTesting
 public class BindingDOMRpcProviderServiceAdapter extends AbstractBindingAdapter<DOMRpcProviderService>
         implements RpcProviderService {
-    private static final Logger LOG = LoggerFactory.getLogger(BindingDOMRpcProviderServiceAdapter.class);
     private static final ImmutableSet<YangInstanceIdentifier> GLOBAL = ImmutableSet.of(YangInstanceIdentifier.empty());
 
     public BindingDOMRpcProviderServiceAdapter(final AdapterContext adapterContext,
@@ -119,12 +113,14 @@ public class BindingDOMRpcProviderServiceAdapter extends AbstractBindingAdapter<
     private <S extends RpcService, T extends S> ObjectRegistration<T> register(
             final CurrentAdapterSerializer serializer, final Class<S> type, final T implementation,
             final Collection<YangInstanceIdentifier> rpcContextPaths) {
-        final var qnameToMethod = createQNameToMethod(currentSerializer(), type);
+        // FIXME: do not use BindingReflections here
+        final var inputName = YangConstants.operationInputQName(BindingReflections.getQNameModule(type)).intern();
+        final var methodHandles = currentSerializer().getRpcMethods(type);
         final var builder = ImmutableMap.<DOMRpcIdentifier, DOMRpcImplementation>builderWithExpectedSize(
-            qnameToMethod.size());
-        for (var entry : qnameToMethod.entrySet()) {
-            final var impl = new LegacyDOMRpcImplementationAdapter<>(adapterContext(), type, implementation,
-                RpcMethodInvoker.from(entry.getValue()));
+            methodHandles.size());
+        for (var entry : methodHandles.entrySet()) {
+            final var impl = new LegacyDOMRpcImplementationAdapter(adapterContext(), inputName,
+                entry.getValue().bindTo(implementation));
             for (var id : createDomRpcIdentifiers(Set.of(entry.getKey()), rpcContextPaths)) {
                 builder.put(id, impl);
             }
@@ -134,34 +130,6 @@ public class BindingDOMRpcProviderServiceAdapter extends AbstractBindingAdapter<
             getDelegate().registerRpcImplementations(builder.build()));
     }
 
-    @Deprecated
-    @VisibleForTesting
-    // FIXME: This should be probably part of Binding Runtime context
-    static ImmutableMap<QName, Method> createQNameToMethod(final CurrentAdapterSerializer serializer,
-            final Class<? extends RpcService> key) {
-        final var moduleName = BindingReflections.getQNameModule(key);
-        final var runtimeContext = serializer.getRuntimeContext();
-        final var module = runtimeContext.getEffectiveModelContext().findModule(moduleName).orElse(null);
-        if (module == null) {
-            LOG.trace("Schema for {} is not available; expected module name: {}; BindingRuntimeContext: {}",
-                key, moduleName, runtimeContext);
-            throw new IllegalStateException(String.format("Schema for %s is not available; expected module name: %s;"
-                + " full BindingRuntimeContext available in trace log", key, moduleName));
-        }
-
-        final var ret = ImmutableBiMap.<QName, Method>builder();
-        try {
-            for (var rpcDef : module.getRpcs()) {
-                final var rpcName = rpcDef.getQName();
-                ret.put(rpcName, key.getMethod(BindingMapping.getRpcMethodName(rpcName),
-                    runtimeContext.getRpcInput(rpcName)));
-            }
-        } catch (NoSuchMethodException e) {
-            throw new IllegalStateException("Rpc defined in model does not have representation in generated class.", e);
-        }
-        return ret.build();
-    }
-
     private static Set<DOMRpcIdentifier> createDomRpcIdentifiers(final Set<QName> rpcs,
             final Collection<YangInstanceIdentifier> paths) {
         final Set<DOMRpcIdentifier> ret = new HashSet<>();
index 7a7cdf6ff19acf08a83178fc0203fb9b6ceed454..6fe5c99b4cd36d877d35aa5b0c183c4c120b00ec 100644 (file)
@@ -16,6 +16,14 @@ import com.google.common.annotations.VisibleForTesting;
 import com.google.common.cache.CacheBuilder;
 import com.google.common.cache.CacheLoader;
 import com.google.common.cache.LoadingCache;
+import com.google.common.collect.ImmutableBiMap;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Maps;
+import com.google.common.util.concurrent.ListenableFuture;
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodType;
+import java.lang.reflect.Method;
 import java.util.Collection;
 import java.util.Map;
 import java.util.Map.Entry;
@@ -31,10 +39,13 @@ import org.opendaylight.mdsal.binding.dom.codec.spi.BindingDOMCodecServices;
 import org.opendaylight.mdsal.binding.dom.codec.spi.ForwardingBindingDOMCodecServices;
 import org.opendaylight.mdsal.binding.model.api.JavaTypeName;
 import org.opendaylight.mdsal.binding.runtime.api.InputRuntimeType;
+import org.opendaylight.mdsal.binding.spec.naming.BindingMapping;
 import org.opendaylight.mdsal.binding.spec.reflect.BindingReflections;
 import org.opendaylight.mdsal.dom.api.DOMDataTreeIdentifier;
 import org.opendaylight.yangtools.yang.binding.DataObject;
 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.binding.RpcService;
+import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.common.QNameModule;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier;
@@ -44,10 +55,16 @@ import org.opendaylight.yangtools.yang.model.api.stmt.ListEffectiveStatement;
 import org.opendaylight.yangtools.yang.model.api.stmt.NotificationEffectiveStatement;
 import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier.Absolute;
 import org.opendaylight.yangtools.yang.model.util.SchemaInferenceStack;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 @Beta
 @VisibleForTesting
 public final class CurrentAdapterSerializer extends ForwardingBindingDOMCodecServices {
+    private static final Logger LOG = LoggerFactory.getLogger(CurrentAdapterSerializer.class);
+    private static final MethodType RPC_SERVICE_METHOD_SIGNATURE = MethodType.methodType(ListenableFuture.class,
+        RpcService.class, DataObject.class);
+
     private final LoadingCache<InstanceIdentifier<?>, YangInstanceIdentifier> cache = CacheBuilder.newBuilder()
             .softValues().build(new CacheLoader<InstanceIdentifier<?>, YangInstanceIdentifier>() {
                 @Override
@@ -57,6 +74,9 @@ public final class CurrentAdapterSerializer extends ForwardingBindingDOMCodecSer
             });
 
     private final ConcurrentMap<JavaTypeName, ContextReferenceExtractor> extractors = new ConcurrentHashMap<>();
+    @Deprecated
+    private final ConcurrentMap<Class<? extends RpcService>, ImmutableMap<QName, MethodHandle>> rpcMethods =
+        new ConcurrentHashMap<>();
     private final @NonNull BindingDOMCodecServices delegate;
 
     public CurrentAdapterSerializer(final BindingDOMCodecServices delegate) {
@@ -127,6 +147,49 @@ public final class CurrentAdapterSerializer extends ForwardingBindingDOMCodecSer
         return raced != null ? raced : created;
     }
 
+    @Deprecated
+    @NonNull ImmutableMap<QName, MethodHandle> getRpcMethods(final @NonNull Class<? extends RpcService> serviceType) {
+        return rpcMethods.computeIfAbsent(serviceType, ignored -> {
+            final var lookup = MethodHandles.publicLookup();
+            return ImmutableMap.copyOf(Maps.transformValues(createQNameToMethod(serviceType), method -> {
+                final MethodHandle raw;
+                try {
+                    raw = lookup.unreflect(method);
+                } catch (IllegalAccessException e) {
+                    throw new IllegalStateException("Lookup on public method failed", e);
+                }
+                return raw.asType(RPC_SERVICE_METHOD_SIGNATURE);
+            }));
+        });
+    }
+
+    @Deprecated
+    @VisibleForTesting
+    // FIXME: This should be probably part of Binding Runtime context
+    ImmutableMap<QName, Method> createQNameToMethod(final Class<? extends RpcService> key) {
+        final var moduleName = BindingReflections.getQNameModule(key);
+        final var runtimeContext = getRuntimeContext();
+        final var module = runtimeContext.getEffectiveModelContext().findModule(moduleName).orElse(null);
+        if (module == null) {
+            LOG.trace("Schema for {} is not available; expected module name: {}; BindingRuntimeContext: {}",
+                key, moduleName, runtimeContext);
+            throw new IllegalStateException(String.format("Schema for %s is not available; expected module name: %s;"
+                + " full BindingRuntimeContext available in trace log", key, moduleName));
+        }
+
+        final var ret = ImmutableBiMap.<QName, Method>builder();
+        try {
+            for (var rpcDef : module.getRpcs()) {
+                final var rpcName = rpcDef.getQName();
+                ret.put(rpcName, key.getMethod(BindingMapping.getRpcMethodName(rpcName),
+                    runtimeContext.getRpcInput(rpcName)));
+            }
+        } catch (NoSuchMethodException e) {
+            throw new IllegalStateException("Rpc defined in model does not have representation in generated class.", e);
+        }
+        return ret.build();
+    }
+
     private @NonNull Entry<SchemaInferenceStack, QNameModule> resolvePath(final @NonNull InstanceIdentifier<?> path) {
         final var stack = SchemaInferenceStack.of(getRuntimeContext().getEffectiveModelContext());
         final var it = toYangInstanceIdentifier(path).getPathArguments().iterator();
index 677176b4846b98a484267a3ed8f082c98609ad06..765ec37287c17230d159053a090d4680a69c28f1 100644 (file)
@@ -9,31 +9,33 @@ package org.opendaylight.mdsal.binding.dom.adapter;
 
 import static java.util.Objects.requireNonNull;
 
+import com.google.common.base.Throwables;
 import com.google.common.util.concurrent.ListenableFuture;
-import org.opendaylight.mdsal.binding.dom.adapter.invoke.RpcMethodInvoker;
-import org.opendaylight.mdsal.binding.spec.reflect.BindingReflections;
+import java.lang.invoke.MethodHandle;
 import org.opendaylight.mdsal.dom.api.DOMRpcIdentifier;
-import org.opendaylight.yangtools.yang.binding.RpcService;
+import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.common.RpcResult;
-import org.opendaylight.yangtools.yang.common.YangConstants;
 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
 
 @Deprecated(since = "11.0.0", forRemoval = true)
-final class LegacyDOMRpcImplementationAdapter<T extends RpcService> extends AbstractDOMRpcImplementationAdapter {
-    private final RpcMethodInvoker invoker;
-    private final T delegate;
+final class LegacyDOMRpcImplementationAdapter extends AbstractDOMRpcImplementationAdapter {
+    private final MethodHandle handle;
 
-    LegacyDOMRpcImplementationAdapter(final AdapterContext adapterContext, final Class<T> type, final T delegate,
-            final RpcMethodInvoker invoker) {
-        // FIXME: do not use BindingReflections here
-        super(adapterContext, YangConstants.operationInputQName(BindingReflections.getQNameModule(type)).intern());
-        this.delegate = requireNonNull(delegate);
-        this.invoker = requireNonNull(invoker);
+    LegacyDOMRpcImplementationAdapter(final AdapterContext adapterContext, final QName inputName,
+            final MethodHandle handle) {
+        super(adapterContext, inputName);
+        this.handle = requireNonNull(handle);
     }
 
     @Override
+    @SuppressWarnings("checkstyle:illegalCatch")
     ListenableFuture<RpcResult<?>> invokeRpc(final CurrentAdapterSerializer serializer, final DOMRpcIdentifier rpc,
             final ContainerNode input) {
-        return invoker.invokeOn(delegate, deserialize(serializer, rpc.getType(), input));
+        try {
+            return (ListenableFuture<RpcResult<?>>) handle.invokeExact(deserialize(serializer, rpc.getType(), input));
+        } catch (Throwable e) {
+            Throwables.throwIfUnchecked(e);
+            throw new IllegalStateException(e);
+        }
     }
 }
diff --git a/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/invoke/RpcMethodInvoker.java b/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/invoke/RpcMethodInvoker.java
deleted file mode 100644 (file)
index 2574b96..0000000
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Copyright (c) 2013 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.mdsal.binding.dom.adapter.invoke;
-
-import com.google.common.annotations.VisibleForTesting;
-import com.google.common.base.Throwables;
-import com.google.common.util.concurrent.ListenableFuture;
-import java.lang.invoke.MethodHandle;
-import java.lang.invoke.MethodHandles;
-import java.lang.invoke.MethodType;
-import java.lang.reflect.Method;
-import org.eclipse.jdt.annotation.NonNull;
-import org.opendaylight.yangtools.yang.binding.DataObject;
-import org.opendaylight.yangtools.yang.binding.RpcService;
-import org.opendaylight.yangtools.yang.common.RpcResult;
-
-@Deprecated(since = "11.0.0", forRemoval = true)
-public final class RpcMethodInvoker {
-    private static final MethodType INVOCATION_SIGNATURE = MethodType.methodType(ListenableFuture.class,
-        RpcService.class, DataObject.class);
-
-    private final MethodHandle handle;
-
-    @VisibleForTesting
-    RpcMethodInvoker(final MethodHandle handle) {
-        this.handle = handle.asType(INVOCATION_SIGNATURE);
-    }
-
-    public static @NonNull RpcMethodInvoker from(final Method method) {
-        try {
-            return new RpcMethodInvoker(MethodHandles.publicLookup().unreflect(method));
-        } catch (IllegalAccessException e) {
-            throw new IllegalStateException("Lookup on public method failed.", e);
-        }
-    }
-
-    @SuppressWarnings("checkstyle:illegalCatch")
-    public @NonNull ListenableFuture<RpcResult<?>> invokeOn(final RpcService impl, final DataObject input) {
-        try {
-            return (ListenableFuture<RpcResult<?>>) handle.invokeExact(impl, input);
-        } catch (Throwable e) {
-            Throwables.throwIfUnchecked(e);
-            throw new IllegalStateException(e);
-        }
-    }
-}
index 31a9044a476eb53678b5c9befb3a4ec2d9abf163..7383c4d037e572bcd6f53d45212bf249eede5e59 100644 (file)
@@ -75,9 +75,7 @@ public class BindingNormalizedCodecTest extends AbstractSchemaAwareTest {
 
     @Test
     public void testGetRpcMethodToQName() {
-        assertTrue(
-            BindingDOMRpcProviderServiceAdapter.createQNameToMethod(serializer,
-                OpendaylightTestRpcServiceService.class)
+        assertTrue(serializer.createQNameToMethod(OpendaylightTestRpcServiceService.class)
             .values().stream()
             .map(Method::getName)
             .anyMatch("rockTheHouse"::equals));
diff --git a/binding/mdsal-binding-dom-adapter/src/test/java/org/opendaylight/mdsal/binding/dom/adapter/invoke/RpcMethodInvokerTest.java b/binding/mdsal-binding-dom-adapter/src/test/java/org/opendaylight/mdsal/binding/dom/adapter/invoke/RpcMethodInvokerTest.java
deleted file mode 100644 (file)
index c2a5a60..0000000
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Copyright (c) 2016 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.mdsal.binding.dom.adapter.invoke;
-
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertThrows;
-
-import com.google.common.util.concurrent.Futures;
-import com.google.common.util.concurrent.ListenableFuture;
-import java.lang.invoke.MethodHandle;
-import java.lang.invoke.MethodHandles;
-import org.junit.Test;
-import org.opendaylight.yangtools.yang.binding.DataObject;
-import org.opendaylight.yangtools.yang.binding.RpcService;
-
-@Deprecated(since = "11.0.0", forRemoval = true)
-public class RpcMethodInvokerTest {
-    private static final TestImplClassWithInput TEST_IMPL_CLASS = new TestImplClassWithInput();
-
-    @Test
-    public void invokeOnTest() throws Exception {
-        final MethodHandle methodHandle = MethodHandles.lookup().unreflect(
-                TestImplClassWithInput.class.getDeclaredMethod("testMethod", RpcService.class, DataObject.class));
-        final RpcMethodInvoker rpcMethodInvokerWithInput = new RpcMethodInvoker(methodHandle);
-        assertNotNull(rpcMethodInvokerWithInput.invokeOn(TEST_IMPL_CLASS, null));
-    }
-
-    @Test
-    public void invokeOnWithException() throws Exception {
-        final MethodHandle methodHandle = MethodHandles.lookup().unreflect(TestImplClassWithInput.class
-                .getDeclaredMethod("testMethodWithException", RpcService.class, DataObject.class));
-        final RpcMethodInvoker rpcMethodInvokerWithInput = new RpcMethodInvoker(methodHandle);
-
-        assertThrows(InternalError.class, () -> rpcMethodInvokerWithInput.invokeOn(TEST_IMPL_CLASS, null));
-    }
-
-    static final class TestImplClassWithInput implements RpcService {
-
-        static ListenableFuture<?> testMethod(final RpcService testArg, final DataObject data) {
-            return Futures.immediateFuture(null);
-        }
-
-        static ListenableFuture<?> testMethodWithException(final RpcService testArg, final DataObject data) {
-            throw new InternalError();
-        }
-    }
-}
\ No newline at end of file