Cleaned up Java Binding code from YANG Tools
[mdsal.git] / binding / yang-binding / src / main / java / org / opendaylight / yangtools / yang / binding / util / 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.yangtools.yang.binding.util;
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.yangtools.yang.binding.DataContainer;
26 import org.opendaylight.yangtools.yang.binding.NotificationListener;
27 import org.opendaylight.yangtools.yang.common.QName;
28
29 /**
30  * Provides single method invocation of notificatoin callbacks on supplied instance.
31  *
32  * Notification Listener invoker provides common invocation interface for any subtype of {@link NotificationListener}.
33  * via {@link #invokeNotification(NotificationListener, QName, DataContainer)} method.
34  *
35  */
36 public final class NotificationListenerInvoker {
37
38     private static final Lookup LOOKUP = MethodHandles.publicLookup();
39
40     private static final LoadingCache<Class<? extends NotificationListener>, NotificationListenerInvoker> INVOKERS = CacheBuilder
41             .newBuilder().weakKeys()
42             .build(new CacheLoader<Class<? extends NotificationListener>, NotificationListenerInvoker>() {
43
44                 @Override
45                 public NotificationListenerInvoker load(final Class<? extends NotificationListener> key) throws Exception {
46                     return createInvoker(key);
47                 }
48
49             });
50
51     private final Map<QName, MethodHandle> methodInvokers;
52
53     public NotificationListenerInvoker(final Map<QName, MethodHandle> map) {
54         this.methodInvokers = map;
55     }
56
57     /**
58      *
59      * Creates RPCServiceInvoker for specified RpcService type
60      *
61      * @param type
62      *            RpcService interface, which was generated from model.
63      * @return Cached instance of {@link NotificationListenerInvoker} for
64      *         supplied RPC type.
65      *
66      */
67     public static NotificationListenerInvoker from(final Class<? extends NotificationListener> type) {
68         Preconditions.checkArgument(type.isInterface());
69         Preconditions.checkArgument(BindingReflections.isBindingClass(type));
70         return INVOKERS.getUnchecked(type);
71     }
72
73     /**
74      * Invokes supplied RPC on provided implementation of RPC Service.
75      *
76      * @param impl
77      *            Imlementation on which notifiaction callback should be
78      *            invoked.
79      * @param rpcName
80      *            Name of RPC to be invoked.
81      * @param input
82      *            Input data for RPC.
83      *
84      */
85     public void invokeNotification(@Nonnull final NotificationListener impl, @Nonnull final QName rpcName,
86             @Nullable final DataContainer input) {
87         Preconditions.checkNotNull(impl, "implemetation must be supplied");
88         final MethodHandle invoker = methodInvokers.get(rpcName);
89         Preconditions.checkArgument(invoker != null, "Supplied notification is not valid for implementation %s", impl);
90         try {
91             invoker.invokeExact(impl, input);
92         } catch (final Throwable e) {
93             throw Throwables.propagate(e);
94         }
95     }
96
97     private static NotificationListenerInvoker createInvoker(final Class<? extends NotificationListener> key) {
98         return new NotificationListenerInvoker(createInvokerMap(key));
99     }
100
101     private static Map<QName, MethodHandle> createInvokerMap(final Class<? extends NotificationListener> key) {
102         final Builder<QName, MethodHandle> ret = ImmutableMap.<QName, MethodHandle>builder();
103         for (final Method method : key.getMethods()) {
104             if (BindingReflections.isNotificationCallback(method)) {
105
106                 final Class<?> notification = method.getParameterTypes()[0];
107                 final QName name = BindingReflections.findQName(notification);
108                 MethodHandle handle;
109                 try {
110                     handle = LOOKUP.unreflect(method).asType(
111                             MethodType.methodType(void.class, NotificationListener.class, DataContainer.class));
112                     ret.put(name, handle);
113                 } catch (final IllegalAccessException e) {
114                     throw new IllegalStateException("Can not access public method.", e);
115                 }
116             }
117
118         }
119         return ret.build();
120     }
121
122 }