しおしお

IntelliJ IDEAのことなんかを書いてます

JSR352-Batch Applicationを試してみた(ジョブにパラメータを指定する)

JSR352-Batch Applicationを試してみた(ジョブの停止-Batchlet) - しおしおの続編。今回は、ジョブにパラーメータを渡す方法を試してみた。

ジョブ起動時にパラメータを指定する方法

以下のコードのように、JobOperator#startの2番めの引数にジョブ起動時のパラメータを指定する。

final Properties jobParams = new Properties();
jobParams.put("param1", "value1");
jobParams.put("param2", "100");

final JobOperator operator = BatchRuntime.getJobOperator();
final long executionId = operator.start(jobId, jobParams);

ジョブパラメータを受け取る

ジョブパラメータは、BatchletやChunk関連の実装クラス、リスナーなどにBatchPropertyとしてインジェクションする。自動的にインジェクションされるわけではないので、ジョブXMLでジョブパラメータをBatchPropertyにセットする設定が必要となる。

ジョブパラメータを使用する実装サンプル

@Dependent
@Named
public class JobParameterSampleBatchlet: AbstractBatchlet() {
  
  @Inject
  @BatchProperty
  var param:String? = null

  override fun process(): String {
    log.info("param: {}", param)
    return "success"
  }

  companion object {
    val log:Logger = LoggerFactory.getLogger(javaClass<JobParameterSampleBatchlet>())
  }

}

ジョブパラメータを設定するジョブXMLのサンプル
ジョブパラメータを設定するには、以下の例のように#{jobParameters['パラメータ名']}というEL式を使用する。
この例では、ジョブ起動時に指定されたパラメータの中で、パラメータ名がjob-paramの値を、paramプロパティに設定する。

<job id="jobParameter" xmlns="http://xmlns.jcp.org/xml/ns/javaee" version="1.0">
  <step id="myStep">
    <batchlet ref="jobParameterSampleBatchlet">
      <properties>
        <property name="param" value="#{jobParameters['job-param']}" />
      </properties>
    </batchlet>
  </step>
</job>

実行すると
こんなパラメータを設定してと

Properties properties = new Properties();
properties.put("job-param", "パラメータ");

ログにジョブパラメータが出力されたので、パラメータがBatchletに設定されているのがわかる。

13:34:13,554 INFO  [siosio.batchlet.JobParameterSampleBatchlet] (Batch Thread - 1) param: パラメータ

おわり

GradleのjarタスクでMANIFEST.MFにgit情報を書き込むプラグイン作ってみた

Gradleのjarタスクで、gitのどのリビジョンをベースに作成したかをMANIFEST.MFに書き込むGradleプラグインを作ってみた。

作った理由

最近、社内のMavenリポジトリに古いソースコードベースのartifactが頻繁にアップされてかなりハマったので。。。
jarがどのリビジョンベースで作られたのかサクッとわかると、個人的にはわりと便利な気がしたので作ってみた感じです。*1

使い方

こんな感じにプラグイン適用するだけな感じです。

buildscript {
  repositories {
    mavenLocal()
  }
  dependencies {
    classpath 'siosio:add-git-info-to-manifest:+'
  }
}

apply plugin: 'AddGitInfoToManifest'

プラグインのコード

siosio/add-git-info-to-manifest · GitHubに上げてみてます。

おわり

*1:同じようなことできるプラグインあるんだろうけど探せなかった・・・

JSR352-Batch Applicationを試してみた(ジョブの停止-Batchlet)

JSR352-Batch Applicationを試してみた(Chunkステップ編) - しおしおの続編で、今回は実行中のジョブの停止を試してみる。

実行中ジョブの停止方法

JobOperator#stopを呼び出すことで実行中のジョブが停止する。
sotpメソッドの引数には、停止対象のジョブ実行を識別するためのexecutionIdを指定します。executionIdは、ジョブの起動時に戻される値です。

