JSF:无法捕获ViewExpiredException

我正在Glassfish v3上开发一个JSF 2.0应用程序,我正在尝试处理ViewExpiredException。 但无论我做什么,我总是得到一个Glassfish错误报告而不是我自己的错误页面。

为了模拟VEE的发生,我将以下函数插入到我的支持bean中,它会激活VEE。 我通过commandLink从我的JSF页面触发此函数。 代码:

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

起初我只是通过向我的web.xml添加错误页面来尝试它:

  javax.faces.application.ViewExpiredException /error.xhtml  

但这不起作用,我没有被重定向到错误,但我显示了Glassfish错误,它显示了一个HTTP状态500页面,其中包含以下内容:

 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 

我尝试的下一件事是编写ExceptionHandlerFactory和CustomExceptionHandler,如JavaServerFaces 2.0 – 完整参考中所述 。 所以我将以下标记插入faces-config.xml:

   exceptions.ExceptionHandlerFactory   

并添加了这些类:工厂:

 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; } } 

自定义exception处理程序:

 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 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(); } } 

但是我仍然没有重定向到我的错误页面 – 我得到的是与上面相同的HTTP 500错误。 我做错了什么,在我的实现中可能缺少哪些exception未正确处理? 任何帮助高度赞赏!

编辑

好吧,老实说。 事实上,我的代码实际上是用Scala编写的,但这是一个很长的故事。 我一直认为这是一个Java问题。 在这种情况下,真正的错误是我自己的愚蠢。 在我的(Scala)代码中,在CustomExceptionHandler中,我忘了添加带有“i.remove();”的行。 因此,ViewExpiredException在处理完之后仍保留在UnhandledExceptionsQueue中,并且“冒泡”。 当它冒泡时,它就变成了ServletException。

我很抱歉让你们两个都迷惑不解!

这个测试用例是假的。 ViewExpiredException通常仅在恢复视图期间抛出(因为它在会话中丢失),而不是在呈现响应期间或实例化bean时。 在您的情况下,在实例化bean期间抛出此exception,并且此exception已包装在ServletException

通常只有在HTTP会话过期时向服务器发送HTTP POST请求时才会抛出真正的 ViewExpiredException 。 因此,基本上有两种方法可靠地重现这一点:

  1. 在Web浏览器中打开一个带有POST表单( h:form默认已经是POST)的JSF页面,关闭服务器并清理其工作目录(这很重要,因为大多数服务器会在关机时将打开的会话序列化为磁盘并在启动时将其反序列化) ,重新启动服务器并提交已打开的表单。 将抛出ViewExpiredException

  2. web.xml设置为1分钟,并在使用POST表单打开JSF页面后1分钟内提交表单。 这也将抛出ViewExpiredException

我不是专家。 这些只是猜测或建议。

1)尝试重定向到标准HTML页面以查看是否有效2) 基于此 ,它应该与第一种方法本身一起使用,尝试编写JSF PhaseListener并在RESTORE VIEW阶段抛出相同的exception。现在,你是投掷INVOKE APPLICATION或UPDATE MODEL阶段。 3)通过sysout,确保配置了错误页面 – 使用Servlet Context(我没有尝试过这个但是应该可以)

即使我很好奇可能是什么问题!