11 Replies Latest reply on Feb 25, 2010 12:44 AM by Tim Evers

    How to validate two fields on the same validator

    Shervin Asgari Master

      Hi.


      I have a User table with UniqueContraint on (username, organization). Meaning that the combination of these two are unique.
      However, I also have a seam validator that checks if this is unique before posting the form.


      Here is a snip of the form.



      <s:decorate template="/layout/definition.xhtml">
                <ui:define name="label">#{messages['username']}</ui:define>
                <h:inputText required="true" value="#{userAdmin.currentUser.username}" rendered="#{userAdmin.currentUser.username == null}">
                     <f:validator validatorId="usernameValidator"/>
                     <s:validate />
                </h:inputText>
                <h:inputText disabled="true" value="#{userAdmin.currentUser.username}" rendered="#{userAdmin.currentUser.username != null}" />
           </s:decorate>
      
      <s:div rendered="#{s:hasRole('sysadmin')}">
           <s:decorate template="/layout/definition.xhtml">
                <ui:define name="label">#{messages['organization']}</ui:define>
                <h:selectOneMenu value="#{userAdmin.currentUser.organization}" >
                     <s:selectItems value="#{organizations}" noSelectionLabel="#{messages['organization.none']}"  var="kommune" label="#{kommune.name}"/>
                     <f:converter converterId="restrictedEntityConverter" />
                </h:selectOneMenu>
           </s:decorate>
           </s:div>



      My UniqueUsernameValidator


      @Name("usernameValidator")
      @Validator
      @BypassInterceptors
      @Transactional
      public class UniqueUserValidator implements javax.faces.validator.Validator, Serializable {
      
           private static final long serialVersionUID = 6086572792387091314L;
      
           @SuppressWarnings("unchecked")
           public void validate(FacesContext facesContext, UIComponent component, Object value) throws ValidatorException {
                EntityManager entityManager = (EntityManager) Component.getInstance("entityManager");
                String newUsername = "" + value;
      
                ProcessUser kommuneAdmin = (ProcessUser) Component.getInstance("currentUser");
      
                if (kommuneAdmin != null && kommuneAdmin.getOrganization() != null) {
                     List<ProcessUser> users = entityManager.createQuery(
                               "SELECT DISTINCT p FROM " + ProcessUser.class.getName() + " p where lower(p.username) = :name" + " AND p.organization.id = :orgId").setParameter("name",
                               newUsername.toLowerCase()).setParameter("orgId", kommuneAdmin.getOrganization().getId()).getResultList();
                     if (!users.isEmpty()) {
                          Map<String, String> messages = Messages.instance();
                          throw new ValidatorException(new FacesMessage(FacesMessage.SEVERITY_ERROR, messages.get("admin.userexists"), messages.get("admin.userexists")));
                     }
                }
           }
      }




      Now the thing is that I can in the usernameValidator recieve the username, however I don't know how to send the organization as a parameter too.
      Any tips?




        • 1. Re: How to validate two fields on the same validator
          Shervin Asgari Master

          FYI.


          I believe the answer to my question is You cannot validate two fields in one component.
          So I solved this by validating in the action method instead.

          • 2. Re: How to validate two fields on the same validator
            alex becker Newbie
            Hi.
            I have the same problem and will not use a validation inside my action.

            I have a registerform with email and password and passwordVerify. All looks fine (validate the email with hibernates @Email, the password fields with @Length).
            Furtermore i will check password.equals(passwordVerify).
            I need a multifield Validator.

            First Try:
            I wrote my  own Equals Validator. But i can't inject the password


            ActionBean...

            `// the password
            @NotEmpty
            @Length(min=8,max=20)
            String pw;
            ..
            @Length(min=8,max=20)
            @NotEmpty
            @Equals(password="#{pw}")
            String passwordVerify;
            `

            Unfortunately is the value of password inside of my EqualsValidator only "#{pw}".
            I tried @Equals(password="pw") --> get the compilermessage (please only constants for Annotions..."

            The second Try:

            ActionBean....



            `     @AssertTrue
                 public boolean verifyPw(){
                      return this.getPw().equals(this.getPwVerify());
                 }
            `

            Unfortunately the method was not called (inside the validate phase of jsf).

            I will not implement a validation inside my actionmethod. This method will be called after the validate phase and i will not show errors messages in a to step dialog.


            Has nobody a suggestion to implement a mulitfield validations (without JSF Validator) with seam and hibernatevalidators?

            Thanks for help.
            alex

            • 3. Re: How to validate two fields on the same validator
              Shervin Asgari Master

              If you dont want to use a validator, you can also use @PrePersist and @PreUpdate to do the check of equality there.


              Furthermore, there is a <s:validateEquality> which is in Seam 2.2.0 which you can use to check if the password fields are the same.

              • 6. Re: How to validate two fields on the same validator
                alex becker Newbie

                Thanks.


                I'm using seam 2.1.2 and can't use the


                <s:validateEquality>.




                And what is, if i have another usecase where i have to validate mutiple fields? I'm looking for a suggestion to implement my own validator.


                alex

                • 7. Re: How to validate two fields on the same validator
                  Arbi Sookazian Master

                  What's the problem with writing multiple classes that implement javax.faces.validator.Validator and are annotated with javax.faces.validator.Validator?


                  Each one will handle one field (unless that validator is reusable for multiple fields of course).

                  • 8. Re: How to validate two fields on the same validator
                    Ingo Jobling Master

                    Hello Alex,


                    You can use f:attribute to pass the value of the other field to your validator:



                    <h:inputText id="organization" value="#{myBean.organization}" />
                    
                    <h:inputText id="username" value="#{myBean.username} >
                        <f:validator validatorId="usernameValidator"/>
                        <f:attribute name="organization" value="#{myBean.organization}" />
                    </h:inputText>
                    


                    Now, within the validator, you can obtain the value of the attribute


                    public class UsernameValidator implements javax.faces.validator.Validator
                    
                        public void validate(FacesContext facesContext, UIComponent component, Object value) throws ValidatorException {
                    
                            Organization organisation = (Organization)component.getAttribute("organization")



                    Regards, Ingo

                    • 9. Re: How to validate two fields on the same validator
                      Ingo Jobling Master

                      Oops, actually :


                      component.getAttributes().get("organisation");



                      • 10. Re: How to validate two fields on the same validator
                        Serkan Eskici Novice

                        OK, if you have annotated your Entity with that @UniqueConstraint, then why don't you just catch the exception in your pages.xml when it is violated ?


                        for example:


                        <exception class="javax.persistence.EntityExistsException">
                           <redirect>
                             <message severity="ERROR">
                                 #{messages['error.entity.exists.exception']}
                                 </message>
                           </redirect>
                        </exception>
                        



                        Note that you don't provide the view-id in redirect, so that the message is shown in the page where the validation error occurs !

                        • 11. Re: How to validate two fields on the same validator
                          Tim Evers Master

                          I think the problem here is that you are confusing the issue of what a validator is.


                          A validator is used to determine if the value in a single field is valid for insert/update. So regardless of if they have entered a duplicate name, the actual values in the inputs are 'valid'.


                          The problem with the values is that they violate a business rule that you have that there cannot be two identical values in the database.


                          This type of validation rule would generally be tested inside your action method because you are not atually testing if the data they entered is 'valid' from a java/casting perspective. You are testing if the data is 'valid' from a business perspective.


                          And what do you mean by this?




                          I will not implement a validation inside my actionmethod. This method will be called after the validate phase and i will not show errors messages in a to step dialog.


                          from an end user perspective how would they know if the validation failed during the Validation Phase as you put it, or the Application Invoke phase? As long as you return null from your method if there is a validation error the end result is identical.


                          I'm really confused as to what you mean by




                          i will not show errors messages in a to step dialog.


                          Ultimately your action method should look like this


                          public String saveButtonClicked() {
                              if (!isUserNameValid()) {
                                  return null;
                              }
                              //do normal processing
                          }
                          
                          public boolean isUserNameValid() {
                              //query to check if there is another user in the system with the same username
                              User u = queryForUserWithUserName(....);
                              if (null != u) {
                                  //Add error message to faces context
                                  return false;
                              }
                              return true;
                          }
                          



                          And after all that is done you sill need to put it in a try catch because another user in your system could have entered the same user just before you and your transaction will fail if you have a unique constraint over the two columns.
                          So regardless of if you do your validation in a 'Validator' you STILL need to cater for it in your application invoke phase anyways.