Skip to content
This repository was archived by the owner on May 9, 2019. It is now read-only.

Asynchronous operations in a web utility

Ari Kamen edited this page Sep 2, 2015 · 7 revisions

Overview

Use case: In some environments, long running HTTP transactions (transactions that take longer than, say, about a minute) are not viable (they get killed). In order to execute a long-running server-side task, a web application must run the task asynchronously: It must perform one quick transaction to initiate the task, then subsequent periodic quick transactions to check on progress (percent complete) and (eventually) get the result. The common framework contains two packages that help web application developers to implement asynchronous tasks:

*.core.gwt.async: Provides a utility class helpful in developing the client-side of asynchronous tasks for GWT clients. *.standard.trackable: Provides a set of classes helpful in developing the server-side of asynchronous tasks.

Developing the server side of an asynchronous task

Package: *.standard.trackable

A task that will be run asynchronously must be trackable (must provide the ability for clients to check on their status, and get their result when done). How to make a task trackable (server-side):

  1. The long-running task should be extracted to a class, called the worker class. See the HealthyWorkerMock class (under src/test) for an example of a worker class. The worker class must implement the trackable interface. It can have any number of constructors and methods required to create and initialize it, and then one method (runAndTrack()) to run the task. It also needs a setProgressRecorder() method (more on this later).
  2. The class that will execute (start and monitor) the task (say, a server side class in the web app) must create and initialize the worker object, and then pass it to an instance of TrackableRunner (via the TrackableRunner constructor). To run the task it calls the startTrackable() method on its instance of TrackableRunner. To (periodically) check on the progress of the task, call the getProgress() method on the TrackableRunner instance.
  3. By the time it is started by the TrackableRunner, the worker object will have a ProgressRecorder (passed into it via the setProgressRecorder method). Each task is assumed to consist of a known number of subtasks. When all subtasks are completed, the task is done. As it makes progress on the task, the worker object should call that ProgressRecorder to provide periodic updates on progress, by telling it how many subtasks have been completed, or incrementing the number completed.
  4. The web application client will call the server to initiate the task, and start a timer to remind it to check on progress in a little while. When the timer expires, call the server to check progress. If progress has reached 100%, get the results and provide them to the user. If not, optionally provide the user with an update on progress (using the percent complete value returned by the check progress operation).

How the Trackable mechanism works: TrackableRunner provides to the worker class a ProgressRecorder that knows how to pass progress updates back to it, and then runs the worker task's startAndTrack method in a separate thread (receiving progress updates via the ProgressRecorder).

Developing the client-side of an asynchronous task

Package: *.core.gwt.async

How to make a task trackable (GWT client-side):

  1. In the RPC classes (*Service and *ServiceAsyc classes), replace the single long-running method (say, generateReport()) with two:

    • One to initiate the long-running task (initiateReportGeneration())
    • One to check on progress (checkProgress())
  2. In the client:

    1. Instantiate an instance of AsyncUtils and store it in a class field (async).
    2. When the user requests the long-running task:
      1. Call the RPC to initiate the long-running task (generateReport()).
      2. In the onSuccess() callback, call async.scheduleTask() to schedule the first checkProgress() call. Pass to it an instance of a class that implements ScheduledTask.
      3. In the runAfterTimer() method of the ScheduledTask object: call the checkProgress() RPC to check progress. In the onSuccess() callback:
        1. If the result of checkProgress() indicates that the task is not yet done:
          1. Optionally: call AsyncUtils.generateProgressBarString() to get a crude progress bar string that can be displayed in the UI.
          2. Call async.scheduleTask() to schedule the next checkProgress() call.
        2. If the result of checkProgress() indicates that the task is done: process the result.
  3. On the server: Follow the instructions under "Developing the server side of an asynchronous task" above.

Clone this wiki locally