package org.jolokia.util;
/*
* Copyright 2009-2013 Roland Huss
*
* 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.
*/
import java.io.*;
import java.lang.reflect.*;
import java.net.URL;
import java.util.*;
/**
* Utility for class lookup.
*
* @author roland
* @since 19.04.11
*/
public final class ClassUtil {
private ClassUtil() {}
/**
* Lookup a class. See {@link ClassUtil#classForName(String, boolean,ClassLoader[])} for details. The class
* gets initialized during lookup.
*
* @param pClassName name to lookup.
* @return the class found or null if no class could be found.
*/
public static <T> Class<T> classForName(String pClassName, ClassLoader ... pClassLoaders) {
return classForName(pClassName, true, pClassLoaders);
}
/**
* Load a certain class. Several class loader are tried: Fires the current thread's context
* class loader, then its parents. If this doesn't work, the class loader which
* loaded this class is used (and its parents)
*
* @param pClassName class name to load
* @param pInitialize whether the class must be initialized
* @param pClassLoaders optional class loaders which are tried as well
* @return the class class found or null if no class could be loaded
*/
public static <T> Class<T> classForName(String pClassName,boolean pInitialize,ClassLoader ... pClassLoaders) {
Set<ClassLoader> tried = new HashSet<ClassLoader>();
for (ClassLoader loader : findClassLoaders(pClassLoaders)) {
// Go up the classloader stack to eventually find the server class. Sometimes the WebAppClassLoader
// hide the server classes loaded by the parent class loader.
while (loader != null) {
try {
if (!tried.contains(loader)) {
return (Class<T>) Class.forName(pClassName, pInitialize, loader);
}
} catch (ClassNotFoundException ignored) {}
tried.add(loader);
loader = loader.getParent();
}
}
return null;
}
private static List<ClassLoader> findClassLoaders(ClassLoader... pClassLoaders) {
List<ClassLoader> classLoadersToTry = new ArrayList<ClassLoader>(Arrays.asList(pClassLoaders));
classLoadersToTry.add(Thread.currentThread().getContextClassLoader());
classLoadersToTry.add(ClassUtil.class.getClassLoader());
List<ClassLoader> ret = new ArrayList<ClassLoader>();
Set<ClassLoader> visited = new HashSet<ClassLoader>();
for (ClassLoader cll : classLoadersToTry) {
if (cll != null && !visited.contains(cll)) {
ret.add(cll);
visited.add(cll);
}
}
return ret;
}
/**
* Get the given path as an input stream or return <code>null</code> if not found
*
* @param pPath path to lookup
* @return input stream or null if not found.
*/
public static InputStream getResourceAsStream(String pPath) {
for (ClassLoader loader : new ClassLoader[] {
Thread.currentThread().getContextClassLoader(),
ClassUtil.class.getClassLoader()
} ) {
if (loader != null) {
InputStream is = loader.getResourceAsStream(pPath);
if (is != null) {
return is;
}
}
}
return null;
}
/**
* Check for the existence of a given class
*
* @param pClassName class name to check
* @return true if the class could be loaded by the thread's conext class loader, false otherwise
*/
public static boolean checkForClass(String pClassName) {
return ClassUtil.classForName(pClassName,false) != null;
}
/**
* Instantiate an instance of the given class with its default constructor. The context class loader is used
* to lookup the class
*
* @param pClassName name of class to instantiate
* @param pArguments optional constructor arguments. Works only for objects with the same class as declared in
* the constructor types (no subclasses)
* @param <T> type object type
* @return instantiated object
* @throws IllegalArgumentException if the class could not be found or instantiated
*/
public static <T> T newInstance(String pClassName, Object ... pArguments) {
Class<T> clazz = classForName(pClassName);
if (clazz == null) {
throw new IllegalArgumentException("Cannot find " + pClassName);
}
return newInstance(clazz, pArguments);
}
/**
* Instantiate an instance of the given class with its default constructor
*
* @param pClass class to instantiate
* @param pArguments optional constructor arguments. Works only for objects with the same class as declared in
* the constructor types (no subclasses)
* @param <T> type object type
* @return instantiated object
* @throws IllegalArgumentException if the class could not be found or instantiated
*/
public static <T> T newInstance(Class<T> pClass, Object ... pArguments) {
try {
if (pClass != null) {
if (pArguments.length == 0) {
return pClass.newInstance();
} else {
Constructor<T> ctr = lookupConstructor(pClass, pArguments);
return ctr.newInstance(pArguments);
}
} else {
throw new IllegalArgumentException("Given class must not be null");
}
} catch (InstantiationException e) {
throw new IllegalArgumentException("Cannot instantiate " + pClass + ": " + e,e);
} catch (IllegalAccessException e) {
throw new IllegalArgumentException("Cannot instantiate " + pClass + ": " + e,e);
} catch (NoSuchMethodException e) {
throw new IllegalArgumentException("Cannot instantiate " + pClass + ": " + e,e);
} catch (InvocationTargetException e) {
throw new IllegalArgumentException("Cannot instantiate " + pClass + ": " + e,e);
}
}
/**
* Apply a method to a given object with optional arguments. The method is looked up the whole class
* hierarchy.
*
* @param pObject object on which to apply the method
* @param pMethod the method name
* @param pArgs optional arguments
* @return return value (if any)
*/
public static Object applyMethod(Object pObject, String pMethod, Object ... pArgs) {
Class<?> clazz = pObject.getClass();
try {
Method method = extractMethod(pMethod, clazz, pArgs);
return method.invoke(pObject,pArgs);
} catch (NoSuchMethodException e) {
throw new IllegalArgumentException("Cannot call method " + pMethod + " on " + pObject + ": " + e,e);
} catch (InvocationTargetException e) {
throw new IllegalArgumentException("Cannot call method " + pMethod + " on " + pObject + ": " + e,e);
} catch (IllegalAccessException e) {
throw new IllegalArgumentException("Cannot call method " + pMethod + " on " + pObject + ": " + e,e);
}
}
/**
* Get all resources from the classpath which are specified by the given path.
*
* @param pResource resource specification to use for lookup
* @return the list or URLs to loookup
*/
public static Set<String> getResources(String pResource) throws IOException {
List<ClassLoader> clls = findClassLoaders();
if (clls.size() != 0) {
Set<String> ret = new HashSet<String>();
for (ClassLoader cll : clls) {
Enumeration<URL> urlEnum = cll.getResources(pResource);
ret.addAll(extractUrlAsStringsFromEnumeration(urlEnum));
}
return ret;
} else {
return extractUrlAsStringsFromEnumeration(ClassLoader.getSystemResources(pResource));
}
}
private static Set<String> extractUrlAsStringsFromEnumeration(Enumeration<URL> urlEnum) {
Set<String> ret = new HashSet<String>();
while (urlEnum.hasMoreElements()) {
ret.add(urlEnum.nextElement().toExternalForm());
}
return ret;
}
// Lookup appropriate constructor
private static <T> Constructor<T> lookupConstructor(Class<T> clazz, Object[] pArguments) throws NoSuchMethodException {
Class[] argTypes = extractArgumentTypes(pArguments);
return clazz.getConstructor(argTypes);
}
private static Method extractMethod(String pMethod, Class<?> clazz, Object[] pArgs) throws NoSuchMethodException {
for (Method method : clazz.getMethods()) {
if (!method.getName().equals(pMethod)) {
continue;
}
Class[] parameters = method.getParameterTypes();
if (parametersMatch(parameters, pArgs)) {
return method;
}
}
throw new NoSuchMethodException("No " + pMethod + " on " + clazz + " with " + pArgs.length + " arguments found ");
}
private static Class[] extractArgumentTypes(Object[] pArguments) {
Class[] argTypes = new Class[pArguments.length];
int i = 0;
for (Object arg : pArguments) {
argTypes[i++] = arg.getClass();
}
return argTypes;
}
private static boolean parametersMatch(Class[] parameters, Object[] pArgs) {
if (parameters.length != pArgs.length) {
return false;
}
for (int i = 0; i < parameters.length; i++) {
if (pArgs[i] == null) {
continue;
}
Class argClass = pArgs[i].getClass();
Class paramClass = parameters[i];
if (!paramClass.isAssignableFrom(argClass)) {
if (checkForPrimitive(argClass, paramClass)) {
continue;
}
return false;
}
}
return true;
}
private static boolean checkForPrimitive(Class argClass, Class paramClass) {
return paramClass.isPrimitive() && PRIMITIVE_TO_OBJECT_MAP.get(paramClass.getName()) != null;
}
private static final Map<String,Class> PRIMITIVE_TO_OBJECT_MAP = new HashMap<String, Class>();
static {
PRIMITIVE_TO_OBJECT_MAP.put("int", Integer.TYPE);
PRIMITIVE_TO_OBJECT_MAP.put("long", Long.TYPE);
PRIMITIVE_TO_OBJECT_MAP.put("double", Double.TYPE);
PRIMITIVE_TO_OBJECT_MAP.put("float", Float.TYPE);
PRIMITIVE_TO_OBJECT_MAP.put("bool", Boolean.TYPE);
PRIMITIVE_TO_OBJECT_MAP.put("char", Character.TYPE);
PRIMITIVE_TO_OBJECT_MAP.put("byte", Byte.TYPE);
PRIMITIVE_TO_OBJECT_MAP.put("void", Void.TYPE);
PRIMITIVE_TO_OBJECT_MAP.put("short", Short.TYPE);
}
}