« マスタ検索画面の結果リストの文字色 | トップページ | ScheduledExecutorService の使い回し »

2009年9月14日 (月)

カルテ保存後ウインドウを閉じる(続)

以前 Exception が出て直していたのだが,頻度は減っていたもののやっぱり時々同じ Exception が出る。根本的に何かおかしいと思ってよく考えてみると,2つの問題点が分かった。編集終了後の EditorFrame を閉じるのは思ったより大変なことだった。

  1. まず,EditorFrame で KarteEditor が作られているのに,KarteEditor の方から EditorFrame.close() を呼んだらだめだろう。KarteEditor から EditorFrame.close() を呼んだら,戻ってきたら自分が既に消えていた,という事態になりうる。【2009/10/18追記】PropertyChangeListener の使い方を勉強して,KarteEditor で save が完了したら EditorFrame に PropertyChange を知らせて対処するように書き換えた。
  2. ChartMediator の調整も必要。ChartMediator は ChartImpl と EditorFrame で各々インスタンスが作られるが,KeyboardFocusManager にリスナが付くので,EditorFrame の Component をクリックした場合でも,ChartImpl と EditorFrame の両方の ChartMediator にイベントが行く仕様になっている。つまり,クリックされた Component は,ChartImpl からと,EditorFrame から,連続して2回 enter や exit を受け取る。
    オリジナルの状態だと ChartImpl と EditorFrame の両方の実体があるので問題ないが,今回のように EditorFrame を先に閉じてしまうと,EditorFrame を閉じる際に発生したイベントが ChartImpl に伝わって,ChartImpl 側の ChartMediator から,もう無いはずの EditorFrame の Component に対して exit(getActions()) が送られて Exception が出る。
    この過程でかかった微妙な時間により,それを受ける EditorFrame の実体がまだ残っていれば Exception は出ず,解放されてしまっていれば Null Pointer Exception が出る。Exception が毎回ではなく,たまに出ていたのはそういうわけだったようだ。

というわけで,EditorFrame.close() は EditorFrame 側で呼ぶように書き直し,ChartMediator は自分の component からのイベントのみ処理するようにした。

 

client/KarteEditor.java

SINGLE_MODE も使う場合は,save1 も同様に変更
private void save2(final SaveParams params) throws DolphinException {
  ・
  ・
      //
      // 文書履歴の更新を通知する
      //
      chart.getDocumentHistory().getDocumentHistory();
      ////↓セーブしたら,frame を閉じるようにする ← これはダメ
      // if (getContext() instanceof EditorFrame) chart.close();
      ////↑
      //// PropertyChangeListener で対応
      boundSupport.firePropertyChange(KarteEditor.SAVE_DONE, false, true);
    
    } else {
        // errMsg を処理する
        // エラーを表示する
        JFrame parent = chart.getFrame();
        String title = ClientContext.getString("karte.task.saveTitle");
        JOptionPane.showMessageDialog(parent,
          errMsg,
          ClientContext.getFrameTitle(title),
          JOptionPane.WARNING_MESSAGE);
      }
    }
  };
  // EditorFrame に save 後閉じる処理を入れたので,ここの Thread が終わらないうちに
  // EditorFrame が閉じてしまわないように,chart が EditorFrame の時は,仕事が終わるまで待ってもらう。
  // if (chart instanceof EditorFrame) task.executeInForeground();
  // else task.execute();  // PropertyChangeListener で対応
////↑
  task.execute();
}

helper/DBTask.java

////↓
/**
 * non thread version of execute
 * Thread を使わないで,Task が終了するまで待って欲しいとき用
 * @return
 */
public T executeInForeground() {
  T ret = null;
  try {
    ret = doInBackground();
    succeeded(ret);
  } catch (Exception ex) {
    System.out.println(ex);
  }
  return ret;
}
////↑

client/EditorFrame.java

public void close() {
        
  if (mode == EditorMode.EDITOR) {

    if (editor.isDirty()) {
  ・
  ・
       switch (option) {
                    
        case 0:
          editor.save();
          //// editor が Dirty でなければ(セーブ完了していれば),EditorFrame を閉じる
          //// if (!editor.isDirty()) stop(); // PropertyChangeListener で対応
          break;

        case 1:
          stop();
          break;
  ・
  ・

client/ChartMediator.java

class FocusPropertyChangeListener implements PropertyChangeListener {

  public void propertyChange(PropertyChangeEvent e) {
    String prop = e.getPropertyName();
    logger.debug("focusManager propertyChange :" + prop);
    if ("focusOwner".equals(prop)) {

    // 自分の component からの propertyChange だけ処理するようにする
    Window w = ((KeyboardFocusManager) e.getSource()).getActiveWindow();
    if ((w != null) && (w == chart.getFrame())) {
      Component comp = (Component) e.getNewValue();
      if (comp instanceof JTextPane) {
        Object obj = ((JTextPane) comp).getClientProperty("kartePane");
        if (obj != null && obj instanceof KartePane) {
          setCurKarteComposit((KarteComposite) obj);
        }
      } else if (comp instanceof KarteComposite) {
        setCurKarteComposit((KarteComposite) comp);
      }
    }
  }
}

« マスタ検索画面の結果リストの文字色 | トップページ | ScheduledExecutorService の使い回し »

OpenDolphin」カテゴリの記事