2 * Copyright (c) 2013 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 javassist.ClassPool
11 import org.opendaylight.yangtools.yang.binding.RpcService
13 import javassist.CtClass
14 import static com.google.common.base.Preconditions.*
16 import javassist.CtField
17 import javassist.Modifier
18 import javassist.CtMethod
19 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier
20 import org.opendaylight.yangtools.yang.binding.annotations.RoutingContext
21 import org.opendaylight.yangtools.yang.binding.BaseIdentity
24 import java.util.HashMap
25 import javassist.NotFoundException
26 import javassist.LoaderClassPath
27 import org.opendaylight.controller.sal.binding.codegen.impl.JavassistUtils.MethodGenerator
28 import org.opendaylight.controller.sal.binding.codegen.impl.JavassistUtils.ClassGenerator
29 import org.opendaylight.yangtools.yang.binding.NotificationListener
30 import org.opendaylight.yangtools.yang.binding.Notification
31 import java.util.Arrays
33 import static extension org.opendaylight.controller.sal.binding.codegen.YangtoolsMappingHelper.*
34 import static extension org.opendaylight.controller.sal.binding.codegen.RuntimeCodeSpecification.*
35 import java.util.HashSet
36 import static org.opendaylight.controller.sal.binding.impl.util.ClassLoaderUtils.*
37 import org.opendaylight.controller.sal.binding.spi.NotificationInvokerFactory
38 import org.opendaylight.controller.sal.binding.spi.NotificationInvokerFactory.NotificationInvoker
40 import org.opendaylight.controller.sal.binding.codegen.RuntimeCodeHelper
41 import java.util.WeakHashMap
42 import javassist.ClassClassPath
43 import org.opendaylight.yangtools.yang.binding.annotations.QName
44 import org.opendaylight.yangtools.yang.binding.DataContainer
45 import org.opendaylight.yangtools.yang.binding.RpcImplementation
47 class RuntimeCodeGenerator implements org.opendaylight.controller.sal.binding.codegen.RuntimeCodeGenerator, NotificationInvokerFactory {
49 val CtClass BROKER_NOTIFICATION_LISTENER;
50 val ClassPool classPool;
51 val Map<Class<? extends NotificationListener>, RuntimeGeneratedInvokerPrototype> invokerClasses;
53 public new(ClassPool pool) {
55 invokerClasses = new WeakHashMap();
56 BROKER_NOTIFICATION_LISTENER = org.opendaylight.controller.sal.binding.api.NotificationListener.asCtClass;
59 override <T extends RpcService> getDirectProxyFor(Class<T> iface) {
60 val supertype = iface.asCtClass
61 val targetCls = createClass(iface.directProxyName, supertype) [
62 field(DELEGATE_FIELD, iface);
63 implementMethodsFrom(supertype) [
64 body = '''return ($r) «DELEGATE_FIELD».«it.name»($$);'''
67 return targetCls.toClass(iface.classLoader).newInstance as T
70 override <T extends RpcService> getRouterFor(Class<T> iface) {
71 val contexts = new HashSet<Class<? extends BaseIdentity>>
73 val instance = <RpcRouterCodegenInstance<T>>withClassLoader(iface.classLoader) [ |
74 val supertype = iface.asCtClass
75 val metadata = supertype.rpcMetadata;
76 val targetCls = createClass(iface.routerName, supertype) [
77 addInterface(RpcImplementation.asCtClass)
79 field(DELEGATE_FIELD, iface)
80 //field(REMOTE_INVOKER_FIELD,iface);
82 for (ctx : metadata.contexts) {
83 field(ctx.routingTableField, Map)
85 implementMethodsFrom(supertype) [
86 if (parameterTypes.size === 1) {
87 val rpcMeta = metadata.rpcMethods.get(name);
90 final «InstanceIdentifier.name» identifier = $1.«rpcMeta.inputRouteGetter.name»()«IF rpcMeta.
91 routeEncapsulated».getValue()«ENDIF»;
92 «supertype.name» instance = («supertype.name») «rpcMeta.context.routingTableField».get(identifier);
93 if(instance == null) {
94 instance = «DELEGATE_FIELD»;
96 if(instance == null) {
97 throw new java.lang.IllegalStateException("No provider is processing supplied message");
99 return ($r) instance.«it.name»($$);
102 } else if (parameterTypes.size === 0) {
103 body = '''return ($r) «DELEGATE_FIELD».«it.name»($$);'''
106 implementMethodsFrom(RpcImplementation.asCtClass) [
108 case "getSupportedInputs":
111 throw new java.lang.UnsupportedOperationException("Not implemented yet");
117 «FOR input : metadata.supportedInputs SEPARATOR " else "»
118 «val rpcMetadata = metadata.rpcInputs.get(input)»
119 if(«input.name».class.equals($1)) {
120 return ($r) this.«rpcMetadata.methodName»((«input.name») $2);
123 throw new java.lang.IllegalArgumentException("Not supported message type");
132 val instance = targetCls.toClass(iface.classLoader,iface.protectionDomain).newInstance as T
133 return new RpcRouterCodegenInstance(iface, instance, metadata.contexts,metadata.supportedInputs);
138 private def RpcServiceMetadata getRpcMetadata(CtClass iface) {
139 val metadata = new RpcServiceMetadata;
141 iface.methods.filter[declaringClass == iface && parameterTypes.size === 1].forEach [ method |
142 val routingPair = method.rpcMetadata;
143 if (routingPair !== null) {
144 metadata.contexts.add(routingPair.context)
145 metadata.rpcMethods.put(method.name,routingPair)
146 val input = routingPair.inputType.javaClass as Class<? extends DataContainer>;
147 metadata.supportedInputs.add(input);
148 metadata.rpcInputs.put(input,routingPair);
154 private def getRpcMetadata(CtMethod method) {
155 val inputClass = method.parameterTypes.get(0);
156 return inputClass.rpcMethodMetadata(inputClass,method.name);
159 private def RpcMetadata rpcMethodMetadata(CtClass dataClass,CtClass inputClass,String rpcMethod) {
160 for (method : dataClass.methods) {
161 if (method.name.startsWith("get") && method.parameterTypes.size === 0) {
162 for (annotation : method.availableAnnotations) {
163 if (annotation instanceof RoutingContext) {
164 val encapsulated = !method.returnType.equals(InstanceIdentifier.asCtClass);
165 return new RpcMetadata(null,rpcMethod,(annotation as RoutingContext).value, method, encapsulated,inputClass);
170 for (iface : dataClass.interfaces) {
171 val ret = rpcMethodMetadata(iface,inputClass,rpcMethod);
172 if(ret != null) return ret;
177 private def getJavaClass(CtClass cls) {
178 Thread.currentThread.contextClassLoader.loadClass(cls.name)
181 override getInvokerFactory() {
185 override invokerFor(NotificationListener instance) {
186 val cls = instance.class
187 val prototype = resolveInvokerClass(cls);
189 return new RuntimeGeneratedInvoker(instance, prototype)
192 protected def generateListenerInvoker(Class<? extends NotificationListener> iface) {
193 val callbacks = iface.methods.filter[notificationCallback]
195 val supportedNotification = callbacks.map[parameterTypes.get(0) as Class<? extends Notification>].toSet;
197 val targetCls = createClass(iface.invokerName, BROKER_NOTIFICATION_LISTENER) [
198 field(DELEGATE_FIELD, iface)
199 implementMethodsFrom(BROKER_NOTIFICATION_LISTENER) [
202 «FOR callback : callbacks SEPARATOR " else "»
203 «val cls = callback.parameterTypes.get(0).name»
204 if($1 instanceof «cls») {
205 «DELEGATE_FIELD».«callback.name»((«cls») $1);
214 val finalClass = targetCls.toClass(iface.classLoader, iface.protectionDomain)
215 return new RuntimeGeneratedInvokerPrototype(supportedNotification,
216 finalClass as Class<? extends org.opendaylight.controller.sal.binding.api.NotificationListener>);
219 private def void method(CtClass it, Class<?> returnType, String name, Class<?> parameter, MethodGenerator function1) {
220 val method = new CtMethod(returnType.asCtClass, name, Arrays.asList(parameter.asCtClass), it);
221 function1.process(method);
222 it.addMethod(method);
225 private def void implementMethodsFrom(CtClass target, CtClass source, MethodGenerator function1) {
226 for (method : source.methods) {
227 if (method.declaringClass == source) {
228 val redeclaredMethod = new CtMethod(method, target, null);
229 function1.process(redeclaredMethod);
230 target.addMethod(redeclaredMethod);
235 private def CtClass createClass(String fqn, ClassGenerator cls) {
236 val target = classPool.makeClass(fqn);
241 private def CtClass createClass(String fqn, CtClass superInterface, ClassGenerator cls) {
242 val target = classPool.makeClass(fqn);
243 target.implementsType(superInterface);
248 private def void implementsType(CtClass it, CtClass supertype) {
249 checkArgument(supertype.interface, "Supertype must be interface");
250 addInterface(supertype);
253 private def asCtClass(Class<?> class1) {
254 classPool.get(class1);
257 private def CtField field(CtClass it, String name, Class<?> returnValue) {
258 val field = new CtField(returnValue.asCtClass, name, it);
259 field.modifiers = Modifier.PUBLIC
264 def get(ClassPool pool, Class<?> cls) {
266 return pool.get(cls.name)
267 } catch (NotFoundException e) {
268 pool.appendClassPath(new LoaderClassPath(cls.classLoader));
270 return pool.get(cls.name)
272 } catch (NotFoundException ef) {
273 pool.appendClassPath(new ClassClassPath(cls));
274 return pool.get(cls.name)
279 protected def resolveInvokerClass(Class<? extends NotificationListener> class1) {
280 val invoker = invokerClasses.get(class1);
281 if (invoker !== null) {
284 val newInvoker = generateListenerInvoker(class1);
285 invokerClasses.put(class1, newInvoker);
291 package class RuntimeGeneratedInvoker implements NotificationInvoker {
294 val NotificationListener delegate;
297 var org.opendaylight.controller.sal.binding.api.NotificationListener invocationProxy;
300 var RuntimeGeneratedInvokerPrototype prototype;
302 new(NotificationListener delegate, RuntimeGeneratedInvokerPrototype prototype) {
303 _delegate = delegate;
304 _prototype = prototype;
305 _invocationProxy = prototype.protoClass.newInstance;
306 RuntimeCodeHelper.setDelegate(_invocationProxy, delegate);
309 override getSupportedNotifications() {
310 prototype.supportedNotifications;
318 package class RuntimeGeneratedInvokerPrototype {
321 val Set<Class<? extends Notification>> supportedNotifications;
324 val Class<? extends org.opendaylight.controller.sal.binding.api.NotificationListener> protoClass;
327 package class RpcServiceMetadata {
330 val contexts = new HashSet<Class<? extends BaseIdentity>>();
333 val rpcMethods = new HashMap<String, RpcMetadata>();
336 val rpcInputs = new HashMap<Class<? extends DataContainer>, RpcMetadata>();
340 val supportedInputs = new HashSet<Class<? extends DataContainer>>();
344 package class RpcMetadata {
350 val String methodName;
353 val Class<? extends BaseIdentity> context;
355 val CtMethod inputRouteGetter;
358 val boolean routeEncapsulated;
361 val CtClass inputType;