eclipse-ee4j/mojarra

class-level bean validation: Array data not copied after bean cloning

thomaspries opened this issue · 2 comments

Describe the bug

After cloning the bean, all scalar values are copied into the new bean for validation, but modified data in array stay at initial value altough the input values are converted an validated correctly.

To Reproduce

Steps to reproduce the behavior:

  1. Follow one of the ubiquitous password-examples for WholeBeanValidation in JSF (e.g. this one)
  2. make simple Datatype
class TestData{ 
int a; 
int b; 
... }
  1. add
TestData[] data;

to Backingbean and initialise the array with some data (to verify, that conversion and validation of TestData works, add scalar TestData x1;)
6. I'm uncertain, if i have to allocate a new Array in the clone method by hand or not, tried both, same effect
7. add Converter and Validator for TestData-type
8. gather data in facelet via

<ui:repeat value="#{testController.data}" varStatus="status" var="d">
  <h:inputText id="valInput_#{status.index}" value="#{testController.data[status.index]}">
     <f:validator validatorId="testDataFacesValidator"/>
     <f:validateBean validationGroups="ValidationGroup"/>
   </h:inputText>
</ui:repeat>
  1. Validation phase:
public class PasswordValidator implements ConstraintValidator<Password, PasswordHolder> {

  @Override
  public void initialize(Password constraintAnnotation) { }

  @Override
  public boolean isValid(PasswordHolder value, ConstraintValidatorContext context) {
    boolean result;

    // both password strings are visible with new data => ok
    // scalar TestData is visible with new data => ok
    // array still contains initial values => not ok

    return result;
  }
}

Expected behavior

I would like to see altered data in the array to validate them together with the other data.

Desktop

  • Client OS: Win 11
  • Server OS: NetBSD
  • payara-6.2024.2
  • Browser Firefox 126
  • Mojarra 4.0.0.payara-p2

This is a known limitation. Nested properties aren't supported by <f:validateWholeBean>. It only supports the bean's own "flat" properties.

Consider using OmniFaces <o:validateBean> instead. It not only supports nested properties but it will also greatly reduce the <f:validateBean> and clone() boilerplate. Detecting nested properties and associating validation errors with them is actually quite complex and a lot of work has been done in crystallizing the <o:validateBean> on this.

In order to use <o:validateBean>:

  1. Remove all <f:validateBean> and <f:validateWholeBean> tags.
  2. Remove Cloneable interface and clone() method from backing bean.
  3. Put @jakarta.validation.Valid annotation on private TestData[] data; property of backing bean.
  4. In place where the <f:validateWholeBean> tag was originally located in XHTML, add the <o:validateBean> with exactly the same attributes:
<o:validateBean value="#{testController}" validationGroups="ValidationGroup" />

Note that you can further simplify your <ui:repeat> on data as follows as well:

<ui:repeat value="#{testController.data}" var="d">
  <h:inputText id="valInput" value="#{d.a}"> <!-- and another one for #{d.b}? -->
    <f:validator validatorId="testDataFacesValidator"/>
  </h:inputText>
</ui:repeat>

Also note that using EL in ID attribute of <h:inputText> within <ui:repeat> has no effect. It has only effect when you use <c:forEach> instead but that's not necessary. See also https://stackoverflow.com/q/3342984

Thank's for your response (I somehow missed that fact in the documentation), OmniFaces looks promising, I will use that.

... I implemented the steps mentions above and it works perfectly. :-))