0

My custom exception handler is not being invoked by the container to intercept (checked or unchecked) exceptions (I verified it through the debugger). I have this Entity bean class with @Email annotation for the email field, and when the user types in an invalid email through the JSF page, an error message is being displayed, however, the error message is not the one I set through the custom exception handler class, rather it is the one I have set as the default message for the @Email annotation. The error message generated by the custom exception handler has a prefix string "Exception caught by the custom Exception handler: ". I surmise that an invalid email should throw a ConstraintViolationException, which would be an ideal case for the exception handler to catch.

The JSF page allows the user information to be updated, so when I update the user's email with an invalid one and click the "Update" CommandButton, the registered action method is not being invoked (I verified it through the debugger). Furthermore, what I don't seem to figure out, is that when the "invalid email" error message is displayed on the JSF page, the "Add User" command Button gets disabled, so I can not navigate to the "Add User" JSF page. Any idea, why the exception handler and the page navigation (Add User) are not working in case of an error?

public class CustomExceptionHandler extends ExceptionHandlerWrapper {

    private ExceptionHandler wrapped;
    private final static Logger logger = Logger.getLogger(CustomExceptionHandler.class.getName());

    public CustomExceptionHandler(ExceptionHandler w) {
        wrapped = w;
    }

@Override
  public ExceptionHandler getWrapped() {
    return wrapped;
  }

  @Override
  public void handle() throws FacesException {
    Iterator iterator = getUnhandledExceptionQueuedEvents().iterator();
    
    while (iterator.hasNext()) {
      ExceptionQueuedEvent event = (ExceptionQueuedEvent) iterator.next();
      ExceptionQueuedEventContext context = (ExceptionQueuedEventContext)event.getSource();
 
      Throwable throwable = context.getException();
      
      FacesContext fc = FacesContext.getCurrentInstance();
      
      try {
          Flash flash = fc.getExternalContext().getFlash();
          
          // Put the exception in the flash scope to be displayed in the error 
          // page if necessary ...
          String errorMessage = "Exception caught by the custom Exception handler: "+throwable.getMessage();
          logger.severe(errorMessage);
          flash.put("errorDetails", errorMessage);
          
          
          NavigationHandler navigationHandler = fc.getApplication().getNavigationHandler();
          
          //navigationHandler.handleNavigation(fc, null, "/loginmanagement");
          navigationHandler.handleNavigation(fc, null, "error?faces-redirect=true");
          
          fc.renderResponse();
      } finally {
          iterator.remove();
      }
    }
    
    // Let the parent handle the rest
    getWrapped().handle();
  }
}

