« 2009年9月 | トップページ | 2009年11月 »

2009年10月

2009年10月29日 (木)

OpenDolphin-1.3.0.3 公開(Java 1.6 専用)

Java 1.6 専用化に合わせて,ソースをかなりいじったため,新しいソースを公開することにした。

ソースは Macintosh バージョンと Windows バージョンに分けた。使っている IDE はいずれも NetBeans 6.7.1。Windows バージョンは,事務で使っているだけなので,Macintosh 版に加えたカスタマイズの一部しか反映していおらず,カルテ編集の詳しい動作確認もしていない。


構築済みのクライアント (Java 1.6 専用)

JBoss Server デプロイ用 ear ファイルと xml ファイル (JBoss 4.2.3 + Java 1.6 専用)。


    ※ このソースには 09/10/31 以降の変更は反映されていません。

Drag & Drop のインターフェース

テーブルで Drag & Drop で項目の順番を変更する場合,Drop する場所が項目の隙間に示されるようにした。ドロップ場所が直感的に分かりやすい。基本的には JTable に setDropMode して TransferHandler をちょっと書き換えればできてしまうが,quaqua ではレンダラが対応していないので,MasterPanelTableCellRenderer.java を作った。

01medtable

  • MedicineTablePanel.java
  • RadItemTablePanel.java
  • ItemTablePanel.java
  • MasterItemTransferHandler.java
  • DiagnosisTablePanel.java
  • RegisteredDiagnosisTransferHandler.java
  • MasterPanelTableCellRenderer.java

 

スタンプ箱も,ドロップ場所が直感的に分かりやすい様にした。フォルダに重ねるとフォルダの中へ,フォルダの前後に置くとその場所にドロップできるようになっている。

Regular1

Regular2

Regular3

 

JTree の場合,こういう所が微妙だった。フォルダの上や下に入るのか,展開されたリストの一番上や一番下に入るのか。下線の左端がどこにあるかで判定するようにした。

Angled2

Angled1

  • StampTree.java
  • StampTreeDropTargetListener.java
  • StampTreeRenderer.java
  • StampTreeTransferHandler.java

結局 Java 1.6 へ移行

