Handling Exceptions in Page Flows

WebLogic Workshop provides a number of standard page flow exception types that you can catch and handle in your web applications. The types represent common exception scenarios for page flows. The following table describes the exceptions:

Exception Type Description
ActionNotFoundException Occurs when the user tries to execute an action that does not exist on the page flow.
EmptyNestingStackException Occurs when the user invokes an action in a nested page flow that is qualified with a @jpf:forward return-action="<action-name-in-calling-pageflow>" annotation, but there is no calling page flow. This can happen in iterative development mode when you have modified files and caused the web application to be redeployed, or when the session expires.
IllegalOutputFormTypeException Occurs when the first output form for a Forward resolves to a @jpf:forward annotation whose return-form or return-form-type attribute demands a different form type.
IllegalPageInputException Occurs when a page input has been added to a Forward that resolves to a @jpf:forward redirect="true" annotation. Page inputs may not be used on redirect forwards.
IllegalRedirectOutputFormException Occurs when an output form has been added to a Forward that resolves to a @jpf:forward redirect="true" annotation. Output forms may not be used on redirect forwards.
InfiniteReturnToActionException Occurs when the user invokes an action that is qualified with a @jpf:forward return-to="previousAction" annotation, but the previous action was the same as the current action (an infinite loop).
LoginExpiredException Occurs when the NotLoggedInException would be thrown, and the current HttpServletRequest refers to a session that no longer exists. For more information, see Handling Sessions Expirations and Timeouts in this topic.
NoCurrentPageFlowException Occurs when the user invokes an action that is qualified with a @jpf:forward annotation marked with either return-to="previousAction" or return-to="previousPage", but there is no current page flow. This happens in iterative development mode when you have modified files and caused the web application to be redeployed, or when the session expires.
NoMatchingActionMethodException