stopメソッドを呼び出して、ジョブの停止要求を受け付けると、ジョブのステータスは停止処理中であることを示すSTOPPINGに変更されます。実行中のステップの処理を停止し、ジョブが停止されるとステータスはSTOPPEDに変更されます。

ジョブを停止させるサンプルコード

    val jobOperator = BatchRuntime.getJobOperator()
    jobOperator.stop(executionId)

Batchletの処理を安全に中止する

ジョブの停止要求があると、Batchlet#stopがコールバックされるので、Batchlet#processの処理を安全に中断する必要がある。

サンプルコード

このバッチレットでは、processで処理の長いSQLが実行される。stop処理では、SQL文の実行をキャンセルするために、Statement#cancelを実行する。

  override fun process(): String {
    // 処理の長いSQL文を実行する
    dataSource!!.use {
      statement = it.createStatement()
      statement.execute("select pg_sleep(30)")
    }
    return "OK"
  }

  override fun stop() {
    LOGGER.info("ジョブを停止します..... execution id={}, step={}, status={}",
        jobContext!!.getExecutionId(), stepContext!!.getStepName(), stepContext!!.getBatchStatus())
    statement.cancel()
  }

Batchletの全コード

実行結果ログ

ステータスが実行中(STARTED)から、中断を示す(STOPPED)に変わっています。

01:53:16,310 INFO  [siosio.listener.LogStepListener] (Batch Thread - 4) step start... job=stoppable-batchlet, step=batchlet, status=STARTED
01:53:20,483 INFO  [siosio.batchlet.StoppableBatchlet] (default task-15) ジョブを停止します..... execution id=120, step=batchlet, status=STOPPING
01:53:20,511 INFO  [siosio.batchlet.StoppableBatchlet] (Batch Thread - 4) ステップの処理がキャンセルされました。 step=batchlet, status=STOPPING
01:53:20,514 INFO  [siosio.listener.LogStepListener] (Batch Thread - 4) step end... job=stoppable-batchlet, step=batchlet, status=STOPPED

ジョブの状態を格納するテーブル上でも、バッチのステータスがSTOPPEDになっている。
f:id:sioiri:20150725015600p:plain

stopメソッド実装しなかったらどうなる?

stopメソッドを実装しないと、Batchlet#processの処理が終わるのを永遠と待ち続ける。stop要求時に即処理を停止したいなら、stopメソッドを実装する必要がある。

おわり。

UpsourceのIntelliJ IDEAプラグインを使ってみた

JetBrainsのUpsource触ってみた(リポジトリブラウザ) - しおしお依頼久しぶりのUpsourceネタ。
お仕事系プロジェクトで使ってみようかなと思って久しぶりに触って見てたら、IntelliJ IDEA用のプラグイン出てたのでプラグインの使い方を少し整理してみた。

プラグインの入れ方

設定画面のPluginsからインストールします。プラグイン名は、Upsource Integrationです。
f:id:sioiri:20150627014648p:plain

プラグインの設定

Upsourceサーバの設定を行い、テスト接続をしておきます。テスト接続の途中で、Upsourceの認証画面が開くので、アカウント情報を入力して、IntelliJ IDEAのプラグインからのアクセスを許可しておきます。
f:id:sioiri:20150627015126p:plain

レビューを依頼する

レビューの依頼はVersion Controlのコミットログ画面から行います。下の画像のようにレビューしてほしいコミットを選択してコンテキストメニューからレビューを作ります。
f:id:sioiri:20150627020305p:plain

新しいレビューを作るか、既存のレビューに選択したコミットを追加するかを効かれます。
とりあえず、新しいレビューを作りたいのでCreate・・・の方を選択します。
f:id:sioiri:20150627020619p:plain

次のレビューのタイトルやレビューする人を選びます。
レビューする人の追加や削除は右側の「+」や「ー」ボタンから行います。
f:id:sioiri:20150627020732p:plain

レビューの作成が完了するとReviewsウィンドウからレビュー対象などが確認できるようになります。
f:id:sioiri:20150627021108p:plain

