Merge "Added config service interface for netty EventExecutor, module implementation...
[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 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
39 import java.util.Set
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
46
47 class RuntimeCodeGenerator implements org.opendaylight.controller.sal.binding.codegen.RuntimeCodeGenerator, NotificationInvokerFactory {
48
49     val CtClass BROKER_NOTIFICATION_LISTENER;
50     val ClassPool classPool;
51     val Map<Class<? extends NotificationListener>, RuntimeGeneratedInvokerPrototype> invokerClasses;
52
53     public new(ClassPool pool) {
54         classPool = pool;
55         invokerClasses = new WeakHashMap();
56         BROKER_NOTIFICATION_LISTENER = org.opendaylight.controller.sal.binding.api.NotificationListener.asCtClass;
57     }
58
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»($$);'''
65             ]
66         ]
67         return targetCls.toClass(iface.classLoader).newInstance as T
68     }
69
70     override <T extends RpcService> getRouterFor(Class<T> iface) {
71         val contexts = new HashSet<Class<? extends BaseIdentity>>
72
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)
78                 
79                 field(DELEGATE_FIELD, iface)
80                 //field(REMOTE_INVOKER_FIELD,iface);
81                 
82                 for (ctx : metadata.contexts) {
83                     field(ctx.routingTableField, Map)
84                 }
85                 implementMethodsFrom(supertype) [
86                     if (parameterTypes.size === 1) {
87                         val rpcMeta = metadata.rpcMethods.get(name);
88                         val bodyTmp = '''
89                         {
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»;
95                             }
96                             if(instance == null) {
97                                 throw new java.lang.IllegalStateException("No provider is processing supplied message");
98                             }
99                             return ($r) instance.«it.name»($$);
100                         }'''
101                         body = bodyTmp
102                     } else if (parameterTypes.size === 0) {
103                         body = '''return ($r) «DELEGATE_FIELD».«it.name»($$);'''
104                     }
105                 ]
106                 implementMethodsFrom(RpcImplementation.asCtClass) [
107                     switch (name) {
108                         case "getSupportedInputs":
109                             body = '''
110                             {
111                                 throw new java.lang.UnsupportedOperationException("Not implemented yet");
112                                 return ($r) null;
113                             }'''
114                         case "invoke": {
115                             val tmpBody = '''
116                             {
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);
121                                 }
122                                 «ENDFOR»
123                                 throw new java.lang.IllegalArgumentException("Not supported message type");
124                                 return ($r) null;
125                             }
126                             '''
127                             body = tmpBody
128                         }
129                     }
130                 ]
131             ]
132             val instance = targetCls.toClass(iface.classLoader,iface.protectionDomain).newInstance as T
133             return new RpcRouterCodegenInstance(iface, instance, metadata.contexts,metadata.supportedInputs);
134         ];
135         return instance;
136     }
137
138     private def RpcServiceMetadata getRpcMetadata(CtClass iface) {
139         val metadata = new RpcServiceMetadata;
140         
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);
149             }
150         ]
151         return metadata;
152     }
153
154     private def getRpcMetadata(CtMethod method) {
155         val inputClass = method.parameterTypes.get(0); 
156         return inputClass.rpcMethodMetadata(inputClass,method.name);
157     }
158
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);
166                     }
167                 }
168             }
169         }
170         for (iface : dataClass.interfaces) {
171             val ret = rpcMethodMetadata(iface,inputClass,rpcMethod);
172             if(ret != null) return ret;
173         }
174         return null;
175     }
176
177     private def getJavaClass(CtClass cls) {
178         Thread.currentThread.contextClassLoader.loadClass(cls.name)
179     }
180
181     override getInvokerFactory() {
182         return this;
183     }
184
185     override invokerFor(NotificationListener instance) {
186         val cls = instance.class
187         val prototype = resolveInvokerClass(cls);
188
189         return new RuntimeGeneratedInvoker(instance, prototype)
190     }
191
192     protected def generateListenerInvoker(Class<? extends NotificationListener> iface) {
193         val callbacks = iface.methods.filter[notificationCallback]
194
195         val supportedNotification = callbacks.map[parameterTypes.get(0) as Class<? extends Notification>].toSet;
196
197         val targetCls = createClass(iface.invokerName, BROKER_NOTIFICATION_LISTENER) [
198             field(DELEGATE_FIELD, iface)
199             implementMethodsFrom(BROKER_NOTIFICATION_LISTENER) [
200                 body = '''
201                     {
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);
206                                     return null;
207                                 }
208                         «ENDFOR»
209                         return null;
210                     }
211                 '''
212             ]
213         ]
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>);
217     }
218
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);
223     }
224
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);
231             }
232         }
233     }
234
235     private def CtClass createClass(String fqn, ClassGenerator cls) {
236         val target = classPool.makeClass(fqn);
237         cls.process(target);
238         return target;
239     }
240
241     private def CtClass createClass(String fqn, CtClass superInterface, ClassGenerator cls) {
242         val target = classPool.makeClass(fqn);
243         target.implementsType(superInterface);
244         cls.process(target);
245         return target;
246     }
247
248     private def void implementsType(CtClass it, CtClass supertype) {
249         checkArgument(supertype.interface, "Supertype must be interface");
250         addInterface(supertype);
251     }
252
253     private def asCtClass(Class<?> class1) {
254         classPool.get(class1);
255     }
256
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
260         addField(field);
261         return field;
262     }
263
264     def get(ClassPool pool, Class<?> cls) {
265         try {
266             return pool.get(cls.name)
267         } catch (NotFoundException e) {
268             pool.appendClassPath(new LoaderClassPath(cls.classLoader));
269             try {
270                 return pool.get(cls.name)
271
272             } catch (NotFoundException ef) {
273                 pool.appendClassPath(new ClassClassPath(cls));
274                 return pool.get(cls.name)
275             }
276         }
277     }
278
279     protected def resolveInvokerClass(Class<? extends NotificationListener> class1) {
280         val invoker = invokerClasses.get(class1);
281         if (invoker !== null) {
282             return invoker;
283         }
284         val newInvoker = generateListenerInvoker(class1);
285         invokerClasses.put(class1, newInvoker);
286         return newInvoker
287     }
288 }
289
290 @Data
291 package class RuntimeGeneratedInvoker implements NotificationInvoker {
292
293     @Property
294     val NotificationListener delegate;
295
296     @Property
297     var org.opendaylight.controller.sal.binding.api.NotificationListener invocationProxy;
298
299     @Property
300     var RuntimeGeneratedInvokerPrototype prototype;
301
302     new(NotificationListener delegate, RuntimeGeneratedInvokerPrototype prototype) {
303         _delegate = delegate;
304         _prototype = prototype;
305         _invocationProxy = prototype.protoClass.newInstance;
306         RuntimeCodeHelper.setDelegate(_invocationProxy, delegate);
307     }
308
309     override getSupportedNotifications() {
310         prototype.supportedNotifications;
311     }
312
313     override close() {
314     }
315 }
316
317 @Data
318 package class RuntimeGeneratedInvokerPrototype {
319
320     @Property
321     val Set<Class<? extends Notification>> supportedNotifications;
322
323     @Property
324     val Class<? extends org.opendaylight.controller.sal.binding.api.NotificationListener> protoClass;
325 }
326
327 package class RpcServiceMetadata {
328
329     @Property
330     val contexts = new HashSet<Class<? extends BaseIdentity>>();
331
332     @Property
333     val rpcMethods = new HashMap<String, RpcMetadata>();
334     
335     @Property
336     val rpcInputs = new HashMap<Class<? extends DataContainer>, RpcMetadata>();
337     
338     
339     @Property
340     val supportedInputs = new HashSet<Class<? extends DataContainer>>();
341 }
342
343 @Data
344 package class RpcMetadata {
345
346     @Property
347     val QName qname;
348
349     @Property
350     val String methodName;
351
352     @Property
353     val Class<? extends BaseIdentity> context;
354     @Property
355     val CtMethod inputRouteGetter;
356
357     @Property
358     val boolean routeEncapsulated;
359     
360     @Property
361     val CtClass inputType;
362 }