GWT/GXT and Session Timeouts

One of the applications we develop at work is a GWT/GXT web application hosted in tomcat.  Recently we started getting complaints about strange behavior when the user’s session timed out. The RPC calls would fail so all sorts of strange error messages would popup, but the user was not redirected back to the login page.

This strange behavior was occurring because, after the initial application load, the only traffic to the server were our RPC calls. When an RPC call is made after the session timeout the call simply fails. The redirect from tomcat is ignored. 

I searched the web for any solutions and came across this solution on the EXT-JS forum. The general idea was to setup a couple of timers after a user logs in. One timer will show the user a warning when the session is about to expire, the other timer will show a “Session Expired” dialog and close the browser when the user presses “OK”.

The problem with that implementation is that it doesn’t take into account user activity. So, if the session timeout is set to 30 minutes, the warning dialog would pop up at 29 minutes after the user logs in – regardless of whether the user was active for those 29 minutes. This behavior would be rather annoying to our users. So, I decided to modify this solution to make it a bit less annoying.

In my modified solution, the user does not get a “Session is about to expire” warning. Whenever the session times out a popup message alerts the user that their session has expired. When they click “OK” they are redirected back to the login page.

The basic algorithm is this:

  1. On GWT module load an RPC call gets the user session timeout in milliseconds.
  2. A timer is setup to run the RPC call again a few seconds after the timeout.
  3. If the call succeeds, the user has been active, so the timer is setup again. Otherwise, the session timeout dialog is shown.

Client side

public class Main implements EntryPoint
{
    private Timer sessionTimeoutResponseTimer;
    private MyServiceAsync service;

    /**
     * Added to the first session timeout check to allow for startup time
     */
    private final int INITIAL_TIMEOUT_PAD = 60000;
    /**
     * Added to the session timeout check timer.
     */
    private final int TIMEOUT_PAD = 15000; 

    /**
     * Entry point method.
     */
    public void onModuleLoad()
    {
        initSessionTimers();
        buildUi();
    }

    private void initSessionTimers()
    {
        service = (MyServiceAsync) GWT.create(MyService.class);
        ((ServiceDefTarget) service)
            .setServiceEntryPoint(GWT.getModuleBaseURL() + "gwt/myService");
        service.getUserSessionTimeout(new AsyncCallback<Long>()
        {
            public void onSuccess(Long timeout)
            {
                sessionTimeoutResponseTimer = new Timer()
                {
                    @Override
                    public void run()
                    {
                        checkUserSessionAlive();
                    }
                };
                sessionTimeoutResponseTimer.schedule(timeout+INITIAL_TIMEOUT_PAD);
            }

            public void onFailure(Throwable caught)
            {
                displaySessionTimedOut();
            }
        });
    }

    private void checkUserSessionAlive()
    {
        service.getUserSessionTimeout(new AsyncCallback<Long>()
        {
            public void onSuccess(Long timeout)
            {
                sessionTimeoutResponseTimer.cancel();
                sessionTimeoutResponseTimer.schedule(timeout+TIMEOUT_PAD);
            }

            public void onFailure(Throwable caught)
            {
                displaySessionTimedOut();
            }
        });

    }

    private void displaySessionTimedOut()
    {
        MessageBox.alert(
                "Session Timeout",
                "Your session has timed out.",
                new Listener<MessageBoxEvent>()
        {

            public void handleEvent(MessageBoxEvent be) {
                Window.Location.reload();
            }
        });
    }
}

Server side

public class MyServiceImpl extends RemoteServiceServlet implements MyService
{
    @Override
    public Integer getUserSessionTimeout() {
        int timeout = getThreadLocalRequest().getSession()
            .getMaxInactiveInterval() * 1000;
        return timeout;
    }
}

Note: The method getMaxInactiveInterval() returns the session timeout in seconds that is configured in web.xml. The session timeout in web.xml is specified in minutes.

public interface MyService extends RemoteService
{
    Integer getUserSessionTimeout();
}

public interface MyServiceAsync extends RemoteService
{
    void getUserSessionTimeout(AsyncCallback<Integer> callback);
}

There is one small problem with this approach. It may keep the session alive longer than the session timeout. To illustrate this, lets say that the session timeout is set to 30 minutes. When the user logs in, a timer is setup to make an RPC call 31 minutes after the user logged in. Let’s say the user works for 15 minutes then switches to something else. At 31 minutes after logging in, the timer will cause an RPC call to be made, which resets the session timeout countdown on the server side. 30 minutes later the timer will then make the call again. This time the session will have timed out and the “Session Timeout” alert is displayed. So, even though the session timeout was set for 30 minutes, the user was inactive for 45 minutes before the session timed out. I suppose with a little thought and another timer you could come up with a way to make the session timeout after exactly 30 minutes of inactivity. Perhaps that will be a topic for future post.

5 thoughts on “GWT/GXT and Session Timeouts

  1. nicola says:

    Hi John, good post: I was looking for a solution for this problem and now I implemented it thanks your post (and the post you readed too).
    The principle that I adopted is the same you exposed but with some slight differences: I retrieve data from server a few before the session has to expire and on the client I use a session length a few less than that on the server. Generally the session expires at the right moment (counting the last user activity that involved the server), only a few before than server session would have expired.
    If you want I can be more detailed, cheers

    • John says:

      I’d be interested in hearing more details. If you have a blog with the details I can link to it. Or if you just give me the details I’ll update my post with your suggestions. Thanks!

  2. nicola says:

    Hi John, in my algorithm I consider:
    – session duration
    – session creation time
    – last valid access time (last access to the server which wasn’t for retrieving session data)
    – last request for session

    In my implementation i first launch session handling when the login has been done. So I retrieve the first two parameters specified above from the server, save them in instance variables, set the to and to ; then I schedule the next session check for – , where is the gap of which the control anticipates the real server session expire (I setted it to 20 seconds for having the time to do a couple of attempts in case of error).
    So now the control passes to a method that checks the session periodically.
    For checking the validity of the session I retrieve the same two parameters from the server (session duration and creation time). First, if the session creation time retrieved now and that stored in the instance variable are different I unvalidate the session: this scenario shouldn’t happen often.
    Then I calculate the time passed between the last valid request (i.e. request not for retrieving session data and this request): if this time is more than + (some as above) i make logout.
    Otherwise the session is valid, so I set to the time retrieved in this check, then I schedule the next check of – + . I hope that my article is clear and the english isn’t too wrong. I’m waiting for the response, bye

    nic

  3. nicola says:

    some things were cut off from my response… I put pseudo code in <> without quoting them… I will correct soon

  4. Annuk says:

    Hii john,
    great work…
    I tried implementing your solution.
    In web.xml the session timeout was set to 1.
    But when i implement your code.it calls the checkusersession method after every minute and the session is still active.it never calls the login page after a minute.

    It should call the login page after a minute???

Leave a Reply

Your email address will not be published. Required fields are marked *