0

As described in BalusC's and A. Tijms' book "The Definitive Guide to JSF in Java EE 8" (and similarly coded in Omnifaces) I have constructed the following CustomException handler for exceptions occuring in regular and ajax requests.

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

    }

}

In web.xml I have

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

For regular requests it works like a charm. However, if there is a (Runtime)Exception in an ajax request, I only get the java script alert box that the asynchronous request returned nothing (in Development mode) or nothing (in Production mode). The above code runs fully through, however, the error page is not displayed. I am using Tomcat 9 and Mojarra 2.3.8 under Java 11. What I am doing wrong?

I tested using the composite component described in http://balusc.omnifaces.org/2013/01/composite-component-with-multiple-input.html where I throw an IllegalStateException within the updateDaysIfNecessary method which is triggered by changing the month in the repective drop down box.

chris21k
  • 241
  • 1
  • 7
  • So you are not using the OmniFaces exceptionhandler (or OmniFaces at all)? Why not use that one? – Kukeltje Jan 02 '19 at 09:33
  • The main reason is to minimize dependencies (jar hell) and another one is to see what is going on behind the scenes, i.e., to learn something. Hope these are reasons enough :) – chris21k Jan 02 '19 at 09:52
  • Jar 'hell' not really since one very good/relevant jar does not make a hell (using full spring does ;-), learning... well... I can understand in some way, but I usually take the approach to learn things behind the scenes if something else that is readily available (omnifaces) does not work). But if you don't use OmniFaces, the tag on the question is not correct, since it is for where problems are. Cheers (the book is a great one btw, bought it too) – Kukeltje Jan 02 '19 at 10:13
  • And did you read https://stackoverflow.com/questions/26588797/why-fullajaxexceptionhandler-does-not-simply-perform-an-externalcontextredirect/ – Kukeltje Jan 02 '19 at 10:20
  • I have red the provided link. Unfortunetly, I cannot see why its contetns relate to the problem, as it is about redirecting. Please note that the above code does no redirect but builds the view as suggested in the book. – chris21k Jan 02 '19 at 10:48
  • As I have coded very close to the book and the implementation of OmniFaces, are you sure the implementation of OmniFaces works correct? However, I did not try that. – chris21k Jan 02 '19 at 10:56
  • Another reason not to use third party code is that I want to have full control myself, which I find is legitim for my own project. Please understand that. Although I admit, that OmniFaces is a good, small and clean library without extra dependencies. @Kukeltje: many thanks for your interest and help! – chris21k Jan 02 '19 at 10:57
  • _"Another reason not to use third party code is that I want to have full control myself, which I find is legitim for my own project."_ Then don't use JSF, CDI, JAX-RS, JSP, etc ;-) The link is about 'ajax' and a errorhandler. I'm not stating it IS related, just posting it as a suggestion since it was not visible you explicitly researched ajax related things. And yes, the omnifaces one is working fine for me. – Kukeltje Jan 02 '19 at 11:35
  • Have you configured the custom exception handler in faces config xml file to make it the handler for all exceptions in the jsf application ? You can also place a debug point to see if the exception indeed handled by the custom handler. – OTM Jan 04 '19 at 04:26
  • In faces_message.xml I have my.xxx_package.CustomExceptionHandlerFactory When during an ajax request an exception is thrown, the handle method and its helpers run fully through (verified with the debugger) and then Mojarra does the wrong thing. Maybe the description in the book is not fully complete, e.g., Mojarra's ajax javascript does not react as desired? Or most likely I have overseen something... – chris21k Jan 04 '19 at 07:07

0 Answers0