X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?a=blobdiff_plain;f=common%2Futil%2Fsrc%2Fmain%2Fjava%2Forg%2Fopendaylight%2Fyangtools%2Futil%2FClassLoaderUtils.java;h=ad4b5fbf72e876cc54271f869bd7dd377c6f6aac;hb=f429749a59a10820b1bad0e9c3502ceac2ec1d07;hp=44dfc4526e6f596ca42e1a08e5fcd5a67939ff68;hpb=37842069b09c08d8d3d3f97e8acbceea79d51b63;p=yangtools.git diff --git a/common/util/src/main/java/org/opendaylight/yangtools/util/ClassLoaderUtils.java b/common/util/src/main/java/org/opendaylight/yangtools/util/ClassLoaderUtils.java index 44dfc4526e..ad4b5fbf72 100644 --- a/common/util/src/main/java/org/opendaylight/yangtools/util/ClassLoaderUtils.java +++ b/common/util/src/main/java/org/opendaylight/yangtools/util/ClassLoaderUtils.java @@ -7,93 +7,128 @@ */ package org.opendaylight.yangtools.util; -import static com.google.common.base.Preconditions.checkNotNull; +import static java.util.Objects.requireNonNull; +import com.google.common.annotations.Beta; import com.google.common.base.Joiner; -import com.google.common.base.Preconditions; -import com.google.common.base.Supplier; -import java.lang.reflect.Constructor; -import java.lang.reflect.InvocationTargetException; +import com.google.common.base.Splitter; +import com.google.common.collect.Iterables; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; -import java.util.Arrays; import java.util.List; +import java.util.Optional; import java.util.concurrent.Callable; +import java.util.function.Function; +import java.util.function.Supplier; +import org.eclipse.jdt.annotation.NonNull; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +/** + * Utility methods for working with ClassLoaders and classes. + */ public final class ClassLoaderUtils { private static final Logger LOG = LoggerFactory.getLogger(ClassLoaderUtils.class); + private static final Joiner DOT_JOINER = Joiner.on("."); + private static final Splitter DOT_SPLITTER = Splitter.on('.'); private ClassLoaderUtils() { - throw new UnsupportedOperationException("Utility class"); + // Hidden on purpose } /** - * - * Runs {@link Supplier} with provided {@link ClassLoader}. - * - * Invokes supplies function and makes sure that original {@link ClassLoader} - * is context {@link ClassLoader} after execution. + * Immediately call {@link Function#apply(Object)} with provided {@link ClassLoader}. This method safely switches + * the thread's Thread Context Class Loader to the specified class loader for the duration of execution of that + * method. * * @param cls {@link ClassLoader} to be used. - * @param function Function to be executed. - * @return Result of supplier invocation. - * + * @param function Function to be applied. + * @param input Function input + * @throws NullPointerException if class loader or function is null */ - public static V withClassLoader(final ClassLoader cls, final Supplier function) { - checkNotNull(cls, "Classloader should not be null"); - checkNotNull(function, "Function should not be null"); - - final ClassLoader oldCls = Thread.currentThread().getContextClassLoader(); + @Beta + public static R applyWithClassLoader(final @NonNull ClassLoader cls, final @NonNull Function function, + final T input) { + final Thread currentThread = Thread.currentThread(); + final ClassLoader oldCls = currentThread.getContextClassLoader(); + currentThread.setContextClassLoader(requireNonNull(cls)); try { - Thread.currentThread().setContextClassLoader(cls); - return function.get(); + return requireNonNull(function).apply(input); } finally { - Thread.currentThread().setContextClassLoader(oldCls); + currentThread.setContextClassLoader(oldCls); } } /** - * - * Runs {@link Callable} with provided {@link ClassLoader}. - * - * Invokes supplies function and makes sure that original {@link ClassLoader} - * is context {@link ClassLoader} after execution. + * Immediately call {@link Callable#call()} with provided {@link ClassLoader}. This method safely switches + * the thread's Thread Context Class Loader to the specified class loader for the duration of execution of that + * method. * * @param cls {@link ClassLoader} to be used. - * @param function Function to be executed. + * @param callable Function to be executed. * @return Result of callable invocation. - * + * @throws NullPointerException if class loader or callable is null */ - public static V withClassLoader(final ClassLoader cls, final Callable function) throws Exception { - checkNotNull(cls, "Classloader should not be null"); - checkNotNull(function, "Function should not be null"); - - final ClassLoader oldCls = Thread.currentThread().getContextClassLoader(); + @Beta + public static V callWithClassLoader(final @NonNull ClassLoader cls, final @NonNull Callable callable) + throws Exception { + final Thread currentThread = Thread.currentThread(); + final ClassLoader oldCls = currentThread.getContextClassLoader(); + currentThread.setContextClassLoader(requireNonNull(cls)); try { - Thread.currentThread().setContextClassLoader(cls); - return function.call(); + return requireNonNull(callable).call(); } finally { - Thread.currentThread().setContextClassLoader(oldCls); + currentThread.setContextClassLoader(oldCls); } } - public static Object construct(final Constructor constructor, final List objects) - throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException { - final Object[] initargs = objects.toArray(); - return constructor.newInstance(initargs); + /** + * Immediately call {@link Supplier#get()} with provided {@link ClassLoader}. This method safely switches + * the thread's Thread Context Class Loader to the specified class loader for the duration of execution of that + * method. + * + * @param cls {@link ClassLoader} to be used. + * @param supplier Function to be executed. + * @return Result of supplier invocation. + * @throws NullPointerException if class loader or supplier is null + */ + @Beta + public static V getWithClassLoader(final @NonNull ClassLoader cls, final @NonNull Supplier supplier) { + final Thread currentThread = Thread.currentThread(); + final ClassLoader oldCls = currentThread.getContextClassLoader(); + currentThread.setContextClassLoader(requireNonNull(cls)); + try { + return requireNonNull(supplier).get(); + } finally { + currentThread.setContextClassLoader(oldCls); + } } /** + * Immediately call {@link Runnable#run()} with provided {@link ClassLoader}. This method safely switches + * the thread's Thread Context Class Loader to the specified class loader for the duration of execution of that + * method. * + * @param cls {@link ClassLoader} to be used. + * @param runnable Function to be executed. + * @throws NullPointerException if class loader or runnable is null + */ + @Beta + public static void runWithClassLoader(final @NonNull ClassLoader cls, final @NonNull Runnable runnable) { + final Thread currentThread = Thread.currentThread(); + final ClassLoader oldCls = currentThread.getContextClassLoader(); + currentThread.setContextClassLoader(requireNonNull(cls)); + try { + requireNonNull(runnable).run(); + } finally { + currentThread.setContextClassLoader(oldCls); + } + } + + /** * Loads class using this supplied classloader. * - * - * @param cls * @param name String name of class. - * @return - * @throws ClassNotFoundException */ public static Class loadClass(final ClassLoader cls, final String name) throws ClassNotFoundException { if ("byte[]".equals(name)) { @@ -109,80 +144,102 @@ public final class ClassLoaderUtils { try { return cls.loadClass(name); } catch (final ClassNotFoundException e) { - final String[] components = name.split("\\."); + final List components = DOT_SPLITTER.splitToList(name); if (isInnerClass(components)) { - final int length = components.length; - final String outerName = Joiner.on(".").join(Arrays.asList(components).subList(0, length - 1)); - final String innerName = outerName + "$" + components[length-1]; + final int length = components.size() - 1; + final String outerName = DOT_JOINER.join(Iterables.limit(components, length)); + final String innerName = outerName + "$" + components.get(length); return cls.loadClass(innerName); - } else { - throw e; } + + throw e; } } - private static boolean isInnerClass(final String[] components) { - final int length = components.length; - if(length < 2) { + private static boolean isInnerClass(final List components) { + final int length = components.size(); + if (length < 2) { return false; } - final String potentialOuter = components[length - 2]; - if(potentialOuter == null) { + + final String potentialOuter = components.get(length - 2); + if (potentialOuter == null) { return false; } return Character.isUpperCase(potentialOuter.charAt(0)); } public static Class loadClassWithTCCL(final String name) throws ClassNotFoundException { - return loadClass(Thread.currentThread().getContextClassLoader(), name); + final Thread thread = Thread.currentThread(); + final ClassLoader tccl = thread.getContextClassLoader(); + if (tccl == null) { + throw new ClassNotFoundException("Thread " + thread + " does not have a Context Class Loader, cannot load " + + name); + } + return loadClass(tccl, name); } - public static Class tryToLoadClassWithTCCL(final String fullyQualifiedName) { + public static Optional> tryToLoadClassWithTCCL(final String fullyQualifiedClassName) { + final Thread thread = Thread.currentThread(); + final ClassLoader tccl = thread.getContextClassLoader(); + if (tccl == null) { + LOG.debug("Thread {} does not have a Context Class Loader, not loading class {}", thread, + fullyQualifiedClassName); + return Optional.empty(); + } + try { - return loadClassWithTCCL(fullyQualifiedName); + return Optional.of(loadClass(tccl, fullyQualifiedClassName)); } catch (final ClassNotFoundException e) { - LOG.debug("Failed to load class {}", fullyQualifiedName, e); - return null; + LOG.debug("Failed to load class {}", fullyQualifiedClassName, e); + return Optional.empty(); } } - public static Class

findFirstGenericArgument(final Class scannedClass, final Class genericType) { - return withClassLoader(scannedClass.getClassLoader(), ClassLoaderUtils.findFirstGenericArgumentTask(scannedClass, genericType)); - } - - private static Supplier> findFirstGenericArgumentTask(final Class scannedClass, final Class genericType) { - return new Supplier>() { - @Override - @SuppressWarnings("unchecked") - public Class

get() { - final ParameterizedType augmentationGeneric = findParameterizedType(scannedClass, genericType); - if (augmentationGeneric != null) { - return (Class

) augmentationGeneric.getActualTypeArguments()[0]; - } - return null; - } - }; + @SuppressWarnings("unchecked") + public static Optional> findFirstGenericArgument(final Class scannedClass, + final Class genericType) { + return getWithClassLoader(scannedClass.getClassLoader(), () -> { + return findParameterizedType(scannedClass, genericType) + .map(ptype -> (Class

) ptype.getActualTypeArguments()[0]); + }); } - public static ParameterizedType findParameterizedType(final Class subclass, final Class genericType) { - Preconditions.checkNotNull(subclass); - Preconditions.checkNotNull(genericType); + /** + * Find the parameterized instantiation of a particular interface implemented by a class. + * + * @param subclass Implementing class + * @param genericType Interface to search for + * @return Parameterized interface as implemented by the class, if present + */ + public static Optional findParameterizedType(final Class subclass, + final Class genericType) { + requireNonNull(genericType); for (final Type type : subclass.getGenericInterfaces()) { - if (type instanceof ParameterizedType && genericType.equals(((ParameterizedType) type).getRawType())) { - return (ParameterizedType) type; + if (type instanceof ParameterizedType) { + final ParameterizedType ptype = (ParameterizedType) type; + if (genericType.equals(ptype.getRawType())) { + return Optional.of(ptype); + } } } LOG.debug("Class {} does not declare interface {}", subclass, genericType); - return null; + return Optional.empty(); } - public static Type getFirstGenericParameter(final Type type) { - if (type instanceof ParameterizedType) { - return ((ParameterizedType) type).getActualTypeArguments()[0]; - } - return null; + /** + * Extract the first generic type argument for a Type. If the type is not parameterized, this method returns empty. + * + * @param type Type to examine + * @return First generic type argument, if present + * @throws NullPointerException if {@code type} is null + */ + public static Optional getFirstGenericParameter(final Type type) { + requireNonNull(type); + return type instanceof ParameterizedType ? Optional.of(((ParameterizedType) type).getActualTypeArguments()[0]) + : Optional.empty(); } }