Twitter Logo Follow us on Twitter
Project Information About this project

Threads in RAP

Like in SWT, we have to distiguish two types of threads, the UI thread and other, non-UI threads, also called “background threads”.

The UI Thread

In SWT, the UI thread is the thread in which the Display was created. Widgets may only be created and accessed in this thread. In RWT, this is no different, and in addition to that, the UI thread also provides the scope of the UI session. Thus, all methods in RWT that require the current UI Session or application context must be called from this thread. This includes most methods of RWT, such as RWT.getUISession() and RWT.getApplicationContext()), but also the SingletonUtil.

Since entrypoints an all event listeners are executed in the UI thread, all application code has full access by default. However, when an application forks new threads (non-UI or background threads), these threads cannot directly access the UI session or the application context. See Session access from a background threads.

UI Updates from background threads

Any RAP Client that connects with a RAP server also stores some data, usually for the duration of the UI Session. Most notably, the client has a complete copy of the UI state, which it presents to the user to interact with. It can only exchange data during HTTP requests, which are triggered by user interactions. This means that if the UI state is changed by a background thread, the client would normally not be updated until the next time the user does something with the UI. See Server Push for more.

Session access from a background thread

RWT determines the UI session based on the current thread. When background threads try to do so, the code will fail with an java.lang.IllegalStateException, saying that this thread has no access. This may happen when using any method of RWT or SingletonUtil. Example:

// INCORRECT
// will throw IllegalStateException: No context available ...
Runnable runnable = new Runnable() {
  public void run() {
    UISession session = RWT.getUISession();
    // ... or ...
    Client client = RWT.getClient();
    // ... or ...
    MySessionSingleton sessionSingleton = MySessionSingleton.getInstance();
    // ...
  }
};
new Thread( runnable ).start();

The solution is to run the code in question in the context of the UI session. To do so, it must be wrapped in a runnable and executed using the method UISession#exec( Runnable runnable ). This will execute the runnable in the context of the UI session, granting the code in its run() method access to all session-scoped data. However, the code can not access any widgets, because it is not executed in the UI Thread (see below).

In case there is no reference to the current UISession available, it can also be obtained from RWT.getUISession( Display display ).

// CORRECT
final Display display = Display.getCurrent();
final Runnable runnable = new Runnable() {
  public void run() {
    UISession uiSession = RWT.getUISession( display );
    uiSession.exec( new Runnable() {
      public void run() {
        Client client = RWT.getClient();
        // ... or ...
        MySessionSingleton sessionSingleton = MySessionSingleton.getInstance();
        // ...
      }
    } );
  }
};
new Thread( runnable ).start();

Session access vs. UI access

Like in SWT, widgets can only be accessed from a UI thread. While UISession#exec executes a runnable in the context of the UI session, it does not execute this runnable within the UI thread but in the same thread that the method is called. Hence, UISession#exec is not suitable to execute code that accesses the UI. To do so, Display#asyncExec (or Display#syncExec) has to be used instead. Please note that UI updates from a background thread will only be “pushed” to the client when the Server Push is active. In contrast to asyncExec, running code using UISession#exec does not cause any additional network traffic.

In summary, when a thread only needs to access the UI session context (e.g. by using SingletonUtil or RWT.getClient), UISession#exec should be used. But when the UI needs to be updated, Display#asyncExec must be used, as it runs the code in the UI thread and thereby gives access to session scope and widgets. Using UISession#exec from within the UI thread is pointless.

Using a Helper

It can be helpful to create a helper class like this:

abstract class SessionRunnable implements Runnable {

  final private UISession session;

  public SessionRunnable() {
    session = RWT.getUISession();
  }

  @Override
  public void run() {
    session.exec( new Runnable() {
      @Override
      public void run() {
        SessionRunnable.this.runInSession();
      }
    } );
  }

  abstract void runInSession();
}

The above example could then be shortened to this:

final Runnable runnable = new SessionRunnable() {
  @Override
  public void runInSession() {
    Client client = RWT.getClient();
    // ... etc ...
  }
};
new Thread( runnable ).start();