Fix checkstyle in mdsal-binding-generator-impl
[mdsal.git] / binding / mdsal-binding-generator-impl / src / main / java / org / opendaylight / mdsal / binding / generator / util / JavassistUtils.java
1 /*
2  * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
3  *
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
7  */
8 package org.opendaylight.mdsal.binding.generator.util;
9
10 import static java.util.Objects.requireNonNull;
11
12 import com.google.common.annotations.Beta;
13 import java.util.Map;
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;
27
28 /**
29  * Users of this utility class are expected to synchronize on this instance
30  * it they need to ensure atomic operations on it.
31  */
32 @NonNullByDefault
33 @ThreadSafe
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<>();
37
38     @GuardedBy("this")
39     private final Map<ClassLoader, ClassPath> loaderClassPaths = new WeakHashMap<>();
40     @GuardedBy("this")
41     private final ClassPool classPool;
42
43     private JavassistUtils(final ClassPool pool) {
44         classPool = requireNonNull(pool);
45     }
46
47     /**
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
50      * returned.
51      *
52      * @param pool Backing class pool
53      * @return shared utility instance for specified pool
54      */
55     public static synchronized JavassistUtils forClassPool(final ClassPool pool) {
56         return INSTANCES.computeIfAbsent(requireNonNull(pool), JavassistUtils::new);
57     }
58
59     /**
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.
62      *
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
68      */
69     @Beta
70     @SuppressWarnings("checkstyle:illegalCatch")
71     public synchronized CtClass instantiatePrototype(final String prototype, final String fqn,
72             final ClassCustomizer customizer) throws CannotCompileException, NotFoundException {
73         final CtClass result = classPool.getAndRename(prototype, fqn);
74         try {
75             customizer.customizeClass(result);
76         } catch (CannotCompileException | NotFoundException e) {
77             result.detach();
78             throw e;
79         } catch (Exception e) {
80             LOG.warn("Failed to customize {} from prototype {}", fqn, prototype, e);
81             result.detach();
82             throw new IllegalStateException(String.format("Failed to instantiate prototype %s as %s", prototype, fqn),
83                 e);
84         }
85
86         result.stopPruning(false);
87         return result;
88     }
89
90     @GuardedBy("this")
91     public CtClass asCtClass(final Class<?> cls) {
92         try {
93             return classPool.get(cls.getName());
94         } catch (NotFoundException nfe1) {
95             appendClassLoaderIfMissing(cls.getClassLoader());
96             try {
97                 return classPool.get(cls.getName());
98             } catch (final NotFoundException nfe2) {
99                 LOG.warn("Appending ClassClassPath for {}", cls, nfe2);
100                 classPool.appendClassPath(new ClassClassPath(cls));
101                 try {
102                     return classPool.get(cls.getName());
103                 } catch (NotFoundException e) {
104                     LOG.warn("Failed to load class {} from pool {}", cls, classPool, e);
105                     throw new IllegalStateException("Failed to load class", e);
106                 }
107             }
108         }
109     }
110
111     public synchronized void appendClassLoaderIfMissing(final ClassLoader loader) {
112         if (!loaderClassPaths.containsKey(loader)) {
113             final ClassPath ctLoader = new LoaderClassPath(loader);
114             classPool.appendClassPath(ctLoader);
115             loaderClassPaths.put(loader, ctLoader);
116         }
117     }
118 }