Prevent creation of SessionScoped beans if not used - jsf-2

I have the following snippet on the home page of my app:
<h:panelGroup rendered="#{loginBean.loggedIn}">
<p>Welcome, #{loginBean.currentUser.firstName}</p>
</h:panelGroup>
LoginBean is #SessionScoped. Since it is being referred to on the home page, an instance of the same will be created when the page loads.
Now, assume that the user never logs in. In that case, my LoginBean is of no use since it won't be holding any information about the user. Wouldn't this be redundant?
Am not saying that this causes problems, but am just wondering about the unnecessary instantiation taking up memory space.

Make the #{loginBean} request/view scoped and manually put the user in the session scope on successful login. The session scope is available as a map by ExternalContext#getSessionMap().
#ManagedBean
#ViewScoped
public class LoginBean {
public void login() {
// ...
if (user != null) {
externalContext.getSessionMap().put("user", user);
}
// ...
}
}
This way you can get away with
<h:panelGroup rendered="#{not empty user}">
<p>Welcome, #{user.firstName}</p>
</h:panelGroup>
See also:
Performing user authentication in Java EE / JSF using j_security_check - the 2nd example (at the bottom)

Related

Same #PostConstruct calling twice, data not surviving

Hi I'm learning JSF/Primefaces. whenever user click on a link it should forward to the edit page with the information, but it's empty.
I tried to debug, following flow happens:
user click the link of the element that wants to edit
QuoteStatusList.init() is called
QuoteStatusForm.init() is called
QuoteStatusForm.edit is called
QuotestatusForm.quoteStatus bean is filled with the information
return "edit"
foward to quoteStatusForm.xhtml
and QuoteStatusForm.init() is called again, all datas filled are lost
I found this but I'm now only using jsf annotation to manage view beans
QuoteStatusList.java
#ManagedBean
#RequestScope
public class QuoteStatusList extends BasePage implements Serializable {
#PostConstruct
public void init(){
log.debug("initing...");
}
...
}
QuoteStatusForm.java
#ManagedBean
#ViewScope
public class QuoteStatusForm extends BasePage implements Serializable {
#PostConstruct
public void init(){
log.debug("initing...");
}
public String edit() {
log.debug("editing..");
if (idQuoteStatus != null && idQuoteStatus != 0) {
quoteStatus = quoteStatusManager.get(idQuoteStatus);
} else {
quoteStatus = new QuoteStatus();
}
return "edit";
}
}
BasePage.java
#ManagedBean
#RequestScoped
public class BasePage {
//nothing is injected
//no other #postConstruct function
}
QuoteStatusList.xhtml
<h:commandLink action="#{quoteStatusForm.edit}" value="#{quoteStatus.idQuoteStatus}">
<f:param name="idQuoteStatus" value="#{quoteStatus.idQuoteStatus}"/>
</h:commandLink>
faces-config.xml
<navigation-rule>
<from-view-id>/quoteStatusList.xhtml</from-view-id>
<navigation-case>
<from-outcome>edit</from-outcome>
<to-view-id>/quoteStatusForm.xhtml</to-view-id>
</navigation-case>
</navigation-rule>
<navigation-rule>
<from-view-id>/quoteStatusForm.xhtml</from-view-id>
<navigation-case>
<from-outcome>edit</from-outcome>
<to-view-id>/quoteStatusForm.xhtml</to-view-id>
</navigation-case>
</navigation-rule>
What you're experiencing is appropriate behaviour for #RequestScoped and #ViewScoped beans.
#RequestScoped - Beans of this scope will not survive a redirect/forward to another page. That means that if you're on a page backed by a bean of this scope, whenever you issue a new HTTP request (either ajax, a full-on page refresh, or a redirect), the instance of that bean you're working on is destroyed, voided, ceases to exist. Along with all its member variables
#ViewScoped - Beans of this scope will also not survive a full redirect/forward. They will however survive page refreshes and ajax. What this means is that, as long as you stay on the same page (backed by a #ViewScoped bean), don't return any navigation case, you're guaranteed to be working with the same instance of the bean.
How are these beans supposed to communicate then? If leaving one page means that you lose everything the backing bean contains, what your options (you should be asking)? Well, there are a number of ways that JSF beans can communicate. Read through the gospel on inter-bean communication
So what's happening in your case
QuoteStatusList list is destroyed when you navigate away from the page that it backs. This also means that when you come back, you're dealing with a brand new instance of that bean (and that's why init is called twice)
QuoteStatusForm was destroyed because you returned edit from that bean, causing the instance you're working with to be destroyed and recreated on page load
What to do:
To avoid destroying QuoteStatusForm, you can just return null from edit

