Updated implementation of internal RPC Router for Binding-Aware Broker and added...
[controller.git] / opendaylight / md-sal / sal-binding-broker / src / main / java / org / opendaylight / controller / sal / binding / codegen / impl / RuntimeCodeGenerator.xtend
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.controller.sal.binding.codegen.impl
9
10 import javassist.ClassPool
11 import org.opendaylight.yangtools.yang.binding.RpcService
12
13 import javassist.CtClass
14 import static com.google.common.base.Preconditions.*
15
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
22
23 import java.util.Map
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
32
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 java.io.ObjectOutputStream.PutField
37 import static org.opendaylight.controller.sal.binding.impl.osgi.ClassLoaderUtils.*
38
39 class RuntimeCodeGenerator implements org.opendaylight.controller.sal.binding.codegen.RuntimeCodeGenerator {
40
41     val ClassPool classPool;
42
43     public new(ClassPool pool) {
44         classPool = pool;
45     }
46
47     override <T extends RpcService> getDirectProxyFor(Class<T> iface) {
48         val supertype = iface.asCtClass
49         val targetCls = createClass(iface.directProxyName, supertype) [
50             field(DELEGATE_FIELD, iface);
51             implementMethodsFrom(supertype) [
52                 body = '''return ($r) «DELEGATE_FIELD».«it.name»($$);'''
53             ]
54         ]
55         return targetCls.toClass(iface.classLoader).newInstance as T
56     }
57
58     override <T extends RpcService> getRouterFor(Class<T> iface) {
59         val contexts = new HashSet<Class<? extends BaseIdentity>>
60
61         val instance = <T>withClassLoader(iface.classLoader) [ |
62             val supertype = iface.asCtClass
63             val targetCls = createClass(iface.routerName, supertype) [
64                 //field(ROUTING_TABLE_FIELD,Map)
65                 field(DELEGATE_FIELD, iface)
66                 val ctxMap = new HashMap<String, Class<? extends BaseIdentity>>();
67                 // We search for routing pairs and add fields
68                 supertype.methods.filter[declaringClass == supertype && parameterTypes.size === 1].forEach [ method |
69                     val routingPair = method.routingContextInput;
70                     if (routingPair !== null) {
71                         ctxMap.put(routingPair.context.routingTableField, routingPair.context);
72                         contexts.add(routingPair.context)
73                     }
74                 ]
75                 for (ctx : ctxMap.entrySet) {
76                     field(ctx.key, Map)
77                 }
78                 implementMethodsFrom(supertype) [
79                     if (parameterTypes.size === 1) {
80                         val routingPair = routingContextInput;
81                         val bodyTmp = '''
82                         {
83                             final «InstanceIdentifier.name» identifier = $1.«routingPair.getter.name»()«IF routingPair.encapsulated».getValue()«ENDIF»;
84                             «supertype.name» instance = («supertype.name») «routingPair.context.routingTableField».get(identifier);
85                             if(instance == null) {
86                                instance = «DELEGATE_FIELD»;
87                             }
88                             if(instance == null) {
89                                 throw new java.lang.IllegalStateException("No provider is processing supplied message");
90                             }
91                             return ($r) instance.«it.name»($$);
92                         }'''
93                         body = bodyTmp
94                     } else if (parameterTypes.size === 0) {
95                         body = '''return ($r) «DELEGATE_FIELD».«it.name»($$);'''
96                     }
97                 ]
98             ]
99             return targetCls.toClass(iface.classLoader).newInstance as T
100         ];
101         return new RpcRouterCodegenInstance(iface, instance, contexts);
102     }
103
104     def Class<?> generateListenerInvoker(Class<? extends NotificationListener> iface) {
105         val targetCls = createClass(iface.invokerName) [
106             field(DELEGATE_FIELD, iface)
107             it.method(Void, "invoke", Notification) [
108                 val callbacks = iface.methods.filter[notificationCallback]
109                 body = '''
110                     {
111                         «FOR callback : callbacks SEPARATOR " else "»
112                             if($1 instanceof «val cls = callback.parameterTypes.get(0).name») {
113                                 «DELEGATE_FIELD».«callback.name»((«cls») $1);
114                                 return;
115                             }
116                         «ENDFOR»
117                     }
118                 '''
119             ]
120         ]
121         return targetCls.toClass(iface.classLoader);
122     }
123
124     def void method(CtClass it, Class<?> returnType, String name, Class<?> parameter, MethodGenerator function1) {
125         val method = new CtMethod(returnType.asCtClass, name, Arrays.asList(parameter.asCtClass), it);
126         function1.process(method);
127         it.addMethod(method);
128     }
129
130     private def routingContextInput(CtMethod method) {
131         val inputClass = method.parameterTypes.get(0);
132         return inputClass.contextInstance;
133     }
134
135     private def RoutingPair getContextInstance(CtClass dataClass) {
136         for (method : dataClass.methods) {
137             if (method.name.startsWith("get") && method.parameterTypes.size === 0) {
138                 for (annotation : method.availableAnnotations) {
139                     if (annotation instanceof RoutingContext) {
140                         val encapsulated = !method.returnType.equals(InstanceIdentifier.asCtClass);
141                         
142                         return new RoutingPair((annotation as RoutingContext).value, method,encapsulated);
143                     }
144                 }
145             }
146         }
147         for (iface : dataClass.interfaces) {
148             val ret = getContextInstance(iface);
149             if(ret != null) return ret;
150         }
151         return null;
152     }
153
154     private def void implementMethodsFrom(CtClass target, CtClass source, MethodGenerator function1) {
155         for (method : source.methods) {
156             if (method.declaringClass == source) {
157                 val redeclaredMethod = new CtMethod(method, target, null);
158                 function1.process(redeclaredMethod);
159                 target.addMethod(redeclaredMethod);
160             }
161         }
162     }
163
164     private def CtClass createClass(String fqn, ClassGenerator cls) {
165         val target = classPool.makeClass(fqn);
166         cls.process(target);
167         return target;
168     }
169
170     private def CtClass createClass(String fqn, CtClass superInterface, ClassGenerator cls) {
171         val target = classPool.makeClass(fqn);
172         target.implementsType(superInterface);
173         cls.process(target);
174         return target;
175     }
176
177     private def void implementsType(CtClass it, CtClass supertype) {
178         checkArgument(supertype.interface, "Supertype must be interface");
179         addInterface(supertype);
180     }
181
182     private def asCtClass(Class<?> class1) {
183         classPool.get(class1);
184     }
185
186     private def CtField field(CtClass it, String name, Class<?> returnValue) {
187         val field = new CtField(returnValue.asCtClass, name, it);
188         field.modifiers = Modifier.PUBLIC
189         addField(field);
190         return field;
191     }
192
193     def get(ClassPool pool, Class<?> cls) {
194         try {
195             return pool.get(cls.name)
196         } catch (NotFoundException e) {
197             pool.appendClassPath(new LoaderClassPath(cls.classLoader));
198             return pool.get(cls.name)
199         }
200     }
201 }