/* * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v1.0 which accompanies this distribution, * and is available at http://www.eclipse.org/legal/epl-v10.html */ package org.opendaylight.mdsal.binding.dom.adapter.invoke; import com.google.common.base.Preconditions; import com.google.common.base.Throwables; import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheLoader; import com.google.common.cache.LoadingCache; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap.Builder; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodHandles.Lookup; import java.lang.invoke.MethodType; import java.lang.reflect.Method; import java.util.Map; import javax.annotation.Nonnull; import javax.annotation.Nullable; import org.opendaylight.mdsal.binding.spec.reflect.BindingReflections; import org.opendaylight.yangtools.yang.binding.DataContainer; import org.opendaylight.yangtools.yang.binding.NotificationListener; import org.opendaylight.yangtools.yang.common.QName; /** * Provides single method invocation of notificatoin callbacks on supplied instance. * *

* Notification Listener invoker provides common invocation interface for any subtype of {@link NotificationListener}. * via {@link #invokeNotification(NotificationListener, QName, DataContainer)} method. */ public final class NotificationListenerInvoker { private static final Lookup LOOKUP = MethodHandles.publicLookup(); private static final LoadingCache, NotificationListenerInvoker> INVOKERS = CacheBuilder.newBuilder().weakKeys() .build(new CacheLoader, NotificationListenerInvoker>() { @Override public NotificationListenerInvoker load(final Class key) { return new NotificationListenerInvoker(createInvokerMap(key)); } private Map createInvokerMap(final Class key) { final Builder ret = ImmutableMap.builder(); for (final Method method : key.getMethods()) { if (BindingReflections.isNotificationCallback(method)) { final Class notification = method.getParameterTypes()[0]; final QName name = BindingReflections.findQName(notification); MethodHandle handle; try { handle = LOOKUP.unreflect(method).asType(MethodType.methodType(void.class, NotificationListener.class, DataContainer.class)); ret.put(name, handle); } catch (final IllegalAccessException e) { throw new IllegalStateException("Can not access public method.", e); } } } return ret.build(); } }); private final Map methodInvokers; NotificationListenerInvoker(final Map map) { this.methodInvokers = map; } /** * Creates RPCServiceInvoker for specified RpcService type. * * @param type * RpcService interface, which was generated from model. * @return Cached instance of {@link NotificationListenerInvoker} for * supplied RPC type. */ public static NotificationListenerInvoker from(final Class type) { Preconditions.checkArgument(type.isInterface()); Preconditions.checkArgument(BindingReflections.isBindingClass(type)); return INVOKERS.getUnchecked(type); } /** * Invokes supplied RPC on provided implementation of RPC Service. * * @param impl Implementation on which notification callback should be invoked. * @param rpcName Name of RPC to be invoked. * @param input Input data for RPC. */ @SuppressWarnings("checkstyle:illegalCatch") public void invokeNotification(@Nonnull final NotificationListener impl, @Nonnull final QName rpcName, @Nullable final DataContainer input) { Preconditions.checkNotNull(impl, "implemetation must be supplied"); final MethodHandle invoker = methodInvokers.get(rpcName); Preconditions.checkArgument(invoker != null, "Supplied notification is not valid for implementation %s", impl); try { invoker.invokeExact(impl, input); } catch (final Throwable e) { Throwables.throwIfUnchecked(e); throw new IllegalStateException(e); } } }