TERASOLUNA Batch Framework for Java (5.x) Development Guideline - version 5.5.0.RELEASE, 2023-3-31, commit-id:e772ac8
> INDEX

ブランクプロジェクトとは

ブランクプロジェクトとは、Spring BatchやMyBatis3をはじめとする各種設定をあらかじめ行った開発プロジェクトの雛形であり、 アプリケーション開発のスタート地点である。
本ガイドラインでは、シングルプロジェクト構成のブランクプロジェクトを提供する。
構成の説明については、プロジェクトの構成を参照。

TERASOLUNA Server 5.xとの違い

TERASOLUNA Server 5.xはマルチプロジェクト構成を推奨している。 この理由は主に、以下の様なメリットを享受するためである。

  • 環境差分の吸収しやすくする

  • ビジネスロジックとプレゼンテーションを分離しやすくする

しかし、本ガイドラインではTERASOLUNA Server 5.xと異なりシングルプロジェクト構成としている。

これは、前述の点はバッチアプリケーションの場合においても考慮すべきだが、 シングルプロジェクト構成にすることで1ジョブに関連する資材を近づけることを優先している。
また、バッチアプリケーションの場合、 環境差分はプロパティファイルや環境変数で切替れば十分なケースが多いことも理由の1つである。

プロジェクトの作成

Maven Archetype Pluginarchetype:generateを使用して、プロジェクトを作成する方法を説明する。

作成環境の前提について

以下を前提とし説明する。

  • Java SE Development Kit 17

  • Apache Maven 3.x

    • インターネットに繋がっていること

    • インターネットにプロキシ経由で繋ぐ場合は、Mavenのプロキシ設定 が行われていること

  • IDE

    • Spring Tool Suite / Eclipse 等

JavaConfigの採用

バージョン5.5.0.RELEASEのブランクプロジェクトでは、ジョブ定義以外のBean定義として JavaConfig または XMLConfig のいずれかを選択することができる。

  • JavaConfig:artifactIdterasolunba-batch-archetypeを指定

  • XMLConfig:artifactIdterasolunba-batch-xmlconfig-archetypeを指定

ブランクプロジェクトで作成されるBean定義ファイルは下記となる。

表 1. ブランクプロジェクトのBean定義ファイル
Bean定義 terasolunba-batch-archetype terasolunba-batch-xmlconfig-archetype

ジョブ定義 *1

job01.xml

job01.xml

ジョブ定義以外

AsyncBatchDaemonConfig.java

async-batch-daemon.xml

JobBaseContextConfig.java

job-base-context.xml

LaunchContextConfig.java

launch-context.xml

  1. バージョン 5.5.0.RELEASE の経過措置として artifactIdに JavaConfigが選択された場合であっても、ジョブ定義はXMLConfigを使用する。

公式からの正式なアナウンスは現状ないものの、Springのドキュメントでコード例の説明がJavaConfigしかないケースが増えているため、 今後はSpring Framework本体がXMLConfigよりもJavaConfigにより注力すると考えられる。

プロジェクトを作成するディレクトリにて、以下のコマンドを実行する。

コマンドプロンプト(Windows)
C:\xxx>mvn archetype:generate ^
  -DarchetypeGroupId=org.terasoluna.batch ^
  -DarchetypeArtifactId=terasoluna-batch-archetype ^
  -DarchetypeVersion=5.5.0.RELEASE
Bash(Unix, Linux, …​)
$ mvn archetype:generate \
  -DarchetypeGroupId=org.terasoluna.batch \
  -DarchetypeArtifactId=terasoluna-batch-archetype \
  -DarchetypeVersion=5.5.0.RELEASE
コマンドプロンプト(Windows)
C:\xxx>mvn archetype:generate ^
  -DarchetypeGroupId=org.terasoluna.batch ^
  -DarchetypeArtifactId=terasoluna-batch-xmlconfig-archetype ^
  -DarchetypeVersion=5.5.0.RELEASE
Bash(Unix, Linux, …​)
$ mvn archetype:generate \
  -DarchetypeGroupId=org.terasoluna.batch \
  -DarchetypeArtifactId=terasoluna-batch-xmlconfig-archetype \
  -DarchetypeVersion=5.5.0.RELEASE
