--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.binding.codegen.impl;
+
+import java.util.Map;
+import java.util.WeakHashMap;
+
+import javassist.ClassPool;
+import javassist.CtClass;
+import javassist.CtMethod;
+import javassist.NotFoundException;
+
+import javax.annotation.concurrent.GuardedBy;
+
+import org.eclipse.xtext.xbase.lib.Extension;
+import org.opendaylight.controller.sal.binding.api.rpc.RpcRouter;
+import org.opendaylight.controller.sal.binding.spi.NotificationInvokerFactory;
+import org.opendaylight.yangtools.sal.binding.generator.util.JavassistUtils;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.binding.NotificationListener;
+import org.opendaylight.yangtools.yang.binding.RpcService;
+import org.opendaylight.yangtools.yang.binding.annotations.RoutingContext;
+import org.opendaylight.yangtools.yang.binding.util.ClassLoaderUtils;
+
+import com.google.common.base.Supplier;
+
+abstract class AbstractRuntimeCodeGenerator implements org.opendaylight.controller.sal.binding.codegen.RuntimeCodeGenerator, NotificationInvokerFactory {
+ @GuardedBy("this")
+ private final Map<Class<? extends NotificationListener>, RuntimeGeneratedInvokerPrototype> invokerClasses = new WeakHashMap<>();
+ private final CtClass brokerNotificationListener;
+
+ @Extension
+ protected final JavassistUtils utils;
+
+ protected AbstractRuntimeCodeGenerator(final ClassPool pool) {
+ utils = JavassistUtils.forClassPool(pool);
+
+ /*
+ * Make sure Javassist ClassPool sees the classloader of RpcService
+ */
+ utils.ensureClassLoader(RpcService.class);
+
+ brokerNotificationListener = utils.asCtClass(org.opendaylight.controller.sal.binding.api.NotificationListener.class);
+ }
+
+ protected final CtClass getBrokerNotificationListener() {
+ return brokerNotificationListener;
+ }
+
+ protected abstract RuntimeGeneratedInvokerPrototype generateListenerInvoker(Class<? extends NotificationListener> cls);
+ protected abstract <T extends RpcService> Supplier<T> directProxySupplier(final Class<T> serviceType);
+ protected abstract <T extends RpcService> Supplier<T> routerSupplier(final Class<T> serviceType, RpcServiceMetadata metadata);
+
+ private RpcServiceMetadata getRpcMetadata(final CtClass iface) throws ClassNotFoundException, NotFoundException {
+ final RpcServiceMetadata metadata = new RpcServiceMetadata();
+
+ for (CtMethod method : iface.getMethods()) {
+ if (iface.equals(method.getDeclaringClass()) && method.getParameterTypes().length == 1) {
+ final RpcMetadata routingPair = getRpcMetadata(method);
+ if (routingPair != null) {
+ metadata.addContext(routingPair.getContext());
+ metadata.addRpcMethod(method.getName(), routingPair);
+
+ /*
+ * Force-load the RPC class representing the "input" of this RPC.
+ *
+ * FIXME: this is pre-existing side-effect of the original code, which
+ * kept a reference to the loaded class, but it did not use it.
+ *
+ * There was no explanation as to why forcing this load was
+ * necessary. As far as I can tell now is that it forces the
+ * resolution of method arguments, which would (according to
+ * my reading of JLS) occur only when the method is invoked via
+ * binding-aware class action, not when coming from
+ * binding-independent world. Whether that makes sense or not,
+ * remains to be investigated.
+ */
+ Thread.currentThread().getContextClassLoader().loadClass(routingPair.getInputType().getName());
+ }
+ }
+ }
+
+ return metadata;
+ }
+
+ private RpcMetadata getRpcMetadata(final CtMethod method) throws NotFoundException {
+ final CtClass inputClass = method.getParameterTypes()[0];
+ return rpcMethodMetadata(inputClass, inputClass, method.getName());
+ }
+
+ private RpcMetadata rpcMethodMetadata(final CtClass dataClass, final CtClass inputClass, final String rpcMethod) throws NotFoundException {
+ for (CtMethod method : dataClass.getMethods()) {
+ if (method.getName().startsWith("get") && method.getParameterTypes().length == 0) {
+ for (Object annotation : method.getAvailableAnnotations()) {
+ if (annotation instanceof RoutingContext) {
+ boolean encapsulated = !method.getReturnType().equals(utils.asCtClass(InstanceIdentifier.class));
+ return new RpcMetadata(rpcMethod, ((RoutingContext)annotation).value(), method, encapsulated, inputClass);
+ }
+ }
+ }
+ }
+
+ for (CtClass iface : dataClass.getInterfaces()) {
+ final RpcMetadata ret = rpcMethodMetadata(iface, inputClass, rpcMethod);
+ if(ret != null) {
+ return ret;
+ }
+ }
+ return null;
+ }
+
+ private synchronized RuntimeGeneratedInvokerPrototype resolveInvokerClass(final Class<? extends NotificationListener> cls) {
+ RuntimeGeneratedInvokerPrototype invoker = invokerClasses.get(cls);
+ if (invoker != null) {
+ return invoker;
+ }
+
+ utils.getLock().lock();
+ try {
+ invoker = ClassLoaderUtils.withClassLoader(cls.getClassLoader(), new Supplier<RuntimeGeneratedInvokerPrototype>() {
+ @Override
+ public RuntimeGeneratedInvokerPrototype get() {
+ return generateListenerInvoker(cls);
+ }
+ });
+
+ invokerClasses.put(cls, invoker);
+ return invoker;
+ } finally {
+ utils.getLock().unlock();
+ }
+ }
+
+ @Override
+ public final NotificationInvokerFactory getInvokerFactory() {
+ return this;
+ }
+
+ @Override
+ public final <T extends RpcService> T getDirectProxyFor(final Class<T> serviceType) {
+ utils.getLock().lock();
+ try {
+ return ClassLoaderUtils.withClassLoader(serviceType.getClassLoader(), directProxySupplier(serviceType));
+ } finally {
+ utils.getLock().unlock();
+ }
+ }
+
+ @Override
+ public final <T extends RpcService> RpcRouter<T> getRouterFor(final Class<T> serviceType, final String name) {
+ final RpcServiceMetadata metadata = ClassLoaderUtils.withClassLoader(serviceType.getClassLoader(), new Supplier<RpcServiceMetadata>() {
+ @Override
+ public RpcServiceMetadata get() {
+ try {
+ return getRpcMetadata(utils.asCtClass(serviceType));
+ } catch (ClassNotFoundException | NotFoundException e) {
+ throw new IllegalStateException(String.format("Failed to load metadata for class {}", serviceType), e);
+ }
+ }
+ });
+
+ utils.getLock().lock();
+ try {
+ final T instance = ClassLoaderUtils.withClassLoader(serviceType.getClassLoader(), routerSupplier(serviceType, metadata));
+ return new RpcRouterCodegenInstance<T>(name, serviceType, instance, metadata.getContexts());
+ } finally {
+ utils.getLock().unlock();
+ }
+ }
+
+ @Override
+ public NotificationInvoker invokerFor(final NotificationListener instance) {
+ final Class<? extends NotificationListener> cls = instance.getClass();
+ final RuntimeGeneratedInvokerPrototype prototype = resolveInvokerClass(cls);
+
+ try {
+ return RuntimeGeneratedInvoker.create(instance, prototype);
+ } catch (InstantiationException | IllegalAccessException e) {
+ throw new IllegalStateException(String.format("Failed to create invoker for %s", instance), e);
+ }
+ }
+}
import org.opendaylight.yangtools.concepts.ListenerRegistration;
import org.opendaylight.yangtools.concepts.util.ListenerRegistry;
import org.opendaylight.yangtools.yang.binding.BaseIdentity;
-import org.opendaylight.yangtools.yang.binding.DataContainer;
import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
import org.opendaylight.yangtools.yang.binding.RpcService;
import org.slf4j.Logger;
import com.google.common.collect.ImmutableSet;
public class RpcRouterCodegenInstance<T extends RpcService> implements //
- RpcRouter<T>, RouteChangeListener<Class<? extends BaseIdentity>, InstanceIdentifier<?>> {
+RpcRouter<T>, RouteChangeListener<Class<? extends BaseIdentity>, InstanceIdentifier<?>> {
private static final Logger LOG = LoggerFactory.getLogger(RpcRouterCodegenInstance.class);
private final String name;
@SuppressWarnings("unchecked")
- public RpcRouterCodegenInstance(String name,Class<T> type, T routerImpl, Set<Class<? extends BaseIdentity>> contexts,
- Set<Class<? extends DataContainer>> inputs) {
+ public RpcRouterCodegenInstance(final String name,final Class<T> type, final T routerImpl, final Iterable<Class<? extends BaseIdentity>> contexts) {
this.name = name;
this.listeners = ListenerRegistry.create();
this.serviceType = type;
@Override
@SuppressWarnings("unchecked")
- public <C extends BaseIdentity> RpcRoutingTable<C, T> getRoutingTable(Class<C> routeContext) {
+ public <C extends BaseIdentity> RpcRoutingTable<C, T> getRoutingTable(final Class<C> routeContext) {
return (RpcRoutingTable<C, T>) routingTables.get(routeContext);
}
@Override
public <L extends RouteChangeListener<Class<? extends BaseIdentity>, InstanceIdentifier<?>>> ListenerRegistration<L> registerRouteChangeListener(
- L listener) {
+ final L listener) {
return listeners.registerWithType(listener);
}
@Override
- public void onRouteChange(RouteChange<Class<? extends BaseIdentity>, InstanceIdentifier<?>> change) {
+ public void onRouteChange(final RouteChange<Class<? extends BaseIdentity>, InstanceIdentifier<?>> change) {
for (ListenerRegistration<RouteChangeListener<Class<? extends BaseIdentity>, InstanceIdentifier<?>>> listener : listeners) {
try {
listener.getInstance().onRouteChange(change);
}
@Override
- public T getService(Class<? extends BaseIdentity> context, InstanceIdentifier<?> path) {
+ public T getService(final Class<? extends BaseIdentity> context, final InstanceIdentifier<?> path) {
return routingTables.get(context).getRoute(path);
}
@Override
- public RoutedRpcRegistration<T> addRoutedRpcImplementation(T service) {
+ public RoutedRpcRegistration<T> addRoutedRpcImplementation(final T service) {
return new RoutedRpcRegistrationImpl(service);
}
@Override
- public RpcRegistration<T> registerDefaultService(T service) {
+ public RpcRegistration<T> registerDefaultService(final T service) {
// TODO Auto-generated method stub
RuntimeCodeHelper.setDelegate(invocationProxy, service);
return null;
private class RoutedRpcRegistrationImpl extends AbstractObjectRegistration<T> implements RoutedRpcRegistration<T> {
- public RoutedRpcRegistrationImpl(T instance) {
+ public RoutedRpcRegistrationImpl(final T instance) {
super(instance);
}
}
@Override
- public void registerPath(Class<? extends BaseIdentity> context, InstanceIdentifier<?> path) {
+ public void registerPath(final Class<? extends BaseIdentity> context, final InstanceIdentifier<?> path) {
routingTables.get(context).updateRoute(path, getInstance());
}
@Override
- public void unregisterPath(Class<? extends BaseIdentity> context, InstanceIdentifier<?> path) {
+ public void unregisterPath(final Class<? extends BaseIdentity> context, final InstanceIdentifier<?> path) {
routingTables.get(context).removeRoute(path, getInstance());
}
@Override
- public void registerInstance(Class<? extends BaseIdentity> context, InstanceIdentifier<?> instance) {
+ public void registerInstance(final Class<? extends BaseIdentity> context, final InstanceIdentifier<?> instance) {
registerPath(context, instance);
}
@Override
- public void unregisterInstance(Class<? extends BaseIdentity> context, InstanceIdentifier<?> instance) {
+ public void unregisterInstance(final Class<? extends BaseIdentity> context, final InstanceIdentifier<?> instance) {
unregisterPath(context, instance);
}
import java.util.HashMap;
import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
import org.opendaylight.yangtools.yang.binding.BaseIdentity;
-import org.opendaylight.yangtools.yang.binding.DataContainer;
+
+import com.google.common.collect.Iterables;
final class RpcServiceMetadata {
- private final HashMap<Class<? extends DataContainer>, RpcMetadata> rpcInputs = new HashMap<>();
- private final HashSet<Class<? extends DataContainer>> supportedInputs = new HashSet<>();
- private final HashSet<Class<? extends BaseIdentity>> contexts = new HashSet<>();
- private final HashMap<String, RpcMetadata> rpcMethods = new HashMap<>();
+ private final Set<Class<? extends BaseIdentity>> contexts = new HashSet<>();
+ private final Map<String, RpcMetadata> rpcMethods = new HashMap<>();
+ private final Iterable<Class<? extends BaseIdentity>> roContexts = Iterables.unmodifiableIterable(contexts);
- public HashSet<Class<? extends BaseIdentity>> getContexts() {
- return this.contexts;
+ public Iterable<Class<? extends BaseIdentity>> getContexts() {
+ return roContexts;
}
- public HashMap<String, RpcMetadata> getRpcMethods() {
- return this.rpcMethods;
+ public RpcMetadata getRpcMethod(final String name) {
+ return rpcMethods.get(name);
}
- public HashMap<Class<? extends DataContainer>, RpcMetadata> getRpcInputs() {
- return this.rpcInputs;
+ public void addContext(final Class<? extends BaseIdentity> context) {
+ contexts.add(context);
}
- public HashSet<Class<? extends DataContainer>> getSupportedInputs() {
- return this.supportedInputs;
+ public void addRpcMethod(final String name, final RpcMetadata routingPair) {
+ rpcMethods.put(name, routingPair);
}
}
package org.opendaylight.controller.sal.binding.codegen.impl
import java.util.Map
-import java.util.WeakHashMap
import javassist.ClassPool
-import javassist.CtClass
-import javassist.CtMethod
-import javassist.LoaderClassPath
-import org.opendaylight.controller.sal.binding.spi.NotificationInvokerFactory
-import org.opendaylight.yangtools.sal.binding.generator.util.JavassistUtils
-import org.opendaylight.yangtools.yang.binding.DataContainer
import org.opendaylight.yangtools.yang.binding.InstanceIdentifier
import org.opendaylight.yangtools.yang.binding.Notification
-import org.opendaylight.yangtools.yang.binding.NotificationListener
import org.opendaylight.yangtools.yang.binding.RpcImplementation
-import org.opendaylight.yangtools.yang.binding.RpcService
-import org.opendaylight.yangtools.yang.binding.annotations.RoutingContext
+import org.opendaylight.controller.sal.binding.codegen.YangtoolsMappingHelper
import org.opendaylight.yangtools.yang.binding.util.ClassLoaderUtils
import static extension org.opendaylight.controller.sal.binding.codegen.RuntimeCodeSpecification.*
-import static extension org.opendaylight.controller.sal.binding.codegen.YangtoolsMappingHelper.*
-
-class RuntimeCodeGenerator implements org.opendaylight.controller.sal.binding.codegen.RuntimeCodeGenerator, NotificationInvokerFactory {
-
- val CtClass BROKER_NOTIFICATION_LISTENER;
- val extension JavassistUtils utils;
- val Map<Class<? extends NotificationListener>, RuntimeGeneratedInvokerPrototype> invokerClasses;
+class RuntimeCodeGenerator extends AbstractRuntimeCodeGenerator {
new(ClassPool pool) {
- utils = new JavassistUtils(pool);
- invokerClasses = new WeakHashMap();
- BROKER_NOTIFICATION_LISTENER = org.opendaylight.controller.sal.binding.api.NotificationListener.asCtClass;
- pool.appendClassPath(new LoaderClassPath(RpcService.classLoader));
+ super(pool)
}
- override <T extends RpcService> getDirectProxyFor(Class<T> iface) {
- val T instance = ClassLoaderUtils.withClassLoaderAndLock(iface.classLoader,lock) [|
+ override directProxySupplier(Class iface) {
+ return [|
val proxyName = iface.directProxyName;
val potentialClass = ClassLoaderUtils.tryToLoadClassWithTCCL(proxyName)
if(potentialClass != null) {
- return potentialClass.newInstance as T;
+ return potentialClass.newInstance;
}
val supertype = iface.asCtClass
val createdCls = createClass(iface.directProxyName, supertype) [
'''
]
]
- return createdCls.toClass(iface.classLoader).newInstance as T
+ return createdCls.toClass(iface.classLoader).newInstance
]
- return instance;
}
- override <T extends RpcService> getRouterFor(Class<T> iface,String routerInstanceName) {
- val metadata = ClassLoaderUtils.withClassLoader(iface.classLoader) [|
- val supertype = iface.asCtClass
- return supertype.rpcMetadata;
- ]
-
- val instance = ClassLoaderUtils.<T>withClassLoaderAndLock(iface.classLoader,lock) [ |
+ override routerSupplier(Class iface, RpcServiceMetadata metadata) {
+ return [ |
val supertype = iface.asCtClass
val routerName = iface.routerName;
val potentialClass = ClassLoaderUtils.tryToLoadClassWithTCCL(routerName)
if(potentialClass != null) {
- return potentialClass.newInstance as T;
+ return potentialClass.newInstance;
}
val targetCls = createClass(iface.routerName, supertype) [
}
implementMethodsFrom(supertype) [
if (parameterTypes.size === 1) {
- val rpcMeta = metadata.rpcMethods.get(name);
+ val rpcMeta = metadata.getRpcMethod(name);
val bodyTmp = '''
{
final «InstanceIdentifier.name» identifier = $1.«rpcMeta.inputRouteGetter.name»()«IF rpcMeta.
'''
]
]
- return targetCls.toClass(iface.classLoader,iface.protectionDomain).newInstance as T
-
+ return targetCls.toClass(iface.classLoader,iface.protectionDomain).newInstance
];
- return new RpcRouterCodegenInstance(routerInstanceName,iface, instance, metadata.contexts,metadata.supportedInputs);
}
- private def RpcServiceMetadata getRpcMetadata(CtClass iface) {
- val metadata = new RpcServiceMetadata;
-
- iface.methods.filter[declaringClass == iface && parameterTypes.size === 1].forEach [ method |
- val routingPair = method.rpcMetadata;
- if (routingPair !== null) {
- metadata.contexts.add(routingPair.context)
- metadata.rpcMethods.put(method.name,routingPair)
- val input = routingPair.inputType.javaClass as Class<? extends DataContainer>;
- metadata.supportedInputs.add(input);
- metadata.rpcInputs.put(input,routingPair);
- }
- ]
- return metadata;
- }
-
- private def getRpcMetadata(CtMethod method) {
- val inputClass = method.parameterTypes.get(0);
- return inputClass.rpcMethodMetadata(inputClass,method.name);
- }
-
- private def RpcMetadata rpcMethodMetadata(CtClass dataClass, CtClass inputClass, String rpcMethod) {
- for (method : dataClass.methods) {
- if (method.name.startsWith("get") && method.parameterTypes.size === 0) {
- for (annotation : method.availableAnnotations) {
- if (annotation instanceof RoutingContext) {
- val encapsulated = !method.returnType.equals(InstanceIdentifier.asCtClass);
- return new RpcMetadata(rpcMethod,(annotation as RoutingContext).value, method, encapsulated,inputClass);
- }
- }
- }
- }
- for (iface : dataClass.interfaces) {
- val ret = rpcMethodMetadata(iface,inputClass,rpcMethod);
- if(ret != null) return ret;
- }
- return null;
- }
-
- private def getJavaClass(CtClass cls) {
- Thread.currentThread.contextClassLoader.loadClass(cls.name)
- }
-
- override getInvokerFactory() {
- return this;
- }
-
- override invokerFor(NotificationListener instance) {
- val cls = instance.class
- val prototype = resolveInvokerClass(cls);
-
- return RuntimeGeneratedInvoker.create(instance, prototype)
- }
-
- protected def generateListenerInvoker(Class<? extends NotificationListener> iface) {
- val callbacks = iface.methods.filter[notificationCallback]
+ override generateListenerInvoker(Class iface) {
+ val callbacks = iface.methods.filter[YangtoolsMappingHelper.isNotificationCallback(it)]
val supportedNotification = callbacks.map[parameterTypes.get(0) as Class<? extends Notification>].toSet;
- val targetCls = createClass(iface.invokerName, BROKER_NOTIFICATION_LISTENER) [
+ val targetCls = createClass(iface.invokerName, brokerNotificationListener) [
field(DELEGATE_FIELD, iface)
- implementMethodsFrom(BROKER_NOTIFICATION_LISTENER) [
+ implementMethodsFrom(brokerNotificationListener) [
body = '''
{
«FOR callback : callbacks SEPARATOR " else "»
return new RuntimeGeneratedInvokerPrototype(supportedNotification,
finalClass as Class<? extends org.opendaylight.controller.sal.binding.api.NotificationListener<?>>);
}
-
- protected def resolveInvokerClass(Class<? extends NotificationListener> class1) {
- return ClassLoaderUtils.<RuntimeGeneratedInvokerPrototype>withClassLoaderAndLock(class1.classLoader,lock) [|
- val invoker = invokerClasses.get(class1);
- if (invoker !== null) {
- return invoker;
- }
- val newInvoker = generateListenerInvoker(class1);
- invokerClasses.put(class1, newInvoker);
- return newInvoker
- ]
- }
}