指摘をあげる

まずは、レビューしたいファイルの変更内容を確認します。画像のように、レビュー対象のコミットを選択して、コンテクストメニューからdiffを表示します。
f:id:sioiri:20150627021455p:plain

指摘を追加したい行にカーソルを移動または、指摘を追加したい部分を選択しメニューにある「+」をクリックします。
これで、指摘を追加するダイアログが表示されるので指摘を登録します。*1*2
f:id:sioiri:20150627021746p:plain

指摘を追加すると、こんな感じにdiff画面で指摘が見れるようになります。
f:id:sioiri:20150627022110p:plain

指摘をなおす

画像のようにIDE上から指摘一覧や、指摘箇所が確認できます。
f:id:sioiri:20150627023553p:plain

対応ができたら指摘に対してコメントを書いてあげたりします。
f:id:sioiri:20150627023758p:plain

コミットします。コミットの際には、今回にコミットをレビューに含めるようにしてあげます。
画像のように既存のレビューに追加する形でコミットを行います。
f:id:sioiri:20150627024154p:plain

こんな感じに自動的にコミットがレビューの中に追加されました。
あとは、プッシュして再レビューしてもらう感じですね。
f:id:sioiri:20150627024340p:plain

おわり。

*1:UIめっちゃわかりづらかった

