4

I'm trying to use a pattern found on a IceFaces page.(I'm not using IceFaces, using PrimeFaces)

In this case I have two beans:

UserController and Usermodel

On my UserModel I have a instance of UserVO (created by another programmer). On My UserController I have this:

@ManagedBean
@RequestScoped
public class UserController implements Serializable
{

    private static final long serialVersionUID = 1L;
    private UserBO bo;
    private UserModel model;

    public UserController()
    {
        bo = new UserBO();
        model = new UserModel();
    }

    public void Login() throws IOException
    {
        model.setUserVo(bo.executeLogin(model.getUserVo()));
        ExternalContext externalContent = FacesContext.getCurrentInstance().getExternalContext();
        if (!model.getUserVo().isError())
        {
            model.setLoggedIn(true);
            externalContent.getSessionMap().put("userSession", model);
            externalContent.redirect(externalContent.getRequestContextPath() + "/views/request/search.html");
        } else
        {
            model.setLoggedIn(false);
            FacesMessage facesMessage = new FacesMessage(FacesMessage.SEVERITY_ERROR, model.getUserVo().getMessage(), model.getUserVo().getLogin());
            FacesContext.getCurrentInstance().addMessage(null, facesMessage);
        }
    }

    public UserBO getBo()
    {
        return bo;
    }

    public void setBo(UserBO bo)
    {
        this.bo = bo;
    }

    public UserModel getModel()
    {
        return model;
    }

    public void setModel(UserModel model)
    {
        this.model = model;
    }
}

As you can see, I create a new instance of UserModel and set it with what was returned from the bo.executeLogin() and it is working, my object is returned.

To make sure the user is logged in, I have a property on my UserModel:

@ManagedBean
@SessionScoped
public class UserModel
{

    private UserVO userVo;
    private Boolean loggedIn = false;

    public UserModel()
    {
        userVo = new UserVO();
    }

    public UserVO getUserVo()
    {
        return userVo;
    }

    public void setUserVo(UserVO userVo)
    {
        this.userVo = userVo;
    }

    public Boolean getLoggedIn()
    {
        return loggedIn;
    }

