10

I am using the managedBean userHome in requestScope, in which the entity 'user' is going to be persist. The user has the leader column which is mapped in ManyToOne relation.My Code looks like this

@ManagedBean
@RequestScoped
public class UserHome {
    private User user = new User();
        // Getters and Setters

    private List<SelectItem> selectItems = new ArrayList<SelectItem>();

    public UserHome() {
        for(User user: availableLeaders) {
            selectItems.add(new SelectItem(user.getName(), user));
        }
    }

    public void persis();
}

User.java

public class User {
    @Id
    @Column
    private Integer id;

    @Column
    privat String name;

    @ManyToOne
    private User leader;
}

I am trying to get the value of this leader through h:selectOneMenu like this

<h:selectOneMenu value="#{userHome.user.leader}" converter="userConverter">
    <f:selectItems value="#{userHome.selectItems}"/>
</h:selectOneMenu>

My converter looks like this

@FacesConverter(forClass = User.class, value="userConverter")
public class UserConverter implements Converter {

    private  Map<String, User> userValues = new HashMap<String, User>();

    public UserConverter() {
        init();
    }

    @Override
    public Object getAsObject(FacesContext context, UIComponent component,
            String value) {
        return userValues.get(value);
    }

    @Override
    public String getAsString(FacesContext context, UIComponent component, Object value) {
        System.out.println("RAJASEKARAN "+value);
        return ((User)value).getName();
    }

    public void init() {
        UserHome userHome = new UserHome();
        for(User user:userHome.availableLeaders()) {
            userValues.put(user.getName(), user);
        }
    }
}

While try to save the user I am getting the error UserEdit:j_idt18: Validation Error: Value is not valid

Gnanam
  • 513
  • 2
  • 10
  • 21

2 Answers2

14

Adding to BalusC's answer: after the postback, you need to make sure that the User instances are either exactly the same ones as you used for rendering the select items, or that you implement equals for your User class.

The code doesn't show where availableLeaders comes from, but if this is fetched from a DB on-demand, then the converter will not convert to the exact same object instance that's in the list that JSF resolves via #{userHome.selectItems}.

After the conversion, JSF will check whether the converted instance can be found in that list using the equals() method.

Arjan Tijms
  • 37,782
  • 12
  • 108
  • 140
  • Hi, Where exactly is it in the mojarra code, that this equals check happen. I am writing a custom component, and I ran into this issue, and I want to debug the mojarra code to see what going on. Thank you – Thang Pham Nov 18 '12 at 20:19
  • This happens in `UISelectMany#validateValue`, with the actual call to `equals` happening in `SelectUtils#matchValue`. – Arjan Tijms Nov 18 '12 at 22:02
  • Yup, after lots of debug, I am able to figure this out, is this consider a bad practice to not validate? My custom component extends from UISelectOne, and this method validateValue give me so much grieve, if I overidde this method with empty content, then things work correctly. But I have a feeling that this is something I should not do. Any thought? – Thang Pham Nov 18 '12 at 23:40
  • 1
    Not letting it do the check opens you to a similar kind of attack that the mass assignment vulnerability allowed to happen on e.g. Github (see http://www.infoq.com/news/2012/03/GitHub-Compromised) – Arjan Tijms Nov 18 '12 at 23:47
  • Thank you, after some thought, I think I have an idea on how to get the validation work. THank you for your help. – Thang Pham Nov 19 '12 at 00:29
  • You're welcome. You might also want to look at: http://showcase-omnifaces.rhcloud.com/showcase/converters/SelectItemsConverter.xhtml this one uses the original list to compare against. If this comes from e.g. the view scope and there is no serialization being done (the default in Mojarra),then `equals` will be true without needing to do anything. – Arjan Tijms Nov 19 '12 at 07:47
8

You've constructed the SelectItem the wrong way. As per the class' documentation, the 1st argument should represent the item value (which is to be converted and submitted) and the 2nd argument should represent the item label (which is to be displayed in list). But you specified them the other way round.

Fix it accordingly:

selectItems.add(new SelectItem(user, user.getName()));

If that still doesn't fix the problem, then it means that the equals() method of User class is not (properly) implemented. JSF will use it to validate the selected User against any of the item values of the list after conversion.


Unrelated to the concrete problem, it may be useful to know that <f:selectItems> in JSF2 offers you the possibility to build the list without the need to build a list of SelectItem manually. Here's an example which achieves exactly the same:

<f:selectItems value="#{userHome.availableLeaders}" var="user" 
    itemValue="#{user}" itemLabel="#{user.name}" />

This allows you to get rid of the additional selectItems property and the loop in the bean constructor.

BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
  • 1
    +1 for keeping the SelectItem type out of the backing bean and having it use simple lists of domain objects ;) – Arjan Tijms Aug 20 '11 at 12:42