Sundeep Kadakia
Sundeep Kadakia

Reputation: 71

How to set parameter value in SessionScoped bean before @PostConstruct gets called in RequestScoped bean

Will appreciate your help resolving following issues (JBoss 6.0, Mojarra - 2.2 Snapshot, facelet 1.1 and PrimeFaces 3.0.M4:

Problem is, the request bean's post construct method gets called before getting the value set. How can we make sure the parameter value on a session bean gets set first and then the request bean's post construct method is called.

Issue #1: When clicked on "Next", which is an ajax call 1. testRequestBB's "initialize" post construct method is called 2. testSessionBB's "next" method is called to set the value

Expected behavior should be other way around, set the value in session bean with ajax call and then request bean should be initialized.

Issue #2: The request bean's "initialize" post construct method is called twice.
- Is it because the request bean gets extended from the base class (though no post construct method in the base class).

What can be done to fix this issue of getting post construct method called two times when test.xhtml page is displayed?

Here is the code:

test.xhtml

<h:dataTable id="testId" emptyMessage="#{messages.noData}" var="test" value="#{testList}">
....
<f:facet name="footer">
    <h:form id="pgId">                  
        <h:commandLink value="#{messages.next} ">
            <f:ajax listener="#{testSessionBB.next}" />
        </h:commandLink>
            .....
    </h:form>
</f:facet>
</h:dataTable>

TestSessionBB.java

@Named("testSessionBB")
@SessionScoped
public class TestSessionBB implements Serializable
{
    private int testStartRow;
    .....

    public String next() 
    {
        if (this.getTestStartRow() + 5 > 15) // hard coded value for simplicity in this post
        {
            this.setTestStartRow(15);
        } else {
            this.setTestStartRow(this.getTestStartRow() + 5);
        }
        log.debug("next - testStartRow: " + this.getTestStartRow());

        return "";
    }
}

TestRequestBB.java

@Named
@RequestScoped
public class TestRequestBB extends testBase implements Serializable {

    ....

    @PostConstruct
    public void initialize()
    {
        log.debug("Initializing TestRequestBB backing bean ...");

        setTestList(allTests()); // load initial list containing 5 rows of test data

        log.debug("Initializing TestRequestBB backing bean done ...");
    }

    @Produces
    @Named
    public List<Test> getTestList()
    {   
        return super.getTestList();
    }
    ....
}

TestBase.java

public abstract class TestBase implements Serializable {

    ..... (contains all common code shared by other classes extending this base class)

    // does not contain any @PostConstruct method

}

Upvotes: 3

Views: 6394

Answers (1)

BalusC
BalusC

Reputation: 1109725

The @PostConstruct will indeed never have access to the updated model values. They are only available after the update model values phase (thus, during invoke action and render response phase). The @PostConstruct is never intented to perform actions on updated model values, it's intented to perform actions on injected dependencies which are in turn also freshly constructed if necessary, such as @EJB, @Inject, @ManagedProperty.

What you want is a method which is invoked during invoke action phase. You need to either do the job inside the ajax listener method of the TestSessionBB instead:

@Inject
private TestRequestBB testRequestBB;

public void next() { // No, no need to return String.
    // ...

    testRequestBB.initialize();
}

Or to add a <f:event type="preRenderView"> to the view. This allows you to execute bean methods at the very end of the invoke action phase, right before the render response phase. Put this somewhere in your view:

<f:event type="preRenderView" listener="#{testRequestBB.initialize}" />

Don't forget to remove the @PostConstruct annotation from the method.

As to the problem of the @PostConstruct method being invoked twice, that's because each @Named results in an entirely separate instance. You've two @Named on the very same class, one on the class itself and one on the getter method itself. I'd suggest to remove the one on the getter method and replace #{testList} in your view by #{testRequestBB.testList}.

<h:dataTable value="#{testRequestBB.testList}" ...>

If you really, really, need to grab request parameters inside a @PostConstruct, then you could grab them manually by ExternalContext#getRequestParameterMap():

@PostConstruct
public void initialize() {
    String foo = (String) FacesContext.getCurrentInstance().getExternalContext().getRequestParameterMap().get("foo");
    // ...
}

If you were using JSF2 @ManagedBean annotation instead of the CDI @Named annotation, you could also have used @ManagedProperty for this instead:

@ManagedProperty("#{param.foo}")
private String foo;

This value will be available during the @PostConstruct. There is no such annotation in CDI, you can however homegrow one yourself if necessary.

See also:

Upvotes: 7

Related Questions