I read BalusC's excellent tutorial on JSF communication and it helped me establish the basics of my app. I would like to share the currently logged in User object that is set in the SessionScoped BaseBean class with all of its subclasses. Can this be done without injecting BaseBean as a @ManagedProperty
for every single backing bean that needs to reference the logged in User?
My classes are listed below. Please let me know if more info is needed and I will be happy to update my question.
All other beans are subclasses of this bean. I am doing this to allow code reuse between beans.
@ManagedBean
@SessionScoped
public class BaseBean {
@EJB
protected UserDao userDao;
// Other DAOs along with methods (like isLoggedIn()) shared between beans
private User loggedInUser;
public User getLoggedInUser() {
return loggedInUser;
}
public void setLoggedInUser(User user) {
loggedInUser = user;
}
public boolean isLoggedIn() {
return loggedInUser != null;
}
}
Backing bean for the login page. To reduce number of DB calls I used the @ManagedProperty approach from the above tutorial to set the User object in the @SessionScoped BaseBean. Right now logging in and setting loggedInUser
works as expected.
@ManagedBean
@RequestScoped
public class LoginBean extends BaseBean {
@ManagedProperty(value = "#{baseBean}")
protected BaseBean baseBean;
private String username;
private String password;
public String login() {
Subject currentUser = SecurityUtils.getSubject();
try {
currentUser.login(username, password);
} catch (Exception e) {
e.printStackTrace();
} finally {
baseBean.setLoggedInUser(userDao.getUser(username));
}
return "index";
}
public String getUserFirstName() {
return baseBean.getLoggedInUser().getFirstName();
}
// Getters and Setters, including for the @ManagedProperty baseBean.
}
This is an example of one backing bean from many. I want to reference the currently logged in User in order to create a report, however if the below code runs the User will be null
! The only way I can get it to work is by adding a @ManagedProperty
entry with getters and setters for BaseBean just like in LoginBean class. I would really like to avoid this as I will be copy-pasting this code to almost every single backing bean that I have!
@ManagedBean
@RequestScoped
public class CreateReport extends BaseBean {
private Report report = new Report();
public String createReport() {
report.setOwner(getLoggedInUser()); // Use inherited method
// instead of DI-ing BaseBean
reportDao.create(report);
return "index";
}
}
One solution that I found is getting the instance of BaseBean from the FacesContext directly (I guess somehow the other beans are not in the same context or "don't see it?"). The following code (from BaseBean) would do what I want, however any bean subclass would have to invoke base()
and that seems awkward and wrong.
protected FacesContext context = FacesContext.getCurrentInstance();
public BaseBean base() {
return (BaseBean) context.getApplication().evaluateExpressionGet(context, "#{baseBean}", BaseBean.class);
}
I can see you want to implement authentication and authentication controls, then you can use JSF Filters. You can keep your BaseBean class with session scope, create a new class which implements javax.servlet.Filter and in this class to get your BaseBean class through of session, for example:
public class LoginFilter implements javax.servlet.Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException, IOException {
HttpServletRequest req = (HttpServletRequest) request;
BaseBean base = (BaseBean) req.getSession().getAttribute("baseBean");
if (base != null && base.isLoggedIn()) {
// to do something
chain.doFilter(request, response);
} else {
// to do something
HttpServletResponse res = (HttpServletResponse) response;
res.sendRedirect(req.getContextPath() + "/index.xhtml");
}
}
public void init(FilterConfig config) throws ServletException {
// to do something
}
public void destroy() {
// to do something
}
}
Now, if you want to create a report with your BaseBean class, you can get your BaseBean class of session:
BaseBean base = (BaseBean) ( FacesContext.getCurrentInstance()
.getExternalContext().getRequest()).getSession().getAttribute("baseBean") );
Then my recommendation in your case is to avoid inheritance and use the advantages of JSF.
I hope this information helps you.
Good Luck.