Bug 1411: Design Binding specification v2
[mdsal.git] / binding2 / prototype / yang-binding2 / src / main / java / org / opendaylight / yangtools / yang / binding / util / NotificationListenerInvoker.java
diff --git a/binding2/prototype/yang-binding2/src/main/java/org/opendaylight/yangtools/yang/binding/util/NotificationListenerInvoker.java b/binding2/prototype/yang-binding2/src/main/java/org/opendaylight/yangtools/yang/binding/util/NotificationListenerInvoker.java
new file mode 100644 (file)
index 0000000..38f3038
--- /dev/null
@@ -0,0 +1,122 @@
+/*
+ * 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.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.InterfaceTyped;
+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, InterfaceTyped)} 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(final Class<? extends NotificationListener> key) throws Exception {
+                    return createInvoker(key);
+                }
+
+            });
+
+    private final Map<QName, MethodHandle> methodInvokers;
+
+    public NotificationListenerInvoker(final 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(final 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 final NotificationListener impl, @Nonnull final QName rpcName,
+            @Nullable final InterfaceTyped input) {
+        Preconditions.checkNotNull(impl, "implemetation must be supplied");
+        final MethodHandle invoker = methodInvokers.get(rpcName);
+        Preconditions.checkArgument(invoker != null, "Supplied notification is not valid for implementation %s", impl);
+        try {
+            invoker.invokeExact(impl, input);
+        } catch (final Throwable e) {
+            throw Throwables.propagate(e);
+        }
+    }
+
+    private static NotificationListenerInvoker createInvoker(final Class<? extends NotificationListener> key) {
+        return new NotificationListenerInvoker(createInvokerMap(key));
+    }
+
+    private static Map<QName, MethodHandle> createInvokerMap(final Class<? extends NotificationListener> key) {
+        final Builder<QName, MethodHandle> ret = ImmutableMap.<QName, MethodHandle>builder();
+        for (final Method method : key.getMethods()) {
+            if (BindingReflections.isNotificationCallback(method)) {
+
+                final Class<?> notification = method.getParameterTypes()[0];
+                final QName name = BindingReflections.findQName(notification);
+                MethodHandle handle;
+                try {
+                    handle = LOOKUP.unreflect(method).asType(
+                            MethodType.methodType(void.class, NotificationListener.class, InterfaceTyped.class));
+                    ret.put(name, handle);
+                } catch (final IllegalAccessException e) {
+                    throw new IllegalStateException("Can not access public method.", e);
+                }
+            }
+
+        }
+        return ret.build();
+    }
+
+}