*2:Macで指摘に日本語いれれなかった(´・ω・`)

JSR352-Batch Applicationを試してみた(Chunkステップ編)

JSR352-Batch Applicationを試してみた(Batchlet編) - しおしおの続編です。今回は、chunkステップを試してみます。

chunkステップは、ItemReaderでデータベースやファイルなどから読んだデータをItemProcessorで変換(ビジネスロジックで色々導出したりしてItemWriterで出力するオブジェクトを生成する感じかな)し、ItemWriterでデータベースやファイルに出力します。
writerに渡されるデータは、ジョブXMLのchunkステップで設定した件数分纏めて渡されてきます。
例えば、item-countに1000を指定した場合、readerとprocessorが1000回繰り返し実行されたあとに、1000件のデータがwriterに渡されます。*1

ジョブの構成

ItemReaderでは1から10までの連番をリードします。ItemProcessorでは、ItemReaderでリードしたデータ(連番)からUserEntityを生成して返します。ItemWriterでは、JPAを使用してUserEntityの情報をデータベースに保存しています。

ジョブXMLは以下の内容になっています。処理する件数が少ないのでChunkサイズ(item-count)も小さめに設定しています。

<job id="chunk-sample" xmlns="http://xmlns.jcp.org/xml/ns/javaee" version="1.0">
  <step id="step">
    <chunk item-count="3">
      <reader ref="sampleReader"/>
      <processor ref="sampleProcessor" />
      <writer ref="userWriter"/>
    </chunk>
  </step>
</job>

ItemReaderの実装

ItemReaderは、AbstractItemReaderを継承して作成します。ItemReaderインタフェースを実装してもいいだけど、AbstractItemReaderは必須じゃないメソッドは空実装持っててくれるので不要なメソッドは実装しないで良い感じです。
ItemReaderの実装では、readItemメソッドで外部リソースから読み込んだ情報を返します。今回は単純にインクリメントした値を返しているだけです。
もし、これ以上読み取るデータがない場合には、nullを返却します。nullを返却するとこれ以上扱うレコードがないと判断され、最後の纏まりがItemWriterで処理されたあとにChunkステップが終了します。

今回の例では、外部リソースを必要としないので、リソースを開いたり閉じたりなどの処理は実装していません。

package siosio.chunk;

import javax.batch.api.chunk.AbstractItemReader;
import javax.enterprise.context.Dependent;
import javax.inject.Named;

@Dependent
@Named
public class SampleReader extends AbstractItemReader {

    int index = 0;

    @Override
    public Object readItem() throws Exception {
        index++;
        if (index <= 10) {
            return index;
        }
        // これ以上レコードがない場合はnullを返す
        return null;
    }
}

ItemProcessorの実装

ItemProcessorはItemProcessorインタフェースをimplementsして作成します。メソッドは1つだけなので、ItemReaderやWriterのように空実装をもった抽象クラスは提供されていません。

今回の例では、ItemReaderでリードした連番を、出力ようにEntityに変換しています。

package siosio.chunk;

import javax.batch.api.chunk.ItemProcessor;
import javax.enterprise.context.Dependent;
import javax.inject.Named;

import siosio.entity.UserEntity;

@Named
@Dependent
public class SampleProcessor implements ItemProcessor {

    @Override
    public Object processItem(Object item) throws Exception {
        return new UserEntity("user name " + item);
    }
}

ItemWriterの実装

ItemWriterはItemReaderと同じ理由でAbstractItemWriterを継承して作成します。

今回は、ItemProcessorで変換した値をJPAを使ってデータベースに保存しています。

package siosio.chunk;

import java.util.List;

import javax.batch.api.chunk.AbstractItemWriter;
import javax.enterprise.context.Dependent;
import javax.inject.Named;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;

@Dependent
@Named
public class UserWriter extends AbstractItemWriter {

    @PersistenceContext
    EntityManager em;

    @Override
    public void writeItems(List<Object> items) throws Exception {
        for (Object item : items) {
            em.persist(item);
        }
    }
}

実行ログ

hibernateのログです。ちゃんとインサートできてますね。

00:02:00,762 DEBUG [org.hibernate.SQL] (Batch Thread - 3) select nextval ('hibernate_sequence')
00:02:00,769 DEBUG [org.hibernate.SQL] (Batch Thread - 3) select nextval ('hibernate_sequence')
00:02:00,783 DEBUG [org.hibernate.SQL] (Batch Thread - 3) select nextval ('hibernate_sequence')
00:02:00,788 DEBUG [org.hibernate.SQL] (Batch Thread - 3) insert into users (name, id) values (?, ?)
00:02:00,790 TRACE [org.hibernate.type.descriptor.sql.BasicBinder] (Batch Thread - 3) binding parameter [1] as [VARCHAR] - [user name 1]
00:02:00,790 TRACE [org.hibernate.type.descriptor.sql.BasicBinder] (Batch Thread - 3) binding parameter [2] as [BIGINT] - [21]
00:02:00,791 DEBUG [org.hibernate.SQL] (Batch Thread - 3) insert into users (name, id) values (?, ?)
00:02:00,791 TRACE [org.hibernate.type.descriptor.sql.BasicBinder] (Batch Thread - 3) binding parameter [1] as [VARCHAR] - [user name 2]
00:02:00,791 TRACE [org.hibernate.type.descriptor.sql.BasicBinder] (Batch Thread - 3) binding parameter [2] as [BIGINT] - [22]
00:02:00,791 DEBUG [org.hibernate.SQL] (Batch Thread - 3) insert into users (name, id) values (?, ?)
00:02:00,792 TRACE [org.hibernate.type.descriptor.sql.BasicBinder] (Batch Thread - 3) binding parameter [1] as [VARCHAR] - [user name 3]
00:02:00,795 TRACE [org.hibernate.type.descriptor.sql.BasicBinder] (Batch Thread - 3) binding parameter [2] as [BIGINT] - [23]
00:02:00,806 DEBUG [org.hibernate.SQL] (Batch Thread - 3) select nextval ('hibernate_sequence')
00:02:00,808 DEBUG [org.hibernate.SQL] (Batch Thread - 3) select nextval ('hibernate_sequence')
00:02:00,810 DEBUG [org.hibernate.SQL] (Batch Thread - 3) select nextval ('hibernate_sequence')
00:02:00,816 DEBUG [org.hibernate.SQL] (Batch Thread - 3) insert into users (name, id) values (?, ?)
00:02:00,818 TRACE [org.hibernate.type.descriptor.sql.BasicBinder] (Batch Thread - 3) binding parameter [1] as [VARCHAR] - [user name 4]
00:02:00,819 TRACE [org.hibernate.type.descriptor.sql.BasicBinder] (Batch Thread - 3) binding parameter [2] as [BIGINT] - [24]
00:02:00,822 DEBUG [org.hibernate.SQL] (Batch Thread - 3) insert into users (name, id) values (?, ?)
00:02:00,822 TRACE [org.hibernate.type.descriptor.sql.BasicBinder] (Batch Thread - 3) binding parameter [1] as [VARCHAR] - [user name 5]
00:02:00,822 TRACE [org.hibernate.type.descriptor.sql.BasicBinder] (Batch Thread - 3) binding parameter [2] as [BIGINT] - [25]
00:02:00,823 DEBUG [org.hibernate.SQL] (Batch Thread - 3) insert into users (name, id) values (?, ?)
00:02:00,824 TRACE [org.hibernate.type.descriptor.sql.BasicBinder] (Batch Thread - 3) binding parameter [1] as [VARCHAR] - [user name 6]
00:02:00,824 TRACE [org.hibernate.type.descriptor.sql.BasicBinder] (Batch Thread - 3) binding parameter [2] as [BIGINT] - [26]
00:02:00,835 DEBUG [org.hibernate.SQL] (Batch Thread - 3) select nextval ('hibernate_sequence')
00:02:00,837 DEBUG [org.hibernate.SQL] (Batch Thread - 3) select nextval ('hibernate_sequence')
00:02:00,840 DEBUG [org.hibernate.SQL] (Batch Thread - 3) select nextval ('hibernate_sequence')
00:02:00,846 DEBUG [org.hibernate.SQL] (Batch Thread - 3) insert into users (name, id) values (?, ?)
00:02:00,858 TRACE [org.hibernate.type.descriptor.sql.BasicBinder] (Batch Thread - 3) binding parameter [1] as [VARCHAR] - [user name 7]
00:02:00,858 TRACE [org.hibernate.type.descriptor.sql.BasicBinder] (Batch Thread - 3) binding parameter [2] as [BIGINT] - [27]
00:02:00,859 DEBUG [org.hibernate.SQL] (Batch Thread - 3) insert into users (name, id) values (?, ?)
00:02:00,859 TRACE [org.hibernate.type.descriptor.sql.BasicBinder] (Batch Thread - 3) binding parameter [1] as [VARCHAR] - [user name 8]
00:02:00,859 TRACE [org.hibernate.type.descriptor.sql.BasicBinder] (Batch Thread - 3) binding parameter [2] as [BIGINT] - [28]
00:02:00,859 DEBUG [org.hibernate.SQL] (Batch Thread - 3) insert into users (name, id) values (?, ?)
00:02:00,860 TRACE [org.hibernate.type.descriptor.sql.BasicBinder] (Batch Thread - 3) binding parameter [1] as [VARCHAR] - [user name 9]
00:02:00,860 TRACE [org.hibernate.type.descriptor.sql.BasicBinder] (Batch Thread - 3) binding parameter [2] as [BIGINT] - [29]
00:02:00,865 DEBUG [org.hibernate.SQL] (Batch Thread - 3) select nextval ('hibernate_sequence')
00:02:00,870 DEBUG [org.hibernate.SQL] (Batch Thread - 3) insert into users (name, id) values (?, ?)
00:02:00,871 TRACE [org.hibernate.type.descriptor.sql.BasicBinder] (Batch Thread - 3) binding parameter [1] as [VARCHAR] - [user name 10]
00:02:00,871 TRACE [org.hibernate.type.descriptor.sql.BasicBinder] (Batch Thread - 3) binding parameter [2] as [BIGINT] - [30]

一応テストを

JSR352-Batch Applicationを試してみた(Batchletをテストしてみた) - しおしおと同じようにArquillianを使ってテストしてみたのでテストコードを。。。

package siosio.chunk;

import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;

import java.util.List;

import javax.batch.runtime.BatchStatus;
import javax.batch.runtime.JobExecution;
import javax.inject.Inject;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
import javax.transaction.UserTransaction;

import org.jboss.arquillian.container.test.api.Deployment;
import org.jboss.arquillian.junit.Arquillian;
import org.jboss.shrinkwrap.api.ShrinkWrap;
import org.jboss.shrinkwrap.api.gradle.archive.importer.embedded.EmbeddedGradleImporter;
import org.jboss.shrinkwrap.api.spec.WebArchive;
import siosio.TestHelper;
import siosio.entity.UserEntity;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;

@RunWith(Arquillian.class)
public class SampleChunkTest {

    @Deployment
    public static WebArchive createDeployment() {
        return ShrinkWrap.create(EmbeddedGradleImporter.class)
                .forThisProjectDirectory()
                .importBuildOutput()
                .as(WebArchive.class)
                .addClasses(TestHelper.class);
    }

    @PersistenceContext
    EntityManager em;

    @Inject
    UserTransaction utx;

    @Before
    public void setUp() throws Exception {
        utx.begin();
        final Query query = em.createNativeQuery("delete from USERS");
        query.executeUpdate();
        utx.commit();
    }


    @Test
    public void testJobCompleted() throws Exception {
        final JobExecution jobExecution = TestHelper.start("chunk-sample");
        assertThat("正常終了していること", jobExecution.getBatchStatus(), is(BatchStatus.COMPLETED));

        final List<UserEntity> result = em.createNamedQuery("findUsersAll", UserEntity.class)
                .getResultList();

        assertThat("10レコード登録されていること", result.size(), is(10));

        for (int i = 0; i < 10; i++) {
            int index = i + 1;
            assertThat("ユーザ名", result.get(i)
                    .getName(), is("user name " + index));
        }
    }
}

おわり。

*1:ItemReaderやItemWriterの実装は、 jsr352/jberet-support/src/main/java/org/jberet/support/io at master · jberet/jsr352 · GitHubがかなり参考になります

Gradleのcoberturaプラグイン2.2.7以上で発生するClassNotFoundExceptionへの対処方法

Gradleのcobertura-pluginのバージョンを2.2.7以上に上げた場合にClassNotFoundExceptionがでてテストが実行できない件の対処方法。

build.gradleの内容

plugins {
  id 'net.saliman.cobertura' version '2.2.8'
}

group 'siosio.test'
version '1.0-SNAPSHOT'

apply plugin: 'java'

sourceCompatibility = 1.7

repositories {
  mavenCentral()
}

dependencies {
  testCompile 'junit:junit:4.12'
}

task wrapper(type: Wrapper) {
  gradleVersion = '2.2'
}

test {


  cobertura {
    coverageSourceDirs = ["${projectDir}/src/main/java/"]
    coverageIgnoreTrivial = true
    coverageFormats = ['html']
    coverageDirs = [project.sourceSets.main.output.classesDir]
  }
}

発生するエラー

こんな感じにClassNotFoundExceptionが発生する。

:test
siosio.CalcTest > name FAILED
    java.lang.NoClassDefFoundError at CalcTest.java:15
        Caused by: java.lang.ClassNotFoundException at CalcTest.java:15

原因

ClassNotFoundException on 2.2.7 but 2.2.6 · Issue #75 · stevesaliman/gradle-cobertura-plugin · GitHubに書かれている通りで、
使用するcoberturaのバージョンが上がってslf4jを使用するようになったため。
プロジェクトの依存にslf4jがあれば問題がないけど、ない場合には追加してあげないとダメっぽいです。

対処内容

issueのページの一番最後に書いてある、nopのloggerを依存に追加してあげる。
こんな感じですね。

  testRuntime 'org.slf4j:slf4j-nop:1.7.12' // for cobertura

これでまともに動くようになりました。

:testClasses
:test
:generateCoberturaReport
Cobertura 2.1.1 - GNU GPL License (NO WARRANTY) - See COPYRIGHT file
Report time: 366ms

おわり。

JSR352-Batch Applicationを試してみた(Batchletをテストしてみた)

JSR352-Batch Applicationを試してみた(BatchletでDBアクセス-JPA編) - しおしお(´・ω・`)で作ったBatchletをArquillianでテストしてみた。

