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
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.spi.NotificationInvokerFactory
17 import org.opendaylight.yangtools.sal.binding.generator.util.JavassistUtils
18 import org.opendaylight.yangtools.yang.binding.DataContainer
19 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier
20 import org.opendaylight.yangtools.yang.binding.Notification
21 import org.opendaylight.yangtools.yang.binding.NotificationListener
22 import org.opendaylight.yangtools.yang.binding.RpcImplementation
23 import org.opendaylight.yangtools.yang.binding.RpcService
24 import org.opendaylight.yangtools.yang.binding.annotations.RoutingContext
25 import org.opendaylight.yangtools.yang.binding.util.ClassLoaderUtils
27 import static extension org.opendaylight.controller.sal.binding.codegen.RuntimeCodeSpecification.*
28 import static extension org.opendaylight.controller.sal.binding.codegen.YangtoolsMappingHelper.*
30 class RuntimeCodeGenerator implements org.opendaylight.controller.sal.binding.codegen.RuntimeCodeGenerator, NotificationInvokerFactory {
32 val CtClass BROKER_NOTIFICATION_LISTENER;
33 val extension JavassistUtils utils;
34 val Map<Class<? extends NotificationListener>, RuntimeGeneratedInvokerPrototype> invokerClasses;
38 utils = new JavassistUtils(pool);
39 invokerClasses = new WeakHashMap();
40 BROKER_NOTIFICATION_LISTENER = org.opendaylight.controller.sal.binding.api.NotificationListener.asCtClass;
41 pool.appendClassPath(new LoaderClassPath(RpcService.classLoader));
44 override <T extends RpcService> getDirectProxyFor(Class<T> iface) {
45 val T instance = ClassLoaderUtils.withClassLoaderAndLock(iface.classLoader,lock) [|
46 val proxyName = iface.directProxyName;
47 val potentialClass = ClassLoaderUtils.tryToLoadClassWithTCCL(proxyName)
48 if(potentialClass != null) {
49 return potentialClass.newInstance as T;
51 val supertype = iface.asCtClass
52 val createdCls = createClass(iface.directProxyName, supertype) [
53 field(DELEGATE_FIELD, iface);
54 implementsType(RpcImplementation.asCtClass)
55 implementMethodsFrom(supertype) [
58 if(«DELEGATE_FIELD» == null) {
59 throw new java.lang.IllegalStateException("No default provider is available");
61 return ($r) «DELEGATE_FIELD».«it.name»($$);
65 implementMethodsFrom(RpcImplementation.asCtClass) [
68 throw new java.lang.IllegalStateException("No provider is processing supplied message");
74 return createdCls.toClass(iface.classLoader).newInstance as T
79 override <T extends RpcService> getRouterFor(Class<T> iface,String routerInstanceName) {
80 val metadata = ClassLoaderUtils.withClassLoader(iface.classLoader) [|
81 val supertype = iface.asCtClass
82 return supertype.rpcMetadata;
85 val instance = ClassLoaderUtils.<T>withClassLoaderAndLock(iface.classLoader,lock) [ |
86 val supertype = iface.asCtClass
87 val routerName = iface.routerName;
88 val potentialClass = ClassLoaderUtils.tryToLoadClassWithTCCL(routerName)
89 if(potentialClass != null) {
90 return potentialClass.newInstance as T;
93 val targetCls = createClass(iface.routerName, supertype) [
96 field(DELEGATE_FIELD, iface)
97 //field(REMOTE_INVOKER_FIELD,iface);
98 implementsType(RpcImplementation.asCtClass)
100 for (ctx : metadata.contexts) {
101 field(ctx.routingTableField, Map)
103 implementMethodsFrom(supertype) [
104 if (parameterTypes.size === 1) {
105 val rpcMeta = metadata.rpcMethods.get(name);
108 final «InstanceIdentifier.name» identifier = $1.«rpcMeta.inputRouteGetter.name»()«IF rpcMeta.
109 routeEncapsulated».getValue()«ENDIF»;
110 «supertype.name» instance = («supertype.name») «rpcMeta.context.routingTableField».get(identifier);
111 if(instance == null) {
112 instance = «DELEGATE_FIELD»;
114 if(instance == null) {
115 throw new java.lang.IllegalStateException("No routable provider is processing routed message for " + String.valueOf(identifier));
117 return ($r) instance.«it.name»($$);
120 } else if (parameterTypes.size === 0) {
121 body = '''return ($r) «DELEGATE_FIELD».«it.name»($$);'''
124 implementMethodsFrom(RpcImplementation.asCtClass) [
127 throw new java.lang.IllegalStateException("No provider is processing supplied message");
133 return targetCls.toClass(iface.classLoader,iface.protectionDomain).newInstance as T
136 return new RpcRouterCodegenInstance(routerInstanceName,iface, instance, metadata.contexts,metadata.supportedInputs);
139 private def RpcServiceMetadata getRpcMetadata(CtClass iface) {
140 val metadata = new RpcServiceMetadata;
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);
155 private def getRpcMetadata(CtMethod method) {
156 val inputClass = method.parameterTypes.get(0);
157 return inputClass.rpcMethodMetadata(inputClass,method.name);
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(rpcMethod,(annotation as RoutingContext).value, method, encapsulated,inputClass);
171 for (iface : dataClass.interfaces) {
172 val ret = rpcMethodMetadata(iface,inputClass,rpcMethod);
173 if(ret != null) return ret;
178 private def getJavaClass(CtClass cls) {
179 Thread.currentThread.contextClassLoader.loadClass(cls.name)
182 override getInvokerFactory() {
186 override invokerFor(NotificationListener instance) {
187 val cls = instance.class
188 val prototype = resolveInvokerClass(cls);
190 return RuntimeGeneratedInvoker.create(instance, prototype)
193 protected def generateListenerInvoker(Class<? extends NotificationListener> iface) {
194 val callbacks = iface.methods.filter[notificationCallback]
196 val supportedNotification = callbacks.map[parameterTypes.get(0) as Class<? extends Notification>].toSet;
198 val targetCls = createClass(iface.invokerName, BROKER_NOTIFICATION_LISTENER) [
199 field(DELEGATE_FIELD, iface)
200 implementMethodsFrom(BROKER_NOTIFICATION_LISTENER) [
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);
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<?>>);
220 protected def resolveInvokerClass(Class<? extends NotificationListener> class1) {
221 return ClassLoaderUtils.<RuntimeGeneratedInvokerPrototype>withClassLoaderAndLock(class1.classLoader,lock) [|
222 val invoker = invokerClasses.get(class1);
223 if (invoker !== null) {
226 val newInvoker = generateListenerInvoker(class1);
227 invokerClasses.put(class1, newInvoker);