Proper place to put initialization code in JSF? - jsf

Where is the proper 'place' in JSF to put initialisation snippet that follows, in order to get it executed just one time when the server starts?
1. ExternalContext extContext = FacesContext.getCurrentInstance().getExternalContext();
2. HttpSession sesion = (HttpSession)extContext.getSession(true);
3. String parA = extContext.getInitParameter("parA");
4. String parB = someCalculations(parA);
5. sesion.setAttribute("parB", parB);
Basically I want to read a parameter parA from web.xml context-param section, do some transformations, and include it in session (as new parB parameter).
PostConstructApplicationEvent and eager=true techniques doesn't works because session is null at this point (line 4).
ServletContextListener technique doesn't works because FacesContext isn't available.
Thanks!

There are no sessions at application start time; this requirement is impossible to meet.
I interpret your requirements as:
perform an expensive application-scope calculation
inject this application-scope result into other scopes
The JSF way to do this is via managed beans. Here is an application-scope bean to perform the one-time transformation of the context parameter:
package foo;
import javax.faces.bean.ApplicationScoped;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ManagedProperty;
#ManagedBean
#ApplicationScoped
public class SomeCalculationsBean {
#ManagedProperty("#{initParam.paraA}")
private String paraA;
private String someCalculation;
public String getParaA() {
return paraA;
}
public void setParaA(String paraA) {
this.paraA = paraA;
this.someCalculation = //do some transformation
}
public String getSomeCalculation() {
return someCalculation;
}
}
This value can then be injected into other scopes as you need it:
package foo;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ManagedProperty;
import javax.faces.bean.SessionScoped;
#ManagedBean
#SessionScoped
public class SomeSessionBean {
#ManagedProperty("#{someCalculationsBean.someCalculation}")
private String paraB;
public String getParaB() {
return paraB;
}
public void setParaB(String paraB) {
this.paraB = paraB;
}
}
Code untested. This implementation assumes JSF annotation support but you can do the same thing with faces-config.xml bean configuration.

Related

Pass a param from ManagedBean to jsf page

I am working with JSF 2.2 and Tomcat 8 and I am just starting to play with them.
I have a command button in a jsf page.
<h:commandButton id="newObject" value="New Object" action="#{someObject.someAction}">
<f:param name="object_id" value="#{someObject.object_id}" />
</h:commandButton>
The ManagedBean is similar to this:
import java.io.Serializable;
import javax.faces.bean.ManagedBean;
#ManagedBean
public class SomeObject implements Serializable{
private static final long serialVersionUID = 1L;
private int object_id;
public int getObject_id() {
return object_id;
}
public void setObject_id(int object_id) {
this.object_id = object_id;
}
public String someAction() {
setObject_id(sqlInsert());
if(getObject_id() != 0) {
System.out.println(getObject_id());
return "new_page";
}
}
}
The sqlInsert method is working fine. I use it to insert a new row in some sql table and get the auto generated key, which is an int. If the insert did not happen it would return 0.
I can navigate to the new_page, but the param object_id is 0. I added println to show the object_id and it is the actual key.
What am I doing wrong?
Since you are using the only #ManagedBean annotation on your Managed Bean and not specifying any Scope of you bean explicitly, your Bean will act as if its a #RequestScoped bean.[See link]
So every time you click your New Object button, the Bean is re initialized and you will loose the state(variable values).
Think and decide which scope you want to use [See link]. For your requirements #ViewScoped might do the job for you.

JSF injected sessionscoped bean's method invocation become null pointer

I use CDI to annotate beans. One bean called SessionManager holds the logined user information with the declaration:
import javax.enterprise.context.SessionScoped;
import javax.inject.Named;
import javax.ejb.Stateful;
#Stateful
#Named
#SessionScoped
public class SessionManagerImpl implements SessionManager, Serializable {
...
public UserDto getLoginedUser() {
...
}
}
And the other is called DashboardController as:
import javax.enterprise.context.RequestScoped;
import javax.inject.Named;
import javax.inject.Inject;
#Named
#RequestScoped
public class DashboardController implements Serializable {
#Inject
private SessionManager sessionManager;
...
public void loadUserInfo() {
...
UserDto userDto = sessionManager.getLoginedUser();
}
}
The first time i open a page refer DashboardController, it works well. And if i continue to use the website, it still works. But if i don't click any page for some minutes, and come back to open the page, it will display a null pointer for the javassist$$getLoginedUser method invocation (sessionManager is not null when i use debug to watch). The session is still valid for i can get values from session map directly using faces context.
What's wrong with the SessionManager? Thanks.
This occurs because your Stateful Session Bean (EJB) has passivated, and is not reintroduced to your session. If there isn't a strong need to make your session scoped object a session bean, I would just make it a SessionScoped managed bean.

Cant access property of managed bean from another managed bean

