2 Replies Latest reply on Jan 5, 2009 5:29 PM by Aldo Bonzi

    my seam is unravelling - can someone help explain what seam is doing with my code ?

    William Woodman Master

      trying to create a basic registratrion module for my test app.


      my form values dont get set in backing action and the values appears to be null.  I think it must be something to do with timing when updates made on forms but i see other strange behaviour when i put log code in where my passwordManager component gets instantiated twice which i also dont understand.


      I also cant the passwordManager component to validate between two fields (password and validatePassword)


      here is my console log when i go from my mainpage to the registration page.


      6:54:39,516 INFO  [STDOUT] new passwordManager created 
      16:54:39,516 INFO  [STDOUT] new passwordManager created 
      16:54:39,516 INFO  [MemberHome] newMember factory create initiated
      16:54:39,532 INFO  [STDOUT] new member constructor called
       
      16:54:39,548 INFO  [PasswordManager] get hashed password null
      16:54:39,548 INFO  [PasswordManager] get hashed confirmation password null
      



      when i trigger the registration action in pages.xml i start a conversation like this.


      pages.xml..


         <page view-id="/register.xhtml" action="#{conversation.begin}" >
         ...
      



      then i have the register.xhtml which uses EL to store first, last names, etc in a component called newMember, that is generated by a Factory method in the MemberHome code. 


      I had to use ajax to get the confirmationPassword valueChangeListener to fire.  I thought this would trigger as i tabbed out of the field - but it doesnt unless i put the ajax tag in.


      here is the form


      register.xhtml


      <ui:composition xmlns="http://www.w3.org/1999/xhtml"
           xmlns:a4j="http://richfaces.org/a4j"
          xmlns:s="http://jboss.com/products/seam/taglib"
          xmlns:ui="http://java.sun.com/jsf/facelets"
          xmlns:f="http://java.sun.com/jsf/core"
          xmlns:h="http://java.sun.com/jsf/html"
          xmlns:rich="http://richfaces.org/rich"
          template="layout/template.xhtml">
      
      <ui:define name="body">
      
          <h:form id="registerMemberForm">
      
              <rich:panel>
                  <f:facet name="header">ForstersList Member Registration</f:facet>
      
                     <s:decorate id="genderField" template="layout/edit.xhtml">
                      <ui:define name="genderField">Gender</ui:define>
                      <h:selectOneRadio id="gender" value="#{newMember.gender}">
                               <s:convertEnum />
                               <s:enumItem enumValue="MALE" label="Male"/>
                               <s:enumItem enumValue="FEMALE" label="Female"/>
                       </h:selectOneRadio>
                  </s:decorate>
      
                   <s:decorate id="firstNameField" template="layout/edit.xhtml">
                      <ui:define name="label">First Name</ui:define>
                      <h:inputText id="member" required="true"
                                   value="#{newMember.firstName}"/>
                  </s:decorate>
      
                  <s:decorate id="lastNameField" template="layout/edit.xhtml">
                      <ui:define name="label">Last Name</ui:define>
                      <h:inputText id="member" required="true"
                                   value="#{newMember.lastName}"/>
                  </s:decorate>
      
                 <s:decorate id="usernameField" template="layout/edit.xhtml">
                      <ui:define name="label">Username</ui:define>
                      <h:inputText id="member" required="true"
                                   value="#{newMember.username}"/>
                  </s:decorate>
      
                 <s:decorate id="passwordField" template="layout/edit.xhtml">
                      <ui:define name="label">Password</ui:define>
                      <h:inputSecret id="password" required="true"
                                   value="#{passwordManager.password}"/>
                  </s:decorate>
      
                 <s:decorate id="confirmPasswordField" template="layout/edit.xhtml">
                      <ui:define name="label">Confirm Password</ui:define>
                      <h:inputSecret id="confirm" required="true"
                                   value="#{passwordManager.confirmationPassword }"
                                   valueChangeListener="#{passwordManager.checkConfirmationPassword}">
                               <a4j:support id="confirmPasswordField"
                                              event = "onblur"
                                              reRender = "confirmPasswordField"
                                              ajaxSingle = "true"
                                              bypassUpdates = "true" />
                      </h:inputSecret>             
                  </s:decorate>
      
                  <div style="clear:both">
                       <span class="required">*</span> required fields
                     </div>
                     
             </rich:panel>
      
              <div class="actionButtons">
                 <h:commandButton id="register"
                                value="register"
                               action="#{registrationManager.register}"
                            immediate="true"
                             rendered="#{!memberHome.managed}"/>
                  <s:button propagation="end"
                                     id="done"
                                  value="Done"
                                   view="/memberList.xhtml"/>
              </div>
      
          </h:form>
      
      </ui:define>
      
      </ui:composition>
      



      my problems seem to start here and on into the registrationManager.register action.  when i look with the debugger enabled - my passwordManager component has null fields (this as the fact that the earlier log shows the passwordManager component beeing instantiated twice.


      in my value change listener function called by the ajax, i see the previous old null value and the value i typed in when i tabbed off the validation password field.  However the password field in the passwordManager is null also (so the form update into the password field has not taken place?


      
      @Scope (ScopeType.CONVERSATION) //now set to conversation scope
      @Name ("passwordManager")
      public class PasswordManager 
      {
           //establish default
           private String digestAlgorithm = "SHA-1";
           private String charset = "UTF-8";
           
           private String password;
           private String confirmationPassword;
           
           @In private FacesMessages facesMessages;
           @Logger Log log;
           
           public PasswordManager()
           {
                System.out.println ("new passwordManager created ");
                //log.info("new passwordManager created ");
           }
           
           public void setDigestAlgorithm (String algorithm)
           {
                this.digestAlgorithm = algorithm;
           }
           
           public void setCharset (String charset)
           {
                this.charset = charset;
           }
           
           /*
            * returns a hex encoded string representation of the plain
            * text password presented
            */
           public String hash (String plainTextPassword)
           {
                try 
                {
                     if (plainTextPassword != null)
                     {
                          MessageDigest digest = MessageDigest.getInstance(digestAlgorithm);
                          digest.update((plainTextPassword.getBytes(charset)));
                          byte[] rawHash = digest.digest();
                          return new String (Hex.encodeHex(rawHash));
                     }
                     else 
                          return null;
                }
                catch (Exception e)
                {
                     throw new RuntimeException (e);
                }
           }
           
           public String getPassword ()
           {
                log.info("get hashed password #0", password);
                return password;
           }
           
           public void setPassword (String password)
           {
                log.info("set hash password #0", password);
                if (password != null)
                     this.password =  hash (password);
                else 
                     this.password = null;
           }
      
           
           public String getConfirmationPassword ()
           {
                log.info("get hashed confirmation password #0", confirmationPassword);
                return confirmationPassword;
           }
           
           public void setConfirmationPassword (String confirmationPassword)
           {
                log.info("set hash confirmation password #0", confirmationPassword);
                if (confirmationPassword != null)
                     this.confirmationPassword = hash (confirmationPassword);
                else 
                     this.confirmationPassword = null;
           }
      
           /*
            * check password and confirmation password are equal
            */
           public boolean verify ()
           {
                return confirmationPassword != null && confirmationPassword.equals(password);
           }
           
           public boolean checkConfirmationPassword (ValueChangeEvent e)
           {
                String confirm = (String) e.getNewValue();
                if (confirm != null && !hash (confirm).equals (password) )
                {
                     facesMessages.addToControl(e.getComponent().getId(), "Your confirmation password is not the same as your password, try again" );
                }
                return confirm != null && confirm.equals(password);
           }
      }
      



      lastly here is my registerAction component  which is supposed to check form fields are correct (by calling the validate method of the passwordManager component) and then persist the member to the database.


      however again in debug when i look at the newMember and passwordManager which are declared with @In and check these variables - they all contain null values in all the field entries of the injected context variables.


      Why dont they come in with the values i type on my form before hitting the register button. 


      I'd really appreciate a little help resolving what seam does and when it should make the updates from forms to backing components.


      registerAction


      @Scope (ScopeType.CONVERSATION)
      @Name("registrationManager")
      public class RegisterAction //implements Register
      {
           // force creation of new member 
         //@In (create=true) private Member newMember;
         //@In (create=true) private PasswordManager passwordManager;
           //these should be present already
           @In  private Member newMember;
           @In  private PasswordManager passwordManager;
         
         
         //@PersistenceContext - can only be used in  ejb
           @In
         private EntityManager entityManager;
      
         @Logger private Log log;
         
         @In
         private FacesMessages facesMessages;
         
         private boolean registered;
         
         /*
         @Begin(join = true) @Create
         public void newRegistration()
         {
            if (newMember == null)
            {
                 MemberHome memberHome = new MemberHome();
                newMember = memberHome.getInstance();
                Role defaultRole = new Role();
                defaultRole.setName("member");
                newMember.addRole(defaultRole);
                  facesMessages.addToControl("new registration", "created conversation and new user with default role");
            }
         }*/
         
         @End (ifOutcome = "success")
         public String  register()
         {
              String hashedPassword;
              
              // test password and confirmation password are the same 
              if (passwordManager.verify())
              {
                   hashedPassword = passwordManager.getPassword();
                   newMember.setPasswordHash(hashedPassword);
                   facesMessages.addToControl("verified", "Passwords match");
                   log.info("registerAction.register checked that member has same password #0", hashedPassword);
              }
              else
              {
                   log.info("registerAction.register confirm #{passwordManager.password} and confirmation password #{passwordManager.confirmationPassword} are not the same ");
                   facesMessages.addToControl("confirm", "Passwords do not match #{passwordManager.password} and #{passwordManager.confirmationPassword}");
                   invalidate ();
                   // forces page to be re rendered
                   return null;
              }
              
              log.info("register() action called");
              facesMessages.add ("register member");
             
             if (isUsernameAvailable ("#{member.username}"))
             {
                  //set the default role at this stage for the new member
                  Role defaultRole = new Role ();
                  defaultRole.setName("member");
                   newMember.addRole(defaultRole);
                   entityManager.persist(newMember);
                  facesMessages.add("Successfully registered new Member as #{member.username}");
                  registered = true;
                  return "success";
              }
              else
              {
                  facesMessages.addToControl("username", "Member with username #{member.username} already exists");
                  return "duplicate";
              }
         }
      
         public boolean isUsernameAvailable (String memberUsername)
         {
             List<Member> existing = (List<Member>)entityManager.createQuery("select m.username from Member m where m.username=#{member.username}")
             .getResultList();
             if (existing.size() == 0)
                  return true;
             else
                  return false;
      
         }
         
         public void invalidate()
         {
              log.info("registerAction.register invalidate action triggered, re enter password ");
              facesMessages.addToControl("registerAction : verify failed", "Re-enter your password");
              passwordManager.setConfirmationPassword(null);
              passwordManager.setPassword(null);
              newMember.setPasswordHash(null);
              registered = false;
         }
         
         public boolean isRegistered()
         {
            return registered;
         }
      
         @Remove @Destroy
         public void destroy() {}
      }
      



      any help in understanding whats happening here would be most appreciated.