しおしお

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

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

おわり。

JSR352-Batch Applicationを試してみた(BatchletでDBアクセス-JPA編)

JSR352-Batch Applicationを試してみた(Batchlet編) - しおしお(´・ω・`)の続編です。

今回は、Batchletでデータベースにアクセスしてみようかなと。データベースへのアクセス方法は、JPA使ってもいいし、素のJDBCオープンソースのライブラリを使ってもいいでしょう。

今回は、JPAを使ってみます。JPAを使った場合、Batchletの実装ではJTAトランザクションが開始されていないので、Batchretで新しいトランザクションを開始して上げる必要があります。

※Bachletの処理は下の流れで行われると仕様に書かれているので、Batchletでデータベースアクセスするのってあんまり想定されていないんかなと思ったり。でも、ガツンと一発で処理したいときはBatchletでやりたいしなぁと

11.4 Regular Batchlet Processsing

  1. <Create StepContext>
  2. <Store step level properties in StepContext>
  3. <->[StepListener.beforeStep...] // thread A
  4. <->Batchlet.process // thread A
  5. // if stop issued:
  6. <->[Batchlet.stop] // thread B, StepContext is available
  7. <->[StepListener.afterStep...] // thread A
  8. <Store StepContext persistent area>
  9. <Destroy StepContext>

さっそく実装例を簡単に。。。

Batchlet実装

ジョブ定義で指定した数分のデータをJPAを使って登録しているだけのBatchletです。登録件数のフィールドがStringになっているのは、参照実装がBatchPropertyで数値を使えないので・・・。(JBeretはString以外の型も使いそうなのはサポートしてくれているのですがね。)

package siosio.batchlet;

import javax.batch.api.AbstractBatchlet;
import javax.batch.api.BatchProperty;
import javax.enterprise.context.Dependent;
import javax.inject.Inject;
import javax.inject.Named;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.transaction.Transactional;
import javax.transaction.Transactional.TxType;

import siosio.entity.UserEntity;

@Dependent
@Named
@Transactional(TxType.REQUIRES_NEW)
public class DbAccessBatchlet extends AbstractBatchlet {

    @PersistenceContext(unitName = "batch")
    EntityManager em;

    @Inject
    @BatchProperty
    String count;

    @Override
    public String process() throws Exception {
        final int insertCount = Integer.parseInt(count);

        for (int i = 0; i < insertCount; i++) {
            em.persist(new UserEntity("name_" + (i + 1)));
        }

        return "SUCCESS";
    }
}

Entityの実装

IDと属性を1つだけ持ったEntityを作っています。とりあえず、動かすことが目的なのでこの程度で十分でしょうと。

package siosio.entity;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.NamedQuery;
import javax.persistence.Table;

@Entity
@Table(name = "users")
public class UserEntity {

    private Long id;

    private String name;

    public UserEntity() {
    }

    public UserEntity(String name) {
        this.name = name;
    }

    @Id
    @GeneratedValue
    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

ジョブ定義ファイル

上で作ったBatchletをステップに指定しています。countプロパティに50を指定しているので、50レコードが登録されます。

<job id="db-access-job" xmlns="http://xmlns.jcp.org/xml/ns/javaee" version="1.0">
  <step id="clear-users">
    <batchlet ref="dbAccessBatchlet">
      <properties>
        <property name="count" value="50" />
      </properties>
    </batchlet>
  </step>
</job>

persistence.xml

特にこれといって何もありません。。。

<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://xmlns.jcp.org/xml/ns/persistence" version="2.1">

  <persistence-unit name="batch" transaction-type="JTA">
    <provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
    <jta-data-source>jdbc/jbatch</jta-data-source>
    <exclude-unlisted-classes>false</exclude-unlisted-classes>
    <properties>
      <property name="javax.persistence.schema-generation.database.action" value="create" />
      <property name="eclipselink.logging.level" value="fine" />
    </properties>
  </persistence-unit>

</persistence>

実行結果の確認を・・・

分かりづらいけど、テーブルに50レコード入ったしちゃんと動いたんだろな感がある。

f:id:sioiri:20150607151221p:plain

ソースコード全量はこちら→siosio/jbatch-sample · GitHub


おわり。

JSR352-Batch Applicationを試してみた(Batchlet編)

JSR352(Batch Application)を動かして見たので、その記録を残しておくと。

まず、動かしてみたいレベルだったのでサクッと動かすことができそうなBatchletを使っています。
実際にバッチアプリケーションを作成する際は、BatchletにはSQL一発ですむ処理を実装したりするのだと思います。

早速コードなどなどを見ていきます。

ジョブ定義

ジョブ定義は、META-INF/batch-jobs配下にxmlファイルで作成する必要があります。
こんな感じですね。
f:id:sioiri:20150606004658p:plain

ファイルの内容は下のようになっています。
そんな難しい構造ではないのでさくっと作れます。jobのid属性には、xmlのファイル名の拡張子をなくしたものを設定します。
今回は、Batchletを動かすので、ステップ定義(step id="step"の部分)にbatchletを定義します。
ref属性には、cdiで管理されているBatchletの名前を設定します。ref属性には、FQCNでBatchletを指定できますが、CDIの管理対象にならないので@Injectが効かなかったりします。(JobContextやStepContextは特別扱いされているっぽくインジェクションされるようです。)

<job id="batchletSample" xmlns="http://xmlns.jcp.org/xml/ns/javaee" version="1.0">
  <step id="step">
    <batchlet ref="helloWorldBatchlet" />
  </step>
</job>

Batchletの実装

Batchletの処理はprocessメソッドで行うので、とりあえず実行したジョブ名称をログ(標準出力)に出してみます。

Namedアノテーションをはっているので、ジョブxmlにはこのオブジェクトの名前を設定するだけで良くなります。(前述のジョブxmlのbatchlet部のように)

import javax.batch.api.AbstractBatchlet;
import javax.batch.runtime.context.JobContext;
import javax.enterprise.context.Dependent;
import javax.inject.Inject;
import javax.inject.Named;

@Named
@Dependent
public class HelloWorldBatchlet extends AbstractBatchlet {

    // ジョブ名称を取得するためにJobContextをインジェクション
    @Inject
    JobContext jobContext;

    @Override
    public String process() throws Exception {
        System.out.format("**************************************************%n"
                        + "  job name: %s%n  **************************************************",
                jobContext.getJobName());
        return "SUCCESS";
    }
}

バッチ実行用のAPI

jax-rs使って、指定されたバッチジョブを実行する感じにしています。kotlinで書いていますが、Javaで書いてもあまり変わらないと思います。
戻り値は、ジョブの実行を一意に識別できるExecutionIdとしています。このIDは、ジョブのリスタート時に指定したりするので割りと重要な値となっています。

ジョブの実行は、BatchRuntimeからJobOperatorを取得してstartを呼び出すだけです。
このstartメソッドには、ジョブ名称(ジョブxml拡張子を除いたもの)を指定します。2番めのパラメータにはバッチ引数を指定するのですが今回は不要なのでnullを渡しています。

@Path(value = "/")
open class BatchResource {

  @GET
  @Path(value = "/run/{jobName}")
  open fun start(PathParam("jobName") jobName: String): String {
    val jobOperator = BatchRuntime.getJobOperator()
    val executionId = jobOperator.start(jobName, null)
    return executionId.toString()
  }
}

実行してみよう

端折りますが、バッチ処理と実行用APIglassfishにデプロイして実行します。glassfishには、JSR352のRIのjBatchがのっています。SE環境でも実行することは可能ですが、面倒臭そうだったのでglassfish上で動かす方を試してみました。

実行は、IntelliJさんのRest Clientからやってみます。
f:id:sioiri:20150606010538p:plain

実行してみると、レスポンスとしてExecutionIdが戻ってきます
f:id:sioiri:20150606011031p:plain

Batchletで行っているジョブ出力の結果も確認できます。
f:id:sioiri:20150606011347p:plain

一応glassfishの管理コンソールからも実行結果を確認できます。
f:id:sioiri:20150606011235p:plain

思っていたよりサクッと動いた感があります。

ソースコード全量はこちら→siosio/jbatch-sample · GitHub


おわり。

IntelliJのGenerateメニューで生成されるアクセッサにJavadocも出力するように

IntelliJ14.1から、Generateメニューで生成するアクセッサのフォーマットがカスタマイズできるようになりました。
これで、旧バージョンでは出来なかったJavadocのカスタマイズなんかもできるようになるので、テンプレートの編集手順まとめてみました。

アクセッサを生成するためのGenerateメニューを開く

こんな感じに選択候補が出てくるので、どれか選びましょう
f:id:sioiri:20150219130048p:plain

生成されるコードのテンプレートを選択する

14.1からは、こんな感じにテンプレートを選択できるようになっています。
デフォルトだと、GetterはIntelliJ DefaultのみでSetterはIntelliJ DefaultとBuilderが選べます。が、これらのテンプレートはJavadocは生成してくれないので、おれおれテンプレートを作ってあげます。
f:id:sioiri:20150219130246p:plain

テンプレートの追加画面を開く

下の画像の手順でテンプレート編集画面を開きます。新規でテンプレート作るの無理なので、IntelliJ Defaultを選択してコピーしてあげましょう。
f:id:sioiri:20150219130536p:plain

テンプレートにJavadocを追加

画像の枠線の中のように好みのJavadocコメントを追加してあげます。フィールド名は、$field.nameで取ってこれます。($fieldが持ってるプロパティは、Ctrl + Spaceでリスト表示されます。)
f:id:sioiri:20150219130848p:plain

生成されたコード

テンプレート通りに生成してくれました。

    /**
     * get test.
     *
     * @return test
     */
    public String getTest() {
        return test;
    }

IntelliJでクラスのヘッダーコメントをプロジェクトごとカスタマイズしよう

@さんの下のツィートを見て、そういえば
IntelliJ IDEA 14.1 EAP is Available! | JetBrains IntelliJ IDEA Blogにファイルテンプレートをプロジェクトで持てるみたいなこと書いてあったなと思って調べてみた。

IntelliJ IDEA14.1のFile Templateの設定画面に行くと、右上にSchemaなるリストボックスが増えてる!!
これを、Projectにしてあげてテンプレートいじると、その設定はプロジェクトのみに有効となります!なのでauthorをプロジェクトごと変えるとか割りと簡単にできる!!

この画像で、選んでるFile HeaderがClassのJavadocになる部分です。
f:id:sioiri:20150210224505p:plain

14.0系の設定画面だと、下の画像のように右上のリストボックスがない!!残念な感じです。
f:id:sioiri:20150210223741p:plain

ちなみに、Generate -> Getter(Setter)の生成ちと変わってて、生成するものをメニューで選択すると、下のようなメニューが出るようになってた。
f:id:sioiri:20150210224312p:plain

アクセッサのテンプレート編集方法はこちら→
http://siosio.hatenablog.com/entry/2015/02/19/131405


おわり。