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