Merge "- netconf SSH bridge bundle - Implement bridge using socket - standard netconf...
[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.controller.sal.binding.impl.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.controller.sal.binding.codegen.util.JavassistUtils
40
41 class RuntimeCodeGenerator implements org.opendaylight.controller.sal.binding.codegen.RuntimeCodeGenerator, NotificationInvokerFactory {
42
43     val CtClass BROKER_NOTIFICATION_LISTENER;
44     val ClassPool classPool;
45     val extension JavassistUtils utils;
46     val Map<Class<? extends NotificationListener>, RuntimeGeneratedInvokerPrototype> invokerClasses;
47
48     public new(ClassPool pool) {
49         classPool = pool;
50         utils = new JavassistUtils(pool);
51         invokerClasses = new WeakHashMap();
52         BROKER_NOTIFICATION_LISTENER = org.opendaylight.controller.sal.binding.api.NotificationListener.asCtClass;
53     }
54
55     override <T extends RpcService> getDirectProxyFor(Class<T> iface) {
56         val supertype = iface.asCtClass
57         val targetCls = createClass(iface.directProxyName, supertype) [
58             field(DELEGATE_FIELD, iface);
59             implementMethodsFrom(supertype) [
60                 body = '''
61                 {
62                     if(«DELEGATE_FIELD» == null) {
63                         throw new java.lang.IllegalStateException("No provider is processing supplied message");
64                     }
65                     return ($r) «DELEGATE_FIELD».«it.name»($$);
66                 }
67                 '''
68             ]
69         ]
70         return targetCls.toClass(iface.classLoader).newInstance as T
71     }
72
73     override <T extends RpcService> getRouterFor(Class<T> iface) {
74         val instance = <RpcRouterCodegenInstance<T>>withClassLoaderAndLock(iface.classLoader,lock) [ |
75             val supertype = iface.asCtClass
76             val metadata = supertype.rpcMetadata;
77             val targetCls = createClass(iface.routerName, supertype) [
78                 addInterface(RpcImplementation.asCtClass)
79                 
80                 field(DELEGATE_FIELD, iface)
81                 //field(REMOTE_INVOKER_FIELD,iface);
82                 
83                 for (ctx : metadata.contexts) {
84                     field(ctx.routingTableField, Map)
85                 }
86                 implementMethodsFrom(supertype) [
87                     if (parameterTypes.size === 1) {
88                         val rpcMeta = metadata.rpcMethods.get(name);
89                         val bodyTmp = '''
90                         {
91                             final «InstanceIdentifier.name» identifier = $1.«rpcMeta.inputRouteGetter.name»()«IF rpcMeta.
92                             routeEncapsulated».getValue()«ENDIF»;
93                             «supertype.name» instance = («supertype.name») «rpcMeta.context.routingTableField».get(identifier);
94                             if(instance == null) {
95                                instance = «DELEGATE_FIELD»;
96                             }
97                             if(instance == null) {
98                                 throw new java.lang.IllegalStateException("No provider is processing supplied message");
99                             }
100                             return ($r) instance.«it.name»($$);
101                         }'''
102                         body = bodyTmp
103                     } else if (parameterTypes.size === 0) {
104                         body = '''return ($r) «DELEGATE_FIELD».«it.name»($$);'''
105                     }
106                 ]
107                 implementMethodsFrom(RpcImplementation.asCtClass) [
108                     switch (name) {
109                         case "getSupportedInputs":
110                             body = '''
111                             {
112                                 throw new java.lang.UnsupportedOperationException("Not implemented yet");
113                                 return ($r) null;
114                             }'''
115                         case "invoke": {
116                             val tmpBody = '''
117                             {
118                                 «FOR input : metadata.supportedInputs SEPARATOR " else "»
119                                 «val rpcMetadata = metadata.rpcInputs.get(input)»
120                                 if(«input.name».class.equals($1)) {
121                                     return ($r) this.«rpcMetadata.methodName»((«input.name») $2);
122                                 }
123                                 «ENDFOR»
124                                 throw new java.lang.IllegalArgumentException("Not supported message type");
125                                 return ($r) null;
126                             }
127                             '''
128                             body = tmpBody
129                         }
130                     }
131                 ]
132             ]
133             val instance = targetCls.toClass(iface.classLoader,iface.protectionDomain).newInstance as T
134             return new RpcRouterCodegenInstance(iface, instance, metadata.contexts,metadata.supportedInputs);
135         ];
136         return instance;
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(null,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 new RuntimeGeneratedInvoker(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     
221
222
223
224     protected def resolveInvokerClass(Class<? extends NotificationListener> class1) {
225         return <RuntimeGeneratedInvokerPrototype>withClassLoaderAndLock(class1.classLoader,lock) [|
226             val invoker = invokerClasses.get(class1);
227             if (invoker !== null) {
228                 return invoker;
229             }
230             val newInvoker = generateListenerInvoker(class1);
231             invokerClasses.put(class1, newInvoker);
232             return newInvoker
233             
234         ]
235     }
236 }
237
238 @Data
239 package class RuntimeGeneratedInvoker implements NotificationInvoker {
240
241     @Property
242     val NotificationListener delegate;
243
244     @Property
245     var org.opendaylight.controller.sal.binding.api.NotificationListener<Notification> invocationProxy;
246
247     @Property
248     var RuntimeGeneratedInvokerPrototype prototype;
249
250     new(NotificationListener delegate, RuntimeGeneratedInvokerPrototype prototype) {
251         _delegate = delegate;
252         _prototype = prototype;
253         _invocationProxy = prototype.protoClass.newInstance as org.opendaylight.controller.sal.binding.api.NotificationListener<Notification>;
254         RuntimeCodeHelper.setDelegate(_invocationProxy, delegate);
255     }
256
257     override getSupportedNotifications() {
258         prototype.supportedNotifications;
259     }
260
261     override close() {
262     }
263 }
264
265 @Data
266 package class RuntimeGeneratedInvokerPrototype {
267
268     @Property
269     val Set<Class<? extends Notification>> supportedNotifications;
270
271     @Property
272     val Class<? extends org.opendaylight.controller.sal.binding.api.NotificationListener<?>> protoClass;
273 }
274
275 package class RpcServiceMetadata {
276
277     @Property
278     val contexts = new HashSet<Class<? extends BaseIdentity>>();
279
280     @Property
281     val rpcMethods = new HashMap<String, RpcMetadata>();
282     
283     @Property
284     val rpcInputs = new HashMap<Class<? extends DataContainer>, RpcMetadata>();
285     
286     
287     @Property
288     val supportedInputs = new HashSet<Class<? extends DataContainer>>();
289 }
290
291 @Data
292 package class RpcMetadata {
293
294     @Property
295     val QName qname;
296
297     @Property
298     val String methodName;
299
300     @Property
301     val Class<? extends BaseIdentity> context;
302     @Property
303     val CtMethod inputRouteGetter;
304
305     @Property
306     val boolean routeEncapsulated;
307     
308     @Property
309     val CtClass inputType;
310 }