View Javadoc

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 }