Occurs when the current action method does not accept the type of form passed in the Forward to the action. This may happen when the user returned to the calling page flow, from a nested page flow, with a specified form (@jpf:forward return-form="<form-name>" or return-form-type="<form-type>", but no action in the calling page flow accepts that form type.

This exception can also occur when you simply forward to another page flow, nested or not, whose begin action (or any other specified action that was the forward target) does not accept the type of form passed in the Forward object.

NoPreviousActionException Occurs when the user attempts to execute an action marked with the @jpf:forward return-to="previousAction" annotation, but there is no previously run action. For example, this could happen if the begin action returns a @jpf:forward marked with return-to="previousAction".
NoPreviousPageException Occurs when the user attempts to execute an action marked with the @jpf:forward return-to="previousPage" annotation, but there is no previous page in the page flow. For example, this could happen if the begin action returns a @jpf:forward marked with return-to="currentPage".
NotLoggedInException

Occurs when the user attempts to execute an action marked with the login-required or roles-allowed attributes (@jpf:controller or @jpf:action annotations), but the user in not logged in.

If the requested session-ID is different than the current session-ID, the LoginExpiredException will be thrown instead of the NotLoggedInException. For more information, see Handling Sessions Expirations and Timeouts in this topic.

UnfulfilledRolesException Occurs when the user attempts to execute an action marked with roles-allowed, but the user is logged into an account that is not associated with any of the required roles.
UnresolvableForwardException Occurs when the user returns a Forward from an action method, but there is no forward with that name on the action method, or at the class level.

Note: For information about the annotations and attributes mentioned in the previous table, see the Related Topics section below.

These exceptions extend com.bea.wlw.netui.pageflow.PageFlowException. If you do nothing to handle these exceptions (with a @jpf:catch annotation), the default error message will be written to the response.

You can handle these exceptions specifically by putting the following annotation at the class level in Global.app, or at the class level of a page flow, or on an action method in a page flow. The @jpf:catch annotation provides a declarative way to catch unhandled exceptions by forwarding to an error page.

* @jpf:catch type="com.bea.wlw.netui.pageflow.PageFlowException" method="..." | path="..."

When you create a new "Web Project" project with WebLogic Workshop, a default Global.app file is created for you in the project's /WEB-INF/src/global directory. The Global.app file allows you to define actions that can be invoked by any page flow in the web project.

Note: In a page flow controller class that you create with the Page Flow Wizard, you may notice the following line:

// protected global.Global globalApp;

It is not required that you uncomment this line in order to have access to methods in Global.app. However, if you do uncomment it, this globalApp declaration will give the page flow easy access to public methods and variables in Global.app.

The default Global.app class definition includes the following annotations:

    /**
     * @jpf:catch type="Exception" method="handleException"
     * @jpf:catch type="PageFlowException" method="handlePageFlowException"
     */
     public class Global extends GlobalApp

The generic, "last resort" handleException method is defined for you in /WEB-INF/src/global/Global.app, as follows. It results in forwarding the user to the /error.jsp page, which is also provided for all new "Web Projects".

    /**
     * @jpf:exception-handler
     * @jpf:forward name="errorPage" path="/error.jsp"
     */
    protected Forward handleException( Exception ex, String actionName,
                                       String message, FormData form )
    {
        System.err.print( "[" + getRequest().getContextPath() + "] " );
        System.err.println( "Unhandled exception caught in Global.app:" );
        ex.printStackTrace();
        return new Forward( "errorPage" );
    }

You can modify the generated /error.jsp page to match the requirements of your web project. It contains code similar to the following:

<!--Generated by WebLogic Workshop-->
<%@ page language="java" contentType="text/html;charset=UTF-8" isErrorPage="true"%>
<%@ taglib uri="netui-tags-databinding.tld" prefix="netui-data"%>
<%@ taglib uri="netui-tags-html.tld" prefix="netui"%>
<%@ taglib uri="netui-tags-template.tld" prefix="netui-template"%>
<netui:html>
  <head>
    <title>Error</title>
  </head>
  <body>
    <p>
      An error has occurred:
    </p>   
    <blockquote>
      <netui:label value="{request.errorMessage}" defaultValue="" />
      <br/>
      <netui:exceptions showMessage="true" />
    </blockquote>
  </body>
</netui:html>
<!-- Some browsers will not display this page unless the response status code is 200. -->
<% response.setStatus(200); %>

You can test that your /error.jsp page is working the way you intended by creating another JSP page, starting the server, and then opening that test page. For example in your web project's root directory, you could add a test page named /testError.jsp that contains the following code.

 <%@ page language="java" contentType="text/html;charset=UTF-8"%>
 <%@ taglib uri="netui-tags-databinding.tld" prefix="netui-data"%>
 <%@ taglib uri="netui-tags-html.tld" prefix="netui"%>
 <%@ taglib uri="netui-tags-template.tld" prefix="netui-template"%>
 <netui:html>
   <head>
     <title>Test Page to Verify Error.jsp</title>
   </head>
   <body>
    <p>Using this page to verify that a deliberate exception causes 
       our /error.jsp to load:</p>
    <p>
     <% if ( true ) throw new Exception( "Can you see this message?" ); %> 
    </p>
   </body>
</netui:html>

The code shown above throws an exception and should result in the loading of your error page. With the server running, access the test page. For example:

http://localhost:7001/myApp/testError.jsp

For the page flow exceptions described in the previous table, the default Global.app provides the handlePageFlowException method:

    /** 
     *
     * @jpf:exception-handler
     */ 
    public Forward handlePageFlowException( PageFlowException ex, String message,
                                            String action, FormData form ) 
        throws java.io.IOException
    { 
        ex.sendError( getRequest(), getResponse() ); 
        return null; 
    } 

The handlePageFlowException handler allows page flow exceptions to write informative error pages to the response. To use the standard exception-handler for these exceptions (handleException), remove in Global.app the handlePageFlowException method and the @jpf:catch annotation for PageFlowException.

Handling Session Expirations and Timeouts

Session expirations or timeouts can occur in your web application and should be handled. As mentioned in the prior table, if the requested session-ID is different than the current session-ID, WebLogic Workshop will issue LoginExpiredException (com.bea.wlw.netui.pageflow.LoginExpiredException) instead of the NotLoggedInException. You can handle session-expiration specifically (using a @jpf:catch annotation for LoginExpiredException). However, LoginExpiredException extends NotLoggedInException, so if you prefer you can safely catch only NotLoggedInException.

The following code shows a way to handle a session timeout in a login example.

    /** 
     * @jpf:action 
     * @jpf:forward name="loginPage" path="Login.jsp" redirect="true"      
     * @jpf:forward name="loginTimeoutPage" path="LoginTimeout.jsp" redirect="true" 
     */ 
     protected Forward begin() 
     { 
        String requestedSessionID = getRequest().getRequestedSessionId(); 
        if ( requestedSessionID != null && ! requestedSessionID.equals( getSession().getId() ) ) 
        { 
           return new Forward( "loginTimeoutPage" ); 
        } 
        else 
        { 
           return new Forward( "loginPage" ); 
        } 
     } 

For information on setting session timeouts, see the "Session" section of Using Data Binding in Page Flows.

Related Topics

@jpf:catch Annotation

@jpf:exception-handler Annotation

@jpf:validation-error-forward Annotation