You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@struts.apache.org by Laine Donlan <ld...@elogex.com> on 2001/11/08 19:14:04 UTC

Declarative Exception Handling

All -

I have made some updates based on the feedback from the list.   I have
attached a text file with diffs from the Struts files that changes -
Action, ActionServlet, and ActionMapping.  I have also attached a jar
file with the required .java files and the updated Struts .dtd. file.

A quick overview of the changes I made:

The Action class now has multiple extension points.  I have added an
execute() method with the same signature as perform() only that it
throws Exception.  The default perform() implementation attempts to call
the execute() method and if an exception occurs - it is used as the root
cause of a ServletException.  This was added to allow for the use of
try{}catch(Exception ex) semantics with actions and exception handling
and alleviate the need to have any exception handling in Action
implementations while at the same time provided reverse compatibility
with all previously developed exceptions.

The ActionServlet will attempt to handle any ServletException that is
thrown from a perform() method.  It uses the root cause of the exception
and searched first the Action's exception mappings and then a set of
optional global exceptions for handling.  The default handling is to
create an ActionError object and place it in the user's session or
request (based on scope of exception) using the ACTION_EXCEPTION_KEY
value.  This handling is isolated in a handleErrorForException method to
allow this handling to be easily customizable by ActionServlet
descendants.

Any comments or feedback would be appreciated.

Laine

 <<diffs.txt>>  <<struts-exceptions.jar>> 






Re: Declarative Exception Handling

Posted by Ted Husted <hu...@apache.org>.
I agree with everything Dmitri said, but was thinking of something like
this (for a base class in the Actions package), which may be what Laine
is also suggesting:

public class DispatchExceptionAction extends Action {
    public ActionForward doPerform(
                 ActionMapping mapping,
                 ActionForm form,
                 HttpServletRequest request,
                 HttpServletResponse response)
    throws Exception {	

	// Override this class instead of perform
	// and return ActionForwards in the 
        // usual way; but use with the alternative 
        // of ExceptionForwards instead of try catch

       return null;
    }

    public ActionForward perform(
                 ActionMapping mapping,
                 ActionForm form,
                 HttpServletRequest request,
                 HttpServletResponse response)
    throws IOException, ServletException {
	ActionForward forward = null;
        try {
            forward = doPerform(mapping,form,request,response);
        }
        catch (Exception e) {

		// Lookup exceptions here
                // and set forward
                // or ..

            throw new ServletException(e);

        }

	return forward;
   }

Later on, I would like to build this deeper into the framework, but
don't know how to manage the signature shift without breaking the
installed base. Perhaps a job for Struts 2 ... which might include a
signature updater ...

I'd also like for us to commit a base exception class like the one Laine
submitted, that also emulates the 1.4 chaining functionality, and any
other goodies like the ones Dmitri mentioned (any chance of donating
that?).

Dmitri - also feel free to submit your other wishes as Bugzilla
enhancements, so they do not get lost in the shuffle.


-- Ted Husted, Husted dot Com, Fairport NY USA.
-- Custom Software ~ Technical Services.
-- Tel +1 716 737-3463
-- http://www.husted.com/struts/

--
To unsubscribe, e-mail:   <ma...@jakarta.apache.org>
For additional commands, e-mail: <ma...@jakarta.apache.org>


RE: Declarative Exception Handling

Posted by Dmitri Valdin <d....@proway.de>.
All -

>Regarding the declarative exception handling, never really heard much
>since the last posting I made about it.  But in that I proposed keeping
>the default perform signature and adding another signature that could be
>used to accomplish the try{}catch(Exception ){} semantics I was looking
>for.  Basically the base Action perform would call the lower level hook
>and handle any exception by making it the root of a SevletException to
>be handled by the Servlet.  I really think this keeps existing Struts
>apps from breaking while providing a more flexible environment for
>future development.

the solution proposed by Laine sounds great and it would be nice if it
would be introduced in the new Struts release.

I'll try to summarize (my ;-) general knowledge about exceptions and discuss
some scenarios of their handling.

Let's have a look on exceptions from the point of view of EJB container.
It divides all exceptions in 2 big categories:

1) application exceptions (derived from java.lang.Exception)

Application exceptions does not cause the EJB container to rollback
the transaction giving client the opportunity to retry the operation
Examples of application exception are standard EJB exceptions like:
CreateException, FinderException, ObjectNotFoundException, etc.
All user defined exceptions derived from java.lang.Exception are also
considered as application exceptions. User / password mismatch is a
typical example of such exception.

2) runtime exceptions (derived from java.lang.RuntimeException)
This exception do cause the EJB container to rollback the transaction.
Examples of this exceptions are EJB standard EJBException and
such exceptions as NullPointerException, ClassCastException, etc.

---

Now. how should all this exceptions be handled by the client.
I suppose, that all runtime exceptions would cause logging the exception
and forwarding to the general error page with smth like
"Oops, Houston, we have a problem..."

All application exceptions would normally force the user to stay
on the same page, display some error message and let hime a chance
to correct them, for example to specify valid password.

In the project I am envolved at the moment we defined 2 base exception
classes: my.package.AppException and my.package.MyRuntimeException.
All other exceptions derive from this two exceptions. Most of all we
don't need to derive, but just use the exceptions as is, for example:

