2 * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
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
8 package org.opendaylight.controller.sal.binding.codegen.impl;
10 import com.google.common.base.Supplier;
13 import java.util.WeakHashMap;
15 import javassist.ClassPool;
16 import javassist.CtClass;
17 import javassist.CtMethod;
18 import javassist.NotFoundException;
20 import javax.annotation.concurrent.GuardedBy;
22 import org.eclipse.xtext.xbase.lib.Extension;
23 import org.opendaylight.controller.sal.binding.api.rpc.RpcRouter;
24 import org.opendaylight.controller.sal.binding.codegen.RpcIsNotRoutedException;
25 import org.opendaylight.controller.sal.binding.spi.NotificationInvokerFactory;
26 import org.opendaylight.yangtools.sal.binding.generator.util.JavassistUtils;
27 import org.opendaylight.yangtools.yang.binding.BindingMapping;
28 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
29 import org.opendaylight.yangtools.yang.binding.NotificationListener;
30 import org.opendaylight.yangtools.yang.binding.RpcService;
31 import org.opendaylight.yangtools.yang.binding.annotations.RoutingContext;
32 import org.opendaylight.yangtools.yang.binding.util.ClassLoaderUtils;
34 abstract class AbstractRuntimeCodeGenerator implements org.opendaylight.controller.sal.binding.codegen.RuntimeCodeGenerator, NotificationInvokerFactory {
36 private final Map<Class<? extends NotificationListener>, RuntimeGeneratedInvokerPrototype> invokerClasses = new WeakHashMap<>();
37 private final CtClass brokerNotificationListener;
40 protected final JavassistUtils utils;
42 protected AbstractRuntimeCodeGenerator(final ClassPool pool) {
43 utils = JavassistUtils.forClassPool(pool);
46 * Make sure Javassist ClassPool sees the classloader of RpcService
48 utils.ensureClassLoader(RpcService.class);
50 brokerNotificationListener = utils.asCtClass(org.opendaylight.controller.sal.binding.api.NotificationListener.class);
53 protected final CtClass getBrokerNotificationListener() {
54 return brokerNotificationListener;
57 protected abstract RuntimeGeneratedInvokerPrototype generateListenerInvoker(Class<? extends NotificationListener> cls);
58 protected abstract <T extends RpcService> Supplier<T> directProxySupplier(final Class<T> serviceType);
59 protected abstract <T extends RpcService> Supplier<T> routerSupplier(final Class<T> serviceType, RpcServiceMetadata metadata);
61 private RpcServiceMetadata getRpcMetadata(final CtClass iface) throws ClassNotFoundException, NotFoundException, RpcIsNotRoutedException {
62 final RpcServiceMetadata metadata = new RpcServiceMetadata();
64 for (CtMethod method : iface.getMethods()) {
65 if (isRpcMethodWithInput(iface, method)) {
66 final RpcMetadata routingPair = getRpcMetadata(method);
67 if (routingPair != null) {
68 metadata.addContext(routingPair.getContext());
69 metadata.addRpcMethod(method.getName(), routingPair);
72 * Force-load the RPC class representing the "input" of this RPC.
74 * FIXME: this is pre-existing side-effect of the original code, which
75 * kept a reference to the loaded class, but it did not use it.
77 * There was no explanation as to why forcing this load was
78 * necessary. As far as I can tell now is that it forces the
79 * resolution of method arguments, which would (according to
80 * my reading of JLS) occur only when the method is invoked via
81 * binding-aware class action, not when coming from
82 * binding-independent world. Whether that makes sense or not,
83 * remains to be investigated.
85 Thread.currentThread().getContextClassLoader().loadClass(routingPair.getInputType().getName());
87 throw new RpcIsNotRoutedException("RPC " + method.getName() + " from "+ iface.getName() +" is not routed");
96 private boolean isRpcMethodWithInput(final CtClass iface, final CtMethod method) throws NotFoundException {
97 if(iface.equals(method.getDeclaringClass())
98 && method.getParameterTypes().length == 1) {
99 final CtClass onlyArg = method.getParameterTypes()[0];
100 if(onlyArg.isInterface() && onlyArg.getName().endsWith(BindingMapping.RPC_INPUT_SUFFIX)) {
107 private RpcMetadata getRpcMetadata(final CtMethod method) throws NotFoundException {
108 final CtClass inputClass = method.getParameterTypes()[0];
109 return rpcMethodMetadata(inputClass, inputClass, method.getName());
112 private RpcMetadata rpcMethodMetadata(final CtClass dataClass, final CtClass inputClass, final String rpcMethod) throws NotFoundException {
113 for (CtMethod method : dataClass.getMethods()) {
114 if (method.getName().startsWith("get") && method.getParameterTypes().length == 0) {
115 for (Object annotation : method.getAvailableAnnotations()) {
116 if (annotation instanceof RoutingContext) {
117 boolean encapsulated = !method.getReturnType().equals(utils.asCtClass(InstanceIdentifier.class));
118 return new RpcMetadata(rpcMethod, ((RoutingContext)annotation).value(), method, encapsulated, inputClass);
124 for (CtClass iface : dataClass.getInterfaces()) {
125 final RpcMetadata ret = rpcMethodMetadata(iface, inputClass, rpcMethod);
133 private synchronized RuntimeGeneratedInvokerPrototype resolveInvokerClass(final Class<? extends NotificationListener> cls) {
134 RuntimeGeneratedInvokerPrototype invoker = invokerClasses.get(cls);
135 if (invoker != null) {
139 utils.getLock().lock();
141 synchronized (utils) {
142 invoker = ClassLoaderUtils.withClassLoader(cls.getClassLoader(), new Supplier<RuntimeGeneratedInvokerPrototype>() {
144 public RuntimeGeneratedInvokerPrototype get() {
145 return generateListenerInvoker(cls);
150 invokerClasses.put(cls, invoker);
153 utils.getLock().unlock();
158 public final NotificationInvokerFactory getInvokerFactory() {
163 public final <T extends RpcService> T getDirectProxyFor(final Class<T> serviceType) {
164 utils.getLock().lock();
166 synchronized (utils) {
167 return ClassLoaderUtils.withClassLoader(serviceType.getClassLoader(), directProxySupplier(serviceType));
170 utils.getLock().unlock();
175 public final <T extends RpcService> RpcRouter<T> getRouterFor(final Class<T> serviceType, final String name) throws RpcIsNotRoutedException {
176 final RpcServiceMetadata metadata = ClassLoaderUtils.withClassLoader(serviceType.getClassLoader(), new Supplier<RpcServiceMetadata>() {
178 public RpcServiceMetadata get() {
180 return getRpcMetadata(utils.asCtClass(serviceType));
181 } catch (ClassNotFoundException | NotFoundException e) {
182 throw new IllegalStateException(String.format("Failed to load metadata for class {}", serviceType), e);
187 utils.getLock().lock();
189 synchronized (utils) {
190 final T instance = ClassLoaderUtils.withClassLoader(serviceType.getClassLoader(), routerSupplier(serviceType, metadata));
191 return new RpcRouterCodegenInstance<T>(name, serviceType, instance, metadata.getContexts());
194 utils.getLock().unlock();
199 public NotificationInvoker invokerFor(final NotificationListener instance) {
200 final Class<? extends NotificationListener> cls = instance.getClass();
201 final RuntimeGeneratedInvokerPrototype prototype = resolveInvokerClass(cls);
204 return RuntimeGeneratedInvoker.create(instance, prototype);
205 } catch (InstantiationException | IllegalAccessException e) {
206 throw new IllegalStateException(String.format("Failed to create invoker for %s", instance), e);