tags:

views:

734

answers:

2

Hi,

I'm working and learning about JSF + Facelets these days. I have a BackingBean and a Facelet xHTML page. When I request the facelet-page (only one time) the backing-bean-method is called multiple times.

What could be the reason for this?

I can't see anything special. Thanks in advance.

Here is the facelet:

<?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"&gt;
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:f="http://java.sun.com/jsf/core" xmlns:h="http://java.sun.com/jsf/html" xmlns:ui="http://java.sun.com/jsf/facelets"&gt;
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>Insert title here</title>
</head>
<body>
<ui:composition template="index.xhtml">
    <ui:define name="content">
        <h:form>Name: <h:inputText id="nameFilterPattern" value="#{kundenBackingBean.nameFilterPattern}" /><h:commandButton value="Suchen"/></h:form>
        <h:dataTable var="kunde" value="#{kundenBackingBean.kunden}" rowClasses="rowHighlight, rowOrdinary">
            <h:column> 
                <f:facet name="header">
                    <h:outputText value="Kundennr" />
                </f:facet>
                <h:outputText value="#{kunde.kundenNr}"/>
            </h:column>
            <h:column>
                <f:facet name="header">
                    <h:outputText value="Name" />
                </f:facet>
                <h:outputText value="#{kunde.name}"/>
            </h:column>
            <h:column>
                <f:facet name="header">
                    <h:outputText value="Vorname" />
                </f:facet>
                <h:outputText value="#{kunde.vorname}"/>
            </h:column>
            <h:column>
                <h:outputLink>Details</h:outputLink>
            </h:column>
        </h:dataTable>
    </ui:define>
</ui:composition>
</body>
</html>

And here is the backing-bean. The method getKunden is called multiple times:

@ManagedBean
@SessionScoped
public class KundenBackingBean extends AbstractBackingBean {

    private String nameFilterPattern;

    public List<Kunde> getKunden(){
        System.out.println("getKunden");
        return getApplication().getKunden(getNameFilterPattern());
    }

    public String getNameFilterPattern() {
        return nameFilterPattern;
    }

    public void setNameFilterPattern(String nameFilterPattern) {
        System.out.println("Name filter: " + nameFilterPattern);
        this.nameFilterPattern = nameFilterPattern;
    }

}
+1  A: 

It can be called from different phases of the JSF lifecylce. My bet would be the phases RestoreView and then RenderResponse -- I haven't been using JSF lately, so I don't remember this in detail.

You can cache the latest filter pattern and the corresponding clients. You reload the clients only if the filter changed. This way, you solve this particular problem, plus avoid reloading data if the filter didn't change.

private String nameFilterPattern;
private String lastNameFilterPatternLoaded;
private List<Kunde> clients;

public List<Kunde> getKunden(){
    System.out.println("getKunden");
    if( nameFilterPattern.equals( lastNameFilterPatternLoaded ) )
    {
        clients = getApplication().getKunden(getNameFilterPattern());
        lastNameFilterPatternLoaded = nameFilterPattern
    }
    return clients;
}

Or you can use a request bean (instead of session) and make sure you load the data only once per request.

ewernli
Thanks for answering. I changed the bean to request scope. The behaviour is the same. How do I make sure to load the data only onces per request? As you can see in the facelet, the method is only referenced once. Could this isse be related the the template stuff: ui:composition / ui:define?
c0d3x
The answer from BalusC that you've accepted covers it all. I guess you don't need further explanation.
ewernli
+3  A: 

The getters of a bean are just there to access model data from the view side. They can be called multiple times. Usually one or two times, but this can grow up to hundreds of times, especially when also used in UIData components or in other attributes than value (like rendered, disabled, etc). This does normally not harm, as it's just a simple method-invocation and doing expensive data loading logic or calculations is usually not to be done in the getters. Preloading/initializing is usually to be done in the bean constructor and/or bean action methods. Getters should in fact only return the data (if necessary also do lazy loading).

If getApplication().getKunden(getNameFilterPattern()); is doing a pretty expensive task, you should really move it to either the bean constructor, or bean @PostConstruct method, or bean initialization block, or bean action method, or introduce lazy loading pattern in the getter. Here's an example which shows how to do this all:

public class Bean {
    private String nameFilterPattern;
    private List<Kunde> kunden;

    // Load during bean construction.
    public Bean() {
        this.kunden = getApplication().getKunden(getNameFilterPattern());
    }

    // OR load during @PostConstruct (will be invoked AFTER construction and resource injection.
    @PostConstruct
    public void init() {
        this.kunden = getApplication().getKunden(getNameFilterPattern());
    }

    // OR during bean initialization (this is invoked BEFORE construction and will apply to ALL constructors).
    {
        this.kunden = getApplication().getKunden(getNameFilterPattern());
    }

    // OR during bean action method (invoked from h:commandLink/Button).
    public String submit() {
        this.kunden = getApplication().getKunden(getNameFilterPattern());
        return "navigationCaseOutcome";
    }

    // OR using lazy loading pattern in getter method.
    public List<Kunde> getKunden() {
        if (this.kunden == null) 
            this.kunden = getApplication().getKunden(getNameFilterPattern());
        }
        return this.kunden;
    }

In your specific case, I think it's the @PostConstruct (if the nameFilterPattern is to be obtained from a GET request parameter), or just the bean action method (if nameFilterPattern is to be obtained from a POST form input field) is suitable.

To learn more about the JSF lifecycle, you may find this self-practice article useful.

BalusC
Nice answer, btw :) http://stackoverflow.com/questions/2090033/why-jsf-calls-getters-multiple-times
ewernli
Yes, it's certainly not the first time I answered like that :) I've aswered it at least 20 times before. Not only here, but also at forums.sun.com and so on.
BalusC