Search code examples
jsfdatatableeljsf-2.2setpropertyactionlistener

JSF NumberFormatException with f:setPropertyActionListener


I'm getting this error with f:setPropertyActionListener and i can't figure out why:

HTTP Status 500 - For input string: "selectedItem"

exception:
javax.servlet.ServletException: For input string: "selectedItem"
javax.faces.webapp.FacesServlet.service(FacesServlet.java:667)
org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)

root cause:
java.lang.NumberFormatException: For input string: "selectedItem"
java.lang.NumberFormatException.forInputString(Unknown Source)
java.lang.Integer.parseInt(Unknown Source)
java.lang.Integer.parseInt(Unknown Source)
javax.el.ListELResolver.coerce(ListELResolver.java:157)
javax.el.ListELResolver.getType(ListELResolver.java:50)
com.sun.faces.el.DemuxCompositeELResolver._getType(DemuxCompositeELResolver.java:215)
com.sun.faces.el.DemuxCompositeELResolver.getType(DemuxCompositeELResolver.java:242)
org.apache.el.parser.AstValue.getType(AstValue.java:60)
org.apache.el.ValueExpressionImpl.getType(ValueExpressionImpl.java:168)
com.sun.faces.facelets.el.TagValueExpression.getType(TagValueExpression.java:98)
com.sun.faces.facelets.tag.jsf.core.SetPropertyActionListenerHandler$SetPropertyListener.processAction(SetPropertyActionListenerHandler.java:209)
javax.faces.event.ActionEvent.processListener(ActionEvent.java:88)
javax.faces.component.UIComponentBase.broadcast(UIComponentBase.java:813)
javax.faces.component.UICommand.broadcast(UICommand.java:300)
javax.faces.component.UIData.broadcast(UIData.java:1108)
javax.faces.component.UIViewRoot.broadcastEvents(UIViewRoot.java:790)
javax.faces.component.UIViewRoot.processApplication(UIViewRoot.java:1282)
com.sun.faces.lifecycle.InvokeApplicationPhase.execute(InvokeApplicationPhase.java:81)
com.sun.faces.lifecycle.Phase.doPhase(Phase.java:101)
com.sun.faces.lifecycle.LifecycleImpl.execute(LifecycleImpl.java:198)
javax.faces.webapp.FacesServlet.service(FacesServlet.java:654)
org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)

Table Class:

// imports omitted
public class Table<E> extends ArrayList<E> {

    private E selectedItem;

    public E getSelectedItem() { return selectedItem; }

    public void setSelectedItem(E value) { selectedItem = value; }
}

MyTable Bean:

// Imports omitted
@ManagedBean
@ViewScoped
public class MyTable extends Table<File> {

    @PostConstruct
    public void initBean() {
        // Loading some files into the list
    }
}

This is the XHTML:

<html> <!-- Namespaces and stuff omitted -->
    <h:head>...</h:head>
    <h:body>
      <h:form>
        <h:dataTable var="item" value="#{myTable}">
          <h:column>
            <h:commandButton value="Try Me!">
              <f:setPropertyActionListener value="#{item}" target="#{myTable.selectedItem}"/>
                <!-- I'm getting a warning from eclipse here: property not found -->
            </h:commandButton>
          </h:column>
        </h:dataTable>
      </h:form>
    </h:body>
</html>

I'm using Eclipse Luna (Java EE IDE) with Tomcat 8 and JSF 2.2.11 (mojarra). Any hints are accepted, thank you!


Solution

  • You kind of coded your self into a corner with your fancy bean implementation. Take a look at the processing steps for the f:setActionPropertyListener. Your code is choking at step 3:

    If the value of the "value" expression is not null, call getType() on the "value" and "target" ValueExpressions to determine their property types

    for the following reasons:

    1. The EL processor has determined that myTable is a List. Because of this, it has delegated the evaluation of the expression myTable.selectedItem to the javax.el.ListELResolver class

    2. The ELResolver, on encountering the myTable base object, determines it's a List and automatically assumes that the following string is referring to a list index, i.e. myTable.selectedItem, where selectedItem is supposed to be a list index (per the EL specification, the [] and . are interchangeable for lists). You can see it in action here. While it may not be immediately apparent in the tomcat source, if you check the comment in a similar implementation in Jboss for example, you have the following comment:

      If the base object is a list, returns the value at the given index. The index is specified by the property argument, and coerced into an integer

    "property argument" here is referring to the selectedItem portion of your expression

    1. The EL processor now attempts to convert the string selectedItem to an integer (to be used as a list index) which in turn explodes in a 500

    You'll make your work a whole lot easier by not combining your data structure and your backing bean, like Rami. Q suggested. Much cleaner that way IMO