エンタープライズギークス (Enterprise Geeks)

企業システムの企画・開発に携わる技術者集団のブログです。開発言語やフレームワークなどアプリケーション開発に関する各種情報を発信しています。ウルシステムズのエンジニア有志が運営しています。

GWTの拡張(3) - 同期処理

GWTを利用するにあたっては、同期処理が大きな課題になるが、参考情報が少ないので紹介しておく。

同期処理の課題は以下の2つにまとめることができる。

1. 二重登録の防止

ボタンを連続クリックしたり、処理をエンターキーにバインドした状態でエンターキーを押下し続けたりすると、デフォルトでは、複数のリクエストがサーバーに送信されてしまう。
このリクエストが登録処理だった場合には、同じ情報が重複して登録される可能性がある。

2. サーバーからの応答待ちの際の画面ロック

サーバーの処理結果をクライアントに確実に伝えるためには、サーバーから応答が返るまで他の操作を行えないようにする必要がある。
しかし、GWTは非同期処理を基本としているため、デフォルトではこのような動作を実現できない。

この同期処理の課題は、次のような方法で対処できる。

  1. イベントを受け取る(lockする)
  2. 入力形式チェックを行う(エラー時はunlockする)
  3. 確認用ダイアログを表示する(キャンセル時はunlockする)
  4. サーバへ送信する(WaitingPanelを表示する)
  5. サーバからの応答を受け取る(WaitingPanelを非表示にする、unlockする)

WaitingPanelについては、以前の記事を参照のこと。)

二重登録」の制御は、上記の(1.イベントを受け取る)から(5. サーバからの応答を受け取る)の間で行う必要がある。

「サーバからの応答待ちの際の画面ロック」は、上記の(4. サーバへ送信する)から(5. サーバからの応答を受け取る)の間で行う必要がある。

// 複数のリクエストの発火を制御するロッククラス
private Lock lock = new Lock();

// [step1. イベントを受け取る (lockする)]
// ボタンクリック時に呼ばれるメソッド
public void onClick(Event e) {
  // ロックを試みる。
  if (!lock.lock()) {
    // 既にロックされていたら、重複リクエストなので抜ける
    return ;
  }
  // サーバに送信するデータ取得(省略)
  Model model = ((...)e.getSource())...getModel();
  // 登録処理
  processInsert(model);
}

// ロック解除メソッド
protected void unlock() {
  lock.unlock();
}

// 登録処理
protected void processInsert(Model model) {
  // [step2. 入力形式チェックを行う (エラー時はunlockする)]
  // 入力形式チェック(BeanValidation)
  if (validate(model)) {
    // エラー時の処理...ロックは解除する
    unlock()
    return ;
  }
  // [step3. 確認用ダイアログを表示する (キャンセル時はunlockする)]
  // 確認ダイアログを表示
  if (!confirm("登録しますか?")) {
    // キャンセルの処理...ロックは解除する
    unlock()
    return ;
  }
  // [step4. サーバへ送信する (WaitingPanelを表示する)]
  // ウェイティングパネルを表示し、画面操作をロックする
  final WaitingPanel waiting = new WaitingPanel();
  waiting.show();
  // サーバ通信処理
  MyRemoteServiceAsync service = GWT.create(MyRemoteService.class);
  // 第1引数:登録するデータのオブジェクト
  // 第2引数:サーバからの応答を待つ非同期コールバック
  service.insert(model, new AsyncCallback<String>() {
    @Override
    public void onSuccess(final String result) {
      // [step5. サーバからの応答を受け取る (WaitingPanel非表示にする、unlockする)]
      // 成功時
      // implements ...
      waiting.hide();
      unlock()
    }

    @Override
    public void onFailure(Throwable caught) {
      // step[5. サーバからの応答を受け取る (WaitingPanel非表示、unlock)]
      // サーバからのエラー時
      waiting.hide();
      unlock()
      // Error handling ...
    }
  });
}

// 複数のリクエストの発火を制御するロッククラス
class Lock{

  private boolean isLocked = false;

  // ロックを試みる。ロックできたらtrue, 既にロックされてい
  // たらfalse
  public synchronized boolean lock() {
    if(isLocked){
      return false;
    }
    isLocked = true;
    return true;
  }

  // ロックの解除
  public synchronized void unlock(){
    isLocked = false;
  }
}

[高橋 友樹]