1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 */
18 package org.apache.commons.crypto.utils;
19
20 import java.lang.ref.WeakReference;
21 import java.lang.reflect.Constructor;
22 import java.util.Arrays;
23 import java.util.Collections;
24 import java.util.Map;
25 import java.util.WeakHashMap;
26
27 import org.apache.commons.crypto.cipher.CryptoCipher;
28
29 /**
30 * General utility methods for working with reflection.
31 */
32 public final class ReflectionUtils {
33
34 /**
35 * A unique class which is used as a sentinel value in the caching for
36 * getClassByName. {@link #getClassByNameOrNull(String)}.
37 */
38 private static abstract class NegativeCacheSentinel {
39 // noop
40 }
41
42 private static final Map<ClassLoader, Map<String, WeakReference<Class<?>>>> CACHE_CLASSES = new WeakHashMap<>();
43
44 private static final ClassLoader CLASSLOADER;
45
46 static {
47 final ClassLoader threadClassLoader = Thread.currentThread().getContextClassLoader();
48 CLASSLOADER = threadClassLoader != null ? threadClassLoader : CryptoCipher.class.getClassLoader();
49 }
50
51 /**
52 * Sentinel value to store negative cache results in {@link #CACHE_CLASSES}.
53 */
54 private static final Class<?> NEGATIVE_CACHE_SENTINEL = NegativeCacheSentinel.class;
55
56 /**
57 * Loads a class by name.
58 *
59 * @param name the class name.
60 * @return the class object.
61 * @throws ClassNotFoundException if the class is not found.
62 */
63 public static Class<?> getClassByName(final String name) throws ClassNotFoundException {
64 final Class<?> ret = getClassByNameOrNull(name);
65 if (ret == null) {
66 throw new ClassNotFoundException("Class " + name + " not found");
67 }
68 return ret;
69 }
70
71 /**
72 * Loads a class by name, returning null rather than throwing an exception if it
73 * couldn't be loaded. This is to avoid the overhead of creating an exception.
74 *
75 * @param name the class name.
76 * @return the class object, or null if it could not be found.
77 */
78 private static Class<?> getClassByNameOrNull(final String name) {
79 final Map<String, WeakReference<Class<?>>> map;
80
81 synchronized (CACHE_CLASSES) {
82 map = CACHE_CLASSES.computeIfAbsent(CLASSLOADER, k -> Collections.synchronizedMap(new WeakHashMap<>()));
83 }
84
85 Class<?> clazz = null;
86 final WeakReference<Class<?>> ref = map.get(name);
87 if (ref != null) {
88 clazz = ref.get();
89 }
90
91 if (clazz == null) {
92 try {
93 clazz = Class.forName(name, true, CLASSLOADER);
94 } catch (final ClassNotFoundException e) {
95 // Leave a marker that the class isn't found
96 map.put(name, new WeakReference<>(NEGATIVE_CACHE_SENTINEL));
97 return null;
98 }
99 // two putters can race here, but they'll put the same class
100 map.put(name, new WeakReference<>(clazz));
101 return clazz;
102 }
103 if (clazz == NEGATIVE_CACHE_SENTINEL) {
104 return null; // not found
105 }
106 // cache hit
107 return clazz;
108 }
109
110 /**
111 * Uses the constructor represented by this {@code Constructor} object to create
112 * and initialize a new instance of the constructor's declaring class, with the
113 * specified initialization parameters.
114 *
115 * @param <T> type for the new instance
116 * @param klass the Class object.
117 * @param args array of objects to be passed as arguments to the constructor
118 * call.
119 * @return a new object created by calling the constructor this object
120 * represents.
121 */
122 public static <T> T newInstance(final Class<T> klass, final Object... args) {
123 try {
124 final Constructor<T> ctor;
125 final int argsLength = args.length;
126
127 if (argsLength == 0) {
128 ctor = klass.getDeclaredConstructor();
129 } else {
130 final Class<?>[] argClses = new Class[argsLength];
131 Arrays.setAll(argClses, i -> args[i].getClass());
132 ctor = klass.getDeclaredConstructor(argClses);
133 }
134 ctor.setAccessible(true);
135 return ctor.newInstance(args);
136 } catch (final Exception e) {
137 throw new IllegalArgumentException(e);
138 }
139 }
140
141 /**
142 * The private constructor of {@link ReflectionUtils}.
143 */
144 private ReflectionUtils() {
145 }
146 }