JSF2.0: ManagedProperty Lifecycle? - jsf

I have a problem I don't understand: Behind any View I have a controller ManagedBean that is RequestScoped and a data ManagedBean, that holds the data for the view and is SessionScoped.
So there are two views, which are login with loginData and loginController and overview with overviewData and overviewController.
The functionality should be like that:
The User logs into the application (loginController method)
If Authentication is successfull, there is a redirect to overview.xhtml (again in loginController method)
Then the overviewData gets its data by the overviewController, which retrieves them from business logic layer
The overview.xhtml shows the retireved data
So, the point is that I want to fill overviewData out of loginController, right after login! (???or if possible right befor overview view is constructed, if possible???).
I tried it with managedProperties, but the one I initiate in loginController is a different object than the managedProperty in overviewController, although they have the same name! How is that possible.
Oh boy, I doubt you guys understand what I mean, so I need to post some code:
LoginController.java
...
#ManagedBean
#RequestScoped
public class LoginController {
#ManagedProperty(value = "#{overviewData}")
private OverviewData overviewData;
OverviewController overviewController;
public LoginController(){
overviewController = new OverviewController ();
}
String login() throws Exception {
UsernamePasswordToken token = new UsernamePasswordToken(loginData.getName(), loginData.getPw().trim());
try {
currentUser.login(token);
overviewController.fillProjects();
...
OverviewController.java
...
#ManagedBean
#RequestScoped
public class OverviewController {
#ManagedProperty(value = "#{overviewData}")
private OverviewData overviewData;
public void fillProjects(){
if(overviewData == null){
overviewData = new OverviewData();
}
overviewData.setProjects(projectService.getProjects()); //retrieves data from business logic
}
...
OverviewData.java
...
#ManagedBean(name = "overviewData")
#SessionScoped
public class OverviewData {
private List<ProjectDTO> projects; //that's what the view needs to display the overview
public void setProjects(List<ProjectDTO> projects) {
this.projects = projects;
}
...
I hope that helps to show my problem, if you don't understand it, pls ask in a comment..
Would be nice if you can help me :-)
Cheers...

You're creating beans yourself using new instead of letting JSF do the job.
overviewController = new OverviewController ();
and
overviewData = new OverviewData();
This is wrong. JSF won't utilize any beans which you've created yourself this way. Remove those lines and add another #ManagedProperty on overviewController inside LoginController (and make the property private).
#ManagedProperty(value="#{overviewController}")
private OverviewController overviewController;
JSF will create the bean itself and set it as managed property directly after parent bean's construction. You just have to access it the usual Java way (without the need for nullchecks).

Related

How can I get the UploadedFile (Primefaces showcase fileUpload auto) to another bean?

I store a file into this bean via primefaces fileUpload
#ManagedBean(name = "uploadController")
class UploadController{
private UploadedFile uploadedFile;
public void handleFileUpload(FileUploadEvent event) {
setUploadedFile(event.getFile());
}
//getter&setter
Is it possible to access to the field uploadedFile into another bean such as
#SessionScoped
public class BrandController implements Serializable {
private UploadedFile logo;
//getter&setter
and link them in a certain way?
I tried this :
#ManagedProperty(value = "#{uploadController.uploadedFile}")
private UploadedFile logo;
But debugging shows that logo is null when I attempt to use methods on it. Thanks for your help.
I think I was using wrong the ManagedProperty.
#ManagedBean(name = "uploadController")
public class UploadController {
#ManagedProperty(value = "#{brandController}")
private BrandController brandController;
public void handleFileUpload(FileUploadEvent event) {
brandController.setLogo(event.getFile());
}
You should read this awesome answer about jsf scopes.
How to choose the right bean scope?
Session and Application scopes allows you to use beans beyond the interaction of the jsf page associated with it, however this could cause you encountered this bean in other places in "unespected" state.
For what has been said previously, for this purposes i used the flash scope, witch is a short living scope, how does it explain in above StackOverflow link as well as in the following:
Pass an object between #ViewScoped beans without using GET params

Safest way to access a session scope bean in another bean

I am new to jsf and using JSF 2.0 to keep user information in a session scoped bean. I need to access this information across other beans for grunt work. Presently, this is how i am doing:-
private UserBean myuser1 = (UserBean)FacesUtils.getManagedBean("UserBean");
and then access properties as
if (myuser1.getUserType == 1) ...
this works but some time throws Argument Error: parameter key is null exception. I have been using following method too:-
private UserBean myuser2 = new UserBean();
if (myuser2.getUserType == 1) ...
In second method, my understanding is that if UserBean is already created in session, it would be retried. There are lots of question about 'how to access one bean in another' so i am confused. Please tell me one clean method which should always work and not throw null pointer exception abruptly.
The simplest way I know of is using #ManagedProperty, I don't know what you mean by safest though.
Let's say this is your sessionScoped bean :
#ManagedBean
#SessionScopped
public class UserBean {
//bean attributes and methods
}
Then you can access it in any other bean (provided it has the same or a narrower scope) as an attribute like this :
#ManagedBean
#ViewScoped //in this cas you can use SessionScoped, FlowScoped, or RequestScoped too
public class AnotherBean {
#ManagedProperty("#{userBean}")
UserBean userB;
//rest of the bean
//be sure to add getters and setters for the injected bean
}
For more details check this
Hope this helps.
Actually,
parameter key is null exception: it's either you didn't initialize the object witch can be solver with either adding
object = new Object(); // in the constructor of the class.
The second problem may be that the object is " DETACHED " you need to call the object using the method merge (with the entity manager).
A detached object is a known value but the JPA system doesn't know if it is the latest version from the DB or even sometimes the id value is not set for some reason (Not managed with jpa in other words it can be your case).
If em is your entity manager and you have the following function:
public Object latestVersion(Object o){ em.merge; }
In your Bean with:
#EJB
Service service;
if you do em.latestVersion(o); the problem of detached object is solved.
And for the real answer:
To access a object from another view you can simply do the following.
#ManagedBean
#SessionScoped
..... Bean1 {
public static Object o;
.....
}
#ManagedBean
..... Bean 2 {
private Object b=Bean1.o;
.....
}
Good luck
The standard practice of setting dependency of a scoped bean in another scoped bean is to use #Inject annotation like
#Inject UserBean userBean; in the bean you want use the UserBean object.
Your UserBean should be a stateful one.
#Stateful
#LocalBean
public class UserBean
{
private String name;
public String getName() { return name; }
public void setName( String name_ ) { name = name_; }
}
And just inject it into a stateless bean to modify its state:
#Stateless
#LocalBean
public class MyStatelessBean
{
#EJB
private UserBean userBean;
public String getUserName() { userBean.getName(); };
public void setUserName( String name_ ) { userBean.setName( name_); }
}
Or you can access it from (not wider scoped) managed beans as well in the same way:
#ManagedBean
#Dependent
public class MyJSFManagedBean
{
#EJB
private UserBean userBean;
}
You wrote in your comment you does not use EJBs at all. The picture modify like this:
The UserBean should be a SessionScoped CDI bean
#Named
#SessionScoped
pubilc class UserBean
{}
The othe CDI bean should be in a nearer scope:
#Named
#Request // or #ViewScoped or #Dependent
public class OwnerBean
{
#Inject
UserBean userBean;
}
The container automatically takes care to create the beans in the right scope and insert them into the owers (any kind of container managed objects : servlets, filters, action listeners, JSF/CDI beans). You need to insert a wider scoped resource into a thinner scoped one.

ApplicationScoped Bean eager=true destroys when switching to different view

JSF2, Primefaces 3.3.FINAL, Spring
I have ApplicationScoped Bean for loading all dropdown data during start up of server. I referenced the same bean via ManagedProperty annotation. But when switching views the bean gets destroyed and i need to recreate the bean and data again.
Sample Code:
#ManagedBean(name = "refDataBean", eager = true)
#ApplicationScoped
public class RefDataBean extends AbsBackingBean implements Serializable{
....
#PostConstruct
public void init(){
//load multiple drop down data - populateData-db call
}
}
#ManagedBean(name = "searchViewBean")
#ViewScoped
public class SearchViewBean{
#ManagedProperty(value = "#{refDataBean}")
private RefDataBean refDataBean;
#PostConstruct
public void init() { //getUser object }
public User retrieveUser(User user) {
List<User> userList = refDataBean.getUserList();
}
public PICTSRefDataBean getPictsRefDataBean() {
return pictsRefDataBean;
}
public void setPictsRefDataBean(final PICTSRefDataBean pictsRefDataBean)
{ this.pictsRefDataBean = pictsRefDataBean; }
}
In one page/view, user.xhtml
<h:selectOneMenu value="#{searchViewBean.selectedUser}" >
<f:selectItems value="#{refDataBean.userList}" var="taskUser"
itemLabel="#{taskUser.fullNameAndId}"
itemValue="#{taskUser.networkLogin}"></f:selectItems>
</h:selectOneMenu>
When switching to another page, team.xhtml, the refDataBean is null and it goes to PostConstruct method of RefDataBean constructing the whole drop down list again. Since its a static data, i expect load it once and should be able to access in any page. I know that Objects in View scope are destroyed when you switch to a different view. How to restrict that? Or what am i missing? Please help

How to pass bean property from one view to another view

I'm using JSF 2.1 and Primefaces:
I have a view scoped managed bean with a managed property and a method that set something on other view scoped managed bean and forward to other page referencing that managed bean:
#ManagedBean
#ViewScoped
public class HelloMB {
#ManagedProperty("otherMB")
private OtherMB other;
public String changeOtherMB() {
otherMB.setAnyObject(new Object());
return "otherPage.xhtml";
}
}
#ManagedBean
#ViewScoped
public class OtherMB {
private Object o;
public void setAnyObject(Object o) {
this.o = o;
}
}
So, when otherPage is rendered o is null.
You have idea how could I solve this? How can I retain an Object in a #ViewScoped managed bean and keep it live on other page without using #SessionScoped?
The view scope is destroyed and recreated once you navigate to a different JSF view. You know, the view scope lives as long as you're interacting with the same JSF view. In this particular case you effectively end up with two instances of the #{otherMB} managed bean during one request. One instance which is used by the source view and another instance which is used by the destination view.
As the second view is created within the very same request, you could just pass it as a request attribute.
#ManagedBean
#ViewScoped
public class HelloMB implements Serializable {
public String changeOtherMB() {
ExternalContext ec = FacesContext.getCurrentInstance().getExternalContext();
ec.getRequestMap().put("anyObject", anyObject);
return "otherPage.xhtml";
}
}
#ManagedBean
#ViewScoped
public class OtherMB {
private Object anyObject;
#PostConstruct
public void init() {
ExternalContext ec = FacesContext.getCurrentInstance().getExternalContext();
this.anyObject = ec.getRequestMap().get("anyObject");
}
}
I however wonder if you're aware about the importance of idempotent vs non-idempotent requests. Perhaps you actually need a "plain vanilla" link without the need to invoke a view scoped bean action method. See the last "See also" link below for an extensive example on that.
See also:
How to choose the right bean scope?
How to navigate in JSF? How to make URL reflect current page (and not previous one)
Creating master-detail pages for entities, how to link them and which bean scope to choose

#ManagedProperty does not reflect changes and keeps returning null

I'm trying to inject the value of one sessionscoped bean into a viewscoped bean but it keeps returning null, here's a snippet:
import javax.faces.application.FacesMessage;
import javax.faces.bean.SessionScoped;
import javax.faces.bean.ManagedBean;
import javax.faces.context.FacesContext;
//Class for managing the current logged-in user
#ManagedBean(name="user")
#SessionScoped
public class User implements Serializable{
private String userName;
public void setUserName(String userName) {
this.userName = userName;
}
public String getUserName() {
return this.userName;
}
And it's used in:
#ManagedBean(name="databrowser")
#ViewScoped
public class dataBrowser implements Serializable {
private List<UploadData> dataItems;
private SelectItem[] dataTypeOptions, qualityOptions, accessOptions;
private UploadData selectedData;
private String filelocation;
#ManagedProperty(value="#{user.userName}")
private String userName;
public String getUserName() {
return this.userName;
}
dataBrowser is used to populate a Primefaces datatable, when it's called userName is null and I'm not sure why.
Recently I have problem with injecting nested managed bean properties by #ManagedProperties too. Once injected it never changed. I did a workaround by evaluating EL in getter instead of injecting it.
Try that:
public String getUserName() {
FacesContext context = FacesContext.getCurrentInstance();
return (String) context.getApplication().evaluateExpressionGet(context,"#{user.userName}", String.class);
}
You can also try injecting entire user bean and get userName field from it in getter.
With all setters/getters in place, I was having the same problem (null reference to user) because of missing empty constructor in User class.
In the example you provided, the dataBrowser and user beans are instantiated before constructing the table, so referencing #{dataBrowser.userName} should already find the userName #ManagedProperty correctly injected (not being a #PostConstruct problem).
I just came across the same problem, and found out by chance, that it is not working, if I try with firefox (actually icedove under linux), but well working, if I try with the eclipse build-in browser.
Even so this does not make sense to me, have you tried with different browsers already?
michal777's answer is very well working. I have extended it to this:
#ManagedProperty("#{nameBean}")
private NameBean nameBean;
public NameBean getNameBean() { return nameBean; }
public void setNameBean(NameBean nameBean) { this.nameBean = nameBean; }
public NameBean getNameBean_Workaround() {
FacesContext context = FacesContext.getCurrentInstance();
return (NameBean) context.getApplication().evaluateExpressionGet(context,"#{nameBean}", NameBean.class);
}
and later on:
if (nameBean != null) {
nameBean.setName("achsooo");
}
else {
getNameBean_Workaround().setName("achsooo2222");
}
Now, in the eclipse browser "achsooo" gets set, and in icedove "achsooo2222" gets set.
#{user.userName} is interpreted by JSF as getUser().getUserName()
So it is better to have a #ManagedProperty of type User, with its getter/setter methods getUser/setUser. With that you can access the user's name by #{user.userName}.
I had this problem, and the problem was actually twofold. (Note also that #ManagedProperty will only ever work in a #ManagedBean class and if that #ManagedProperty class is of the same or lesser scope (application, session, view, request, etc.).) Here is how I fixed it:
Problem 1: JSF is stupid and doesn't handle #ManagedProperty injection properly in abstract classes.
Solution:
Make every class that uses #ManagedProperty be annotated with #ManagedBean.
Make every abstract class that uses the property not be annotated with #ManagedProperty and instead only provide abstract getter and setter methods that non-abstract classes will each override.
Use the abstract class's getter method instead of the #ManagedProperty itself in abstract classes.
Problem 2: JSF is stupid and doesn't handle #ManagedProperty injection properly in #ManagedBean classes not created by JSF (i.e. you are creating these classes yourself using new).
Solution options:
Let JSF create the class that uses the #ManagedProperty.
Use the following code:
MyClass example = Utils.getELValue("EL Expression Goes Here", MyClass.class);
public static <T> T getELValue(final String elName, final Class<T> clazz) {
FacesContext fc = FacesContext.getCurrentInstance();
return (T) fc.getApplication().getELResolver().getValue(fc.getELContext(), null, elName);
// Potential (untested) alternative:
// ((HttpServletRequest)FacesContext.getCurrentInstance().getExternalContext().getRequest()).getSession().getAttribute("")
}

Resources