Don't know how to iterate over supplied "items" in <c:forEach>

user182944 picture user182944 · Apr 10, 2013 · Viewed 9.5k times · Source

I am using JSF 1.2 in my application. I am trying to iterate through a String array which is defined in my backing bean like this:

private String[] services;

Below is the managed bean entry in faces-config file:

<managed-bean>     
    <managed-bean-name>registrationBean</managed-bean-name>
    <managed-bean-class>com.bean.RegistrationBean</managed-bean-class>
    <managed-bean-scope>request</managed-bean-scope>        
</managed-bean>

I am trying to iterate through the services selected by the user and display it in my screen. Below is the code I used:

<c:forEach items="#{registrationBean.services}" var="service">
    <c:out value="${service}"></c:out>
</c:forEach>

But I am getting the error:

Don't know how to iterate over supplied "items" in <c:forEach>

Kindly let me know how to resolve this.

EDIT

If I change String[] to List<String> then I am getting this exception:

java.lang.RuntimeException: wrapped Exception: java.lang.UnsupportedOperationException
com.icesoft.faces.webapp.http.servlet.MainServlet.service(MainServlet.java:156)
javax.servlet.http.HttpServlet.service(HttpServlet.java:717)
com.icesoft.faces.webapp.xmlhttp.BlockingServlet.service(BlockingServlet.java:56)

Below is the Backing Bean initialization:

private List<String> services;
public RegistrationBean() {
    this.services = new ArrayList<String>();
}

Faces-config.xml:

<?xml version='1.0' encoding='UTF-8'?>

<managed-bean>     
    <managed-bean-name>registrationBean</managed-bean-name>
    <managed-bean-class>com.bean.RegistrationBean</managed-bean-class>
    <managed-bean-scope>request</managed-bean-scope>        
</managed-bean>   

<navigation-rule>
    <description>This will navigate to the Success screen.</description>
    <from-view-id>/registration.jspx</from-view-id>
    <navigation-case>
        <from-outcome>success</from-outcome>
        <to-view-id>/success.jspx</to-view-id>
    </navigation-case>
</navigation-rule>

Web.xml:

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5">

<description>
    ICEfaces Address Demo
</description>

<context-param>
    <param-name>javax.faces.STATE_SAVING_METHOD</param-name>
    <param-value>server</param-value>
</context-param>

<context-param>
    <param-name>javax.faces.application.CONFIG_FILES</param-name>
    <param-value>/WEB-INF/faces-config.xml</param-value>
</context-param>

<context-param>
    <param-name>com.sun.faces.validateXml</param-name>
    <param-value>true</param-value>
</context-param>
<context-param>
    <param-name>com.icesoft.faces.synchronousUpdate</param-name>
    <param-value>true</param-value>
</context-param>

<context-param>
  <param-name>javax.faces.DEFAULT_SUFFIX</param-name>
  <param-value>.jspx</param-value>
</context-param>

<listener>
    <listener-class>com.icesoft.faces.util.event.servlet.ContextEventRepeater</listener-class>
</listener>

<!-- Faces Servlet -->
<servlet>
    <servlet-name>Faces Servlet</servlet-name>
    <servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
    <load-on-startup> 1 </load-on-startup>
</servlet>

<servlet>
    <servlet-name>Persistent Faces Servlet</servlet-name>
    <servlet-class>com.icesoft.faces.webapp.xmlhttp.PersistentFacesServlet</servlet-class>
    <load-on-startup> 1 </load-on-startup>
</servlet>

<servlet>
    <servlet-name>Blocking Servlet</servlet-name>
    <servlet-class>com.icesoft.faces.webapp.xmlhttp.BlockingServlet</servlet-class>
    <load-on-startup> 1 </load-on-startup>
</servlet>

<!-- Faces Servlet Mapping -->
<servlet-mapping>
    <servlet-name>Faces Servlet</servlet-name>
    <url-pattern>*.faces</url-pattern>
</servlet-mapping>

<servlet-mapping>
    <servlet-name>Faces Servlet</servlet-name>
    <url-pattern>*.jspx</url-pattern>
</servlet-mapping>

<!-- Persistent Faces Servlet Mapping -->
<servlet-mapping>
    <servlet-name>Persistent Faces Servlet</servlet-name>
    <url-pattern>*.iface</url-pattern>
</servlet-mapping>

<servlet-mapping>
    <servlet-name>Persistent Faces Servlet</servlet-name>
    <url-pattern>/xmlhttp/*</url-pattern>
</servlet-mapping>

<servlet-mapping>
    <servlet-name>Blocking Servlet</servlet-name>
    <url-pattern>/block/*</url-pattern>
</servlet-mapping>

<session-config>
  <session-timeout>30</session-timeout>
</session-config>


<welcome-file-list>
    <welcome-file>index.jsp</welcome-file>
</welcome-file-list>
</web-app>

Answer

BalusC picture BalusC · Apr 10, 2013

I can't explain why you have this problem provided that you're on JSF 1.2. Most likely your project or environment is messed up with duplicate or conflicting libraries. Or your faces-config.xml is declared conform JSF 1.1 instead of JSF 1.2. Or perhaps ICEfaces played a role somehow, but I can't tell that as I've never really used that library.

In any case, I can explain specifically this problem when you're actually using JSF 1.1. The #{} is then not supported in JSP tags at all and the <c:forEach items> would produce exactly this error.

Don't know how to iterate over supplied "items" in <c:forEach>

You should then be using ${} all the time in JSP tags. However, the ${} won't autocreate JSF managed beans when they are not present in the scope yet. In that case, the <c:forEach> would effectively get an empty collection and render nothing (but thus not produce exactly the error you're facing!).

So, you'd need to make sure that the JSF managed bean is already been autocreated before the <c:forEach> is entered. You can do that by using fullworthy JSF component like <h:panelGroup rendered="#{not empty bean.list}"> wrapping the tag, or a <h:outputText value="#{bean.text}"> before the tag.

E.g.

<h:someComponent someAttribute="#{registrationBean.something}" />
<c:forEach items="${registrationBean.services}" var="service">
    <c:out value="${service}" />
</c:forEach>

A fullworthy JSF 1.x alternative would be to use Tomahawk's <t:dataList> instead of JSTL <c:forEach>.

<t:dataList value="#{registrationBean.services}" var="service">
    <h:outputText value="#{service}" />
</t:dataList>