2 * Copyright (c) 2017 Pantheon Technologies s.r.o. 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.javav2.dom.adapter.impl.notification;
10 import static java.util.Objects.requireNonNull;
12 import com.google.common.annotations.Beta;
13 import com.google.common.base.Preconditions;
14 import com.google.common.base.Throwables;
15 import com.google.common.cache.CacheBuilder;
16 import com.google.common.cache.CacheLoader;
17 import com.google.common.cache.LoadingCache;
18 import com.google.common.collect.ImmutableMap;
19 import com.google.common.collect.ImmutableMap.Builder;
20 import java.lang.invoke.MethodHandle;
21 import java.lang.invoke.MethodHandles;
22 import java.lang.invoke.MethodHandles.Lookup;
23 import java.lang.invoke.MethodType;
24 import java.lang.reflect.Method;
26 import javax.annotation.Nonnull;
27 import javax.annotation.Nullable;
28 import org.opendaylight.mdsal.binding.javav2.runtime.reflection.BindingReflections;
29 import org.opendaylight.mdsal.binding.javav2.spec.base.Instantiable;
30 import org.opendaylight.mdsal.binding.javav2.spec.runtime.NotificationListener;
31 import org.opendaylight.yangtools.yang.common.QName;
34 * Provides single method invocation of notification callbacks on supplied instance.
37 * Notification Listener invoker provides common invocation interface for any subtype of
38 * {@link NotificationListener}. via {@link #invokeNotification(NotificationListener, QName, Instantiable)}
42 public final class NotificationListenerInvoker {
44 private static final Lookup LOOKUP = MethodHandles.publicLookup();
46 private static final LoadingCache<Class<? extends NotificationListener>, NotificationListenerInvoker> INVOKERS =
47 CacheBuilder.newBuilder().weakKeys()
48 .build(new CacheLoader<Class<? extends NotificationListener>, NotificationListenerInvoker>() {
50 private NotificationListenerInvoker
51 createInvoker(final Class<? extends NotificationListener> key) {
52 return new NotificationListenerInvoker(createInvokerMap(key));
55 private Map<QName, MethodHandle>
56 createInvokerMap(final Class<? extends NotificationListener> key) {
57 final Builder<QName, MethodHandle> ret = ImmutableMap.builder();
58 for (final Method method : key.getMethods()) {
59 if (BindingReflections.isNotificationCallback(method)) {
61 final Class<?> notification = method.getParameterTypes()[0];
62 final QName name = BindingReflections.findQName(notification);
65 handle = LOOKUP.unreflect(method).asType(MethodType.methodType(void.class,
66 NotificationListener.class, Instantiable.class));
67 ret.put(name, handle);
68 } catch (final IllegalAccessException e) {
69 throw new IllegalStateException("Can not access public method.", e);
78 public NotificationListenerInvoker load(final Class<? extends NotificationListener> key)
80 return createInvoker(key);
85 private final Map<QName, MethodHandle> methodInvokers;
87 public NotificationListenerInvoker(final Map<QName, MethodHandle> map) {
88 this.methodInvokers = map;
92 * Creates Notification service invoker for specified type.
95 * - NotificationListener interface, which was generated from model.
96 * @return Cached instance of {@link NotificationListenerInvoker} for supplied notification type.
99 public static NotificationListenerInvoker from(final Class<? extends NotificationListener> type) {
100 Preconditions.checkArgument(type.isInterface());
101 Preconditions.checkArgument(BindingReflections.isBindingClass(type));
102 return INVOKERS.getUnchecked(type);
106 * Invokes supplied Notification on provided implementation of NotificationListener.
109 * - implementation on which notification callback should be invoked.
110 * @param notificationName
111 * - name of notification to be invoked.
113 * - input data for notification.
116 @SuppressWarnings("checkstyle:IllegalCatch")
117 public void invokeNotification(@Nonnull final NotificationListener impl, @Nonnull final QName notificationName,
118 @Nullable final Instantiable<?> input) {
119 requireNonNull(impl, "implementation must be supplied");
120 final MethodHandle invoker = methodInvokers.get(notificationName);
121 Preconditions.checkArgument(invoker != null, "Supplied notification is not valid for implementation %s", impl);
123 invoker.invokeExact(impl, input);
124 } catch (final Throwable e) {
125 Throwables.throwIfUnchecked(e);
126 throw new RuntimeException(e);