0aee95ea4175a72c9ebbd4b14827b827547ae335
[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
16 import javassist.CtField
17 import javassist.Modifier
18 import javassist.CtMethod
19 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier
20 import org.opendaylight.yangtools.yang.binding.annotations.RoutingContext
21 import org.opendaylight.yangtools.yang.binding.BaseIdentity
22
23 import java.util.Map
24 import java.util.HashMap
25 import javassist.NotFoundException
26 import javassist.LoaderClassPath
27 import org.opendaylight.controller.sal.binding.codegen.impl.JavassistUtils.MethodGenerator
28 import org.opendaylight.controller.sal.binding.codegen.impl.JavassistUtils.ClassGenerator
29 import org.opendaylight.yangtools.yang.binding.NotificationListener
30 import org.opendaylight.yangtools.yang.binding.Notification
31 import java.util.Arrays
32
33 import static extension org.opendaylight.controller.sal.binding.codegen.YangtoolsMappingHelper.*
34 import static extension org.opendaylight.controller.sal.binding.codegen.RuntimeCodeSpecification.*
35 import java.util.HashSet
36 import java.io.ObjectOutputStream.PutField
37 import static org.opendaylight.controller.sal.binding.impl.osgi.ClassLoaderUtils.*
38 import javax.xml.ws.spi.Invoker
39 import org.opendaylight.controller.sal.binding.spi.NotificationInvokerFactory
40 import org.opendaylight.controller.sal.binding.spi.NotificationInvokerFactory.NotificationInvoker
41 import java.util.Set
42 import java.util.Collections
43 import org.opendaylight.controller.sal.binding.codegen.RuntimeCodeHelper
44 import java.util.WeakHashMap
45 import javassist.ClassClassPath
46
47 class RuntimeCodeGenerator implements org.opendaylight.controller.sal.binding.codegen.RuntimeCodeGenerator, NotificationInvokerFactory {
48
49     val CtClass BROKER_NOTIFICATION_LISTENER;
50     val ClassPool classPool;
51     val Map<Class<? extends NotificationListener>, RuntimeGeneratedInvokerPrototype> invokerClasses;
52
53     public new(ClassPool pool) {
54         classPool = pool;
55         invokerClasses = new WeakHashMap();
56         BROKER_NOTIFICATION_LISTENER = org.opendaylight.controller.sal.binding.api.NotificationListener.asCtClass;
57     }
58
59     override <T extends RpcService> getDirectProxyFor(Class<T> iface) {
60         val supertype = iface.asCtClass
61         val targetCls = createClass(iface.directProxyName, supertype) [
62             field(DELEGATE_FIELD, iface);
63             implementMethodsFrom(supertype) [
64                 body = '''return ($r) «DELEGATE_FIELD».«it.name»($$);'''
65             ]
66         ]
67         return targetCls.toClass(iface.classLoader).newInstance as T
68     }
69
70     override <T extends RpcService> getRouterFor(Class<T> iface) {
71         val contexts = new HashSet<Class<? extends BaseIdentity>>
72
73         val instance = <T>withClassLoader(iface.classLoader) [ |
74             val supertype = iface.asCtClass
75             val targetCls = createClass(iface.routerName, supertype) [
76                 //field(ROUTING_TABLE_FIELD,Map)
77                 field(DELEGATE_FIELD, iface)
78                 val ctxMap = new HashMap<String, Class<? extends BaseIdentity>>();
79                 // We search for routing pairs and add fields
80                 supertype.methods.filter[declaringClass == supertype && parameterTypes.size === 1].forEach [ method |
81                     val routingPair = method.routingContextInput;
82                     if (routingPair !== null) {
83                         ctxMap.put(routingPair.context.routingTableField, routingPair.context);
84                         contexts.add(routingPair.context)
85                     }
86                 ]
87                 for (ctx : ctxMap.entrySet) {
88                     field(ctx.key, Map)
89                 }
90                 implementMethodsFrom(supertype) [
91                     if (parameterTypes.size === 1) {
92                         val routingPair = routingContextInput;
93                         val bodyTmp = '''
94                         {
95                             final «InstanceIdentifier.name» identifier = $1.«routingPair.getter.name»()«IF routingPair.
96                             encapsulated».getValue()«ENDIF»;
97                             «supertype.name» instance = («supertype.name») «routingPair.context.routingTableField».get(identifier);
98                             if(instance == null) {
99                                instance = «DELEGATE_FIELD»;
100                             }
101                             if(instance == null) {
102                                 throw new java.lang.IllegalStateException("No provider is processing supplied message");
103                             }
104                             return ($r) instance.«it.name»($$);
105                         }'''
106                         body = bodyTmp
107                     } else if (parameterTypes.size === 0) {
108                         body = '''return ($r) «DELEGATE_FIELD».«it.name»($$);'''
109                     }
110                 ]
111             ]
112             return targetCls.toClass(iface.classLoader).newInstance as T
113         ];
114         return new RpcRouterCodegenInstance(iface, instance, contexts);
115     }
116
117     protected def generateListenerInvoker(Class<? extends NotificationListener> iface) {
118         val callbacks = iface.methods.filter[notificationCallback]
119
120         val supportedNotification = callbacks.map[parameterTypes.get(0) as Class<? extends Notification>].toSet;
121
122         val targetCls = createClass(iface.invokerName,BROKER_NOTIFICATION_LISTENER ) [
123             field(DELEGATE_FIELD, iface)
124             implementMethodsFrom(BROKER_NOTIFICATION_LISTENER) [
125                 body = '''
126                     {
127                         «FOR callback : callbacks SEPARATOR " else "»
128                         «val cls = callback.parameterTypes.get(0).name»
129                             if($1 instanceof «cls») {
130                                 «DELEGATE_FIELD».«callback.name»((«cls») $1);
131                                 return null;
132                             }
133                         «ENDFOR»
134                         return null;
135                     }
136                 '''
137             ]
138         ]
139         val finalClass = targetCls.toClass(iface.classLoader,iface.protectionDomain)
140         return new RuntimeGeneratedInvokerPrototype(supportedNotification,
141             finalClass as Class<? extends org.opendaylight.controller.sal.binding.api.NotificationListener>);
142     }
143
144     def void method(CtClass it, Class<?> returnType, String name, Class<?> parameter, MethodGenerator function1) {
145         val method = new CtMethod(returnType.asCtClass, name, Arrays.asList(parameter.asCtClass), it);
146         function1.process(method);
147         it.addMethod(method);
148     }
149
150     private def routingContextInput(CtMethod method) {
151         val inputClass = method.parameterTypes.get(0);
152         return inputClass.contextInstance;
153     }
154
155     private def RoutingPair getContextInstance(CtClass dataClass) {
156         for (method : dataClass.methods) {
157             if (method.name.startsWith("get") && method.parameterTypes.size === 0) {
158                 for (annotation : method.availableAnnotations) {
159                     if (annotation instanceof RoutingContext) {
160                         val encapsulated = !method.returnType.equals(InstanceIdentifier.asCtClass);
161
162                         return new RoutingPair((annotation as RoutingContext).value, method, encapsulated);
163                     }
164                 }
165             }
166         }
167         for (iface : dataClass.interfaces) {
168             val ret = getContextInstance(iface);
169             if(ret != null) return ret;
170         }
171         return null;
172     }
173
174     private def void implementMethodsFrom(CtClass target, CtClass source, MethodGenerator function1) {
175         for (method : source.methods) {
176             if (method.declaringClass == source) {
177                 val redeclaredMethod = new CtMethod(method, target, null);
178                 function1.process(redeclaredMethod);
179                 target.addMethod(redeclaredMethod);
180             }
181         }
182     }
183
184     private def CtClass createClass(String fqn, ClassGenerator cls) {
185         val target = classPool.makeClass(fqn);
186         cls.process(target);
187         return target;
188     }
189
190     private def CtClass createClass(String fqn, CtClass superInterface, ClassGenerator cls) {
191         val target = classPool.makeClass(fqn);
192         target.implementsType(superInterface);
193         cls.process(target);
194         return target;
195     }
196
197     private def void implementsType(CtClass it, CtClass supertype) {
198         checkArgument(supertype.interface, "Supertype must be interface");
199         addInterface(supertype);
200     }
201
202     private def asCtClass(Class<?> class1) {
203         classPool.get(class1);
204     }
205
206     private def CtField field(CtClass it, String name, Class<?> returnValue) {
207         val field = new CtField(returnValue.asCtClass, name, it);
208         field.modifiers = Modifier.PUBLIC
209         addField(field);
210         return field;
211     }
212
213     def get(ClassPool pool, Class<?> cls) {
214         try {
215             return pool.get(cls.name)
216         } catch (NotFoundException e) {
217             pool.appendClassPath(new LoaderClassPath(cls.classLoader));
218             try {
219                 return pool.get(cls.name)
220
221             } catch (NotFoundException ef) {
222                 pool.appendClassPath(new ClassClassPath(cls));
223                 return pool.get(cls.name)
224             }
225         }
226     }
227
228     override getInvokerFactory() {
229         return this;
230     }
231
232     override invokerFor(NotificationListener instance) {
233         val cls = instance.class
234         val prototype = resolveInvokerClass(cls);
235         
236         return new RuntimeGeneratedInvoker(instance,prototype)
237     }
238
239     def resolveInvokerClass(Class<? extends NotificationListener> class1) {
240         val invoker = invokerClasses.get(class1);
241         if (invoker !== null) {
242             return invoker;
243         }
244         val newInvoker = generateListenerInvoker(class1);
245         invokerClasses.put(class1, newInvoker);
246         return newInvoker
247     }
248 }
249
250 @Data
251 class RuntimeGeneratedInvoker implements NotificationInvoker {
252     
253     @Property
254     val NotificationListener delegate;
255
256     
257     @Property
258     var org.opendaylight.controller.sal.binding.api.NotificationListener 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;
267         RuntimeCodeHelper.setDelegate(_invocationProxy, delegate);
268     }
269
270     override getSupportedNotifications() {
271         prototype.supportedNotifications;
272     }
273
274     override close() {
275         
276     }
277 }
278
279 @Data
280 class RuntimeGeneratedInvokerPrototype {
281
282     @Property
283     val Set<Class<? extends Notification>> supportedNotifications;
284
285     @Property
286     val Class<? extends org.opendaylight.controller.sal.binding.api.NotificationListener> protoClass;
287 }