1

I replaced rich:pickList to selectManyListbox for testing, when submit values to server it still displays error: "Validation Error: Value is not valid". I also set breakpoint to debug StaffConverter (getAsobject), but system never invokes it. Pls let me know some reasons why my converter never invoked and suggest me how to fix this. thanks

xhtml file:

<h:selectManyListbox value="#{reportController.selectedStaffList}" converter="staffConverter">
   <f:selectItems value="#{reportController.staffList}" 
                  var="item" itemValue="#{item}" itemLabel="#{item.name}" />
</h:selectManyListbox>

<rich:pickList value="#{reportController.selectedStaffList}" converter="staffConverter" sourceCaption="Available flight" targetCaption="Selected flight" listWidth="195px" listHeight="100px" orderable="true">
   <f:selectItems value="#{reportController.staffList}" var="item" itemValue="#{item}" itemLabel="#{item.name}" />
</rich:pickList>

My Converter:

@FacesConverter(forClass = Staff.class)
public static class StaffConverter implements Converter {

    public Object getAsObject(FacesContext facesContext, UIComponent component, String value) {
        if (value == null || value.length() == 0) {
            return null;
        }
        StaffController controller = StaffController.getInstance();
        return controller.facade.find(Staff.class, getKey(value));
    }

    java.lang.Integer getKey(String value) {
        java.lang.Integer key;
        key = Integer.valueOf(value);
        return key;
    }

    String getStringKey(java.lang.Integer value) {
        StringBuffer sb = new StringBuffer();
        sb.append(value);
        return sb.toString();
    }

    public String getAsString(FacesContext facesContext, UIComponent component, Object object) {
        if (object == null) {
            return null;
        }
        if (object instanceof Staff) {
            Staff o = (Staff) object;
            return getStringKey(o.getStaffCode());
        } else {
            throw new IllegalArgumentException("object " + object + " is of type " + object.getClass().getName()
                    + "; expected type: " + StaffController.class.getName());
        }
    }
}

I implemented equals method in Staff:

@Override
public boolean equals(Object object) {
    if (!(object instanceof Staff)) {
        return false;
    }
    Staff other = (Staff) object;
    return (this.staffCode == other.staffCode);
}
Bryan
  • 79
  • 1
  • 7
  • Hi BalusC, staffCode has type of integer. I set breakpoint to debug but it never jumps into getAsObject function, I don't understand why. – Bryan Oct 15 '12 at 15:33
  • Using `==` on objects is wrong, but that can't be the cause if `getAsObject()` is never invoked. Well, I don't see the cause. Perhaps it's RichFaces specific. – BalusC Oct 15 '12 at 15:35
  • I replaced my code from == to equals: this.staffCode.equals(other.staffCode) but it still displays error: frmReport:j_idt26: Validation Error: Value is not valid – Bryan Oct 15 '12 at 15:50
  • Yes, I know, I already said that it can't be the cause if `getAsObject()` is never invoked. But it at least eliminates a possible future problem. – BalusC Oct 15 '12 at 15:50
  • Hi BalusC, I've just updated my code, but still displays that error, Could you pls correct me if I am wrong something ? – Bryan Oct 16 '12 at 15:08
  • Maybe the @FacesConverter(forClass = Staff.class) isn't getting picked up. Trying using a named converter, and explicitly reference your converter un your selectManyListbox by name. – Brian Leathem Oct 16 '12 at 18:35

2 Answers2

2

Just to have it posted somewhere for people - like me - who like searching for solutions for development issues on google or stackoverflow...

I had this issue several times, depending on which kind of pojos I was using in the converters... Finally I think I found an elegant solution. In my case I use JPA entity classes directly as I wanted to save the DTO layer. Well, with some entities the rich:pickList worked, with others not... I also tracked it down to the equals method. In the example below for instance it did not work for the userGroupConverter bean.

My solution is simply to inline overwrite the equals method, so the one from the entity (I often use lombok) is untouched and does not need to be changed, at all! So in my converter below I only compare the name field in equals:

