2015年1月24日土曜日

[Java][batch]Javaプログラムをバッチファイルから実行する(AppMain.java)

別の記事で掲載したバッチファイルから呼び出されるJava側の入り口のクラスを記述する。

AppMainはmainメソッドを持つプログラムエントリポイントとなるクラスである。
ここではCommandInterfaceというインタフェースによって、複数のJavaクラスの呼び分けができるようになっている。

Javaクラスをバッチから呼び出したい場合、全てのクラスにmainを記述するのは移植性の面で問題がある。mainメソッドはstaticであるため、ついついロジック本体のクラスをnewせずにメンバ等も全てstaticに書いてしまいがちである。

入り口はstaticなmainメソッドでもよいが、実際にはそこから複数のロジックを呼び出したい。
そのような場合は、入り口クラスに呼び出したいロジッククラスの完全修飾名を渡し、更に必要な引数も渡せる仕組みが必要である。

入り口が String[] args というパラメータしか取れないので、これと全く同じ戻り値と引数を持つインターフェースを作成し、個々のロジッククラスを呼び出すように記述している。

CommandInterface他のソースコードは、別の記事として掲載する。

<以下、ソースコード>
package app;

import org.apache.log4j.Logger;

import cmd.CommandInterface;
import exception.AppException;

/**
 * プログラムエントリポイント
 */
public class AppMain {

    /** ログ */
    private static Logger log = Logger.getLogger(AppMain.class.getName());

    /**
     * プログラムエントリポイント
     *
     * @param args
     *            コマンドライン引数
     */
    public static void main(String[] args) {

        try {
            // コマンドライン引数が指定されていない場合は、エラーとする
            if (args == null || args.length == 0) {
                throw new AppException("コマンドライン引数がありません。");
            }

            // 第1引数がクラスでない場合は、エラーとする
            CommandInterface ommandInterface = null;
            try {
                Class<?> clazz = Class.forName(args[0]);
                ommandInterface = (CommandInterface) clazz.newInstance();
            } catch (Exception e) {
                writeStarckTraceLog(e);
                return;
            }

            // メイン処理を呼び出す
            ommandInterface.doMain(args);

        } catch (AppException e) {
            writeStarckTraceLog(e);

        } catch (Exception e) {
            writeStarckTraceLog(e);
        }
    }

    /**
     * 例外のスタックトレースをログに記録する。
     *
     * @param e
     *            例外
     */
    private static void writeStarckTraceLog(Exception e) {
        String crlf = System.getProperty("line.separator");
        StringBuffer msg = new StringBuffer(e.getClass().getName() + " "
                + e.getMessage() + crlf);
        StackTraceElement[] elements = e.getStackTrace();
        if (elements != null && elements.length > 0) {
            for (StackTraceElement element : elements) {
                msg.append("\t" + element.toString() + crlf);
            }
        }
        log.error(msg);
    }
}

[Java][log4j]設定ファイルの書き方サンプル

log4jの設定ファイルのサンプルを記述する。
1MBのファイルを最大5ファイルローテーションさせる書き方は、次の通り。

<log4j.properties>
# Cyclic log specification
log4j.appender.LOGFILE=org.apache.log4j.RollingFileAppender
log4j.appender.LOGFILE.MaxFileSize=1MB
log4j.appender.LOGFILE.MaxBackupIndex=5

# log file name
log4j.appender.LOGFILE.File=cmdtool.log

# append mode
log4j.appender.LOGFILE.Append=true

# log level
log4j.appender.LOGFILE.Threshold=DEBUG

# logging pattern
log4j.appender.LOGFILE.layout=org.apache.log4j.PatternLayout
log4j.appender.LOGFILE.layout.ConversionPattern=%d{yyyy/MM/dd HH:mm:ss.SSS} [%p] %C{1}(%L) %m %n

# output LOGFILE by log level DEBUG
log4j.rootCategory=DEBUG, LOGFILE

