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 java.io.InputStream;
23
24 import org.apache.velocity.context.InternalContextAdapter;
25 import org.apache.velocity.exception.MethodInvocationException;
26 import org.apache.velocity.exception.VelocityException;
27 import org.apache.velocity.runtime.parser.node.SimpleNode;
28 import org.apache.velocity.runtime.parser.node.ASTMethod.MethodCacheKey;
29 import org.apache.velocity.util.introspection.Info;
30 import org.apache.velocity.util.introspection.IntrospectionCacheData;
31 import org.apache.velocity.util.introspection.VelMethod;
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: ClassUtils.java 898032 2010-01-11 19:51:03Z nbubna $
41 * @since 1.5
42 */
43 public class ClassUtils {
44
45 /**
46 * Utility class; cannot be instantiated.
47 */
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 */
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 */
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 */
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 coresponding 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 aquirred
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 */
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 MethodCacheKey mck = new MethodCacheKey(methodName, paramClasses);
183 IntrospectionCacheData icd = context.icacheGet(mck);
184
185 /*
186 * like ASTIdentifier, if we have cache information, and the Class of
187 * Object o is the same as that in the cache, we are safe.
188 */
189 if (icd != null && (o != null && icd.contextData == o.getClass()))
190 {
191
192 /*
193 * get the method from the cache
194 */
195 method = (VelMethod) icd.thingy;
196 }
197 else
198 {
199 /*
200 * otherwise, do the introspection, and then cache it
201 */
202 method = node.getRuntimeServices().getUberspect().getMethod(o, methodName, params,
203 new Info(node.getTemplateName(), node.getLine(), node.getColumn()));
204
205 if ((method != null) && (o != null))
206 {
207 icd = new IntrospectionCacheData();
208 icd.contextData = o.getClass();
209 icd.thingy = method;
210
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 StringBuffer plist = new StringBuffer();
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 }
244 catch (MethodInvocationException mie)
245 {
246 /*
247 * this can come from the doIntrospection(), as the arg values are
248 * evaluated to find the right method signature. We just want to propogate
249 * it here, not do anything fancy
250 */
251
252 throw mie;
253 }
254 catch (RuntimeException e)
255 {
256 /**
257 * pass through application level runtime exceptions
258 */
259 throw e;
260 }
261 catch (Exception e)
262 {
263 /*
264 * can come from the doIntropection() also, from Introspector
265 */
266 String msg = "ASTMethod.execute() : exception from introspection";
267 throw new VelocityException(msg, e);
268 }
269
270 return method;
271 }
272
273 }