/**
* Copyright (C) 2013 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.util;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import com.opengamma.OpenGammaRuntimeException;
/**
* Class utilities
*/
public final class ClassUtils {
/**
* A cache of loaded classes.
*/
private static final ConcurrentMap<String, Class<?>> s_classCache = new ConcurrentHashMap<>();
/**
* A cache of singletons.
*/
private static final ConcurrentMap<Class<?>, Object> s_singletonCache = new ConcurrentHashMap<>();
/**
* Method for resolving a class.
*/
private static final Method RESOLVE_METHOD;
static {
try {
RESOLVE_METHOD = ClassLoader.class.getDeclaredMethod("resolveClass", Class.class);
RESOLVE_METHOD.setAccessible(true);
} catch (NoSuchMethodException | SecurityException ex) {
throw new ExceptionInInitializerError(ex);
}
}
/**
* Prevents instantiation.
*/
private ClassUtils() {
}
//-------------------------------------------------------------------------
/**
* Loads a class from a class name, or fetches one from the calling thread's cache.
* The calling thread's class loader is used.
* <p>
* Some class loaders involve quite heavy synchronization overheads which can impact
* performance on multi-core systems if called heavy (for example as part of decoding a Fudge message).
* <p>
* The class will be fully initialized (static initializers invoked).
*
* @param className the class name, not null
* @return the class object, not null
* @throws ClassNotFoundException
*/
public static Class<?> loadClass(String className) throws ClassNotFoundException {
Class<?> clazz = s_classCache.get(className);
if (clazz == null) {
ClassLoader loader = Thread.currentThread().getContextClassLoader();
if (loader == null) {
clazz = Class.forName(className);
} else {
clazz = Class.forName(className, true, loader);
}
s_classCache.putIfAbsent(className, clazz);
}
return clazz;
}
/**
* Loads a class from a class name, or fetches one from the calling thread's cache.
* The calling thread's class loader is used.
* <p>
* Some class loaders involve quite heavy synchronization overheads which can impact
* performance on multi-core systems if called heavy (for example as part of decoding a Fudge message).
* <p>
* The class will be fully initialized (static initializers invoked).
*
* @param className the class name, not null
* @return the class object, not null
* @throws RuntimeException if the class cannot be found
*/
public static Class<?> loadClassRuntime(String className) {
try {
return loadClass(className);
} catch (ClassNotFoundException ex) {
throw new RuntimeException(ex);
}
}
/**
* Loads a class from a class name, or fetches one from the calling thread's cache.
* The calling thread's class loader is used.
* <p>
* Some class loaders involve quite heavy synchronization overheads which can impact
* performance on multi-core systems if called heavy (for example as part of decoding a Fudge message).
* <p>
* The class will be fully initialized (static initializers invoked).
*
* @param <T> the type to cast to
* @param className the class name, not null
* @param type the type to cast to, not null
* @return the class object, not null
* @throws RuntimeException if the class cannot be found
* @throws ClassCastException if the class is not a subtype of the specified type
*/
public static <T> Class<? extends T> loadClassRuntime(String className, Class<T> type) {
try {
return loadClass(className).asSubclass(type);
} catch (ClassNotFoundException ex) {
throw new RuntimeException(ex);
}
}
/**
* Initializes a class to ensure it is fully loaded.
* <p>
* The JVM has two separate steps in class loading, the initial load
* followed by the initialization.
* Static initializers are invoked in the second step.
* This method forces the second step.
*
* @param <T> the type
* @param clazz the class to initialize, not null
* @return the input class, not null
*/
public static <T> Class<T> initClass(Class<T> clazz) {
String className = clazz.getName();
if (s_classCache.containsKey(className) == false) {
try {
Class.forName(className, true, clazz.getClassLoader());
} catch (ClassNotFoundException ex) {
throw new OpenGammaRuntimeException(ex.getMessage(), ex);
}
s_classCache.putIfAbsent(className, clazz);
}
return clazz;
}
//-------------------------------------------------------------------------
/**
* Obtains the singleton instance of a type.
* <p>
* This finds and returns the singleton associated with a type.
*
* @param <T> the type
* @param type the type to find an instance for, not null
* @return the singleton instance, not null
*/
public static <T> T singletonInstance(Class<T> type) {
Object result = s_singletonCache.get(type);
if (result == null) {
result = singletonInstance0(type);
s_singletonCache.putIfAbsent(type, result);
}
return type.cast(result);
}
private static <T> T singletonInstance0(Class<T> type) {
try {
Field field = type.getDeclaredField("INSTANCE");
if (Modifier.isStatic(field.getModifiers()) && Modifier.isPublic(field.getModifiers())) {
return type.cast(field.get(null));
}
Method method = type.getMethod("instance");
if (Modifier.isStatic(method.getModifiers()) && Modifier.isPublic(method.getModifiers())) {
return type.cast(method.invoke(null));
}
method = type.getMethod("getInstance");
if (Modifier.isStatic(method.getModifiers()) && Modifier.isPublic(method.getModifiers())) {
return type.cast(method.invoke(null));
}
throw new IllegalArgumentException("No suitable singleton found");
} catch (ReflectiveOperationException ex) {
throw new IllegalArgumentException("Exception while accessing singleton", ex);
}
}
}