JavaConfig/XMLConfigの説明の併記

以降、説明対象が同じであるがBean定義の媒体が異なる場合、JavaConfig/XMLConfigという形式で併記する。また、JavaConfig/XMLConfigで異なる説明が必要な箇所では、上記のようにタブ切り替えを用いる。

その後、利用者の状況に合わせて、以下を対話式に設定する。

  • groupId

  • artifactId

  • version

  • package

以下の値を設定し実行した例を示す。

表 2. ブランクプロジェクトの各要素の説明
項目名 設定例

groupId

com.example.batch

artifactId

batch

version

1.0.0-SNAPSHOT

package

com.example.batch

コマンドプロンプトでの実行例
C:\xxx>mvn archetype:generate ^
More? -DarchetypeGroupId=org.terasoluna.batch ^
More? -DarchetypeArtifactId=terasoluna-batch-archetype ^
More? -DarchetypeVersion=5.5.0.RELEASE
[INFO] Scanning for projects…​
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] Building Maven Stub Project (No POM) 1
[INFO] ------------------------------------------------------------------------

(.. omitted)

Define value for property 'groupId': com.example.batch
Define value for property 'artifactId': batch
Define value for property 'version' 1.0-SNAPSHOT: : 1.0.0-SNAPSHOT
Define value for property 'package' com.example.batch: :
Confirm properties configuration:
groupId: com.example.batch
artifactId: batch
version: 1.0.0-SNAPSHOT
package: com.example.batch
 Y: : y
[INFO] ------------------------------------------------------------------------
[INFO] Using following parameters for creating project from Archetype: terasolua-batch-archetype:5.5.0.RELEASE
[INFO] ------------------------------------------------------------------------
[INFO] Parameter: groupId, Value: com.example.batch
[INFO] Parameter: artifactId, Value: batch
[INFO] Parameter: version, Value: 1.0.0-SNAPSHOT
[INFO] Parameter: package, Value: com.example.batch
[INFO] Parameter: packageInPathFormat, Value: com/example/batch
[INFO] Parameter: package, Value: com.example.batch
[INFO] Parameter: version, Value: 1.0.0-SNAPSHOT
[INFO] Parameter: groupId, Value: com.example.batch
[INFO] Parameter: artifactId, Value: batch
[INFO] Project created from Archetype in dir: C:\xxx\batch
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 02:02 min
[INFO] Finished at: 2019-09-03T09:24:55+09:00
[INFO] Final Memory: 13M/89M
[INFO] ------------------------------------------------------------------------
Bashでの実行例
$ mvn archetype:generate \
> -DarchetypeGroupId=org.terasoluna.batch \
> -DarchetypeArtifactId=terasoluna-batch-archetype \
> -DarchetypeVersion=5.5.0.RELEASE
[INFO] Scanning for projects…​
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] Building Maven Stub Project (No POM) 1
[INFO] ------------------------------------------------------------------------

(.. omitted)

Define value for property 'groupId': com.example.batch
Define value for property 'artifactId': batch
Define value for property 'version' 1.0-SNAPSHOT: : 1.0.0-SNAPSHOT
Define value for property 'package' com.example.batch: :
Confirm properties configuration:
groupId: com.example.batch
artifactId: batch
version: 1.0.0-SNAPSHOT
package: com.example.batch
 Y: : y
[INFO] ----------------------------------------------------------------------------
[INFO] Using following parameters for creating project from Archetype: terasoluna-batch-archetype:5.5.0.RELEASE
[INFO] ----------------------------------------------------------------------------
[INFO] Parameter: groupId, Value: com.example.batch
[INFO] Parameter: artifactId, Value: batch
[INFO] Parameter: version, Value: 1.0.0-SNAPSHOT
[INFO] Parameter: package, Value: com.example.batch
[INFO] Parameter: packageInPathFormat, Value: com/example/batch
[INFO] Parameter: package, Value: com.example.batch
[INFO] Parameter: version, Value: 1.0.0-SNAPSHOT
[INFO] Parameter: groupId, Value: com.example.batch
[INFO] Parameter: artifactId, Value: batch
[INFO] Project created from Archetype in dir: C:\xxx\batch
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 01:46 min
[INFO] Finished at: 2019-09-03T02:39:57+00:00
[INFO] Final Memory: 15M/179M
[INFO] ------------------------------------------------------------------------

