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.Collections; 023import java.util.Map; 024import java.util.WeakHashMap; 025 026import org.apache.commons.crypto.cipher.CryptoCipher; 027 028/** 029 * General utility methods for working with reflection. 030 */ 031public final class ReflectionUtils { 032 033 private static final Map<ClassLoader, Map<String, WeakReference<Class<?>>>> CACHE_CLASSES = new WeakHashMap<>(); 034 035 private static final ClassLoader CLASSLOADER; 036 037 static { 038 final ClassLoader threadClassLoader = Thread.currentThread().getContextClassLoader(); 039 CLASSLOADER = (threadClassLoader != null) ? threadClassLoader : CryptoCipher.class.getClassLoader(); 040 } 041 042 /** 043 * Sentinel value to store negative cache results in {@link #CACHE_CLASSES}. 044 */ 045 private static final Class<?> NEGATIVE_CACHE_SENTINEL = NegativeCacheSentinel.class; 046 047 /** 048 * The private constructor of {@link ReflectionUtils}. 049 */ 050 private ReflectionUtils() { 051 } 052 053 /** 054 * A unique class which is used as a sentinel value in the caching for 055 * getClassByName. {@link #getClassByNameOrNull(String)}. 056 */ 057 private static abstract class NegativeCacheSentinel { 058 // noop 059 } 060 061 /** 062 * Uses the constructor represented by this {@code Constructor} object to create 063 * and initialize a new instance of the constructor's declaring class, with the 064 * specified initialization parameters. 065 * 066 * @param <T> type for the new instance 067 * @param klass the Class object. 068 * @param args array of objects to be passed as arguments to the constructor 069 * call. 070 * @return a new object created by calling the constructor this object 071 * represents. 072 */ 073 public static <T> T newInstance(final Class<T> klass, final Object... args) { 074 try { 075 Constructor<T> ctor; 076 077 if (args.length == 0) { 078 ctor = klass.getDeclaredConstructor(); 079 } else { 080 final Class<?>[] argClses = new Class[args.length]; 081 for (int i = 0; i < args.length; i++) { 082 argClses[i] = args[i].getClass(); 083 } 084 ctor = klass.getDeclaredConstructor(argClses); 085 } 086 ctor.setAccessible(true); 087 return ctor.newInstance(args); 088 } catch (final Exception e) { 089 throw new IllegalArgumentException(e); 090 } 091 } 092 093 /** 094 * Loads a class by name. 095 * 096 * @param name the class name. 097 * @return the class object. 098 * @throws ClassNotFoundException if the class is not found. 099 */ 100 public static Class<?> getClassByName(final String name) throws ClassNotFoundException { 101 final Class<?> ret = getClassByNameOrNull(name); 102 if (ret == null) { 103 throw new ClassNotFoundException("Class " + name + " not found"); 104 } 105 return ret; 106 } 107 108 /** 109 * Loads a class by name, returning null rather than throwing an exception if it 110 * couldn't be loaded. This is to avoid the overhead of creating an exception. 111 * 112 * @param name the class name. 113 * @return the class object, or null if it could not be found. 114 */ 115 private static Class<?> getClassByNameOrNull(final String name) { 116 Map<String, WeakReference<Class<?>>> map; 117 118 synchronized (CACHE_CLASSES) { 119 map = CACHE_CLASSES.get(CLASSLOADER); 120 if (map == null) { 121 map = Collections.synchronizedMap(new WeakHashMap<String, WeakReference<Class<?>>>()); 122 CACHE_CLASSES.put(CLASSLOADER, map); 123 } 124 } 125 126 Class<?> clazz = null; 127 final WeakReference<Class<?>> ref = map.get(name); 128 if (ref != null) { 129 clazz = ref.get(); 130 } 131 132 if (clazz == null) { 133 try { 134 clazz = Class.forName(name, true, CLASSLOADER); 135 } catch (final ClassNotFoundException e) { 136 // Leave a marker that the class isn't found 137 map.put(name, new WeakReference<Class<?>>(NEGATIVE_CACHE_SENTINEL)); 138 return null; 139 } 140 // two putters can race here, but they'll put the same class 141 map.put(name, new WeakReference<Class<?>>(clazz)); 142 return clazz; 143 } else if (clazz == NEGATIVE_CACHE_SENTINEL) { 144 return null; // not found 145 } else { 146 // cache hit 147 return clazz; 148 } 149 } 150}