preRenderComponent calls #PostConstruct multiple times - jsf

I am working on a page with a session-scoped managed bean.
On the page I have a preRenderComponent:
<f:metadata>
<f:event listener="#{pageBean.init}" type="preRenderComponent"></f:event>
</f:metadata>
This page is a template client, that shares a template with some other pages. The template contains a side navigation bar with links to each of the template client pages.
And the page bean:
#Named
#Default
#SessionScoped
public class pageBean implements Serializable {
#PostConstruct
public void init(){
System.out.println("Page Bean init.");
//call methods that populate data on the page
}
}
And the issue occurs as follows:
If I removed the line on the page with preRenderComponent, the pageBean init() method will still be called when the page is accessed.
If I kept the line said above, the init() method will be called when accessed, but it will also be called whenever I clicked on the side navigation bar and accessed another page which uses the same template.
I have referred to this question:CDI bean constructor and #PostConstruct called multiple times
and made sure I was not mixing JSF with CDI, yet this issue still occurs. Though it seems I could resolve this problem by just simply removing the preRenderComponent line, I really wish to understand what is going on here and figure out a way to avoid it in the future.
Any information is much appreciated.

Related

How to instantiate a backing bean on page load

For a project we are migrating some java applications to WebSphere 8.5. In the process we are trying to get rid of some legacy frameworks. One of them is shale (apache attic). The only component from shale that is used is the view controller to instantiate a request scoped jsf managed beans for every page. Every bean has an init method that is called on page load. I'd like to use #PostConstruct on this method. The only problem I have that the bean gets instantiated when a method on the bean is called. Unfortunately the bean is not always called and the init method does populate data on a session scoped bean. There is a naming convention that links pages and beans so we could use a listener to instantiate the bean based on the request. Another solution might be changing the scope to viewscope (probably to much a hassle on websphere 8.5).
I was wondering if there is something I can do to make the PostConstruct work? And are there other options I'm missing?
edit:
I have a PhaseListener in place that performs the basic functionality. It matches the requested page to the corresponding bean (by naming convention). The following is used to instantiate the bean but it looks a bit ugly.
expressionFactory.createValueExpression(elContext, "#{" + managedBeanName + "}", Object.class)
Is there a more elegant way to do this?
Perhaps you could try using <f:event/> ?
In your view, you could add this to the page.
<f:event type="postAddToView" listener="#{backingBean.myInitMethod()"/>
https://stackoverflow.com/a/14004230/4706826
Gives you info on when the events get executed.
Put a #PostConstruct annotated method in the backing bean. This annotation tells the bean to execute the annotated method every time its constructor is being called.
Example:
#ManagedBean
#ViewScoped
public class MyManagedBean{
#PostConstruct
public void initView() throws Exception{
...initialize page values, execute database queries, etc.
}

Oracle-ADF inlineFrame initializing view scoped bean twice

Oracle ADF 12.1.2, Java 1.7, Oracle WebLogic 12.1.2
I am facing strange issue related with af:inlineFrame component. Trying to display/render ADF page inside of af:popup within af:inlineFrame component. The weird thing is when the popup displayed; view scoped bean's #PostConstruct method called twice. That means bean is initialized twice. However it needed to be initialized once since bean is referenced from the page that is going to be displayed inside af:inlineFrame.
Correct flow gotta be:
Click to button openPopup() method called.
openPopup() sets URI then opens popup.
inlineFrame source property set as it's going to display framePage.jspx.
JSF scans framePage.jspx code finds out there is a reference to FrameBean inside af:outputLabel
Construct FrameBean then call #PostConstruct method.
Call appropriate getter and render page.
What happens in my case:
Click to button openPopup() method called.
openPopup() sets URI opens popup.
inlineFrame source property set as it's going to display framePage.jspx.
JSF scans framePage.jspx code finds out there is a reference to FrameBean inside af:outputLabel
Construct FrameBean then call #PostConstruct method.
Call appropriate getter and render page.
Construct FrameBean then call #PostConstruct method.
Call appropriate getter and render page.
Popup located like:
<af:popup id="mainPopup" binding="#{mainBean.mainPopup}">
<af:dialog id="mainDialog">
<af:inlineFrame source="#{mainBean.URI}">
</af:inlineFrame>
</af:dialog>
</af:popup>
Showing popup via af:button action="#{mainBean.openPopup}":
public void openPopup() {
this.setURI("http://localhost:7001/app/framePage.jspx");
RichPopup.PopupHints hints = new RichPopup.PopupHints();
this.getMainPopup().show(hints);
}
framePage.jspx:
<?xml version='1.0' encoding='UTF-8'?>
<jsp:root xmlns:jsp="http://java.sun.com/JSP/Page" version="2.1"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:af="http://xmlns.oracle.com/adf/faces/rich">
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<jsp:directive.page contentType="text/html;charset=UTF-8" />
<f:view>
<af:document title="Frame Demo" id="demoDocument">
<af:form id="demoForm">
<af:outputLabel value="HELLO Common user!"></af:outputLabel>
<af:outputLabel value="#{frameBean.commonId}"> </af:outputLabel>
</af:form>
</af:document>
</f:view>
</jsp:root>
FrameBean:
#ManagedBean
#ViewScoped
public class FrameBean {
private String commonId;
#PostConstruct
public void afterInit() {
}
public String getCommonId() {
return commonId;
}
public void setCommonId(String commonId) {
this.commonId = commonId;
}
}
Making FrameBean #SessionScoped solves this issue since bean is kept with session but I don't want to keep it within session. Also setting source property of af:inlineFrame in jspx as hardcoded not fixing the problem.
I don't know how 'in-depth' was CDI tested with ADF, but surely is not the most common way of using ADF - at least all ADF documentation goes old-fashion way. I tried myself to enable CDI in one of my projects, but I got errors by following this blog:
http://www.jobinesh.com/2014/08/enabling-cdi-in-adf-applications.html
Furthermore,
you are using inline frames, which is another uncharted territory. From a design best-practice perspective, you should 'think in page flows', so instead of using an inline frame, you may use a task-flow-opening-as-dialog, task flow containing framePage.jspx. More about it, here: https://blogs.oracle.com/DavidGiammona/entry/task_flow_call_activity_run_as.
I know this doesn't answer directly to your question, please take it as a general note.

JSF View Scoped Bean Reconstructed Multiple Times [duplicate]

This question already has an answer here:
#ViewScoped calls #PostConstruct on every postback request
(1 answer)
Closed 6 years ago.
I thought #ViewScoped was supposed to prevent the bean from being reconstructed while the user is on the same page... So why is my #ViewScoped JSf controller bean being created multiple times even before the action handler causes the browser to navigate away from that view?
Can anyone point me in the right direction here?
Here is my code:
The View (domain/edit.xhtml)
<h:form prependId="false">
<h:inputText id="descriptionField" value="#{domainEdit.domain.description}" />
<h:commandButton id="saveButton" value="save" action="#{domainEdit.save}" />
</h:form>
The ViewScoped controller (DomainEdit.java)
#Named("domainEdit")
#ViewScoped
public class DomainEdit implements Serializable {
private static final long serialVersionUID = 1L;
protected DomainEdit() {
}
#PostConstruct
protected void init() {
System.out.println("post construct called.");
}
#PreDestroy
public void destroy() {
System.out.println("pre destroy called.");
}
public DomainEntity getDomain() {
System.out.println("displaying domain...");
// some code to return the domain
return domain;
}
public String save() {
System.out.println("saving...");
// some saving code
return "view";
}
}
Output
I get the following output when I deploy this and perform the following:
Navigate to the edit view (edit.xhtml)
post construct called.
displaying domain...
pre destroy called.
Change the content of the domainDescriptionField input text
nothing logged
Click 'save'
post construct called.
displaying domain...
pre destroy called.
post construct called.
displaying domain...
pre destroy called.
post construct called.
displaying domain...
pre destroy called.
post construct called.
displaying domain...
pre destroy called.
post construct called.
displaying domain...
saving domain...
pre destroy called.
Unless you're using JSF 2.2 (which is still not out yet at this moment) or MyFaces CODI (which I'd have expected that you would explicitly mention that), the #ViewScoped doesn't work in CDI. This also pretty much matches your problem symptoms.
Manage the bean by JSF instead of CDI. Replace #Named("domainEdit") by #ManagedBean from javax.faces.bean package. Or, install MyFaces CODI to bridge JSF #ViewScoped to CDI.

