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