ずっと Java 1.5 で行くつもりだったが,ヒマだったのでやっぱり Java 1.6 に移行することにした。サーバ環境を jboss-4.2.3.GA + jdk6 に,クライアントも Snow Leopard の Java 1.6 環境にした。カルテ検索が動かなくなるなどいろいろあって,思ったより結構大変な作業になってしまった。しかし,苦労した甲斐あって,Java 1.6 にしてちょっと操作性がよくなったし,レスポンスも早くなったような感じもあるかもしれなくもない。

  • サーバは xen の dom-U で走っている Debian Etch である。Etch に Java 6 をインストールするには,backport を使う。
    1. /etc/apt/sources.list に以下を加える。
      deb http://www.backports.org/debian etch-backports main contrib non-free
      
      
    2. キーを取得
      # wget -O - http://backports.org/debian/archive.key | apt-key add -
      
      
    3. Java 1.6 インストール
      # aptitude update
      # aptitude install sun-java6-jdk
      
    4. JAVA_HOME を,Java 6 を使うように設定。
    5. ちょっと古いけど問題なく動いた
      $ java -version
      java version "1.6.0_07"
      Java(TM) SE Runtime Environment (build 1.6.0_07-b06)
      Java HotSpot(TM) Client VM (build 10.0-b23, mixed mode, sharing)
      
    6. JBoss 4.2.3 では,起動スクリプトでJBOSS_HOST のアドレスを書き換えてきちんと設定しないと接続を受け付けないので注意。 ダウンロード jboss423_init_debian.sh (3.2K)
  • Java 1.6 では swing-layout が標準で入っているので,lib から swing-layout-1.0.3.jar は外してかまわない。ただし,NetBeans で Matisse を使っているコード(LoginView.java 等の xxxView.java 系)では,強制的に swing-layout-1.0.3.jar が使われてしまう。Java 1.6 標準のものを使うには,全ての xxxView.java で,インスペクタのルートを右クリックして「プロパティー」を選択し,「レイアウト生成スタイル」を「標準 Java 6コード」に変更する。
  • java 1.6 では SwingWorker も標準になったので,swing-worker-1.1.jar も外せるはずなのだが, appframework-1.3.jar がこれを使う仕様のため外せない。Java 1.6 標準の SwingWorker を使うようにコンパイルし直したライブラリを使うと外せる。
    1. Swing Application Framework のサイト から,ソースをダウンロードして,NetBeans に読み込む。普通に「プロジェクトを開く」で開ける。
    2. AppFramework プロジェクトのライブラリから SwingWorker を削除すると,エラーマークが付く。
    3. org.jdesktop.application パッケージの Task.java と TaskMonitor.java の import 文から,エラーになっている文を削除し,import javax.swing.SwingWorker で置き換える。
    4. エラーマークが消えるので,構築する。これで,Java 1.6 標準の SwingWorker を使った AppFramework.jar ができあがり。ダウンロード AppFramework.jar (567.0K)
  • Java 1.6 では JTree.DorpLocation クラスが導入されて,JTree の項目と項目の間に挿入できるようになった。今までスタンプ箱は,「フォルダの上に置いたらフォルダの前に挿入,ALT を押していたらフォルダの中へ」という風にカスタマイズしており,スマートでないと常々思っていた。JTree.DropLocation を使えば,「フォルダの前後に置いたらフォルダの前後へ,フォルダに重ねたら中へ」という直感的なインターフェースにできるはずである。と簡単に思ってやってみたら実はそんなに簡単な話ではなく,結局ロジックを大幅に見直すことになってしまった。しかも JTree.DropLocation は使わずじまい。
  • さらに JTable.DropLocation も加わったので,JTable の行の入れ替え操作も,行の間に線が出て挿入場所が直感的に分かるようになった。これは JTree に比べるとずっと簡単に組み込めるのだが,なんと quaqua が対応しておらず,結局 renderer から作るはめに。
  • Java 1.6 では,TableRowSorter が標準で入っており,TableSorter.java を組み込まなくても JTable#setAutoCreateRowSorter(true) だけでできてしまう。
    • ただし,TableModel と Table の 対応関係がずれるので,int selected = table.convertRowIndexToModel(table.getSelectedRow()) とか置き換えなくてはならない。これに伴い,TableSorter.java を組み込む改変はやめて,setAutoCreateRowSorter(true) でソートするようにコードを書き直してみた。
    • そうすると,動的 startNumRow にしているために,ソートすると何も入っていない項目を含めてソートされてしまい,ソートしたとき何も入っていない項目が上の方に表示されてしまうというなさけない事態に。動的 startNumRow はとりやめ,代わりに,quaqua の JTable#putClientProperty("Quaqua.Table.Style", "striped") を設定して対応した。
    • この変更で,テーブル表示が quaqua 依存になってしまった。また,テーブルの範囲が項目がある範囲だけになったので,テーブルにドラッグ&ドロップする場合の動作など,細かいところに手を入れることになった。思ったより大変だった。
    • テーブル表示を quaqua に依存しない Windows バージョン(動的 startNumRow + TableSorter.java)と,quaqua に依存した Macintosh バージョンのソースを分けることにした。

2009年10月26日 (月)

カルテ保存後ウインドウを閉じる(多分完結編)

サーバとの通信に使う DBTask は,Chart が存在することを前提にした Thread になっている。カルテ保存後に EditorFrame を閉じるようにカスタマイズしてしまっため,EditorFrame を閉じた後すかさず Chart をショートカットキーを使って素早く閉じた場合など,DBTask が Chart を閉じた後にも残っていて,消えかけの Chart にアクセスする可能性があるのではないかということがとても気になっていた。(DBTask は普通は一瞬で終わるので問題ないはずなのだが,たまに時間がかかったりする可能性がある)

 

 そこで,データベースにアクセスするスレッドをまとめて ExecutorService で管理し,さらに,その Executor の task を Chart を閉じるときに shutdown し,shutdown されたことを確認してから Chart を閉じるようにしてみた。

 

 まとめたスレッドの状況を調べてみたら,シェーマが多いカルテをものすごく素早く閉じたときに DBTask が残ることがあるのを確認できた。でも,普通に操作していたらまず問題にならないので,結局杞憂だったようだ。でもとりあえず,これで「カルテ保存後ウインドウを閉じる」の心配の種は根絶できたと思う。

2009年10月25日 (日)

