From: Tony Tkacik Date: Tue, 10 Jun 2014 11:30:21 +0000 (+0000) Subject: Merge "BUG-614: introduce AbstractRuntimeCodeGenerator" X-Git-Tag: release/helium~670 X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?a=commitdiff_plain;h=4f8e371e1b7f6a2aa31115407c3f37738030f4c5;hp=f3ba8a814797fb5d50e24e3f9144f01406121464;p=controller.git Merge "BUG-614: introduce AbstractRuntimeCodeGenerator" --- diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/codegen/impl/AbstractRuntimeCodeGenerator.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/codegen/impl/AbstractRuntimeCodeGenerator.java new file mode 100644 index 0000000000..9605a4d372 --- /dev/null +++ b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/codegen/impl/AbstractRuntimeCodeGenerator.java @@ -0,0 +1,187 @@ +/* + * 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, 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 cls); + protected abstract Supplier directProxySupplier(final Class serviceType); + protected abstract Supplier routerSupplier(final Class 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 cls) { + RuntimeGeneratedInvokerPrototype invoker = invokerClasses.get(cls); + if (invoker != null) { + return invoker; + } + + utils.getLock().lock(); + try { + invoker = ClassLoaderUtils.withClassLoader(cls.getClassLoader(), new Supplier() { + @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 getDirectProxyFor(final Class serviceType) { + utils.getLock().lock(); + try { + return ClassLoaderUtils.withClassLoader(serviceType.getClassLoader(), directProxySupplier(serviceType)); + } finally { + utils.getLock().unlock(); + } + } + + @Override + public final RpcRouter getRouterFor(final Class serviceType, final String name) { + final RpcServiceMetadata metadata = ClassLoaderUtils.withClassLoader(serviceType.getClassLoader(), new Supplier() { + @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(name, serviceType, instance, metadata.getContexts()); + } finally { + utils.getLock().unlock(); + } + } + + @Override + public NotificationInvoker invokerFor(final NotificationListener instance) { + final Class 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); + } + } +} diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/codegen/impl/RpcRouterCodegenInstance.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/codegen/impl/RpcRouterCodegenInstance.java index 5578f75ae2..052fd2169a 100644 --- a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/codegen/impl/RpcRouterCodegenInstance.java +++ b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/codegen/impl/RpcRouterCodegenInstance.java @@ -24,7 +24,6 @@ import org.opendaylight.yangtools.concepts.AbstractObjectRegistration; 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; @@ -34,7 +33,7 @@ import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; public class RpcRouterCodegenInstance implements // - RpcRouter, RouteChangeListener, InstanceIdentifier> { +RpcRouter, RouteChangeListener, InstanceIdentifier> { private static final Logger LOG = LoggerFactory.getLogger(RpcRouterCodegenInstance.class); @@ -53,8 +52,7 @@ public class RpcRouterCodegenInstance implements // private final String name; @SuppressWarnings("unchecked") - public RpcRouterCodegenInstance(String name,Class type, T routerImpl, Set> contexts, - Set> inputs) { + public RpcRouterCodegenInstance(final String name,final Class type, final T routerImpl, final Iterable> contexts) { this.name = name; this.listeners = ListenerRegistry.create(); this.serviceType = type; @@ -86,7 +84,7 @@ public class RpcRouterCodegenInstance implements // @Override @SuppressWarnings("unchecked") - public RpcRoutingTable getRoutingTable(Class routeContext) { + public RpcRoutingTable getRoutingTable(final Class routeContext) { return (RpcRoutingTable) routingTables.get(routeContext); } @@ -102,12 +100,12 @@ public class RpcRouterCodegenInstance implements // @Override public , InstanceIdentifier>> ListenerRegistration registerRouteChangeListener( - L listener) { + final L listener) { return listeners.registerWithType(listener); } @Override - public void onRouteChange(RouteChange, InstanceIdentifier> change) { + public void onRouteChange(final RouteChange, InstanceIdentifier> change) { for (ListenerRegistration, InstanceIdentifier>> listener : listeners) { try { listener.getInstance().onRouteChange(change); @@ -118,17 +116,17 @@ public class RpcRouterCodegenInstance implements // } @Override - public T getService(Class context, InstanceIdentifier path) { + public T getService(final Class context, final InstanceIdentifier path) { return routingTables.get(context).getRoute(path); } @Override - public RoutedRpcRegistration addRoutedRpcImplementation(T service) { + public RoutedRpcRegistration addRoutedRpcImplementation(final T service) { return new RoutedRpcRegistrationImpl(service); } @Override - public RpcRegistration registerDefaultService(T service) { + public RpcRegistration registerDefaultService(final T service) { // TODO Auto-generated method stub RuntimeCodeHelper.setDelegate(invocationProxy, service); return null; @@ -136,7 +134,7 @@ public class RpcRouterCodegenInstance implements // private class RoutedRpcRegistrationImpl extends AbstractObjectRegistration implements RoutedRpcRegistration { - public RoutedRpcRegistrationImpl(T instance) { + public RoutedRpcRegistrationImpl(final T instance) { super(instance); } @@ -146,22 +144,22 @@ public class RpcRouterCodegenInstance implements // } @Override - public void registerPath(Class context, InstanceIdentifier path) { + public void registerPath(final Class context, final InstanceIdentifier path) { routingTables.get(context).updateRoute(path, getInstance()); } @Override - public void unregisterPath(Class context, InstanceIdentifier path) { + public void unregisterPath(final Class context, final InstanceIdentifier path) { routingTables.get(context).removeRoute(path, getInstance()); } @Override - public void registerInstance(Class context, InstanceIdentifier instance) { + public void registerInstance(final Class context, final InstanceIdentifier instance) { registerPath(context, instance); } @Override - public void unregisterInstance(Class context, InstanceIdentifier instance) { + public void unregisterInstance(final Class context, final InstanceIdentifier instance) { unregisterPath(context, instance); } diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/codegen/impl/RpcServiceMetadata.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/codegen/impl/RpcServiceMetadata.java index 399afe74e8..430b7a7e31 100644 --- a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/codegen/impl/RpcServiceMetadata.java +++ b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/codegen/impl/RpcServiceMetadata.java @@ -9,29 +9,31 @@ package org.opendaylight.controller.sal.binding.codegen.impl; 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, RpcMetadata> rpcInputs = new HashMap<>(); - private final HashSet> supportedInputs = new HashSet<>(); - private final HashSet> contexts = new HashSet<>(); - private final HashMap rpcMethods = new HashMap<>(); + private final Set> contexts = new HashSet<>(); + private final Map rpcMethods = new HashMap<>(); + private final Iterable> roContexts = Iterables.unmodifiableIterable(contexts); - public HashSet> getContexts() { - return this.contexts; + public Iterable> getContexts() { + return roContexts; } - public HashMap getRpcMethods() { - return this.rpcMethods; + public RpcMetadata getRpcMethod(final String name) { + return rpcMethods.get(name); } - public HashMap, RpcMetadata> getRpcInputs() { - return this.rpcInputs; + public void addContext(final Class context) { + contexts.add(context); } - public HashSet> getSupportedInputs() { - return this.supportedInputs; + public void addRpcMethod(final String name, final RpcMetadata routingPair) { + rpcMethods.put(name, routingPair); } } diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/codegen/impl/RuntimeCodeGenerator.xtend b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/codegen/impl/RuntimeCodeGenerator.xtend index ac782051a8..1be6e2dfb1 100644 --- a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/codegen/impl/RuntimeCodeGenerator.xtend +++ b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/codegen/impl/RuntimeCodeGenerator.xtend @@ -8,45 +8,27 @@ 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, 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 getDirectProxyFor(Class 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) [ @@ -71,23 +53,17 @@ class RuntimeCodeGenerator implements org.opendaylight.controller.sal.binding.co ''' ] ] - return createdCls.toClass(iface.classLoader).newInstance as T + return createdCls.toClass(iface.classLoader).newInstance ] - return instance; } - override getRouterFor(Class iface,String routerInstanceName) { - val metadata = ClassLoaderUtils.withClassLoader(iface.classLoader) [| - val supertype = iface.asCtClass - return supertype.rpcMetadata; - ] - - val instance = ClassLoaderUtils.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) [ @@ -102,7 +78,7 @@ class RuntimeCodeGenerator implements org.opendaylight.controller.sal.binding.co } 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. @@ -130,74 +106,18 @@ class RuntimeCodeGenerator implements org.opendaylight.controller.sal.binding.co ''' ] ] - 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; - 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 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].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 "» @@ -216,16 +136,4 @@ class RuntimeCodeGenerator implements org.opendaylight.controller.sal.binding.co return new RuntimeGeneratedInvokerPrototype(supportedNotification, finalClass as Class>); } - - protected def resolveInvokerClass(Class class1) { - return ClassLoaderUtils.withClassLoaderAndLock(class1.classLoader,lock) [| - val invoker = invokerClasses.get(class1); - if (invoker !== null) { - return invoker; - } - val newInvoker = generateListenerInvoker(class1); - invokerClasses.put(class1, newInvoker); - return newInvoker - ] - } }