From aab5a09be514bc7622416bf2a6d4d6a7465f9d7c Mon Sep 17 00:00:00 2001 From: Robert Varga Date: Wed, 27 May 2015 12:54:03 +0200 Subject: [PATCH] Allow instantiation of more efficient RpcServiceInvokers Current invoker derived from binding class has the downside of performing localName->methodName conversion on each invocation, which costs us in the lookup path, as deriving the binding method name is a costly operation. Add a new static factory method for creating a RpcServiceInvoker instance base on a QName->Method map. Provide two additional implementations: a generic one, which performs a direct lookup in the map and a specialized one, which performs lookup only on the localName part of the qname. The latter is used after we have made sure all QNames come from the same QNameModule. Change-Id: I6a4b33f0487838b3ae6c15470823e4bc01114aa5 Signed-off-by: Robert Varga --- .../util/AbstractMappedRpcInvoker.java | 54 +++++++++++ .../util/ClassBasedRpcServiceInvoker.java | 50 +++++++++++ .../util/LocalNameRpcServiceInvoker.java | 43 +++++++++ .../binding/util/QNameRpcServiceInvoker.java | 27 ++++++ .../yang/binding/util/RpcMethodInvoker.java | 4 +- .../yang/binding/util/RpcServiceInvoker.java | 90 +++++++------------ 6 files changed, 209 insertions(+), 59 deletions(-) create mode 100644 yang/yang-binding/src/main/java/org/opendaylight/yangtools/yang/binding/util/AbstractMappedRpcInvoker.java create mode 100644 yang/yang-binding/src/main/java/org/opendaylight/yangtools/yang/binding/util/ClassBasedRpcServiceInvoker.java create mode 100644 yang/yang-binding/src/main/java/org/opendaylight/yangtools/yang/binding/util/LocalNameRpcServiceInvoker.java create mode 100644 yang/yang-binding/src/main/java/org/opendaylight/yangtools/yang/binding/util/QNameRpcServiceInvoker.java diff --git a/yang/yang-binding/src/main/java/org/opendaylight/yangtools/yang/binding/util/AbstractMappedRpcInvoker.java b/yang/yang-binding/src/main/java/org/opendaylight/yangtools/yang/binding/util/AbstractMappedRpcInvoker.java new file mode 100644 index 0000000000..8ee514e53c --- /dev/null +++ b/yang/yang-binding/src/main/java/org/opendaylight/yangtools/yang/binding/util/AbstractMappedRpcInvoker.java @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2015 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.yangtools.yang.binding.util; + +import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableMap.Builder; +import java.lang.reflect.Method; +import java.util.Map; +import java.util.Map.Entry; +import java.util.concurrent.Future; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import org.opendaylight.yangtools.yang.binding.DataObject; +import org.opendaylight.yangtools.yang.binding.RpcService; +import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.common.RpcResult; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +abstract class AbstractMappedRpcInvoker extends RpcServiceInvoker { + private static final Logger LOG = LoggerFactory.getLogger(AbstractMappedRpcInvoker.class); + private final Map map; + + protected AbstractMappedRpcInvoker(final Map map) { + final Builder b = ImmutableMap.builder(); + + for (Entry e : map.entrySet()) { + if (BindingReflections.isRpcMethod(e.getValue())) { + b.put(e.getKey(), RpcMethodInvoker.from(e.getValue())); + } else { + LOG.debug("Method {} is not an RPC method, ignoring it", e.getValue()); + } + } + + this.map = b.build(); + } + + protected abstract T qnameToKey(final QName qname); + + @Override + public final Future> invokeRpc(@Nonnull final RpcService impl, @Nonnull final QName rpcName, @Nullable final DataObject input) { + Preconditions.checkNotNull(impl, "implemetation must be supplied"); + + RpcMethodInvoker invoker = map.get(qnameToKey(rpcName)); + Preconditions.checkArgument(invoker != null, "Supplied RPC is not valid for implementation %s", impl); + return invoker.invokeOn(impl, input); + } +} diff --git a/yang/yang-binding/src/main/java/org/opendaylight/yangtools/yang/binding/util/ClassBasedRpcServiceInvoker.java b/yang/yang-binding/src/main/java/org/opendaylight/yangtools/yang/binding/util/ClassBasedRpcServiceInvoker.java new file mode 100644 index 0000000000..69a8179c83 --- /dev/null +++ b/yang/yang-binding/src/main/java/org/opendaylight/yangtools/yang/binding/util/ClassBasedRpcServiceInvoker.java @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2015 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.yangtools.yang.binding.util; + +import com.google.common.base.Preconditions; +import com.google.common.cache.CacheBuilder; +import com.google.common.cache.CacheLoader; +import com.google.common.cache.LoadingCache; +import java.lang.reflect.Method; +import java.util.HashMap; +import java.util.Map; +import org.opendaylight.yangtools.yang.binding.BindingMapping; +import org.opendaylight.yangtools.yang.binding.RpcService; +import org.opendaylight.yangtools.yang.common.QName; + +final class ClassBasedRpcServiceInvoker extends AbstractMappedRpcInvoker { + private static final LoadingCache, RpcServiceInvoker> INVOKERS = CacheBuilder.newBuilder() + .weakKeys() + .build(new CacheLoader, RpcServiceInvoker>() { + @Override + public RpcServiceInvoker load(final Class key) { + final Map ret = new HashMap<>(); + for (Method m : key.getMethods()) { + ret.put(m.getName(), m); + } + + return new ClassBasedRpcServiceInvoker(ret); + } + }); + + ClassBasedRpcServiceInvoker(final Map ret) { + super(ret); + } + + @Override + protected String qnameToKey(final QName qname) { + return BindingMapping.getMethodName(qname); + } + + static RpcServiceInvoker instanceFor(final Class type) { + Preconditions.checkArgument(type.isInterface()); + Preconditions.checkArgument(BindingReflections.isBindingClass(type)); + return INVOKERS.getUnchecked(type); + } +} diff --git a/yang/yang-binding/src/main/java/org/opendaylight/yangtools/yang/binding/util/LocalNameRpcServiceInvoker.java b/yang/yang-binding/src/main/java/org/opendaylight/yangtools/yang/binding/util/LocalNameRpcServiceInvoker.java new file mode 100644 index 0000000000..3dbce2883d --- /dev/null +++ b/yang/yang-binding/src/main/java/org/opendaylight/yangtools/yang/binding/util/LocalNameRpcServiceInvoker.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2015 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.yangtools.yang.binding.util; + +import com.google.common.base.Preconditions; +import java.lang.reflect.Method; +import java.util.HashMap; +import java.util.Map; +import java.util.Map.Entry; +import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.common.QNameModule; + +final class LocalNameRpcServiceInvoker extends AbstractMappedRpcInvoker { + private final QNameModule module; + + private LocalNameRpcServiceInvoker(final QNameModule module, final Map map) { + super(map); + this.module = Preconditions.checkNotNull(module); + } + + static RpcServiceInvoker instanceFor(final QNameModule module, final Map qnameToMethod) { + final Map map = new HashMap<>(); + for (Entry e : qnameToMethod.entrySet()) { + map.put(e.getKey().getLocalName(), e.getValue()); + } + + return new LocalNameRpcServiceInvoker(module, map); + } + + @Override + protected String qnameToKey(final QName qname) { + if (module.equals(qname.getModule())) { + return qname.getLocalName(); + } else { + return null; + } + } +} diff --git a/yang/yang-binding/src/main/java/org/opendaylight/yangtools/yang/binding/util/QNameRpcServiceInvoker.java b/yang/yang-binding/src/main/java/org/opendaylight/yangtools/yang/binding/util/QNameRpcServiceInvoker.java new file mode 100644 index 0000000000..82153d4195 --- /dev/null +++ b/yang/yang-binding/src/main/java/org/opendaylight/yangtools/yang/binding/util/QNameRpcServiceInvoker.java @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2015 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.yangtools.yang.binding.util; + +import java.lang.reflect.Method; +import java.util.Map; +import org.opendaylight.yangtools.yang.common.QName; + +final class QNameRpcServiceInvoker extends AbstractMappedRpcInvoker { + private QNameRpcServiceInvoker(final Map qnameToMethod) { + super(qnameToMethod); + } + + static RpcServiceInvoker instanceFor(final Map qnameToMethod) { + return new QNameRpcServiceInvoker(qnameToMethod); + } + + @Override + protected QName qnameToKey(final QName qname) { + return qname; + } +} diff --git a/yang/yang-binding/src/main/java/org/opendaylight/yangtools/yang/binding/util/RpcMethodInvoker.java b/yang/yang-binding/src/main/java/org/opendaylight/yangtools/yang/binding/util/RpcMethodInvoker.java index 2b121fcc20..466dfaf9b7 100644 --- a/yang/yang-binding/src/main/java/org/opendaylight/yangtools/yang/binding/util/RpcMethodInvoker.java +++ b/yang/yang-binding/src/main/java/org/opendaylight/yangtools/yang/binding/util/RpcMethodInvoker.java @@ -24,11 +24,11 @@ abstract class RpcMethodInvoker { protected abstract Future> invokeOn(RpcService impl, DataObject input); - protected static RpcMethodInvoker from(Method method) { + protected static RpcMethodInvoker from(final Method method) { Optional> input = BindingReflections.resolveRpcInputClass(method); try { MethodHandle methodHandle = LOOKUP.unreflect(method); - if(input.isPresent()) { + if (input.isPresent()) { return new RpcMethodInvokerWithInput(methodHandle); } return new RpcMethodInvokerWithoutInput(methodHandle); diff --git a/yang/yang-binding/src/main/java/org/opendaylight/yangtools/yang/binding/util/RpcServiceInvoker.java b/yang/yang-binding/src/main/java/org/opendaylight/yangtools/yang/binding/util/RpcServiceInvoker.java index 849b0a2d07..b6b9b47bbe 100644 --- a/yang/yang-binding/src/main/java/org/opendaylight/yangtools/yang/binding/util/RpcServiceInvoker.java +++ b/yang/yang-binding/src/main/java/org/opendaylight/yangtools/yang/binding/util/RpcServiceInvoker.java @@ -8,95 +8,71 @@ package org.opendaylight.yangtools.yang.binding.util; import com.google.common.base.Preconditions; -import com.google.common.cache.CacheBuilder; -import com.google.common.cache.CacheLoader; -import com.google.common.cache.LoadingCache; -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.ImmutableMap.Builder; import java.lang.reflect.Method; import java.util.Map; import java.util.concurrent.Future; import javax.annotation.Nonnull; import javax.annotation.Nullable; -import org.opendaylight.yangtools.yang.binding.BindingMapping; import org.opendaylight.yangtools.yang.binding.DataObject; 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.common.RpcResult; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * Provides single method invocation of RPCs on supplied instance. * * Rpc Service invoker provides common invocation interface for any subtype of {@link RpcService}. * via {@link #invokeRpc(RpcService, QName, DataObject)} method. - * - * - * */ -public final class RpcServiceInvoker { - - private static final LoadingCache, RpcServiceInvoker> INVOKERS = CacheBuilder.newBuilder() - .weakKeys() - .build(new CacheLoader, RpcServiceInvoker>() { - - @Override - public RpcServiceInvoker load(Class key) throws Exception { - return createInvoker(key); - } - - }); - - private final Map methodInvokers; - - private RpcServiceInvoker(Map methodInvokers) { - this.methodInvokers = Preconditions.checkNotNull(methodInvokers); - } +public abstract class RpcServiceInvoker { + private static final Logger LOG = LoggerFactory.getLogger(RpcServiceInvoker.class); /** - * * Creates RPCServiceInvoker for specified RpcService type * * @param type RpcService interface, which was generated from model. * @return Cached instance of {@link RpcServiceInvoker} for supplied RPC type. * */ - public static RpcServiceInvoker from(Class type) { - Preconditions.checkArgument(type.isInterface()); - Preconditions.checkArgument(BindingReflections.isBindingClass(type)); - return INVOKERS.getUnchecked(type); + public static RpcServiceInvoker from(final Class type) { + return ClassBasedRpcServiceInvoker.instanceFor(type); } /** - * Invokes supplied RPC on provided implementation of RPC Service. + * Creates an RPCServiceInvoker for specified QName-<Method mapping. * - * @param impl Imlementation on which RPC should be invoked. - * @param rpcName Name of RPC to be invoked. - * @param input Input data for RPC. - * @return Future which will complete once rpc procesing is finished. + * @param qnameToMethod translation mapping, must not be null nor empty. + * @return An {@link RpcMethodInvoker} instance. */ - public Future> invokeRpc(@Nonnull RpcService impl, @Nonnull QName rpcName,@Nullable DataObject input ) { - Preconditions.checkNotNull(impl, "implemetation must be supplied"); - return invoke(impl,BindingMapping.getMethodName(rpcName),input); - } - - private static RpcServiceInvoker createInvoker(Class key) { - return new RpcServiceInvoker(createInvokerMap(key)); - } + public static RpcServiceInvoker from(final Map qnameToMethod) { + Preconditions.checkArgument(!qnameToMethod.isEmpty()); + QNameModule module = null; - private static Map createInvokerMap(Class key) { - Builder ret = ImmutableMap.builder(); - for(Method method : key.getMethods()) { - if(BindingReflections.isRpcMethod(method)) { - ret.put(method.getName(), RpcMethodInvoker.from(method)); + for (QName qname : qnameToMethod.keySet()) { + if (module != null) { + if (!module.equals(qname.getModule())) { + LOG.debug("QNames from different modules {} and {}, falling back to QName map", module, qname.getModule()); + return QNameRpcServiceInvoker.instanceFor(qnameToMethod); + } + } else { + module = qname.getModule(); } - } - return ret.build(); - } - private Future> invoke(RpcService impl, String methodName, DataObject input) { - RpcMethodInvoker invoker = methodInvokers.get(methodName); - Preconditions.checkArgument(invoker != null,"Supplied rpc is not valid for implementation %s",impl); - return invoker.invokeOn(impl, input); + // All module are equal, which means we can use localName only + return LocalNameRpcServiceInvoker.instanceFor(module, qnameToMethod); } + + /** + * Invokes supplied RPC on provided implementation of RPC Service. + * + * @param impl Imlementation on which RPC should be invoked. + * @param rpcName Name of RPC to be invoked. + * @param input Input data for RPC. + * @return Future which will complete once rpc procesing is finished. + */ + public abstract Future> invokeRpc(@Nonnull final RpcService impl, @Nonnull final QName rpcName, @Nullable final DataObject input); } -- 2.36.6