You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@velocity.apache.org by "Henning Schmiedehausen (JIRA)" <ji...@apache.org> on 2006/11/04 21:08:18 UTC

[jira] Assigned: (VELOCITY-414) Extend the MethodInvocation exception to be able to give the velocity macro writer a usefull error page

     [ http://issues.apache.org/jira/browse/VELOCITY-414?page=all ]

Henning Schmiedehausen reassigned VELOCITY-414:
-----------------------------------------------

    Assignee: Henning Schmiedehausen

> Extend the MethodInvocation exception to be able to give the velocity macro writer a usefull error page
> -------------------------------------------------------------------------------------------------------
>
>                 Key: VELOCITY-414
>                 URL: http://issues.apache.org/jira/browse/VELOCITY-414
>             Project: Velocity
>          Issue Type: Improvement
>          Components: Source
>    Affects Versions: 1.5, 1.4
>            Reporter: Matthijs Lambooy
>         Assigned To: Henning Schmiedehausen
>             Fix For: 1.5
>
>
> We use velocity macros that invoke methods in a java written web engine.
> When an invoked method fails because of an exception,  it is not
> possible to use the
> MethodInvocation exception to give the velocity macro writer a usefull
> error page since the MethodInvocation Exception has not cause set.
> So to be short the reason why the method invocation failed can not be
> routed back to the veloticy macro writer on a running system.
> I extended the MethodInvocationException.java and the method execute in
> ASTMethod.java 
> proposed changes in MethodInvocationException.java : 
> ===============================================
> package org.apache.velocity.exception; 
> import org.apache.velocity.exception.VelocityException;
> import org.apache.velocity.runtime.parser.Token;
> /*
>  * Copyright 2001,2004 The Apache Software Foundation.
>  * 
>  * Licensed 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.
>  */
> /**
>  *  Application-level exception thrown when a reference method is 
>  *  invoked and an exception is thrown.
>  *  <br>
>  *  When this exception is thrown, a best effort will be made to have
>  *  useful information in the exception's message.  For complete 
>  *  information, consult the runtime log.
>  *
>  * @author <a href="mailto:geirm@optonline.net">Geir Magnusson Jr.</a>
>  * @version $Id: MethodInvocationException.java,v 1.2.14.1 2004/03/03 23:22:54 geirm Exp $
>  */
> public class MethodInvocationException extends VelocityException
> {
>     private String methodName = "";
>     private String referenceName = "";
>     private Throwable wrapped = null;
>     private int line; // Added by CX
>     private int column; // Added by CX
>     /**
>      *  CTOR - wraps the passed in exception for
>      *  examination later
>      *
>      *  @param message 
>      *  @param e Throwable that we are wrapping
>      *  @param methodName name of method that threw the exception
>      */
>     public MethodInvocationException( String message, Throwable e, String methodName )
>     {
>         super(message);
>         this.wrapped = e;
>         this.methodName = methodName;
>     }       
>     /**
>      *  Returns the name of the method that threw the
>      *  exception
>      *
>      *  @return String name of method
>      */
>     public String getMethodName()
>     {
>         return methodName;
>     }
>     /**
>      *  returns the wrapped Throwable that caused this
>      *  MethodInvocationException to be thrown
>      *  
>      *  @return Throwable thrown by method invocation
>      */
>     public Throwable getWrappedThrowable()
>     {
>         return wrapped;
>     }
>     /**
>      *  Sets the reference name that threw this exception
>      *
>      *  @param reference name of reference
>      */
>     public void setReferenceName( String ref )
>     {
>         referenceName = ref;
>     }
>     /**
>      *  Retrieves the name of the reference that caused the 
>      *  exception
>      *
>      *  @return name of reference
>      */
>     public String getReferenceName()
>     {
>         return referenceName;
>     }
>     /**
>      *  Retrieves the line number where the error occured
>      * 
>      * @return line number
>      */
> 	public int getLine() 
> 	{
> 		return line;
> 	}
> 	
>     /**
>      *  Sets the line number where the error occured
>      *
>      *  @param line
>      */
> 	public void setLine(int line) 
> 	{
> 		this.line = line;
> 	}
> 	/**
> 	 * Retrieves the line number where the error occured
> 	 * 
> 	 * @return column number
> 	 */
> 	public int getColumn() 
> 	{
> 		return column;
> 	}
> 	/**
> 	 *  Sets the column number where the error occured
> 	 *  
> 	 * @param column
> 	 */
> 	public void setColumn(int column) 
> 	{
> 		this.column = column;
> 	}
>     
> }
> ===============================================
> Proposed changes in ASTMethod.java
> ===============================================
> package org.apache.velocity.runtime.parser.node;
> /*
>  * Copyright 2000-2001,2004 The Apache Software Foundation.
>  * 
>  * Licensed 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.runtime.parser.*;
> import org.apache.velocity.util.introspection.IntrospectionCacheData;
> import org.apache.velocity.util.introspection.VelMethod;
> import org.apache.velocity.util.introspection.Info;
> import org.apache.velocity.exception.MethodInvocationException;
> import java.lang.reflect.InvocationTargetException;
> import org.apache.velocity.app.event.EventCartridge;
> /**
>  *  ASTMethod.java
>  *
>  *  Method support for references :  $foo.method()
>  *
>  *  NOTE :
>  *
>  *  introspection is now done at render time.
>  *
>  *  Please look at the Parser.jjt file which is
>  *  what controls the generation of this class.
>  *
>  * @author <a href="mailto:jvanzyl@apache.org">Jason van Zyl</a>
>  * @author <a href="mailto:geirm@optonline.net">Geir Magnusson Jr.</a>
>  * @version $Id: ASTMethod.java,v 1.24.4.1 2004/03/03 23:22:59 geirm Exp $ 
>  */
> public class ASTMethod extends SimpleNode
> {
>     private String methodName = "";
>     private int paramCount = 0;
>     public ASTMethod(int id)
>     {
>         super(id);
>     }
>     public ASTMethod(Parser p, int id)
>     {
>         super(p, id);
>     }
>     /** Accept the visitor. **/
>     public Object jjtAccept(ParserVisitor visitor, Object data)
>     {
>         return visitor.visit(this, data);
>     }
>     /**
>      *  simple init - init our subtree and get what we can from 
>      *  the AST
>      */
>     public Object init(  InternalContextAdapter context, Object data)
>         throws Exception
>     {
>         super.init(  context, data );
>         /*
>          *  this is about all we can do
>          */
>         methodName = getFirstToken().image;
>         paramCount = jjtGetNumChildren() - 1;
>         return data;
>     }
>     /**
>      *  invokes the method.  Returns null if a problem, the
>      *  actual return if the method returns something, or 
>      *  an empty string "" if the method returns void
>      */
>     public Object execute(Object o, InternalContextAdapter context)
>         throws MethodInvocationException
>     {
>         /*
>          *  new strategy (strategery!) for introspection. Since we want 
>          *  to be thread- as well as context-safe, we *must* do it now,
>          *  at execution time.  There can be no in-node caching,
>          *  but if we are careful, we can do it in the context.
>          */
>         VelMethod method = null;
>         Object [] params = new Object[paramCount];
>         try
>         {
>             /*
>              *   check the cache 
>              */
>             IntrospectionCacheData icd =  context.icacheGet( this );
>             Class c = 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 == c )
>             {
>                 /*
>                  * sadly, we do need recalc the values of the args, as this can 
>                  * change from visit to visit
>                  */
>                 for (int j = 0; j < paramCount; j++)
>                     params[j] = jjtGetChild(j + 1).value(context);
>                 /*
>                  * and get the method from the cache
>                  */
>                 method = (VelMethod) icd.thingy;
>             }
>             else
>             {
>                 /*
>                  *  otherwise, do the introspection, and then
>                  *  cache it
>                  */
>                 for (int j = 0; j < paramCount; j++)
>                     params[j] = jjtGetChild(j + 1).value(context);
>                 method = rsvc.getUberspect().getMethod(o, methodName, params, new Info("",1,1));
>                 if (method != null)
>                 {    
>                     icd = new IntrospectionCacheData();
>                     icd.contextData = c;
>                     icd.thingy = method;
>                     context.icachePut( this, 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)
>                 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 propogate it here, not do anything fancy
>              */
>             throw mie;
>         }
>         catch( Exception e )
>         {
>             /*
>              *  can come from the doIntropection() also, from Introspector
>              */
>             rsvc.error("ASTMethod.execute() : exception from introspection : " + e);
>             return null;
>         }
>         try
>         {
>             /*
>              *  get the returned object.  It may be null, and that is
>              *  valid for something declared with a void return type.
>              *  Since the caller is expecting something to be returned,
>              *  as long as things are peachy, we can return an empty 
>              *  String so ASTReference() correctly figures out that
>              *  all is well.
>              */
>             Object obj = method.invoke(o, params);
>             
>             if (obj == null)
>             {
>                 if( method.getReturnType() == Void.TYPE)
>                      return new String("");
>             }
>             
>             return obj;
>         }
>         catch( InvocationTargetException ite )
>         {
>             /*
>              *  In the event that the invocation of the method
>              *  itself throws an exception, we want to catch that
>              *  wrap it, and throw.  We don't log here as we want to figure
>              *  out which reference threw the exception, so do that 
>              *  above
>              */
>             EventCartridge ec = context.getEventCartridge();
>             /*
>              *  if we have an event cartridge, see if it wants to veto
>              *  also, let non-Exception Throwables go...
>              */
>             if ( ec != null && ite.getTargetException() instanceof java.lang.Exception)
>             {
>                 try
>                 {
>                     return ec.methodException( o.getClass(), methodName, (Exception)ite.getTargetException() );
>                 }
>                 catch( Exception e )
>                 {
>                     MethodInvocationException miex = new MethodInvocationException( 
>                         "Invocation of method '" 
>                         + methodName + "' in  " + o.getClass() 
>                         + " threw exception " 
>                         + e.getClass() + " : " + e.getMessage(), 
>                         e, methodName );
>                     miex.initCause(ite.getTargetException());
>                     miex.setLine(first.beginLine);
>                     miex.setColumn(first.beginColumn);
>                     throw miex;
>                 }
>             }
>             else
>             {
>                 /*
>                  * no event cartridge to override. Just throw
>                  */
>             	MethodInvocationException miex = new MethodInvocationException( 
>                 "Invocation of method '" 
>                 + methodName + "' in  " + o.getClass() 
>                 + " threw exception " 
>                 + ite.getTargetException().getClass() + " : "
>                 + ite.getTargetException().getMessage(), 
>                 ite.getTargetException(), methodName );
>                 miex.initCause(ite.getTargetException());
>                 miex.setLine(first.beginLine);
>                 miex.setColumn(first.beginColumn);
>                 throw miex;
>             }
>         }
>         catch( Exception e )
>         {
>             rsvc.error("ASTMethod.execute() : exception invoking method '" 
>                                + methodName + "' in " + o.getClass() + " : "  + e );
>                                
>             return null;
>         }            
>     }
> }

-- 
This message is automatically generated by JIRA.
-
If you think it was sent incorrectly contact one of the administrators: http://issues.apache.org/jira/secure/Administrators.jspa
-
For more information on JIRA, see: http://www.atlassian.com/software/jira

        

---------------------------------------------------------------------
To unsubscribe, e-mail: velocity-dev-unsubscribe@jakarta.apache.org
For additional commands, e-mail: velocity-dev-help@jakarta.apache.org