0

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.

BaseBean Class

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;
    }

}

LoginBean Class

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.

}

CreateReport Class

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";
    }

}

Used Software

  • Glassfish 4
  • Mojarra 2.2

Edit

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);
}
Lev
  • 100
  • 8
  • It appears that you are trying to go ahead of the intention of `@ManagedProperty` and/or managed beans. – Tiny Mar 30 '15 at 15:44
  • I am flexible to an extent. `@ManagedProperty` or `FacesContext` are the solutions that I have found so far, however I want to find a better approach using Mojarra. Where my flexibility stops is the tech: I have to use Mojarra and Glassfish. – Lev Mar 30 '15 at 16:15
  • It's probably best you use the `@ManagedProperty`, for readability and maintainability. Having a method that does the magic might not look too good in code, to the next guy – kolossus Mar 31 '15 at 20:43

2 Answers2

0

making BaseBean a managed bean itself, and using it as a superclass for all other managed beans are two things that should not go along.

instead, you can:

  • remove @ManagedBean annotation from BaseBean.
  • save loggedInUser to session.
  • keep isLoggedIn as a protected method in BaseBean. you will be able to reach session via FacesContext there and get loggedInUser from session.

    ((HttpServletRequest)FacesContext.getCurrentInstance() .getExternalContext().getRequest()).getSession()

ps: i don't know what the hell i was thinking when offering a static variable.

tt_emrah
  • 1,043
  • 1
  • 8
  • 19
  • This won't work (double-checked just in case). The value of a `static` variable will exist for the life of the application. If I make my `loggedInUser` static then all users will share this person's credentials. Removing `@ManagedBean` from BaseBean also makes it impossible to share the `isLoggedIn()` method between bean subclasses (something I really need for render checks). – Lev Mar 30 '15 at 16:13
  • however, i couldn't get why removing `@ManagedBean` from `BaseBean` will prevent you from using `isLoggedIn`. you are already inheriting `BaseBean` in each managed bean and it's a public method? – tt_emrah Mar 30 '15 at 20:42
0

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.

hmrojas.p
  • 562
  • 1
  • 5
  • 15
  • My solution was more general as I set the user object as part of the HttpSession. Going to award your answer as it came the closest to what works for me. My code: private HttpSession session = (HttpSession) FacesContext.getCurrentInstance().getExternalContext().getSession(true); private static final String LOGGED_IN_USER = "loggedInUser"; public User getLoggedInUser() { return (User) session.getAttribute(LOGGED_IN_USER); } public void setLoggedInUser(User user) { session.setAttribute(LOGGED_IN_USER, user); }` – Lev Apr 18 '15 at 19:41
  • Oh yeah, you right, I'm happy that my solution helps you, thanks for accept my answer :) – hmrojas.p Apr 20 '15 at 15:42