10 Replies Latest reply on Jan 14, 2009 10:57 AM by captainvoid

    listShuttle problem after 2. submit with validation errors

    captainvoid

      I have a form with a simple text input (required) and a listShuttle.

      If I leave the text field blank and submit the form I get the expected "value required" message and the page with the form is displayed again. But when
      I submit the form a second time, no matter if I leave the text field bank or not, I get a Component j_id3:j_id7 has invalid value expression example.Role@5e2305e message for the listShuttle.
      I cannot figure out what's wrong, I have the equals and hashCode method for the class whose instances are loaded in the listShuttle and I have a converter defined in faces-config.xml.
      I debugged the application as far as I could- the converter methods are called and also the hashCode/equals methods but (and this might be the cause) sometimes with instances that do not origin from my converter!
      Could you RF guys please have a look at this. It is the same set up as in https://jira.jboss.org/jira/browse/RF-1819 - which should be resolved but may be not?
      Thanks for any help!

      Environment:
      RichFaces 3.2.2 GA
      JSF RI 1.2
      Seam 1.2
      Facelets

      xhtml:

      <h:form>
      
       <div>
       <h:outputLabel value="Name" for="personNameInput" />
       <h:inputText id="personNameInput" value="#{person.name}" required="true" />
       </div>
      
       <div>
       <rich:panel id="listShuttlePanel">
       <rich:listShuttle
       sourceValue="#{editAction.availableRoles}"
       targetValue="#{person.roles}"
       sourceCaptionLabel="Available roles"
       targetCaptionLabel="Currently selected roles"
       converter="roleConverter"
       var="role">
      
       <rich:column>
       <h:outputText value="#{role.name}" ></h:outputText>
       </rich:column>
       </rich:listShuttle>
       </rich:panel>
       </div>
      
      
       <div>
       <h:panelGroup>
       <h:commandButton action="#{editAction.save}" value="Save" />
       </h:panelGroup>
       </div>
      
      </h:form>
      


      Person.java
      @Name("person")
      public class Person {
      
       private String Name;
      
       private List<Role> roles;
      
       public Person() {
       roles = new ArrayList<Role>();
       }
      
       public List<Role> getRoles() {
       return roles;
       }
      
       public void setRoles(List<Role> roles) {
       this.roles = roles;
       }
      
       public String getName() {
       return Name;
       }
      
       public void setName(String name) {
       Name = name;
       }
      
      }
      


      Role.java
      public class Role {
      
       private String id;
      
       private String name;
      
       public String getId() {
       return id;
       }
      
       public void setId(String id) {
       this.id = id;
       }
      
       public String getName() {
       return name;
       }
      
       public void setName(String name) {
       this.name = name;
       }
      
       public boolean equals(Object other) {
       System.out.println("===== equals called ======");
       if ( (this == other ) ) return true;
       if ( (other == null ) ) return false;
       if ( !(other instanceof Role) ) return false;
       Role castOther = ( Role ) other;
       return (this.getId().equals(castOther.getId()));
       }
      
       public int hashCode() {
       System.out.println("===== hashCode called ======");
       int result = 17;
       result += 37 * result + (this.getId() == null ? 0 : this.getId().hashCode());
       return result;
       }
      }
      


      RoleConverter:
      public class RoleConverter implements Converter {
      
       public static final String SEPARATOR = "@";
      
       public Object getAsObject(FacesContext context, UIComponent ui, String str) {
       System.out.println("===== Converter: getAsObject called ======");
       int splitIndex = str.indexOf(SEPARATOR);
       String roleId = str.substring(0, splitIndex);
       String roleName = str.substring(splitIndex + 1);
       Role role = new Role();
       role.setId(roleId);
       role.setName(roleName);
       return role;
       }
      
       public String getAsString(FacesContext context, UIComponent ui, Object obj) {
       System.out.println("===== Converter: getAsString called ======");
       Role role = (Role) obj;
       return role.getId() + SEPARATOR + role.getName();
       }
      
      }
      


      EditAction.java
      @Name("editAction")
      @Scope(ScopeType.PAGE)
      @AutoCreate
      public class EditAction {
      
       @Logger
       private Log logger;
      
       @In(create=true)
       private Person person;
      
       private List<Role> availableRoles;
      
       @Create
       public void init() {
       logger.debug("++++++ initializing availableRoles list ++++");
       availableRoles = new ArrayList<Role>();
       Role guestRole = new Role();
       guestRole.setId("guest");
       guestRole.setName("Guest user");
       Role userRole = new Role();
       userRole.setId("user");
       userRole.setName("Normal user");
       Role adminRole = new Role();
       adminRole.setId("admin");
       adminRole.setName("Administrator");
       availableRoles.add(guestRole);
       availableRoles.add(userRole);
       availableRoles.add(adminRole);
       }
      
       public List<Role> getAvailableRoles() {
       logger.debug("++++ getAvailableRoles called ++++");
       return availableRoles;
       }
      
       public void setAvailableRoles(List<Role> availableRoles) {
       logger.debug("++++ setAvailableRoles called ++++");
       this.availableRoles = availableRoles;
       }
      
       public String save() {
       logger.debug("+++ saving person '" + person.getName() + "'");
       logger.debug("+++ selected roles: []", person.getRoles());
       return "/edit/success.xhtml";
       }
      
      }
      


        • 1. Re: listShuttle problem after 2. submit with validation erro
          ilya_shaikovsky

          so check in the debug the values returned by your converter.

          • 2. Re: listShuttle problem after 2. submit with validation erro

            Check your converter very carefully. If Role.getName is null, it will output the word 'null' and on the getAsObject, it will look for a role with the name as 'null' instead of the value null.

            We had to make an arbitrary value to output in getAsString for null values and then on getAsObject, if it was equal to that value, set the property as null. That seemed to solve the similar problem we had.

            • 3. Re: listShuttle problem after 2. submit with validation erro
              captainvoid

              Thanks for the quick replies Ilya and Scott,

              I improved my debug output and carefully checked the values that are passed to the converter methods - but everything seems to be ok (no null values).
              But: It seems that the equals method in my Role class is called with an instance of Role which doesn't have the id property set:
              (debug output after the second submit)

              [18:29:37,675] example.PhaseTracker [INFO] BEFORE RESTORE_VIEW 1
              [18:29:37,675] example.PhaseTracker [INFO] AFTER RESTORE_VIEW 1
              [18:29:37,675] example.PhaseTracker [DEBUG] ====================> queued messages:
              [18:29:37,691] example.PhaseTracker [INFO] BEFORE APPLY_REQUEST_VALUES 2
              [18:29:37,691] example.RoleConverter [DEBUG] ===== Converter: getAsObject [guest@Guest user] called ======
              [18:29:37,691] example.RoleConverter [DEBUG] ====== return [Role - id [guest] - name [Guest user]]
              [18:29:37,691] example.RoleConverter [DEBUG] ===== Converter: getAsObject [user@Normal user] called ======
              [18:29:37,691] example.RoleConverter [DEBUG] ====== return [Role - id [user] - name [Normal user]]
              [18:29:37,691] example.RoleConverter [DEBUG] ===== Converter: getAsObject [admin@Administrator] called ======
              [18:29:37,706] example.RoleConverter [DEBUG] ====== return [Role - id [admin] - name [Administrator]]
              [18:29:37,706] example.Role [DEBUG] ===== equals called with [Role - id [null] - name [null]] ======
               [18:29:37,706] example.Role [DEBUG] =====> return [false] ======
               [18:29:37,706] example.Role [DEBUG] ===== equals called with [Role - id [null] - name [null]] ======
               [18:29:37,722] example.Role [DEBUG] =====> return [false] ======
               [18:29:37,738] example.Role [DEBUG] ===== equals called with [Role - id [null] - name [null]] ======
               [18:29:37,738] example.Role [DEBUG] =====> return [false] ======
              [18:29:37,738] example.PhaseTracker [INFO] AFTER APPLY_REQUEST_VALUES 2
              [18:29:37,738] example.PhaseTracker [DEBUG] ====================> queued messages:
              [18:29:37,738] example.PhaseTracker [DEBUG] "Component j_id3:j_id7 has invalid value expression Role - id [guest] - name [Guest user]"
              [18:29:37,738] example.PhaseTracker [INFO] BEFORE RENDER_RESPONSE 6
              [18:29:37,753] example.Role [DEBUG] ===== hashCode called return [98709598] ======
              [18:29:37,753] example.Role [DEBUG] ===== hashCode called return [98709598] ======
              [18:29:37,753] example.RoleConverter [DEBUG] ===== Converter: getAsString called [Role - id [guest] - name [Guest user]]======
              [18:29:37,753] example.RoleConverter [DEBUG] ===== return [guest@Guest user]
              [18:29:37,753] example.Role [DEBUG] ===== hashCode called return [3599953] ======
              [18:29:37,753] example.Role [DEBUG] ===== hashCode called return [3599953] ======
              [18:29:37,753] example.RoleConverter [DEBUG] ===== Converter: getAsString called [Role - id [user] - name [Normal user]]======
              [18:29:37,753] example.RoleConverter [DEBUG] ===== return [user@Normal user]
              [18:29:37,753] example.Role [DEBUG] ===== hashCode called return [92669397] ======
              [18:29:37,753] example.Role [DEBUG] ===== hashCode called return [92669397] ======
              [18:29:37,753] example.RoleConverter [DEBUG] ===== Converter: getAsString called [Role - id [admin] - name [Administrator]]======
              [18:29:37,769] example.RoleConverter [DEBUG] ===== return [admin@Administrator]
              [18:29:37,784] example.PhaseTracker [INFO] AFTER RENDER_RESPONSE 6
              [18:29:37,784] example.PhaseTracker [DEBUG] ====================> queued messages:
              [18:29:37,784] example.PhaseTracker [DEBUG] "Component j_id3:j_id7 has invalid value expression Role - id [guest] - name [Guest user]"
              
              

              So I wonder where this instance with id=null is coming from..
              I don't have the richfaces sources by hand but it seems that the call is originating from HtmlListShuttle.isSuitableValue(Object, Object)
              (stacktrace of the equals-method invocation):
              Daemon Thread [http-8081-2] (Suspended (breakpoint at line 34 in Role))
               Role.equals(Object) line: 34
               ArrayList<E>.indexOf(Object) line: 216
               ArrayList<E>.contains(Object) line: 199
               HtmlListShuttle(UIOrderingBaseComponent).isSuitableValue(Object, Object) line: 76
               HtmlListShuttle(UIListShuttle).processDecodes(FacesContext, Object) line: 220
               HtmlListShuttle(UIDataAdaptor).processDecodes(FacesContext) line: 1168
               HtmlListShuttle(UIOrderingBaseComponent).processDecodes(FacesContext) line: 235
               HtmlPanel(UIComponentBase).processDecodes(FacesContext) line: 1014
               HtmlForm(UIForm).processDecodes(FacesContext) line: 209
               AjaxViewRoot$1.invokeContextCallback(FacesContext, UIComponent) line: 392
               AjaxViewRoot.processPhase(FacesContext, PhaseId, InvokerCallback) line: 238
               AjaxViewRoot.processDecodes(FacesContext) line: 409
               ApplyRequestValuesPhase.execute(FacesContext) line: 78
               ApplyRequestValuesPhase(Phase).doPhase(FacesContext, Lifecycle, ListIterator<PhaseListener>) line: 100
               LifecycleImpl.execute(FacesContext) line: 118
               FacesServlet.service(ServletRequest, ServletResponse) line: 265
               ApplicationFilterChain.internalDoFilter(ServletRequest, ServletResponse) line: 290
               ApplicationFilterChain.doFilter(ServletRequest, ServletResponse) line: 206
               Filter(BaseFilter).doFilter(ServletRequest, ServletResponse, FilterChain) line: 522
               ApplicationFilterChain.internalDoFilter(ServletRequest, ServletResponse) line: 235
               ApplicationFilterChain.doFilter(ServletRequest, ServletResponse) line: 206
               SeamFilter$FilterChainImpl.doFilter(ServletRequest, ServletResponse) line: 63
               MyExceptionFilter(ExceptionFilter).doFilter(ServletRequest, ServletResponse, FilterChain) line: 57
               MyExceptionFilter.doFilter(ServletRequest, ServletResponse, FilterChain) line: 46
               SeamFilter$FilterChainImpl.doFilter(ServletRequest, ServletResponse) line: 49
               CharacterEncodingFilter.doFilter(ServletRequest, ServletResponse, FilterChain) line: 43
               SeamFilter$FilterChainImpl.doFilter(ServletRequest, ServletResponse) line: 49
               RedirectFilter.doFilter(ServletRequest, ServletResponse, FilterChain) line: 45
               SeamFilter$FilterChainImpl.doFilter(ServletRequest, ServletResponse) line: 49
               MultipartFilter.doFilter(ServletRequest, ServletResponse, FilterChain) line: 79
               SeamFilter$FilterChainImpl.doFilter(ServletRequest, ServletResponse) line: 49
               SeamFilter.doFilter(ServletRequest, ServletResponse, FilterChain) line: 84
               ApplicationFilterChain.internalDoFilter(ServletRequest, ServletResponse) line: 235
               ApplicationFilterChain.doFilter(ServletRequest, ServletResponse) line: 206
               ConfigurableXMLFilter(BaseXMLFilter).doXmlFilter(FilterChain, HttpServletRequest, HttpServletResponse) line: 177
               Filter(BaseFilter).handleRequest(HttpServletRequest, HttpServletResponse, FilterChain) line: 267
               Filter(BaseFilter).processUploadsAndHandleRequest(HttpServletRequest, HttpServletResponse, FilterChain) line: 380
               Filter(BaseFilter).doFilter(ServletRequest, ServletResponse, FilterChain) line: 507
               ApplicationFilterChain.internalDoFilter(ServletRequest, ServletResponse) line: 235
               ApplicationFilterChain.doFilter(ServletRequest, ServletResponse) line: 206
               StandardWrapperValve.invoke(Request, Response) line: 233
               StandardContextValve.invoke(Request, Response) line: 175
               StandardHostValve.invoke(Request, Response) line: 128
               ErrorReportValve.invoke(Request, Response) line: 102
               StandardEngineValve.invoke(Request, Response) line: 109
               CoyoteAdapter.service(Request, Response) line: 263
               Http11Processor.process(Socket) line: 844
               Http11Protocol$Http11ConnectionHandler.process(Socket) line: 584
               JIoEndpoint$Worker.run() line: 447
               Thread.run() line: 619
              


              So very likely this comparison is responsible for the invalid value message.

              Ilya, could it be a bug?

              • 4. Re: listShuttle problem after 2. submit with validation erro
                captainvoid

                I added some more debug statements, also in the constructor of my Role class.
                It turns out that during the RESTORE VIEW PHASE three Role instances are created - without setting their identity.
                Here the debug output after the second submit (text field left blank):

                [10:29:35,062] example.PhaseTracker [INFO] BEFORE RESTORE_VIEW 1
                [10:29:35,062] example.Role [DEBUG] +++++++++++ Role constructed ++++++++++++++++++
                [10:29:35,062] example.Role [DEBUG] +++++++++++ Role constructed ++++++++++++++++++
                [10:29:35,062] example.Role [DEBUG] +++++++++++ Role constructed ++++++++++++++++++
                [10:29:35,062] example.PhaseTracker [INFO] AFTER RESTORE_VIEW 1
                [10:29:35,062] example.PhaseTracker [DEBUG] ====================> queued messages:
                [10:29:35,062] example.PhaseTracker [INFO] BEFORE APPLY_REQUEST_VALUES 2
                [10:29:35,078] example.RoleConverter [DEBUG] ===== Converter: getAsObject [guest@Guest user] called ======
                [10:29:35,078] example.Role [DEBUG] +++++++++++ Role constructed ++++++++++++++++++
                [10:29:35,078] example.Role [DEBUG] +++++++++++ Role id set ++++++++++++++++++
                [10:29:35,078] example.RoleConverter [DEBUG] ====== return [Role - id [guest] - name [Guest user]]
                [10:29:35,093] example.RoleConverter [DEBUG] ===== Converter: getAsObject [user@Normal user] called ======
                [10:29:35,093] example.Role [DEBUG] +++++++++++ Role constructed ++++++++++++++++++
                [10:29:35,093] example.Role [DEBUG] +++++++++++ Role id set ++++++++++++++++++
                [10:29:35,093] example.RoleConverter [DEBUG] ====== return [Role - id [user] - name [Normal user]]
                [10:29:35,093] example.RoleConverter [DEBUG] ===== Converter: getAsObject [admin@Administrator] called ======
                [10:29:35,093] example.Role [DEBUG] +++++++++++ Role constructed ++++++++++++++++++
                [10:29:35,093] example.Role [DEBUG] +++++++++++ Role id set ++++++++++++++++++
                [10:29:35,093] example.RoleConverter [DEBUG] ====== return [Role - id [admin] - name [Administrator]]
                [10:29:35,093] example.Role [DEBUG] ===== equals[Role - id [null] - name [null]] called on [Role - id [guest] - name [Guest user]] ======
                [10:29:35,093] example.Role [DEBUG] =====> return [false] ======
                [10:29:35,093] example.Role [DEBUG] ===== equals[Role - id [null] - name [null]] called on [Role - id [guest] - name [Guest user]] ======
                [10:29:35,093] example.Role [DEBUG] =====> return [false] ======
                [10:29:35,109] example.Role [DEBUG] ===== equals[Role - id [null] - name [null]] called on [Role - id [guest] - name [Guest user]] ======
                [10:29:35,109] example.Role [DEBUG] =====> return [false] ======
                [10:29:35,109] example.PhaseTracker [INFO] AFTER APPLY_REQUEST_VALUES 2
                [10:29:35,109] example.PhaseTracker [DEBUG] ====================> queued messages:
                [10:29:35,109] example.PhaseTracker [DEBUG] "Component j_id3:j_id7 has invalid value expression Role - id [guest] - name [Guest user]"
                [10:29:35,109] example.PhaseTracker [INFO] BEFORE RENDER_RESPONSE 6
                [10:29:35,125] example.Role [DEBUG] ===== hashCode called return [98709598] ======
                [10:29:35,125] example.Role [DEBUG] ===== hashCode called return [98709598] ======
                [10:29:35,125] example.RoleConverter [DEBUG] ===== Converter: getAsString called [Role - id [guest] - name [Guest user]]======
                [10:29:35,125] example.RoleConverter [DEBUG] ===== return [guest@Guest user]
                [10:29:35,125] example.Role [DEBUG] ===== hashCode called return [3599953] ======
                [10:29:35,125] example.Role [DEBUG] ===== hashCode called return [3599953] ======
                [10:29:35,125] example.RoleConverter [DEBUG] ===== Converter: getAsString called [Role - id [user] - name [Normal user]]======
                [10:29:35,125] example.RoleConverter [DEBUG] ===== return [user@Normal user]
                [10:29:35,125] example.Role [DEBUG] ===== hashCode called return [92669397] ======
                [10:29:35,140] example.Role [DEBUG] ===== hashCode called return [92669397] ======
                [10:29:35,140] example.RoleConverter [DEBUG] ===== Converter: getAsString called [Role - id [admin] - name [Administrator]]======
                [10:29:35,140] example.RoleConverter [DEBUG] ===== return [admin@Administrator]
                [10:29:35,171] example.PhaseTracker [INFO] AFTER RENDER_RESPONSE 6
                [10:29:35,171] example.PhaseTracker [DEBUG] ====================> queued messages:
                [10:29:35,171] example.PhaseTracker [DEBUG] "Component j_id3:j_id7 has invalid value expression Role - id [guest] - name [Guest user]"
                


                Ilya, this must be a bug - can't see what I'm doing wrong.
                Any help very appreciated.

                • 5. Re: listShuttle problem after 2. submit with validation erro
                  ilya_shaikovsky

                  No, I still believe that it isn't a bug. I written two examples for demosite for Ordering List and List Shuttle with proper converters defined and object methods overriden. please check this examples and your case in debugger carefully.

                  • 6. Re: listShuttle problem after 2. submit with validation erro
                    captainvoid

                    Hi Ilya, thanks for your attention,

                    I carefully compared your code on the demosite to my code.

                    My converter is identical to yours and the the hashCode/equals methods are _almost_ the same (your implementation also works if a property is null, but this is not relevant in this case). I even copied your implementation of equals/hashcode to my object. But it makes no difference.

                    How do you explain that after the second submit, in the restore view phase, domain objects get "magically" instantiated without my converter being called?
                    I can send the war if this would help..?

                    • 7. Re: listShuttle problem after 2. submit with validation erro
                      ilya_shaikovsky

                      yes you could send me war file directly. Make sure I will able to just run and check it please(no db connections needed and so on). Also do not forget java sources. in such case we will find the reason much faster. ;)

                      • 8. Re: listShuttle problem after 2. submit with validation erro
                        captainvoid

                        Hi Ilya,
                        I sent you the war file at your address at exadel.
                        I appreciate very much your effort.

                        • 9. Re: listShuttle problem after 2. submit with validation erro
                          ilya_shaikovsky

                          Thanks for the sample!

                          We've found the problem. Your Role object should implements serializable in order to save state of the list shuttle. This addition reveals the problem on my side.

                          • 10. Re: listShuttle problem after 2. submit with validation erro
                            captainvoid

                            Problem solved!

                            Thanks very much for your help!