とりあえず、BatchletでJPA使ってるのでhttp://arquillian.org/guides/testing_java_persistence/あたりを見ながら見よう見まねでやってみた。

テスト用のライブラリの追加

Arquillian系のライブラリとGlassfish embeddedでテストを実行するためのライブラリをテストスコープに追加してあげる。

  testCompile 'org.jboss.arquillian.junit:arquillian-junit-container:1.0.0.Final'
  testCompile 'org.jboss.arquillian.container:arquillian-glassfish-embedded-3.1:1.0.0.CR4'
  testCompile 'org.glassfish.main.extras:glassfish-embedded-all:4.1'

テストコード

ジョブが正常終了するケースと異常終了するケースの2つをやってみてます。

// Arquillianランナーでテストを実行する
@RunWith(Arquillian.class)
public class DbAccessBatchletTest {

    // テスト対象のアプリケーションのアーカイブを指定する
    @Deployment
    public static Archive<?> createDeployment() {
        return ShrinkWrap.create(WebArchive.class, "test.war")
                .addPackages(true, "siosio")
                .addAsResource("test-persistence.xml", "META-INF/persistence.xml");
    }

    // テストデータのセットアップや検証で使用するJPA関連のクラスをインジェクトする
    @PersistenceContext
    EntityManager em;