<実際に出力されるログのサンプル>
2015/01/24 23:43:13.243 [INFO] TestClass(10) ログ文字列

「TestClass」はログ記録を実施したクラス、横の数字は行番号、「ログ文字列」はログに出力した内容となっている。

[Java][batch]Javaプログラムをバッチファイルから実行する

バッチファイルからJavaを呼び出す方法について記述する。
バッチファイルからJavaコマンドを呼び出す書式は、次の通り。
java -cp [クラスパス] [実行するクラスの完全修飾名] [引数1] [引数2] [引数3]・・・

クラスパスはカレントディレクトリを基準とした相対パスで記述するのが一般的。
Windowsの場合、パス名はセミコロン(;)で区切る。
例えば次のように記述した場合、
java -cp .;bin;lib\log4j.jar

ピリオド(.)はカレントディレクトリを指すので、「カレントディレクトリ」、「binディレクトリ」、「libディレクトリ配下のlog4j.jar」という3つのパスを指定していることになる。
クラスを動かすために必要な全てのクラスパスが通っていないとクラスが見つからないエラーになるので注意を要する。

バッチファイルの内容は、次の通り。 (以下のサンプルでは、絶対パスをバッチファイル内に記述しないために、外部ファイルからバッチ内で使う可変引数を読み込む)
呼び出しているクラスはapp.AppMainというクラスであり、cmd.FileFinderやcmdFilePickerなどのクラスはapp.AppMainから呼び出される個別機能である。(入り口さえ作っておけば、複数機能を同じバッチから呼び出せることを示すためのサンプル)

呼び出しているapp.AppMain他のクラスは、別の記事に掲載する。

<[batch.properties]バッチ内で可変引数として読み込ませる値>
JAVA_CMD=C:\pleiades\java\6\bin\java.exe
LOG4J_JAR=lib\org.apache.log4j_1.2.15.v201012070815.jar

<[doCmd.bat]バッチファイルの内容>
@echo off
rem 外部ファイルから可変引数を呼び出す
for /f "tokens=1,2 delims==" %%a in ('findstr JAVA_CMD batch.properties') do set JAVA_CMD=%%b
for /f "tokens=1,2 delims==" %%a in ('findstr LOG4J_JAR batch.properties') do set LOG4J_JAR=%%b

rem コマンドラインからJavaクラスを呼び出すための変数設定
set CLASS_PATH=.;bin;%LOG4J_JAR%
set APP_MAIN=app.AppMain
set CMD_TOOL=%JAVA_CMD% -cp %CLASS_PATH% %APP_MAIN%

rem ファイル検索コマンド
set FIND_FILE=%CMD_TOOL% cmd.FileFinder

rem ファイル抽出コマンド
set PICK_UP_FILE=%CMD_TOOL% cmd.FilePicker

rem ファイル検索を実施する。
rem 第1引数 : 検索対象ディレクトリ名
rem 第2引数 : 検索するファイル名(正規表現)
rem 第3引数 : 検索結果出力ファイル名
rem %FIND_FILE% "path1" "pattern" "resultfilename"

rem ファイルを抽出する
rem 第1引数 : 検索対象ディレクトリ名
rem 第2引数 : 検索するファイル名(正規表現)
rem 第3引数 : 検索結果出力先フォルダ名
%PICK_UP_FILE% "path1" "pattern" "path2"

pause

2015年1月17日土曜日

[業務知識][生命保険]告知書

 保険契約締結時に、健康状態に問題がないか被保険者に申告してもらうための書類を指す。「がん、心臓病、脳卒中などの病気にかかったことがないか」「過去1年に入院したことがないか」「過去3年以内に手術したことがないか」など、被保険者の健康状態に関する質問で構成される。告知書は保険契約を締結できるかどうかの判断に使われる重要な資料であり、虚偽の申告をすると保険金が受け取れないなどの問題が発生する。

