ViewExpiredException, показанное на странице java.lang.Throwable error в web.xml

Я работаю над веб-приложением JSF, в котором мне нужно открыть страницу "Session Expired", если истечет срок действия, но общая страница технической ошибки для всех остальных. Приложение запускается только на странице технической ошибки при запуске исключения. Здесь определения страниц ошибок:

<error-page> 
    <exception-type>javax.faces.application.ViewExpiredException</exception-type> 
    <location>/jsps/utility/sessionExpired.jsp</location> 
</error-page> 
<error-page> 
    <exception-type>java.lang.Throwable</exception-type> 
    <location>/jsps/utility/technicalError.jsp</location> 
</error-page> 
<error-page>
    <error-code>500</error-code>
    <location>/jsps/utility/technicalError.jsp</location>
</error-page>

Я удалил элементы страницы технической ошибки techError.jsp, и он работал нормально, но когда я их вернул, я не могу попасть на страницу sessionExpired.jsp. Как сообщить веб-контейнеру порядок оценки этих тегов, чтобы появилась правильная страница? Спасибо.

Ответ 1

Это связано с тем, что ViewExpiredException завернут в ServletException в соответствии со спецификацией JSF. Здесь выдержка из главы 10.2.6.2 спецификации JSF 1.2:

10.2.6.2 FacesServlet

Вызвать метод execute() сохраненного экземпляра Lifecycle, передав FacesContext экземпляр для этого запроса в качестве параметра. Если метод execute()бросает a FacesException, перебрасывает его как ServletException с помощью FacesException в качестве основной причины.

Как распределяются страницы ошибок, указан в спецификации API сервлета. Здесь выдержка из раздела 9.9.2 Спецификация API сервлета 2.5:

SRV.9.9.2 Страницы ошибок

Если объявление no error-page, содержащее exception-type, подходит для использования соответствие иерархии классов, а созданное исключение - ServletException или подкласса, контейнер извлекает завернутое исключение, как определено в метод ServletException.getRootCause. Выполняется второй проход по ошибке объявления страницы, снова попытка совпадения с страницей ошибки, но вместо этого используется обернутое исключение.

В иерархии классов ServletException уже соответствует Throwable, поэтому его основная причина не будет извлечена для второго прохода.

Чтобы доказать это заданное поведение, замените javax.faces.application.ViewExpiredException на javax.servlet.ServletException на <exception-type> и повторите попытку. Вы увидите ожидаемую страницу с ошибкой.

Чтобы решить эту проблему, просто удалите страницу с ошибкой на java.lang.Throwable или java.lang.Exception. Если ни одна конкретная страница ошибок не соответствует, то она все равно вернется к коду ошибки 500. Итак, все, что вам нужно, это:

<error-page> 
    <exception-type>javax.faces.application.ViewExpiredException</exception-type> 
    <location>/jsps/utility/sessionExpired.jsp</location> 
</error-page> 
<error-page>
    <error-code>500</error-code>
    <location>/jsps/utility/technicalError.jsp</location>
</error-page>

Обновить: согласно (удаленному) комментарию OP: для надежного тестирования это невозможно сделать throw new ViewExpiredException() в конструкторе или методе bean или так. Это, в свою очередь, обернулось бы в какое-то исключение EL. Вы можете в конечном итоге добавить строку отладки rootCause в Filter, чтобы увидеть ее самостоятельно.

Если вы используете Eclipse/Tomcat, быстрый способ протестировать ViewExpiredException следующий:

  • Создайте страницу JSF с помощью простой командной кнопки, разверните и запустите ее и откройте в веб-браузере.
  • Вернитесь к Eclipse, щелкните правой кнопкой мыши Tomcat-сервер и выберите "Clean Tomcat Work Directory". Это перезапустит Tomcat и, чтобы удалить все сеансы сериализации (важно! Просто перезапустить Tomcat недостаточно).
  • Вернитесь к веб-браузеру и нажмите кнопку с командой (без предварительной загрузки страницы заранее!).