    @Inject
    UserTransaction utx;

    // セットアップ処理で登録対象のテーブルをクリーニング
    @Before
    public void setUp() throws Exception {
        utx.begin();
        final Query query = em.createNativeQuery("delete from USERS");
        query.executeUpdate();
        utx.commit();
    }

    // 正常に終わるケース
    // 期待通りのレコード数が登録されていること
    @Test
    public void testCompleted() throws Exception {
        final JobExecution jobExecution = TestHelper.start("db-access-job");
        assertThat("正常終了していること", jobExecution.getBatchStatus(), is(BatchStatus.COMPLETED));

        final TypedQuery<UserEntity> resultQuery = em.createNamedQuery("findUsersAll", UserEntity.class);
        final List<UserEntity> result = resultQuery.getResultList();
        assertThat("50レコード登録されていること", result.size(), is(50));
    }

    // 異常終了するケース
    // 一意制約違反が発生するであろうデータを用意してテストを流す。
    @Test
    public void testFailed() throws Exception {
        utx.begin();
        em.persist(new UserEntity(35L, "重複するとおもわれるデータ"));
        em.persist(new UserEntity(85L, "重複するとおもわれるデータ"));
        utx.commit();

        final JobExecution jobExecution = TestHelper.start("db-access-job");
        assertThat("異常終了していること", jobExecution.getBatchStatus(), is(BatchStatus.FAILED));

        final TypedQuery<UserEntity> resultQuery = em.createNamedQuery("findUsersAll", UserEntity.class);
        final List<UserEntity> result = resultQuery.getResultList();
        assertThat("登録は失敗するので、初期データの2レコードのみ存在している", result.size(), is(2));

    }
}

その他のファイル

jbatch-sample/glassfish-resources.xml at master · siosio/jbatch-sample · GitHub
これはテストで使用するDataSourceの設定を記述します。

jbatch-sample/test-persistence.xml at master · siosio/jbatch-sample · GitHub
テストで使用するJPAの設定ファイルです。上記で設定したDataSourceを使用します。

jbatch-sample/arquillian.xml at master · siosio/jbatch-sample · GitHub
Arquillianの設定ファイル?でいいのかな。Arquillianにglassfish-resources.xmlを見てねって教えてあげるために必要なようです。

実行結果

いい感じに動いた!!
f:id:sioiri:20150611232451p:plain

おわり。