2 * Copyright (c) 2017 Pantheon Technologies s.r.o. 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.mdsal.binding.javav2.runtime.javassist;
10 import static java.util.Objects.requireNonNull;
12 import com.google.common.annotations.Beta;
13 import com.google.common.base.Preconditions;
14 import java.util.Collection;
16 import java.util.WeakHashMap;
17 import javassist.CannotCompileException;
18 import javassist.ClassClassPath;
19 import javassist.ClassPath;
20 import javassist.ClassPool;
21 import javassist.CtClass;
22 import javassist.CtField;
23 import javassist.CtMethod;
24 import javassist.LoaderClassPath;
25 import javassist.Modifier;
26 import javassist.NotFoundException;
27 import org.slf4j.Logger;
28 import org.slf4j.LoggerFactory;
31 * Users of this utility class are expected to synchronize on this instance it
32 * they need to ensure atomic operations on it.
35 public final class JavassistUtils {
37 private static final Logger LOG = LoggerFactory.getLogger(JavassistUtils.class);
39 private static final Map<ClassPool, JavassistUtils> INSTANCES = new WeakHashMap<>();
40 private final Map<ClassLoader, ClassPath> loaderClassPaths = new WeakHashMap<>();
41 private final ClassPool classPool;
43 private JavassistUtils(final ClassPool pool) {
44 classPool = requireNonNull(pool);
48 * Get a utility instance for a particular class pool. A new instance is
49 * created if this is a new pool. If an instance already exists, is is
53 * - backing class pool
54 * @return shared utility instance for specified pool
56 public static synchronized JavassistUtils forClassPool(final ClassPool pool) {
57 JavassistUtils ret = INSTANCES.get(requireNonNull(pool));
59 ret = new JavassistUtils(pool);
60 INSTANCES.put(pool, ret);
66 * Generate and add method to class.
69 * - class for adding method
70 * @param methodReturnType
71 * - return type of method
74 * @param methodParameter
75 * - parameter of method
76 * @param methodGenerator
78 * @throws CannotCompileException
80 public void method(final CtClass baseClass, final Class<?> methodReturnType, final String methodName,
81 final Class<?> methodParameter, final MethodGenerator methodGenerator) throws CannotCompileException {
82 final CtClass[] pa = new CtClass[] { asCtClass(methodParameter) };
83 final CtMethod method = new CtMethod(asCtClass(methodReturnType), methodName, pa, baseClass);
85 methodGenerator.process(method);
86 baseClass.addMethod(method);
90 * Generate and add method to class.
93 * - class for adding method
94 * @param methodReturnType
95 * - return type of method
98 * @param methodParameters
99 * - parameters of method
100 * @param methodGenerator
102 * @throws CannotCompileException
104 public void method(final CtClass baseClass, final Class<?> methodReturnType, final String methodName,
105 final Collection<? extends Class<?>> methodParameters, final MethodGenerator methodGenerator)
106 throws CannotCompileException {
107 final CtClass[] pa = new CtClass[methodParameters.size()];
110 for (final Class<?> parameter : methodParameters) {
111 pa[i] = asCtClass(parameter);
115 final CtMethod method = new CtMethod(asCtClass(methodReturnType), methodName, pa, baseClass);
116 methodGenerator.process(method);
117 baseClass.addMethod(method);
121 * Generate and add static method to class.
124 * - class for adding method
125 * @param methodReturnType
126 * - return type of method
129 * @param methodParameter
130 * - parameter of method
131 * @param methodGenerator
133 * @throws CannotCompileException
135 public void staticMethod(final CtClass baseClass, final Class<?> methodReturnType, final String methodName,
136 final Class<?> methodParameter, final MethodGenerator methodGenerator) throws CannotCompileException {
137 final CtClass[] pa = new CtClass[] { asCtClass(methodParameter) };
138 final CtMethod method = new CtMethod(asCtClass(methodReturnType), methodName, pa, baseClass);
139 methodGenerator.process(method);
140 baseClass.addMethod(method);
144 * Implement methods to class from other class.
147 * - class for implementing methods
149 * - source class of methods to be implemented in target
150 * @param methodGenerator
152 * @throws CannotCompileException
154 public void implementMethodsFrom(final CtClass target, final CtClass source, final MethodGenerator methodGenerator)
155 throws CannotCompileException {
156 for (final CtMethod method : source.getMethods()) {
157 if (method.getDeclaringClass() == source) {
158 final CtMethod redeclaredMethod = new CtMethod(method, target, null);
159 methodGenerator.process(redeclaredMethod);
160 target.addMethod(redeclaredMethod);
166 * Generate and add class to global class pool.
170 * @param classGenerator
172 * @return generated class
173 * @throws CannotCompileException
175 public CtClass createClass(final String className, final ClassGenerator classGenerator) throws CannotCompileException {
176 final CtClass target = classPool.makeClass(className);
177 classGenerator.process(target);
182 * Generate and add class to global class pool with implemented interface.
186 * @param superInterface
187 * - interface to be implemented to the class
188 * @param classGenerator
190 * @return generated class with interface
191 * @throws CannotCompileException
193 public CtClass createClass(final String className, final CtClass superInterface, final ClassGenerator classGenerator)
194 throws CannotCompileException {
195 final CtClass target = classPool.makeClass(className);
196 implementsType(target, superInterface);
197 classGenerator.process(target);
202 * Instantiate a new class based on a prototype. The class is set to
203 * automatically prune.
206 * - prototype class fully qualified name
208 * - target class fully qualified name
210 * - customization callback to be invoked on the new class
211 * @return An instance of the new class
212 * @throws NotFoundException
213 * - when the prototype class is not found
215 public synchronized CtClass instantiatePrototype(final String prototype, final String target,
216 final ClassCustomizer customizer) throws NotFoundException {
217 final CtClass result = classPool.getAndRename(prototype, target);
220 customizer.customizeClass(result);
221 } catch (final Exception e) {
222 LOG.warn("Failed to customize {} from prototype {}", target, prototype, e);
224 throw new IllegalStateException(String.format("Failed to instantiate prototype %s as %s", prototype, target),
228 result.stopPruning(false);
233 * Implements type to class.
236 * - class for implements interface
237 * @param superInterface
238 * - interface to be implemented
240 public void implementsType(final CtClass baseClass, final CtClass superInterface) {
241 Preconditions.checkArgument(superInterface.isInterface(), "Supertype must be interface");
242 baseClass.addInterface(superInterface);
246 * Get class from class pool.
249 * - class for getting from class pool
252 public CtClass asCtClass(final Class<?> class1) {
253 return get(this.classPool, class1);
257 * Create and add field to class.
260 * - class for adding field
265 * @return class of field
266 * @throws CannotCompileException
268 public CtField field(final CtClass baseClass, final String fieldName, final Class<?> fieldType)
269 throws CannotCompileException {
270 final CtField field = new CtField(asCtClass(fieldType), fieldName, baseClass);
271 field.setModifiers(Modifier.PUBLIC);
272 baseClass.addField(field);
277 * Create and add static field to class.
280 * - class for adding field
285 * @return class of field
286 * @throws CannotCompileException
288 public CtField staticField(final CtClass baseClass, final String fieldName, final Class<?> fieldType)
289 throws CannotCompileException {
290 return staticField(baseClass, fieldName, fieldType, null);
294 * Create and add static field to class.
297 * - class for adding field
302 * @param sourceGenerator
304 * @return class of field
305 * @throws CannotCompileException
307 public CtField staticField(final CtClass baseClass, final String fieldName, final Class<?> fieldType,
308 final SourceCodeGenerator sourceGenerator) throws CannotCompileException {
309 final CtField field = new CtField(asCtClass(fieldType), fieldName, baseClass);
310 field.setModifiers(Modifier.PUBLIC + Modifier.STATIC);
311 baseClass.addField(field);
313 if (sourceGenerator != null) {
314 sourceGenerator.appendField(field, null);
321 * Get class from pool.
326 * - search class in class pool
327 * @return class if exists
329 public CtClass get(final ClassPool pool, final Class<? extends Object> clazz) {
331 return pool.get(clazz.getName());
332 } catch (final NotFoundException nfe1) {
333 appendClassLoaderIfMissing(clazz.getClassLoader());
335 return pool.get(clazz.getName());
336 } catch (final NotFoundException nfe2) {
337 LOG.warn("Appending ClassClassPath for {}", clazz, nfe2);
338 pool.appendClassPath(new ClassClassPath(clazz));
340 return pool.get(clazz.getName());
341 } catch (final NotFoundException e) {
342 LOG.warn("Failed to load class {} from pool {}", clazz, pool, e);
343 throw new IllegalStateException("Failed to load class", e);
350 * Append class to class pool if doesn't exist.
353 * - class loader of search class
355 public synchronized void appendClassLoaderIfMissing(final ClassLoader loader) {
356 if (!loaderClassPaths.containsKey(loader)) {
357 final ClassPath ctLoader = new LoaderClassPath(loader);
358 classPool.appendClassPath(ctLoader);
359 loaderClassPaths.put(loader, ctLoader);
364 * Ensure if is class in class loader.
369 public void ensureClassLoader(final Class<?> child) {
370 appendClassLoaderIfMissing(child.getClassLoader());