2 * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
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
8 package org.opendaylight.controller.sal.binding.codegen.impl
10 import javassist.ClassPool
11 import org.opendaylight.yangtools.yang.binding.RpcService
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
20 import java.util.HashMap
23 import org.opendaylight.yangtools.yang.binding.NotificationListener
24 import org.opendaylight.yangtools.yang.binding.Notification
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.yangtools.concepts.util.ClassLoaderUtils.*
31 import org.opendaylight.controller.sal.binding.spi.NotificationInvokerFactory
32 import org.opendaylight.controller.sal.binding.spi.NotificationInvokerFactory.NotificationInvoker
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 import org.opendaylight.controller.sal.binding.impl.util.ClassLoaderUtils
41 import javassist.LoaderClassPath
43 class RuntimeCodeGenerator implements org.opendaylight.controller.sal.binding.codegen.RuntimeCodeGenerator, NotificationInvokerFactory {
45 val CtClass BROKER_NOTIFICATION_LISTENER;
46 val ClassPool classPool;
47 val extension JavassistUtils utils;
48 val Map<Class<? extends NotificationListener>, RuntimeGeneratedInvokerPrototype> invokerClasses;
53 utils = new JavassistUtils(pool);
54 invokerClasses = new WeakHashMap();
55 BROKER_NOTIFICATION_LISTENER = org.opendaylight.controller.sal.binding.api.NotificationListener.asCtClass;
56 pool.appendClassPath(new LoaderClassPath(RpcService.classLoader));
59 override <T extends RpcService> getDirectProxyFor(Class<T> iface) {
60 val T instance = withClassLoaderAndLock(iface.classLoader,lock) [|
61 val proxyName = iface.directProxyName;
62 val potentialClass = ClassLoaderUtils.tryToLoadClassWithTCCL(proxyName)
63 if(potentialClass != null) {
64 return potentialClass.newInstance as T;
66 val supertype = iface.asCtClass
67 val createdCls = createClass(iface.directProxyName, supertype) [
68 field(DELEGATE_FIELD, iface);
69 implementsType(RpcImplementation.asCtClass)
70 implementMethodsFrom(supertype) [
73 if(«DELEGATE_FIELD» == null) {
74 throw new java.lang.IllegalStateException("No provider is processing supplied message");
76 return ($r) «DELEGATE_FIELD».«it.name»($$);
80 implementMethodsFrom(RpcImplementation.asCtClass) [
83 throw new java.lang.IllegalStateException("No provider is processing supplied message");
89 return createdCls.toClass(iface.classLoader).newInstance as T
94 override <T extends RpcService> getRouterFor(Class<T> iface) {
95 val metadata = withClassLoader(iface.classLoader) [|
96 val supertype = iface.asCtClass
97 return supertype.rpcMetadata;
100 val instance = <T>withClassLoaderAndLock(iface.classLoader,lock) [ |
101 val supertype = iface.asCtClass
102 val routerName = iface.routerName;
103 val potentialClass = ClassLoaderUtils.tryToLoadClassWithTCCL(routerName)
104 if(potentialClass != null) {
105 return potentialClass.newInstance as T;
108 val targetCls = createClass(iface.routerName, supertype) [
111 field(DELEGATE_FIELD, iface)
112 //field(REMOTE_INVOKER_FIELD,iface);
113 implementsType(RpcImplementation.asCtClass)
115 for (ctx : metadata.contexts) {
116 field(ctx.routingTableField, Map)
118 implementMethodsFrom(supertype) [
119 if (parameterTypes.size === 1) {
120 val rpcMeta = metadata.rpcMethods.get(name);
123 final «InstanceIdentifier.name» identifier = $1.«rpcMeta.inputRouteGetter.name»()«IF rpcMeta.
124 routeEncapsulated».getValue()«ENDIF»;
125 «supertype.name» instance = («supertype.name») «rpcMeta.context.routingTableField».get(identifier);
126 if(instance == null) {
127 instance = «DELEGATE_FIELD»;
129 if(instance == null) {
130 throw new java.lang.IllegalStateException("No provider is processing supplied message");
132 return ($r) instance.«it.name»($$);
135 } else if (parameterTypes.size === 0) {
136 body = '''return ($r) «DELEGATE_FIELD».«it.name»($$);'''
139 implementMethodsFrom(RpcImplementation.asCtClass) [
142 throw new java.lang.IllegalStateException("No provider is processing supplied message");
148 return targetCls.toClass(iface.classLoader,iface.protectionDomain).newInstance as T
151 return new RpcRouterCodegenInstance(iface, instance, metadata.contexts,metadata.supportedInputs);
154 private def RpcServiceMetadata getRpcMetadata(CtClass iface) {
155 val metadata = new RpcServiceMetadata;
157 iface.methods.filter[declaringClass == iface && parameterTypes.size === 1].forEach [ method |
158 val routingPair = method.rpcMetadata;
159 if (routingPair !== null) {
160 metadata.contexts.add(routingPair.context)
161 metadata.rpcMethods.put(method.name,routingPair)
162 val input = routingPair.inputType.javaClass as Class<? extends DataContainer>;
163 metadata.supportedInputs.add(input);
164 metadata.rpcInputs.put(input,routingPair);
170 private def getRpcMetadata(CtMethod method) {
171 val inputClass = method.parameterTypes.get(0);
172 return inputClass.rpcMethodMetadata(inputClass,method.name);
175 private def RpcMetadata rpcMethodMetadata(CtClass dataClass,CtClass inputClass,String rpcMethod) {
176 for (method : dataClass.methods) {
177 if (method.name.startsWith("get") && method.parameterTypes.size === 0) {
178 for (annotation : method.availableAnnotations) {
179 if (annotation instanceof RoutingContext) {
180 val encapsulated = !method.returnType.equals(InstanceIdentifier.asCtClass);
181 return new RpcMetadata(null,rpcMethod,(annotation as RoutingContext).value, method, encapsulated,inputClass);
186 for (iface : dataClass.interfaces) {
187 val ret = rpcMethodMetadata(iface,inputClass,rpcMethod);
188 if(ret != null) return ret;
193 private def getJavaClass(CtClass cls) {
194 Thread.currentThread.contextClassLoader.loadClass(cls.name)
197 override getInvokerFactory() {
201 override invokerFor(NotificationListener instance) {
202 val cls = instance.class
203 val prototype = resolveInvokerClass(cls);
205 return new RuntimeGeneratedInvoker(instance, prototype)
208 protected def generateListenerInvoker(Class<? extends NotificationListener> iface) {
209 val callbacks = iface.methods.filter[notificationCallback]
211 val supportedNotification = callbacks.map[parameterTypes.get(0) as Class<? extends Notification>].toSet;
213 val targetCls = createClass(iface.invokerName, BROKER_NOTIFICATION_LISTENER) [
214 field(DELEGATE_FIELD, iface)
215 implementMethodsFrom(BROKER_NOTIFICATION_LISTENER) [
218 «FOR callback : callbacks SEPARATOR " else "»
219 «val cls = callback.parameterTypes.get(0).name»
220 if($1 instanceof «cls») {
221 «DELEGATE_FIELD».«callback.name»((«cls») $1);
230 val finalClass = targetCls.toClass(iface.classLoader, iface.protectionDomain)
231 return new RuntimeGeneratedInvokerPrototype(supportedNotification,
232 finalClass as Class<? extends org.opendaylight.controller.sal.binding.api.NotificationListener<?>>);
239 protected def resolveInvokerClass(Class<? extends NotificationListener> class1) {
240 return <RuntimeGeneratedInvokerPrototype>withClassLoaderAndLock(class1.classLoader,lock) [|
241 val invoker = invokerClasses.get(class1);
242 if (invoker !== null) {
245 val newInvoker = generateListenerInvoker(class1);
246 invokerClasses.put(class1, newInvoker);
254 package class RuntimeGeneratedInvoker implements NotificationInvoker {
257 val NotificationListener delegate;
260 var org.opendaylight.controller.sal.binding.api.NotificationListener<Notification> invocationProxy;
263 var RuntimeGeneratedInvokerPrototype prototype;
265 new(NotificationListener delegate, RuntimeGeneratedInvokerPrototype prototype) {
266 _delegate = delegate;
267 _prototype = prototype;
268 _invocationProxy = prototype.protoClass.newInstance as org.opendaylight.controller.sal.binding.api.NotificationListener<Notification>;
269 RuntimeCodeHelper.setDelegate(_invocationProxy, delegate);
272 override getSupportedNotifications() {
273 prototype.supportedNotifications;
281 package class RuntimeGeneratedInvokerPrototype {
284 val Set<Class<? extends Notification>> supportedNotifications;
287 val Class<? extends org.opendaylight.controller.sal.binding.api.NotificationListener<?>> protoClass;
290 package class RpcServiceMetadata {
293 val contexts = new HashSet<Class<? extends BaseIdentity>>();
296 val rpcMethods = new HashMap<String, RpcMetadata>();
299 val rpcInputs = new HashMap<Class<? extends DataContainer>, RpcMetadata>();
303 val supportedInputs = new HashSet<Class<? extends DataContainer>>();
307 package class RpcMetadata {
313 val String methodName;
316 val Class<? extends BaseIdentity> context;
318 val CtMethod inputRouteGetter;
321 val boolean routeEncapsulated;
324 val CtClass inputType;