3 Replies Latest reply on Apr 5, 2007 5:10 AM by christian.bauer

    JSF rendering of invalid values

    christian.bauer

      To debug another problem I need to know how JSF gets component values during RENDER RESPONSE after validation fails. I basically need someone who knows JSF internals :)

      Form:

      <h:form id="commentForm">
      
       <s:validateAll>
      
       <s:decorate id="foobarDecorate">
       <div class="entry">
       <div class="label">Entity test string:</div>
       <div class="input">
       <h:inputText tabindex="1" size="40" maxlength="100" required="true"
       id="foobar" value="#{testBean.testEntity.testString}">
       </h:inputText>
       </div>
       </div>
       </s:decorate>
      
       <s:decorate id="bazDecorate">
       <div class="entry">
       <div class="label">Test string:</div>
       <div class="input">
       <h:inputText tabindex="1" size="40" maxlength="100" required="true"
       id="baz" value="#{testBean.testString}">
       </h:inputText>
       </div>
       </div>
       </s:decorate>
      
       </s:validateAll>
      
       <div class="entry">
       <div class="label"> </div>
       <div class="input">
      
       <h:commandLink action="#{testBean.doSomething}"
       styleClass="button"><span class="buttonLabel">Do Something</span></h:commandLink>
      
       </div>
       </div>
      
      </h:form>
      


      The backing bean:

      @Name("testBean")
      @Scope(ScopeType.PAGE)
      public class TestBean implements Serializable {
      
       @In(required = false)
       private FacesMessages facesMessages;
      
       private String testString;
      
       public String getTestString() {
       System.out.println("#### GETTING: " + testString);
       return testString;
       }
      
       public void setTestString(String testString) {
       System.out.println("#### SETTING: " + testString);
       this.testString = testString;
       }
      
       private TestEntity testEntity;
      
       public TestEntity getTestEntity() {
       System.out.println("######## GETTING ENTITY INSTANCE");
       return testEntity;
       }
      
       @Create
       public void create() {
       System.out.println("###################### NEW TEST ENTITY");
       testEntity = new TestEntity();
       }
      
       public void doSomething() {
       System.out.println("################ DO SOMETHING ############################");
      
       facesMessages.addFromResourceBundleOrDefault(
       FacesMessage.SEVERITY_ERROR,
       "didSomething", "Hello! This is a message! Current test string is: " + testString + " and in entity it's: " + testEntity.testString );
      
       facesMessages.addToControl(
       "foobar",
       FacesMessage.SEVERITY_ERROR,
       "Message for component!");
      
       }
      
       public static class TestEntity implements Serializable {
      
       @Length(min = 3, max = 255)
       private String testString;
      
       public String getTestString() {
       System.out.println("#### GETTING INSIDE ENTITY: " + testString);
       return testString;
       }
      
       public void setTestString(String testString) {
       System.out.println("#### SETTING INSIDE ENTITY: " + testString);
       this.testString = testString;
       }
       }
      
      }
      


      When I enter "a" and "b" into the two form fields, validation for the first field should fail (@Length on the entity class). It does so, and it shows me the form again with "a" and "b" in the fields and the decorated validation error message.

      How do the values "a" and "b" get into the form components during RENDER RESPONSE? This is what I see in the logs:

      07:47:49,321 DEBUG [SeamPhaseListener] before phase: APPLY_REQUEST_VALUES(2)
      ... Nothing interesting ...
      07:47:49,325 DEBUG [SeamPhaseListener] after phase: APPLY_REQUEST_VALUES(2)
      07:47:49,325 DEBUG [SeamPhaseListener] before phase: PROCESS_VALIDATIONS(3)
      ... Nothing interesting ...
      07:47:49,327 DEBUG [RootInterceptor] intercepted: testBean.getTestEntity
      07:47:49,328 INFO [STDOUT] ######## GETTING ENTITY INSTANCE
      07:47:49,328 DEBUG [RootInterceptor] intercepted: testBean.getTestEntity
      07:47:49,329 INFO [STDOUT] ######## GETTING ENTITY INSTANCE
      07:47:49,333 DEBUG [RootInterceptor] intercepted: testBean.getTestString
      07:47:49,333 INFO [STDOUT] #### GETTING: null
      07:47:49,333 DEBUG [SeamPhaseListener] after phase: PROCESS_VALIDATIONS(3)
      07:47:49,334 DEBUG [AbstractSeamPhaseListener] committing transaction after phase: PROCESS_VALIDATIONS(3)
      07:47:49,334 DEBUG [SeamPhaseListener] before phase: RENDER_RESPONSE(6)
      ... Nothing interesting ...
      07:47:49,664 DEBUG [RootInterceptor] intercepted: testBean.getTestEntity
      07:47:49,665 INFO [STDOUT] ######## GETTING ENTITY INSTANCE
      07:47:49,665 INFO [STDOUT] #### GETTING INSIDE ENTITY: null
      07:47:49,690 DEBUG [SeamPhaseListener] after phase: RENDER_RESPONSE(6)
      


      So during PROCESS VALIDATIONS, my backing bean getters are being called. Why that is the case I don't know, maybe this is triggered by <s:validateAll> (haven't been able to figure out how this is implemented) or simply the JSF implementation randomly calling my model.

      Then validation obviously fails. During RENDER RESPONSE, the two input components are rendered again. They magically get the values "a" and "b" that I entered into the form. From where and how? Obviously not from my model or backing bean, because only one of them calls it's bound getter, and even that returns null.



        • 1. Re: JSF rendering of invalid values
          christian.bauer

          After more debugging:

          RENDER RESPONSE renders the exact same component instance that was invalid, and the value that is rendered is the "submittedValue"

          Now I need to find out how the implementation knows what the original component was.

          • 2. Re: JSF rendering of invalid values
            jalupa

            A JSF component has following properties (conceptual)
            a) a submitted value
            b) a local value
            c) a value

            The submitted value is filled in in the Apply Request Values phase, typically in the decode method of the component.
            The local value is filled in the Process Validations phase: this is the converted and validated submitted value. At that moment, the submitted value is cleared (only when conversion/validation succeeded).
            When all conversions and validations succeed, the local value is set as the value of the component, which means that the value(binding)expression is evaluated to propagate the local value to the backing bean (setter). Again, at that moment, the local value of the component is cleared. Of course this happens in the Update Model Values phase.

            When a page is being rendered, JSF will first look for a submitted value, if no submitted value is present, JSF will look for the local value of the component, if no local value is present, JSF will evaluate the value(binding)expression to get the value of the component (which is the value on the backing bean) (getter).

            This should explain the magic of "a" and "b" :-). "a" never got further than the submitted value of the component, "b" made it to the local value of the component.

            Now, why is the getter on the backing bean sometimes called in the process validations phase (or even in the Apply Request Values phase depending on the value of the immediate attribute)?
            In the Process Validations phase, ValueChangeEvents are being queued. Of course, a ValueChangeEvent may only be queued when the value changed (duh), therefor JSF evaluates the value(binding)expression, to compare the local value of the component (mind "local value", this means this normally only happens when conversion and validation were successful) with the value of the component (i.e. the value on the backing bean), so the getter on the backing bean is called.
            This explains why getTestString on the backingbean is being called.

            As there are validation errors for the first field, you wouldn't expect getTestEntity being called. I guess this is a Seam thing (I think it happens in the validate(String,Object) method of the Expressions class (called from ModelValidator).

            • 3. Re: JSF rendering of invalid values
              christian.bauer

              OK, thanks. I now understand the process up to that point.

              It looks like my actual problem is (not the same as the one I've shown) that I have a custom component that has a list of children in the decode() method that runs in RESTORE VIEW but that the children list is empty in the encodeBegin() method after APPLY_RQ_VALUES, PROCESS_VALIDS, in RENDER RESPONSE.

              I verified this with a debugger, let's see if I can find out who is clearing that collection.