Remove ClassLoaderUtils.construct()
[yangtools.git] / common / util / src / main / java / org / opendaylight / yangtools / util / ClassLoaderUtils.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.yangtools.util;
9
10 import static java.util.Objects.requireNonNull;
11
12 import com.google.common.annotations.Beta;
13 import com.google.common.base.Joiner;
14 import com.google.common.base.Splitter;
15 import com.google.common.collect.Iterables;
16 import java.lang.reflect.ParameterizedType;
17 import java.lang.reflect.Type;
18 import java.util.List;
19 import java.util.concurrent.Callable;
20 import java.util.function.Function;
21 import java.util.function.Supplier;
22 import org.eclipse.jdt.annotation.NonNull;
23 import org.slf4j.Logger;
24 import org.slf4j.LoggerFactory;
25
26 /**
27  * Utility methods for working with ClassLoaders and classes.
28  */
29 public final class ClassLoaderUtils {
30     private static final Logger LOG = LoggerFactory.getLogger(ClassLoaderUtils.class);
31     private static final Joiner DOT_JOINER = Joiner.on(".");
32     private static final Splitter DOT_SPLITTER = Splitter.on('.');
33
34     private ClassLoaderUtils() {
35         throw new UnsupportedOperationException("Utility class");
36     }
37
38     /**
39      * Immediately call {@link Function#apply(Object)} with provided {@link ClassLoader}. This method safely switches
40      * the thread's Thread Context Class Loader to the specified class loader for the duration of execution of that
41      * method.
42      *
43      * @param cls {@link ClassLoader} to be used.
44      * @param function Function to be applied.
45      * @param input Function input
46      * @throws NullPointerException if class loader or function is null
47      */
48     @Beta
49     public static <T, R> R applyWithClassLoader(final @NonNull ClassLoader cls, final @NonNull Function<T, R> function,
50             final T input) {
51         final Thread currentThread = Thread.currentThread();
52         final ClassLoader oldCls = currentThread.getContextClassLoader();
53         currentThread.setContextClassLoader(requireNonNull(cls));
54         try {
55             return requireNonNull(function).apply(input);
56         } finally {
57             currentThread.setContextClassLoader(oldCls);
58         }
59     }
60
61     /**
62      * Immediately call {@link Callable#call()} with provided {@link ClassLoader}. This method safely switches
63      * the thread's Thread Context Class Loader to the specified class loader for the duration of execution of that
64      * method.
65      *
66      * @param cls {@link ClassLoader} to be used.
67      * @param callable Function to be executed.
68      * @return Result of callable invocation.
69      * @throws NullPointerException if class loader or callable is null
70      */
71     @Beta
72     public static <V> V callWithClassLoader(final @NonNull ClassLoader cls, final @NonNull Callable<V> callable)
73             throws Exception {
74         final Thread currentThread = Thread.currentThread();
75         final ClassLoader oldCls = currentThread.getContextClassLoader();
76         currentThread.setContextClassLoader(requireNonNull(cls));
77         try {
78             return requireNonNull(callable).call();
79         } finally {
80             currentThread.setContextClassLoader(oldCls);
81         }
82     }
83
84     /**
85      * Immediately call {@link Supplier#get()} with provided {@link ClassLoader}. This method safely switches
86      * the thread's Thread Context Class Loader to the specified class loader for the duration of execution of that
87      * method.
88      *
89      * @param cls {@link ClassLoader} to be used.
90      * @param supplier Function to be executed.
91      * @return Result of supplier invocation.
92      * @throws NullPointerException if class loader or supplier is null
93      */
94     @Beta
95     public static <V> V getWithClassLoader(final @NonNull ClassLoader cls, final @NonNull Supplier<V> supplier) {
96         final Thread currentThread = Thread.currentThread();
97         final ClassLoader oldCls = currentThread.getContextClassLoader();
98         currentThread.setContextClassLoader(requireNonNull(cls));
99         try {
100             return requireNonNull(supplier).get();
101         } finally {
102             currentThread.setContextClassLoader(oldCls);
103         }
104     }
105
106     /**
107      * Immediately call {@link Runnable#run()} with provided {@link ClassLoader}. This method safely switches
108      * the thread's Thread Context Class Loader to the specified class loader for the duration of execution of that
109      * method.
110      *
111      * @param cls {@link ClassLoader} to be used.
112      * @param runnable Function to be executed.
113      * @throws NullPointerException if class loader or runnable is null
114      */
115     @Beta
116     public static void runWithClassLoader(final @NonNull ClassLoader cls, final @NonNull Runnable runnable) {
117         final Thread currentThread = Thread.currentThread();
118         final ClassLoader oldCls = currentThread.getContextClassLoader();
119         currentThread.setContextClassLoader(requireNonNull(cls));
120         try {
121             requireNonNull(runnable).run();
122         } finally {
123             currentThread.setContextClassLoader(oldCls);
124         }
125     }
126
127     /**
128      * Runs {@link Supplier} with provided {@link ClassLoader}.
129      *
130      * <p>
131      * Invokes supplies function and makes sure that original {@link ClassLoader}
132      * is context {@link ClassLoader} after execution.
133      *
134      * @param cls {@link ClassLoader} to be used.
135      * @param function Function to be executed.
136      * @return Result of supplier invocation.
137      *
138      * @deprecated Use {@link #getWithClassLoader(ClassLoader, Supplier)} instead.
139      */
140     @Deprecated
141     public static <V> V withClassLoader(final ClassLoader cls, final Supplier<V> function) {
142         return getWithClassLoader(cls, function);
143     }
144
145     /**
146      * Runs {@link Callable} with provided {@link ClassLoader}.
147      *
148      * <p>
149      * Invokes supplies function and makes sure that original {@link ClassLoader}
150      * is context {@link ClassLoader} after execution.
151      *
152      * @param cls {@link ClassLoader} to be used.
153      * @param function Function to be executed.
154      * @return Result of callable invocation.
155      *
156      * @deprecated Use {@link #callWithClassLoader(ClassLoader, Callable)} instead.
157      */
158     @Deprecated
159     public static <V> V withClassLoader(final ClassLoader cls, final Callable<V> function) throws Exception {
160         return callWithClassLoader(cls, function);
161     }
162
163     /**
164      * Loads class using this supplied classloader.
165      *
166      * @param name String name of class.
167      */
168     public static Class<?> loadClass(final ClassLoader cls, final String name) throws ClassNotFoundException {
169         if ("byte[]".equals(name)) {
170             return byte[].class;
171         }
172         if ("char[]".equals(name)) {
173             return char[].class;
174         }
175         return loadClass0(cls,name);
176     }
177
178     private static Class<?> loadClass0(final ClassLoader cls, final String name) throws ClassNotFoundException {
179         try {
180             return cls.loadClass(name);
181         } catch (final ClassNotFoundException e) {
182             final List<String> components = DOT_SPLITTER.splitToList(name);
183
184             if (isInnerClass(components)) {
185                 final int length = components.size() - 1;
186                 final String outerName = DOT_JOINER.join(Iterables.limit(components, length));
187                 final String innerName = outerName + "$" + components.get(length);
188                 return cls.loadClass(innerName);
189             }
190
191             throw e;
192         }
193     }
194
195     private static boolean isInnerClass(final List<String> components) {
196         final int length = components.size();
197         if (length < 2) {
198             return false;
199         }
200
201         final String potentialOuter = components.get(length - 2);
202         if (potentialOuter == null) {
203             return false;
204         }
205         return Character.isUpperCase(potentialOuter.charAt(0));
206     }
207
208     public static Class<?> loadClassWithTCCL(final String name) throws ClassNotFoundException {
209         final Thread thread = Thread.currentThread();
210         final ClassLoader tccl = thread.getContextClassLoader();
211         if (tccl == null) {
212             throw new ClassNotFoundException("Thread " + thread + " does not have a Context Class Loader, cannot load "
213                     + name);
214         }
215         return loadClass(tccl, name);
216     }
217
218     // FIXME: 3.0.0: Document and return Optional
219     public static Class<?> tryToLoadClassWithTCCL(final String fullyQualifiedClassName) {
220         final Thread thread = Thread.currentThread();
221         final ClassLoader tccl = thread.getContextClassLoader();
222         if (tccl == null) {
223             LOG.debug("Thread {} does not have a Context Class Loader, not loading class {}", thread,
224                 fullyQualifiedClassName);
225             return null;
226         }
227
228         try {
229             return loadClass(tccl, fullyQualifiedClassName);
230         } catch (final ClassNotFoundException e) {
231             LOG.debug("Failed to load class {}", fullyQualifiedClassName, e);
232             return null;
233         }
234     }
235
236     @SuppressWarnings("unchecked")
237     public static <S, G, P> Class<P> findFirstGenericArgument(final Class<S> scannedClass, final Class<G> genericType) {
238         return getWithClassLoader(scannedClass.getClassLoader(), () -> {
239             final ParameterizedType augmentationGeneric = findParameterizedType(scannedClass, genericType);
240             return augmentationGeneric != null ? (Class<P>) augmentationGeneric.getActualTypeArguments()[0] : null;
241         });
242     }
243
244     // FIXME: 3.0.0: Document and return Optional
245     public static ParameterizedType findParameterizedType(final Class<?> subclass, final Class<?> genericType) {
246         requireNonNull(subclass);
247         requireNonNull(genericType);
248
249         for (final Type type : subclass.getGenericInterfaces()) {
250             if (type instanceof ParameterizedType && genericType.equals(((ParameterizedType) type).getRawType())) {
251                 return (ParameterizedType) type;
252             }
253         }
254
255         LOG.debug("Class {} does not declare interface {}", subclass, genericType);
256         return null;
257     }
258
259     // FIXME: 3.0.0: Document and return Optional
260     public static Type getFirstGenericParameter(final Type type) {
261         if (type instanceof ParameterizedType) {
262             return ((ParameterizedType) type).getActualTypeArguments()[0];
263         }
264         return null;
265     }
266 }