Introduced MethodHandle based invokers for Notification and RPCs 93/16093/3
authorTony Tkacik <ttkacik@cisco.com>
Thu, 5 Mar 2015 21:04:43 +0000 (22:04 +0100)
committerTony Tkacik <ttkacik@cisco.com>
Sun, 8 Mar 2015 21:13:05 +0000 (22:13 +0100)
This invokers are used to provide genereralized access to any
generated callback / rpc method using one approach for
any RPC.

Change-Id: I4d6ce7c71f658b5e2c6e805479aa47a8f09fb989
Signed-off-by: Tony Tkacik <ttkacik@cisco.com>
yang/yang-binding/src/main/java/org/opendaylight/yangtools/yang/binding/util/BindingReflections.java
yang/yang-binding/src/main/java/org/opendaylight/yangtools/yang/binding/util/NotificationListenerInvoker.java [new file with mode: 0644]
yang/yang-binding/src/main/java/org/opendaylight/yangtools/yang/binding/util/RpcMethodInvoker.java [new file with mode: 0644]
yang/yang-binding/src/main/java/org/opendaylight/yangtools/yang/binding/util/RpcMethodInvokerWithInput.java [new file with mode: 0644]
yang/yang-binding/src/main/java/org/opendaylight/yangtools/yang/binding/util/RpcMethodInvokerWithoutInput.java [new file with mode: 0644]
yang/yang-binding/src/main/java/org/opendaylight/yangtools/yang/binding/util/RpcServiceInvoker.java [new file with mode: 0644]

index 6d6decb8ec74d5bb25fa98e8b727f4306cc9b3dd..3f4c85e4b2a9b2aecc1c83a029be47ef227c1f62 100644 (file)
@@ -9,6 +9,7 @@ package org.opendaylight.yangtools.yang.binding.util;
 
 import static com.google.common.base.Preconditions.checkArgument;
 import static com.google.common.base.Preconditions.checkState;
+
 import com.google.common.base.Optional;
 import com.google.common.cache.CacheBuilder;
 import com.google.common.cache.CacheLoader;
