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 java.util.HashMap
11 import java.util.HashSet
13 import java.util.WeakHashMap
14 import javassist.ClassPool
15 import javassist.CtClass
16 import javassist.CtMethod
17 import javassist.LoaderClassPath
18 import org.opendaylight.controller.sal.binding.codegen.RuntimeCodeHelper
19 import org.opendaylight.controller.sal.binding.spi.NotificationInvokerFactory
20 import org.opendaylight.controller.sal.binding.spi.NotificationInvokerFactory.NotificationInvoker
21 import org.opendaylight.yangtools.sal.binding.generator.util.ClassLoaderUtils
22 import org.opendaylight.yangtools.sal.binding.generator.util.JavassistUtils
23 import org.opendaylight.yangtools.yang.binding.BaseIdentity
24 import org.opendaylight.yangtools.yang.binding.DataContainer
25 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier
26 import org.opendaylight.yangtools.yang.binding.Notification
27 import org.opendaylight.yangtools.yang.binding.NotificationListener
28 import org.opendaylight.yangtools.yang.binding.RpcImplementation
29 import org.opendaylight.yangtools.yang.binding.RpcService
30 import org.opendaylight.yangtools.yang.binding.annotations.QName
31 import org.opendaylight.yangtools.yang.binding.annotations.RoutingContext
33 import static org.opendaylight.yangtools.concepts.util.ClassLoaderUtils.*
35 import static extension org.opendaylight.controller.sal.binding.codegen.RuntimeCodeSpecification.*
36 import static extension org.opendaylight.controller.sal.binding.codegen.YangtoolsMappingHelper.*
38 class RuntimeCodeGenerator implements org.opendaylight.controller.sal.binding.codegen.RuntimeCodeGenerator, NotificationInvokerFactory {
40 val CtClass BROKER_NOTIFICATION_LISTENER;
41 val extension JavassistUtils utils;
42 val Map<Class<? extends NotificationListener>, RuntimeGeneratedInvokerPrototype> invokerClasses;
46 utils = new JavassistUtils(pool);
47 invokerClasses = new WeakHashMap();
48 BROKER_NOTIFICATION_LISTENER = org.opendaylight.controller.sal.binding.api.NotificationListener.asCtClass;
49 pool.appendClassPath(new LoaderClassPath(RpcService.classLoader));
52 override <T extends RpcService> getDirectProxyFor(Class<T> iface) {
53 val T instance = withClassLoaderAndLock(iface.classLoader,lock) [|
54 val proxyName = iface.directProxyName;
55 val potentialClass = ClassLoaderUtils.tryToLoadClassWithTCCL(proxyName)
56 if(potentialClass != null) {
57 return potentialClass.newInstance as T;
59 val supertype = iface.asCtClass
60 val createdCls = createClass(iface.directProxyName, supertype) [
61 field(DELEGATE_FIELD, iface);
62 implementsType(RpcImplementation.asCtClass)
63 implementMethodsFrom(supertype) [
66 if(«DELEGATE_FIELD» == null) {
67 throw new java.lang.IllegalStateException("No default provider is available");
69 return ($r) «DELEGATE_FIELD».«it.name»($$);
73 implementMethodsFrom(RpcImplementation.asCtClass) [
76 throw new java.lang.IllegalStateException("No provider is processing supplied message");
82 return createdCls.toClass(iface.classLoader).newInstance as T
87 override <T extends RpcService> getRouterFor(Class<T> iface,String routerInstanceName) {
88 val metadata = withClassLoader(iface.classLoader) [|
89 val supertype = iface.asCtClass
90 return supertype.rpcMetadata;
93 val instance = <T>withClassLoaderAndLock(iface.classLoader,lock) [ |
94 val supertype = iface.asCtClass
95 val routerName = iface.routerName;
96 val potentialClass = ClassLoaderUtils.tryToLoadClassWithTCCL(routerName)
97 if(potentialClass != null) {
98 return potentialClass.newInstance as T;
101 val targetCls = createClass(iface.routerName, supertype) [
104 field(DELEGATE_FIELD, iface)
105 //field(REMOTE_INVOKER_FIELD,iface);
106 implementsType(RpcImplementation.asCtClass)
108 for (ctx : metadata.contexts) {
109 field(ctx.routingTableField, Map)
111 implementMethodsFrom(supertype) [
112 if (parameterTypes.size === 1) {
113 val rpcMeta = metadata.rpcMethods.get(name);
116 final «InstanceIdentifier.name» identifier = $1.«rpcMeta.inputRouteGetter.name»()«IF rpcMeta.
117 routeEncapsulated».getValue()«ENDIF»;
118 «supertype.name» instance = («supertype.name») «rpcMeta.context.routingTableField».get(identifier);
119 if(instance == null) {
120 instance = «DELEGATE_FIELD»;
122 if(instance == null) {
123 throw new java.lang.IllegalStateException("No routable provider is processing routed message for " + String.valueOf(identifier));
125 return ($r) instance.«it.name»($$);
128 } else if (parameterTypes.size === 0) {
129 body = '''return ($r) «DELEGATE_FIELD».«it.name»($$);'''
132 implementMethodsFrom(RpcImplementation.asCtClass) [
135 throw new java.lang.IllegalStateException("No provider is processing supplied message");
141 return targetCls.toClass(iface.classLoader,iface.protectionDomain).newInstance as T
144 return new RpcRouterCodegenInstance(routerInstanceName,iface, instance, metadata.contexts,metadata.supportedInputs);
147 private def RpcServiceMetadata getRpcMetadata(CtClass iface) {
148 val metadata = new RpcServiceMetadata;
150 iface.methods.filter[declaringClass == iface && parameterTypes.size === 1].forEach [ method |
151 val routingPair = method.rpcMetadata;
152 if (routingPair !== null) {
153 metadata.contexts.add(routingPair.context)
154 metadata.rpcMethods.put(method.name,routingPair)
155 val input = routingPair.inputType.javaClass as Class<? extends DataContainer>;
156 metadata.supportedInputs.add(input);
157 metadata.rpcInputs.put(input,routingPair);
163 private def getRpcMetadata(CtMethod method) {
164 val inputClass = method.parameterTypes.get(0);
165 return inputClass.rpcMethodMetadata(inputClass,method.name);
168 private def RpcMetadata rpcMethodMetadata(CtClass dataClass,CtClass inputClass,String rpcMethod) {
169 for (method : dataClass.methods) {
170 if (method.name.startsWith("get") && method.parameterTypes.size === 0) {
171 for (annotation : method.availableAnnotations) {
172 if (annotation instanceof RoutingContext) {
173 val encapsulated = !method.returnType.equals(InstanceIdentifier.asCtClass);
174 return new RpcMetadata(null,rpcMethod,(annotation as RoutingContext).value, method, encapsulated,inputClass);
179 for (iface : dataClass.interfaces) {
180 val ret = rpcMethodMetadata(iface,inputClass,rpcMethod);
181 if(ret != null) return ret;
186 private def getJavaClass(CtClass cls) {
187 Thread.currentThread.contextClassLoader.loadClass(cls.name)
190 override getInvokerFactory() {
194 override invokerFor(NotificationListener instance) {
195 val cls = instance.class
196 val prototype = resolveInvokerClass(cls);
198 return new RuntimeGeneratedInvoker(instance, prototype)
201 protected def generateListenerInvoker(Class<? extends NotificationListener> iface) {
202 val callbacks = iface.methods.filter[notificationCallback]
204 val supportedNotification = callbacks.map[parameterTypes.get(0) as Class<? extends Notification>].toSet;
206 val targetCls = createClass(iface.invokerName, BROKER_NOTIFICATION_LISTENER) [
207 field(DELEGATE_FIELD, iface)
208 implementMethodsFrom(BROKER_NOTIFICATION_LISTENER) [
211 «FOR callback : callbacks SEPARATOR " else "»
212 «val cls = callback.parameterTypes.get(0).name»
213 if($1 instanceof «cls») {
214 «DELEGATE_FIELD».«callback.name»((«cls») $1);
223 val finalClass = targetCls.toClass(iface.classLoader, iface.protectionDomain)
224 return new RuntimeGeneratedInvokerPrototype(supportedNotification,
225 finalClass as Class<? extends org.opendaylight.controller.sal.binding.api.NotificationListener<?>>);
232 protected def resolveInvokerClass(Class<? extends NotificationListener> class1) {
233 return <RuntimeGeneratedInvokerPrototype>withClassLoaderAndLock(class1.classLoader,lock) [|
234 val invoker = invokerClasses.get(class1);
235 if (invoker !== null) {
238 val newInvoker = generateListenerInvoker(class1);
239 invokerClasses.put(class1, newInvoker);
247 package class RuntimeGeneratedInvoker implements NotificationInvoker {
250 val NotificationListener delegate;
253 var org.opendaylight.controller.sal.binding.api.NotificationListener<Notification> invocationProxy;
256 var RuntimeGeneratedInvokerPrototype prototype;
258 new(NotificationListener delegate, RuntimeGeneratedInvokerPrototype prototype) {
259 _delegate = delegate;
260 _prototype = prototype;
261 _invocationProxy = prototype.protoClass.newInstance as org.opendaylight.controller.sal.binding.api.NotificationListener<Notification>;
262 RuntimeCodeHelper.setDelegate(_invocationProxy, delegate);
265 override getSupportedNotifications() {
266 prototype.supportedNotifications;
273 package class RpcServiceMetadata {
276 val contexts = new HashSet<Class<? extends BaseIdentity>>();
279 val rpcMethods = new HashMap<String, RpcMetadata>();
282 val rpcInputs = new HashMap<Class<? extends DataContainer>, RpcMetadata>();
286 val supportedInputs = new HashSet<Class<? extends DataContainer>>();
290 package class RpcMetadata {
296 val String methodName;
299 val Class<? extends BaseIdentity> context;
301 val CtMethod inputRouteGetter;
304 val boolean routeEncapsulated;
307 val CtClass inputType;