2 * Copyright (c) 2014 Cisco Systems, Inc. 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.generator.util;
10 import static java.util.Objects.requireNonNull;
12 import com.google.common.annotations.Beta;
14 import java.util.WeakHashMap;
15 import javassist.CannotCompileException;
16 import javassist.ClassClassPath;
17 import javassist.ClassPath;
18 import javassist.ClassPool;
19 import javassist.CtClass;
20 import javassist.LoaderClassPath;
21 import javassist.NotFoundException;
22 import javax.annotation.concurrent.GuardedBy;
23 import org.eclipse.jdt.annotation.NonNullByDefault;
24 import org.slf4j.Logger;
25 import org.slf4j.LoggerFactory;
28 * Users of this utility class are expected to synchronize on this instance it they need to ensure atomic operations
29 * on it. Individual operations are synchronized and therefore are thread-safe.
32 public final class JavassistUtils {
33 private static final Logger LOG = LoggerFactory.getLogger(JavassistUtils.class);
34 private static final Map<ClassPool, JavassistUtils> INSTANCES = new WeakHashMap<>();
37 private final Map<ClassLoader, ClassPath> loaderClassPaths = new WeakHashMap<>();
39 private final ClassPool classPool;
41 private JavassistUtils(final ClassPool pool) {
42 classPool = requireNonNull(pool);
46 * Get a utility instance for a particular class pool. A new instance is
47 * created if this is a new pool. If an instance already exists, is is
50 * @param pool Backing class pool
51 * @return shared utility instance for specified pool
53 public static synchronized JavassistUtils forClassPool(final ClassPool pool) {
54 return INSTANCES.computeIfAbsent(requireNonNull(pool), JavassistUtils::new);
58 * Instantiate a new class based on a prototype. The class is set to automatically prune. The {@code customizer}
59 * is guaranteed to run with this object locked.
61 * @param prototype Prototype class fully qualified name
62 * @param fqn Target class fully qualified name
63 * @param customizer Customization callback to be invoked on the new class
64 * @return An instance of the new class
65 * @throws NotFoundException when the prototype class is not found
68 @SuppressWarnings("checkstyle:illegalCatch")
69 public synchronized CtClass instantiatePrototype(final String prototype, final String fqn,
70 final ClassCustomizer customizer) throws CannotCompileException, NotFoundException {
71 final CtClass result = classPool.getAndRename(prototype, fqn);
73 customizer.customizeClass(result);
74 } catch (CannotCompileException | NotFoundException e) {
77 } catch (Exception e) {
78 LOG.warn("Failed to customize {} from prototype {}", fqn, prototype, e);
80 throw new IllegalStateException(String.format("Failed to instantiate prototype %s as %s", prototype, fqn),
84 result.stopPruning(false);
89 public CtClass asCtClass(final Class<?> cls) {
91 return classPool.get(cls.getName());
92 } catch (NotFoundException nfe1) {
93 appendClassLoaderIfMissing(cls.getClassLoader());
95 return classPool.get(cls.getName());
96 } catch (final NotFoundException nfe2) {
97 LOG.warn("Appending ClassClassPath for {}", cls, nfe2);
98 classPool.appendClassPath(new ClassClassPath(cls));
100 return classPool.get(cls.getName());
101 } catch (NotFoundException e) {
102 LOG.warn("Failed to load class {} from pool {}", cls, classPool, e);
103 throw new IllegalStateException("Failed to load class", e);
109 public synchronized void appendClassLoaderIfMissing(final ClassLoader loader) {
110 if (!loaderClassPaths.containsKey(loader)) {
111 final ClassPath ctLoader = new LoaderClassPath(loader);
112 classPool.appendClassPath(ctLoader);
113 loaderClassPaths.put(loader, ctLoader);