Get rid of CheckedFutures
[mdsal.git] / binding2 / mdsal-binding2-dom-adapter / src / main / java / org / opendaylight / mdsal / binding / javav2 / dom / adapter / impl / notification / NotificationListenerInvoker.java
1 /*
2  * Copyright (c) 2017 Pantheon Technologies s.r.o. and others.  All rights reserved.
3  *
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
7  */
8 package org.opendaylight.mdsal.binding.javav2.dom.adapter.impl.notification;
9
10 import com.google.common.annotations.Beta;
11 import com.google.common.base.Preconditions;
12 import com.google.common.base.Throwables;
13 import com.google.common.cache.CacheBuilder;
14 import com.google.common.cache.CacheLoader;
15 import com.google.common.cache.LoadingCache;
16 import com.google.common.collect.ImmutableMap;
17 import com.google.common.collect.ImmutableMap.Builder;
18 import java.lang.invoke.MethodHandle;
19 import java.lang.invoke.MethodHandles;
20 import java.lang.invoke.MethodHandles.Lookup;
21 import java.lang.invoke.MethodType;
22 import java.lang.reflect.Method;
23 import java.util.Map;
24 import javax.annotation.Nonnull;
25 import javax.annotation.Nullable;
26 import org.opendaylight.mdsal.binding.javav2.runtime.reflection.BindingReflections;
27 import org.opendaylight.mdsal.binding.javav2.spec.base.Instantiable;
28 import org.opendaylight.mdsal.binding.javav2.spec.runtime.NotificationListener;
29 import org.opendaylight.yangtools.yang.common.QName;
30
31 /**
32  * Provides single method invocation of notification callbacks on supplied instance.
33  *
34  * <p>
35  * Notification Listener invoker provides common invocation interface for any subtype of
36  * {@link NotificationListener}. via {@link #invokeNotification(NotificationListener, QName, Instantiable)}
37  * method.
38  */
39 @Beta
40 public final class NotificationListenerInvoker {
41
42     private static final Lookup LOOKUP = MethodHandles.publicLookup();
43
44     private static final LoadingCache<Class<? extends NotificationListener>, NotificationListenerInvoker> INVOKERS =
45             CacheBuilder.newBuilder().weakKeys()
46                     .build(new CacheLoader<Class<? extends NotificationListener>, NotificationListenerInvoker>() {
47
48                         private NotificationListenerInvoker
49                                 createInvoker(final Class<? extends NotificationListener> key) {
50                             return new NotificationListenerInvoker(createInvokerMap(key));
51                         }
52
53                         private Map<QName, MethodHandle>
54                                 createInvokerMap(final Class<? extends NotificationListener> key) {
55                             final Builder<QName, MethodHandle> ret = ImmutableMap.builder();
56                             for (final Method method : key.getMethods()) {
57                                 if (BindingReflections.isNotificationCallback(method)) {
58
59                                     final Class<?> notification = method.getParameterTypes()[0];
60                                     final QName name = BindingReflections.findQName(notification);
61                                     MethodHandle handle;
62                                     try {
63                                         handle = LOOKUP.unreflect(method).asType(MethodType.methodType(void.class,
64                                                 NotificationListener.class, Instantiable.class));
65                                         ret.put(name, handle);
66                                     } catch (final IllegalAccessException e) {
67                                         throw new IllegalStateException("Can not access public method.", e);
68                                     }
69                                 }
70
71                             }
72                             return ret.build();
73                         }
74
75                         @Override
76                         public NotificationListenerInvoker load(final Class<? extends NotificationListener> key)
77                                 throws Exception {
78                             return createInvoker(key);
79                         }
80
81                     });
82
83     private final Map<QName, MethodHandle> methodInvokers;
84
85     public NotificationListenerInvoker(final Map<QName, MethodHandle> map) {
86         this.methodInvokers = map;
87     }
88
89     /**
90      * Creates Notification service invoker for specified type.
91      *
92      * @param type
93      *            - NotificationListener interface, which was generated from model.
94      * @return Cached instance of {@link NotificationListenerInvoker} for supplied notification type.
95      *
96      */
97     public static NotificationListenerInvoker from(final Class<? extends NotificationListener> type) {
98         Preconditions.checkArgument(type.isInterface());
99         Preconditions.checkArgument(BindingReflections.isBindingClass(type));
100         return INVOKERS.getUnchecked(type);
101     }
102
103     /**
104      * Invokes supplied Notification on provided implementation of NotificationListener.
105      *
106      * @param impl
107      *            - implementation on which notification callback should be invoked.
108      * @param notificationName
109      *            - name of notification to be invoked.
110      * @param input
111      *            - input data for notification.
112      *
113      */
114     @SuppressWarnings("checkstyle:IllegalCatch")
115     public void invokeNotification(@Nonnull final NotificationListener impl, @Nonnull final QName notificationName,
116             @Nullable final Instantiable<?> input) {
117         Preconditions.checkNotNull(impl, "implementation must be supplied");
118         final MethodHandle invoker = methodInvokers.get(notificationName);
119         Preconditions.checkArgument(invoker != null, "Supplied notification is not valid for implementation %s", impl);
120         try {
121             invoker.invokeExact(impl, input);
122         } catch (final Throwable e) {
123             Throwables.throwIfUnchecked(e);
124             throw new RuntimeException(e);
125         }
126     }
127 }
128