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