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.codegen.RuntimeCodeHelper
17 import org.opendaylight.controller.sal.binding.spi.NotificationInvokerFactory
18 import org.opendaylight.controller.sal.binding.spi.NotificationInvokerFactory.NotificationInvoker
19 import org.opendaylight.yangtools.sal.binding.generator.util.JavassistUtils
20 import org.opendaylight.yangtools.yang.binding.DataContainer
21 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier
22 import org.opendaylight.yangtools.yang.binding.Notification
23 import org.opendaylight.yangtools.yang.binding.NotificationListener
24 import org.opendaylight.yangtools.yang.binding.RpcImplementation
25 import org.opendaylight.yangtools.yang.binding.RpcService
26 import org.opendaylight.yangtools.yang.binding.annotations.RoutingContext
27 import org.opendaylight.yangtools.yang.binding.util.ClassLoaderUtils
29 import static extension org.opendaylight.controller.sal.binding.codegen.RuntimeCodeSpecification.*
30 import static extension org.opendaylight.controller.sal.binding.codegen.YangtoolsMappingHelper.*
32 class RuntimeCodeGenerator implements org.opendaylight.controller.sal.binding.codegen.RuntimeCodeGenerator, NotificationInvokerFactory {
34 val CtClass BROKER_NOTIFICATION_LISTENER;
35 val extension JavassistUtils utils;
36 val Map<Class<? extends NotificationListener>, RuntimeGeneratedInvokerPrototype> invokerClasses;
40 utils = new JavassistUtils(pool);
41 invokerClasses = new WeakHashMap();
42 BROKER_NOTIFICATION_LISTENER = org.opendaylight.controller.sal.binding.api.NotificationListener.asCtClass;
43 pool.appendClassPath(new LoaderClassPath(RpcService.classLoader));
46 override <T extends RpcService> getDirectProxyFor(Class<T> iface) {
47 val T instance = ClassLoaderUtils.withClassLoaderAndLock(iface.classLoader,lock) [|
48 val proxyName = iface.directProxyName;
49 val potentialClass = ClassLoaderUtils.tryToLoadClassWithTCCL(proxyName)
50 if(potentialClass != null) {
51 return potentialClass.newInstance as T;
53 val supertype = iface.asCtClass
54 val createdCls = createClass(iface.directProxyName, supertype) [
55 field(DELEGATE_FIELD, iface);
56 implementsType(RpcImplementation.asCtClass)
57 implementMethodsFrom(supertype) [
60 if(«DELEGATE_FIELD» == null) {
61 throw new java.lang.IllegalStateException("No default provider is available");
63 return ($r) «DELEGATE_FIELD».«it.name»($$);
67 implementMethodsFrom(RpcImplementation.asCtClass) [
70 throw new java.lang.IllegalStateException("No provider is processing supplied message");
76 return createdCls.toClass(iface.classLoader).newInstance as T
81 override <T extends RpcService> getRouterFor(Class<T> iface,String routerInstanceName) {
82 val metadata = ClassLoaderUtils.withClassLoader(iface.classLoader) [|
83 val supertype = iface.asCtClass
84 return supertype.rpcMetadata;
87 val instance = ClassLoaderUtils.<T>withClassLoaderAndLock(iface.classLoader,lock) [ |
88 val supertype = iface.asCtClass
89 val routerName = iface.routerName;
90 val potentialClass = ClassLoaderUtils.tryToLoadClassWithTCCL(routerName)
91 if(potentialClass != null) {
92 return potentialClass.newInstance as T;
95 val targetCls = createClass(iface.routerName, supertype) [
98 field(DELEGATE_FIELD, iface)
99 //field(REMOTE_INVOKER_FIELD,iface);
100 implementsType(RpcImplementation.asCtClass)
102 for (ctx : metadata.contexts) {
103 field(ctx.routingTableField, Map)
105 implementMethodsFrom(supertype) [
106 if (parameterTypes.size === 1) {
107 val rpcMeta = metadata.rpcMethods.get(name);
110 final «InstanceIdentifier.name» identifier = $1.«rpcMeta.inputRouteGetter.name»()«IF rpcMeta.
111 routeEncapsulated».getValue()«ENDIF»;
112 «supertype.name» instance = («supertype.name») «rpcMeta.context.routingTableField».get(identifier);
113 if(instance == null) {
114 instance = «DELEGATE_FIELD»;
116 if(instance == null) {
117 throw new java.lang.IllegalStateException("No routable provider is processing routed message for " + String.valueOf(identifier));
119 return ($r) instance.«it.name»($$);
122 } else if (parameterTypes.size === 0) {
123 body = '''return ($r) «DELEGATE_FIELD».«it.name»($$);'''
126 implementMethodsFrom(RpcImplementation.asCtClass) [
129 throw new java.lang.IllegalStateException("No provider is processing supplied message");
135 return targetCls.toClass(iface.classLoader,iface.protectionDomain).newInstance as T
138 return new RpcRouterCodegenInstance(routerInstanceName,iface, instance, metadata.contexts,metadata.supportedInputs);
141 private def RpcServiceMetadata getRpcMetadata(CtClass iface) {
142 val metadata = new RpcServiceMetadata;
144 iface.methods.filter[declaringClass == iface && parameterTypes.size === 1].forEach [ method |
145 val routingPair = method.rpcMetadata;
146 if (routingPair !== null) {
147 metadata.contexts.add(routingPair.context)
148 metadata.rpcMethods.put(method.name,routingPair)
149 val input = routingPair.inputType.javaClass as Class<? extends DataContainer>;
150 metadata.supportedInputs.add(input);
151 metadata.rpcInputs.put(input,routingPair);
157 private def getRpcMetadata(CtMethod method) {
158 val inputClass = method.parameterTypes.get(0);
159 return inputClass.rpcMethodMetadata(inputClass,method.name);
162 private def RpcMetadata rpcMethodMetadata(CtClass dataClass, CtClass inputClass, String rpcMethod) {
163 for (method : dataClass.methods) {
164 if (method.name.startsWith("get") && method.parameterTypes.size === 0) {
165 for (annotation : method.availableAnnotations) {
166 if (annotation instanceof RoutingContext) {
167 val encapsulated = !method.returnType.equals(InstanceIdentifier.asCtClass);
168 return new RpcMetadata(rpcMethod,(annotation as RoutingContext).value, method, encapsulated,inputClass);
173 for (iface : dataClass.interfaces) {
174 val ret = rpcMethodMetadata(iface,inputClass,rpcMethod);
175 if(ret != null) return ret;
180 private def getJavaClass(CtClass cls) {
181 Thread.currentThread.contextClassLoader.loadClass(cls.name)
184 override getInvokerFactory() {
188 override invokerFor(NotificationListener instance) {
189 val cls = instance.class
190 val prototype = resolveInvokerClass(cls);
192 return new RuntimeGeneratedInvoker(instance, prototype)
195 protected def generateListenerInvoker(Class<? extends NotificationListener> iface) {
196 val callbacks = iface.methods.filter[notificationCallback]
198 val supportedNotification = callbacks.map[parameterTypes.get(0) as Class<? extends Notification>].toSet;
200 val targetCls = createClass(iface.invokerName, BROKER_NOTIFICATION_LISTENER) [
201 field(DELEGATE_FIELD, iface)
202 implementMethodsFrom(BROKER_NOTIFICATION_LISTENER) [
205 «FOR callback : callbacks SEPARATOR " else "»
206 «val cls = callback.parameterTypes.get(0).name»
207 if($1 instanceof «cls») {
208 «DELEGATE_FIELD».«callback.name»((«cls») $1);
217 val finalClass = targetCls.toClass(iface.classLoader, iface.protectionDomain)
218 return new RuntimeGeneratedInvokerPrototype(supportedNotification,
219 finalClass as Class<? extends org.opendaylight.controller.sal.binding.api.NotificationListener<?>>);
226 protected def resolveInvokerClass(Class<? extends NotificationListener> class1) {
227 return ClassLoaderUtils.<RuntimeGeneratedInvokerPrototype>withClassLoaderAndLock(class1.classLoader,lock) [|
228 val invoker = invokerClasses.get(class1);
229 if (invoker !== null) {
232 val newInvoker = generateListenerInvoker(class1);
233 invokerClasses.put(class1, newInvoker);
241 package class RuntimeGeneratedInvoker implements NotificationInvoker {
244 val NotificationListener delegate;
247 var org.opendaylight.controller.sal.binding.api.NotificationListener<Notification> invocationProxy;
250 var RuntimeGeneratedInvokerPrototype prototype;
252 new(NotificationListener delegate, RuntimeGeneratedInvokerPrototype prototype) {
253 _delegate = delegate;
254 _prototype = prototype;
255 _invocationProxy = prototype.protoClass.newInstance as org.opendaylight.controller.sal.binding.api.NotificationListener<Notification>;
256 RuntimeCodeHelper.setDelegate(_invocationProxy, delegate);
259 override getSupportedNotifications() {
260 prototype.supportedNotifications;