JSF Custom ExceptionHandler не работает с AJAX - PullRequest
0 голосов
/ 02 января 2019

Как описано в книге BalusC и A. Tijms "Полное руководство по JSF в Java EE 8" (и аналогично закодированной в Omnifaces), я создал следующий обработчик CustomException для исключений, возникающих в обычных и ajax-запросах.

public class CustomExceptionHandlerFactory extends ExceptionHandlerFactory {

    public CustomExceptionHandlerFactory(ExceptionHandlerFactory wrapped) {
        super(wrapped);
    }

    @Override
    public ExceptionHandler getExceptionHandler() {
        return new CustomExceptionHandler(getWrapped().getExceptionHandler());
    }

    private class CustomExceptionHandler extends ExceptionHandlerWrapper {

        private CustomExceptionHandler(ExceptionHandler wrapped) {
            super(wrapped);
        }

        @Override
        public void handle() throws FacesException {
            handleException(FacesContext.getCurrentInstance());
            getWrapped().handle();
        }

        private void handleException(FacesContext fctx) {
            Iterator<ExceptionQueuedEvent> it
                = getUnhandledExceptionQueuedEvents().iterator();
            if (fctx == null
                    || fctx.getExternalContext().isResponseCommitted()
                    || !it.hasNext()) {
                return;
            }

            Throwable t = it.next().getContext().getException();
            Throwable tc = t;
            while ((tc instanceof FacesException || tc instanceof ELException)
                    && tc.getCause() != null) {
                tc = tc.getCause();
            }

            renderErrorPageView(fctx, t);

            it.remove();

            while (it.hasNext()) {
                it.next();
                it.remove();
            }
        }

        private void renderErrorPageView(FacesContext fctx, Throwable t) {
            ExternalContext ctx = fctx.getExternalContext();
            String uri = ctx.getRequestContextPath()
                + ctx.getRequestServletPath();
            Map<String, Object> requestMap = ctx.getRequestMap();
            requestMap.put(RequestDispatcher.ERROR_REQUEST_URI, uri);
            requestMap.put(RequestDispatcher.ERROR_EXCEPTION, t);

            String viewId = "/view/stop_error.xhtml";
            Application app = fctx.getApplication();
            ViewHandler viewHandler = app.getViewHandler();
            UIViewRoot viewRoot = viewHandler.createView(fctx, viewId);
            fctx.setViewRoot(viewRoot);
            try {
                ctx.responseReset();
                if (!fctx.getPartialViewContext().isAjaxRequest()) {
                    ctx.setResponseStatus(
                        HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
                }
                ViewDeclarationLanguage vdl
                    = viewHandler.getViewDeclarationLanguage(fctx, viewId);
                vdl.buildView(fctx, viewRoot);
                fctx.getPartialViewContext().setRenderAll(true);
                vdl.renderView(fctx, viewRoot);
                fctx.responseComplete();
            }
            catch (IOException e) {
                throw new FacesException(e);
            }
            finally {
                requestMap.remove(RequestDispatcher.ERROR_EXCEPTION);
            }            
        }

    }

}

В web.xml у меня есть

<error-page>
    <exception-type>javax.faces.application.ViewExpredException</exception-type>
    <location>/faces/view/expired.xhtml</location>
</error-page>
<error-page>
    <error-code>500</error-code>
    <location>/faces/view/stop_error.xhtml</location>
</error-page>

Для обычных запросов это работает как шарм. Однако, если в ajax-запросе есть исключение (время выполнения), я получаю только окно предупреждения java-скрипта о том, что асинхронный запрос не возвращает ничего (в режиме разработки) или ничего (в режиме производства). Приведенный выше код полностью проходит, однако страница ошибки не отображается. Я использую Tomcat 9 и Mojarra 2.3.8 под Java 11. Что я делаю не так?

Я тестировал, используя составной компонент, описанный в http://balusc.omnifaces.org/2013/01/composite-component-with-multiple-input.html, где я выбрасываю исключение IllegalStateException в методе updateDaysIfNeeded, который вызывается изменением месяца в раскрывающемся списке.

...