Why does @PostConstruct callback fire every time even though bean is @ViewScoped? JSF
Asked Answered
F

5

32

I am using datatable on page and using binding attribute to bind it to my backing bean. This is my 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:p="http://primefaces.prime.com.tr/ui">
    <h:head>
        <title>Facelet Title</title>
    </h:head>
    <h:body>
            <h:form prependId="false">

                <h:dataTable var="item" value="#{testBean.stringCollection}" binding="#{testBean.dataTable}">
                    <h:column>
                        <h:outputText value="#{item}"/>
                    </h:column>
                    <h:column>
                        <h:commandButton value="Click" actionListener="#{testBean.action}"/>
                    </h:column>
                </h:dataTable>

            </h:form>

    </h:body>
</html>

This is my bean :-

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;
import javax.faces.component.html.HtmlDataTable;

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

    private List<String> stringCollection;

    public List<String> getStringCollection() {
        return stringCollection;
    }

    public void setStringCollection(List<String> stringCollection) {
        this.stringCollection = stringCollection;
    }

    private HtmlDataTable dataTable;

    public HtmlDataTable getDataTable() {
        return dataTable;
    }

    public void setDataTable(HtmlDataTable dataTable) {
        this.dataTable = dataTable;
    }

    @PostConstruct
    public void init(){
        System.out.println("Post Construct fired!!");
        stringCollection = new ArrayList<String>();
        stringCollection.add("a");
        stringCollection.add("b");
        stringCollection.add("c");

    }

    public void action(){
        System.out.println("Clicked!!");

    }
}

Please tell me why is the @PostConstruct firing each and every time i click on button? It should fire only once as long as i am on same page beacause my bean is @ViewScoped. Further, if i remove the binding attribute then everything works fine and @PostConstruct callback fires only once. Then why every time when i use binding attribute? I need binding attribute and want to perform initialisation tasks like fetching data from webservice, etc only once. What should i do? Where should i write my initialisation task?

Frizzle answered 9/5, 2010 at 9:45 Comment(1)
Is there a solution for this? same problem here...Cuddy
H
34

Interesting, when you're using component binding on a view scoped bean, the view scope breaks.

I am not sure if that is a bug in JSF2, I would have to read the entire JSF2 specification first. As far now your best bet is to drop the component binding for now and pass the selected item via new EL 2.2 method argument syntax:

<h:dataTable var="item" value="#{testBean.stringCollection}">
    <h:column>
        <h:outputText value="#{item}"/>
    </h:column>
    <h:column>
        <h:commandButton value="Click" action="#{testBean.action(item)}"/>
    </h:column>
</h:dataTable>

See also:


Update (Dec 2012): 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, the binding attribute 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 1492 and JSF spec isssue 787 which will be fixed for JSF 2.2. Until then, your best bet is to use binding on request scoped beans exclusively, or to look for alternate ways for the particular functional requirement.


Update (Mar 2015): The JSF 2.2 fix was backported to Mojarra 2.1.18. So if you're still using JSF 2.0/2.1, you'd best upgrade to at least that version. See also a.o. What is component binding in JSF? When it is preferred to be used? and JSTL in JSF2 Facelets... makes sense?

Herbertherbicide answered 9/5, 2010 at 16:37 Comment(7)
The JSF spec doesn't say any word about this. I've filed an issue. By the way I could now reproduce your problem that only the 1st item was returned everytime. I made a mistake in my environment, the same bean was namely already declared as request scoped in faces-config.xml, which has overridden the annotations.Herbertherbicide
Hmm! :) so is that too a bug? about datatable?Frizzle
See the updated answer, DataModel#getRowData() is a better choice.Herbertherbicide
You're welcome. Actually, thank you for blogging inspiration :)Herbertherbicide
Hi BalusC, I read your article about ViewScoped as well as reading this posts. I have two questions for you. First: why would people binding a table to the managed bean. In this example, I think he mention of binding the dataTable to initialize its value, but you can just initialize a list of object in @PostConstruct, and display them in dataTable. Would that not accomplish the same result? Secondly: in your project, u used DataModel, so if u wrap the list of object, that u want to display in dataTable, in a DataModel, then whichever row the user selected, u will know if u call getRowData()?Syringe
1) It's a leftover from JSF 1.x. 2) Yes. Just getRowData() in action method will give you the row where action is been invoked.Herbertherbicide
Hi balus, any solutions for the problem in the viewScope??, thank you...i sorry for my english.Humming
S
4

As other said, I would say that the best thing to do is to drop component binding (you don't need it here).

But I would add that you can achieve the same as you're trying to do in a more object-oriented fashion by using action parameters, like this:

<h:commandButton value="Click" action="#{testBean.action(item)}"/>

... and in your java code:

  public void action(Item item){
    System.out.println("Clicked!!" + item);
}
Senegambia answered 16/3, 2011 at 15:10 Comment(2)
Noted should be that this requires a Servlet 3.0 / EL 2.2 capable container. JSF 2.0 is designed to be backwards compatible with Servlet 2.5 and this may thus fail when you run JSF 2.0 on a Servlet 2.5 container. See also this answer for all ways.Herbertherbicide
I think it requires EL 2.2, but not Servlet 3.0. Until last week, we used to run EL 2.2 on Glassfish 2, which doesn't have Servlet 3.0, and it worked nicely.Senegambia
E
0

If you have a viewscoped bean and if you want to retain values that were entered on the form or don't want postconstruct fired, you should return null from your action method.

If you return some outcome (e.g. invalid) and then point the invalid outcome to the same page using faces-config.xml, then the viewscoped bean gets recreated and thus it causes postconstruct to fire again.

Eustis answered 26/3, 2013 at 17:20 Comment(0)
P
0

Other solution:

  • Binding the HtmlDataTable in a request scope bean.
  • Inject this request scope bean in the view scope bean.

JBoss Seam use this solution for binding JSF componentes to a conversation scope component.

Ponce answered 16/5, 2013 at 15:50 Comment(0)
L
0

The balusc's answer helped me a lot, i would like to say that i had that bug with mojarra version 2.1.7, i am currently using 2.1.29-01 released in january-2015 and this bug is fixed, my problem was binding a tabview to a viewscoped bean. With this version I dont have that bug and binding and postconstruct is working fine. I use Jboss 5.2 and i have to use mojarra 2.1.x so i hope this answer help other people in the same situation.

http://mvnrepository.com/artifact/com.sun.faces/jsf-api/2.1.29-01 http://mvnrepository.com/artifact/com.sun.faces/jsf-impl/2.1.29-01

Lumbye answered 20/3, 2015 at 12:54 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.