Thursday, November 8, 2012

GWT Auto Logout & Open Dialogs

Currently, I work for CounterPath, developing a large GWT application (as well as doing some GWT work for Green Motion Travel).

At CounterPath, my GWT application has an auto-logout feature.
This is to avoid the situation where an application user doesn't do anything for a while, and then edits a pile of information and clicks 'Save', only to find out their session timer has expired, meaning they just lost their work.

The auto-logout feature keeps an internal timer that matches the server's session timeout.
The internal timer is reset every time I do some server interaction.
To avoid inserting these calls all over the place in my code, I added an Async RPC facade;  My application uses the facade, and the facade makes the call to the server.
The downside is that I have to maintain another copy of the RPC interface.  Fortunately, that part is quite stable.

For example, I have:
  • MyData - the RPC interface
  • MyDataAsync - the Async version of MyData
and now,
  • MyDataAsyncSession - a class that implements MyDataAsync

MyData:

public interface MyData extends RemoteService
{
    public Data getData();
}

MyDataAsync:

public interface MyDataAsync
{
    public void getData(AsyncCallback<Data> callback);
    etc...
}

And, in my client code,

MyDataAsyncSession:

public class MyDataAsyncSession implements MyDataAsync
{
    MyDataAsync realRPC = (MyDataAsync)GWT.create(MyData.class);

    @Override
    public void getData(AsyncCallback<Data> result)
    {
        resetSessionTimer();
        realRPC.getData(result);
    }

    etc...
In my actual client code, I would then have:
MyDataAsyncSession rpc = new MyDataAsyncSession();
rpc.getData(new AsyncCallback<Data>(){...});
In this way, I don't have to worry about sprinkling calls to resetSessionTimer all over my code.
If this was all on the server, I could probably use AOP to insert calls to resetSessionTimer() in the right places...

On to the main point of this post.
In a few places, I use popup dialogs.
This leads to a problem: If you do something that generates a popup dialog, and then let it sit there for 30 minutes, the GWT application will auto-log-out, which will hide the other panels and show the login panel.  However, it won't hide the popup dialogs!  They will continue to show whatever was being worked on.

At first, I was considering a popup registration system, maintaining a list of popups that are currently open.  This could get messy, with some popups being auto-close, and others closed with a button or other control.
Fortunately, I found an easier way:  Just search the default root panel for PopupPanel widgets, and close them!

Here is the code, which is now part of my doLogout() function:

Code to Auto-Close Popup Panels

// Create a widget processing list, and
  // add the default root panel to it.
  List widgetList = new ArrayList();
  widgetList.add(RootPanel.get());
  while(!widgetList.isEmpty())
  {
      // Pull the first widget from the list
      Widget w = widgetList.remove(0);

      // If it is a popup, hide it!
      if (w instanceof PopupPanel)
      {
          ((PopupPanel)w).hide();
      }
      else if (w instanceof HasWidgets)
      {
          // Add any child widgets to the processing list.
          Iterator iter = ((HasWidgets)w).iterator();
          while(iter.hasNext())
          {
              Widget child = iter.next();
              widgetList.add(child);
          }
      }
  }

That's a lot easier than having to maintain a list of open popups!

No comments: