0

I try to use JSF in combination with Bean Validation. Basically, everything works well, the validation works as expected, I get the correct message, but there is an exception on my Glassfish console:

Warnung:   EJB5184:A system exception occurred during an invocation on EJB MyEntityFacade, method: public void com.mycompany.testbv.AbstractFacade.create(java.lang.Object)
Warnung:   javax.ejb.EJBException
at com.sun.ejb.containers.EJBContainerTransactionManager.processSystemException(EJBContainerTransactionManager.java:748)
....
....
at org.glassfish.grizzly.threadpool.AbstractThreadPool$Worker.run(AbstractThreadPool.java:544)
at java.lang.Thread.run(Thread.java:744)
Caused by: javax.validation.ConstraintViolationException: Bean Validation constraint(s) violated while executing Automatic Bean Validation on callback event:'prePersist'. Please refer to embedded ConstraintViolations for details.

This exception occurs if I use custom constraints as well as predefined constraints.

Here is my sample code.

Sample Entity:

@Entity
@ValidEntity
public class MyEntity implements Serializable {

    private static final long serialVersionUID = 3104398374500914142L;
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;
    @Size(min = 2)
    private String name;

    public MyEntity(String name) {
        this.name = name;
    }

    public MyEntity() {

    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

Custom constraint:

@Constraint(validatedBy = MyValidator.class)
@Target({FIELD, METHOD, TYPE})
@Retention(RUNTIME)
public @interface ValidEntity {
    String message() default "fail";
    Class<?>[] groups() default {};
    Class<? extends Payload>[] payload() default {};
}

Custom validator:

public class MyValidator implements ConstraintValidator<ValidEntity, MyEntity>{

    @Override
    public void initialize(ValidEntity a) {
    }

    @Override
    public boolean isValid(MyEntity t, ConstraintValidatorContext cvc) {
        return false;
    }
}

Sample Controller:

@Named
@SessionScoped
public class MyController implements Serializable {

    private static final long serialVersionUID = -6739023629679382999L;

    @Inject
    MyEntityFacade myEntityFacade;
    String text;

    public String getText() {
        return text;
    }

    public void setText(String text) {
        this.text = text;
    }

    public void saveNewEntity() {
        try {
            myEntityFacade.create(new MyEntity(text));
        } catch (Exception e) {
            Throwable t = e;
            while (t != null) {
                if (t instanceof ConstraintViolationException) {
                    FacesContext context = FacesContext.getCurrentInstance();
                    Set<ConstraintViolation<?>> constraintViolations = ((ConstraintViolationException) t).getConstraintViolations();
                    for (ConstraintViolation<?> constraintViolation : constraintViolations) {
                        FacesMessage facesMessage = new FacesMessage(constraintViolation.getMessage());
                        facesMessage.setSeverity(FacesMessage.SEVERITY_ERROR);
                        context.addMessage(null, facesMessage);
                    }
                }
                t = t.getCause();
            }
        }
    }
}

Sample jsf page:

<html xmlns="http://www.w3.org/1999/xhtml"
  xmlns:h="http://xmlns.jcp.org/jsf/html">
    <h:head></h:head>
    <h:body>
        <h:form>
            <h:messages id="messages" />
            <h:inputText value="#{myController.text}" />
            <h:commandButton value="Save" action="#{myController.saveNewEntity()}" />
        </h:form>
    </h:body>
</html>

The MyEntityFacade only calls persist from entity manager.

As mentioned before, the application is running fine and the correct messages are shwon, but I want to avoid this exception in the Glassfish console.

Setting the validation mode in persistence.xml to NONE as discussed here is no option, because I want a validation.

I use JSF in version 2.2, the implementation is Mojarra. The version of Bean Validation is 1.1, the implementation is Hibernate Validator. Application Server is Glassfish 4.0.

Community
  • 1
  • 1
mare
  • 402
  • 6
  • 14

1 Answers1

1

Class-level constraints do not work with JSF. Take a look at this answer. When you press the 'Save' button JSF checks only if name has at least 2 chars and does not take into account the ValidEntity constraint. JPA, on the other hand, complains that the bean is not valid and throws an exception.

UPDATE

1) the @Size constraint is on MyEntity.name property while in the facelet you have MyController.text property. In the JSF perspective there is nothing to validate. It has no knowledge of the MyEntity at all.

2) ValidEntity is always invalid, so JPA will always throw the exception (unless you disable validation) even if you properly set the MyEntity.name in the facelet.

Community
  • 1
  • 1
Alf
  • 2,291
  • 1
  • 28
  • 34
  • Thanks a lot for your quick answer. Well the exception is thrown by JPA? But why isn't it possible to catch it correctly? And as I mentioned in my question, the exception is also thrown if only the @Size contraint is violated. – mare Apr 10 '14 at 08:32
  • See the update. I didn't see that in your controller there is no reference to MyEntity – Alf Apr 10 '14 at 09:17
  • Ah, you are completely right with the text property in the facelet. Yes, it is just a test code, and the custom validator should always fail and a exception should be thrown, but my problem or rather my question is: Why is this exception not completly caught in the MyController class? Something is caught, because the message "fail" is printed to the JSF messages component, but on the GF console, the stacktrace (see my initial question) is printed anyway. This is a bit confusing for me. – mare Apr 10 '14 at 09:24
  • It's just a matter of logging. JPA throws the exception, Glassfish EJB Container catches it, wrap it in a EJBException and write it in the log file. However the point is that you have no JSF validation at all. JSF validate data before it calls the action. If data are valid then it calls the action otherwise it automatically inform the user. You have no need to catch the exception in the saveNewEntity() method – Alf Apr 10 '14 at 09:37
  • Try to bind to MyEntity.name directly in your facelet. Than if you type only one character in the text input you will not see any Glassfish error anymore since JSF will handle the validation before it calls the facade. – Alf Apr 10 '14 at 09:49
  • This is what I tried after your first answer. And you're right. A violation of the Size constraint doesn't lead no longer to an output of the stack trace. But for the other constraint (ValidEntity) it still leads to an output, although the catch block in mycontroller handle the exception and print the predefined message ('fail') to the jsf messages element. So, why is the stack trace printed, although the exception seems to be handled correctly. – mare Apr 10 '14 at 10:10
  • Because it is a class-level constraint and, as I wrote in the answer, JSF does not handle this kind of constraint. – Alf Apr 10 '14 at 10:22