further continuing of double press

In a previous question BalusC gave me good advice on how a button, in place of a commandButton is useful for non ajax navigation. In particular it updates the destination address in the http: position which is useful for the user to bookmark a page.
I tried to use this information to my advantage until I came upon a problem. In a button I tried to use outcome="#{backing.something}" to find out that it gives me a null result. This looks like a timing problem in that action="#{}" is evaluated only when the button is pressed whereas outcome apparently wants a fixed string which gets checked when the page is loaded.
So I went back to commandButton with ajax="false". This has a problem that my navigation address is the page I came from, not the one I am navigating to. This is the wrong bookmark for the user.
I appreciate all the help I have received in stackoverflow on my learning exercise.
Ilan
The <h/p:button outcome> is not intented to invoke a bean action method, but to contain the outcome string directly. Any EL in there is evaluated immediately as a value expression. So the method behind it would immediately be invoked when you just open the page containing the <h/p:button>.
There are in your particular case basically two ways to invoke a bean action method on navigation. If you need to invoke it before the navigation takes place and the action isn't intented to be re-invoked everytime when the enduser reopens/reloads the GET request, then make it a POST-Redirect-GET request. It's a matter of adding faces-redirect=true to the outcome value in query string syntax.
E.g.
<p:commandButton action="#{bean.submit}" ... />
with
public String submit() {
// ...
return "nextpage?faces-redirect=true";
}
This way the browser will be redirected to the target page after POST, hence the enduser will see the target URL being reflected in the address bar.
Or if you need to invoke the action everytime when the enduser reopens/reloads the GET request, do the job in the (post)constructor or preRenderView listener method of the request/view scoped backing bean instead.
E.g.
<p:button outcome="nextpage" ... />
with
#ManagedBean
#RequestScoped
public class NextpageBacking {
public NextpageBacking() {
// In constructor.
}
#PostConstruct
public void onPostConstruct() {
// Or in postconstructor (will be invoked after construction AND injection).
}
public void onPreRenderView() {
// Or before rendering the view (will be invoked after all view params are set).
}
// ...
}
The pre render view listener method needs to be definied as follows in the nextpage
<f:event type="preRenderView" listener="#{nextpageBacking.onPreRenderView}" />
See also:
What can <f:metadata>, <f:viewParam> and <f:viewAction> be used for?
Communication in JSF 2.0 - Processing GET request parameters

Using Session Bean provided data on JSF welcome page

I use JSF managed beans calling EJB methods that are provide data from database. I want to use some data already on the welcome page of the application. What is the best solution for it?
EJBs are injected into JSF managed beans and it looks like the injection is done after executing the constructor. So I am not able to call EJB methods in the constructor.
The normal place for EJB call is in the JSF action methods but how to call such a method prior to loding the first page of the application?
A possible solution would be to call the EJB method conditionally in a getter that is used on the welcome page, for example:
public List getProductList(){
if (this.productList == null)
this.productList = myEJB.getProductList();
return this.productList;
}
Is there any better solution? For example, in some config file?
You can do it in a method which is annotated with #PostConstruct. This will be executed once after the bean is constructed and all JSF managed property and resource injection is done.
#PostConstruct
public void init() {
this.productList = myEJB.getProductList();
}
if you want to make a call from xhtml view
<f:view>
<f:metadata>
<f:viewAction action="${myController.init()}" onPostback="true"/>
</f:metadata>
</f:view>
and your controller
public class MyController{
public void init(){
this.productList = myEJB.getProductList();
...

Resources