From 23bb8e62b8ea9716e6e1b8404f02c49786490ba5 Mon Sep 17 00:00:00 2001 From: Jakub Toth Date: Tue, 6 Jun 2017 10:33:16 +0200 Subject: [PATCH] Binding v2 DOM Codec - Javassist part Change-Id: I9f07629ef79729650e44b1caba239a25a23db81e Signed-off-by: Jakub Toth --- .../impl/util/javassist/ClassCustomizer.java | 28 ++ .../impl/util/javassist/ClassGenerator.java | 28 ++ .../impl/util/javassist/JavassistUtils.java | 370 ++++++++++++++++++ .../impl/util/javassist/MethodGenerator.java | 28 ++ .../util/javassist/SourceCodeGenerator.java | 37 ++ 5 files changed, 491 insertions(+) create mode 100644 binding2/mdsal-binding2-generator-impl/src/main/java/org/opendaylight/mdsal/binding/javav2/generator/impl/util/javassist/ClassCustomizer.java create mode 100644 binding2/mdsal-binding2-generator-impl/src/main/java/org/opendaylight/mdsal/binding/javav2/generator/impl/util/javassist/ClassGenerator.java create mode 100644 binding2/mdsal-binding2-generator-impl/src/main/java/org/opendaylight/mdsal/binding/javav2/generator/impl/util/javassist/JavassistUtils.java create mode 100644 binding2/mdsal-binding2-generator-impl/src/main/java/org/opendaylight/mdsal/binding/javav2/generator/impl/util/javassist/MethodGenerator.java create mode 100644 binding2/mdsal-binding2-generator-impl/src/main/java/org/opendaylight/mdsal/binding/javav2/generator/impl/util/javassist/SourceCodeGenerator.java diff --git a/binding2/mdsal-binding2-generator-impl/src/main/java/org/opendaylight/mdsal/binding/javav2/generator/impl/util/javassist/ClassCustomizer.java b/binding2/mdsal-binding2-generator-impl/src/main/java/org/opendaylight/mdsal/binding/javav2/generator/impl/util/javassist/ClassCustomizer.java new file mode 100644 index 0000000000..5b94d28b49 --- /dev/null +++ b/binding2/mdsal-binding2-generator-impl/src/main/java/org/opendaylight/mdsal/binding/javav2/generator/impl/util/javassist/ClassCustomizer.java @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2017 Pantheon Technologies s.r.o. 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.mdsal.binding.javav2.generator.impl.util.javassist; + +import com.google.common.annotations.Beta; +import javassist.CtClass; + +/** + * Interface allowing customization of classes after loading. + */ +@Beta +public interface ClassCustomizer { + + /** + * Customize a class. + * + * @param cls + * - class to be customized + * @throws Exception + * - when a problem ensues + */ + void customizeClass(CtClass cls) throws Exception; +} \ No newline at end of file diff --git a/binding2/mdsal-binding2-generator-impl/src/main/java/org/opendaylight/mdsal/binding/javav2/generator/impl/util/javassist/ClassGenerator.java b/binding2/mdsal-binding2-generator-impl/src/main/java/org/opendaylight/mdsal/binding/javav2/generator/impl/util/javassist/ClassGenerator.java new file mode 100644 index 0000000000..d50ad046dc --- /dev/null +++ b/binding2/mdsal-binding2-generator-impl/src/main/java/org/opendaylight/mdsal/binding/javav2/generator/impl/util/javassist/ClassGenerator.java @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2017 Pantheon Technologies s.r.o. 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.mdsal.binding.javav2.generator.impl.util.javassist; + +import com.google.common.annotations.Beta; +import javassist.CannotCompileException; +import javassist.CtClass; + +/** + * Interface allowing generate new classes. + */ +@Beta +public interface ClassGenerator { + + /** + * Process class. + * + * @param baseClass + * - class to be generated + * @throws CannotCompileException + */ + void process(CtClass baseClass) throws CannotCompileException; +} diff --git a/binding2/mdsal-binding2-generator-impl/src/main/java/org/opendaylight/mdsal/binding/javav2/generator/impl/util/javassist/JavassistUtils.java b/binding2/mdsal-binding2-generator-impl/src/main/java/org/opendaylight/mdsal/binding/javav2/generator/impl/util/javassist/JavassistUtils.java new file mode 100644 index 0000000000..291b20cc72 --- /dev/null +++ b/binding2/mdsal-binding2-generator-impl/src/main/java/org/opendaylight/mdsal/binding/javav2/generator/impl/util/javassist/JavassistUtils.java @@ -0,0 +1,370 @@ +/* + * Copyright (c) 2017 Pantheon Technologies s.r.o. 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.mdsal.binding.javav2.generator.impl.util.javassist; + +import com.google.common.annotations.Beta; +import com.google.common.base.Preconditions; +import java.util.Collection; +import java.util.Map; +import java.util.WeakHashMap; +import javassist.CannotCompileException; +import javassist.ClassClassPath; +import javassist.ClassPath; +import javassist.ClassPool; +import javassist.CtClass; +import javassist.CtField; +import javassist.CtMethod; +import javassist.LoaderClassPath; +import javassist.Modifier; +import javassist.NotFoundException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Users of this utility class are expected to synchronize on this instance it + * they need to ensure atomic operations on it. + */ +@Beta +public final class JavassistUtils { + + private static final Logger LOG = LoggerFactory.getLogger(JavassistUtils.class); + + private static final Map INSTANCES = new WeakHashMap<>(); + private final Map loaderClassPaths = new WeakHashMap<>(); + private final ClassPool classPool; + + private JavassistUtils(final ClassPool pool) { + classPool = Preconditions.checkNotNull(pool); + } + + /** + * Get a utility instance for a particular class pool. A new instance is + * created if this is a new pool. If an instance already exists, is is + * returned. + * + * @param pool + * - backing class pool + * @return shared utility instance for specified pool + */ + public static synchronized JavassistUtils forClassPool(final ClassPool pool) { + JavassistUtils ret = INSTANCES.get(Preconditions.checkNotNull(pool)); + if (ret == null) { + ret = new JavassistUtils(pool); + INSTANCES.put(pool, ret); + } + return ret; + } + + /** + * Generate and add method to class. + * + * @param baseClass + * - class for adding method + * @param methodReturnType + * - return type of method + * @param methodName + * - name of method + * @param methodParameter + * - parameter of method + * @param methodGenerator + * - method generator + * @throws CannotCompileException + */ + public void method(final CtClass baseClass, final Class methodReturnType, final String methodName, + final Class methodParameter, final MethodGenerator methodGenerator) throws CannotCompileException { + final CtClass[] pa = new CtClass[] { asCtClass(methodParameter) }; + final CtMethod method = new CtMethod(asCtClass(methodReturnType), methodName, pa, baseClass); + + methodGenerator.process(method); + baseClass.addMethod(method); + } + + /** + * Generate and add method to class. + * + * @param baseClass + * - class for adding method + * @param methodReturnType + * - return type of method + * @param methodName + * - name of method + * @param methodParameters + * - parameters of method + * @param methodGenerator + * - method generator + * @throws CannotCompileException + */ + public void method(final CtClass baseClass, final Class methodReturnType, final String methodName, + final Collection> methodParameters, final MethodGenerator methodGenerator) + throws CannotCompileException { + final CtClass[] pa = new CtClass[methodParameters.size()]; + + int i = 0; + for (final Class parameter : methodParameters) { + pa[i] = asCtClass(parameter); + ++i; + } + + final CtMethod method = new CtMethod(asCtClass(methodReturnType), methodName, pa, baseClass); + methodGenerator.process(method); + baseClass.addMethod(method); + } + + /** + * Generate and add static method to class. + * + * @param baseClass + * - class for adding method + * @param methodReturnType + * - return type of method + * @param methodName + * - name of method + * @param methodParameter + * - parameter of method + * @param methodGenerator + * - method generator + * @throws CannotCompileException + */ + public void staticMethod(final CtClass baseClass, final Class methodReturnType, final String methodName, + final Class methodParameter, final MethodGenerator methodGenerator) throws CannotCompileException { + final CtClass[] pa = new CtClass[] { asCtClass(methodParameter) }; + final CtMethod method = new CtMethod(asCtClass(methodReturnType), methodName, pa, baseClass); + methodGenerator.process(method); + baseClass.addMethod(method); + } + + /** + * Implement methods to class from other class. + * + * @param target + * - class for implementing methods + * @param source + * - source class of methods to be implemented in target + * @param methodGenerator + * - method generator + * @throws CannotCompileException + */ + public void implementMethodsFrom(final CtClass target, final CtClass source, final MethodGenerator methodGenerator) + throws CannotCompileException { + for (final CtMethod method : source.getMethods()) { + if (method.getDeclaringClass() == source) { + final CtMethod redeclaredMethod = new CtMethod(method, target, null); + methodGenerator.process(redeclaredMethod); + target.addMethod(redeclaredMethod); + } + } + } + + /** + * Generate and add class to global class pool. + * + * @param className + * - name of class + * @param classGenerator + * - class generator + * @return generated class + * @throws CannotCompileException + */ + public CtClass createClass(final String className, final ClassGenerator classGenerator) throws CannotCompileException { + final CtClass target = classPool.makeClass(className); + classGenerator.process(target); + return target; + } + + /** + * Generate and add class to global class pool with implemented interface. + * + * @param className + * - name of class + * @param superInterface + * - interface to be implemented to the class + * @param classGenerator + * - class generator + * @return generated class with interface + * @throws CannotCompileException + */ + public CtClass createClass(final String className, final CtClass superInterface, final ClassGenerator classGenerator) + throws CannotCompileException { + final CtClass target = classPool.makeClass(className); + implementsType(target, superInterface); + classGenerator.process(target); + return target; + } + + /** + * Instantiate a new class based on a prototype. The class is set to + * automatically prune. + * + * @param prototype + * - prototype class fully qualified name + * @param target + * - target class fully qualified name + * @param customizer + * - customization callback to be invoked on the new class + * @return An instance of the new class + * @throws NotFoundException + * - when the prototype class is not found + */ + public synchronized CtClass instantiatePrototype(final String prototype, final String target, + final ClassCustomizer customizer) throws NotFoundException { + final CtClass result = classPool.getAndRename(prototype, target); + + try { + customizer.customizeClass(result); + } catch (final Exception e) { + LOG.warn("Failed to customize {} from prototype {}", target, prototype, e); + result.detach(); + throw new IllegalStateException(String.format("Failed to instantiate prototype %s as %s", prototype, target), + e); + } + + result.stopPruning(false); + return result; + } + + /** + * Implements type to class. + * + * @param baseClass + * - class for implements interface + * @param superInterface + * - interface to be implemented + */ + public void implementsType(final CtClass baseClass, final CtClass superInterface) { + Preconditions.checkArgument(superInterface.isInterface(), "Supertype must be interface"); + baseClass.addInterface(superInterface); + } + + /** + * Get class from class pool. + * + * @param class1 + * - class for getting from class pool + * @return class + */ + public CtClass asCtClass(final Class class1) { + return get(this.classPool, class1); + } + + /** + * Create and add field to class. + * + * @param baseClass + * - class for adding field + * @param fieldName + * - name of field + * @param fieldType + * - type of field + * @return class of field + * @throws CannotCompileException + */ + public CtField field(final CtClass baseClass, final String fieldName, final Class fieldType) + throws CannotCompileException { + final CtField field = new CtField(asCtClass(fieldType), fieldName, baseClass); + field.setModifiers(Modifier.PUBLIC); + baseClass.addField(field); + return field; + } + + /** + * Create and add static field to class. + * + * @param baseClass + * - class for adding field + * @param fieldName + * - name of field + * @param fieldType + * - type of field + * @return class of field + * @throws CannotCompileException + */ + public CtField staticField(final CtClass baseClass, final String fieldName, final Class fieldType) + throws CannotCompileException { + return staticField(baseClass, fieldName, fieldType, null); + } + + /** + * Create and add static field to class. + * + * @param baseClass + * - class for adding field + * @param fieldName + * - name of field + * @param fieldType + * - type of field + * @param sourceGenerator + * - source generator + * @return class of field + * @throws CannotCompileException + */ + public CtField staticField(final CtClass baseClass, final String fieldName, final Class fieldType, + final SourceCodeGenerator sourceGenerator) throws CannotCompileException { + final CtField field = new CtField(asCtClass(fieldType), fieldName, baseClass); + field.setModifiers(Modifier.PUBLIC + Modifier.STATIC); + baseClass.addField(field); + + if (sourceGenerator != null) { + sourceGenerator.appendField(field, null); + } + + return field; + } + + /** + * Get class from pool. + * + * @param pool + * - class pool + * @param clazz + * - search class in class pool + * @return class if exists + */ + public CtClass get(final ClassPool pool, final Class clazz) { + try { + return pool.get(clazz.getName()); + } catch (final NotFoundException nfe1) { + appendClassLoaderIfMissing(clazz.getClassLoader()); + try { + return pool.get(clazz.getName()); + } catch (final NotFoundException nfe2) { + LOG.warn("Appending ClassClassPath for {}", clazz, nfe2); + pool.appendClassPath(new ClassClassPath(clazz)); + try { + return pool.get(clazz.getName()); + } catch (final NotFoundException e) { + LOG.warn("Failed to load class {} from pool {}", clazz, pool, e); + throw new IllegalStateException("Failed to load class", e); + } + } + } + } + + /** + * Append class to class pool if doesn't exist. + * + * @param loader + * - class loader of search class + */ + public synchronized void appendClassLoaderIfMissing(final ClassLoader loader) { + if (!loaderClassPaths.containsKey(loader)) { + final ClassPath ctLoader = new LoaderClassPath(loader); + classPool.appendClassPath(ctLoader); + loaderClassPaths.put(loader, ctLoader); + } + } + + /** + * Ensure if is class in class loader. + * + * @param child + * - search class + */ + public void ensureClassLoader(final Class child) { + appendClassLoaderIfMissing(child.getClassLoader()); + } +} diff --git a/binding2/mdsal-binding2-generator-impl/src/main/java/org/opendaylight/mdsal/binding/javav2/generator/impl/util/javassist/MethodGenerator.java b/binding2/mdsal-binding2-generator-impl/src/main/java/org/opendaylight/mdsal/binding/javav2/generator/impl/util/javassist/MethodGenerator.java new file mode 100644 index 0000000000..cb911beb50 --- /dev/null +++ b/binding2/mdsal-binding2-generator-impl/src/main/java/org/opendaylight/mdsal/binding/javav2/generator/impl/util/javassist/MethodGenerator.java @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2017 Pantheon Technologies s.r.o. 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.mdsal.binding.javav2.generator.impl.util.javassist; + +import com.google.common.annotations.Beta; +import javassist.CannotCompileException; +import javassist.CtMethod; + +/** + * Interface allowing generator new methods. + */ +@Beta +public interface MethodGenerator { + + /** + * Process method. + * + * @param method + * - method to be generated + * @throws CannotCompileException + */ + void process(CtMethod method) throws CannotCompileException; +} diff --git a/binding2/mdsal-binding2-generator-impl/src/main/java/org/opendaylight/mdsal/binding/javav2/generator/impl/util/javassist/SourceCodeGenerator.java b/binding2/mdsal-binding2-generator-impl/src/main/java/org/opendaylight/mdsal/binding/javav2/generator/impl/util/javassist/SourceCodeGenerator.java new file mode 100644 index 0000000000..ec31c887fb --- /dev/null +++ b/binding2/mdsal-binding2-generator-impl/src/main/java/org/opendaylight/mdsal/binding/javav2/generator/impl/util/javassist/SourceCodeGenerator.java @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2017 Pantheon Technologies s.r.o. 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.mdsal.binding.javav2.generator.impl.util.javassist; + +import com.google.common.annotations.Beta; +import javassist.CtClass; +import javassist.CtField; +import javassist.CtMethod; + +/** + * Interface for a class that that generates readable source code for a runtime generated class. + * The appendField/appendMethod methods append source code to a temporary output. When outputGeneratedSource + * is called, the entire class source code is generated and outputted. + */ +@Beta +public interface SourceCodeGenerator { + + /** + * Appends the given class field and value to the temporary output. + */ + void appendField(CtField field, String value); + + /** + * Appends the given method and source code body to the temporary output. + */ + void appendMethod(CtMethod method, String code); + + /** + * Generates the full source code for the given class and outputs it. + */ + void outputGeneratedSource(CtClass ctClass); +} -- 2.36.6