FindBugs に指摘されたこと

  1. FindBugs 曰く「このメソッドは、データベースリソース(例えば、コネクションや結果セット)を生成しますが、それをフィールドに代入したり、他のメソッドに渡したり、戻り値として返送したりしておらず、このメソッドを起点とする実行経路の中にクローズが行われない経路があります」

    dao/SqlDaoBean.java

    protected int getHospNum() {
     ・
     ・        
      try {
        con = getConnection();
        st = con.createStatement();
        ResultSet rs = st.executeQuery(sql);
        if (rs.next()) {
          hospNum = rs.getInt(1);
        }
      }  catch (SQLException e) {
        processError(e);
      }  catch (Exception e) {
        processError(e);
      } finally {
    //↓ FindBugs に指摘された
        closeConnection(con);
        closeStatement(st);
    //↑
      }
     ・
     ・
    
  2. FindBugs曰く「このメソッドの中にあるswitch文には、同じコードがあります。単なるコードの重複かもしれませんが、コーディングミスの可能性もあります」

    client/KartePane.java 他

    private void controlMenus(ActionMap map) {
      // 各Stateはenableになる条件だけを管理する
      switch (curState) {
      
        case NONE:
          break;
    
        case SOA:
          // SOAPaneにFocusがありテキスト選択がない状態
          if (getTextPane().isEditable()) {
            map.get(GUIConst.ACTION_PASTE).setEnabled(canPaste());
            map.get(GUIConst.ACTION_INSERT_TEXT).setEnabled(true);
            map.get(GUIConst.ACTION_INSERT_SCHEMA).setEnabled(true);
          }
          break;
    
        case SOA_TEXT:
        case P_TEXT: // ←ここに持ってきた
          // SOAPaneにFocusがありテキスト選択がある状態
          map.get(GUIConst.ACTION_CUT).setEnabled(getTextPane().isEditable());
          map.get(GUIConst.ACTION_COPY).setEnabled(true);
          boolean pasteOk = (getTextPane().isEditable() && canPaste()) ? true : false;
          map.get(GUIConst.ACTION_PASTE).setEnabled(pasteOk);
          break;
    
        case P:
          // PPaneにFocusがありテキスト選択がない状態
          if (getTextPane().isEditable()) {
            map.get(GUIConst.ACTION_PASTE).setEnabled(canPaste());
            map.get(GUIConst.ACTION_INSERT_TEXT).setEnabled(true);
            map.get(GUIConst.ACTION_INSERT_STAMP).setEnabled(true);
          }
          break;
    
    //  case P_TEXT:  // こっちは pasteOk が boolean 宣言されないけど,同じことみたい
    //    // PPaneにFocusがありテキスト選択がある状態
    //    map.get(GUIConst.ACTION_CUT).setEnabled(getTextPane().isEditable());
    //    map.get(GUIConst.ACTION_COPY).setEnabled(true);
    //    pasteOk = (getTextPane().isEditable() && canPaste()) ? true : false;
    //    map.get(GUIConst.ACTION_PASTE).setEnabled(pasteOk);
    //    break;
    
  3. FindBugs 曰く「Exceptionが発生しないのにExceptionをキャッチしています。このtry-catchブロックはtry節の中でExceptionをスローしないのにExceptionのキャッチ節があり・・・」

    Exception を catch するのに,いろいろある Exception をいちいち catch するのは面倒くさいので,まとめて catch (Exception e) で処理していたのだが,FindBugs にそれはだめだと指摘された。そこで,ものすごい数だったけど全部 Exception を書き直してみたところ,確かに今まで見なかった Exception が catch されたりした。しかし,大きな問題になるようなものはなかったので,結局無駄骨だった。でも勉強になったからよしとする。

  4. FindBugs 曰く「compareTo(...)を宣言し、Object.equals()を使用しています。このクラスは、compareTo(...)を宣言していますがequals()をjava.lang.Objectから継承しています。一般にcompareToが0を返す条件は、equalsがtrueを返す条件と一致する必要があります。これを守らないと複雑で予測不可能な問題が、例えばPriorityQueueで発生するでしょう。Java 5では、PriorityQueue.remove()は、compareTo()を使用していますがJava 6では、equals()を使用しています」

    言われたとおり,(x.compareTo(y)==0) == (x.equals(y)) となるように,equals を実装する。必要ないような気もする。

    infomodel/AllergyModel.java 他

     /**
      * 同定日で比較する。
      * @param other 比較対象オブジェクト
      * @return 比較値
      */
    public int compareTo(Object other) {
      if (other != null && getClass() == other.getClass()) {
        String val1 = getIdentifiedDate();
        String val2 = ((AllergyModel)other).getIdentifiedDate();
        return val1.compareTo(val2);
      }
      return 1;
    }
    //↓
    @Override
    public boolean equals (Object other) {
      if ((other instanceof AllergyModel) && compareTo(other) == 0) {
        return true;
      } else {
        return false;
      }
    }
    //↑
    
  5. 他にもたくさん

2009年10月21日 (水)

Stamp あるいは Schema を編集しただけの場合

カルテ編集(modifyKarte)の時,テキストを編集しないでスタンプやシェーマだけ編集した場合 dirty 判定されない。そのため,シェーマだけを修正した場合などに,セーブする前にウインドウを閉じてしまって orz になることがあった。スタンプやシェーマを編集しただけの時も dirty になるようにした。

client/KartePane.java

protected void setDirty(boolean newDirty) {
//↓ stamp や schema の編集をしたら dirty にするため
//  if (newDirty != dirty) {
  if (dirty == false && newDirty == true) {
    dirty = newDirty;
    getParent().setDirty(dirty);
  }
}

client/StampHolder.java

public void edit() {
 ・
 ・        
    stampEditor.start();
//↓ stamp を edit したら,kartePane の Dirty 設定
    if (kartePane.getTextPane().isEditable()) {
      kartePane.setDirty(true);
    }

client/SchemaHolder.java

public void edit() {
 ・
 ・        
    Runnable awt = new Runnable() {
      public void run() {
        editor.start();
      }
    };
    EventQueue.invokeLater(awt);
//↓ schema edit 開始したら dirty と設定
    if (kartePane.getTextPane().isEditable()) {
      kartePane.setDirty(true);
    }
  }

2009年10月16日 (金)

FindBugs 導入

FindBugs 導入手順
  1. NetBeans の「ツール」→「プラグイン」→「設定」→「追加」
  2. プロバイダ名:SQE (java.net)
  3. URL:https://sqe.dev.java.net/updatecenters/sqe/updates.xml
  4. 「使用可能なプラグイン」で,SQE Java,SQE Update Center,SQE Platform にチェックを入れて「インストール」

2009年10月14日 (水)

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

カルテ保存後にウインドウを閉じる問題で,もう1ヶ所,気になる場所を発見。ここも,病名をデータベースに書き込んでいる途中で Chart を閉じてしまうと,DBTask.java 内部から Chart を呼んでいるところが,消えかけの Chart にアクセスしてしまう可能性があるのではないだろうか。ここも,DiagnosisPutTask が完了するまで待つことにした。【2009/10/18追記】他にも知らないところで DBTask が走っていると,消えかけの Chart にアクセスする可能性があるかもしれないので,もっと根本的な解決法を検討中。

 

client/DiagnosisDocument.java

/**
 * 新規及び変更された傷病名を保存する。
 */
@Override
public void save() {
 ・
 ・
  DocumentDelegater ddl = new DocumentDelegater();
  DiagnosisPutTask task = new DiagnosisPutTask(getContext(), addedDiagnosis, updatedDiagnosis, sendDiagnosis, ddl);
//task.execute();
//↓diagnosis を save 中に Chart が dispose されないように待つことにする
  task.executeInForeground();
}

2009年10月13日 (火)

スタンプ箱ロック

Stampbox

    スタンプ箱ロックは StampTreePanel.java に入れていたが,これだと各 StampTreePanel 独立になってしまう。大元の StampBoxPlugin.java に組み込んで,StampTreePanel の切換にかかわらず有効になるように変更した。StampTreePanel.java で lockBtn を作っている部分を StampBoxPlugin.java に移行し,Locked の状態は StampBoxPlugin で維持するようにする。そして,StampTree.java の isLocked の判断部分を stampBox.isLocked() で判断するようにした。

2009年10月 9日 (金)

いろいろ toFront する

受付リストで,既に開かれているカルテをダブルクリックした場合,そのカルテを前に持ってくる。

plugin/WatingListImpl.java

public void openKarte(PatientVisitModel pvtModel) {

//↓  既に開かれていれば,そのカルテを前に
  List<ChartImpl> allCharts = ChartImpl.getAllChart();
  for (ChartImpl chart : allCharts) {
    if (chart.getPatientVisit().getId() == pvtModel.getId()) {
      chart.getFrame().toFront();
    return;
    }
  }
//↑
  if (pvtModel != null && canOpen(pvtModel)) {
    getContext().openKarte(pvtModel);
  } else {
    Toolkit.getDefaultToolkit().beep();
  }
}

 

患者検索で,既に開かれているカルテをダブルクリックした場合,そのカルテを前に持ってくる。

plugin/PatientSearchImpl.java

public void openKarte() {
 ・
 ・
//↓  既に開かれていれば,そのカルテを前に
  if (isKarteOpened(getSelectedPatinet())) {
    List<ChartImpl> allCharts = ChartImpl.getAllChart();
    for (ChartImpl chart : allCharts) {
      if (chart.getPatient().getId() == getSelectedPatinet().getId()) {
        chart.getFrame().toFront();
      }
    }
  }
//↑
}

 

スタンプやシェーマを挿入した場合,そのカルテを前に持ってくる。

client/KartePane.java

public void propertyChange(PropertyChangeEvent e) {
 ・
 ・
    if (o != null) {
      // 編集された Stamp をこのペインに挿入する
      ModuleModel stamp = (ModuleModel) o;
      stamp(stamp);
    }
  }
////↓ stamp が挿入されたら toFront する。
  this.getParent().getContext().getFrame().toFront();
}

 

EditorFrame を閉じたらインスペクタを前に出す。

client/EditorFrame.java

public void stop() {
  mediator.dispose();
  allEditorFrames.remove(this);
  try {
    ClientContext.getLocalStorage().save(getFrame().getBounds(), "editorFrameBounds.xml");
            
  } catch (IOException ex) {
    ex.printStackTrace();
  }
  getFrame().setVisible(false);
  getFrame().dispose();
//↓
  realChart.getFrame().toFront();
}

2009年10月 8日 (木)

本当に終了しますか?

ウインドウを閉じるのに「コマンドーW」のショートカットを多用するのだが,時々間違って「コマンドーQ」を押して,orz になることがあった。「本当に終了しますか」ダイアログを出すことにした。

 

client/Dolphin.java

public void processExit() {

  if (isDirty()) {
    alertDirty();
    return;
  }
//↓
  int ans = JOptionPane.showConfirmDialog( null,
    "本当に終了しますか",
    "終了確認",
    JOptionPane.YES_NO_OPTION,
    JOptionPane.WARNING_MESSAGE);
  if (ans != JOptionPane.YES_OPTION) {
      this.getFrame().toFront();
      return;
    }
//↑

2009年10月 5日 (月)

StampEditorDialog を立ち上げたときのフォーカス

StampEditorDialog でエディタを立ち上げたとき,MasterPanel の enter() が呼ばれても,keywordField にフォーカスが取れない。原因を調べてみたら,StampEidtorDialog の initialize() がスレッドで実行されているためだった。直接 initialize() するとうまくいくのだが,わざわざスレッドになっているのには意味があるはずなので,何か不具合が起こる可能性がある。

 

client/StampEditorDialog.java

public void start() {
/*
  Runnable initilizer = new Runnable() {
    public void run() {
      initialize();
    }
  };
  Thread t = new Thread(initilizer);
  t.setPriority(Thread.NORM_PRIORITY);
  t.start();
  //initialize();
*/
  initialize();
}

2009年10月 3日 (土)

MasterPanel のキーワード保存

MasterPanel でマスタ検索をするとき,用法に薬の名前を入れてしまって,orz になることがあったので,そのままタブを切り替えてもキーワードが保存されるようにした。

 

order/MasterPanel.java

 ・
 ・
//↓ keywordField の文字を保存する static 変数
private static String keywordFieldText;
 ・
 ・
public MasterPanel(final String master) {
 ・
 ・
//↓
  keywordFieldText = "";      
  // 初期化する
  initialize();
}
 ・
 ・
public void enter() {
  setItemCount(tableModel.getObjectCount());
  //↓
  EventQueue.invokeLater(new Runnable() {
    public void run() {
      keywordField.setText(keywordFieldText);
      keywordField.requestFocusInWindow();
      keywordField.selectAll();
      }
  });
}
//↑
 ・
 ・

2009年10月 1日 (木)

Stand Alone PVT Server

増田内科様のカスタマイズを見ると,PVT Server をサーバ機で走らせるようにされている。自分でも同じことをしたいと思ったのだが,当方のサーバは X をインストールしていない Debian Etch なので,OpenDolphin のクライアントはそのままでは走らない。しかたがないので,console で OpenDolphin の PVT Server 部分だけを起動できるように StandAlonePVTServer を作成した。サーバマシン起動時に起動スクリプトで立ち上げることにして,JBoss サーバが立ち上がるまで 10 秒ごとにログインをリトライするようにした。

 

server/StandAlonePVTServer.java

package open.dolphin.server;

import java.util.Iterator;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.prefs.Preferences;
import open.dolphin.client.ClientContext;
import open.dolphin.client.ClientContextStub;
import open.dolphin.delegater.UserDelegater;
import open.dolphin.infomodel.UserModel;
import open.dolphin.plugin.PluginLoader;
import open.dolphin.project.DolphinPrincipal;
import open.dolphin.project.Project;
import open.dolphin.project.ProjectStub;
import org.apache.log4j.Logger;

/**
 * Console verion of PVT Server
 * @author pns
 */
public class StandAlonePVTServer {
  private static final String DEFAULT_FACILITY_OID = "1.3.6.1.4.1.9414.10.1";
  private final String NODE_NAME = "/open/dolphin/project";
  private PVTServer pvtServer;
  private Logger pvtLogger;
  private ScheduledFuture timerHandler;
  
  private StandAlonePVTServer(String hostIp, String userId, String password) {

    // ClientContext を生成する
    ClientContextStub stub = new ClientContextStub();
    ClientContext.setClientContextStub(stub);

    pvtLogger = ClientContext.getLogger("pvt");

    pvtLogger.info("Host Address: " + hostIp);
    pvtLogger.info("User ID: " + userId);

    // set dolphin server address
    Preferences prefs = Preferences.userRoot().node(NODE_NAME);
    prefs.put(Project.HOST_ADDRESS, hostIp);

    // pref に設定した host address を Project に書き込み
    Project.setProjectStub(new ProjectStub());

    // facility ID と user ID をセット
    DolphinPrincipal principal = new DolphinPrincipal();
    principal.setFacilityId(DEFAULT_FACILITY_OID);
    principal.setUserId(userId);

    // 10秒ごとにログインをトライする
    Login l = new Login(principal, password);
    ScheduledExecutorService schedule = Executors.newSingleThreadScheduledExecutor();
    timerHandler = schedule.scheduleWithFixedDelay(l, 0, 10, TimeUnit.SECONDS);
  }

  protected class Login implements Runnable {
    private UserDelegater userDlg;
    private UserModel userModel;
    private DolphinPrincipal principal;
    private String password;

    public Login(DolphinPrincipal principal, String password) {
      userDlg = new UserDelegater();
      this.principal = principal;
      this.password = password;
    }

    public void run() {
      userModel = null;
      try {
        userModel = userDlg.login(principal, password);
      } catch (Exception ex) {
        System.out.println(ex);
      }
      if (userModel != null) {
        startPVTServer();
        timerHandler.cancel(true);
        pvtLogger.info("Login process completed");
      } else {
        pvtLogger.info("Login failed. Retry in 10 sec.");
      }
    }
  }

  private void startPVTServer() {

    // plugin loader
    PluginLoader loader = PluginLoader.load(PVTServer.class, ClientContext.getPluginClassLoader());
    Iterator iter = loader.iterator();
    if (iter.hasNext()) {
      pvtServer = iter.next();
      pvtServer.setContext(null);
      pvtServer.start();
    }

    // ^C でサーバを止めるための設定
    Runtime.getRuntime().addShutdownHook(new Thread(){
      @Override
      public void run() {
        pvtLogger.info("Shutdown process starts.");
        pvtServer.stop();
      }
    });
  }

  public static void main(String[] args) {
    String usage = "Usage: java -cp OpenDolphin-1.3.0.X.jar open.dolphin.server.StandAlonePVTServer -Uxxxx -Pxxxx -Sxxx.xxx.xxx.xxx";
    String userId = "";
    String userPassword = "";
    String serverAddress = "";
    for (String arg : args){
      if ("-U".equals(arg.substring(0, 2))){
        userId = arg.substring(2, arg.length());
      }
      if ("-P".equals(arg.substring(0, 2))){
        userPassword = arg.substring(2, arg.length());
      }
      if ("-S".equals(arg.substring(0,2))){
        serverAddress = arg.substring(2, arg.length());
      }
    }
    if (!userId.equals("") && !userPassword.equals("") && !serverAddress.equals("")) {
      new StandAlonePVTServer(serverAddress, userId, userPassword);
    } else {
      System.out.println(usage);
      System.exit(1);
    }
  }
}

起動スクリプト pvtserver.sh

#!/bin/bash

OPENDOLPHIN="OpenDolphin-1.3.0.2.jar"
PVTSERVER="open.dolphin.server.StandAlonePVTServer"
PVTSERVER_HOME="/home/dolphin/opendolphin-client"
PVTSERVER_USER="dolphin"
JBOSS_HOST="192.168.1.100"
JAVA="/usr/bin/java"
USER="admin"
PASS="admin"

SUBIT="su - $PVTSERVER_USER -c "

# log
PVTSERVER_CONSOLE=$PVTSERVER_HOME/log/console.log

if [ -n "$PVTSERVER_CONSOLE" -a ! -d "$PVTSERVER_CONSOLE" ]; then
  # ensure the file exists
  touch $PVTSERVER_CONSOLE
  if [ ! -z "$SUBIT" ]; then
    chown $PVTSERVER_USER $PVTSERVER_CONSOLE
  fi 
fi

if [ -n "$PVTSERVER_CONSOLE" -a ! -f "$PVTSERVER_CONSOLE" ]; then
  echo "WARNING: location for saving console log invalid: $PVTSERVER_CONSOLE"
  echo "WARNING: ignoring it and using /dev/null"
  PVTSERVER_CONSOLE="/dev/null"
fi

# define what will be done with the console log
PVTSERVER_CONSOLE=${PVTSERVER_CONSOLE:-"/dev/null"}

PVTSERVERSH="$JAVA -cp $OPENDOLPHIN $PVTSERVER -U$USER -P$PASS -S$JBOSS_HOST"
PVTSERVER_CMD_START="cd $PVTSERVER_HOME/; $PVTSERVERSH"

if [ ! -d "$PVTSERVER_HOME" ]; then
  echo PVTSERVER_HOME does not exist as a valid directory : $PVTSERVER_HOME
  exit 1
fi

# echo PVTSERVER_CMD_START = $PVTSERVER_CMD_START

function procrunning() {
   procid=0
   PVTSERVERSCRIPT=$(echo $PVTSERVERSH | awk '{print $1}')
   for procid in `/bin/pidof -x "$PVTSERVERSCRIPT"`; do
       ps -fp $procid | grep "${PVTSERVERSH% *}" > /dev/null && pid=$procid
   done
}


stop() {
    pid=0
    procrunning
    if [ $pid = '0' ]; then
        echo -n -e "\n * No PVTServer is currently running\n"
        exit 1
    fi

    RETVAL=1

    # If process is still running
    for id in `ps --pid $pid | awk '{print $1}' | grep -v "^PID$"`; do
       if [ -z "$SUBIT" ]; then
           kill $id
       else
           $SUBIT "kill $id"
       fi
    done

    sleep=0
    while [ $sleep -lt 5 -a $RETVAL -eq 1 ]; do
        echo -n -e "\n * waiting for processes to stop";
        sleep 1
        sleep=`expr $sleep + 10`
        pid=0
        procrunning
        if [ $pid == '0' ]; then
            RETVAL=0
        fi
    done

    # Still not dead... kill it

    count=0
    pid=0
    procrunning

    if [ $RETVAL != 0 ] ; then
        echo -e "\n * Timeout: Shutdown command was sent, but process is still running with PID $pid"
        exit 1
    fi

    echo
    exit 0
}

case "$1" in
start)
    echo " * Starting PVTServer, check $PVTSERVER_CONSOLE"
    cd $PVTSERVER_HOME/
    if [ -z "$SUBIT" ]; then
        eval $PVTSERVER_CMD_START >${PVTSERVER_CONSOLE} 2>&1 &
    else
        $SUBIT "$PVTSERVER_CMD_START >${PVTSERVER_CONSOLE} 2>&1 &" 
    fi
    ;;
stop)
    stop
    ;;
restart)
    $0 stop
    $0 start
    ;;
*)
    echo "usage: $0 (start|stop|restart|help)"
esac

« 2009年9月 | トップページ | 2009年11月 »