1 2 Previous Next 23 Replies Latest reply on Jan 27, 2012 9:25 AM by Sri Ram Gidda

    List Shuttle Validation Error

    Josh Martin Newbie

      Hello everyone.


      Using:
      Seam 2.0.2, JBoss AS 4.3.2, MySQL


      I am getting a validation error from my list shuttle.  I have implemented a converter and have overrided the equals and hashCode on my entity.  Here are some snippets from my code:


      from console.xhtml:


             <rich:modalPanel id="editUserRolePanel" width="300" height="100">
                      <f:facet name="header">
                              <h:outputText value="Add/Remove Roles" />
                      </f:facet>
      
              <s:div id="userRolesToEdit">
                              <h:form>
                                      <rich:listShuttle id="roleShuttle" sourceValue="#{usermanager.unassignedRoles}" targetValue="#{user.roles}" var="r" converter="roleconverter" fastOrderControlsVisible="false" orderControlsVisible="false">
                                              <rich:column>
                                                      <h:outputText value="#{r.roleId}" />
                                              </rich:column>
                                      </rich:listShuttle>
                                      
                                      <rich:spacer height="15" />
                                      
                                      <center>
                                              <h:commandButton value="Save" action="#{usermanager.edit}" oncomplete="Richfaces.hideModalPanel('editUserRolePanel'); return false" reRender="usersTable" />
                                      <h:commandButton value="Cancel" action="#{usermanager.cancel}" oncomplete="Richfaces.hideModalPanel('editUserRolePanel'); return false" />
                              </center>
                              </h:form>
                      </s:div>
              </rich:modalPanel>



      RoleConverter.class:



      @Stateless
      @Name( "roleconverter" )
      @org.jboss.seam.annotations.faces.Converter     
      public class RoleConverter implements Converter {
              
              @PersistenceContext
              private EntityManager em;
      
              public Object getAsObject( FacesContext context, UIComponent component, String value ) {
      
                      if (value != null) {
                                      
                              System.out.println( "RoleConverter: converting string " + value + " to Role.");
                              return (Role) em.find( Role.class, value );
                      }
                      
                      return null;
              }
      
              public String getAsString( FacesContext context, UIComponent component, Object value ) {
      
                      if (value != null) {
                              
                              System.out.println( "RoleConverter: converting role " + ((Role) value).getRoleId() + " to String.");
                              return ((Role) value).getRoleId();
                      }
                      
                      return null;
              }
      }



      from UserManagerAction.java:



      @Name( "usermanager" )
      @Stateful
      @Scope( ScopeType.CONVERSATION )
      @SerializedConcurrentAccess
      @Interceptors( SeamInterceptor.class )
      public class UserManagerAction implements ManagerAction, Serializable {
      
              @Logger
              private Log log;
      
              @PersistenceContext
              private EntityManager em;
      
              @In( create = true )
              @Out( required = false )
              @DataModelSelection
              private User user;
              
              @DataModel
              private List<User> foundUsers;
              
              @In( required = false )
              @Out( required = false )
              private List<Role> unassignedRoles;
      
              ...
      
              @Begin
              public String init() {
      
                      log.info( "Opening user: ", this.user.getUserId() );
                      
                      unassignedRoles = em.createQuery( "select r from Role r" ).getResultList();
                      unassignedRoles.removeAll( this.user.getRoles() );
                      
                      return null;
              }
      
              @End
              public String edit() {
      
                      log.info( "Saving changes to user: ", this.user.getUserId() );
                      
                      System.out.println( user.getRoles() );
      
                      this.em.merge( this.user );
                      this.find();
                      this.user = null;
      
                      return null;
              }
      
              ...
      
              // Appropriate getters and setters
      }



      The shuttle gets the right values when it loads and the debug statements show the converter is working, however, when save is clicked, the lists are not updated and I get the following error and no exceptions at the console:




      10:41:41,030 INFO  [lifecycle] WARNING: FacesMessage(s) have been enqueued, but may not have been displayed.
      sourceId=j_id54:roleShuttle[severity=(ERROR 2), summary=(j_id54:roleShuttle: Validation Error: Value admin is not valid), detail=(j_id54:roleShuttle: Validation Error: Value admin is not valid)]


      I really could use your help on this one as I am new to Seam and only been working with it for 2-3 weeks.  This error has baffled me for a few days now and online resources havent been much help.


      Thanks

        • 1. Re: List Shuttle Validation Error
          Josh Martin Newbie

          I still have not found a solution.  If you guys need more info/code just let me know.  I think it might be how equals is being called.  I put in some log statements and its comparing things that are not equal.


          Heres the java code for the class:



          @Entity
          @Name( "role" )
          public class Role implements Serializable {
          
               private String roleId;
               private String description;
          
               private List<User> users;
          
               public Role() {
                    
                    super();
               }
               
               public Role( String roleId ) {
          
                    super();
                    this.roleId = roleId;
               }
               
               public Role( String roleId, String description ) {
          
                    super();
                    this.roleId = roleId;
                    this.description = description;
               }
          
               @Id
               public String getRoleId() {
          
                    return roleId;
               }
          
               public void setRoleId( String roleId ) {
          
                    this.roleId = roleId;
               }
          
               public String getDescription() {
          
                    return description;
               }
          
               public void setDescription( String description ) {
          
                    this.description = description;
               }
          
               @ManyToMany( cascade = { CascadeType.REMOVE }, fetch = FetchType.EAGER, mappedBy = "roles" )
               @JoinTable( name = "UserRole", joinColumns = { @JoinColumn( name = "userId" ) }, inverseJoinColumns = { @JoinColumn( name = "roleId" ) } )
               public List<User> getUsers() {
          
                    return this.users;
               }
          
               public void setUsers( List<User> users ) {
          
                    this.users = users;
               }
          
               @Override
               public int hashCode() {
          
                    final int prime = 31;
                    int result = 1;
                    result = prime * result + ((description == null) ? 0 : description.hashCode());
                    result = prime * result + ((roleId == null) ? 0 : roleId.hashCode());
                    return result;
               }
          
               @Override
               public boolean equals( Object obj ) {
          
                    System.out.print( "---------- Comparing " + this.toString() + " to " + obj.toString() + ". ----------");
                    if (this == obj) {
                         System.out.println( "[TRUE] Addresses are the same." );
                         return true;
                    }
                    if (obj == null) {
                         System.out.println( "[FALSE] Obj is null." );
                         return false;
                    }
                    if (getClass() != obj.getClass()) {
                         System.out.println( "[FALSE] Classes arent the same." );
                         return false;
                    }
                    final Role other = (Role) obj;
                    
                    if (description == null) {
                         if (other.description != null) {
                              System.out.println( "[FALSE] Description on other is null." );
                              return false;
                         }
                    }
                    else if (!description.equals( other.description )) {
                         System.out.println( "[FALSE] Descriptions not equal." );
                         System.out.println( "This: " + this.getDescription() );
                         System.out.println( "Other: " + other.getDescription() );
                         return false;
                    }
                    if (roleId == null) {
                         if (other.roleId != null) {
                              System.out.println( "[FALSE] RoleId on other is null." );
                              return false;
                         }
                    }
                    else if (!roleId.equals( other.roleId )) {
                         System.out.println( "[FALSE] RoleIds not equal." );
                         return false;
                    }
          
                    System.out.println( "[TRUE] Passed all tests." );
                    return true;
               }
          
               @Override
               public String toString() {
                    
                    return roleId;
               }
          }
          


          And this is the output:



          08:41:42,193 INFO  [STDOUT] RoleConverter: converting string employee to Role.


          08:41:42,295 INFO  [STDOUT] ---------- Comparing employee to admin. ----------


          08:41:42,295 INFO  [STDOUT] [FALSE] Descriptions not equal.


          08:41:42,296 INFO  [STDOUT] This: All employees.


          08:41:42,296 INFO  [STDOUT] Other: For administrators only


          08:41:42,311 INFO  [STDOUT] RoleConverter: converting role admin to String.


          08:41:42,311 INFO  [STDOUT] ---------- Comparing employee to guest. ----------


          08:41:42,311 INFO  [STDOUT] [FALSE] Descriptions not equal.


          08:41:42,311 INFO  [STDOUT] This: All employees.


          08:41:42,311 INFO  [STDOUT] Other: Non-employees


          08:41:42,314 INFO  [STDOUT] RoleConverter: converting role guest to String.


          08:41:42,314 INFO  [STDOUT] ---------- Comparing employee to employee. ----------


          08:41:42,314 INFO  [STDOUT] [TRUE] Addresses are the same.


          08:41:42,325 INFO  [STDOUT] RoleConverter: converting role employee to String.




          As you can see, its doing some strange things.  I think I have .equals implemented correctly, but Im not so sure about the action(s) and the actual shuttle.  The error message that I am getting seems to be related to this because it doesnt pass the equals and it fails first with the role admin and that is the role referenced in the error.  Any ideas?


          Thanks

          • 2. Re: List Shuttle Validation Error
            Guillaume Jeudy Master

            Any reasons why you had to implement your own converter ? Have you tried it Seam's bundled entityconverter ?


            Add the following snippet inside your rich:listShuttle and remove the converter attribute.


            <s:entityConverter />

            • 3. Re: List Shuttle Validation Error
              Josh Martin Newbie

              Thank you for the reply.  I tried adding the suggested tag and removing the converter attribute from the shuttle.  When trying to navigate to the page i get the following exception:




              com.sun.facelets.tag.TagException: /userModalPanels.xhtml @102,27 <s:entityConverter> Tag Library supports namespace: http://jboss.com/products/seam/taglib, but no tag was defined for name: entityConverter

              Im pretty sure I have the required tag library included.  These are the ones I have:


              <ui:composition xmlns="http://www.w3.org/1999/xhtml"
                              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"
                              xmlns:a4j="http://richfaces.org/a4j">


              • 4. Re: List Shuttle Validation Error
                Josh Martin Newbie

                Ive got it now where it will recognize the tag, however, Im still getting the same validation error on admin when I try to save the changes.

                • 5. Re: List Shuttle Validation Error
                  Guillaume Jeudy Master

                  The error you are getting means for some reasons your POST doesnt get passed the JSF validation phase.


                  Check for things that may impact the validation phase, like JSF validators or required attribute.

                  • 6. Re: List Shuttle Validation Error
                    Josh Martin Newbie

                    I dont have any required attributes in this form, but I do in a separate form not related to this one.  I do have a few access rules in security.drl implemented to limit access to the console page and the employee page.  I havent had any problems accessing these with any of my other POSTs (creates, edits, deletes, reads, role editing separate from these for now).


                    Here is the code to give you a better idea of what Ive implemented as far as validation goes:


                    security.drl:


                    package Permissions;
                    
                    import java.security.Principal;
                    
                    import org.jboss.seam.security.PermissionCheck;
                    import org.jboss.seam.security.Role;
                    
                    rule CanUserViewEmployeeHome
                    when
                            c: PermissionCheck( name == "/employeeHome.xhtml", action == "render" )
                            Role( name == "employee" )
                    then
                            c.grant();
                    end
                    
                    rule CanUserViewConsole
                    when
                            c: PermissionCheck( name == "/console.xhtml" )
                            Role( name == "admin" )
                    then
                            c.grant();
                    end



                    pages.xml:



                        <page view-id="/employeeHome.xhtml" login-required="true">
                            <restrict />
                        </page>
                        
                        <page view-id="/console.xhtml" login-required="true">
                            <restrict />
                        </page>




                    And the code for the shuttle (note: no required attributes were used):



                           <rich:modalPanel id="editUserRolePanel" width="300" height="100">
                                    <f:facet name="header">
                                            <h:outputText value="Add/Remove Roles" />
                                    </f:facet>
                    
                            <s:div id="userRolesToEdit">
                                            <h:form>
                                                    <rich:listShuttle id="roleShuttle" sourceValue="#{usermanager.unassignedRoles}" targetValue="#{user.roles}" var="r" fastOrderControlsVisible="false" orderControlsVisible="false">
                                                            <s:convertEntity></s:convertEntity>
                                                            <rich:column>
                                                                    <h:outputText value="#{r.roleId}" />
                                                            </rich:column>
                                                    </rich:listShuttle>
                                                    
                                                    <rich:spacer height="15" />
                                                    
                                                    <center>
                                                            <h:commandButton value="Save" action="#{usermanager.edit}" oncomplete="Richfaces.hideModalPanel('editUserRolePanel'); return false" reRender="usersTable" />
                                                    <h:commandButton value="Cancel" action="#{usermanager.cancel}" oncomplete="Richfaces.hideModalPanel('editUserRolePanel'); return false" />
                                            </center>
                                            </h:form>
                                    </s:div>
                            </rich:modalPanel>




                    So as far as I know, I am not doing any other validation, unless there is something other from the above that I have missed.  Is there any default settings I should know about from the generic Seam login application?

                    • 7. Re: List Shuttle Validation Error
                      Guillaume Jeudy Master

                      droolz is authorization, what im talking about is the JSF PROCESSVALIDATIONS phase, this phase happens before the INVOKEAPPLICATION phase where the action method on your save button would be invoked. You can add a breakpoint in UserManager.edit() and you will see it will never get there.


                      What happens exactly when you hit 'Save', does it rerender the whole page or simply rerender your usersTable?


                      What I would do is move out the funky ajax4jsf stuff and see if you get the same problem with a plain JSF postback. Also please post any other error messages you might be getting.


                      • 8. Re: List Shuttle Validation Error
                        Josh Martin Newbie

                        Ive removed the oncompletes and the associated javascript stuff.  They were only there because I thought they were necessary to close the modal panels (got that from a richfaces tutorial).  The whole page should refresh at this point because I am using the normal html command buttons.  I still get the same error as before:



                        16:34:52,350 INFO  [lifecycle] WARNING: FacesMessage(s) have been enqueued, but may not have been displayed.
                        sourceId=j_id54:roleShuttle[severity=(ERROR 2), summary=(j_id54:roleShuttle: Validation Error: Value admin is not valid), detail=(j_id54:roleShuttle: Validation Error: Value admin is not valid)]


                        • 9. Re: List Shuttle Validation Error
                          Josh Martin Newbie

                          The edit action is not getting called... forgot to mention that.

                          • 10. Re: List Shuttle Validation Error
                            Guillaume Jeudy Master

                            yes it is normal the edit action is not getting called because of the JSF PROCESS_VALIDATIONS phase fails JSF shortcircuits the cycle and jumps to RENDER_RESPONSE phase skipping the INVOKE_APPLICATION phase.


                            One thing I don't like about JSF is that it is hard to troubleshoot the PROCESS_VALIDATIONS phase because the error is thrown by the framework.


                            After a little googling around it seems like a common cause of validation errors combined with the usage of custom converters is the equals() check failing.


                            So basically you are currently using roleId and description in equals(), is the description part of the logical business key ? Hint: Does it help uniquely identify your role and is it non-mutable ?


                            My take is that you should only leave roleId in the equals and hashCode methods. Immutability of your business key is what matters here and enables JSF to bind your selected Role back to the Role stored in your backing bean.


                            Makes sense ?

                            • 11. Re: List Shuttle Validation Error
                              Josh Martin Newbie

                              Yes that makes sense and was originally what I was doing until my boss for some reason or other told me to check everything but the List objects (which went against what I learned in school).  He knew I was having problems with this, so I guess his rational was 'check all the bases to be safe.'  roleId is the primary key, and is the only thing that is unique in the table.  It didnt make sense to be doing all those extra checks against attributes that were not unique, but oh well.  Ill take them out and give it a try and I'll let you know how it goes.

                              • 13. Re: List Shuttle Validation Error
                                Josh Martin Newbie

                                I think I might've found a useful/related topic in the JBoss Seam Forum concerning my problem:


                                http://www.jboss.com/index.html?module=bb&op=viewtopic&t=128067


                                I think this MIGHT be my problem, as I myself did not put any validation in.  The only thing is though I am not specifically using the edit.xhtml anywhere in my code, so Im not sure how it would be reaching the validateAll tag.


                                Any thoughts?

                                • 14. Re: List Shuttle Validation Error
                                  Guillaume Jeudy Master

                                  s:validateAll uses hibernate validator annotations on your domain model, therefore even if it were included without your knowledge it would not raise a validation error unless you have hibernate validator annotations on your domain model that don't pass given your specific situation.

                                  1 2 Previous Next