3

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

}
Sundeep Kadakia
  • 71
  • 1
  • 2
  • 4
  • 1
    A few remarks; you don't show any code that uses `TestRequestBB`. The Facelet only uses `TestSessionBB`. Also, if you're using a beta version of JSF (2.2 in this case) you should expect problems, so if there's any weird behavior test with a released version first. And what's up with facelets 1.1? Did you package that jar in your application? JSF comes with Facelets (2.0 in the case of JSF 2.x). Don't package it separately. Finally, implementing a pager in a session scoped bean probably isn't a very good idea. – Arjan Tijms Feb 19 '12 at 08:17
  • When "testList" on datatable is called, which is declared as @Produced method, it gets TestReqestBB initialized. Yes, you're right, I am using the facelets one bundled with jsf 2.2, and not facelet 1.1. About Mojarra 2.2. snapshot, similar behavior of calling PostConstruct method twice was seen with earlier stable version as well, hence I used the latest snapshot to see if it can go away but not so far. Will appreciate all suggestions to resolve this issue. – Sundeep Kadakia Feb 19 '12 at 21:04
  • I have the same problem, just with a RequestScoped-bean. I pass a Request-Parameter to that page/bean. The bean is constructed with CDI and has a PostConstruct-method that should then load the Object that belongs to the ID that came with the request. But the value is applied in the "Apply Request Value" phase and postconstruct is called to early (after the constructor is finished). I am on Mojarra 2.1.2. Thanks – lostiniceland Feb 24 '12 at 10:53
  • @lostiniceland: so for you the `@PostConstruct` is also called twice? – BalusC Feb 24 '12 at 13:06
  • @BalusC No. Only once but before the request-value is mapped to the bean. What I read about the PostConstruct-Annotation than this is called directly after all dependency injection has been done...but it doesnt take the JSF-lifecycle into account. I guess I am doing something completely wrong since it should be a standard procedure but my book and the other examples I've found so far are simple helloworld-ones where the parameters are coming from other SessionScoped-Beans (not passed with the request). – lostiniceland Feb 24 '12 at 14:55

1 Answers1

7

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:

Community
  • 1
  • 1
BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
  • I came accross the preRenderView-listener yesterday evening during my initial research. The approach to directly work on the jsf-phases seemed a little bit "hacky" at first ;) and I expected that there must be something else. Anyway...thanks again for your detailed help. – lostiniceland Feb 24 '12 at 15:40
  • 1
    @lostiniceland: Your initial business job had just to be done inside the ajax listener method. You want to invoke a business action, there the action(listener) method is for. Much better, however, is to use a single view scoped bean so that you don't need to mingle a request and session scoped bean together. Storing view-specific state in a session scoped bean is a bad design. If you were using JSF2 annotations, you could have used `@ViewScoped` for this. In CDI you can simulate this with `@ConversationScoped`. – BalusC Feb 24 '12 at 15:52
  • If each `@Named` results in an entirely separate instance, how do you ensure that a view uses the correct @Named? – Thufir Apr 12 '12 at 12:49
  • @thufir: just specify one and specify the proper scope along it. – BalusC Apr 12 '12 at 12:56