Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JSTL c:forEach causes @ViewScoped bean to invoke @PostConstruct on every request

Again i see that the @PostConstruct is firing every time even though no binding attribute is used. See this code :-

<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:ui="http://java.sun.com/jsf/facelets"
      xmlns:c="http://java.sun.com/jsp/jstl/core">
    <h:head>
        <title>Facelet Title</title>
    </h:head>
    <h:body>
        <h:form>
            <c:forEach var="item" items="#{TestBean.listItems}">
                <h:outputText value="#{item}"/>
            </c:forEach>
            <h:commandButton value="Click" actionListener="#{TestBean.actionListener}"/>
        </h:form>
    </h:body>
</html>

And this is the simplest possible bean in JSF :-

package managedBeans;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import javax.annotation.PostConstruct;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ViewScoped;

@ManagedBean(name="TestBean")
@ViewScoped
public class TestBean implements Serializable {

    private List<String> listItems;

    public List<String> getListItems() {
        return listItems;
    }

    public void setListItems(List<String> listItems) {
        this.listItems = listItems;
    }

    public TestBean() {

    }

    @PostConstruct
    public void init(){
        System.out.println("Post Construct fired!");
        listItems = new ArrayList<String>();
        listItems.add("Mango");
        listItems.add("Apple");
        listItems.add("Banana");
    }

    public void actionListener(){
        System.out.println("Action Listener fired!");
    }

}

Do you see any behaviour that should cause postconstruct callback to fire each time? I think JSF 2.0 is highly unstable. If it has to fire PostConstruct each and every time what purpose does @ViewScoped serve. Why not to use @RequestScoped only? I thought i have made some mistake in my application. But when i created this simplest possible in JSF, i still get this error. Am i not understanding the scopes of JSF? or are they not testing it properly? Further, if you remove c:forEach and replace it with ui:repeat, then it works fine.

Waiting for replies to confirm whether it is bug or it is intentional to stop the programmers from using jstl?

like image 249
TCM Avatar asked May 16 '10 02:05

TCM


1 Answers

This problem has the same grounds as your previous question which is answered here: Why does @PostConstruct callback fire every time even though bean is @ViewScoped? JSF.

This is indeed a bug in JSF2. It's a chicken-egg issue. The view scoped beans are stored in the JSF view state. So the view scoped beans are only available after restore view phase. However, JSTL tags runs during restore view phase, while the view scoped beans are not available yet. This causes creation of a brand new view scoped bean instance, which is then later replaced by the real view scoped bean which was stored in the restored JSF view state.

This is reported as JSF issue 1665 and JSF spec isssue 787 which is fixed for JSF 2.2 and ported back in Mojarra 2.1.18. So, just upgrading to a minimum of Mojarra 2.1.18 should do.

If you can't upgrade, then your best bet is to use JSTL tags on request/session/application scoped beans exclusively, or to look for alternate ways for the particular functional requirement. You can replace the <c:forEach> with <ui:repeat> as you already found out.

See also:

  • Communication in JSF 2.0 - @ViewScoped fails in taghandlers
  • JSTL in JSF2 Facelets... makes sense?
like image 111
BalusC Avatar answered Sep 18 '22 15:09

BalusC