Search code examples
jsf-2convertersmanaged-bean

how to use same List instance variable of backing bean for h:selectOneListbox and h:selectManyListbox


I have a requirement to use same variable of a request scoped backing bean which is a List datatype for h:selectOneListbox and h:selectManyListbox. I get "ip: Validation Error: Value is not valid" error with h:selectOneListbox even after using a converter. Can some one help me resolving this ?

In page1.xhtml I've given it as:

<h:selectManyListbox id="ip" value="#{inputBean.ipAddress}" size="5">
<f:selectItems value="#{inputBean.ipAddressList}" />
</h:selectManyListbox>

In page2.xhtml I've given it as:

<h:selectOneListbox id="ip" value="#{inputBean.ipAddress}" size="5">
    <f:selectItems value="#{inputBean.ipAddressList}" />
    <f:converter converterId="ipAddressConverter"></f:converter>
</h:selectOneListbox>

My Input bean, a request scoped managed bean looks like this:

@ManagedBean
@RequestScoped
public class InputTablePlot implements Serializable{
private static final long serialVersionUID = 1L;
@ManagedProperty("#{database}")
private Database database;
private Connection connection;
    private StringBuilder query;
    private PreparedStatement pst_query;
    private ResultSet rs;

private List<Long> ipAddress;
private Map<String, Long> ipAddrMenu;

public InputBean()
    {
        ipAddrMenu = new LinkedHashMap<String, Long>();
    }
@PostConstruct
    public void init()
    {
ipAddrMenu.clear();
try
        {
            connection = database.getDbConnection();
            query = new StringBuilder();
            query.append("SELECT distinct source AS ipaddr FROM addrtable ORDER BY source");
            pst_query = connection.prepareStatement(query.toString());
            rs = pst_query.executeQuery();
            while (rs.next())
            {
                ipAddrMenu.put(ipLongToString(rs.getLong("ipaddr")), rs.getLong("ipaddr")); // Adding
                // sources
            }
            rs.close();
            pst_query.close();
            connection.close();
        }
        catch (SQLException e)
        {
            e.printStackTrace();
        }
}
       public List<Long> getIpAddress()
    {
        System.out.println("In Getter" + ipAddress);
        return ipAddress;
    }

    public void setIpAddress(List<Long> ipAddress)
    {
        System.out.println("In Setter");
        System.out.println(ipAddress);
        this.ipAddress = ipAddress;

    }
        public Map<String, Long> getIpAddressList()
    {
        return ipAddrMenu;
    }

    public void setIpAddressList(Map<String, Long> ipAddressList)
    {
        this.ipAddrMenu = ipAddressList;
    }

@Override
    public int hashCode()
    {
final int prime = 31;
        int result = 1;
result = prime * result + ((connection == null) ? 0 : connection.hashCode());
        result = prime * result + ((database == null) ? 0 : database.hashCode());
        result = prime * result + ((ipAddrMenu == null) ? 0 : ipAddrMenu.hashCode());
        result = prime * result + ((ipAddress == null) ? 0 : ipAddress.hashCode());
return result;
}

@Override
    public boolean equals(Object obj)
    {
if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        InputBean other = (InputBean) obj;
if (connection == null)
        {
            if (other.connection != null)
                return false;
        }
        else if (!connection.equals(other.connection))
            return false;
        if (database == null)
        {
            if (other.database != null)
                return false;
        }
        else if (!m_database.equals(other.database))
            return false;
        if (ipAddrMenu == null)
        {
            if (other.ipAddrMenu != null)
                return false;
        }
        else if (!ipAddrMenu.equals(other.ipAddrMenu))
            return false;
        if (ipAddress == null)
        {
            if (other.ipAddress != null)
                return false;
        }
        else if (!ipAddress.equals(other.ipAddress))
            return false;

return true;
}

}

Converter code:

import java.util.ArrayList;
import java.util.List;

import javax.faces.application.FacesMessage;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.convert.Converter;


public class IpAddressConverter implements Converter
{

    @Override
    public Object getAsObject(FacesContext arg0, UIComponent arg1, String arg2)
    {
        List<Long> ipAddr = new ArrayList<Long>();
        try{
            ipAddr.add(Long.valueOf(arg2)); 
        }catch(NumberFormatException e){
            e.printStackTrace();
            FacesContext facesContext = FacesContext.getCurrentInstance();
            FacesMessage facesMessage = new FacesMessage("Problem with conversion of ip!");
            facesContext.addMessage(null, facesMessage);
        }
        for(int i=0; i< ipAddr.size(); i++){
            System.out.println("ipAddr >>> "+ipAddr);   
        }
        return ipAddr;
    }

    @Override
    public String getAsString(FacesContext arg0, UIComponent arg1, Object arg2)
    {
        String val = null;
        try{
        Long ip = (Long) arg2;
        val = ip.toString();
        }catch(Exception e){
            e.printStackTrace();
            FacesContext facesContext = FacesContext.getCurrentInstance();
            FacesMessage facesMessage = new FacesMessage("Problem with conversion of ip!");
            facesContext.addMessage(null, facesMessage);
        }
        System.out.println("value >>> "+val);
        return val;
    }

}

Solution

  • It failed because List<Long> as returned by the converter doesn't match the individual Long item of the available items while JSF is validating the submitted (and converted!) value against the list of available items (as part of safeguard against tampered/spoofed HTTP requests wherein attacker manipulated the selected item value). The converter is supposed to return a Long. All with all, such a converter is simply insuitable for usage in an UISelectOne component.

    You'd better bind the value straight to a single property. If you insist in using a List property, then just prepare it with a single empty item and specify the list item index in the value.

    private List<Long> ipAddress = new ArrayList<>(1);
    

    with

    <h:selectOneListbox id="ip" value="#{inputBean.ipAddress[0]}" size="5">
        <f:selectItems value="#{inputBean.ipAddressList}" />
    </h:selectOneListbox>
    

    See also: