tags:

views:

35

answers:

2

My webapp has users who login. There is a timeout. Before the session expires, I would like to execute a method to cleanup some locks.

I've implemented a sessionListener but once I reach public void sessionDestroyed(HttpSessionEvent event) the session is already gone and I need some data from it, so I would like to execute a method (which needs the session alive and be able to access FacesConfig.getCurrentInstance()) before the session is actually expired.

How can I do that? Any ideas? This is my Session Listener:

public class MySessionListener implements HttpSessionListener {

    private static final Logger log = LoggerFactory.getLogger(MySessionListener.class);

public MySessionListener() {

}

public void sessionCreated(HttpSessionEvent event) {

log.debug("Current Session created : "
 + event.getSession().getId()+ " at "+ new Date());

}

public void sessionDestroyed(HttpSessionEvent event) {

// get the destroying session...

HttpSession session = event.getSession();
prepareLogoutInfoAndLogoutActiveUser(session);

log.debug("Current Session destroyed :"
 + session.getId()+ " Logging out user...");

/*

* nobody can reach user data after this point because 
* session is invalidated already.
* So, get the user data from session and save its 
* logout information before losing it.
* User's redirection to the timeout page will be 
* handled by the SessionTimeoutFilter.
*/

// Only if needed


}

/**
* Clean your logout operations.
*/

public void prepareLogoutInfoAndLogoutActiveUser(HttpSession httpSession) {

     UserBean user = FacesContext.getCurrentInstance().getApplication().evaluateExpressionGet(FacesContext.getCurrentInstance(), "#{user}", UserBean.class);
     LockBean lock = FacesContext.getCurrentInstance().getApplication().evaluateExpressionGet(FacesContext.getCurrentInstance(), "#{lock}", LockBean.class);
     lock.unlock(user.getUsername());
     log.info("Unlocked examination for user: "+user.getUsername());

    }

}

But I'm getting NullPointerException at FacesContext.getCurrentInstance().getApplication() because either getCurrentInstance is null or getApplication returns null

+3  A: 

You can achieve that by implementing a HttpSessionBindingListener you need to register a session which holds a lock by calling registerSession (the string "sessionBindingListener" may not be changed). The container will callback the valueUnbound() method after the session timed out and before the session is destroyed.

public class ObjectLock implements Serializable,HttpSessionBindingListener {
    public void valueBound(HttpSessionBindingEvent event) {
        log.info("valueBound:" + event.getName() + " session:" + event.getSession().getId() );

    }

    public void registerSession() {
        FacesContext.getCurrentInstance().getExternalContext().getSessionMap().put( "sessionBindingListener", this  );
        log.info( "registered sessionBindingListener"  );
    }

    public void valueUnbound(HttpSessionBindingEvent event) {
        log.info("valueUnBound:" + event.getName() + " session:" + event.getSession().getId() );
               // add you unlock code here:
        clearLocksForSession( event.getSession().getId() );
    }
}
stacker
Works like a charm. +1 and accepted.
pakore
I put it under `UserBean` class, which knows `LockBean`. I had this already implemented there from a previous question answered by...BalusC :D, so I just needed to add the `lock.unlock` line and it worked. Thanks both of you.
pakore
Implementing `HttpSessionBindingListener` is indeed better. You only need to ensure that `registerSession()` is called during a JSF request (however, that method is useless if `ObjectLock` is already a session scoped managed bean.
BalusC
+1  A: 

The FacesContext isn't available during the sessionDestroyed() simply because there's not necessarily any means of a HTTP request during the session destroy. Session destroy is fully container managed. The FacesServlet, who's the responsible for creating the FacesContext, only kicks in during HTTP requests on the particular url-pattern.

To achieve the requirement, take the advantage of the knowledge that session scoped managed beans are in essence stored as session attribues by their managed bean name as key. So, the following should work:

public void prepareLogoutInfoAndLogoutActiveUser(HttpSession httpSession) {
    UserBean user = (UserBean) httpSession.getAttribute("user");
    LockBean lock = (LockBean) httpSession.getAttribute("lock");
    lock.unlock(user.getUsername());
    log.info("Unlocked examination for user: "+user.getUsername());
}
BalusC
+1 Because this approach is as valid as stacker one. But I can only accept one answer. Thanks anyway.
pakore