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