@@ -20,6 +21,7 @@ import java.lang.reflect.Field;
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 import java.lang.reflect.Type;
+import java.net.URI;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.LinkedList;
@@ -44,6 +46,7 @@ import org.opendaylight.yangtools.yang.binding.RpcService;
 import org.opendaylight.yangtools.yang.binding.YangModelBindingProvider;
 import org.opendaylight.yangtools.yang.binding.YangModuleInfo;
 import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -59,8 +62,6 @@ public class BindingReflections {
             .expireAfterAccess(EXPIRATION_TIME, TimeUnit.SECONDS) //
             .build(new ClassToQNameLoader());
 
-
-
     private BindingReflections() {
         throw new UnsupportedOperationException("Utility class.");
     }
@@ -68,8 +69,8 @@ public class BindingReflections {
     /**
      * Find augmentation target class from concrete Augmentation class
      *
-     * This method uses first generic argument of
-     * implemented {@link Augmentation} interface.
+     * This method uses first generic argument of implemented
+     * {@link Augmentation} interface.
      *
      * @param augmentation
      *            {@link Augmentation} subclass for which we want to determine
@@ -85,8 +86,8 @@ public class BindingReflections {
     /**
      * Find data hierarchy parent from concrete Data class
      *
-     * This method uses first generic argument of
-     * implemented {@link ChildOf} interface.
+     * This method uses first generic argument of implemented {@link ChildOf}
+     * interface.
      *
      * @param childClass
      *            child class for which we want to find the parent class.
@@ -100,7 +101,8 @@ public class BindingReflections {
      * Find data hierarchy parent from concrete Data class
      *
      * This method is shorthand which gets DataObject class by invoking
-     * {@link DataObject#getImplementedInterface()} and uses {@link #findHierarchicalParent(Class)}.
+     * {@link DataObject#getImplementedInterface()} and uses
+     * {@link #findHierarchicalParent(Class)}.
      *
      * @param child
      *            Child object for which the parent needs to be located.
@@ -133,7 +135,8 @@ public class BindingReflections {
      *
      *
      *
-     * @param possibleMethod Method to check
+     * @param possibleMethod
+     *            Method to check
      * @return true if method is RPC invocation, false otherwise.
      */
     public static boolean isRpcMethod(final Method possibleMethod) {
@@ -146,9 +149,10 @@ public class BindingReflections {
      *
      * Extracts Output class for RPC method
      *
-     * @param targetMethod method to scan
-     * @return Optional.absent() if result type could not be get,
-     *         or return type is Void.
+     * @param targetMethod
+     *            method to scan
+     * @return Optional.absent() if result type could not be get, or return type
+     *         is Void.
      */
     @SuppressWarnings("rawtypes")
     public static Optional<Class<?>> resolveRpcOutputClass(final Method targetMethod) {
@@ -166,7 +170,8 @@ public class BindingReflections {
      *
      * Extracts input class for RPC method
      *
-     * @param targetMethod method to scan
+     * @param targetMethod
+     *            method to scan
      * @return Optional.absent() if rpc has no input, Rpc input type otherwise.
      */
     @SuppressWarnings("unchecked")
@@ -215,7 +220,8 @@ public class BindingReflections {
     /**
      * Returns root package name for suplied package.
      *
-     * @param pkg Package for which find model root package.
+     * @param pkg
+     *            Package for which find model root package.
      * @return Package of model root.
      */
     public static String getModelRootPackageName(final Package pkg) {
@@ -225,7 +231,8 @@ public class BindingReflections {
     /**
      * Returns root package name for supplied package name.
      *
-     * @param name Package for which find model root package.
+     * @param name
+     *            Package for which find model root package.
      * @return Package of model root.
      */
     public static String getModelRootPackageName(final String name) {
@@ -238,12 +245,30 @@ public class BindingReflections {
         return match.group(0);
     }
 
+    public static final QNameModule getQNameModule(Class<?> clz) {
+        if(DataContainer.class.isAssignableFrom(clz) || BaseIdentity.class.isAssignableFrom(clz)) {
+            return findQName(clz).getModule();
+        }
+        try {
+            YangModuleInfo modInfo = BindingReflections.getModuleInfo(clz);
+            return getQNameModule(modInfo);
+        } catch (Exception e) {
+            throw new IllegalStateException("Unable to get QName of defining model.", e);
+        }
+    }
+
+    public static final QNameModule getQNameModule(YangModuleInfo modInfo) {
+        return QNameModule.create(URI.create(modInfo.getNamespace()), QName.parseRevision(modInfo.getRevision()));
+    }
+
     /**
      *
-     * Returns instance of {@link YangModuleInfo} of declaring model for specific class.
+     * Returns instance of {@link YangModuleInfo} of declaring model for
+     * specific class.
      *
      * @param cls
-     * @return Instance of {@link YangModuleInfo} associated with model, from which this class was derived.
+     * @return Instance of {@link YangModuleInfo} associated with model, from
+     *         which this class was derived.
      * @throws Exception
      */
     public static YangModuleInfo getModuleInfo(final Class<?> cls) throws Exception {
@@ -268,7 +293,8 @@ public class BindingReflections {
      *
      * Check if supplied class is derived from YANG model.
      *
-     * @param cls Class to check
+     * @param cls
+     *            Class to check
      * @return true if class is derived from YANG model.
      */
     public static boolean isBindingClass(final Class<?> cls) {
@@ -305,7 +331,7 @@ public class BindingReflections {
      * @return
      */
     public static boolean isNotification(final Class<?> potentialNotification) {
-        checkArgument(potentialNotification != null,"potentialNotification must not be null.");
+        checkArgument(potentialNotification != null, "potentialNotification must not be null.");
         return Notification.class.isAssignableFrom(potentialNotification);
     }
 
@@ -323,21 +349,24 @@ public class BindingReflections {
     }
 
     /**
-    *
-    * Loads {@link YangModuleInfo} infos available on supplied classloader.
-    *
-    * {@link YangModuleInfo} are discovered using {@link ServiceLoader}
-    * for {@link YangModelBindingProvider}. {@link YangModelBindingProvider}
-    * are simple classes which holds only pointers to actual instance
-    * {@link YangModuleInfo}.
-    *
-    * When {@link YangModuleInfo} is available, all dependencies are recursivelly collected
-    * into returning set by collecting results of {@link YangModuleInfo#getImportedModules()}.
-    *
-    *
-    * @param loader Classloader for which {@link YangModuleInfo} should be retrieved.
-    * @return Set of {@link YangModuleInfo} available for supplied classloader.
-    */
+     *
+     * Loads {@link YangModuleInfo} infos available on supplied classloader.
+     *
+     * {@link YangModuleInfo} are discovered using {@link ServiceLoader} for
+     * {@link YangModelBindingProvider}. {@link YangModelBindingProvider} are
+     * simple classes which holds only pointers to actual instance
+     * {@link YangModuleInfo}.
+     *
+     * When {@link YangModuleInfo} is available, all dependencies are
+     * recursivelly collected into returning set by collecting results of
+     * {@link YangModuleInfo#getImportedModules()}.
+     *
+     *
+     * @param loader
+     *            Classloader for which {@link YangModuleInfo} should be
+     *            retrieved.
+     * @return Set of {@link YangModuleInfo} available for supplied classloader.
+     */
     public static ImmutableSet<YangModuleInfo> loadModuleInfos(final ClassLoader loader) {
         Builder<YangModuleInfo> moduleInfoSet = ImmutableSet.<YangModuleInfo> builder();
         ServiceLoader<YangModelBindingProvider> serviceLoader = ServiceLoader.load(YangModelBindingProvider.class,
@@ -362,7 +391,8 @@ public class BindingReflections {
      *
      * Checks if supplied class represents RPC Input / RPC Output.
      *
-     * @param targetType Class to be checked
+     * @param targetType
+     *            Class to be checked
      * @return true if class represents RPC Input or RPC Output class.
      */
     public static boolean isRpcType(final Class<? extends DataObject> targetType) {
@@ -374,9 +404,11 @@ public class BindingReflections {
 
     /**
      *
-     * Scans supplied class and returns an iterable of all data children classes.
+     * Scans supplied class and returns an iterable of all data children
+     * classes.
      *
-     * @param type YANG Modeled Entity derived from DataContainer
+     * @param type
+     *            YANG Modeled Entity derived from DataContainer
      * @return Iterable of all data children, which have YANG modeled entity
      */
     @SuppressWarnings("unchecked")
@@ -394,24 +426,26 @@ public class BindingReflections {
     }
 
     /**
-    *
-    * Scans supplied class and returns an iterable of all data children classes.
-    *
-    * @param type YANG Modeled Entity derived from DataContainer
-    * @return Iterable of all data children, which have YANG modeled entity
-    */
-   public static Map<Class<?>,Method> getChildrenClassToMethod(final Class<?> type) {
-       checkArgument(type != null, "Target type must not be null");
-       checkArgument(DataContainer.class.isAssignableFrom(type), "Supplied type must be derived from DataContainer");
-       Map<Class<?>,Method> ret = new HashMap<>();
-       for (Method method : type.getMethods()) {
-           Optional<Class<? extends DataContainer>> entity = getYangModeledReturnType(method);
-           if (entity.isPresent()) {
-               ret.put(entity.get(),method);
-           }
-       }
-       return ret;
-   }
+     *
+     * Scans supplied class and returns an iterable of all data children
+     * classes.
+     *
+     * @param type
+     *            YANG Modeled Entity derived from DataContainer
+     * @return Iterable of all data children, which have YANG modeled entity
+     */
+    public static Map<Class<?>, Method> getChildrenClassToMethod(final Class<?> type) {
+        checkArgument(type != null, "Target type must not be null");
+        checkArgument(DataContainer.class.isAssignableFrom(type), "Supplied type must be derived from DataContainer");
+        Map<Class<?>, Method> ret = new HashMap<>();
+        for (Method method : type.getMethods()) {
+            Optional<Class<? extends DataContainer>> entity = getYangModeledReturnType(method);
+            if (entity.isPresent()) {
+                ret.put(entity.get(), method);
+            }
+        }
+        return ret;
+    }
 
     @SuppressWarnings("unchecked")
     private static Optional<Class<? extends DataContainer>> getYangModeledReturnType(final Method method) {
@@ -444,10 +478,9 @@ public class BindingReflections {
             } catch (Exception e) {
                 /*
                  *
-                 * It is safe to log this this exception on debug, since
-                 * this method should not fail. Only failures are possible if
-                 * the runtime / backing.
-                 *
+                 * It is safe to log this this exception on debug, since this
+                 * method should not fail. Only failures are possible if the
+                 * runtime / backing.
                  */
                 LOG.debug("Unable to find YANG modeled return type for {}", method, e);
             }
@@ -467,10 +500,12 @@ public class BindingReflections {
      *
      * Tries to resolve QName for supplied class.
      *
-     * Looks up for static field with name from constant {@link BindingMapping#QNAME_STATIC_FIELD_NAME}
-     * and returns value if present.
+     * Looks up for static field with name from constant
+     * {@link BindingMapping#QNAME_STATIC_FIELD_NAME} and returns value if
+     * present.
      *
-     * If field is not present uses {@link #computeQName(Class)} to compute QName for missing types.
+     * If field is not present uses {@link #computeQName(Class)} to compute
+     * QName for missing types.
      *
      * @param key
      * @return
@@ -489,12 +524,11 @@ public class BindingReflections {
         } catch (SecurityException | IllegalArgumentException | IllegalAccessException e) {
             /*
              *
-             * It is safe to log this this exception on debug, since
-             * this method should not fail. Only failures are possible if
-             * the runtime / backing.
-             *
+             * It is safe to log this this exception on debug, since this method
+             * should not fail. Only failures are possible if the runtime /
+             * backing.
              */
-            LOG.debug("Unexpected exception during extracting QName for {}",key,e);
+            LOG.debug("Unexpected exception during extracting QName for {}", key, e);
         }
         return Optional.absent();
     }
@@ -502,76 +536,78 @@ public class BindingReflections {
     /**
      * Computes QName for supplied class
      *
-     * Namespace and revision are same as {@link YangModuleInfo}
-     * associated with supplied class.
+     * Namespace and revision are same as {@link YangModuleInfo} associated with
+     * supplied class.
      * <p>
      * If class is
      * <ul>
-     *  <li>rpc input: local name is "input".
-     *  <li>rpc output: local name is "output".
-     *  <li>augmentation: local name is "module name".
+     * <li>rpc input: local name is "input".
+     * <li>rpc output: local name is "output".
+     * <li>augmentation: local name is "module name".
      * </ul>
      *
-     * There is also fallback, if it is not possible to compute QName
-     * using following algorithm returns module QName.
+     * There is also fallback, if it is not possible to compute QName using
+     * following algorithm returns module QName.
      *
-     * FIXME: Extend this algorithm to also provide QName for YANG modeled simple types.
+     * FIXME: Extend this algorithm to also provide QName for YANG modeled
+     * simple types.
      *
-     * @throws IllegalStateException If YangModuleInfo could not be resolved
-     * @throws IllegalArgumentException If supplied class was not derived from YANG model.
+     * @throws IllegalStateException
+     *             If YangModuleInfo could not be resolved
+     * @throws IllegalArgumentException
+     *             If supplied class was not derived from YANG model.
      *
      */
     @SuppressWarnings({ "rawtypes", "unchecked" })
     private static QName computeQName(final Class key) {
-        if(isBindingClass(key)) {
+        if (isBindingClass(key)) {
             YangModuleInfo moduleInfo;
             try {
                 moduleInfo = getModuleInfo(key);
             } catch (Exception e) {
-                throw new IllegalStateException("Unable to get QName for " + key + ". YangModuleInfo was not found.",e);
+                throw new IllegalStateException("Unable to get QName for " + key + ". YangModuleInfo was not found.", e);
             }
             final QName module = getModuleQName(moduleInfo);
             if (Augmentation.class.isAssignableFrom(key)) {
                 return module;
-            } else if(isRpcType(key)) {
+            } else if (isRpcType(key)) {
                 final String className = key.getSimpleName();
-                if(className.endsWith(BindingMapping.RPC_OUTPUT_SUFFIX)) {
-                    return QName.create(module,"output");
+                if (className.endsWith(BindingMapping.RPC_OUTPUT_SUFFIX)) {
+                    return QName.create(module, "output");
                 } else {
-                    return QName.create(module,"input");
+                    return QName.create(module, "input");
                 }
             }
             /*
-             *  Fallback for Binding types which fo not have QNAME
-             *  field
+             * Fallback for Binding types which fo not have QNAME field
              */
             return module;
         } else {
-            throw new IllegalArgumentException("Supplied class "+key+"is not derived from YANG.");
+            throw new IllegalArgumentException("Supplied class " + key + "is not derived from YANG.");
         }
     }
 
     /**
      * Given a {@link YangModuleInfo}, create a QName representing it. The QName
-     * is formed by reusing the module's namespace and revision using the module's
-     * name as the QName's local name.
+     * is formed by reusing the module's namespace and revision using the
+     * module's name as the QName's local name.
      *
-     * @param moduleInfo module information
+     * @param moduleInfo
+     *            module information
      * @return QName representing the module
      */
     public static QName getModuleQName(final YangModuleInfo moduleInfo) {
         checkArgument(moduleInfo != null, "moduleInfo must not be null.");
-        return QName.create(moduleInfo.getNamespace(), moduleInfo.getRevision(),
-                moduleInfo.getName());
+        return QName.create(moduleInfo.getNamespace(), moduleInfo.getRevision(), moduleInfo.getName());
     }
 
     /**
      * Extracts augmentation from Binding DTO field using reflection
      *
-     * @param input Instance of DataObject which is augmentable and
-     *      may contain augmentation
-     * @return Map of augmentations if read was successful, otherwise
-     *      empty map.
+     * @param input
+     *            Instance of DataObject which is augmentable and may contain
+     *            augmentation
+     * @return Map of augmentations if read was successful, otherwise empty map.
      */
     public static Map<Class<? extends Augmentation<?>>, Augmentation<?>> getAugmentations(final Augmentable<?> input) {
         return AugmentationFieldGetter.getGetter(input.getClass()).getAugmentations(input);
@@ -579,46 +615,55 @@ public class BindingReflections {
 
     /**
      *
-     * Determines if two augmentation classes or case classes represents same data.
+     * Determines if two augmentation classes or case classes represents same
+     * data.
      * <p>
      * Two augmentations or cases could be substituted only if and if:
      * <ul>
      * <li>Both implements same interfaces</li>
      * <li>Both have same children</li>
-     * <li>If augmentations: Both have same augmentation target class. Target class was generated for data node in grouping.</li>
-     * <li>If cases: Both are from same choice. Choice class was generated for data node in grouping.</li>
+     * <li>If augmentations: Both have same augmentation target class. Target
+     * class was generated for data node in grouping.</li>
+     * <li>If cases: Both are from same choice. Choice class was generated for
+     * data node in grouping.</li>
      * </ul>
      * <p>
-     * <b>Explanation:</b> Binding Specification reuses classes generated for groupings as part of normal data tree,
-     * this classes from grouping could be used at various locations and user may not be aware of it
-     * and may use incorrect case or augmentation in particular subtree (via copy constructors, etc).
-     *
-     * @param potential Class which is potential substition
-     * @param target Class which should be used at particular subtree
+     * <b>Explanation:</b> Binding Specification reuses classes generated for
+     * groupings as part of normal data tree, this classes from grouping could
+     * be used at various locations and user may not be aware of it and may use
+     * incorrect case or augmentation in particular subtree (via copy
+     * constructors, etc).
+     *
+     * @param potential
+     *            Class which is potential substition
+     * @param target
+     *            Class which should be used at particular subtree
      * @return true if and only if classes represents same data.
      */
-    @SuppressWarnings({"rawtypes","unchecked"})
+    @SuppressWarnings({ "rawtypes", "unchecked" })
     public static boolean isSubstitutionFor(final Class potential, final Class target) {
         HashSet<Class> subImplemented = Sets.newHashSet(potential.getInterfaces());
         HashSet<Class> targetImplemented = Sets.newHashSet(target.getInterfaces());
-        if(!subImplemented.equals(targetImplemented)) {
+        if (!subImplemented.equals(targetImplemented)) {
             return false;
         }
-        if(Augmentation.class.isAssignableFrom(potential)
-                && !BindingReflections.findAugmentationTarget(potential).equals(BindingReflections.findAugmentationTarget(target))) {
-                return false;
+        if (Augmentation.class.isAssignableFrom(potential)
+                && !BindingReflections.findAugmentationTarget(potential).equals(
+                        BindingReflections.findAugmentationTarget(target))) {
+            return false;
         }
-        for(Method potentialMethod : potential.getMethods()) {
+        for (Method potentialMethod : potential.getMethods()) {
             try {
                 Method targetMethod = target.getMethod(potentialMethod.getName(), potentialMethod.getParameterTypes());
-                if(!potentialMethod.getReturnType().equals(targetMethod.getReturnType())) {
+                if (!potentialMethod.getReturnType().equals(targetMethod.getReturnType())) {
                     return false;
                 }
             } catch (NoSuchMethodException e) {
-                // Counterpart method is missing, so classes could not be substituted.
+                // Counterpart method is missing, so classes could not be
+                // substituted.
                 return false;
             } catch (SecurityException e) {
-                throw new IllegalStateException("Could not compare methods",e);
+                throw new IllegalStateException("Could not compare methods", e);
             }
         }
         return true;
diff --git a/yang/yang-binding/src/main/java/org/opendaylight/yangtools/yang/binding/util/NotificationListenerInvoker.java b/yang/yang-binding/src/main/java/org/opendaylight/yangtools/yang/binding/util/NotificationListenerInvoker.java
new file mode 100644 (file)
index 0000000..ca37fc1
--- /dev/null
@@ -0,0 +1,122 @@
+/*
+ * 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.yangtools.yang.binding.util;
+
+import com.google.common.base.Preconditions;
+import com.google.common.base.Throwables;
+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.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodHandles.Lookup;
+import java.lang.invoke.MethodType;
+import java.lang.reflect.Method;
+import java.util.Map;
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import org.opendaylight.yangtools.yang.binding.DataContainer;
+import org.opendaylight.yangtools.yang.binding.NotificationListener;
+import org.opendaylight.yangtools.yang.common.QName;
+
+/**
+ * Provides single method invocation of notificatoin callbacks on supplied instance.
+ *
+ * Notification Listener invoker provides common invocation interface for any subtype of {@link NotificationListener}.
+ * via {@link #invokeNotification(NotificationListener, QName, DataContainer)} method.
+ *
+ */
+public final class NotificationListenerInvoker {
+
+    private static final Lookup LOOKUP = MethodHandles.publicLookup();
+
+    private static final LoadingCache<Class<? extends NotificationListener>, NotificationListenerInvoker> INVOKERS = CacheBuilder
+            .newBuilder().weakKeys()
+            .build(new CacheLoader<Class<? extends NotificationListener>, NotificationListenerInvoker>() {
+
+                @Override
+                public NotificationListenerInvoker load(Class<? extends NotificationListener> key) throws Exception {
+                    return createInvoker(key);
+                }
+
+            });
+
+    private final Map<QName, MethodHandle> methodInvokers;
+
+    public NotificationListenerInvoker(Map<QName, MethodHandle> map) {
+        this.methodInvokers = map;
+    }
+
+    /**
+     *
+     * Creates RPCServiceInvoker for specified RpcService type
+     *
+     * @param type
+     *            RpcService interface, which was generated from model.
+     * @return Cached instance of {@link NotificationListenerInvoker} for
+     *         supplied RPC type.
+     *
+     */
+    public static NotificationListenerInvoker from(Class<? extends NotificationListener> type) {
+        Preconditions.checkArgument(type.isInterface());
+        Preconditions.checkArgument(BindingReflections.isBindingClass(type));
+        return INVOKERS.getUnchecked(type);
+    }
+
+    /**
+     * Invokes supplied RPC on provided implementation of RPC Service.
+     *
+     * @param impl
+     *            Imlementation on which notifiaction callback should be
+     *            invoked.
+     * @param rpcName
+     *            Name of RPC to be invoked.
+     * @param input
+     *            Input data for RPC.
+     *
+     */
+    public void invokeNotification(@Nonnull NotificationListener impl, @Nonnull QName rpcName,
+            @Nullable DataContainer input) {
+        Preconditions.checkNotNull(impl, "implemetation must be supplied");
+        MethodHandle invoker = methodInvokers.get(rpcName);
+        Preconditions.checkArgument(invoker != null, "Supplied notification is not valid for implementation %s", impl);
+        try {
+            invoker.invokeExact(impl, input);
+        } catch (Throwable e) {
+            throw Throwables.propagate(e);
+        }
+    }
+
+    private static NotificationListenerInvoker createInvoker(Class<? extends NotificationListener> key) {
+        return new NotificationListenerInvoker(createInvokerMap(key));
+    }
+
+    private static Map<QName, MethodHandle> createInvokerMap(Class<? extends NotificationListener> key) {
+        Builder<QName, MethodHandle> ret = ImmutableMap.<QName, MethodHandle> builder();
+        for (Method method : key.getMethods()) {
+            if (BindingReflections.isNotificationCallback(method)) {
+
+                Class<?> notification = method.getParameterTypes()[0];
+                QName name = BindingReflections.findQName(notification);
+                MethodHandle handle;
+                try {
+                    handle = LOOKUP.unreflect(method).asType(
+                            MethodType.methodType(Void.class, NotificationListener.class, DataContainer.class));
+                    ret.put(name, handle);
+                } catch (IllegalAccessException e) {
+                    throw new IllegalStateException("Can not access public method.", e);
+                }
+            }
+
+        }
+        return ret.build();
+    }
+
+}
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
new file mode 100644 (file)
index 0000000..c93cd8a
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * 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.yangtools.yang.binding.util;
+
+import com.google.common.base.Optional;
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodHandles.Lookup;
+import java.lang.reflect.Method;
+import java.util.concurrent.Future;
+import org.opendaylight.yangtools.yang.binding.DataContainer;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.RpcService;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+
+abstract class RpcMethodInvoker {
+
+    private static Lookup LOOKUP = MethodHandles.publicLookup();
+
+    protected abstract Future<RpcResult<?>> invokeOn(RpcService impl, DataObject input);
+
+    protected static RpcMethodInvoker from(Method method) {
+        Optional<Class<? extends DataContainer>> input = BindingReflections.resolveRpcInputClass(method);
+        try {
+            MethodHandle methodHandle = LOOKUP.unreflect(method);
+            if(input.isPresent()) {
+                return new RpcMethodInvokerWithInput(methodHandle);
+            }
+            return new RpcMethodInvokerWithoutInput(methodHandle);
+        } catch (IllegalAccessException e) {
+            throw new IllegalStateException("Lookup on public method failed.",e);
+        }
+
+    }
+
+}
\ No newline at end of file
diff --git a/yang/yang-binding/src/main/java/org/opendaylight/yangtools/yang/binding/util/RpcMethodInvokerWithInput.java b/yang/yang-binding/src/main/java/org/opendaylight/yangtools/yang/binding/util/RpcMethodInvokerWithInput.java
new file mode 100644 (file)
index 0000000..a77dd35
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * 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.yangtools.yang.binding.util;
+
+import com.google.common.base.Throwables;
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodType;
+import java.util.concurrent.Future;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.RpcService;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+
+class RpcMethodInvokerWithInput extends RpcMethodInvoker {
+
+    private static final MethodType INVOCATION_SIGNATURE = MethodType.methodType(Future.class, RpcService.class,DataObject.class);
+    private final MethodHandle handle;
+
+    RpcMethodInvokerWithInput(MethodHandle methodHandle) {
+        this.handle = methodHandle.asType(INVOCATION_SIGNATURE);
+    }
+
+    @Override
+    public Future<RpcResult<?>> invokeOn(RpcService impl, DataObject input) {
+        try {
+            return (Future<RpcResult<?>>) handle.invokeExact(impl,input);
+        } catch (Throwable e) {
+            throw Throwables.propagate(e);
+        }
+    }
+
+}
\ No newline at end of file
diff --git a/yang/yang-binding/src/main/java/org/opendaylight/yangtools/yang/binding/util/RpcMethodInvokerWithoutInput.java b/yang/yang-binding/src/main/java/org/opendaylight/yangtools/yang/binding/util/RpcMethodInvokerWithoutInput.java
new file mode 100644 (file)
index 0000000..740b98f
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * 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.yangtools.yang.binding.util;
+
+import com.google.common.base.Throwables;
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodType;
+import java.util.concurrent.Future;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.RpcService;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+
+class RpcMethodInvokerWithoutInput extends RpcMethodInvoker {
+
+    private static final MethodType INVOCATION_SIGNATURE = MethodType.methodType(Future.class, RpcService.class);
+    private final MethodHandle handle;
+
+    RpcMethodInvokerWithoutInput(MethodHandle methodHandle) {
+        this.handle = methodHandle.asType(INVOCATION_SIGNATURE);
+    }
+
+    @Override
+    public Future<RpcResult<?>> invokeOn(RpcService impl, DataObject input) {
+        try {
+            return (Future<RpcResult<?>>) handle.invokeExact(impl);
+        } catch (Throwable e) {
+            throw Throwables.propagate(e);
+        }
+    }
+
+}
\ No newline at end of file
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
new file mode 100644 (file)
index 0000000..849b0a2
--- /dev/null
@@ -0,0 +1,102 @@
+/*
+ * 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.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.RpcResult;
+
+/**
+ * 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);
+    }
+
+    /**
+     *
+     * 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);
+    }
+
+    /**
+     * 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 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));
+    }
+
+    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));
+            }
+
+        }
+        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);
+    }
+}