RichFaces extendedDataTable: prevent model reloading during 'onselectionchange' event

I have an "extendedDataTable" like this:
<h:form>
<rich:extendedDataTable id="someTbl"
value="#{someBean.allData}"
var="dataItem"
selection="#{dataSelectionListener.selection}"
selectionMode="single">
<a4j:ajax execute="#form"
render="#none"
event="selectionchange"
listener="#{dataSelectionListener.onSelectionChange}"/>
<rich:column>
<h:outputText value="#{dataItem.name}"/>
</rich:column>
</rich:extendedDataTable>
</h:form>
and managed bean:
#Named
#RequestScoped
public class SomeBean {
#Inject
private SomeService someService;
public List<DataItem> getAllData() {
// Getting data from DB
return someService.getAllData();
}
}
dataSelectionListener is also managed bean with scope "session".
Every time I select some row in my table method getAllData is called twice (first before calling of the method onSelectionChange and thereafter). It causes two unwanted queries to DB.
What is the right way to resolve this issue?
Sorry if my question is somewhat silly and thanks in advance.
I think that your problem is related to #RequestScoped annotation. Request scope means that the object is defined as an attribute attached to the HttpRequest object and shares its lifetime. The HttpRequest object is created when the application container accepts an HTTP request from the client. It is effectively destroyed (goes out of scope) when the container has finished transmitting the response to that request. When a new HTTP request comes in, a new request-scope object is created. So anything that you want to exist between HTTP requests has to be placed in a longer-lived scope (such as session scope), or the data has to to be sent back as part of the response in such a way that it will be sent back as part of the next request.
After short research (thanks to the old article in BalusC's blog) I found out that double call of the method that provides data for the table is okay in JSF world (first time getter is called during phase APPLY_REQUEST_VALUES and second - during phase RENDER_RESPONSE). So I really shouldn't use bean with scope "request". As I understood the best scope for this case is "view" in conjunction with a lazy data loading in the getter:
#ManagedBean
#ViewScoped
public class SomeBean {
#Inject
private SomeService someService;
private List<DataItem> allData;
public List<DataItem> getAllData() {
if (allData == null)
// Getting data from DB
allData = someService.getAllData();
return allData;
}
}

<h:link> disabled attribute not evaluating properly

I have an #Stateful EJB in a #SessionScoped bean.
My EJB:
#Stateful
public class SomeEjb implements someEjbInterface{
private SomeEntity entity;
#Override
public Boolean getEntityAssigned(){
return entity!= null;
}
#Override
public void selectEntity(String id){
//assign entity with some values retrieved from db according to the criteria
}
}
My Session Scoped Bean:
#ManagedBean
#SessionScoped
public class SessionBean{
#EJB
private SomeEntity entity;
//getter and setter
public String selectEntity(){
entity.selectEntity(someId);
return null;
//Edited: if using this, no problem will occur.
// return "index";
}
}
My Page index.xhtml (xmlns omitted):
<h:form>
<h:commandButton value="Select entity" action="#{sessionBean.selectEntity()}">
</h:form>
<h:link outcome="someOutcome" disabled="#{sessionBean.entity.entityAssigned}">
I expect that the link is initially disabled, when I click "Select entity", the ejb will retrieve an entity from database, if the retrieve succeed, then the link will be enabled.
The problem is that when I click the button, the link will break (rendering an tag with href attribute but no innerHtml to click). It can only be fixed if I reload the page without data resubmission(re-enter the page by pressing enter at the url, not using F5 which will resubmit the form).
The error message is:
HTML nesting warning on closing span: element a rendered by component : {Component-Path : some long component path refer to the link element} not explicitly closed
Does anyone know what did I messed up with the rendering?
Edit:
I just found out that the problem does not exist if I return the outcome of that same page instead of null, which probably discard #ViewScoped bean I used to call sessionBean.selectEntity(). Can anyone explain the mechanism for the cause of this difference?
The documentation says that the "toString" of the object returned by the method will be used to handel navigation, you can try returning "" (no navigation, only refresh the page).
Please add the relevant parts of the xhtml.

jsf2.0 - How to get the values in other jsf page's bean in request scope

I have two pages myaccount.xhtml and selectbank.xhtml
In my account page there is one option for recharge account in which user will enter the amount when user will press submit button then it will goto the select bank page using following bean method.
public String gotoPayMethod() {
FacesMessage doneMessage=new FacesMessage("Redirecting to Payment Type Page");
FacesContext.getCurrentInstance().addMessage(null, doneMessage);
return "SelectBank";
}
When user will goto to selectbank there user will have to submit payment method but in this page it shows the amount as null which was entered in the previous page.
Both the pages are using the same bean and the scope of the bean is request scope.
So how can I access that value without passing this values through URL GET method.
Just for my satisfaction I used session scope then it was working but I know thats not the proper way because I start using session scope for each pages then it will not be efficient.
Thanks
Well, if your beans are RequestScoped than you don't have same bean for both pages. These beans are recreated for every request, so you should pass parameters. Change return statement of your gotoPayMethod() to:
return "SelectBank?faces-redirect=true&includeViewParams=true";
and on selectbank.xhtml add:
<f:metadata>
<f:viewParam name="amount" value="#{bean.amount}" />
</f:metadata>
Adapt this to your property and bean name.
If using parameters is not a solution you can add this parameter in the session, and remove it from session in second bean when you retrieve it:
FacesContext.getCurrentInstance().getExternalContext().getSessionMap().put("amount", amount);
((HttpServletRequest)FacesContext.getCurrentInstance().getExternalContext().getRequest()).getSession().removeAttribute("amount");
Second construction for removing the attribute is necessary as Map returned from getSessionMap() is immutable.
You can use the #{flash} object that will keep your data until the next view. This way you won't need to deal with view parameters.
Details from myaccount.xhtml:
<h:form>
<h:outputText value="Enter amount: " />
<h:inputText value="#{flash.amount}" />
<br/>
<h:commandButton value="Go to payment method" action="#{bean.gotoPayMethod}" />
<h:form>
Bean of both views:
#ManagedBean
#RequestScoped
public class Bean {
#ManagedProperty("#{flash}")
private Flash flash;
private int amount = -1;
public Bean () { }
public String getAmount() {
if(amount == -1) {
int val = Integer.parseInt((String)flash.get("amount"));
flash.keep("amount");
amount = val;
}
return amount;
}
public Flash getFlash() {
return flash;
}
public void setFlash(Flash flash) {
this.flash = flash;
}
public String gotoPayMethod() {
//do business job
return "SelectBank?faces-redirect=true";
}
}
Details from selectbank.xhtml:
<h:outputText value="Amount entered by user is #{bean.amount}" />
Your use case is not of simple request/response cycle, the life span is more than one request response which makes it candidate for session scope.
Using hidden variable or GET parameters in URL is not good practice especially for a banking application. Where security is so important dont compromise on small memory foot print.
If flash scope map simplifies the case you can use it, but I would not go for such a thing.
Update: Forgot to mention you can check Conversation scope too.

JSF 2.0 Setting a variable in the session scoped before a view is shown?

I would like to apply a variable in the session scoped using before the view is displayed, than this view will use this variable.
Here is the link:
<h:link value="#{msg.persondeactivate}" outcome="persondeactivate" />
Here is the faces-config.xml
<navigation-rule>
<navigation-case>
<from-outcome>persondeactivate</from-outcome>
<to-view-id>/deactivatePerson.xhtml</to-view-id>
</navigation-case>
</navigation-rule>
Here is the view (deactivatePerson.xhtml):
...<h:outputText value="#{msg.personIsDeactivate}" rendered="#{controller.personDeactivated}" style="color:green" />... <h:commandButton action="#{controller.deaktivieren}" value="#{msg.deactivate}"></h:commandButton>...
Here is the managed bean:
#ManagedBean #SessionScoped public class Controller { ... private boolean personDeactivated = false; public String deaktivieren(){ personDeactivated = false;
// Deactivate process personDeactivated = true; return "persondeactivate";} ... }
I want that the variable personDeactivated is set to false before the view (deactivatePerson.xhtml) for the second time by is called.
It does not work.
Can someone please tell me what is wrong?
Thanks in advance.
You can use <f:event type="preRenderView"> to invoke a backing bean listener method before the view is rendered.
<f:event type="preRenderView" listener="#{controller.onPreRenderView}" />
with
public void onPreRenderView() {
if (!FacesContext.getCurrentInstance().isPostback()) {
// Do your job here when the view is been freshly requested by GET.
}
else {
// Do your job here when a POST request is been performed by command link/button.
}
}
Unrelated to the concrete problem, I have the impression that the Controller is actually in the wrong scope. A much better solution would be to make it #ViewScoped instead. This way the bean instance will be freshly created on every new GET request and live as long as you're POSTbacking to the very same view (and thus you won't encounter inconsitenties and unintuitive behaviour when having the same page open in multiple browser tabs/windows in the same session which would share the very same session scoped bean!). See also How to choose the right bean scope?

Resources