Merge "Fix for bug #236 and bug #240 Have made changes in opendaylight-table-types...
[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 javassist.CtMethod
15 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier
16 import org.opendaylight.yangtools.yang.binding.annotations.RoutingContext
17 import org.opendaylight.yangtools.yang.binding.BaseIdentity
18
19 import java.util.Map
20 import java.util.HashMap
21
22
23 import org.opendaylight.yangtools.yang.binding.NotificationListener
24 import org.opendaylight.yangtools.yang.binding.Notification
25
26
27 import static extension org.opendaylight.controller.sal.binding.codegen.YangtoolsMappingHelper.*
28 import static extension org.opendaylight.controller.sal.binding.codegen.RuntimeCodeSpecification.*
29 import java.util.HashSet
30 import static org.opendaylight.yangtools.concepts.util.ClassLoaderUtils.*
31 import org.opendaylight.controller.sal.binding.spi.NotificationInvokerFactory
32 import org.opendaylight.controller.sal.binding.spi.NotificationInvokerFactory.NotificationInvoker
33 import java.util.Set
34 import org.opendaylight.controller.sal.binding.codegen.RuntimeCodeHelper
35 import java.util.WeakHashMap
36 import org.opendaylight.yangtools.yang.binding.annotations.QName
37 import org.opendaylight.yangtools.yang.binding.DataContainer
38 import org.opendaylight.yangtools.yang.binding.RpcImplementation
39 import org.opendaylight.yangtools.sal.binding.generator.util.JavassistUtils
40 import org.opendaylight.yangtools.sal.binding.generator.util.ClassLoaderUtils
41 import javassist.LoaderClassPath
42
43 class RuntimeCodeGenerator implements org.opendaylight.controller.sal.binding.codegen.RuntimeCodeGenerator, NotificationInvokerFactory {
44
45     val CtClass BROKER_NOTIFICATION_LISTENER;
46     val ClassPool classPool;
47     val extension JavassistUtils utils;
48     val Map<Class<? extends NotificationListener>, RuntimeGeneratedInvokerPrototype> invokerClasses;
49
50
51     new(ClassPool pool) {
52         classPool = pool;
53         utils = new JavassistUtils(pool);
54         invokerClasses = new WeakHashMap();
55         BROKER_NOTIFICATION_LISTENER = org.opendaylight.controller.sal.binding.api.NotificationListener.asCtClass;
56         pool.appendClassPath(new LoaderClassPath(RpcService.classLoader));
57     }
58
59     override <T extends RpcService> getDirectProxyFor(Class<T> iface) {
60         val T instance =  withClassLoaderAndLock(iface.classLoader,lock) [|
61             val proxyName = iface.directProxyName;
62             val potentialClass = ClassLoaderUtils.tryToLoadClassWithTCCL(proxyName)
63             if(potentialClass != null) {
64                 return potentialClass.newInstance as T;
65             }
66             val supertype = iface.asCtClass
67             val createdCls = createClass(iface.directProxyName, supertype) [
68                 field(DELEGATE_FIELD, iface);
69                 implementsType(RpcImplementation.asCtClass)
70                 implementMethodsFrom(supertype) [
71                     body = '''
72                     {
73                         if(«DELEGATE_FIELD» == null) {
74                             throw new java.lang.IllegalStateException("No default provider is available");
75                         }
76                         return ($r) «DELEGATE_FIELD».«it.name»($$);
77                     }
78                     '''
79                 ]
80                 implementMethodsFrom(RpcImplementation.asCtClass) [
81                     body = '''
82                     {
83                         throw new java.lang.IllegalStateException("No provider is processing supplied message");
84                         return ($r) null;
85                     }
86                     '''
87                 ]
88             ]
89             return createdCls.toClass(iface.classLoader).newInstance as T
90         ]
91         return instance;
92     }
93
94     override <T extends RpcService> getRouterFor(Class<T> iface,String routerInstanceName) {
95         val metadata = withClassLoader(iface.classLoader) [|
96             val supertype = iface.asCtClass
97             return supertype.rpcMetadata;
98         ]
99
100         val instance = <T>withClassLoaderAndLock(iface.classLoader,lock) [ |
101             val supertype = iface.asCtClass
102             val routerName = iface.routerName;
103             val potentialClass = ClassLoaderUtils.tryToLoadClassWithTCCL(routerName)
104             if(potentialClass != null) {
105                 return potentialClass.newInstance as T;
106             }
107
108             val targetCls = createClass(iface.routerName, supertype) [
109
110
111                 field(DELEGATE_FIELD, iface)
112                 //field(REMOTE_INVOKER_FIELD,iface);
113                 implementsType(RpcImplementation.asCtClass)
114
115                 for (ctx : metadata.contexts) {
116                     field(ctx.routingTableField, Map)
117                 }
118                 implementMethodsFrom(supertype) [
119                     if (parameterTypes.size === 1) {
120                         val rpcMeta = metadata.rpcMethods.get(name);
121                         val bodyTmp = '''
122                         {
123                             final «InstanceIdentifier.name» identifier = $1.«rpcMeta.inputRouteGetter.name»()«IF rpcMeta.
124                             routeEncapsulated».getValue()«ENDIF»;
125                             «supertype.name» instance = («supertype.name») «rpcMeta.context.routingTableField».get(identifier);
126                             if(instance == null) {
127                                instance = «DELEGATE_FIELD»;
128                             }
129                             if(instance == null) {
130                                 throw new java.lang.IllegalStateException("No routable provider is processing routed message for " + String.valueOf(identifier));
131                             }
132                             return ($r) instance.«it.name»($$);
133                         }'''
134                         body = bodyTmp
135                     } else if (parameterTypes.size === 0) {
136                         body = '''return ($r) «DELEGATE_FIELD».«it.name»($$);'''
137                     }
138                 ]
139                 implementMethodsFrom(RpcImplementation.asCtClass) [
140                     body = '''
141                     {
142                         throw new java.lang.IllegalStateException("No provider is processing supplied message");
143                         return ($r) null;
144                     }
145                     '''
146                 ]
147             ]
148             return targetCls.toClass(iface.classLoader,iface.protectionDomain).newInstance as T
149
150         ];
151         return new RpcRouterCodegenInstance(routerInstanceName,iface, instance, metadata.contexts,metadata.supportedInputs);
152     }
153
154     private def RpcServiceMetadata getRpcMetadata(CtClass iface) {
155         val metadata = new RpcServiceMetadata;
156
157         iface.methods.filter[declaringClass == iface && parameterTypes.size === 1].forEach [ method |
158             val routingPair = method.rpcMetadata;
159             if (routingPair !== null) {
160                 metadata.contexts.add(routingPair.context)
161                 metadata.rpcMethods.put(method.name,routingPair)
162                 val input = routingPair.inputType.javaClass as Class<? extends DataContainer>;
163                 metadata.supportedInputs.add(input);
164                 metadata.rpcInputs.put(input,routingPair);
165             }
166         ]
167         return metadata;
168     }
169
170     private def getRpcMetadata(CtMethod method) {
171         val inputClass = method.parameterTypes.get(0);
172         return inputClass.rpcMethodMetadata(inputClass,method.name);
173     }
174
175     private def RpcMetadata rpcMethodMetadata(CtClass dataClass,CtClass inputClass,String rpcMethod) {
176         for (method : dataClass.methods) {
177             if (method.name.startsWith("get") && method.parameterTypes.size === 0) {
178                 for (annotation : method.availableAnnotations) {
179                     if (annotation instanceof RoutingContext) {
180                         val encapsulated = !method.returnType.equals(InstanceIdentifier.asCtClass);
181                         return new RpcMetadata(null,rpcMethod,(annotation as RoutingContext).value, method, encapsulated,inputClass);
182                     }
183                 }
184             }
185         }
186         for (iface : dataClass.interfaces) {
187             val ret = rpcMethodMetadata(iface,inputClass,rpcMethod);
188             if(ret != null) return ret;
189         }
190         return null;
191     }
192
193     private def getJavaClass(CtClass cls) {
194         Thread.currentThread.contextClassLoader.loadClass(cls.name)
195     }
196
197     override getInvokerFactory() {
198         return this;
199     }
200
201     override invokerFor(NotificationListener instance) {
202         val cls = instance.class
203         val prototype = resolveInvokerClass(cls);
204
205         return new RuntimeGeneratedInvoker(instance, prototype)
206     }
207
208     protected def generateListenerInvoker(Class<? extends NotificationListener> iface) {
209         val callbacks = iface.methods.filter[notificationCallback]
210
211         val supportedNotification = callbacks.map[parameterTypes.get(0) as Class<? extends Notification>].toSet;
212
213         val targetCls = createClass(iface.invokerName, BROKER_NOTIFICATION_LISTENER) [
214             field(DELEGATE_FIELD, iface)
215             implementMethodsFrom(BROKER_NOTIFICATION_LISTENER) [
216                 body = '''
217                     {
218                         «FOR callback : callbacks SEPARATOR " else "»
219                             «val cls = callback.parameterTypes.get(0).name»
220                                 if($1 instanceof «cls») {
221                                     «DELEGATE_FIELD».«callback.name»((«cls») $1);
222                                     return null;
223                                 }
224                         «ENDFOR»
225                         return null;
226                     }
227                 '''
228             ]
229         ]
230         val finalClass = targetCls.toClass(iface.classLoader, iface.protectionDomain)
231         return new RuntimeGeneratedInvokerPrototype(supportedNotification,
232             finalClass as Class<? extends org.opendaylight.controller.sal.binding.api.NotificationListener<?>>);
233     }
234
235
236
237
238
239     protected def resolveInvokerClass(Class<? extends NotificationListener> class1) {
240         return <RuntimeGeneratedInvokerPrototype>withClassLoaderAndLock(class1.classLoader,lock) [|
241             val invoker = invokerClasses.get(class1);
242             if (invoker !== null) {
243                 return invoker;
244             }
245             val newInvoker = generateListenerInvoker(class1);
246             invokerClasses.put(class1, newInvoker);
247             return newInvoker
248
249         ]
250     }
251 }
252
253 @Data
254 package class RuntimeGeneratedInvoker implements NotificationInvoker {
255
256     @Property
257     val NotificationListener delegate;
258
259     @Property
260     var org.opendaylight.controller.sal.binding.api.NotificationListener<Notification> invocationProxy;
261
262     @Property
263     var RuntimeGeneratedInvokerPrototype prototype;
264
265     new(NotificationListener delegate, RuntimeGeneratedInvokerPrototype prototype) {
266         _delegate = delegate;
267         _prototype = prototype;
268         _invocationProxy = prototype.protoClass.newInstance as org.opendaylight.controller.sal.binding.api.NotificationListener<Notification>;
269         RuntimeCodeHelper.setDelegate(_invocationProxy, delegate);
270     }
271
272     override getSupportedNotifications() {
273         prototype.supportedNotifications;
274     }
275
276     override close() {
277     }
278 }
279
280 @Data
281 package class RuntimeGeneratedInvokerPrototype {
282
283     @Property
284     val Set<Class<? extends Notification>> supportedNotifications;
285
286     @Property
287     val Class<? extends org.opendaylight.controller.sal.binding.api.NotificationListener<?>> protoClass;
288 }
289
290 package class RpcServiceMetadata {
291
292     @Property
293     val contexts = new HashSet<Class<? extends BaseIdentity>>();
294
295     @Property
296     val rpcMethods = new HashMap<String, RpcMetadata>();
297
298     @Property
299     val rpcInputs = new HashMap<Class<? extends DataContainer>, RpcMetadata>();
300
301
302     @Property
303     val supportedInputs = new HashSet<Class<? extends DataContainer>>();
304 }
305
306 @Data
307 package class RpcMetadata {
308
309     @Property
310     val QName qname;
311
312     @Property
313     val String methodName;
314
315     @Property
316     val Class<? extends BaseIdentity> context;
317     @Property
318     val CtMethod inputRouteGetter;
319
320     @Property
321     val boolean routeEncapsulated;
322
323     @Property
324     val CtClass inputType;
325 }