• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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