1 /* 2 * $Id: ClassUtil.java 791161 2009-07-04 18:53:36Z apetrelli $ 3 * 4 * Licensed to the Apache Software Foundation (ASF) under one 5 * or more contributor license agreements. See the NOTICE file 6 * distributed with this work for additional information 7 * regarding copyright ownership. The ASF licenses this file 8 * to you under the Apache License, Version 2.0 (the 9 * "License"); you may not use this file except in compliance 10 * with the License. You may obtain a copy of the License at 11 * 12 * http://www.apache.org/licenses/LICENSE-2.0 13 * 14 * Unless required by applicable law or agreed to in writing, 15 * software distributed under the License is distributed on an 16 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17 * KIND, either express or implied. See the License for the 18 * specific language governing permissions and limitations 19 * under the License. 20 */ 21 package org.apache.tiles.reflect; 22 23 import java.beans.BeanInfo; 24 import java.beans.Introspector; 25 import java.beans.PropertyDescriptor; 26 import java.lang.reflect.InvocationTargetException; 27 import java.lang.reflect.Method; 28 import java.util.Map; 29 30 import org.slf4j.Logger; 31 import org.slf4j.LoggerFactory; 32 33 34 /** 35 * Utilities to work with dynamic class loading and instantiation. 36 * 37 * @version $Rev: 791161 $ $Date: 2009-07-04 20:53:36 +0200 (sab, 04 lug 2009) $ 38 * @since 2.0.7 39 */ 40 public final class ClassUtil { 41 42 /** 43 * Constructor, private to avoid instantiation. 44 */ 45 private ClassUtil() { 46 } 47 48 /** 49 * Returns the class and casts it to the correct subclass.<br> 50 * It tries to use the thread's current classloader first and, if it does 51 * not succeed, uses the classloader of ClassUtil. 52 * 53 * @param <T> The subclass to use. 54 * @param className The name of the class to load. 55 * @param baseClass The base class to subclass to. 56 * @return The loaded class. 57 * @throws ClassNotFoundException If the class has not been found. 58 * @since 2.1.3 59 */ 60 public static <T> Class<? extends T> getClass(String className, 61 Class<T> baseClass) throws ClassNotFoundException { 62 ClassLoader classLoader = Thread.currentThread() 63 .getContextClassLoader(); 64 if (classLoader == null) { 65 classLoader = ClassUtil.class.getClassLoader(); 66 } 67 return Class.forName(className, true, classLoader) 68 .asSubclass(baseClass); 69 } 70 71 /** 72 * Returns an instance of the given class name, by calling the default 73 * constructor. 74 * 75 * @param className The class name to load and to instantiate. 76 * @return The new instance of the class name. 77 * @throws CannotInstantiateObjectException If something goes wrong during 78 * instantiation. 79 * @since 2.0.7 80 */ 81 public static Object instantiate(String className) { 82 return instantiate(className, false); 83 } 84 85 /** 86 * Returns an instance of the given class name, by calling the default 87 * constructor. 88 * 89 * @param className The class name to load and to instantiate. 90 * @param returnNull If <code>true</code>, if the class is not found it 91 * returns <code>true</code>, otherwise it throws a 92 * <code>TilesException</code>. 93 * @return The new instance of the class name. 94 * @throws CannotInstantiateObjectException If something goes wrong during instantiation. 95 * @since 2.0.7 96 */ 97 public static Object instantiate(String className, boolean returnNull) { 98 ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); 99 if (classLoader == null) { 100 classLoader = ClassUtil.class.getClassLoader(); 101 } 102 try { 103 Class<? extends Object> namedClass = getClass(className, Object.class); 104 return namedClass.newInstance(); 105 } catch (ClassNotFoundException e) { 106 if (returnNull) { 107 return null; 108 } 109 throw new CannotInstantiateObjectException( 110 "Unable to resolve factory class: '" + className + "'", e); 111 } catch (IllegalAccessException e) { 112 throw new CannotInstantiateObjectException( 113 "Unable to access factory class: '" + className + "'", e); 114 } catch (InstantiationException e) { 115 throw new CannotInstantiateObjectException( 116 "Unable to instantiate factory class: '" 117 + className 118 + "'. Make sure that this class has a default constructor", 119 e); 120 } 121 } 122 123 /** 124 * Gets a method and forces it to be accessible, even if it is not. 125 * 126 * @param clazz The class from which the method will be got. 127 * @param methodName The name of the method. 128 * @param parameterTypes The parameter types that the method must match. 129 * @return The method, if it is found. 130 * @since 2.0.7 131 */ 132 public static Method getForcedAccessibleMethod(Class<?> clazz, 133 String methodName, Class<?>... parameterTypes) { 134 Method method; 135 try { 136 method = clazz.getMethod(methodName, parameterTypes); 137 } catch (SecurityException e) { 138 throw new CannotAccessMethodException("Cannot access method '" 139 + methodName + "' in class '" + clazz.getName() 140 + "' for security reasons", e); 141 } catch (NoSuchMethodException e) { 142 throw new CannotAccessMethodException("The method '" 143 + methodName + "' in class '" + clazz.getName() 144 + "' does not exist", e); 145 } 146 if (!method.isAccessible()) { 147 method.setAccessible(true); 148 } 149 return method; 150 } 151 152 /** 153 * Invokes a method, masking with a runtime exception all the exceptions. 154 * 155 * @param obj The object from which a method will be called. 156 * @param method The method to call. 157 * @param args The arguments of the method. 158 * @return The object returned, if the method is not "void". 159 * @since 2.0.7 160 */ 161 public static Object invokeMethod(Object obj, Method method, Object... args) { 162 try { 163 return method.invoke(obj, args); 164 } catch (IllegalArgumentException e) { 165 throw new CannotAccessMethodException("The arguments for '" 166 + method.getName() + "' in class '" 167 + obj.getClass().getName() + "' are not valid", e); 168 } catch (IllegalAccessException e) { 169 throw new CannotAccessMethodException("Cannot access '" 170 + method.getName() + "' in class '" 171 + obj.getClass().getName() + "'", e); 172 } catch (InvocationTargetException e) { 173 throw new CannotAccessMethodException( 174 "An exception has been thrown inside '" + method.getName() 175 + "' in class '" + obj.getClass().getName() + "'", e); 176 } 177 } 178 179 /** 180 * Collects bean infos from a class and filling a list. 181 * 182 * @param clazz The class to be inspected. 183 * @param name2descriptor The map in the form: name of the property -> 184 * descriptor. 185 * @since 2.2.0 186 */ 187 public static void collectBeanInfo(Class<?> clazz, 188 Map<String, PropertyDescriptor> name2descriptor) { 189 Logger log = LoggerFactory.getLogger(ClassUtil.class); 190 BeanInfo info = null; 191 try { 192 info = Introspector.getBeanInfo(clazz); 193 } catch (Exception ex) { 194 if (log.isDebugEnabled()) { 195 log.debug("Cannot inspect class " + clazz, ex); 196 } 197 } 198 if (info == null) { 199 return; 200 } 201 for (PropertyDescriptor pd : info.getPropertyDescriptors()) { 202 pd.setValue("type", pd.getPropertyType()); 203 pd.setValue("resolvableAtDesignTime", Boolean.TRUE); 204 name2descriptor.put(pd.getName(), pd); 205 } 206 } 207 }