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}