以上により、プロジェクトの作成が完了した。

正しく作成出来たかどうかは、以下の要領で確認できる。

コマンドプロンプトでの実行(正しく作成できたことの確認)
C:\xxx>cd batch
C:\xxx\batch>mvn clean dependency:copy-dependencies -DoutputDirectory=lib package
C:\xxx\batch>java -cp "lib/*;target/*" ^
org.springframework.batch.core.launch.support.CommandLineJobRunner ^
META-INF/jobs/job01.xml job01
Bashでの実行(正しく作成できたことの確認)
$ cd batch
$ mvn clean dependency:copy-dependencies -DoutputDirectory=lib package
$ java -cp 'lib/*:target/*' \
org.springframework.batch.core.launch.support.CommandLineJobRunner \
META-INF/jobs/job01.xml job01

以下の出力が得られ、C:\xxx\batch\target配下にoutput.csvが作成されていれば、プロジェクトは正しく作成できている。

コマンドプロンプトでの出力例
C:\xxx\batch>mvn clean dependency:copy-dependencies -DoutputDirectory=lib package
[INFO] Scanning for projects...
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] Building TERASOLUNA Batch Framework for Java (5.x) Blank Project 1.0.0-SNAPSHOT
[INFO] ------------------------------------------------------------------------

(.. omitted)

[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 56.497 s
[INFO] Finished at: 2019-09-03T10:39:59+09:00
[INFO] Final Memory: 25M/145M
[INFO] ------------------------------------------------------------------------

C:\xxx\batch>java -cp "lib/*;target/*" ^
More? org.springframework.batch.core.launch.support.CommandLineJobRunner ^
More? META-INF/jobs/job01.xml job01

(.. omitted)

[2019/09/03 10:41:24] [main] [o.s.b.c.l.s.SimpleJobLauncher] [INFO ] Job: [FlowJob: [name=job01]] launched with the following parameters: [{jsr_batch_run_id=1}]
[2019/09/03 10:41:24] [main] [o.s.b.c.j.SimpleStepHandler] [INFO ] Executing step: [job01.step01]
[2019/09/03 10:41:24] [main] [o.s.b.c.l.s.SimpleJobLauncher] [INFO ] Job: [FlowJob: [name=job01]] completed with the following parameters: [{jsr_batch_run_id=1}] and the following status: [COMPLETED]
Bashでの出力例
$ mvn clean dependency:copy-dependencies -DoutputDirectory=lib package
[INFO] Scanning for projects...
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] Building TERASOLUNA Batch Framework for Java (5.x) Blank Project 1.0.0-SNAPSHOT
[INFO] ------------------------------------------------------------------------

(.. omitted)

