/* * Copyright 2013 Stormpath, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.stormpath.sdk.lang; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.InputStream; import java.lang.reflect.Constructor; /** * @since 0.1 */ public class Classes { /** * Private internal log instance. */ private static final Logger log = LoggerFactory.getLogger(Classes.class); /** * @since 0.1 */ private static final ClassLoaderAccessor THREAD_CL_ACCESSOR = new ExceptionIgnoringAccessor() { @Override protected ClassLoader doGetClassLoader() throws Throwable { return Thread.currentThread().getContextClassLoader(); } }; /** * @since 0.1 */ private static final ClassLoaderAccessor CLASS_CL_ACCESSOR = new ExceptionIgnoringAccessor() { @Override protected ClassLoader doGetClassLoader() throws Throwable { return Classes.class.getClassLoader(); } }; /** * @since 0.1 */ private static final ClassLoaderAccessor SYSTEM_CL_ACCESSOR = new ExceptionIgnoringAccessor() { @Override protected ClassLoader doGetClassLoader() throws Throwable { return ClassLoader.getSystemClassLoader(); } }; /** * Attempts to load the specified class name from the current thread's * {@link Thread#getContextClassLoader() context class loader}, then the * current ClassLoader (<code>Classes.class.getClassLoader()</code>), then the system/application * ClassLoader (<code>ClassLoader.getSystemClassLoader()</code>, in that order. If any of them cannot locate * the specified class, an <code>UnknownClassException</code> is thrown (our RuntimeException equivalent of * the JRE's <code>ClassNotFoundException</code>. * * @param fqcn the fully qualified class name to load * @return the located class * @throws UnknownClassException if the class cannot be found. */ public static <T> Class<T> forName(String fqcn) throws UnknownClassException { Class<T> clazz = THREAD_CL_ACCESSOR.loadClass(fqcn); if (clazz == null) { if (log.isTraceEnabled()) { log.trace("Unable to load class named [" + fqcn + "] from the thread context ClassLoader. Trying the current ClassLoader..."); } clazz = CLASS_CL_ACCESSOR.loadClass(fqcn); } if (clazz == null) { if (log.isTraceEnabled()) { log.trace("Unable to load class named [" + fqcn + "] from the current ClassLoader. " + "Trying the system/application ClassLoader..."); } clazz = SYSTEM_CL_ACCESSOR.loadClass(fqcn); } if (clazz == null) { String msg = "Unable to load class named [" + fqcn + "] from the thread context, current, or " + "system/application ClassLoaders. All heuristics have been exhausted. Class could not be found."; if (fqcn != null && fqcn.startsWith("com.stormpath.sdk.impl")) { msg += " Have you remembered to include the stormpath-sdk-impl .jar in your runtime classpath?"; } throw new UnknownClassException(msg); } return clazz; } /** * Returns the specified resource by checking the current thread's * {@link Thread#getContextClassLoader() context class loader}, then the * current ClassLoader (<code>Classes.class.getClassLoader()</code>), then the system/application * ClassLoader (<code>ClassLoader.getSystemClassLoader()</code>, in that order, using * {@link ClassLoader#getResourceAsStream(String) getResourceAsStream(name)}. * * @param name the name of the resource to acquire from the classloader(s). * @return the InputStream of the resource found, or <code>null</code> if the resource cannot be found from any * of the three mentioned ClassLoaders. * @since 0.8 */ public static InputStream getResourceAsStream(String name) { InputStream is = THREAD_CL_ACCESSOR.getResourceStream(name); if (is == null) { is = CLASS_CL_ACCESSOR.getResourceStream(name); } if (is == null) { is = SYSTEM_CL_ACCESSOR.getResourceStream(name); } return is; } public static boolean isAvailable(String fullyQualifiedClassName) { try { forName(fullyQualifiedClassName); return true; } catch (UnknownClassException e) { return false; } } public static <T> T newInstance(String fqcn) { Class<T> clazz = forName(fqcn); return newInstance(clazz); } public static <T> T newInstance(String fqcn, Object... args) { Class<T> clazz = forName(fqcn); return newInstance(clazz, args); } public static <T> T newInstance(Class<T> clazz) { if (clazz == null) { String msg = "Class method parameter cannot be null."; throw new IllegalArgumentException(msg); } try { return clazz.newInstance(); } catch (Exception e) { throw new InstantiationException("Unable to instantiate class [" + clazz.getName() + "]", e); } } public static <T> T newInstance(Class<T> clazz, Object... args) { Class[] argTypes = new Class[args.length]; for (int i = 0; i < args.length; i++) { argTypes[i] = args[i].getClass(); } Constructor<T> ctor = getConstructor(clazz, argTypes); return instantiate(ctor, args); } public static <T> Constructor<T> getConstructor(Class<T> clazz, Class... argTypes) { try { return clazz.getConstructor(argTypes); } catch (NoSuchMethodException e) { throw new IllegalStateException(e); } } public static <T> T instantiate(Constructor<T> ctor, Object... args) { try { return ctor.newInstance(args); } catch (Exception e) { String msg = "Unable to instantiate instance with constructor [" + ctor + "]"; throw new InstantiationException(msg, e); } } /** * @since 1.0 */ private static interface ClassLoaderAccessor { <T> Class<T> loadClass(String fqcn); InputStream getResourceStream(String name); } /** * @since 1.0 */ private static abstract class ExceptionIgnoringAccessor implements ClassLoaderAccessor { @SuppressWarnings("unchecked") public <T> Class<T> loadClass(String fqcn) { Class<T> clazz = null; ClassLoader cl = getClassLoader(); if (cl != null) { try { clazz = (Class<T>)cl.loadClass(fqcn); } catch (ClassNotFoundException e) { if (log.isTraceEnabled()) { log.trace("Unable to load clazz named [" + fqcn + "] from class loader [" + cl + "]"); } } } return clazz; } public InputStream getResourceStream(String name) { InputStream is = null; ClassLoader cl = getClassLoader(); if (cl != null) { is = cl.getResourceAsStream(name); } return is; } protected final ClassLoader getClassLoader() { try { return doGetClassLoader(); } catch (Throwable t) { if (log.isDebugEnabled()) { log.debug("Unable to acquire ClassLoader.", t); } } return null; } protected abstract ClassLoader doGetClassLoader() throws Throwable; } }