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