2 * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6 * and is available at http://www.eclipse.org/legal/epl-v10.html
8 package org.opendaylight.mdsal.binding.dom.adapter.invoke;
10 import static com.google.common.base.Preconditions.checkArgument;
11 import static java.util.Objects.requireNonNull;
13 import com.google.common.base.Throwables;
14 import com.google.common.cache.CacheBuilder;
15 import com.google.common.cache.CacheLoader;
16 import com.google.common.cache.LoadingCache;
17 import com.google.common.collect.ImmutableMap;
18 import com.google.common.collect.ImmutableMap.Builder;
19 import java.lang.invoke.MethodHandle;
20 import java.lang.invoke.MethodHandles;
21 import java.lang.invoke.MethodType;
22 import java.lang.reflect.Method;
23 import org.eclipse.jdt.annotation.NonNull;
24 import org.eclipse.jdt.annotation.Nullable;
25 import org.opendaylight.mdsal.binding.spec.reflect.BindingReflections;
26 import org.opendaylight.yangtools.yang.binding.DataContainer;
27 import org.opendaylight.yangtools.yang.binding.Notification;
28 import org.opendaylight.yangtools.yang.binding.NotificationListener;
29 import org.opendaylight.yangtools.yang.common.QName;
32 * Provides single method invocation of notificatoin callbacks on supplied instance.
35 * Notification Listener invoker provides common invocation interface for any subtype of {@link NotificationListener}.
36 * via {@link #invokeNotification(NotificationListener, QName, DataContainer)} method.
38 public final class NotificationListenerInvoker {
39 private static final LoadingCache<Class<? extends NotificationListener>, NotificationListenerInvoker> INVOKERS =
40 CacheBuilder.newBuilder().weakKeys()
41 .build(new CacheLoader<Class<? extends NotificationListener>, NotificationListenerInvoker>() {
43 public NotificationListenerInvoker load(final Class<? extends NotificationListener> key) {
44 return new NotificationListenerInvoker(createInvokerMap(key));
47 private ImmutableMap<QName, MethodHandle> createInvokerMap(
48 final Class<? extends NotificationListener> key) {
49 final Builder<QName, MethodHandle> ret = ImmutableMap.builder();
50 for (final Method method : key.getMethods()) {
51 if (isNotificationCallback(method)) {
52 final Class<?> notification = method.getParameterTypes()[0];
53 final QName name = BindingReflections.findQName(notification);
56 handle = MethodHandles.publicLookup().unreflect(method).asType(
57 MethodType.methodType(void.class, NotificationListener.class, DataContainer.class));
58 ret.put(name, handle);
59 } catch (final IllegalAccessException e) {
60 throw new IllegalStateException("Can not access public method.", e);
69 private final ImmutableMap<QName, MethodHandle> methodInvokers;
71 NotificationListenerInvoker(final ImmutableMap<QName, MethodHandle> map) {
76 * Creates RPCServiceInvoker for specified RpcService type.
79 * RpcService interface, which was generated from model.
80 * @return Cached instance of {@link NotificationListenerInvoker} for
83 public static NotificationListenerInvoker from(final Class<? extends NotificationListener> type) {
84 checkArgument(type.isInterface());
85 checkArgument(BindingReflections.isBindingClass(type));
86 return INVOKERS.getUnchecked(type);
90 * Invokes supplied RPC on provided implementation of RPC Service.
92 * @param impl Implementation on which notification callback should be invoked.
93 * @param rpcName Name of RPC to be invoked.
94 * @param input Input data for RPC.
96 @SuppressWarnings("checkstyle:illegalCatch")
97 public void invokeNotification(final @NonNull NotificationListener impl, final @NonNull QName rpcName,
98 final @Nullable DataContainer input) {
99 requireNonNull(impl, "implemetation must be supplied");
100 final MethodHandle invoker = methodInvokers.get(rpcName);
101 checkArgument(invoker != null, "Supplied notification is not valid for implementation %s", impl);
103 invoker.invokeExact(impl, input);
104 } catch (final Throwable e) {
105 Throwables.throwIfUnchecked(e);
106 throw new IllegalStateException(e);
111 * Checks if supplied method is callback for notifications.
113 * @param method method to check
114 * @return true if method is notification callback.
116 public static boolean isNotificationCallback(final Method method) {
117 if (method.getName().startsWith("on") && method.getParameterCount() == 1) {
118 Class<?> potentialNotification = method.getParameterTypes()[0];
119 if (Notification.class.isAssignableFrom(potentialNotification)
120 && method.getName().equals("on" + potentialNotification.getSimpleName())) {