Java tutorial
/* Milyn - Copyright (C) 2006 - 2010 This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (version 2.1) as published by the Free Software Foundation. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details: http://www.gnu.org/licenses/lgpl.txt */ package org.dhatim.util; import java.io.BufferedReader; import java.io.Closeable; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.lang.annotation.Annotation; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Proxy; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.net.URL; import java.net.URLConnection; import java.util.*; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.dhatim.assertion.AssertArgument; import org.dhatim.classpath.InstanceOfFilter; import org.dhatim.classpath.IsAnnotationPresentFilter; import org.dhatim.classpath.Scanner; /** * Utility methods to aid in class/resource loading. * * @author Kevin Conner */ public class ClassUtil { private static Log logger = LogFactory.getLog(ClassUtil.class); private static final Map<String, Class> primitives; static { primitives = new HashMap<String, Class>(); primitives.put("int", Integer.TYPE); primitives.put("long", Long.TYPE); primitives.put("boolean", Boolean.TYPE); primitives.put("float", Float.TYPE); primitives.put("double", Double.TYPE); primitives.put("char", Character.TYPE); primitives.put("byte", Byte.TYPE); primitives.put("short", Short.TYPE); } /** * Load the specified class. * * @param className * The name of the class to load. * @param caller * The class of the caller. * @return The specified class. * @throws ClassNotFoundException * If the class cannot be found. */ public static Class forName(final String className, final Class caller) throws ClassNotFoundException { final ClassLoader threadClassLoader = Thread.currentThread().getContextClassLoader(); Class primitiveClass = primitives.get(className); if (primitiveClass != null) { return primitiveClass; } if (threadClassLoader != null) { try { return threadClassLoader.loadClass(className); } catch (final ClassNotFoundException cnfe) { } // ignore } ClassLoader classLoader = caller.getClassLoader(); if (classLoader != null) { try { return classLoader.loadClass(className); } catch (final ClassNotFoundException cnfe) { } // ignore } return Class.forName(className, true, ClassLoader.getSystemClassLoader()); } /** * Get the specified resource as a stream. * * @param resourceName * The name of the class to load. * @param caller * The class of the caller. * @return The input stream for the resource or null if not found. */ public static InputStream getResourceAsStream(final String resourceName, final Class caller) { final String resource; if (!resourceName.startsWith("/")) { final Package callerPackage = caller.getPackage(); if (callerPackage != null) { resource = callerPackage.getName().replace('.', '/') + '/' + resourceName; } else { resource = resourceName; } return getResourceAsStream(resource, caller.getClassLoader()); } else { return getResourceAsStream(resourceName, caller.getClassLoader()); } } /** * Get the specified resource as a stream. * * @param resourceName The name of the class to load. * @param classLoader The ClassLoader to use, if the resource is not located via the * Thread context ClassLoader. * @return The input stream for the resource or null if not found. */ public static InputStream getResourceAsStream(final String resourceName, final ClassLoader classLoader) { final ClassLoader threadClassLoader = Thread.currentThread().getContextClassLoader(); final String resource; if (resourceName.startsWith("/")) { resource = resourceName.substring(1); } else { resource = resourceName; } if (threadClassLoader != null) { final InputStream is = threadClassLoader.getResourceAsStream(resource); if (is != null) { return is; } } if (classLoader != null) { final InputStream is = classLoader.getResourceAsStream(resource); if (is != null) { return is; } } return ClassLoader.getSystemResourceAsStream(resource); } public static List<URL> getResources(String resourcePath, Class<?> caller) throws IOException { return getResources(resourcePath, caller.getClassLoader()); } public static List<URL> getResources(String resourcePath, ClassLoader callerClassLoader) throws IOException { Set<URL> resources = new LinkedHashSet<URL>(); if (resourcePath.startsWith("/")) { resourcePath = resourcePath.substring(1); } ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader(); if (contextClassLoader != null) { resources.addAll(CollectionsUtil.toList(contextClassLoader.getResources(resourcePath))); } if (callerClassLoader != null) { resources.addAll(CollectionsUtil.toList(callerClassLoader.getResources(resourcePath))); } return new ArrayList<URL>(resources); } public static List<Class> findInstancesOf(final Class type, String[] igrnoreList, String[] includeList) { InstanceOfFilter filter = new InstanceOfFilter(type, igrnoreList, includeList); return findInstancesOf(type, filter); } public static List<Class> findInstancesOf(final Class type) { InstanceOfFilter filter = new InstanceOfFilter(type); return findInstancesOf(type, filter); } private static List<Class> findInstancesOf(Class type, InstanceOfFilter filter) { Scanner scanner = new Scanner(filter); try { long startTime = System.currentTimeMillis(); scanner.scanClasspath(Thread.currentThread().getContextClassLoader()); logger.debug("Scanned classpath for instances of '" + type.getName() + "'. Found " + filter.getClasses().size() + " matches. Scan took " + (System.currentTimeMillis() - startTime) + "ms."); } catch (IOException e) { throw new RuntimeException("Failed to search classspath for instances of '" + type.getName() + "'.", e); } return filter.getClasses(); } public static List<Class> findAnnotatedWith(Class<? extends Annotation> type, String[] igrnoreList, String[] includeList) { IsAnnotationPresentFilter filter = new IsAnnotationPresentFilter(type, igrnoreList, includeList); return findAnnotatedWith(type, filter); } public static List<Class> findAnnotatedWith(Class<? extends Annotation> type) { IsAnnotationPresentFilter filter = new IsAnnotationPresentFilter(type); return findAnnotatedWith(type, filter); } private static List<Class> findAnnotatedWith(Class<? extends Annotation> type, IsAnnotationPresentFilter filter) { Scanner scanner = new Scanner(filter); try { long startTime = System.currentTimeMillis(); scanner.scanClasspath(Thread.currentThread().getContextClassLoader()); logger.debug("Scanned classpath for class annotated with annotation '" + type.getName() + "'. Found " + filter.getClasses().size() + " matches. Scan took " + (System.currentTimeMillis() - startTime) + "ms."); } catch (IOException e) { throw new RuntimeException( "Failed to search classspath for class annotated with annotation '" + type.getName() + "'.", e); } return filter.getClasses(); } public static Object newProxyInstance(Class[] classes, InvocationHandler handler) { final ClassLoader threadClassLoader = Thread.currentThread().getContextClassLoader(); if (threadClassLoader != null) { return Proxy.newProxyInstance(threadClassLoader, classes, handler); } else { return Proxy.newProxyInstance(ClassUtil.class.getClassLoader(), classes, handler); } } /** * Will try to create a List of classes that are listed * in the passed in file. * The fileName is expected to be found on the classpath. * * @param fileName The name of the file containing the list of classes, * one class name per line. * @param instanceOf The instanceof filter. * @return List<Class<T>> list of the classes contained in the file. */ public static <T> List<Class<T>> getClasses(final String fileName, Class<T> instanceOf) { AssertArgument.isNotNull(fileName, "fileName"); AssertArgument.isNotNull(instanceOf, "instanceOf"); long start = System.currentTimeMillis(); List<Class<T>> classes = new ArrayList<Class<T>>(); List<URL> cpURLs; int resCount = 0; try { cpURLs = getResources(fileName, ClassUtil.class); } catch (IOException e) { throw new RuntimeException("Error getting resource URLs for resource : " + fileName, e); } for (URL url : cpURLs) { addClasses(url, instanceOf, classes); resCount++; } logger.debug("Loaded " + classes.size() + " classes from " + resCount + " URLs through class list file " + fileName + ". Process took " + (System.currentTimeMillis() - start) + "ms. Turn on debug logging for more info."); return classes; } private static <T> void addClasses(URL url, Class<T> instanceOf, List<Class<T>> classes) { InputStream ins = null; BufferedReader br = null; try { String className; int count = 0; // Get the input stream from the connection. Need to set the defaultUseCaches URLConnection connection = url.openConnection(); connection.setUseCaches(true); ins = connection.getInputStream(); br = new BufferedReader(new InputStreamReader(ins)); while ((className = br.readLine()) != null) { Class clazz; className = className.trim(); // Ignore blank lines and lines that start with a hash... if (className.equals("") || className.startsWith("#")) { continue; } try { clazz = forName(className, ClassUtil.class); } catch (ClassNotFoundException e) { logger.debug("Failed to load class '" + className + "'. Class not found.", e); continue; } if (instanceOf.isAssignableFrom(clazz)) { if (!contains(clazz.getName(), classes)) { classes.add(clazz); } logger.debug("Adding " + className + " to list of classes"); count++; } else { logger.debug("Not adding class '" + clazz.getName() + "' to list. Class does not implement/extend '" + instanceOf.getName() + "'."); } } logger.debug("Loaded '" + count + "' classes listed in '" + url + "'."); } catch (IOException e) { throw new RuntimeException("Failed to read from file : " + url, e); } finally { close(br); close(ins); } } private static <T> boolean contains(String name, List<Class<T>> classes) { for (Class<T> aClass : classes) { if (aClass.getName().equals(name)) { logger.debug("Class '" + name + "' already found on classpath. Not adding to list."); return true; } } return false; } private static void close(final Closeable closable) { if (closable != null) { try { closable.close(); } catch (IOException e) { logger.debug("Exception while trying to close : " + closable, e); } } } public static String toFilePath(Package aPackage) { return "/" + aPackage.getName().replace('.', '/'); } /** * Checks if the class in the first parameter is assignable * to one of the classes in the second or any later parameter. * * @param toFind * @param classes * @return */ public static boolean containsAssignableClass(final Class<?> toFind, final Class<?>... classes) { return indexOffFirstAssignableClass(toFind, classes) != -1; } public static <U> void setField(Field field, U instance, Object value) throws IllegalAccessException { boolean isAccessible = field.isAccessible(); if (!isAccessible) { field.setAccessible(true); } try { field.set(instance, value); } finally { field.setAccessible(isAccessible); } } public static <U> Object getField(Field field, U instance) throws IllegalAccessException { boolean isAccessible = field.isAccessible(); if (!isAccessible) { field.setAccessible(true); } try { return field.get(instance); } finally { field.setAccessible(isAccessible); } } public static List<Field> getAnnotatedFields(Class runtimeClass, Class<? extends Annotation> annotationClass) { List<Field> streamWriterFields = new ArrayList<Field>(); getAnnotatedFields(runtimeClass, streamWriterFields, annotationClass); return streamWriterFields; } private static void getAnnotatedFields(Class runtimeClass, List<Field> annotatedFields, Class<? extends Annotation> annotationClass) { Field[] fields = runtimeClass.getDeclaredFields(); // Work back up the Inheritance tree first... Class superClass = runtimeClass.getSuperclass(); if (superClass != null) { getAnnotatedFields(superClass, annotatedFields, annotationClass); } for (Field field : fields) { if (field.isAnnotationPresent(annotationClass)) { annotatedFields.add(field); } } } /** * * @param toFind * @param classes * @return */ public static int indexOffFirstAssignableClass(final Class<?> toFind, final Class<?>... classes) { for (int i = 0; i < classes.length; i++) { final Class<?> cls = classes[i]; if (cls.isAssignableFrom(toFind)) { return i; } } return -1; } public static String toSetterName(String property) { StringBuffer setterName = new StringBuffer(); // Add the property string to the buffer... setterName.append(property); // Uppercase the first character... setterName.setCharAt(0, Character.toUpperCase(property.charAt(0))); // Prefix with "set"... setterName.insert(0, "set"); return setterName.toString(); } public static String toGetterName(String property) { StringBuffer getterName = new StringBuffer(); // Add the property string to the buffer... getterName.append(property); // Uppercase the first character... getterName.setCharAt(0, Character.toUpperCase(property.charAt(0))); // Prefix with "get"... getterName.insert(0, "get"); return getterName.toString(); } public static String toIsGetterName(String property) { StringBuffer getterName = new StringBuffer(); // Add the property string to the buffer... getterName.append(property); // Uppercase the first character... getterName.setCharAt(0, Character.toUpperCase(property.charAt(0))); // Prefix with "is"... getterName.insert(0, "is"); return getterName.toString(); } public static Method getSetterMethod(String setterName, Object bean, Class<?> setterParamType) { return getSetterMethod(setterName, bean.getClass(), setterParamType); } public static Method getSetterMethod(String setterName, Class beanclass, Class<?> setterParamType) { Method[] methods = beanclass.getMethods(); for (Method method : methods) { if (method.getName().equals(setterName)) { Class<?>[] params = method.getParameterTypes(); if (params != null && params.length == 1 && params[0].isAssignableFrom(setterParamType)) { return method; } } } return null; } public static Method getSetterMethodByProperty(String propertyName, Class<?> beanClass, Class<?> setterParamType) { return getSetterMethod(toSetterName(propertyName), beanClass, setterParamType); } public static Method getGetterMethod(String getterName, Object bean, Class<?> returnType) { return getGetterMethod(getterName, bean.getClass(), returnType); } public static Method getGetterMethod(String getterName, Class beanclass, Class<?> returnType) { Method[] methods = beanclass.getMethods(); for (Method method : methods) { if (method.getName().equals(getterName)) { if (returnType != null) { if (method.getReturnType().isAssignableFrom(returnType)) { return method; } } else { return method; } } } return null; } public static Method getGetterMethodByProperty(String propertyName, Class<?> beanClass, Class<?> returnType) { Method getter = getGetterMethod(toGetterName(propertyName), beanClass, returnType); if (getter == null) { getter = getGetterMethod(toIsGetterName(propertyName), beanClass, returnType); } return getter; } }