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.yangtools.util;
10 import static java.util.Objects.requireNonNull;
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;
27 * Utility methods for working with ClassLoaders and classes.
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('.');
34 private ClassLoaderUtils() {
35 throw new UnsupportedOperationException("Utility class");
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
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
49 public static <T, R> R applyWithClassLoader(final @NonNull ClassLoader cls, final @NonNull Function<T, R> function,
51 final Thread currentThread = Thread.currentThread();
52 final ClassLoader oldCls = currentThread.getContextClassLoader();
53 currentThread.setContextClassLoader(requireNonNull(cls));
55 return requireNonNull(function).apply(input);
57 currentThread.setContextClassLoader(oldCls);
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
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
72 public static <V> V callWithClassLoader(final @NonNull ClassLoader cls, final @NonNull Callable<V> callable)
74 final Thread currentThread = Thread.currentThread();
75 final ClassLoader oldCls = currentThread.getContextClassLoader();
76 currentThread.setContextClassLoader(requireNonNull(cls));
78 return requireNonNull(callable).call();
80 currentThread.setContextClassLoader(oldCls);
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
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
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));
100 return requireNonNull(supplier).get();
102 currentThread.setContextClassLoader(oldCls);
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
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
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));
121 requireNonNull(runnable).run();
123 currentThread.setContextClassLoader(oldCls);
128 * Runs {@link Supplier} with provided {@link ClassLoader}.
131 * Invokes supplies function and makes sure that original {@link ClassLoader}
132 * is context {@link ClassLoader} after execution.
134 * @param cls {@link ClassLoader} to be used.
135 * @param function Function to be executed.
136 * @return Result of supplier invocation.
138 * @deprecated Use {@link #getWithClassLoader(ClassLoader, Supplier)} instead.
141 public static <V> V withClassLoader(final ClassLoader cls, final Supplier<V> function) {
142 return getWithClassLoader(cls, function);
146 * Runs {@link Callable} with provided {@link ClassLoader}.
149 * Invokes supplies function and makes sure that original {@link ClassLoader}
150 * is context {@link ClassLoader} after execution.
152 * @param cls {@link ClassLoader} to be used.
153 * @param function Function to be executed.
154 * @return Result of callable invocation.
156 * @deprecated Use {@link #callWithClassLoader(ClassLoader, Callable)} instead.
159 public static <V> V withClassLoader(final ClassLoader cls, final Callable<V> function) throws Exception {
160 return callWithClassLoader(cls, function);
164 * Loads class using this supplied classloader.
166 * @param name String name of class.
168 public static Class<?> loadClass(final ClassLoader cls, final String name) throws ClassNotFoundException {
169 if ("byte[]".equals(name)) {
172 if ("char[]".equals(name)) {
175 return loadClass0(cls,name);
178 private static Class<?> loadClass0(final ClassLoader cls, final String name) throws ClassNotFoundException {
180 return cls.loadClass(name);
181 } catch (final ClassNotFoundException e) {
182 final List<String> components = DOT_SPLITTER.splitToList(name);
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);
195 private static boolean isInnerClass(final List<String> components) {
196 final int length = components.size();
201 final String potentialOuter = components.get(length - 2);
202 if (potentialOuter == null) {
205 return Character.isUpperCase(potentialOuter.charAt(0));
208 public static Class<?> loadClassWithTCCL(final String name) throws ClassNotFoundException {
209 final Thread thread = Thread.currentThread();
210 final ClassLoader tccl = thread.getContextClassLoader();
212 throw new ClassNotFoundException("Thread " + thread + " does not have a Context Class Loader, cannot load "
215 return loadClass(tccl, name);
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();
223 LOG.debug("Thread {} does not have a Context Class Loader, not loading class {}", thread,
224 fullyQualifiedClassName);
229 return loadClass(tccl, fullyQualifiedClassName);
230 } catch (final ClassNotFoundException e) {
231 LOG.debug("Failed to load class {}", fullyQualifiedClassName, e);
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;
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);
249 for (final Type type : subclass.getGenericInterfaces()) {
250 if (type instanceof ParameterizedType && genericType.equals(((ParameterizedType) type).getRawType())) {
251 return (ParameterizedType) type;
255 LOG.debug("Class {} does not declare interface {}", subclass, genericType);
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];