The factory class: public class CustomExceptionHandlerFactory extends ExceptionHandlerFactory { private ExceptionHandlerFactory parent;

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

The faces-config.xml

<?xml version='1.0' encoding='UTF-8'?>
<faces-config version="2.3"
              xmlns="http://xmlns.jcp.org/xml/ns/javaee"
              xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
              xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-facesconfig_2_3.xsd">

    <application>
        <resource-bundle>
            <base-name>webmessages</base-name>
            <var>bundle</var>
        </resource-bundle>
        <locale-config>
            <default-locale>en</default-locale>
            <!--      <supported-locale>es</supported-locale>   -->
        </locale-config>
    </application>
    <error-page>
        <exception-type>java.lang.RuntimeException</exception-type>
        <location>/loginmanagement.xhtml</location>
    </error-page> 
    <factory>
        <exception-handler-factory>
            org.me.mavenlistservicedb.applicationexception.CustomExceptionHandlerFactory
        </exception-handler-factory>
    </factory>
</faces-config>

Here is the relevant excerpt of the JSF page

<h:column>
                            <f:facet name="header">#{bundle.loginmanagementemail}</f:facet>
                            <h:inputText value = "#{l.email}"
                                         size ="30" rendered = "#{l.canUpdate}" />
                            <h:outputText value = "#{l.email}"
                                          rendered = "#{not l.canUpdate}" />
</h:column>
 <f:facet name="footer">
                        <h:panelGroup style="display: block; border-color: aquamarine;text-align: center;">

                            <h:commandButton id="update"
                                             value="Save updates"
                                             action="#{loginManagment.saveUpdate}" />
                            <h:commandButton id="add"
                                             value="Add User"
                                             action="adduser" />
                        </h:panelGroup>
</f:facet>

I use Netbeans on Windows 10 with Glassfish 5.1.

Thanks

BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
Darvin
  • 148
  • 8
  • BalusC: Thanks for the insight. Your recommendation is well taken and indeed, I do want the CVE to be thrown so my exception handler can catch it, though, I am not sure how JPA will perform the validation? I just set for the email component, entered an invalid email address and the record was persisted successfully, which tells me the @Email annotation on the User.email field was ignored by the JPA. I was hoping to see the JPA throw a CVE? – Darvin Jan 08 '21 at 19:22
  • BalusC: If you make your comment an answer, I will mark it as accepted. Following the link in your comment, I have successfully disabled the bean validation. Now I MIGHT have to post a new question on why JPA is not throwing a VCE when bean validation is disabled but I need to search the web first. Thanks – Darvin Jan 09 '21 at 15:09
  • BalusC: I agree with you, perhaps, I need to reconsider my error handling page and have the application to show the error next to the field that is causing the problem, however, what I don't understand is (1)- I followed your advice and turned off the BV and let the JPA deal with the validation but the JPA is not throwing VCE, it's throwing EJBException instead with no clear message regarding the error. I want CVE to be thrown so I can interrogate the cause, the source, and the path to the component that is causing the error. Is this a bad practice? – Darvin Jan 10 '21 at 16:55
  • Auh!! Thank you, I exhausted google search on this, all day yesterday. BalusC, I am a newbie on JSF but I just found your blog and start to read it ardently. Earlier in this post, you gave me a great idea of how to go about BV, but there are a few loose ends that I don't have the answers to, so I am gonna start a new post and I hope you would clarify them for me if you don't mind. Thanks again – Darvin Jan 11 '21 at 15:18
  • The custom exception handler needed for centralized exception processing. This thread started when I (as a newbie) didn't know why the handler is not being invoked. Thanks to your response I discerned that I need to disable the BV and have the container to do its job. Now, displaying the error messages at the bottom of the JSF page is not a deal-breaker and as I said in the new thread, "ultimately, I would like" to display the errors next their respective component, so that's not a requirement rather a wish list. I will keep the custom handler and leave out all the f:Validatexxx ........ – Darvin Jan 14 '21 at 08:16
  • fvalidate.xxx.. tags from the JSFs pages. Looking through the Oracle EE tutorial (my only learning resource at the moment), I do not see any mechanism in place to access the component's client Id from the exception handler otherwise, I could have sent a FacesMessage to the failing component and that would be displayed on the h: message of the component. Again, thanks for the good insights thus far. – Darvin Jan 14 '21 at 08:27
  • Before turning off the bean validation, the onus of validation was on the JSF page, things like "required=true", "requiredMessage= Must enter valid data", and some form of "f:validate" tag when needed. In addition to the JSF part, the exceptions thrown by the JPA were caught by the EJB, then using a util class to convert the exception and in the case of CVE, it extracted the needed information (like the "message", the entity class causing the exception, the entity field, the value and so on) and rethrow it to be caught by the backing managed bean......... – Darvin Jan 14 '21 at 15:56
  • The backing managed bean would form add a FacesMessage to the FacesContext and return to the JSF page. After changes to the code, the JSF pages are trimmer, there is no try-catch block in the backing beans when dealing with EJBs, the util class eliminated, and the JPA exceptions are rethrown as checked application exception just to avoid container nulling the injected EJBs. The error msgs are displayed at the bottom of the page, which is not a deal-breaker. Since I admire your comments, and now that I gave you the full picture, if you still think, I should revert to the old way, I would. – Darvin Jan 14 '21 at 16:06
  • I have been using @NotNull on the entity beans since I removed the required="true" attributes, because as you know most of those "h:inputText" components translate to fields on the Entity bean. I am going to look into ValidationMessages.properties and MessageInterpolator. Thanks again – Darvin Jan 14 '21 at 16:55

0 Answers0