    public void setLoggedIn(Boolean loggedIn)
    {
        this.loggedIn = loggedIn;
    }

I have a template.xhtml with:

<ui:fragment rendered="#{userModel.loggedIn}">
            <ui:include src="../includes/top.xhtml"/>
</ui:fragment>

And the thing is that it is not working, is not getting the loggedIn property value.

My guess is that accessing this way I'm kinda creating a new instance of UserModel, if so, it is a problem because my UserController is not session scoped, only the UserModel

EDIT

Instead of using this loggedIn property I know I can simply check if the UserModel userVo property is set but the problem is about the session scoped bean, I can't access it from UserController, where it is set because it isn't scoped session, and my template.xhtml will be used by every page.

Matt Handy
  • 29,855
  • 2
  • 89
  • 112
  • Do you have a link to the IceFaces example you followed? – StockB Oct 01 '12 at 20:55
  • 1
    @StockB sorry dude, I don't have the link anymore and can't find it but here are some useful. http://stackoverflow.com/questions/7223055/distinction-between-different-types-of-managed-beans/7223910#7223910 and also http://stackoverflow.com/questions/5104094/what-components-are-mvc-in-jsf-mvc-framework on this second link check the chosen answer, it is a explanation based on that IceFaces post, hope it helps –  Oct 03 '12 at 12:46

3 Answers3

8

(this was originally posted on https://stackoverflow.com/questions/10691324/working-with-3-java-beans-controller-backing-model, but the OP deleted the question and I didn't want to throw away the answer, I think a repost on this question is suitable as well)

You were probably focusing too much on this ICEfaces blog which contains mainly nonsense.

You should have a single JSF managed bean which acts as a controller, you already have it: UserController. You should have a simple entity bean which represents the model, you already have it: UserVO. You should have an enterprise bean which represents the service, you already have it: UserBO. Those last two doesn't need to be JSF managed beans.

Based on your question history you're using Glassfish, thus you can make use of JPA and EJB. So the model class should be a JPA @Entity and the service class should be a @Stateless EJB.

The use case of a "login user" is however special. You don't want to have the entity as a session scoped managed bean, or it would be implicitly created by JSF. You'd better put it straight in the session scope yourself, so that you can check on #{not empty user} if the user is logged in or not.

All with all it should look something like this:

@Entity
public class User {

    private String username;
    private String password;

    // ...
}
@Stateless
public class UserService {

    @PersistenceContext
    private EntityManager em;

    public User find(String username, String password) {
        return em.createQuery("SELECT u FROM User u WHERE username = :username AND password = MD5(:password)", User.class)
           .setParameter("username", username)
           .setParameter("password", password)
           .getSingleResult();
    }

}
@ManagedBean
@ViewScoped
public class UserController {

    private String username;
    private String password;

    @EJB
    private UserService service;

    public String login() {
        FacesContext context = FacesContext.getCurrentInstance();

        try {
            User user = userService.find(username, password);
            context.getExternalContext().getSessionMap().put("user", user);
            return "/views/commons/home.html?faces-redirect=true";
        }
        catch (NoResultException) {
            context.addMessage(null, new FacesMessage(FacesMessage.SEVERITY_ERROR, "Unknown login, please try again", null));
            return null;
        }
    }

    public String logout() {
        FacesContext.getCurrentInstance().getExternalContext().invalidateSession();
        return "/views/commons/login.html?faces-redirect=true";
    }

    // ...
}

with

<h:form>
    <h:inputText value="#{userController.username}" required="true" />
    <h:inputSecret value="#{userController.password}" required="true" />
    <h:commandButton value="login" action="#{userController.login}"/>
    <h:messages />
</h:form>

Alternatively, you can make the UserController a session scoped bean which holds the User entity, you'd only need to add an extra method to check if the user is logged in or not so that you can use it in EL by #{userController.loggedIn}.

@ManagedBean
@SessionScoped
public class UserController {

    private User user = new User();

    @EJB
    private UserService service;

    public String login() {
        FacesContext context = FacesContext.getCurrentInstance();

        try {
            user = userService.find(user.getUsername(), user.getPassword());
            return "/views/commons/home.html?faces-redirect=true";
        }
        catch (NoResultException) {
            context.addMessage(null, new FacesMessage(FacesMessage.SEVERITY_ERROR, "Unknown login, please try again", null));
            return null;
        }
    }

    public String logout() {
        FacesContext.getCurrentInstance().getExternalContext().invalidateSession();
        return "/views/commons/login.html?faces-redirect=true";
    }

    public boolean isLoggedIn() {
        return user.getId() != null;
    }

    // ...
}

with

<h:form>
    <h:inputText value="#{userController.user.username}" required="true" />
    <h:inputSecret value="#{userController.user.password}" required="true" />
    <h:commandButton value="login" action="#{userController.login}"/>
    <h:messages />
</h:form>
Community
  • 1
  • 1
BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
  • I thought that `@SessionScoped` was a option to using the session like: `context.getExternalContext().getSessionMap().put("user", user);` –  May 21 '12 at 20:15
  • @Gerep: the `@SessionScoped` declares a session scoped **JSF managed bean**. An entity is not supposed to be a JSF managed bean. – BalusC May 21 '12 at 20:16
  • @BalusC so what is the point of it? Sorry about the **weird questions**, I'm having a hard time with Java but I'm motivated and already bought a book =) –  May 21 '12 at 20:20
  • Your entity should not contain JSF managed bean specific logic such as action methods and so on. It should purely contain model data. There's no point of making it a JSF managed bean. It should be referenced by a real JSF managed bean or in the specific case of "login user" manually be placed in session scope. – BalusC May 21 '12 at 20:22
  • so adding them manually or adding the @SessionScoped is the same thing? –  May 21 '12 at 20:29
  • No, definitely not. If you use `@ManagedBean @SessionScoped` on the entity, then JSF may autocreate one if not exist in the scope. You don't want to have that to represent a "logged-in user". Note that either way they end up in the session scope anyway and is referenceable by `#{user}` (thus also in `@ManagedProperty`). – BalusC May 21 '12 at 20:32
  • @BalusC please, if possible explain what is the need of this line: `FacesContext context = FacesContext.getCurrentInstance();`, just to add the session manually? –  May 22 '12 at 11:53
  • It gets a reference of the current faces context. Either to put the object in session map, or to add a faces message. – BalusC May 22 '12 at 12:23
  • Hi @BalusC; Thanks for your answer. Is it bad practice to have the `password` as a `@SessionScoped` bean's attribute because `@SessionScoped` bean is set as the `HttpSession` attribute? – Arash Feb 20 '22 at 09:38
5

Instead of creating a new UserModel instance in your UserController, inject it with @ManagedProperty.

In UserController:

@ManagedProperty(value="#{userModel}")
private UserModel model;

// add getter and setter for userModel (important!)

Then you don't have to instantiate it in the constructor and will always get the session scoped instance of UserModel in your controller.

UPDATE:

I think you are complicating the login process with your strict MVC approach. In JSF the borderlines between model, view and controller are somewhat blurred or overlapping.

I recommend reading this interesting question and answers and especially this answer for more information on that topic.

As to your concrete problem. I am not quite sure what is the reason but what you should definitely avoid is to instantiate managed bean by youself and fiddle around with both injected and self-initialized instances of beans.

Also I would recommend to merge your beans together into a single bean. Then you don't have the problems with circular dependencies and null references.

Community
  • 1
  • 1
Matt Handy
  • 29,855
  • 2
  • 89
  • 112
  • thanks, so every time I need to work with session scoped beans I need to use `@ManagedProperty`, right? –  May 18 '12 at 20:11
  • 1
    `@ManagedProperty` can be used to inject a bean or field of the same or a broader scope. So it will work from SessionScope into RequestScope but not the other way around. If you want to get the container managed instance of a bean, always use injection (or look-up as alternative) but don't instantiate the class. – Matt Handy May 18 '12 at 20:13
  • Thanks a lot, I've wasted some hours on it =) –  May 18 '12 at 20:15
  • offtopic, do you use a pattern and or have a link for me to study? –  May 18 '12 at 20:16
  • 2
    I recommend BalusC's [Communication in JSF 2.0](http://balusc.blogspot.com.br/2011/09/communication-in-jsf-20.html). :) – Elias Dorneles May 18 '12 at 20:18
  • One vote from me and another from a friend that was also looking for a solution. And thanks both for the suggestion. BalusC is a legend here, helped me a lot with other doubts about JSF. –  May 18 '12 at 20:22
  • @MattHandy when do I user `#{userModel}`? Can I simply access the model property? –  May 21 '12 at 19:42
  • Just read your new question. Wait some minues for an answer. ;-) – Matt Handy May 21 '12 at 19:44
  • @Gerep: Why did you delete your new question. I just wrote an answer, tried to submit and couldn't because the question was removed :-(. Was it because of the comment by StepTNT? Fortunately I copied my answer and will add it to the answer of this question that it doesn't get lost. – Matt Handy May 21 '12 at 19:59
  • Actually he deleted the old one, as BalusC stated in his answer here. Anyway, seems like that both our ways work. I usually inject beans with the `@EJB` annotation but I wasn't sure if using the `@ManagedProperty` was wrong. He deleted the question while I was editing my comment! – StepTNT May 21 '12 at 20:10
  • My problem is that I'm too focused on trying to find a pattern an stick with it...I've been searching for a while and this is messing with my head and specially with my "code" –  May 21 '12 at 20:12
  • 1
    No problem ;-). I recommend reading the links in my answer update (just fixed the broken links). – Matt Handy May 21 '12 at 20:14
0

You might use dependency injection to get the values from that bean with session scope.

@Inject UserModel user; 

Then you might use this Object in your UserControl bean. By the way you don't need to implement serializable when you are dealing with RequestScope beans.