throw new my.package.AppException("error.password.mismatch");
throw new my.package.AppException("database.load", "myDatabase");
throw new my.package.MyRuntimeException("What the hell we are doing here");

It is quite obvious, that the text for this exception comes from
application resources file, well we have a separate file for that purpose.

This basis exception classes come with some goodies, like formatting
of stack trace, and handling of nested exceptions, so the client can know
the origin of the exception, even if original exception was thrown somewhere
deep in EJB. The exceptions are similar to weblogic NestedException and
NestedRuntimeException.

We have managed to get rid of exception handling in Action classes - there
is no try / catch block there - in the way, which was proposed by Laine,
but programmatically. We have introduced an Action class of our own:
(Coding conventions was not my invention ;-)


    protected abstract ActionForward doPerform(
        ActionMapping oMapping,
        ActionForm oForm,
        HttpServletRequest request,
        HttpServletResponse oResponse)
    throws IOException, ServletException, BspAppException;

    public ActionForward perform(ActionMapping oMapping,
        ActionForm oForm,
        HttpServletRequest oRequest,
        HttpServletResponse oResponse)
    throws IOException, ServletException
    {
        HttpSession oHttpSession = oRequest.getSession();

        // Check if we have a new HttpSession.
        // It can happen only if is was expired or tomcat was restarted.
        if (oHttpSession.isNew()) {
            addException(new BspAppException("ERR_INVALID_HTTP_SESSION"));
            return oMapping.findForward("logon");
        }

        ActionForward oAction = null;

        try {
            // Clear old messages.
            this.clearMessages(oRequest);

            // Call the overridden method.
            oAction = doPerform(oMapping, oForm, oRequest, oResponse);
        } catch (BspAppException e) {
            String sMessage = e.getMessage();

            // Add exception to the message list.
            this.addException(e);

            // Check for some specific exceptions.
            if ("ERR_JMS_RECV_TIMEOUT".equals(sMessage)) {
                s_oLog.warn(e);
                // Save the complete request and ask user to retry.
                this.saveRequest(oRequest);
                oAction = oMapping.findForward("retry");
            } else if ("ERR_INVALID_HTTP_SESSION".equals(sMessage)) {
                s_oLog.warn(e);
                // Force user to logon.
                oAction = oMapping.findForward("logon");
            } else if ("ERR_INVALID_HOST_SESSION".equals(sMessage) ||
                    "ERR_INVALID_APP_SESSION".equals(sMessage)) {
                s_oLog.warn(e);
                // Save the complete request and force user to logon.
                this.saveRequest(oRequest);
                // Remove user key, to let user to login again.
                oRequest.getSession().removeAttribute(Constants.USER_KEY);
                oAction = oMapping.findForward("logon");
            } else {
                // This is an application exception, user have to retry.
                // We don't need to log this exception.
                if (s_oLog.isDebugEnabled()) {
                    s_oLog.debug(e);
                }
                // Do we have an exception mapping ?
                oAction = oMapping.findForward("exception");

                if (oAction == null) {
                    // Stay on the same page.
                    oAction = new ActionForward(oMapping.getInput());
                }
            }
        } catch (Exception e) {
            // This is a real problem. Forward to error page.
            s_oLog.error(e);
            oAction = oMapping.findForward("error");
        }

        // If due to some reasons oAction equal to null, forward to empty
page.
        if (oAction == null) {
            s_oLog.warn("oAction = null");
            oAction = oMapping.findForward("empty");
        }

        // Save messages in request (if they exist of cource)
        this.saveMessages(oRequest);

        if (s_oLog.isDebugEnabled() && oAction != null) {
            s_oLog.debug("perform: forward name=" + oAction.getName()
                + " path=" + oAction.getPath());
        }

        return oAction;
    }

There is nothing special about this class exception special handling
of some application exceptions, like expired host session or JMS problem.
Don't blame me for using strings. We have to support host exception from
Cobol applications, which come to us as Strings through XML protocol.

And now back to Laine's solution. I really like it very much, but I am not
not sure, that application exceptions should be wrapped as ServletException.
I would like to get rid of handling exceptions and any kind of wrapping in
Action classes.

I see no harm in introducing of execute method, which throws Exception.
All old applications would still work, and all new applications can use
new feature.

Configuration of exceptions, proposed by Laine expects mapping between
key and exception. And what do you think about regarding the exception
message as a key and search for it in application resources ?

If yes, then it would be not bad to provide a couple of basic exceptions,
like AppException and RuntimeException. It is not wise to reinvet the wheel.

I have a couple of wishes more, they have nothing to do with Lanes's
proposal:

1) extend html:errors tag with format attribute:

	<html:text property="password" size="16" maxlength="16"/>
	<html:errors property="password" format="short"/>

provided application resources contain:

	error.short=<font size='4' color='#B22222'><b>!</b></font>

it would cause "!" to appear beside the field in case the password field
contains an exception. It is not always reasonable to show the whole text.

2) it would be not bad to have a chance to specify the list of "must" fields
in action configuration (similar to "in role" / "not in role") proposal,
so struts could handle exception like "Please fill in all required fields"
automatically. Personally I prefer this way instead of having list of errors
like "missing field one", "missing field two" and so on.

Regards,

Dmitri Valdin





--
To unsubscribe, e-mail:   <ma...@jakarta.apache.org>
For additional commands, e-mail: <ma...@jakarta.apache.org>