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 javax.annotation.concurrent.ThreadSafe;
24 import org.eclipse.jdt.annotation.NonNullByDefault;
25 import org.slf4j.Logger;
26 import org.slf4j.LoggerFactory;
29 * Users of this utility class are expected to synchronize on this instance
30 * it they need to ensure atomic operations on it.
34 public final class JavassistUtils {
35 private static final Logger LOG = LoggerFactory.getLogger(JavassistUtils.class);
36 private static final Map<ClassPool, JavassistUtils> INSTANCES = new WeakHashMap<>();
39 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
52 * @param pool Backing class pool
53 * @return shared utility instance for specified pool
55 public static synchronized JavassistUtils forClassPool(final ClassPool pool) {
56 return INSTANCES.computeIfAbsent(requireNonNull(pool), JavassistUtils::new);
60 * Instantiate a new class based on a prototype. The class is set to automatically prune. The {@code customizer}
61 * is guaranteed to run with this object locked.
63 * @param prototype Prototype class fully qualified name
64 * @param fqn Target class fully qualified name
65 * @param customizer Customization callback to be invoked on the new class
66 * @return An instance of the new class
67 * @throws NotFoundException when the prototype class is not found
70 public synchronized CtClass instantiatePrototype(final String prototype, final String fqn,
71 final ClassCustomizer customizer) throws CannotCompileException, NotFoundException {
72 final CtClass result = classPool.getAndRename(prototype, fqn);
74 customizer.customizeClass(result);
75 } catch (CannotCompileException | NotFoundException e) {
78 } catch (Exception e) {
79 LOG.warn("Failed to customize {} from prototype {}", fqn, prototype, e);
81 throw new IllegalStateException(String.format("Failed to instantiate prototype %s as %s", prototype, fqn),
85 result.stopPruning(false);
90 public CtClass asCtClass(final Class<?> cls) {
92 return classPool.get(cls.getName());
93 } catch (NotFoundException nfe1) {
94 appendClassLoaderIfMissing(cls.getClassLoader());
96 return classPool.get(cls.getName());
97 } catch (final NotFoundException nfe2) {
98 LOG.warn("Appending ClassClassPath for {}", cls, nfe2);
99 classPool.appendClassPath(new ClassClassPath(cls));
101 return classPool.get(cls.getName());
102 } catch (NotFoundException e) {
103 LOG.warn("Failed to load class {} from pool {}", cls, classPool, e);
104 throw new IllegalStateException("Failed to load class", e);
110 public synchronized void appendClassLoaderIfMissing(final ClassLoader loader) {
111 if (!loaderClassPaths.containsKey(loader)) {
112 final ClassPath ctLoader = new LoaderClassPath(loader);
113 classPool.appendClassPath(ctLoader);
114 loaderClassPaths.put(loader, ctLoader);