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