[業務知識][生命保険]引受

 生命保険会社が被保険者との契約を締結することを保険を引き受けるという。
 保険の業務において「引受」とは、保険契約締結の業務全般を指す。引受は保険会社本体が行う。保険会社本体の営業職や保険代理店が営業を行った結果、保険契約を締結するという手順が一般的であるが、近年ではインターネット上から被保険者本人が応募して引受業務が発生することが多くなっている。
 電子計算機システムによる引受は営業のためのコストが大幅に削減できることから、保険料の低減・顧客サービスの向上にもつながるとされ、新たな保険契約の形式として注目されている。

[業務知識][保険]保険者と被保険者、受取人、指定代理人

 「保険者」とは保険を提供する側のことで保険会社のことを指す。「被保険者」とは保険に加入する側のことを指す。これは生命保険、損害保険の別に関わらず、保険業務全般に共通する用語である。ただし生命保険は人に対して保険をかけるが、損害保険は人以外のものに保険をかけるので注意が必要である。たとえば火災保険ならば家、自動車保険ならば車に保険がかかる。損保の場合は補償を受け取る者、という意味で被保険者となる。
 「受取人」は保険金を受け取る人を指す。生命保険の被保険者が死亡した、などの理由により保険金を受け取れる状態となったときに、受取人に指定されている人が保険金を受け取る。従来型の生命保険は被保険者が死亡した場合、残された家族が経済的に困らないように、という意味を込めて通常は配偶者や子供が受取人となっていることが多かった。
 しかし近年では医療保険などのように、被保険者本人が病気になって困ったときお金を受け取れるようにするタイプの保険も多く、その場合は被保険者と受取人が同じになる。また余命を宣告されたときに、生きているうちに保険金を受け取りたい」というニーズに対応し、生前に本人が保険金を受け取ることができる保険も増えてきた。この「生きているうちに」というのは「リビングニーズ特約」と呼ばれ、保険契約の特約として付与できることが多い。
 「指定代理人」とは、本人の代わりに保険金を受け取れる代理人のことである。たとえば医療保険やガン保険など、被保険者と受取人が同じである契約の場合、保険金を受け取れる状態になっても本人の体調が悪くて保険金の請求ができない可能性がある。そのような場合に、指定代理人が本人に代わって保険料を受け取れるようにするための仕組みである。
 かつてはガンであることを本人に知らせずに治療のための保険金を受け取るために使う、という意味もあったようだが、今では指定代理人の仕組みをこのように使うことはまずない。近年、日本はアメリカと同じように急激に訴訟社会化しており、ガンであるのに「ガンではない」と医者が言うことは、裁判で負けるという理由でありえなくなった。かつては「ガン=死」というイメージから、本人への告知はなるべく行わないことも多かったそうだが、現在は末期ガンなら「末期ガンです」と本人に隠さず告知するのが、医療の常識となっている。

2012年7月26日木曜日

[Java][SV]SVファイルIO(ソースコード)

package util;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

/**
 * セパレーテッドバリューファイル
 */
public class SvFile {

    /** デリミタ */
    private String delm = null;

    /** SV行データリスト */
    List<List<String>> svLineList = null;

    public List<List<String>> getSvLineList() {
        return svLineList;
    }

    public void setSvLineList(List<List<String>> svLineList) {
        this.svLineList = svLineList;
    }

    /**
     * コンストラクタ
     */
    public SvFile() {
    }

    /**
     * 指定されたファイルのデータを読み込みます。デリミタを指定しないコンストラクタの場合、デリミタはタブとなります。
     *
     * @param fileName
     *            ファイル名
     * @throws IOException
     */
    public void loadFile(String fileName) throws IOException {

        // SVファイルをロードする
        loadFile(fileName, "\t", false);
    }

    /**
     * デリミタを区切り文字として、指定されたファイルのデータを読み込みます。最初の行は読み飛ばしません。
     *
     * @param fileName
     *            ファイル名
     * @param delm
     *            デリミタ
     * @throws IOException
     */
    public void loadFile(String fileName, String delm) throws IOException {

        // SVファイルをロードする
        loadFile(fileName, delm, false);
    }

