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 static com.google.common.base.Preconditions.*
15 import javassist.CtMethod
16 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier
17 import org.opendaylight.yangtools.yang.binding.annotations.RoutingContext
18 import org.opendaylight.yangtools.yang.binding.BaseIdentity
21 import java.util.HashMap
24 import org.opendaylight.yangtools.yang.binding.NotificationListener
25 import org.opendaylight.yangtools.yang.binding.Notification
28 import static extension org.opendaylight.controller.sal.binding.codegen.YangtoolsMappingHelper.*
29 import static extension org.opendaylight.controller.sal.binding.codegen.RuntimeCodeSpecification.*
30 import java.util.HashSet
31 import static org.opendaylight.controller.sal.binding.impl.util.ClassLoaderUtils.*
32 import org.opendaylight.controller.sal.binding.spi.NotificationInvokerFactory
33 import org.opendaylight.controller.sal.binding.spi.NotificationInvokerFactory.NotificationInvoker
35 import org.opendaylight.controller.sal.binding.codegen.RuntimeCodeHelper
36 import java.util.WeakHashMap
37 import org.opendaylight.yangtools.yang.binding.annotations.QName
38 import org.opendaylight.yangtools.yang.binding.DataContainer
39 import org.opendaylight.yangtools.yang.binding.RpcImplementation
40 import org.opendaylight.controller.sal.binding.codegen.util.JavassistUtils
42 class RuntimeCodeGenerator implements org.opendaylight.controller.sal.binding.codegen.RuntimeCodeGenerator, NotificationInvokerFactory {
44 val CtClass BROKER_NOTIFICATION_LISTENER;
45 val ClassPool classPool;
46 val extension JavassistUtils utils;
47 val Map<Class<? extends NotificationListener>, RuntimeGeneratedInvokerPrototype> invokerClasses;
49 public new(ClassPool pool) {
51 utils = new JavassistUtils(pool);
52 invokerClasses = new WeakHashMap();
53 BROKER_NOTIFICATION_LISTENER = org.opendaylight.controller.sal.binding.api.NotificationListener.asCtClass;
56 override <T extends RpcService> getDirectProxyFor(Class<T> iface) {
57 val supertype = iface.asCtClass
58 val targetCls = createClass(iface.directProxyName, supertype) [
59 field(DELEGATE_FIELD, iface);
60 implementMethodsFrom(supertype) [
61 body = '''return ($r) «DELEGATE_FIELD».«it.name»($$);'''
64 return targetCls.toClass(iface.classLoader).newInstance as T
67 override <T extends RpcService> getRouterFor(Class<T> iface) {
68 val contexts = new HashSet<Class<? extends BaseIdentity>>
70 val instance = <RpcRouterCodegenInstance<T>>withClassLoader(iface.classLoader) [ |
71 val supertype = iface.asCtClass
72 val metadata = supertype.rpcMetadata;
73 val targetCls = createClass(iface.routerName, supertype) [
74 addInterface(RpcImplementation.asCtClass)
76 field(DELEGATE_FIELD, iface)
77 //field(REMOTE_INVOKER_FIELD,iface);
79 for (ctx : metadata.contexts) {
80 field(ctx.routingTableField, Map)
82 implementMethodsFrom(supertype) [
83 if (parameterTypes.size === 1) {
84 val rpcMeta = metadata.rpcMethods.get(name);
87 final «InstanceIdentifier.name» identifier = $1.«rpcMeta.inputRouteGetter.name»()«IF rpcMeta.
88 routeEncapsulated».getValue()«ENDIF»;
89 «supertype.name» instance = («supertype.name») «rpcMeta.context.routingTableField».get(identifier);
90 if(instance == null) {
91 instance = «DELEGATE_FIELD»;
93 if(instance == null) {
94 throw new java.lang.IllegalStateException("No provider is processing supplied message");
96 return ($r) instance.«it.name»($$);
99 } else if (parameterTypes.size === 0) {
100 body = '''return ($r) «DELEGATE_FIELD».«it.name»($$);'''
103 implementMethodsFrom(RpcImplementation.asCtClass) [
105 case "getSupportedInputs":
108 throw new java.lang.UnsupportedOperationException("Not implemented yet");
114 «FOR input : metadata.supportedInputs SEPARATOR " else "»
115 «val rpcMetadata = metadata.rpcInputs.get(input)»
116 if(«input.name».class.equals($1)) {
117 return ($r) this.«rpcMetadata.methodName»((«input.name») $2);
120 throw new java.lang.IllegalArgumentException("Not supported message type");
129 val instance = targetCls.toClass(iface.classLoader,iface.protectionDomain).newInstance as T
130 return new RpcRouterCodegenInstance(iface, instance, metadata.contexts,metadata.supportedInputs);
135 private def RpcServiceMetadata getRpcMetadata(CtClass iface) {
136 val metadata = new RpcServiceMetadata;
138 iface.methods.filter[declaringClass == iface && parameterTypes.size === 1].forEach [ method |
139 val routingPair = method.rpcMetadata;
140 if (routingPair !== null) {
141 metadata.contexts.add(routingPair.context)
142 metadata.rpcMethods.put(method.name,routingPair)
143 val input = routingPair.inputType.javaClass as Class<? extends DataContainer>;
144 metadata.supportedInputs.add(input);
145 metadata.rpcInputs.put(input,routingPair);
151 private def getRpcMetadata(CtMethod method) {
152 val inputClass = method.parameterTypes.get(0);
153 return inputClass.rpcMethodMetadata(inputClass,method.name);
156 private def RpcMetadata rpcMethodMetadata(CtClass dataClass,CtClass inputClass,String rpcMethod) {
157 for (method : dataClass.methods) {
158 if (method.name.startsWith("get") && method.parameterTypes.size === 0) {
159 for (annotation : method.availableAnnotations) {
160 if (annotation instanceof RoutingContext) {
161 val encapsulated = !method.returnType.equals(InstanceIdentifier.asCtClass);
162 return new RpcMetadata(null,rpcMethod,(annotation as RoutingContext).value, method, encapsulated,inputClass);
167 for (iface : dataClass.interfaces) {
168 val ret = rpcMethodMetadata(iface,inputClass,rpcMethod);
169 if(ret != null) return ret;
174 private def getJavaClass(CtClass cls) {
175 Thread.currentThread.contextClassLoader.loadClass(cls.name)
178 override getInvokerFactory() {
182 override invokerFor(NotificationListener instance) {
183 val cls = instance.class
184 val prototype = resolveInvokerClass(cls);
186 return new RuntimeGeneratedInvoker(instance, prototype)
189 protected def generateListenerInvoker(Class<? extends NotificationListener> iface) {
190 val callbacks = iface.methods.filter[notificationCallback]
192 val supportedNotification = callbacks.map[parameterTypes.get(0) as Class<? extends Notification>].toSet;
194 val targetCls = createClass(iface.invokerName, BROKER_NOTIFICATION_LISTENER) [
195 field(DELEGATE_FIELD, iface)
196 implementMethodsFrom(BROKER_NOTIFICATION_LISTENER) [
199 «FOR callback : callbacks SEPARATOR " else "»
200 «val cls = callback.parameterTypes.get(0).name»
201 if($1 instanceof «cls») {
202 «DELEGATE_FIELD».«callback.name»((«cls») $1);
211 val finalClass = targetCls.toClass(iface.classLoader, iface.protectionDomain)
212 return new RuntimeGeneratedInvokerPrototype(supportedNotification,
213 finalClass as Class<? extends org.opendaylight.controller.sal.binding.api.NotificationListener>);
220 protected def resolveInvokerClass(Class<? extends NotificationListener> class1) {
221 val invoker = invokerClasses.get(class1);
222 if (invoker !== null) {
225 val newInvoker = generateListenerInvoker(class1);
226 invokerClasses.put(class1, newInvoker);
232 package class RuntimeGeneratedInvoker implements NotificationInvoker {
235 val NotificationListener delegate;
238 var org.opendaylight.controller.sal.binding.api.NotificationListener invocationProxy;
241 var RuntimeGeneratedInvokerPrototype prototype;
243 new(NotificationListener delegate, RuntimeGeneratedInvokerPrototype prototype) {
244 _delegate = delegate;
245 _prototype = prototype;
246 _invocationProxy = prototype.protoClass.newInstance;
247 RuntimeCodeHelper.setDelegate(_invocationProxy, delegate);
250 override getSupportedNotifications() {
251 prototype.supportedNotifications;
259 package class RuntimeGeneratedInvokerPrototype {
262 val Set<Class<? extends Notification>> supportedNotifications;
265 val Class<? extends org.opendaylight.controller.sal.binding.api.NotificationListener> protoClass;
268 package class RpcServiceMetadata {
271 val contexts = new HashSet<Class<? extends BaseIdentity>>();
274 val rpcMethods = new HashMap<String, RpcMetadata>();
277 val rpcInputs = new HashMap<Class<? extends DataContainer>, RpcMetadata>();
281 val supportedInputs = new HashSet<Class<? extends DataContainer>>();
285 package class RpcMetadata {
291 val String methodName;
294 val Class<? extends BaseIdentity> context;
296 val CtMethod inputRouteGetter;
299 val boolean routeEncapsulated;
302 val CtClass inputType;