Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Invoke method just before session expires

Tags:

java

servlets

jsf

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

like image 996
pakore Avatar asked Oct 15 '10 14:10

pakore


People also ask

What happens when a session expires?

When the session expires, or session timeout occurs, the Session_End event in global. asax is raised (except when session is handled by the DB) and the session collection is finally cleared. If any objects are NOT holding a reference to any of values in the session collection, then GC will collect it.

How do I expire a PHP session after 30 minutes?

if you want to expire the session after 30 minutes of activity instead of after 30 minutes since start, you'll also need to use setcookie with an expire of time()+60*30 to keep the session cookie active.


2 Answers

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() );
    }
}
like image 104
stacker Avatar answered Oct 19 '22 17:10

stacker


One more elegant solution:

Just add a @PreDestroy Annotation to a Session-Bean! If the Session is going to be destroyed, it will call PreDestroy on all SessionBeans beforehand, there you can logout and everything!

Although this doesn't currently work with many ApplicationServers, seems to be an unclear segment of the JSF Specification. So going with the accepted answer (HttpSessionBindingListener) will be necessary, until @PreDestroy will work as intended on all servers.

like image 20
Falco Avatar answered Oct 19 '22 17:10

Falco