diff options
Diffstat (limited to 'velocity-engine-core/src/main/java/org/apache/velocity/util/ClassUtils.java')
-rw-r--r-- | velocity-engine-core/src/main/java/org/apache/velocity/util/ClassUtils.java | 271 |
1 files changed, 271 insertions, 0 deletions
diff --git a/velocity-engine-core/src/main/java/org/apache/velocity/util/ClassUtils.java b/velocity-engine-core/src/main/java/org/apache/velocity/util/ClassUtils.java new file mode 100644 index 00000000..34ddf556 --- /dev/null +++ b/velocity-engine-core/src/main/java/org/apache/velocity/util/ClassUtils.java @@ -0,0 +1,271 @@ +package org.apache.velocity.util; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import org.apache.velocity.context.InternalContextAdapter; +import org.apache.velocity.exception.MethodInvocationException; +import org.apache.velocity.exception.VelocityException; +import org.apache.velocity.runtime.parser.node.ASTMethod.MethodCacheKey; +import org.apache.velocity.runtime.parser.node.SimpleNode; +import org.apache.velocity.util.introspection.Info; +import org.apache.velocity.util.introspection.IntrospectionCacheData; +import org.apache.velocity.util.introspection.VelMethod; + +import java.io.InputStream; + + + +/** + * Simple utility functions for manipulating classes and resources + * from the classloader. + * + * @author <a href="mailto:wglass@apache.org">Will Glass-Husain</a> + * @version $Id$ + * @since 1.5 + */ +public class ClassUtils { + + /** + * Utility class; cannot be instantiated. + */ + private ClassUtils() + { + } + + /** + * Return the specified class. Checks the ThreadContext classloader first, + * then uses the System classloader. Should replace all calls to + * <code>Class.forName( claz )</code> (which only calls the System class + * loader) when the class might be in a different classloader (e.g. in a + * webapp). + * + * @param clazz the name of the class to instantiate + * @return the requested Class object + * @throws ClassNotFoundException + */ + public static Class<?> getClass(String clazz) throws ClassNotFoundException + { + /** + * Use the Thread context classloader if possible + */ + ClassLoader loader = Thread.currentThread().getContextClassLoader(); + if (loader != null) + { + try + { + return Class.forName(clazz, true, loader); + } + catch (ClassNotFoundException E) + { + /* + * If not found with ThreadContext loader, fall thru to + * try System classloader below (works around bug in ant). + */ + } + } + /* + * Thread context classloader isn't working out, so use system loader. + */ + return Class.forName(clazz); + } + + /** + * Return a new instance of the given class. Checks the ThreadContext + * classloader first, then uses the System classloader. Should replace all + * calls to <code>Class.forName( claz ).newInstance()</code> (which only + * calls the System class loader) when the class might be in a different + * classloader (e.g. in a webapp). + * + * @param clazz the name of the class to instantiate + * @return an instance of the specified class + * @throws ClassNotFoundException + * @throws IllegalAccessException + * @throws InstantiationException + */ + public static Object getNewInstance(String clazz) + throws ClassNotFoundException,IllegalAccessException,InstantiationException + { + return getClass(clazz).newInstance(); + } + + /** + * Finds a resource with the given name. Checks the Thread Context + * classloader, then uses the System classloader. Should replace all + * calls to <code>Class.getResourceAsString</code> when the resource + * might come from a different classloader. (e.g. a webapp). + * @param claz Class to use when getting the System classloader (used if no Thread + * Context classloader available or fails to get resource). + * @param name name of the resource + * @return InputStream for the resource. + */ + public static InputStream getResourceAsStream(Class<?> claz, String name) + { + InputStream result = null; + + /* + * remove leading slash so path will work with classes in a JAR file + */ + while (name.startsWith("/")) + { + name = name.substring(1); + } + + ClassLoader classLoader = Thread.currentThread() + .getContextClassLoader(); + + if (classLoader == null) + { + classLoader = claz.getClassLoader(); + result = classLoader.getResourceAsStream( name ); + } + else + { + result= classLoader.getResourceAsStream( name ); + + /* + * for compatibility with texen / ant tasks, fall back to + * old method when resource is not found. + */ + + if (result == null) + { + classLoader = claz.getClassLoader(); + if (classLoader != null) + result = classLoader.getResourceAsStream( name ); + } + } + + return result; + + } + + /** + * Lookup a VelMethod object given the method signature that is specified in + * the passed in parameters. This method first searches the cache, if not found in + * the cache then uses reflections to inspect Object o, for the given method. + * @param methodName Name of method + * @param params Array of objects that are parameters to the method + * @param paramClasses Array of Classes corresponding to the types in params. + * @param o Object to introspect for the given method. + * @param context Context from which the method cache is acquired + * @param node ASTNode, used for error reporting. + * @param strictRef If no method is found, throw an exception, never return null in this case + * @return VelMethod object if the object is found, null if not matching method is found + */ + public static VelMethod getMethod(String methodName, Object[] params, + Class<?>[] paramClasses, Object o, InternalContextAdapter context, + SimpleNode node, boolean strictRef) + { + VelMethod method = null; + try + { + /* + * check the cache + */ + boolean classObject = (o instanceof Class); + MethodCacheKey mck = new MethodCacheKey(methodName, paramClasses, classObject); + IntrospectionCacheData icd = context.icacheGet(mck); + Class<?> clazz = classObject ? (Class<?>)o : o.getClass(); + + /* + * like ASTIdentifier, if we have cache information, and the Class of + * Object o is the same as that in the cache, we are safe. + */ + if (icd != null && icd.contextData == clazz) + { + /* + * get the method from the cache + */ + method = (VelMethod) icd.thingy; + } + else + { + /* + * otherwise, do the introspection, and then cache it + */ + method = node.getRuntimeServices().getUberspect().getMethod(o, methodName, params, + new Info(node.getTemplateName(), node.getLine(), node.getColumn())); + + if (method != null) + { + icd = new IntrospectionCacheData(); + icd.contextData = clazz; + icd.thingy = method; + context.icachePut(mck, icd); + } + } + + /* + * if we still haven't gotten the method, either we are calling a method + * that doesn't exist (which is fine...) or I screwed it up. + */ + if (method == null) + { + if (strictRef) + { + // Create a parameter list for the exception error message + StringBuilder plist = new StringBuilder(); + for (int i = 0; i < params.length; i++) + { + Class<?> param = paramClasses[i]; + plist.append(param == null ? "null" : param.getName()); + if (i < params.length - 1) + plist.append(", "); + } + throw new MethodInvocationException("Object '" + + o.getClass().getName() + "' does not contain method " + + methodName + "(" + plist + ")", null, methodName, node + .getTemplateName(), node.getLine(), node.getColumn()); + } + else + { + return null; + } + } + } + catch (MethodInvocationException mie) + { + /* + * this can come from the doIntrospection(), as the arg values are + * evaluated to find the right method signature. We just want to propagate + * it here, not do anything fancy + */ + throw mie; + } + catch (RuntimeException e) + { + /** + * pass through application level runtime exceptions + */ + throw e; + } + catch (Exception e) + { + /* + * can come from the doIntropection() also, from Introspector + */ + String msg = "ASTMethod.execute() : exception from introspection"; + throw new VelocityException(msg, e); + } + + return method; + } + +} |