1 2 Previous Next 15 Replies Latest reply on Oct 10, 2007 8:56 AM by pmuir

    Hibernate and regular JSF validation and EntityHome prob

      I have a very simple page that edits the "first name" property of a Person entity using an EntityHome object. The page has an input text control and an update button that is only rendered if the "managed" property is true. I want to place two constraints on editting the first name:

      (1) minimum length must be at least 2 characters
      (2) first name cannot equal to "jsf"

      I'm using Hibernate validators to do (1) via @Length(min = 2), and I'm using regular JSF validation to do (2). (2) is enabled by appending f:validator tag in my input text control.

      Simple enough, but I'm encountering a couple strange behaviours.

      If you enter "jsf" to force regular JSF validation, then the "managed" property of the EntityHome becomes false and the update button disappears, but you'll get the validation error message on the screen. Why does the "managed" property becomes false after a regular JSF validation error?

      If you enter the letter 'a' to force a hibernate validation error (because minimum length is less than 2), then I get an exception with message:
      "Caused by org.hibernate.validator.InvalidStateException with message: "validation failed for: com.mxnmedia.siteaudit.model.Person". Note at this time I still have the f:validator tag attached to the input text control. Now, if I remove f:validator tag to disable regular JSF validation, then I'll get the expected "length must be between 2 and 2147483647" message AND the update button will still be there.

      Below is the minimal code I have to reproduce this. I was using latest Seam-CVS. Edit page is accessed by [whatever_your_context_path_is/test.seam?id=1]

      ## JsfValidator.java

      @Name("jsfValidator")
      @Validator
      public class JsfValidator implements javax.faces.validator.Validator, Serializable {
       public void validate(FacesContext facesContext, UIComponent uiComponent, Object object)
       throws ValidatorException {
       if ("jsf".equals((String) object))
       throw new ValidatorException(new FacesMessage("Validation failed"));
       }
      }
      


      ## components.xml
      <fwk:entity-home name="personHome" entity-class="Person"/>
      


      ## import.sql
      INSERT INTO person(id,firstname) VALUES (1,'Joe');
      


      ## Person.java
      @Entity
      public class Person {
      
       @Id
       @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "generator")
       @SequenceGenerator(name = "generator", sequenceName = "person_seq")
       private Long id;
      
       @Length(min = 2)
       private String firstName;
      
       // getters and setters for firstName and id
      


      ## test.page.xml
      <page ..>
       <begin-conversation join="true"/>
       <param name="id" value="#{personHome.id}" converterId="javax.faces.Long"/>
      </page>
      


      ## test.xhtml
      <h:form>
       <f:facet name="afterInvalidField">
       <s:message/>
       </f:facet>
      
       <s:validateAll>
       <s:decorate>
       <h:inputText value="#{personHome.instance.firstName}"
       required="true">
       <f:validator validatorId="jsfValidator"/>
       </h:inputText>
       </s:decorate>
       </s:validateAll>
      
       <h:commandButton action="#{personHome.update}"
       value="Update"
       rendered="#{personHome.managed}"/>
      </h:form>
      


        • 1. Re: Hibernate and regular JSF validation and EntityHome prob
          gavin.king

          The reason for (2) is that s:validateAll ONLY adds a (Hibernate) validator if you didn't specify any validators yourself. Since you have your own JSF validator, you need to specify <s:validate/> in addition to the JSF validator.

          As for (1), I thought you just reported in JIRA that this problem was solved for you.

          • 2. Re: Hibernate and regular JSF validation and EntityHome prob
            gavin.king

            Oh and shouldn't your EntityHome be conversation scoped?

            • 3. Re: Hibernate and regular JSF validation and EntityHome prob

               

              "gavin.king@jboss.com" wrote:
              The reason for (2) is that s:validateAll ONLY adds a (Hibernate) validator if you didn't specify any validators yourself. Since you have your own JSF validator, you need to specify <s:validate/> in addition to the JSF validator.

              Thanks--that solves (2). But this case isn't documented, and would certainly cause confusion for those who encounter similar troubles down the road by trying to mix s:validateAll and regular-JSF validators, which isn't that uncommon. The Seam validation reference documentation may need a slight enhancement:
              However, it is not much less verbose than what we started with, so let's try <s:validateAll>..
              /* example with s:validateAll here */
              This tag simply adds an <s:validate> to every input in the form. For a large form, it can save a lot of typing!
              


              "gavin.king@jboss.com" wrote:

              As for (1), I thought you just reported in JIRA that this problem was solved for you.

              I reported this on JIRA this morning on my home PC, but prior to posting this thread I had tested (1) at work literally 15-20 times (with the most up-to-date Seam-CVS) just to make sure I'm not imagining things. The problem still exists on my end. My earlier report that it was fixed was erroneous, and I apologize if I had responded too quickly. I think I lost some credibility here, so maybe I need someone to vouch for me on this issue when they encounter it. =)

              "gavin.king@jboss.com" wrote:

              Oh and shouldn't your EntityHome be conversation scoped?

              EntityHome is, by default, conversation-scoped right? I am assuming my declaration in components.xml as [fwk:entity-home name="personHome" entity-class="Person"] wouldn't change the default scope. Anyhow, I doubled-check and ensured that when the long-running conversation started, the EntityHome was joining that conversation properly (as per debug.seam)

              • 4. Re: Hibernate and regular JSF validation and EntityHome prob
                gavin.king

                OK, yes, that is the default.

                So, now try putting a breakpoint in EntityHome.isManaged() and finding out why it returns false.

                Does the EntityHome somehow "forget" its instance? Does the persistence context change between calls?

                • 5. Re: Hibernate and regular JSF validation and EntityHome prob

                   

                  @Transactional
                  public boolean isManaged()
                  {
                   return getInstance()!=null &&
                   getEntityManager().contains( getInstance() );
                  }
                  


                  getEntityManager().contains( getInstance() ) is returning false is the culprit.

                  So I checked whether the EM has changed by looking at the hash code (is this a valid way of doing it?), and the hash codes remain constant throughout the conversation. As well, the (T) getComponentInstance( getPersitenceContextName() ) is only called once, which is expected, so it doesn't appear the EM is ever null.

                  The getInstance() hasn't changed either--it's not null.

                  Is there anywhere in Seam where em's cache is cleared either explicitly or implicitly?

                  • 6. Re: Hibernate and regular JSF validation and EntityHome prob
                    gavin.king

                    How many times does ManagedPersistenceContext.initEntityManager() get called?

                    How many times does Home.setInstance() get called?

                    Put breakpoints.

                    • 7. Re: Hibernate and regular JSF validation and EntityHome prob

                       

                      "gavin.king@jboss.com" wrote:
                      How many times does ManagedPersistenceContext.initEntityManager() get called?

                      How many times does Home.setInstance() get called?

                      Put breakpoints.


                      For both questions, once.

                      OK, I'm exhausted after trying to trace through at the end of the day...BUT here's my finding:

                      I traced down to handleTransactionsAfterPhase() in SeamPhaseListener.java, line 312.

                       public void handleTransactionsAfterPhasel(PhaseEvent event)
                       {
                       if ( Init.instance().isTransactionManagementEnabled() )
                       {
                       log.info("handleTransactionsAfterPhase committing here**************");
                       PhaseId phaseId = event.getPhaseId();
                       boolean commitTran = phaseId==PhaseId.INVOKE_APPLICATION ||
                       event.getFacesContext().getRenderResponse() || //TODO: no need to commit the tx if we failed to restore the view
                       event.getFacesContext().getResponseComplete() ||
                       ( phaseId==PhaseId.RENDER_RESPONSE && !Init.instance().isClientSideConversations() );
                      
                       if (commitTran)
                       {
                       commitOrRollback(phaseId); //we commit before destroying contexts, cos the contexts have the PC in them
                       }
                       }
                       }
                      


                      This code suggests to commit/rollback transaction if validation fails, since you are checking if event.getFacesContext().getRenderResponse(). This is the same check you use in org.jboss.seam.core.Validation in afterProcessValidations. Correct me if I'm wrong.

                      Could this be why my managed property is set to false, and the em could not find the instance anymore?




                      • 8. Re: Hibernate and regular JSF validation and EntityHome prob
                        gavin.king

                        No. Its an extended persistence context. It outlasts the transaction.

                        • 9. Re: Hibernate and regular JSF validation and EntityHome prob

                          When

                          commitOrRollback(phaseId);
                          


                          is called, it's actually doing a rollback instead of a commit. Does doing a rollback would cause this strange behaviour then?

                          It looks like RollbackInterceptor is doing a rollback, since a ValidationException is thrown.


                          • 10. Re: Hibernate and regular JSF validation and EntityHome prob
                            gavin.king

                            Yes, it would definitely cause your problem.

                            Hold on: you didn't mention anything about a ValidationException before!

                            A ValidationException is a runtime exception, and hence causes tx rollbacks. (This is standard EJB rules.)

                            You should not throw ValidationExceptions.

                            • 11. Re: Hibernate and regular JSF validation and EntityHome prob

                              Hmm, so I guess regular-JSF validations is not recommended and should not be used then?

                              Very well... I'm going to let this issue rest, but hopefully others will find this discussion useful and will save them some trouble if they encounter these sorts of issues.

                              Thanks for your help.

                              • 12. Re: Hibernate and regular JSF validation and EntityHome prob
                                gavin.king

                                I really don't know what you're talking about. "Regular" JSF validation definitely doesn't work by you throwing exceptions around!

                                • 13. Re: Hibernate and regular JSF validation and EntityHome prob
                                  gavin.king

                                  Oh, hold on, I think I understand now - you are using a Seam component marked @Validator as your JSF validator. And it rolls back the transaction when you throw the exception.

                                  Yeah, you would need to mark the Seam component @Intercept(NEVER).

                                  I guess I should have special handling for ValidatorException.

                                  • 14. Re: Hibernate and regular JSF validation and EntityHome prob

                                     

                                    "gavin.king@jboss.com" wrote:
                                    [...]
                                    I guess I should have special handling for ValidatorException.
                                    [...]


                                    Did you get to do that? I have a similar problem..
                                    Should I file a JIRA issue?

                                    1 2 Previous Next