Switch to Objects.requireNonNull
[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 static java.util.Objects.requireNonNull;
11
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;
25 import java.util.Map;
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;
32
33 /**
34  * Provides single method invocation of notification callbacks on supplied instance.
35  *
36  * <p>
37  * Notification Listener invoker provides common invocation interface for any subtype of
38  * {@link NotificationListener}. via {@link #invokeNotification(NotificationListener, QName, Instantiable)}
39  * method.
40  */
41 @Beta
42 public final class NotificationListenerInvoker {
43
44     private static final Lookup LOOKUP = MethodHandles.publicLookup();
45
46     private static final LoadingCache<Class<? extends NotificationListener>, NotificationListenerInvoker> INVOKERS =
47             CacheBuilder.newBuilder().weakKeys()
48                     .build(new CacheLoader<Class<? extends NotificationListener>, NotificationListenerInvoker>() {
49
50                         private NotificationListenerInvoker
51                                 createInvoker(final Class<? extends NotificationListener> key) {
52                             return new NotificationListenerInvoker(createInvokerMap(key));
53                         }
54
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)) {
60
61                                     final Class<?> notification = method.getParameterTypes()[0];
62                                     final QName name = BindingReflections.findQName(notification);
63                                     MethodHandle handle;
64                                     try {
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);
70                                     }
71                                 }
72
73                             }
74                             return ret.build();
75                         }
76
77                         @Override
78                         public NotificationListenerInvoker load(final Class<? extends NotificationListener> key)
79                                 throws Exception {
80                             return createInvoker(key);
81                         }
82
83                     });
84
85     private final Map<QName, MethodHandle> methodInvokers;
86
87     public NotificationListenerInvoker(final Map<QName, MethodHandle> map) {
88         this.methodInvokers = map;
89     }
90
91     /**
92      * Creates Notification service invoker for specified type.
93      *
94      * @param type
95      *            - NotificationListener interface, which was generated from model.
96      * @return Cached instance of {@link NotificationListenerInvoker} for supplied notification type.
97      *
98      */
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);
103     }
104
105     /**
106      * Invokes supplied Notification on provided implementation of NotificationListener.
107      *
108      * @param impl
109      *            - implementation on which notification callback should be invoked.
110      * @param notificationName
111      *            - name of notification to be invoked.
112      * @param input
113      *            - input data for notification.
114      *
115      */
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);
122         try {
123             invoker.invokeExact(impl, input);
124         } catch (final Throwable e) {
125             Throwables.throwIfUnchecked(e);
126             throw new RuntimeException(e);
127         }
128     }
129 }
130