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