From 3ccf250746baa1e98b1e7660fc0b83b141c7a9f6 Mon Sep 17 00:00:00 2001 From: Robert Varga Date: Thu, 24 Nov 2022 22:18:24 +0100 Subject: [PATCH] Eliminate RpcMethodInvoker 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 --- .../BindingDOMRpcProviderServiceAdapter.java | 48 +++----------- .../dom/adapter/CurrentAdapterSerializer.java | 63 +++++++++++++++++++ .../LegacyDOMRpcImplementationAdapter.java | 30 ++++----- .../dom/adapter/invoke/RpcMethodInvoker.java | 51 --------------- .../adapter/BindingNormalizedCodecTest.java | 4 +- .../adapter/invoke/RpcMethodInvokerTest.java | 52 --------------- 6 files changed, 88 insertions(+), 160 deletions(-) delete mode 100644 binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/invoke/RpcMethodInvoker.java delete mode 100644 binding/mdsal-binding-dom-adapter/src/test/java/org/opendaylight/mdsal/binding/dom/adapter/invoke/RpcMethodInvokerTest.java diff --git a/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/BindingDOMRpcProviderServiceAdapter.java b/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/BindingDOMRpcProviderServiceAdapter.java index d9f5eabc0b..a6092dfc99 100644 --- a/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/BindingDOMRpcProviderServiceAdapter.java +++ b/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/BindingDOMRpcProviderServiceAdapter.java @@ -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 implements RpcProviderService { - private static final Logger LOG = LoggerFactory.getLogger(BindingDOMRpcProviderServiceAdapter.class); private static final ImmutableSet GLOBAL = ImmutableSet.of(YangInstanceIdentifier.empty()); public BindingDOMRpcProviderServiceAdapter(final AdapterContext adapterContext, @@ -119,12 +113,14 @@ public class BindingDOMRpcProviderServiceAdapter extends AbstractBindingAdapter< private ObjectRegistration register( final CurrentAdapterSerializer serializer, final Class type, final T implementation, final Collection 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.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 createQNameToMethod(final CurrentAdapterSerializer serializer, - final Class 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.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 createDomRpcIdentifiers(final Set rpcs, final Collection paths) { final Set ret = new HashSet<>(); diff --git a/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/CurrentAdapterSerializer.java b/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/CurrentAdapterSerializer.java index 7a7cdf6ff1..6fe5c99b4c 100644 --- a/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/CurrentAdapterSerializer.java +++ b/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/CurrentAdapterSerializer.java @@ -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, YangInstanceIdentifier> cache = CacheBuilder.newBuilder() .softValues().build(new CacheLoader, YangInstanceIdentifier>() { @Override @@ -57,6 +74,9 @@ public final class CurrentAdapterSerializer extends ForwardingBindingDOMCodecSer }); private final ConcurrentMap extractors = new ConcurrentHashMap<>(); + @Deprecated + private final ConcurrentMap, ImmutableMap> 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 getRpcMethods(final @NonNull Class 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 createQNameToMethod(final Class 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.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 resolvePath(final @NonNull InstanceIdentifier path) { final var stack = SchemaInferenceStack.of(getRuntimeContext().getEffectiveModelContext()); final var it = toYangInstanceIdentifier(path).getPathArguments().iterator(); diff --git a/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/LegacyDOMRpcImplementationAdapter.java b/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/LegacyDOMRpcImplementationAdapter.java index 677176b484..765ec37287 100644 --- a/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/LegacyDOMRpcImplementationAdapter.java +++ b/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/LegacyDOMRpcImplementationAdapter.java @@ -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 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 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> invokeRpc(final CurrentAdapterSerializer serializer, final DOMRpcIdentifier rpc, final ContainerNode input) { - return invoker.invokeOn(delegate, deserialize(serializer, rpc.getType(), input)); + try { + return (ListenableFuture>) 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 index 2574b96ebc..0000000000 --- a/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/invoke/RpcMethodInvoker.java +++ /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> invokeOn(final RpcService impl, final DataObject input) { - try { - return (ListenableFuture>) handle.invokeExact(impl, input); - } catch (Throwable e) { - Throwables.throwIfUnchecked(e); - throw new IllegalStateException(e); - } - } -} diff --git a/binding/mdsal-binding-dom-adapter/src/test/java/org/opendaylight/mdsal/binding/dom/adapter/BindingNormalizedCodecTest.java b/binding/mdsal-binding-dom-adapter/src/test/java/org/opendaylight/mdsal/binding/dom/adapter/BindingNormalizedCodecTest.java index 31a9044a47..7383c4d037 100644 --- a/binding/mdsal-binding-dom-adapter/src/test/java/org/opendaylight/mdsal/binding/dom/adapter/BindingNormalizedCodecTest.java +++ b/binding/mdsal-binding-dom-adapter/src/test/java/org/opendaylight/mdsal/binding/dom/adapter/BindingNormalizedCodecTest.java @@ -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 index c2a5a60bc8..0000000000 --- a/binding/mdsal-binding-dom-adapter/src/test/java/org/opendaylight/mdsal/binding/dom/adapter/invoke/RpcMethodInvokerTest.java +++ /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 -- 2.36.6