i have a strange behaviour in my app:
I use a SessionScope bean (Bean A) to hold users preferences. In my other Bean (Bean B), which is in RequestScope, I inject the SessionScope bean.
Bean B has a #PostConstruct method to retrieve a list of values from the database depending on the value in Bean A. The application gets confused when the user changes the value in Bean A and its value in Bean B is not correct at the time #PostConstruct method is invoked. I tested it with logs.
I think all setter methods will be updated before Invoke Application Phase?
Here is a code sample:
Bean A:
#Named
#SessionScoped
public class SessionBean implements Serializable {
private static final long serialVersionUID = -4214048619877179708L;
#Inject private Logger log;
private BankAccount selectedBankAccount;
public BankAccount getSelectedBankAccount() {
return selectedBankAccount;
}
public void setSelectedBankAccount(BankAccount selectedBankAccount) {
log.info("ba: " + selectedBankAccount);
this.selectedBankAccount = selectedBankAccount;
}
Bean B:
#RequestScoped
public class SubAccountListProducer {
#Inject private SessionBean sessionBean;
#Inject private EntityManager em;
#PostConstruct
public void retrieveAllSubAccount() {
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<SubAccount> criteria = cb.createQuery(SubAccount.class);
Root<SubAccount> account = criteria.from(SubAccount.class);
log.info("retrieveAllSubAccount: " + sessionBean.getSelectedBankAccount());
criteria.select(account).where(cb.equal(account.get("bankAccount"), sessionBean.getSelectedBankAccount()));
criteria.select(account).orderBy(cb.desc(account.get("name")));
entityList = em.createQuery(criteria).getResultList();
}
Sample logs:
ba: BankAccount [accountId=123456789, bankName=Ing DiBa, blz=50010517]
retrieveAllSubAccount: BankAccount [accountId=123456789, bankName=Ing DiBa, blz=50010517]
retrieveAllSubAccount: BankAccount [accountId=123456789, bankName=Ing DiBa, blz=50010517]
ba: BankAccount [accountId=987654321, bankName=Barclaycard Barclays Bank, blz=20130600]
As you can see... the first two logs are correct... if the user changes preferences (updates the SessionBean), the view will be rerendered with JSF and the last two logs are not in correct order and my app gets confused.
Thank you for help.
The #PostConstruct is not executed during invoke action phase. It is executed directly after bean's construction. The PostConstruct should only be used to preinitialize some stuff depending on injected dependencies directly after bean's construction. Because your bean is request scoped instead of conversation scoped (or view scoped), it will be constructed on every single request.
You need to do the updating/refreshing job in the real action method instead, which is the method you've specified in the <h:commandButton>/<h:commandLink>. E.g.
<h:commandButton value="Submit" action="#{bean.submit}" />
with
public void submit() {
// ...
retrieveAllSubAccount();
}
I also suggest to put your bean in the CDI conversation scope or JSF view scope, so that it don't unnecessarily get reconstructed on every postback to the same view.
Related
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
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;
}
}
Here is backing bean of my log in page. Why does it preserve values after calling the logIn() method? I mean after I submit the form and return to the login page my previously entered username is already there. I thought that by using #RequestScoped annotation the values are not preserved.
#Controller
#ManagedBean
#RequestScoped
public class LogInBean implements Serializable {
private static final long serialVersionUID = 2092611147930386873L;
#Autowired
private UserService userService;
private String username;
private String password;
private boolean rememberMe;
public String logIn() {
return "index?faces-redirect=true";
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public boolean isRememberMe() {
return rememberMe;
}
public void setRememberMe(boolean rememberMe) {
this.rememberMe = rememberMe;
}
}
It's because you're actually using an application scoped Spring managed bean instead of a request scoped JSF managed bean.
Your main mistake is that you're mixing JSF bean management annotations with a Spring bean management annotation and somehow are expecting that they seamlessly understand each other.
#Controller
#ManagedBean
#RequestScoped
This is thus not true. You effectively end up with two completely independent managed bean instances, one managed by JSF and another managed by Spring. The #RequestScoped annotation is specific to JSF bean management facility. The Spring's equivalent, #Scope("request") is absent and therefore the Spring managed bean defaults to the application scope. When referencing the bean in EL scope like so #{logInBean}, the Spring managed one gets precedence over JSF managed one because of the Spring bean EL resolver and you ultimately end up with an application scoped Spring managed bean. This totally explains the symptoms.
Fix it accordingly by getting rid of JSF bean management annotations and placing the desired Spring scope annotation:
#Controller
#Scope("request")
(an alternative would be to get rid of Spring bean management annotation and replace #Autowired by #EJB, like as one would normally do when using standard Java EE stack without any 3rd party library)
If you still face this problem, then another possible cause would be that you're submitting the login synchronously while not having autocomplete="off" on the input fields, head to this Q&A then: Disable web browser password save.
In my JSF2-Primefaces3 webapplications I am referencing beans named "HomeBean" from bean "CityBean" I am using ManagedBean annotations and DO NOT have faces-config.xml at all. Please find the code as below:
HomeBean
#ManagedBean(name="HmBen")
#SessionScoped
public class HomeBean implements Serializable {
private static final long serialVersionUID = 1L;
private static Logger logger = Logger.getLogger(HomeBean.class);
private List<Offer> offersList;
public HomeBean() {
}
}
CityBean
#ManagedBean(name="CtyBen")
#SessionScoped
public class CityBean implements Serializable {
private static final long serialVersionUID = 1L;
#ManagedProperty(value="#{HmBen}")
private HomeBean homeBean;
public CityBean() {
}
/**
* Following setters are required as per the documentation for Dependancy
* Injection of beans to work
*/
public void setHomeBean(HomeBean homeBean) {
this.homeBean = homeBean;
}
}
NOTE: this is just a section of the code due to character limits imposed by Stackoverflow.
I am getting following exception:
Caused by: com.sun.faces.mgbean.ManagedBeanCreationException: Unable to set property homeBean for managed bean CtyBen
at com.sun.faces.mgbean.ManagedBeanBuilder$BakedBeanProperty.set(ManagedBeanBuilder.java:615)
at com.sun.faces.mgbean.ManagedBeanBuilder.buildBean(ManagedBeanBuilder.java:133)
... 20 more
Caused by: java.lang.IllegalStateException: Cannot create a session after the response has been committed
at org.apache.catalina.connector.Request.doGetSession(Request.java:2377)
Does anyone has any clue about this?
Response already committed let me explain what does it mean. on page rendering it is rendering from top to bottom. Server giving response in bytes. after rendering some bytes server gets notification(some if condition on page) that's now you have to render bytes at that time server can not take bytes which already sent to user at that time Server throwing IllegalState exception. Give your .xhtml page. please check following question posted by me a year ago.
Jsf2.0 forwarding page error without parameter
Currently I am trying to inject a stateless EJB into a CDI managed controller on Jboss 6 AS Final. The controller is managed in the context an accessible from the JSF pages. If I inject the stateless bean with #EJB it is working. If I inject the stateless EJB with #Inject I get the following Exception:
My controller:
#Named("TestController")
public class TestController {
#Inject
private TestManagerLocal myTestManager;
...
}
}
My stateless bean:
#SuppressWarnings("unchecked")
#Stateless
public class TestManagerBean implements TestManagerLocal {
#PersistenceContext
private EntityManager em;
...
}
The Interface of the Bean is annotated with #Local.
If I try to call myTestManager I get the following exception:
WELD-000079 Could not find the EJB in JNDI: class
de.crud.org$jboss$weld$bean-jboss$classloader:id="vfs:$$$usr$local$jboss$server$default$deploy$test$ear"-SessionBean-TestManagerBean_$$_WeldProxy
THX a lot.
For those not having the luxury to change an ear to a war, I've found the following workaround:
Create an EJB in the war
Inject that EJB with the EJBs from the EJB module
Add CDI producer methods
Qualify #Inject with the qualifier for those producer methods:
Code:
// This bean is defined in the WEB module
#Stateless
public class EJBFactory {
#EJB
protected UserDAO userDAO;
// ~X other EJBs injected here
#Produces #EJBBean
public UserDAO getUserDAO() {
return userDAO;
}
// ~X other producer methods here
}
Now EJBs from anywhere in the EAR can be injected with:
// This bean is also defined in the web module
#RequestScoped
public class MyBean {
#Inject #EJBBean
private UserDAO userDAO; // injection works
public void test() {
userDao.getByID(...); // works
}
}
EJBBean is a simple standard qualifier annotation. For completeness, here it is:
#Qualifier
#Retention(RUNTIME)
#Target({TYPE, METHOD, FIELD, PARAMETER})
public #interface EJBBean {
}
The problem was, that I built and deployed my application as an ear. Weld is working when I deploy my application as an war including all EJBs.
Currently there are various problems arising from the fact that WARs in EAR-Deployments don't share the same classloader. See https://issues.jboss.org/browse/JBAS-8683 for the ongoing discussion in the JBoss-AS JIRA (and vote it up :-) )
UPDATE I found this information on how to disable separate classloaders, option 1 worked for me, but be extremely careful with this. The separation of classloaders hasn't been introduced for no reason, so apparently there are new problems on the road ahead...