• 2019年5月1日水曜日
アリスト戦記
アリスト戦記 https://blog.aristo-solutions.net/2019/05/javatemplatebatch.html

バッチの二重起動チェックを作成

Javaでバッチが二重起動しないようにするためのチェックは、大抵はファイルで行う。

ファイルを作成し、ロックを取得し、ロックが取れればOK、取れなければNGという判定ロジックだ。

基本ソース

ロジックの根幹を成す「ロック」は以下で実現出来る。

      FileOutputStream fos = new FileOutputStream("R:\\/lock");
      FileChannel fc = fos.getChannel();
      FileLock fl = fc.tryLock();

      if (fl == null) {

         log.error("多重起動です。");

      } else {

         log.info("正常です。");

      }

      fl.close();
      fc.close();
      fos.close();

でも、これだと余りにソースが汚い。

そこで本番ではロック機能を別クラスに切り出して、本体のソースはこんなに汚くならないようにする実装がオススメである。

二重起動チェッカー

チェックツール側

僕が作った二重起動防止ツールはこちら。
ツール本体だ。
このクラスだけで動くように作っているから、そのまま持って行ってくれ。

/**
 * 二重起動チェックツール
 *
 * @author アリストマスター
 *
 */
public class DoubleStartChecker implements Closeable {

   /** FileOutputStream */
   private FileOutputStream fos = null;

   /** FileChannel */
   private FileChannel fc = null;

   /** FileLock */
   private FileLock fl = null;

   /**
    * ファイル二重起動チェック
    *
    * @param lockFilePath ロック判定に使用するファイルのパス
    * @return true:正常 / false:error
    * @throws IOException
    */
   public boolean check(String lockFilePath) throws IOException {

      fos = new FileOutputStream(lockFilePath);
      fc = fos.getChannel();
      fl = fc.tryLock();

      if (fl == null) {

         return false;

      } else {

         return true;

      }
   }

   /* (非 Javadoc)
    * @see java.io.Closeable#close()
    */
   @Override
   public void close() throws IOException {

      if (fl != null) {
         fl.close();
      }

      if (fc != null) {
         fc.close();
      }

      if (fos != null) {
         fos.close();
      }

   }

}

なんてことはない。
元々のソースを「チェック」と「クローズ」に分解しただけだ。
インターフェース「Closable」をセットし忘れないことがコツ。

呼び出し側

この二重起動チェッカーを使うことで、呼び出し側はこんな風に書ける。

   /**
    * バッチの二重起動チェック
    *
    * @param args 入力引数
    * @throws InputParameterCheckException
    * @throws IOException
    */
   @Override
   protected void doubleStartCheck() throws IOException {

      log.debug("doubleStartCheck:{}", "start");

      try (DoubleStartChecker dsc = new DoubleStartChecker()) {

         if (!dsc.check(lockFilePath)) {

            throw new DoubleStartCheckException("DoubleStartCheckSampleExecuterが二重起動しています。");

         } else {

            log.info("二重起動していません。");

         }

      }

      log.debug("doubleStartCheck:{}", "end");

   };


随分綺麗になったでしょ?

二重ロックでfalseになった場合、僕は例外を投げるのが好みに合っているが、そうではない使い方をしたいは例外を投げる部分を違う内容に書き換えたら良い。

注意事項

注意点は、ツール側の「tryLock」だ。

どうも、ガベージコレクションが発動してインスタンスがクリアされてしまうと、ロックが解除されてしまうというケースがあるという噂を聞いたことがある。(自分自身では再現出来ていないが)

しかし、上記の呼び出し方だと「DoubleStartChecker」はずっと残っており、クリアされるタイミングは無いから安心である。

tryで呼び出すのを忘れないようにな。

0 件のコメント:

コメントを投稿