    /**
     * デリミタを区切り文字として、指定されたファイルのデータを読み込みます。
     *
     * @param fileName
     *            ファイル名
     * @param delm
     *            デリミタ
     * @param skipFirstLine
     *            最初の行を読み飛ばす場合はtrue、そうでなければfalse
     * @throws IOException
     */
    private void loadFile(String fileName, String delm, boolean skipFirstLine)
            throws IOException {

        // デリミタを記憶しておく
        this.delm = delm;

        // SV行データリストを初期化する
        setSvLineList(new ArrayList<List<String>>());

        // ファイルを開く
        BufferedReader br = null;
        try {
            br = new BufferedReader(new FileReader(new File(fileName)));

            // 最初の行をスキップする設定の場合は、読み捨てを行う
            String line = "";
            if (skipFirstLine) {
                line = br.readLine();
            }

            // 全ての行を処理するまでループ
            while ((line = br.readLine()) != null) {

                // SV行のデータをロードする
                loadLine(line);
            }

            // 最後に必ずファイルを閉じる
        } finally {
            if (br != null) {
                br.close();
            }
        }
    }

    /**
     * SVファイルの行データをロードします。SV値は改行なし、ダブルクオートによるエスケープもなしと前提します。
     *
     * @param line
     *            行データ
     */
    private void loadLine(String line) {

        // 文字列をスプリッタで分割する
        String[] svValArray = line.split(delm);

        // 行データ内の全てのSV値を処理するまでループ
        List<String> svValList = new ArrayList<String>();
        for (String svVal : svValArray) {

            // 【重要!】null値を検出した場合は空文字列を設定する
            if (svVal.equals("(null)")) {
                svValList.add("");
            }

            // 【重要!】SV値はダブルクオートで囲むルールとする
            else if (svVal.length() == "".length()) {
                svValList.add("");
            } else if (svVal.startsWith("\"") && svVal.endsWith("\"")) {
                svValList.add(svVal.substring(1, (svVal.length() - 1)));
            }
        }

        // リストに行データを追加する
        getSvLineList().add(svValList);
    }

    /**
     * 引数で指定されたSV行データリストで既存のデータを上書きします。
     *
     * @param fileName
     *            ファイル名
     * @param delm
     *            デリミタ
     * @param svLineList
     *            SV行データリスト
     * @throws IOException
     */
    public void save(String fileName, List<List<String>> svLineList)
            throws IOException {

        // セーブする
        save(fileName, "\t", svLineList);
    }

    /**
     * 引数で指定されたSV行データリストで既存のデータを上書きします。
     *
     * @param fileName
     *            ファイル名
     * @param delm
     *            デリミタ
     * @param svLineList
     *            SV行データリスト
     * @throws IOException
     */
    public void save(String fileName, String delm, List<List<String>> svLineList)
            throws IOException {

        BufferedWriter bw = null;
        try {
            // ファイルを書き込み用に開く
            bw = new BufferedWriter(new FileWriter(new File(fileName)));

            // 全ての行データを処理するまでループ
            for (List<String> svLine : svLineList) {

                // SV行データの各カラムをデリミタで接続する
                StringBuffer sb = new StringBuffer("");
                for (String column : svLine) {

                    // 1つ以上のカラムをSV行に設定している場合は、区切り文字を入れる
                    if (sb.length() > 0) {
                        sb.append(delm);
                    }

                    // 【重要!】空文字列はnull値として記録する
                    if (column == null || column.length() == 0) {
                        sb.append("(null)");
                    }

                    // 【重要!】SV値はダブルクオートで囲むルールとする
                    else {
                        sb.append("\"" + column + "\"");
                    }
                }

                // ファイルに書き込みを行い、最後に改行を入れる
                bw.write(sb.toString());
                bw.newLine();
            }

        } finally {

            // ファイルをクローズする
            bw.close();
        }
    }
}