[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 02:39 min
[INFO] Finished at: 2019-09-03T02:43:01+00:00
[INFO] Final Memory: 27M/189M
[INFO] ------------------------------------------------------------------------

$ java -cp 'lib/*:target/*' \
> org.springframework.batch.core.launch.support.CommandLineJobRunner \
> META-INF/jobs/job01.xml job01

(.. omitted)

[2019/09/03 02:43:11] [main] [o.s.b.c.l.s.SimpleJobLauncher] [INFO ] Job: [FlowJob: [name=job01]] launched with the following parameters: [{jsr_batch_run_id=1}]
[2019/09/03 02:43:11] [main] [o.s.b.c.j.SimpleStepHandler] [INFO ] Executing step: [job01.step01]
[2019/09/03 02:43:11] [main] [o.s.b.c.l.s.SimpleJobLauncher] [INFO ] Job: [FlowJob: [name=job01]] completed with the following parameters: [{jsr_batch_run_id=1}] and the following status: [COMPLETED]

プロジェクトの構成

前述までで作成したプロジェクトの構成について説明する。 プロジェクトは、以下の点を考慮した構成となっている。

  • 起動方式に依存しないジョブの実装を実現する

  • Spring BatchやMyBatisといった各種設定の手間を省く

  • 環境依存の切替を容易にする

以下に構成を示し、各要素について説明する。
(わかりやすさのため、前述のmvn archetype:generate実行時の出力をもとに説明する。)

BlankProject Structure
図 1. プロジェクトのディレクトリ構造
BlankProject Structure
図 2. プロジェクトのディレクトリ構造
表 3. ブランクプロジェクトの各要素の説明
項番 説明

(1)

バッチアプリケーション全体の各種クラスを格納するrootパッケージ。

(2)

バッチアプリケーション全体に関わるBean定義ファイルを格納するディレクトリ。
Spring BatchやMyBatisの初期設定や、同期/非同期といった起動契機に依らずにジョブを起動するための設定を行っている。

(3)

非同期実行(DBポーリング)機能に関連する設定を記述したBean定義ファイル。

(4)

ジョブ固有のBean定義ファイルにてimportすることで、各種設定を削減するためのBean定義ファイル。
これをimportすることで、ジョブは起動契機によるBean定義の差を吸収することが出来る。

(5)

Spring Batchの挙動や、ジョブ共通の設定に対するBean定義ファイル。

(6)

1ジョブに関する各種クラスを格納するパッケージ。
ここには、DTO、TaskletやProcessorの実装、MyBatis3のMapperインタフェースを格納する。
初期状態を参考にユーザにて自由にカスタムしてよいが、各ジョブで利用する資材をジョブごとにまとめて格納することで、 <context:component-scan><mybatis:scan>によるスキャン範囲を最小限にする構成が望ましい。
スキャン範囲の影響や設定方法ついてはMyBatis-Springの設定を参考にすること。

(7)

バッチアプリケーション全体に関わる設定ファイル。
初期状態では、データベースの接続や、非同期実行に関する設定を記述している。 ユーザにて、自由に追記してよい。

(8)

Logback(ログ出力)の設定ファイル。

(9)

BeanValidationを用いた入力チェックにて、エラーとなった際に表示するメッセージを定義する設定ファイル。
初期状態では、BeanValidationと、その実装であるHibernateValidatorのデフォルトメッセージを定義したうえで、 すべてコメントアウトしている。
この状態ではデフォルトメッセージを使うため、メッセージをカスタマイズしたい場合にのみ アンコメントし任意のメッセージに修正すること。

(10)

MyBatis3のMapperインタフェースの対となるMapper XMLファイル。

(11)

主にログ出力時に用いるメッセージを定義するプロパティファイル。

(12)

ジョブ固有のBean定義ファイルを格納するディレクトリ。
階層構造はジョブ数に応じて自由に構成してよい。

また、各ファイルの関連図を以下に示す。

Files Relation
図 3. 各ファイルの関連図
Files Relation
図 4. 各ファイルの関連図

開発の流れ

ジョブを開発する一連の流れについて説明する。
ここでは、詳細な説明ではなく、大まかな流れを把握することを主眼とする。

IDEへの取り込み

生成したプロジェクトはMavenのプロジェクト構成に従っているため、 各種IDEによって、Mavenプロジェクトとしてimportする。
詳細な手順は割愛する。

アプリケーション全体の設定

ユーザの状況に応じて以下をカスタマイズする。

これら以外の設定をカスタマイズする方法については、個々の機能にて説明する。

pom.xmlのプロジェクト情報

プロジェクトのPOMには以下の情報が仮の値で設定されているため、状況に応じて設定すること。

  • プロジェクト名(name要素)

  • プロジェクト説明(description要素)

  • プロジェクトURL(url要素)

  • プロジェクト創設年(inceptionYear要素)

  • プロジェクトライセンス(licenses要素)

  • プロジェクト組織(organization要素)

データベース関連の設定

データベース関連の設定は複数箇所にあるため、それぞれを修正すること。

pom.xml
<!-- (1) -->
<dependency>
    <groupId>com.h2database</groupId>
    <artifactId>h2</artifactId>
    <scope>runtime</scope>
</dependency>

<dependency>
    <groupId>org.postgresql</groupId>
    <artifactId>postgresql</artifactId>
    <scope>runtime</scope>
</dependency>
batch-application.properties
# (2)
# Admin DataSource settings.
admin.jdbc.driver=org.h2.Driver
admin.jdbc.url=jdbc:h2:mem:batch-admin;DB_CLOSE_DELAY=-1
admin.jdbc.username=sa
admin.jdbc.password=

# (2)
# Job DataSource settings.
#jdbc.driver=org.postgresql.Driver
#jdbc.url=jdbc:postgresql://localhost:5432/postgres
#jdbc.username=postgres
#jdbc.password=postgres
jdbc.driver=org.h2.Driver
jdbc.url=jdbc:h2:mem:batch;DB_CLOSE_DELAY=-1
jdbc.username=sa
jdbc.password=

# (3)
# Spring Batch schema initialize.
data-source.initialize.enabled=true
spring-batch.schema.script=classpath:org/springframework/batch/core/schema-h2.sql
terasoluna-batch.commit.script=classpath:org/terasoluna/batch/async/db/schema-commit.sql
com.example.batch.config.LaunchContextConfig.java
// (3)
@Bean
public DataSourceInitializer dataSourceInitializer(@Qualifier("adminDataSource") DataSource adminDataSource,
                                                   @Value("${data-source.initialize.enabled:false}") boolean enabled,
                                                   @Value("${spring-batch.schema.script}") Resource script,
                                                   @Value("${terasoluna-batch.commit.script}") Resource commitScript) {
    final DataSourceInitializer dataSourceInitializer = new DataSourceInitializer();
    dataSourceInitializer.setDataSource(adminDataSource);
    dataSourceInitializer.setEnabled(enabled);
    ResourceDatabasePopulator resourceDatabasePopulator = new ResourceDatabasePopulator(script, commitScript);
    resourceDatabasePopulator.setContinueOnError(true);
    dataSourceInitializer.setDatabasePopulator(resourceDatabasePopulator);
    return dataSourceInitializer;
}

// (4)
@Bean(destroyMethod = "close")
public BasicDataSource adminDataSource(@Value("${admin.jdbc.driver}") String driverClassName,
                                       @Value("${admin.jdbc.url}") String url,
                                       @Value("${admin.jdbc.username}") String username,
                                       @Value("${admin.jdbc.password}") String password) {
    final BasicDataSource dataSource = new BasicDataSource();
    dataSource.setDriverClassName(driverClassName);
    dataSource.setUrl(url);
    dataSource.setUsername(username);
    dataSource.setPassword(password);
    dataSource.setMaxTotal(10);
    dataSource.setMinIdle(1);
    dataSource.setMaxWaitMillis(5000);
    dataSource.setDefaultAutoCommit(false);
    return dataSource;
}

// (4)
@Bean(destroyMethod = "close")
public BasicDataSource jobDataSource(@Value("${jdbc.driver}") String driverClassName,
                                     @Value("${jdbc.url}") String url,
                                     @Value("${jdbc.username}") String username,
                                     @Value("${jdbc.password}") String password) {
    final BasicDataSource basicDataSource = new BasicDataSource();
    basicDataSource.setDriverClassName(driverClassName);
    basicDataSource.setUrl(url);
    basicDataSource.setUsername(username);
    basicDataSource.setPassword(password);
    basicDataSource.setMaxTotal(10);
    basicDataSource.setMinIdle(1);
    basicDataSource.setMaxWaitMillis(5000);
    basicDataSource.setDefaultAutoCommit(false);
    return basicDataSource;
}

// (5)
@Bean
public SqlSessionFactory jobSqlSessionFactory(@Qualifier("jobDataSource") DataSource jobDataSource) throws Exception {
    final SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
    sqlSessionFactoryBean.setDataSource(jobDataSource);

    final org.apache.ibatis.session.Configuration configuration = new org.apache.ibatis.session.Configuration();
    configuration.setLocalCacheScope(LocalCacheScope.STATEMENT);
    configuration.setLazyLoadingEnabled(true);
    configuration.setAggressiveLazyLoading(false);
    configuration.setDefaultFetchSize(1000);
    configuration.setDefaultExecutorType(ExecutorType.REUSE);

    sqlSessionFactoryBean.setConfiguration(configuration);
    return sqlSessionFactoryBean.getObject();
}
META-INF/spring/launch-context.xml
<!-- (3) -->
<jdbc:initialize-database data-source="adminDataSource"
                          enabled="${data-source.initialize.enabled:false}"
                          ignore-failures="ALL">
    <jdbc:script location="${spring-batch.schema.script}" />
    <jdbc:script location="${terasoluna-batch.commit.script}" />
</jdbc:initialize-database>

<!-- (4) -->
<bean id="adminDataSource" class="org.apache.commons.dbcp2.BasicDataSource"
      destroy-method="close"
      p:driverClassName="${admin.jdbc.driver}"
      p:url="${admin.jdbc.url}"
      p:username="${admin.jdbc.username}"
      p:password="${admin.jdbc.password}"
      p:maxTotal="10"
      p:minIdle="1"
      p:maxWaitMillis="5000"
      p:defaultAutoCommit="false"/>

<!-- (4) -->
<bean id="jobDataSource" class="org.apache.commons.dbcp2.BasicDataSource"
      destroy-method="close"
      p:driverClassName="${jdbc.driver}"
      p:url="${jdbc.url}"
      p:username="${jdbc.username}"
      p:password="${jdbc.password}"
      p:maxTotal="10"
      p:minIdle="1"
      p:maxWaitMillis="5000"
      p:defaultAutoCommit="false" />

<!-- (5) -->
<bean id="jobSqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"
      p:dataSource-ref="jobDataSource" >
    <property name="configuration">
        <bean class="org.apache.ibatis.session.Configuration"
            p:localCacheScope="STATEMENT"
            p:lazyLoadingEnabled="true"
            p:aggressiveLazyLoading="false"
            p:defaultFetchSize="1000"
            p:defaultExecutorType="REUSE" />
    </property>
</bean>
com.example.batch.config.AsyncBatchDaemonConfig.java
// (5)
@Bean
public SqlSessionFactory adminSqlSessionFactory(@Qualifier("adminDataSource") DataSource adminDataSource,
                                                DatabaseIdProvider databaseIdProvider) throws Exception {
    final SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
    sqlSessionFactoryBean.setDataSource(adminDataSource);
    sqlSessionFactoryBean.setDatabaseIdProvider(databaseIdProvider);
    final org.apache.ibatis.session.Configuration configuration = new org.apache.ibatis.session.Configuration();
    configuration.setLocalCacheScope(LocalCacheScope.STATEMENT);
    configuration.setLazyLoadingEnabled(true);
    configuration.setAggressiveLazyLoading(false);
    configuration.setDefaultFetchSize(1000);
    configuration.setDefaultExecutorType(ExecutorType.REUSE);
    sqlSessionFactoryBean.setConfiguration(configuration);
    return sqlSessionFactoryBean.getObject();
}
META-INF/spring/async-batch-daemon.xml
<!-- (5) -->
<bean id="adminSqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"
      p:dataSource-ref="adminDataSource" >
    <property name="configuration">
        <bean class="org.apache.ibatis.session.Configuration"
              p:localCacheScope="STATEMENT"
              p:lazyLoadingEnabled="true"
              p:aggressiveLazyLoading="false"
              p:defaultFetchSize="1000"
              p:defaultExecutorType="REUSE" />
    </property>
</bean>
表 4. データベース関連の設定における各要素の説明
項番 説明

(1)

pom.xmlでは利用するデータベースへの接続に使用するJDBCドライバの依存関係を定義する。
初期状態ではH2 Database(インメモリデータベース)とPostgreSQLが設定されているが、必要に応じて追加削除を行うこと。

(2)

JDBCドライバの接続設定をする。
- admin.jdbc.xxxはSpring BatchやTERASOLUNA Batch 5.xが利用する
- jdbc.xxx~はジョブ個別が利用する

(3)

Spring BatchやTERASOLUNA Batch 5.xが利用するデータベースの初期化処理を実行するか否か、および、利用するスクリプトを定義する。
Spring BatchはJobRepositoryにアクセスするため、データベースが必須となる。 また、TERASOLUNA Batch 5.xは非同期実行(DBポーリング)にてジョブ要求テーブルにアクセスするため、 データベースが必須となる。
有効にするか否かは、以下を基準とするとよい。
- H2 Databaseを利用する場合は有効にする。無効にするとJobRepositoryジョブ要求テーブルにアクセスできずエラーになる。
- H2 Databaseを利用しない場合は事故を予防するために無効にする。

(4)

データソースの設定をする。
必要に応じて接続数等をチューニングする。

(5)

MyBatisの挙動を設定する。
必要に応じてフェッチサイズ等をチューニングする。

ジョブの作成

プロジェクトのビルドと実行

プロジェクトのビルドと実行について説明する。

アプリケーションのビルド

プロジェクトのルートディレクトリに移動し、以下のコマンドを発行する。

ビルド(Windows/Bash)
$ mvn clean dependency:copy-dependencies -DoutputDirectory=lib package
[INFO] Scanning for projects...
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] Building TERASOLUNA Batch Framework for Java (5.x) Blank Project 1.0.0-SNAPSHOT
[INFO] ------------------------------------------------------------------------

