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