Clean up more Sonar warnings
[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 java.lang.reflect.ParameterizedType;
13 import java.lang.reflect.Type;
14 import java.util.Optional;
15 import java.util.concurrent.Callable;
16 import java.util.function.Function;
17 import java.util.function.Supplier;
18 import org.eclipse.jdt.annotation.NonNull;
19 import org.slf4j.Logger;
20 import org.slf4j.LoggerFactory;
21
22 /**
23  * Utility methods for working with ClassLoaders and classes.
24  */
25 public final class ClassLoaderUtils {
26     private static final Logger LOG = LoggerFactory.getLogger(ClassLoaderUtils.class);
27
28     private ClassLoaderUtils() {
29         // Hidden on purpose
30     }
31
32     /**
33      * Immediately call {@link Function#apply(Object)} with provided {@link ClassLoader}. This method safely switches
34      * the thread's Thread Context Class Loader to the specified class loader for the duration of execution of that
35      * method.
36      *
37      * @param <T> the type of the input to the function
38      * @param <R> the type of the result of the function
39      * @param cls {@link ClassLoader} to be used.
40      * @param function Function to be applied.
41      * @param input Function input
42      * @return Result of function invocation.
43      * @throws NullPointerException if class loader or function is null
44      */
45     public static <T, R> R applyWithClassLoader(final @NonNull ClassLoader cls, final @NonNull Function<T, R> function,
46             final T input) {
47         final var currentThread = Thread.currentThread();
48         final var oldCls = currentThread.getContextClassLoader();
49         currentThread.setContextClassLoader(requireNonNull(cls));
50         try {
51             return requireNonNull(function).apply(input);
52         } finally {
53             currentThread.setContextClassLoader(oldCls);
54         }
55     }
56
57     /**
58      * Immediately call {@link Callable#call()} with provided {@link ClassLoader}. This method safely switches
59      * the thread's Thread Context Class Loader to the specified class loader for the duration of execution of that
60      * method.
61      *
62      * @param <V> the result type of the callable
63      * @param cls {@link ClassLoader} to be used.
64      * @param callable Function to be executed.
65      * @return Result of callable invocation.
66      * @throws NullPointerException if class loader or callable is null
67      * @throws Exception if the callable fails
68      */
69     public static <V> V callWithClassLoader(final @NonNull ClassLoader cls, final @NonNull Callable<V> callable)
70             throws Exception {
71         final var currentThread = Thread.currentThread();
72         final var oldCls = currentThread.getContextClassLoader();
73         currentThread.setContextClassLoader(requireNonNull(cls));
74         try {
75             return requireNonNull(callable).call();
76         } finally {
77             currentThread.setContextClassLoader(oldCls);
78         }
79     }
80
81     /**
82      * Immediately call {@link Supplier#get()} with provided {@link ClassLoader}. This method safely switches
83      * the thread's Thread Context Class Loader to the specified class loader for the duration of execution of that
84      * method.
85      *
86      * @param <V> the result type of the supplier
87      * @param cls {@link ClassLoader} to be used.
88      * @param supplier Function to be executed.
89      * @return Result of supplier invocation.
90      * @throws NullPointerException if class loader or supplier is null
91      */
92     public static <V> V getWithClassLoader(final @NonNull ClassLoader cls, final @NonNull Supplier<V> supplier) {
93         final var currentThread = Thread.currentThread();
94         final var oldCls = currentThread.getContextClassLoader();
95         currentThread.setContextClassLoader(requireNonNull(cls));
96         try {
97             return requireNonNull(supplier).get();
98         } finally {
99             currentThread.setContextClassLoader(oldCls);
100         }
101     }
102
103     /**
104      * Immediately call {@link Runnable#run()} with provided {@link ClassLoader}. This method safely switches
105      * the thread's Thread Context Class Loader to the specified class loader for the duration of execution of that
106      * method.
107      *
108      * @param cls {@link ClassLoader} to be used.
109      * @param runnable Function to be executed.
110      * @throws NullPointerException if class loader or runnable is null
111      */
112     public static void runWithClassLoader(final @NonNull ClassLoader cls, final @NonNull Runnable runnable) {
113         final var currentThread = Thread.currentThread();
114         final var oldCls = currentThread.getContextClassLoader();
115         currentThread.setContextClassLoader(requireNonNull(cls));
116         try {
117             requireNonNull(runnable).run();
118         } finally {
119             currentThread.setContextClassLoader(oldCls);
120         }
121     }
122
123     @SuppressWarnings("unchecked")
124     public static <S, G, P> Optional<Class<P>> findFirstGenericArgument(final Class<S> scannedClass,
125             final Class<G> genericType) {
126         return getWithClassLoader(scannedClass.getClassLoader(),
127             () -> findParameterizedType(scannedClass, genericType)
128                 .map(ptype -> (Class<P>) ptype.getActualTypeArguments()[0]));
129     }
130
131     /**
132      * Find the parameterized instantiation of a particular interface implemented by a class.
133      *
134      * @param subclass Implementing class
135      * @param genericType Interface to search for
136      * @return Parameterized interface as implemented by the class, if present
137      */
138     public static Optional<ParameterizedType> findParameterizedType(final Class<?> subclass,
139             final Class<?> genericType) {
140         requireNonNull(genericType);
141
142         for (var type : subclass.getGenericInterfaces()) {
143             if (type instanceof ParameterizedType ptype && genericType.equals(ptype.getRawType())) {
144                 return Optional.of(ptype);
145             }
146         }
147
148         LOG.debug("Class {} does not declare interface {}", subclass, genericType);
149         return Optional.empty();
150     }
151
152     /**
153      * Extract the first generic type argument for a Type. If the type is not parameterized, this method returns empty.
154      *
155      * @param type Type to examine
156      * @return First generic type argument, if present
157      * @throws NullPointerException if {@code type} is null
158      */
159     public static Optional<Type> getFirstGenericParameter(final Type type) {
160         requireNonNull(type);
161         return type instanceof ParameterizedType ptype ? Optional.of(ptype.getActualTypeArguments()[0])
162             : Optional.empty();
163     }
164 }