Allow instantiation of more efficient RpcServiceInvokers 20/21220/4
authorRobert Varga <rovarga@cisco.com>
Wed, 27 May 2015 10:54:03 +0000 (12:54 +0200)
committerRobert Varga <rovarga@cisco.com>
Wed, 27 May 2015 15:33:24 +0000 (17:33 +0200)
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 <rovarga@cisco.com>
yang/yang-binding/src/main/java/org/opendaylight/yangtools/yang/binding/util/AbstractMappedRpcInvoker.java [new file with mode: 0644]
yang/yang-binding/src/main/java/org/opendaylight/yangtools/yang/binding/util/ClassBasedRpcServiceInvoker.java [new file with mode: 0644]
yang/yang-binding/src/main/java/org/opendaylight/yangtools/yang/binding/util/LocalNameRpcServiceInvoker.java [new file with mode: 0644]
yang/yang-binding/src/main/java/org/opendaylight/yangtools/yang/binding/util/QNameRpcServiceInvoker.java [new file with mode: 0644]
yang/yang-binding/src/main/java/org/opendaylight/yangtools/yang/binding/util/RpcMethodInvoker.java
yang/yang-binding/src/main/java/org/opendaylight/yangtools/yang/binding/util/RpcServiceInvoker.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 (file)
index 0000000..8ee514e
--- /dev/null
@@ -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<T> extends RpcServiceInvoker {
+    private static final Logger LOG = LoggerFactory.getLogger(AbstractMappedRpcInvoker.class);
+    private final Map<T, RpcMethodInvoker> map;
+
+    protected AbstractMappedRpcInvoker(final Map<T, Method> map) {
+        final Builder<T, RpcMethodInvoker> b = ImmutableMap.builder();
+
+        for (Entry<T, Method> 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<RpcResult<?>> 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 (file)
index 0000000..69a8179
--- /dev/null
@@ -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<String> {
+    private static final LoadingCache<Class<? extends RpcService>, RpcServiceInvoker> INVOKERS = CacheBuilder.newBuilder()
+            .weakKeys()
+            .build(new CacheLoader<Class<? extends RpcService>, RpcServiceInvoker>() {
+                @Override
+                public RpcServiceInvoker load(final Class<? extends RpcService> key) {
+                    final Map<String, Method> ret = new HashMap<>();
+                    for (Method m : key.getMethods()) {
+                        ret.put(m.getName(), m);
+                    }
+
+                    return new ClassBasedRpcServiceInvoker(ret);
+                }
+            });
+
+    ClassBasedRpcServiceInvoker(final Map<String, Method> ret) {
+        super(ret);
+    }
+
+    @Override
+    protected String qnameToKey(final QName qname) {
+        return BindingMapping.getMethodName(qname);
+    }
+
+    static RpcServiceInvoker instanceFor(final Class<? extends RpcService> 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 (file)
index 0000000..3dbce28
--- /dev/null
@@ -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<String> {
+    private final QNameModule module;
+
+    private LocalNameRpcServiceInvoker(final QNameModule module, final Map<String, Method> map) {
+        super(map);
+        this.module = Preconditions.checkNotNull(module);
+    }
+
+    static RpcServiceInvoker instanceFor(final QNameModule module, final Map<QName, Method> qnameToMethod) {
+        final Map<String, Method> map = new HashMap<>();
+        for (Entry<QName, Method> 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 (file)
index 0000000..82153d4
--- /dev/null
@@ -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<QName> {
+    private QNameRpcServiceInvoker(final Map<QName, Method> qnameToMethod) {
+        super(qnameToMethod);
+    }
+
+    static RpcServiceInvoker instanceFor(final Map<QName, Method> qnameToMethod) {
+        return new QNameRpcServiceInvoker(qnameToMethod);
+    }
+
+    @Override
+    protected QName qnameToKey(final QName qname) {
+        return qname;
+    }
+}
index 2b121fcc2087b626763d337d4e975da29136b3d3..466dfaf9b784a5d0f370440b522476f1ffe86f9d 100644 (file)
@@ -24,11 +24,11 @@ abstract class RpcMethodInvoker {
 
     protected abstract Future<RpcResult<?>> invokeOn(RpcService impl, DataObject input);
 
-    protected static RpcMethodInvoker from(Method method) {
+    protected static RpcMethodInvoker from(final Method method) {
         Optional<Class<? extends DataContainer>> input = BindingReflections.resolveRpcInputClass(method);
         try {
             MethodHandle methodHandle = LOOKUP.unreflect(method);
-            if(input.isPresent()) {
+            if (input.isPresent()) {
                 return new RpcMethodInvokerWithInput(methodHandle);
             }
             return new RpcMethodInvokerWithoutInput(methodHandle);
index 849b0a2d07d6332b35147a842b4f84fa6bdf7a68..b6b9b47bbe4e246ad40bf6cff89cad6364f87a89 100644 (file)
@@ -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<Class<? extends RpcService>, RpcServiceInvoker> INVOKERS = CacheBuilder.newBuilder()
-            .weakKeys()
-            .build(new CacheLoader<Class<? extends RpcService>, RpcServiceInvoker>() {
-
-                @Override
-                public RpcServiceInvoker load(Class<? extends RpcService> key) throws Exception {
-                    return createInvoker(key);
-                }
-
-            });
-
-    private final Map<String, RpcMethodInvoker> methodInvokers;
-
-    private RpcServiceInvoker(Map<String, RpcMethodInvoker> 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<? extends RpcService> type) {
-        Preconditions.checkArgument(type.isInterface());
-        Preconditions.checkArgument(BindingReflections.isBindingClass(type));
-        return INVOKERS.getUnchecked(type);
+    public static RpcServiceInvoker from(final Class<? extends RpcService> type) {
+        return ClassBasedRpcServiceInvoker.instanceFor(type);
     }
 
     /**
-     * Invokes supplied RPC on provided implementation of RPC Service.
+     * Creates an RPCServiceInvoker for specified QName-&lt;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<RpcResult<?>> 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<? extends RpcService> key) {
-        return new RpcServiceInvoker(createInvokerMap(key));
-    }
+    public static RpcServiceInvoker from(final Map<QName, Method> qnameToMethod) {
+        Preconditions.checkArgument(!qnameToMethod.isEmpty());
+        QNameModule module = null;
 
-    private static Map<String, RpcMethodInvoker> createInvokerMap(Class<? extends RpcService> key) {
-        Builder<String, RpcMethodInvoker> ret = ImmutableMap.<String, RpcMethodInvoker>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<RpcResult<?>> 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<RpcResult<?>> invokeRpc(@Nonnull final RpcService impl, @Nonnull final QName rpcName, @Nullable final DataObject input);
 }