1 2 Previous Next 17 Replies Latest reply on Jun 12, 2013 1:45 PM by bleathem

    Problem with picklist (prefill & select)

    jn.colin

      Hi

       

      I know this issue has been discussed several times, but I could not find a suitable solution for my problem.

       

      I'm using RichFaces 4.3.2 with JSF 2.1.19. I have  a picklist (see definition below) where I need to populate the select area with a list of value I extract from the DB.

       

      <rich:panel styleClass="fullwidth"
          rendered="#{!adminProjectBean.currentProject.visibleToAll}">
          <f:facet name="header">
              <h:outputText value="Select users"></h:outputText>
          </f:facet>
          <rich:pickList sourceCaption="Available users"
              targetCaption="Authorized users" listWidth="165px"
              listHeight="100px" orderable="false"
              value="#{adminProjectBean.selectedUsers}" var="user">
              <f:selectItems value="#{adminProjectBean.availableUsers}" />
              <f:converter converterId="MyVisibilityConverter" />
              <rich:column>
                  #{user.firstName}
              </rich:column>
              <rich:column>
                  #{user.lastName}
              </rich:column>
          </rich:pickList>
      </rich:panel>
      

       

       

      I have a selectItems for the available values, loaded as:

       

      availableUsers = DAOFactory.getDAOFactory(DAOFactory.MYSQL).getUserDAO().getUsers();
      

       

      Available values display properly.

       

      The preselected list is loaded with:

       

      List selection = DAOFactory.getDAOFactory(DAOFactory.MYSQL).getUserDAO().getProjectUsers(currentProject.getId());
      if ((selection == null) || (selection.size() == 0)) {
           currentProject.setVisibleToAll(true);                     
      } else { 
           selectedUsers.addAll(selection);
           currentProject.setVisibleToAll(false);
      }
      

       

      Both queries load objects of type UserTO. The problem is that the selected list (selectedUsers) contains the right records, but the picklist's right side is always empty. I've read all similar issues I could find, I did implement a Converter (see attached), I added an equals method to the UserTO object, but still, I can't get any item prefilled in the selected area.

       

      @FacesConverter("MyVisibilityConverter")
      public class VisibilityConverter implements Converter {
                List<UserTO> userMap;
      
      
                /*
                 * (non-Javadoc)
                 * 
                 * @see
                 * javax.faces.convert.Converter#getAsObject(javax.faces.context.FacesContext
                 * , javax.faces.component.UIComponent, java.lang.String)
                 */
                @Override
                public Object getAsObject(FacesContext context, UIComponent component,
                                    String value) {
                          if (userMap != null) {
                                    for (UserTO user : userMap) {
                                              if (user.getUserid() == Long.parseLong(value)) {
                                                        System.out.println("getAsObject: " + user.getUsername());
                                                        return user;
                                              }
                                    }
                          } else {
                                    Logger.getLogger(VisibilityConverter.class.getName()).log(
                                                        Level.SEVERE, null, "map is empty");
                          }
                          return null;
                }
      
      
                /*
                 * (non-Javadoc)
                 * 
                 * @see
                 * javax.faces.convert.Converter#getAsString(javax.faces.context.FacesContext
                 * , javax.faces.component.UIComponent, java.lang.Object)
                 */
                @Override
                public String getAsString(FacesContext context, UIComponent component,
                                    Object value) {
                          if (value == null)
                                    return null;
                          System.out.println("getAsString " + ((UserTO) value).getUsername());
                          return Long.toString(((UserTO) value).getUserid());
                }
      
      
                /**
                 * @param selectedUsers
                 */
                public void setMap(List<UserTO> selectedUsers) {
                          userMap = selectedUsers;
                }
      }
      

       

       

      What is strange though is that if I copy an Object from the available list to the selected list (for instance: selectedUsers.add(availableUsers.get(0)), then, it gets properly displayed.

       

      Could you please help?

        • 1. Re: Problem with picklist (prefill & select)
          bleathem

          How do you populate the userMap in your converter?

          • 2. Re: Problem with picklist (prefill & select)
            jn.colin

            The userMap is populated in the constructor of the bean (AdminProjectBean, ViewScope); I can see that it is populated properly, at least, the map passed to the converter contains all available items.

             

            public AdminProjectBean() {

                            try {

                                    availableUsers = DAOFactory.getDAOFactory(DAOFactory.MYSQL)

                                                    .getUserDAO().getUsers();

                            } catch (DAOException e) {

                                    Logger.getLogger(AdminProjectBean.class.getName()).log(

                                                    Level.SEVERE, null, e);

                            }

                            loadProjects();

                            converter = new VisibilityConverter();

                            converter.setMap(availableUsers);

                    }

            • 3. Re: Problem with picklist (prefill & select)
              bleathem

              Your converter initialization looks suspicious.  You are not initializing the same instance as the one used by the FacesContext.

               

              There are 2 ways to fix this:

               

              1) Use a stateful converter, and bind to the converter instance via an EL expression (see http://stackoverflow.com/a/7531487/264797)

               

              2) Lookup your DAO within your converter, and lazy initialize the map in your converter on first access.

              • 4. Re: Problem with picklist (prefill & select)
                jn.colin

                Hi Brian,

                I indeed noticed I was initializing two different converters, so I fixed it by referencing correctly the converter in the (ViewScoped) bean:

                 

                <rich:pickList sourceCaption="Available users"

                    targetCaption="Authorized users" listWidth="165px"

                    listHeight="100px" orderable="false"

                    value="#{adminProjectBean.selectedUsers}" var="user"

                    converter="#{adminProjectBean.converter}">

                    <f:selectItems value="#{adminProjectBean.availableUsers}" />

                    <rich:column>

                        #{user.firstName}

                    </rich:column>

                    <rich:column>

                        #{user.lastName}

                    </rich:column>

                </rich:pickList>

                 

                As you can see in the file attached, the converter is instantiated in the bean constructor, and the map is populated at the same place. However, it does not solve the problem.

                 

                Both lists are initialized by two distinct queries to the DB. And what is **very** strange (at least to me), is that if I populate the selected lists from objects picked from the available list, then it works fine. To be concrete, when I initialize the converter map in the bean constructor, I also create a hashmap with the object ids and the object itself:

                 

                public AdminProjectBean() {

                    try {

                        availableUsers = DAOFactory.getDAOFactory(DAOFactory.MYSQL)

                                .getUserDAO().getUsers();

                        for (UserTO u : availableUsers) {

                            availableUserIds.put(u.getUserid(), u);

                        }

                    } catch (DAOException e) {

                        Logger.getLogger(AdminProjectBean.class.getName()).log(

                                Level.SEVERE, null, e);

                    }

                    loadProjects();

                    converter = new VisibilityConverter();

                    converter.setMap(availableUsers);

                }

                 

                and then, I read an intermediary selected list from the DB, but construct the final one by picking objects from the available maps. So I have the same instances on both listss, and then it works. So it looks like I'm missing a comparison between both lists. However, I do implement the equals method in the UserTO class.

                 

                Thanks a lot

                 

                Jean-Noël

                • 5. Re: Problem with picklist (prefill & select)
                  bleathem

                  Can you debug the application?  Set a breakpoint here:

                  https://github.com/richfaces4/components/blob/master/input/ui/src/main/java/org/richfaces/renderkit/SelectManyHelper.java#L255

                  and you will be able to see the result of the comparison.

                  • 6. Re: Problem with picklist (prefill & select)
                    jn.colin

                    I don't know how to debug the application on the server, I'd be grateful if you had some pointers to start with; anyway, I could get it to work by populating the selected list from objects picked in the available list, but still, I'd be interested to know why it does not work when both lists are loaded independently

                     

                    Best regards

                    • 7. Re: Problem with picklist (prefill & select)
                      bleathem

                      If you manage your server lifecycle via the IDE, simply start the server in debug mode.  then you can set breakpoints in your applications, or in the source files of your library jars and inspect values when you hit those breakpoints.

                       

                      If you are like me, and start your servers via the command line (rather than having the IDE manage the server lifecycle), then here's how I debug server side Java apps.  It works for JBoss EAP, Wildfly, and Tomcat.  Presumably it works for other servers as well.

                       

                      1) Set the environment variable: 

                      JAVA_OPTS="-Xdebug -Xnoagent -Djava.compiler=NONE -Xrunjdwp:transport=dt_socket,address=5005,server=y,suspend=n"

                       

                      2) Start your server.

                       

                      3) In your IDE, start a remote debugger, telling it to attach to the port 5005.

                       

                      Take the time to figure out how to get it to work, it's a tremendous tool in working out bugs in your applications!

                      • 8. Re: Problem with picklist (prefill & select)
                        jn.colin

                        Hi

                         

                        I managed to start tomcat in debug mode, and attach eclipse to it; was quite fun, thanks for the entertainment.

                         

                        What I did is to set a breakpoint at the beginning of the method you told me (getClientSelectItems) to make sure it was invoked, and then at the line 'selected = true', to see if selected values were considered as being part of available values or not.

                         

                        while (selectItems.hasNext()) {

                                    SelectItem selectItem = selectItems.next();

                                    boolean selected;

                                    int sortOrder;

                                    if (valuesSet.contains(selectItem.getValue())) { // TODO: this requires value#equals() to be overridden. Redo with comparators?

                                        selected = true;

                                        sortOrder = values.indexOf(selectItem.getValue());

                                    } else {

                                        selected = false;

                                        sortOrder = count;

                                    }

                                    ClientSelectItem clientSelectItem = SelectHelper.generateClientSelectItem(facesContext, select, selectItem, sortOrder, selected);

                                    clientSelectItems.add(clientSelectItem);

                                    if (!selected) {

                                        count++;

                                    }

                                }

                         

                        As expected, the result depends on how I populated the selected values list. If it was selected with a separate DB query (this corresponds to the commented lines //this does not work), the second breakpoint is never reached, while if I populate the selected values list from the same objects than those in the available list (this corresponds to the commented lines //this works), then it works properly. So it seems that valuesSet.contains(selectItem.getValue()) above never returns true, because objects are not compared using my custom equals method.

                         

                        List<UserTO> selection = DAOFactory

                                                .getDAOFactory(DAOFactory.MYSQL).getUserDAO()

                                                .getProjectUsers(currentProject.getId());

                                        if ((selection == null) || (selection.size() == 0)) {

                                            selectedUsers = new ArrayList<UserTO>();

                                            currentProject.setVisibleToAll(true);

                                        } else {

                                            // this works

                                            for (UserTO u : selection) {

                                                selectedUsers.add(availableUserIds.get(u.getUserid()));

                                            }

                                            // this does not work

                        //                     selectedUsers.addAll(selection);

                                            currentProject.setVisibleToAll(false);

                                        }

                         

                        Any further idea?

                         

                        Many thanks


                        • 9. Re: Problem with picklist (prefill & select)
                          bleathem

                          That's some great detective work, good job!  It's surprising that the 2nd breakpoint is not hit.  In your debugger, you can "step through" the code one line-at-a-time after you hit a breakpoint.  After you hit the first breakpoint, can you step forward to determine why you don't reach the 2nd breakpoint?

                           

                          Get used to running the debugger, it's a powerful tool and will help you lots in your application development, as you can use it to step through your own code that your write.

                          • 10. Re: Problem with picklist (prefill & select)
                            jn.colin

                            Hi Brian,

                             

                            From what I can see, the following line:

                                    if (valuesSet.contains(selectItem.getValue())) { // TODO: this requires value#equals() to be overridden. Redo with comparators?

                             

                            is not calling the equals method of the object, but rather it compares hash of the objects, and I guess that's where the difference comes from. Because object has a different hash value, it's not considered as belonging to the list, and that's why it's never selected. While if I build the selected list directly with objects from the available object list, then hash have the same value and selection is done correctly. At least, this is my understanding

                             

                             

                             

                            Screen Shot 2013-06-03 at 11.45.27.png

                            • 11. Re: Problem with picklist (prefill & select)
                              bleathem

                              The javadoc says that the collection API delegates to the equals method when using Collection#contains:

                              http://docs.oracle.com/javase/6/docs/api/java/util/Collection.html#contains(java.lang.Object)

                               

                              Can you post your equals method?  Do you have any tests for your equals method?  It's not comparing any transient attributes in the equals check, is it?

                              • 12. Re: Problem with picklist (prefill & select)
                                jn.colin

                                Here's the equals method

                                 

                                @Override

                                          public boolean equals(Object obj) {

                                                    if (!(obj instanceof UserTO))

                                  return false;

                                //                    System.out.println("equals " + this.userid + " "

                                //                                        + ((UserTO) obj).getUserid());

                                                    return (this.userid == ((UserTO) obj).getUserid());

                                          }

                                 

                                I set a break point at its first line, but it never gets called. While if again I populate the selected list from elements of the available list, equals get called properly

                                 

                                When I look at the call stack, equals is not called from the contains call, but rather from the indexOf one, two lines below.

                                        while (selectItems.hasNext()) {

                                            SelectItem selectItem = selectItems.next();

                                            boolean selected;

                                            int sortOrder;

                                            if (valuesSet.contains(selectItem.getValue())) { // TODO: this requires value#equals() to be overridden. Redo with comparators?

                                                selected = true;

                                                sortOrder = values.indexOf(selectItem.getValue());

                                            } else {

                                                selected = false;

                                                sortOrder = count;

                                            }

                                 

                                Thanks again

                                • 13. Re: Problem with picklist (prefill & select)
                                  bleathem

                                  Jean-Noel Colin wrote:

                                   

                                  Here's the equals method

                                  ...

                                                      return (this.userid == ((UserTO) obj).getUserid());

                                  ...

                                   

                                  Assuming userid id is an Object of some kind (an Interger or a Long for intance), change that "==" to a .equals().

                                  • 14. Re: Problem with picklist (prefill & select)
                                    jn.colin

                                    userid is a long, so I can't call equals on it.

                                     

                                    /jnc

                                    1 2 Previous Next