1 package org.apache.velocity.util; 2 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 22 import org.apache.velocity.context.InternalContextAdapter; 23 import org.apache.velocity.exception.MethodInvocationException; 24 import org.apache.velocity.exception.VelocityException; 25 import org.apache.velocity.runtime.parser.node.ASTMethod.MethodCacheKey; 26 import org.apache.velocity.runtime.parser.node.SimpleNode; 27 import org.apache.velocity.util.introspection.Info; 28 import org.apache.velocity.util.introspection.IntrospectionCacheData; 29 import org.apache.velocity.util.introspection.VelMethod; 30 31 import java.io.InputStream; 32 33 34 35 /** 36 * Simple utility functions for manipulating classes and resources 37 * from the classloader. 38 * 39 * @author <a href="mailto:wglass@apache.org">Will Glass-Husain</a> 40 * @version $Id$ 41 * @since 1.5 42 */ 43 public class ClassUtils { 44 45 /** 46 * Utility class; cannot be instantiated. 47 */ ClassUtils()48 private ClassUtils() 49 { 50 } 51 52 /** 53 * Return the specified class. Checks the ThreadContext classloader first, 54 * then uses the System classloader. Should replace all calls to 55 * <code>Class.forName( claz )</code> (which only calls the System class 56 * loader) when the class might be in a different classloader (e.g. in a 57 * webapp). 58 * 59 * @param clazz the name of the class to instantiate 60 * @return the requested Class object 61 * @throws ClassNotFoundException 62 */ getClass(String clazz)63 public static Class<?> getClass(String clazz) throws ClassNotFoundException 64 { 65 /** 66 * Use the Thread context classloader if possible 67 */ 68 ClassLoader loader = Thread.currentThread().getContextClassLoader(); 69 if (loader != null) 70 { 71 try 72 { 73 return Class.forName(clazz, true, loader); 74 } 75 catch (ClassNotFoundException E) 76 { 77 /* 78 * If not found with ThreadContext loader, fall thru to 79 * try System classloader below (works around bug in ant). 80 */ 81 } 82 } 83 /* 84 * Thread context classloader isn't working out, so use system loader. 85 */ 86 return Class.forName(clazz); 87 } 88 89 /** 90 * Return a new instance of the given class. Checks the ThreadContext 91 * classloader first, then uses the System classloader. Should replace all 92 * calls to <code>Class.forName( claz ).newInstance()</code> (which only 93 * calls the System class loader) when the class might be in a different 94 * classloader (e.g. in a webapp). 95 * 96 * @param clazz the name of the class to instantiate 97 * @return an instance of the specified class 98 * @throws ClassNotFoundException 99 * @throws IllegalAccessException 100 * @throws InstantiationException 101 */ getNewInstance(String clazz)102 public static Object getNewInstance(String clazz) 103 throws ClassNotFoundException,IllegalAccessException,InstantiationException 104 { 105 return getClass(clazz).newInstance(); 106 } 107 108 /** 109 * Finds a resource with the given name. Checks the Thread Context 110 * classloader, then uses the System classloader. Should replace all 111 * calls to <code>Class.getResourceAsString</code> when the resource 112 * might come from a different classloader. (e.g. a webapp). 113 * @param claz Class to use when getting the System classloader (used if no Thread 114 * Context classloader available or fails to get resource). 115 * @param name name of the resource 116 * @return InputStream for the resource. 117 */ getResourceAsStream(Class<?> claz, String name)118 public static InputStream getResourceAsStream(Class<?> claz, String name) 119 { 120 InputStream result = null; 121 122 /* 123 * remove leading slash so path will work with classes in a JAR file 124 */ 125 while (name.startsWith("/")) 126 { 127 name = name.substring(1); 128 } 129 130 ClassLoader classLoader = Thread.currentThread() 131 .getContextClassLoader(); 132 133 if (classLoader == null) 134 { 135 classLoader = claz.getClassLoader(); 136 result = classLoader.getResourceAsStream( name ); 137 } 138 else 139 { 140 result= classLoader.getResourceAsStream( name ); 141 142 /* 143 * for compatibility with texen / ant tasks, fall back to 144 * old method when resource is not found. 145 */ 146 147 if (result == null) 148 { 149 classLoader = claz.getClassLoader(); 150 if (classLoader != null) 151 result = classLoader.getResourceAsStream( name ); 152 } 153 } 154 155 return result; 156 157 } 158 159 /** 160 * Lookup a VelMethod object given the method signature that is specified in 161 * the passed in parameters. This method first searches the cache, if not found in 162 * the cache then uses reflections to inspect Object o, for the given method. 163 * @param methodName Name of method 164 * @param params Array of objects that are parameters to the method 165 * @param paramClasses Array of Classes corresponding to the types in params. 166 * @param o Object to introspect for the given method. 167 * @param context Context from which the method cache is acquired 168 * @param node ASTNode, used for error reporting. 169 * @param strictRef If no method is found, throw an exception, never return null in this case 170 * @return VelMethod object if the object is found, null if not matching method is found 171 */ getMethod(String methodName, Object[] params, Class<?>[] paramClasses, Object o, InternalContextAdapter context, SimpleNode node, boolean strictRef)172 public static VelMethod getMethod(String methodName, Object[] params, 173 Class<?>[] paramClasses, Object o, InternalContextAdapter context, 174 SimpleNode node, boolean strictRef) 175 { 176 VelMethod method = null; 177 try 178 { 179 /* 180 * check the cache 181 */ 182 boolean classObject = (o instanceof Class); 183 MethodCacheKey mck = new MethodCacheKey(methodName, paramClasses, classObject); 184 IntrospectionCacheData icd = context.icacheGet(mck); 185 Class<?> clazz = classObject ? (Class<?>)o : o.getClass(); 186 187 /* 188 * like ASTIdentifier, if we have cache information, and the Class of 189 * Object o is the same as that in the cache, we are safe. 190 */ 191 if (icd != null && icd.contextData == clazz) 192 { 193 /* 194 * get the method from the cache 195 */ 196 method = (VelMethod) icd.thingy; 197 } 198 else 199 { 200 /* 201 * otherwise, do the introspection, and then cache it 202 */ 203 method = node.getRuntimeServices().getUberspect().getMethod(o, methodName, params, 204 new Info(node.getTemplateName(), node.getLine(), node.getColumn())); 205 206 if (method != null) 207 { 208 icd = new IntrospectionCacheData(); 209 icd.contextData = clazz; 210 icd.thingy = method; 211 context.icachePut(mck, icd); 212 } 213 } 214 215 /* 216 * if we still haven't gotten the method, either we are calling a method 217 * that doesn't exist (which is fine...) or I screwed it up. 218 */ 219 if (method == null) 220 { 221 if (strictRef) 222 { 223 // Create a parameter list for the exception error message 224 StringBuilder plist = new StringBuilder(); 225 for (int i = 0; i < params.length; i++) 226 { 227 Class<?> param = paramClasses[i]; 228 plist.append(param == null ? "null" : param.getName()); 229 if (i < params.length - 1) 230 plist.append(", "); 231 } 232 throw new MethodInvocationException("Object '" 233 + o.getClass().getName() + "' does not contain method " 234 + methodName + "(" + plist + ")", null, methodName, node 235 .getTemplateName(), node.getLine(), node.getColumn()); 236 } 237 else 238 { 239 return null; 240 } 241 } 242 } 243 catch (MethodInvocationException mie) 244 { 245 /* 246 * this can come from the doIntrospection(), as the arg values are 247 * evaluated to find the right method signature. We just want to propagate 248 * it here, not do anything fancy 249 */ 250 throw mie; 251 } 252 catch (RuntimeException e) 253 { 254 /** 255 * pass through application level runtime exceptions 256 */ 257 throw e; 258 } 259 catch (Exception e) 260 { 261 /* 262 * can come from the doIntropection() also, from Introspector 263 */ 264 String msg = "ASTMethod.execute() : exception from introspection"; 265 throw new VelocityException(msg, e); 266 } 267 268 return method; 269 } 270 271 } 272