(.. omitted)

[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 02:39 min
[INFO] Finished at: 2019-09-03T02:43:01+00:00
[INFO] Final Memory: 27M/189M
[INFO] ------------------------------------------------------------------------

これにより、以下が生成される。

  • <ルートディレクトリ>/target/[artifactId]-[version].jar

    • 作成したバッチアプリケーションのJarが生成される

  • <ルートディレクトリ>/lib/(依存Jarファイル)

    • 依存するJarファイル一式がコピーされる

試験環境や商用環境へ配備する際は、これらのJarファイルを任意のディレクトリにコピーすればよい。

環境に応じた設定ファイルの切替

プロジェクトのpom.xmlでは、初期値として以下のProfileを設定している。

pom.xmlのProfiles設定
<profiles>
    <!-- Including application properties and log settings into package. (default) -->
    <profile>
        <id>IncludeSettings</id>
        <activation>
            <activeByDefault>true</activeByDefault>
        </activation>
        <properties>
            <exclude-property/>
            <exclude-log/>
        </properties>
    </profile>

    <!-- Excluding application properties and log settings into package. -->
    <profile>
        <id>ExcludeSettings</id>
        <activation>
            <activeByDefault>false</activeByDefault>
        </activation>
        <properties>
            <exclude-property>batch-application.properties</exclude-property>
            <exclude-log>logback.xml</exclude-log>
        </properties>
    </profile>
</profiles>

ここでは、環境依存となる設定ファイルを含めるかどうかを切替ている。 この設定を活用して、環境配備の際に設定ファイルを別途配置することで環境差分を吸収することができる。 また、これを応用して、試験環境と商用環境でJarに含める設定ファイルを変えることもできる。 以下に、一例を示す。

環境ごとに設定ファイルを切替えるpom.xmlの記述例
<dependencies>
    <!-- omitted -->
    <dependency>
        <groupId>${jdbc.driver.groupId}</groupId>
        <artifactId>${jdbc.driver.artifactId}</artifactId>
        <scope>runtime</scope>
    </dependency>
</dependencies>

<!-- omitted -->

<profiles>
    <!-- omitted -->
    <profile>
        <id>postgresql12-local</id>
        <properties>
            <jdbc.driver.groupId>org.postgresql</jdbc.driver.groupId>
            <jdbc.driver.artifactId>postgresql</jdbc.driver.artifactId>
            <!-- omitted -->
        </properties>
    </profile>
    <!-- omitted -->
    <profile>
        <id>oracle19c-local</id>
        <properties>
            <jdbc.driver.groupId>com.oracle.database.jdbc</jdbc.driver.groupId>
            <jdbc.driver.artifactId>ojdbc8</jdbc.driver.artifactId>
            <!-- omitted -->
        </properties>
    </profile>
</profiles>

なお、MavenのProfileは以下の要領で、コマンド実行時に有効化することができる。
必要に応じて、複数Profileを有効化することもできる。必要に応じて、有効活用してほしい。

MavenのProfileを有効化する例
$ mvn -P profile-1,profile-2

アプリケーションの実行

前段でビルドした結果をもとに、ジョブを実行する例を示す。
[artifactId][version]プロジェクトの作成にて設定したものに、ユーザに応じて読み替えてほしい。

コマンドプロンプト(Windows)
C:\xxx>java -cp "target\[artifactId]-[version].jar;lib\*" ^
org.springframework.batch.core.launch.support.CommandLineJobRunner ^
META-INF/jobs/job01.xml job01

(.. omitted)
[2019/09/03 10:41:24] [main] [o.s.b.c.l.s.SimpleJobLauncher] [INFO ] Job: [FlowJob: [name=job01]] launched with the following parameters: [{jsr_batch_run_id=1}]
[2019/09/03 10:41:24] [main] [o.s.b.c.j.SimpleStepHandler] [INFO ] Executing step: [job01.step01]
[2019/09/03 10:41:24] [main] [o.s.b.c.l.s.SimpleJobLauncher] [INFO ] Job: [FlowJob: [name=job01]] completed with the following parameters: [{jsr_batch_run_id=1}] and the following status: [COMPLETED]
Bash(Unix, Linux, …​)
$ java -cp 'target/[artifactId]-[version].jar:lib/*' \
org.springframework.batch.core.launch.support.CommandLineJobRunner \
META-INF/jobs/job01.xml job01

(.. omitted)
[2019/09/03 02:43:11] [main] [o.s.b.c.l.s.SimpleJobLauncher] [INFO ] Job: [FlowJob: [name=job01]] launched with the following parameters: [{jsr_batch_run_id=1}]
[2019/09/03 02:43:11] [main] [o.s.b.c.j.SimpleStepHandler] [INFO ] Executing step: [job01.step01]
[2019/09/03 02:43:11] [main] [o.s.b.c.l.s.SimpleJobLauncher] [INFO ] Job: [FlowJob: [name=job01]] completed with the following parameters: [{jsr_batch_run_id=1}] and the following status: [COMPLETED]

これにより、<ルートディレクトリ>/target/output.csvが生成される。

javaコマンドが返却する終了コードをハンドリングする必要性

実際のシステムでは、 ジョブスケジューラからジョブを発行する際にjavaコマンドを直接発行するのではなく、 java起動用のシェルスクリプトを挟んで起動することが一般的である。

これはjavaコマンド起動前の環境変数を設定するためや、javaコマンドの終了コードをハンドリングするためである。 この、javaコマンドの終了コードのハンドリングは、以下を理由に常に行うことを推奨する。

  • javaコマンドの終了コードは正常:0、異常:1であるが、ジョブスケジューラはジョブの成功/失敗を終了コードの範囲で判断する。 そのため、ジョブスケジューラの設定によっては、javaコマンドは異常終了したのにもかかわらずジョブスケジューラは正常終了したと判断してしまう。

  • OSやジョブスケジューラが扱うことができる終了コードは有限の範囲である。

    • OSやジョブスケジューラの仕様に応じて、ユーザにて使用する終了コードの範囲を定義することが重要である。

    • 一般的に、POSIX標準で策定されている0から255の間に収めることが多い。

      • ブランクプロジェクトでは、正常:0、それ以外:255として終了コードを返却するよう設定している。

以下に、終了コードのハンドリング例を示す。

終了コードのハンドリング例
#!/bin/bash

# ..omitted.

java -cp ...
RETURN_CODE=$?
if [ $RETURN_CODE = 1 ]; then
   return 255
else
   return $RETURN_CODE
fi
TERASOLUNA Batch Framework for Java (5.x) Development Guideline - version 5.5.0.RELEASE, 2023-3-31, commit-id:e772ac8