VercauterenP
VercauterenP

Reputation: 71

SessionScoped controller not working

EDIT: Okay, so I tried setting a few console.writes to check what's happening... It seems my logout script is called upon navigation. But I don't call it anywhere except on my logout button.

Here is my template code:

<div class="navbar">
    <div class="navbar-inner">
        <ul class="nav">
            <li class="active"><a href="#{root}index.xhtml">Home</a></li>
            <li><a href="#{root}races/list.xhtml">Races</a></li>
            <li><a href="#{root}horses/list.xhtml">Horses</a></li>
            <h:panelGroup rendered="#{memberController.logged == true}">
                <li><a href="#{root}profile/history.xhtml">History</a></li>
                <li><a href="#" onclick="#{memberController.logout()}">Logout</a></li>
            </h:panelGroup>
            <h:panelGroup rendered="#{memberController.logged == false}">
                <li><a href="#{root}users/login.xhtml">Login</a></li>
                <li><a href="#{root}users/register.xhtml">Create Account</a></li>
            </h:panelGroup>
        </ul>
    </div>
</div>

Original message:

I'm creating a website for my school project (Java EE)... It's our first year doing so. Now as this is evening school and only had a semester learning it, you might see that my way of doing things ain't the best out there :)

So to get started, I'm trying to create a login feature but instead of those hundered lines of security codes, we may use a simple session scoped member object.

So here you have a few of my classes:

Member class:

@Entity
@Table(name = "members")
public class Member implements Serializable {
    //+ Getters, setters, HashCode and equals

    @Id
    @GeneratedValue(strategy = GenerationType.TABLE)
    private long id;
    private double money;

    @NotNull(message = "Username cannot be null")
    @Size(min = 4, message = "Username should be of minimum 4 characters")
    private String userName;

    @NotNull(message = "Password cannot be null")
    @Size(min = 4, message = "Password should be of minimum 4 characters")
    private String password;

    @PostPersist
    private void initDefault() {
        this.money = 500;
    }
}

MemberBean class:

@Stateless
public class MemberBean {

    @PersistenceContext(unitName="HorseRacingPU")
    private EntityManager em;

    public Member getMember(long id){
        return em.find(Member.class, id);
    }

    public Member getMember(String username, String password){
        TypedQuery<Member> q = em.createQuery("SELECT u FROM Member u WHERE u.userName=?1 AND u.password=?2", Member.class);
        q.setParameter(1, username);
        q.setParameter(2, password);
        return q.getSingleResult();
    }

    public List<Member> getAllMembers(){
        TypedQuery<Member> q = em.createQuery("SELECT u FROM Member u", Member.class);
        return q.getResultList();
    }

    public Member addOrUpdateMember(Member u){
        Member original = em.find(Member.class, u.getId());
        if(original == null){
            em.persist(u);
            return u;
        }else{
            return em.merge(u);
        }
    }

    public Member deleteMember(long id){
        Member original = em.find(Member.class, id);
        if(original != null){
            em.remove(original);
        }
        return original;
    }
}

MemberController class:

@SessionScoped
public class MemberController implements Serializable {

    @EJB
    private MemberBean bean;
    private String username;
    private String password;
    private Member member;
    private boolean logged = false;
// + their getters and setters

    public List<Member> getAllMembers() {
        return bean.getAllMembers();
    }

    public String login() {
        member = bean.getMember(username, password);
        if (member != null) {
            logged = true;
            return "/races/list.xhtml?faces-redirect=true";
        }
        return "/users/login.xhtml?faces-redirect=true";
    }

    public String logout() {
        FacesContext.getCurrentInstance().getExternalContext().invalidateSession();
        return "/index.xhtml?faces-redirect=true";
    }

    public void checkLogin(ComponentSystemEvent e) {
        if (!logged) {
            FacesContext context = FacesContext.getCurrentInstance();
            ConfigurableNavigationHandler handler = (ConfigurableNavigationHandler) context.getApplication().getNavigationHandler();
            handler.performNavigation("/users/login.xhtml?faces-redirect=true");
        }
    }

    public Member getMember() {
        return member;
    }

    public void submit() {
        bean.addOrUpdateMember(member);
    }
}

The main error I'm getting is the following:

INFO: Exception when handling error trying to reset the response.

A more specific detail error can be found here: http://pastebin.com/h5nTNnes

So what happens is that when I login, everything works great. The moment I navigate to another url (after being forwarded to /races/list) I get logged out. The error itself shows when I use the checkLogin():

<f:event type="preRenderView" listener="#{memberController.checkLogin}" />

I'm not sure whether this is related, but when I login without any demo data (or with wrong credentials) I get an evaluation exception and that no entity could be retrieved. Here more details: http://pastebin.com/Tv9mQ1K9

What could this be? I scratched my head for 3 days now and can't seem to find an issue anywhere.

Upvotes: 1

Views: 519

Answers (1)

BalusC
BalusC

Reputation: 1108632

This,

<li><a href="#" onclick="#{memberController.logout()}">Logout</a></li>

is not right.

The onclick attribute should reference a JavaScript handler. E.g. alert('peek-a-boo');. JSF/EL treats it as a plain vanilla string and expects that the logout() method returns some JavaScript code as String which should then be inlined in the HTML result. Imagine that the method actually returned alert('peek-a-boo');, then the final result (as you see in browser by rightclick, View Source) would be this:

<li><a href="#" onclick="alert('peek-a-boo');">Logout</a></li>

However, in your particular case you're actually performing a logout and returning a string value of /index.xhtml?faces-redirect=true. So the generated HTML ends up being

<li><a href="#" onclick="/index.xhtml?faces-redirect=true">Logout</a></li>

Which is invalid JS code. But that's not the major problem: the user is been logged out without clicking the link!

You need a fullworthy JSF command component instead. E.g. <h:commandLink>.

<li><h:form><h:commandLink value="Logout" action="#{memberController.logout()}"/></h:form></li>

The method is this way only invoked when the link is actually clicked, which is exactly what you need.

Upvotes: 1

Related Questions