I want to access the property of a #SessionScoped bean in another bean using #ManagedProperty. In short, I want to access the name property of firstBean in secondBean.
#ManagedBean
#SessionScoped
public class FirstBean implements Serializable{
private String name;
//...other attributes
//...constructor
public String getSelectedModel() {
return selectedModel;
}
public void setSelectedModel(String selectedModel) {
this.selectedModel = selectedModel;
}
//other getters&setters
}
And second bean:
#ManagedBean
#SessionScoped
public class SecondBean implements Serializable{
#ManagedProperty(value="#{firstBean}")
private FirstBean firstBean
public SecondBean() {
System.out.println(firstBean.getName());
}
public IndexBean getFirstBean() {
return firstBean;
}
public void setFirstBean(FirstBean firstBean) {
this.firstBean = firstBean;
}
When I run this, I always get NullPointerException on System.out.println(firstBean.getName()); in the constructor of second bean, which seems to mean that I need to create a new instance of firstBean.
But strangely, when I commented out this line, I can do something like this with no errors, which means that firstBean is actually a property of secondBean.
<h:outputText value="#{secondBean.firstBean.name}" />
What's the problem here?
It's not possible to access an injected dependency in the constructor. You're basically expecting that Java is able to do something like this:
SecondBean secondBean; // Declare.
secondBean.firstBean = new FirstBean(); // Inject.
secondBean = new SecondBean(); // Construct.
It's absolutely not possible to set an instance variable if the instance is not constructed yet. Instead, it works as follows:
SecondBean secondBean; // Declare.
secondBean = new SecondBean(); // Construct.
secondBean.firstBean = new FirstBean(); // Inject.
Then, in order to perform business actions based on injected dependencies, use a method annotated with #PostConstruct. It will be invoked by the dependency injection manager directly after construction and dependency injection.
So, just replace
public SecondBean() {
System.out.println(firstBean.getName());
}
by
#PostConstruct
public void init() { // Note: method name is fully to your choice.
System.out.println(firstBean.getName());
}

#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("")
}

Can't #Inject a #ManagedBean in another #ManagedBean

Ok here is my session bean. I can always retrieve the currentUser from any Servlet or Filter. That's not the problem The problem is the fileList, and currentFile. I've tested with simple int's and Strings and its' the same effect. If I set a value from my view scoped bean I can get the data from another class.
#ManagedBean(name = "userSessionBean")
#SessionScoped
public class UserSessionBean implements Serializable, HttpSessionBindingListener {
final Logger logger = LoggerFactory.getLogger(UserSessionBean.class);
#Inject
private User currentUser;
#EJB
UserService userService;
private List<File> fileList;
private File currentFile;
public UserSessionBean() {
fileList = new ArrayList<File>();
currentFile = new File("");
}
#PostConstruct
public void onLoad() {
Principal principal = FacesContext.getCurrentInstance().getExternalContext().getUserPrincipal();
String email = principal.getName();
if (email != null) {
currentUser = userService.findUserbyEmail(email);
} else {
logger.error("Couldn't find user information from login!");
}
}
Here is an example.
My view scoped bean. This is how it is decorated.
#ManagedBean
#ViewScoped
public class ViewLines implements Serializable {
#Inject
private UserSessionBean userSessionBean;
Now the code.
userSessionBean.setCurrentFile(file);
System.out.println("UserSessionBean : " + userSessionBean.getCurrentFile().getName());
I can see the current file name perfectly. This is actually being printed from a jsf action method. So obviously the currentFile is being set.
Now if I do this.
#WebFilter(value = "/Download")
public class FileFilter implements Filter {
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException, IOException {
HttpSession session = ((HttpServletRequest) request).getSession(false);
UserSessionBean userSessionBean = (UserSessionBean) session.getAttribute("userSessionBean");
System.out.println(userSessionBean.getCurrentUser().getUserId()); //works
System.out.println("File filter" + userSessionBean.getCurrentFile().getName()); //doesn't work
chain.doFilter(request, response);
}
#Override
public void init(FilterConfig filterConfig) throws ServletException {
}
#Override
public void destroy() {
}
}
currentUser shows up fine but I can't see the file. It's just blank. The same thing happens with Strings, int's, etc.
Thanks for any help you can provide on this.
INFO: UserSessionBean : Line 3B--8531268875812004316.csv (value printed from view scoped bean)
INFO: File filter tester.csv (value printed when filter is ran.)
**EDIT**
This worked.
FacesContext context = FacesContext.getCurrentInstance();
userSessionBean = (UserSessionBean) context.getApplication().evaluateExpressionGet(context, "#{userSessionBean}", UserSessionBean.class);
I put this in the constructor of the ViewScoped and everything was fine. Now why isn't the inject doing what I thought? At first I thought maybe because I was using JSF managed beans instead of the new CDI beans. But I changed the beans to the new style(with named) and that was the same effect.
Does the inject only allow you to access the beans but not change their attributes?
You're mixing JSF and CDI. Your UserSessionBean is a JSF #ManagedBean, yet you're using CDI #Inject to inject it in another bean. CDI doesn't reuse the JSF managed one, it instead creates a brand new one. Use the one or the other, not both. The correct annotation to inject a JSF-managed bean is #ManagedProperty.
Replace
#Inject
private UserSessionBean userSessionBean;
by
#ManagedProperty(value="#{userSessionBean}")
private UserSessionBean userSessionBean;
and ensure that you don't have a import javax.enterprise.context anywhere in your code (which is the package of CDI annotations).
Alternatively, migrate all JSF bean management annotations to CDI bean management annotations.
import javax.inject.Named;
import javax.enterprise.context.SessionScoped;
#Named
#SessionScoped
public class UserSessionBean implements Serializable {}
import javax.inject.Named;
import javax.faces.view.ViewScoped;
#Named
#ViewScoped
public class ViewLines implements Serializable {}
Additional advantage is that you can just #Inject it inside a regular servlet or filter without the need to manually grab it as request/session/application attribute.
Moreover, JSF bean management annotations are deprecated since JSF 2.3. See also Backing beans (#ManagedBean) or CDI Beans (#Named)?
My best GUESS as to why this is happening, is because the variable file, is being set in view scope, and then passed by reference into the session scoped bean. Maybe this is happening because when the view scope bean is destroyed, it still has a reference to that variable, but doesn't bother to see if there's any other references to it in session scope, where it should be preserved. Hence, when it's destroyed, it's removed from both view and session scope in this case.
Could you try calling setCurrentFile with an object instantiated with 'new'? That might prove or disprove this hypothesis of mine.
Otherwise, my best advice would be to crack open the debugger, and see exactly where getCurrentFile is being changed.

Resources