Remove useless UnsupportedOperationException throws
[yangtools.git] / common / util / src / main / java / org / opendaylight / yangtools / util / ClassLoaderUtils.java
index f258cf839deea5fdb6066483924ff4edfdb6699d..ad4b5fbf72e876cc54271f869bd7dd377c6f6aac 100644 (file)
  */
 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.Splitter;
-import com.google.common.base.Supplier;
-import java.lang.reflect.Constructor;
-import java.lang.reflect.InvocationTargetException;
+import com.google.common.collect.Iterables;
 import java.lang.reflect.ParameterizedType;
 import java.lang.reflect.Type;
 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> V withClassLoader(final ClassLoader cls, final Supplier<V> function) {
-        checkNotNull(cls, "Classloader should not be null");
-        checkNotNull(function, "Function should not be null");
-
-        final ClassLoader oldCls = Thread.currentThread().getContextClassLoader();
+    @Beta
+    public static <T, R> R applyWithClassLoader(final @NonNull ClassLoader cls, final @NonNull Function<T, R> 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> V withClassLoader(final ClassLoader cls, final Callable<V> 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> V callWithClassLoader(final @NonNull ClassLoader cls, final @NonNull Callable<V> 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<Object> 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> V getWithClassLoader(final @NonNull ClassLoader cls, final @NonNull Supplier<V> 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)) {
@@ -113,12 +148,12 @@ public final class ClassLoaderUtils {
 
             if (isInnerClass(components)) {
                 final int length = components.size() - 1;
-                final String outerName = Joiner.on(".").join(components.subList(0, length));
+                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;
         }
     }
 
@@ -136,54 +171,75 @@ public final class ClassLoaderUtils {
     }
 
     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<Class<?>> 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 <S,G,P> Class<P> findFirstGenericArgument(final Class<S> scannedClass, final Class<G> genericType) {
-        return withClassLoader(scannedClass.getClassLoader(), ClassLoaderUtils.<S,G,P>findFirstGenericArgumentTask(scannedClass, genericType));
-    }
-
-    private static <S,G,P> Supplier<Class<P>> findFirstGenericArgumentTask(final Class<S> scannedClass, final Class<G> genericType) {
-        return new Supplier<Class<P>>() {
-            @Override
-            @SuppressWarnings("unchecked")
-            public Class<P> get() {
-                final ParameterizedType augmentationGeneric = findParameterizedType(scannedClass, genericType);
-                if (augmentationGeneric != null) {
-                    return (Class<P>) augmentationGeneric.getActualTypeArguments()[0];
-                }
-                return null;
-            }
-        };
+    @SuppressWarnings("unchecked")
+    public static <S, G, P> Optional<Class<P>> findFirstGenericArgument(final Class<S> scannedClass,
+            final Class<G> genericType) {
+        return getWithClassLoader(scannedClass.getClassLoader(), () -> {
+            return findParameterizedType(scannedClass, genericType)
+                    .map(ptype -> (Class<P>) 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<ParameterizedType> 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<Type> getFirstGenericParameter(final Type type) {
+        requireNonNull(type);
+        return type instanceof ParameterizedType ? Optional.of(((ParameterizedType) type).getActualTypeArguments()[0])
+                : Optional.empty();
     }
 }