Reputation: 71
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:
<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>
@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 "";
}
}
@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();
}
....
}
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
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.
Upvotes: 7