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.MethodHandles.Lookup;
22 import java.lang.invoke.MethodType;
23 import java.lang.reflect.Method;
24 import org.eclipse.jdt.annotation.NonNull;
25 import org.eclipse.jdt.annotation.Nullable;
26 import org.opendaylight.mdsal.binding.spec.reflect.BindingReflections;
27 import org.opendaylight.yangtools.yang.binding.DataContainer;
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 {
40 private static final Lookup LOOKUP = MethodHandles.publicLookup();
42 private static final LoadingCache<Class<? extends NotificationListener>, NotificationListenerInvoker> INVOKERS =
43 CacheBuilder.newBuilder().weakKeys()
44 .build(new CacheLoader<Class<? extends NotificationListener>, NotificationListenerInvoker>() {
46 public NotificationListenerInvoker load(final Class<? extends NotificationListener> key) {
47 return new NotificationListenerInvoker(createInvokerMap(key));
50 private ImmutableMap<QName, MethodHandle> createInvokerMap(
51 final Class<? extends NotificationListener> key) {
52 final Builder<QName, MethodHandle> ret = ImmutableMap.builder();
53 for (final Method method : key.getMethods()) {
54 if (BindingReflections.isNotificationCallback(method)) {
56 final Class<?> notification = method.getParameterTypes()[0];
57 final QName name = BindingReflections.findQName(notification);
60 handle = LOOKUP.unreflect(method).asType(MethodType.methodType(void.class,
61 NotificationListener.class, DataContainer.class));
62 ret.put(name, handle);
63 } catch (final IllegalAccessException e) {
64 throw new IllegalStateException("Can not access public method.", e);
73 private final ImmutableMap<QName, MethodHandle> methodInvokers;
75 NotificationListenerInvoker(final ImmutableMap<QName, MethodHandle> map) {
76 this.methodInvokers = map;
80 * Creates RPCServiceInvoker for specified RpcService type.
83 * RpcService interface, which was generated from model.
84 * @return Cached instance of {@link NotificationListenerInvoker} for
87 public static NotificationListenerInvoker from(final Class<? extends NotificationListener> type) {
88 checkArgument(type.isInterface());
89 checkArgument(BindingReflections.isBindingClass(type));
90 return INVOKERS.getUnchecked(type);
94 * Invokes supplied RPC on provided implementation of RPC Service.
96 * @param impl Implementation on which notification callback should be invoked.
97 * @param rpcName Name of RPC to be invoked.
98 * @param input Input data for RPC.
100 @SuppressWarnings("checkstyle:illegalCatch")
101 public void invokeNotification(final @NonNull NotificationListener impl, final @NonNull QName rpcName,
102 final @Nullable DataContainer input) {
103 requireNonNull(impl, "implemetation must be supplied");
104 final MethodHandle invoker = methodInvokers.get(rpcName);
105 checkArgument(invoker != null, "Supplied notification is not valid for implementation %s", impl);
107 invoker.invokeExact(impl, input);
108 } catch (final Throwable e) {
109 Throwables.throwIfUnchecked(e);
110 throw new RuntimeException(e);