Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
204 views
in Technique[技术] by (71.8m points)

java - JSF: Cannot catch ViewExpiredException

I'm developing a JSF 2.0 application on Glassfish v3 and i'm trying to handle the ViewExpiredException. But whatever i do, i always get a Glassfish error report instead of my own error page.

To simulate the occurrence of the VEE, i inserted the following function into my backing bean, which fires the VEE. I'm triggering this function from my JSF page through a commandLink. The Code:

@Named
public class PersonHome {
  (...)
  public void throwVEE() {
    throw new ViewExpiredException();
  }
}

At first i tried it by simply adding an error-page to my web.xml:

<error-page>
  <exception-type>javax.faces.application.ViewExpiredException</exception-type>
  <location>/error.xhtml</location>
</error-page>  

But this doesn't work, i'm not redirected to error but i'm shown the Glassfish errorpage, which shows a HTTP Status 500 page with the following content:

description:The server encountered an internal error () that prevented it from fulfilling this request.
exception: javax.servlet.ServletException: javax.faces.application.ViewExpiredException
root cause: javax.faces.el.EvaluationException:javax.faces.application.ViewExpiredException
root cause:javax.faces.application.ViewExpiredException

Next thing i tried was to write ExceptionHandlerFactory and a CustomExceptionHandler, as described in JavaServerFaces 2.0 - The Complete Reference. So i inserted the following tag into faces-config.xml:

<factory>
  <exception-handler-factory>
    exceptions.ExceptionHandlerFactory
  </exception-handler-factory>
</factory>

And added these classes: The factory:

package exceptions;

import javax.faces.context.ExceptionHandler;

public class ExceptionHandlerFactory extends javax.faces.context.ExceptionHandlerFactory {

    private javax.faces.context.ExceptionHandlerFactory parent;

    public ExceptionHandlerFactory(javax.faces.context.ExceptionHandlerFactory parent) {
        this.parent = parent;
    }

    @Override
    public ExceptionHandler getExceptionHandler() {
        ExceptionHandler result = parent.getExceptionHandler();
        result = new CustomExceptionHandler(result);
        return result;
    }

}

The custom exception handler:

package exceptions;

import java.util.Iterator;

import javax.faces.FacesException;
import javax.faces.application.NavigationHandler;
import javax.faces.application.ViewExpiredException;
import javax.faces.context.ExceptionHandler;
import javax.faces.context.ExceptionHandlerWrapper;
import javax.faces.context.FacesContext;
import javax.faces.event.ExceptionQueuedEvent;
import javax.faces.event.ExceptionQueuedEventContext;

class CustomExceptionHandler extends ExceptionHandlerWrapper {

    private ExceptionHandler parent;

    public CustomExceptionHandler(ExceptionHandler parent) {
        this.parent = parent;
    }

    @Override
    public ExceptionHandler getWrapped() {
        return this.parent;
    }

    @Override
    public void handle() throws FacesException {
        for (Iterator<ExceptionQueuedEvent> i = getUnhandledExceptionQueuedEvents().iterator(); i.hasNext();) {
            ExceptionQueuedEvent event = i.next();
            System.out.println("Iterating over ExceptionQueuedEvents. Current:" + event.toString());
            ExceptionQueuedEventContext context = (ExceptionQueuedEventContext) event.getSource();
            Throwable t = context.getException();
            if (t instanceof ViewExpiredException) {
                ViewExpiredException vee = (ViewExpiredException) t;
                FacesContext fc = FacesContext.getCurrentInstance();

                NavigationHandler nav =
                        fc.getApplication().getNavigationHandler();
                try {
                    // Push some useful stuff to the flash scope for
                    // use in the page
                    fc.getExternalContext().getFlash().put("expiredViewId", vee.getViewId());

                    nav.handleNavigation(fc, null, "/login?faces-redirect=true");
                    fc.renderResponse();

                } finally {
                    i.remove();
                }
            }
        }
        // At this point, the queue will not contain any ViewExpiredEvents.
        // Therefore, let the parent handle them.
        getWrapped().handle();
    }
}

But STILL i'm NOT redirected to my error page - i'm getting the same HTTP 500 error like above. What am i doing wrong, what could be missing in my implementation that the exception isn't handled correctly? Any help highly appreciated!

EDIT

Ok, i'm honest. In fact, my code is actually written in Scala, but thats a long story. i thought it was a Java problem all the time. The REAL error in this case was my own stupidness. In my (Scala) code, in CustomExceptionHandler, i forgot to add the line with the "i.remove();" So the ViewExpiredException stayed in the UnhandledExceptionsQueue after handling it, and it "bubbled up". And when it bubbles up, it becomes a ServletException.

I'm really sorry for confusing you both!

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

This test case is bogus. The ViewExpiredException is usually only thrown during restoring the view (because it's missing in the session), not during rendering the response nor instantiating the bean. In your case this exception is thrown during instantiating the bean and this exception is been wrapped in a ServletException.

The real ViewExpiredException is usually only thrown when you send a HTTP POST request to the server while the HTTP session is expired. So there are basically two ways to reproduce this reliably:

  1. Open a JSF page with a POST form (h:form is by default already POST) in a webbrowser, shutdown the server and clean its work directory (important, because most servers will serialize open sessions to disk on shutdown and unserialize them on startup), restart the server and submit the already opened form. A ViewExpiredException will be thrown.

  2. Set the <session-timeout> in web.xml to 1 minute and submit the form over 1 minute after opening the JSF page with the POST form. This will throw the ViewExpiredException as well.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...