xhtml:

    <rich:pickList id="pickListUserGroupSelection"
        value="#{usersBean.selectedUserGroups}" switchByDblClick="true"
        sourceCaption="Available user groups" targetCaption="Groups assigned to user"
        listWidth="365px" listHeight="100px" orderable="false"
        converter="#{userGroupConverter}"
        disabled="#{!rich:isUserInRole('USERS_MAINTAIN')}">

        <f:validateRequired disabled="true" />
        <rich:validator disabled="true" />

        <f:selectItems value="#{usersBean.userGroups}" var="userGroup"
            itemValue="#{userGroup}"
            itemLabel="#{userGroup.name}" />

        <f:selectItems value="#{usersBean.selectedUserGroups}" var="userGroup"
            itemValue="#{userGroup}"
            itemLabel="#{userGroup.name}" />

    </rich:pickList>
    <rich:message for="pickListUserGroupSelection" />

Converter:

package ...;

import ...

/**
 * JSF UserGroup converter.<br>
 * Description:<br>
 * JSF UserGroup converter for rich:pickList elements.<br>
 * <br>
 * Copyright: Copyright (c) 2014<br>
 */
@Named
@Slf4j
@RequestScoped // must be request scoped, as it can change every time!
public class UserGroupConverter implements Converter, Serializable {

    /**
     *
     */
    private static final long serialVersionUID = 9057357226886146751L;

    @Getter
    Map<String, UserGroup> groupMap;

    @Inject
    MessageUtil messageUtil;

    @Inject
    UserGroupDao userGroupDao;

    @PostConstruct
    public void postConstruct() {

        groupMap = new HashMap<>();

        List<UserGroup> userGroups;
        try {
            userGroups = userGroupDao.findAll(new String[] {UserGroup.FIELD_USER_ROLE_NAMES});

            if(userGroups != null) {

                for (UserGroup userGroup : userGroups) {

                    // 20150713: for some reason the UserGroup entity's equals method is not sufficient here and causes a JSF validation error
                    // "Validation Error: Value is not valid". I tried this overridden equals method and now it works fine :-)
                    @SuppressWarnings("serial")
                    UserGroup newGroup = new UserGroup() {

                        @Override
                        public boolean equals(Object obj){
                            if (!(obj instanceof UserGroup)){
                                return false;
                            }

                            return (getName() != null)
                                 ? getName().equals(((UserGroup) obj).getName())
                                 : (obj == this);
                        }
                    };
                    newGroup.setName(userGroup.getName());

                    groupMap.put(newGroup.getName(), newGroup);
                }
            }

        } catch (DaoException e) {

            log.error(e.getMessage(), e);

            FacesContext fc = FacesContext.getCurrentInstance();
            FacesMessage message = new FacesMessage(FacesMessage.SEVERITY_INFO, "Error initializing user group converter!", null);
            fc.addMessage(null, message);
        }
    }

    /*
     * (non-Javadoc)
     * @see javax.faces.convert.Converter#getAsObject(javax.faces.context.FacesContext, javax.faces.component.UIComponent, java.lang.String)
     */
    @Override
    public UserGroup getAsObject(FacesContext context, UIComponent component,
            String value) {

        UserGroup ug = null;

        try {

            ug = getGroupMap().get(value);
        } catch (Exception e) {

            log.error(e.getMessage(), e);

            FacesContext fc = FacesContext.getCurrentInstance();
            FacesMessage message = new FacesMessage(FacesMessage.SEVERITY_INFO, "Error converting user group!", null);
            fc.addMessage(null, message);
        }

        return ug;
    }

    /*
     * (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) {

        String name = ((UserGroup) value).getName();

        return name;
    }

}

Brgds and have fun!

eztup
  • 311
  • 3
  • 5
0

Thanks Brian and BalusC for your help. I fixed my problem by adding named converter inside selectManyListbox & rich:picklist so they run well. But normally, I only use @FacesConverter(forClass = Staff.class), and don't need to add named converter in jsf so they still run well except for selectManyListbox & rich:picklist

Bryan
  • 79
  • 1
  • 7