001 /* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018package org.apache.commons.crypto.utils; 019 020import java.lang.ref.WeakReference; 021import java.lang.reflect.Constructor; 022import java.util.Arrays; 023import java.util.Collections; 024import java.util.Map; 025import java.util.WeakHashMap; 026 027import org.apache.commons.crypto.cipher.CryptoCipher; 028 029/** 030 * General utility methods for working with reflection. 031 */ 032public final class ReflectionUtils { 033 034 /** 035 * A unique class which is used as a sentinel value in the caching for 036 * getClassByName. {@link #getClassByNameOrNull(String)}. 037 */ 038 private static abstract class NegativeCacheSentinel { 039 // noop 040 } 041 042 private static final Map<ClassLoader, Map<String, WeakReference<Class<?>>>> CACHE_CLASSES = new WeakHashMap<>(); 043 044 private static final ClassLoader CLASSLOADER; 045 046 static { 047 final ClassLoader threadClassLoader = Thread.currentThread().getContextClassLoader(); 048 CLASSLOADER = threadClassLoader != null ? threadClassLoader : CryptoCipher.class.getClassLoader(); 049 } 050 051 /** 052 * Sentinel value to store negative cache results in {@link #CACHE_CLASSES}. 053 */ 054 private static final Class<?> NEGATIVE_CACHE_SENTINEL = NegativeCacheSentinel.class; 055 056 /** 057 * Loads a class by name. 058 * 059 * @param name the class name. 060 * @return the class object. 061 * @throws ClassNotFoundException if the class is not found. 062 */ 063 public static Class<?> getClassByName(final String name) throws ClassNotFoundException { 064 final Class<?> ret = getClassByNameOrNull(name); 065 if (ret == null) { 066 throw new ClassNotFoundException("Class " + name + " not found"); 067 } 068 return ret; 069 } 070 071 /** 072 * Loads a class by name, returning null rather than throwing an exception if it 073 * couldn't be loaded. This is to avoid the overhead of creating an exception. 074 * 075 * @param name the class name. 076 * @return the class object, or null if it could not be found. 077 */ 078 private static Class<?> getClassByNameOrNull(final String name) { 079 final Map<String, WeakReference<Class<?>>> map; 080 081 synchronized (CACHE_CLASSES) { 082 map = CACHE_CLASSES.computeIfAbsent(CLASSLOADER, k -> Collections.synchronizedMap(new WeakHashMap<>())); 083 } 084 085 Class<?> clazz = null; 086 final WeakReference<Class<?>> ref = map.get(name); 087 if (ref != null) { 088 clazz = ref.get(); 089 } 090 091 if (clazz == null) { 092 try { 093 clazz = Class.forName(name, true, CLASSLOADER); 094 } catch (final ClassNotFoundException e) { 095 // Leave a marker that the class isn't found 096 map.put(name, new WeakReference<>(NEGATIVE_CACHE_SENTINEL)); 097 return null; 098 } 099 // 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}