X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?a=blobdiff_plain;f=opendaylight%2Fmd-sal%2Fsal-binding-broker%2Fsrc%2Fmain%2Fjava%2Forg%2Fopendaylight%2Fcontroller%2Fsal%2Fbinding%2Fcodegen%2Fimpl%2FAbstractRuntimeCodeGenerator.java;fp=opendaylight%2Fmd-sal%2Fsal-binding-broker%2Fsrc%2Fmain%2Fjava%2Forg%2Fopendaylight%2Fcontroller%2Fsal%2Fbinding%2Fcodegen%2Fimpl%2FAbstractRuntimeCodeGenerator.java;h=9605a4d3723b6f21ccf8ce5810ad078ba3e3bbd6;hb=35000765cff05e4f59541bc497dfe253a3188644;hp=0000000000000000000000000000000000000000;hpb=62a55c174776a51409c185d3eb9f0a5c2d59ab63;p=controller.git 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); + } + } +}