Java EE 7 検証環境構築(13) jBatch REST サービス経由で実行する
- Java EE 7 検証環境構築(1) WildFly + JBoss Tools で EARプロジェクトを作成し Arquillian で ユニットテストをグリーンにするところまで
- Java EE 7 検証環境構築(2) WildFly に DataSourceを作成
- Java EE 7 検証環境構築(3) JPAからMySQLに接続するユニットテストをArquillianで実行
- Java EE 7 検証環境構築(4) Java EE での DI(Dependency Injection) および CDI(Contexts and Dependency Injection)をながめる
- Java EE 7 検証環境構築(5) JBoss Toolsが生成したサンプルソースのCDIを確認する
- Java EE 7 検証環境構築(6) JPA エンティティの作成と挿入
- Java EE 7 検証環境構築(7) JPA 問い合わせ(1) 名前付きクエリを使ってみる。テストでトランザクションも意識する
- Java EE 7 検証環境構築(8) JPA 問い合わせ(2) 動的クエリとCriteria API を試す
- Java EE 7 検証環境構築(9) jBatch 概要をおさえる
- Java EE 7 検証環境構築(10) JBoss Tools で作成した EARプロジェクトをJava EE 6 から 7 に変更する
- Java EE 7 検証環境構築(11) jBatch用 プロジェクトの作成を行う
- Java EE 7 検証環境構築(12) jBatch 簡易サンプル作成と Arquillian でユニットテスト
- Java EE 7 検証環境構築(13) jBatch REST サービス経由で実行する
- Java EE 7 検証環境構築(14) WildFly の管理をGUIで行う
- Java EE 7 検証環境構築(15) WildFly を サービスとして設定する(Windows/Linux)
- Java EE 7 検証環境構築(16) WildFly と Apache を mod_jk で連携させる(Widows)
EJBプロジェクトでのユニットテストができるようになったので、Webプロジェクトからの呼び出しのユニットテストを実施出来るようにしてみる。
1.Webプロジェクトでのユニットテスト
batch-web プロジェクトの pom.xml に以下の設定を追加する。
batch-web プロジェクトに REST Webサービスのエントリーポイントを作成し、そこからバッチを起動させる。
1.1 Java EE API の参照
まず、Webプロジェクトでは、EJBとjBatch の参照がされていないので、pom.xml の dependencies エレメント配下に以下を追記する。
<!-- Import the EJB API, we use provided scope as the API is included in JBoss AS 7 --> <dependency> <groupId>org.jboss.spec.javax.ejb</groupId> <artifactId>jboss-ejb-api_3.2_spec</artifactId> <scope>provided</scope> </dependency> <dependency> <groupId>javax.batch</groupId> <artifactId>javax.batch-api</artifactId> <version>1.0</version> <scope>provided</scope> </dependency>
1.2 ユニットテスト用ライブラリ参照の追加
また、Arquillian でテストを実施するための依存関係も追記する。
<!-- Test scope dependencies --> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <scope>test</scope> </dependency> <!-- Optional, but highly recommended --> <!-- Arquillian allows you to test enterprise code such as EJBs and Transactional(JTA) JPA from JUnit/TestNG --> <dependency> <groupId>org.jboss.arquillian.junit</groupId> <artifactId>arquillian-junit-container</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.jboss.arquillian.protocol</groupId> <artifactId>arquillian-protocol-servlet</artifactId> <scope>test</scope> </dependency>
1.3 Maven プロファイル
Maven プロファイルの定義。WildFly を先に実行させておいて、テスト時にデプロイするプロファイル(arq-wildfly-remote) と、WildFly 停止状態からテストを実行するプロファイルを(arq-wildfly-managed)定義する。
どちらを利用するかは、プロジェクトのコンテキストメニューから、Maven ー Select Maven Profiles で選択する。
<profile> <!-- The default profile skips all tests, though you can tune it to run just unit tests based on a custom pattern --> <!-- Seperate profiles are provided for running all tests, including Arquillian tests that execute in the specified container --> <id>default</id> <activation> <activeByDefault>true</activeByDefault> </activation> <build> <plugins> <plugin> <artifactId>maven-surefire-plugin</artifactId> <version>${version.surefire.plugin}</version> <configuration> <skip>true</skip> </configuration> </plugin> </plugins> </build> </profile> <profile> <!-- https://www.typea.info/blog/index.php/2014/04/28/java_ee_7_1_wildfly_jboss_tools_ear_arquillian --> <id>arq-wildfly-managed</id> <dependencies> <dependency> <groupId>org.wildfly</groupId> <artifactId>wildfly-arquillian-container-managed</artifactId> <version>8.0.0.Final</version> <scope>test</scope> </dependency> </dependencies> </profile> <profile> <!-- https://www.typea.info/blog/index.php/2014/04/28/java_ee_7_1_wildfly_jboss_tools_ear_arquillian --> <id>arq-wildfly-remote</id> <dependencies> <dependency> <groupId>org.wildfly</groupId> <artifactId>wildfly-arquillian-container-remote</artifactId> <version>8.0.0.Final</version> <scope>test</scope> </dependency> </dependencies> </profile> </profiles>
2. REST サービス
作成した、バッチをRESTサービスから呼び出すコードを書く。
@Path("/simplejob") @RequestScoped public class SimpleJobRestService { @Inject private Logger log; @GET @Produces(MediaType.APPLICATION_JSON) public String execute() throws Exception { JobOperator jobOperator = BatchRuntime.getJobOperator(); long executionId = jobOperator.start("simplejob", new Properties()); BufferedReader reader = new BufferedReader(new FileReader("c:\\work\\test_out.txt")); StringBuilder buf = new StringBuilder(); String line = null; while((line = reader.readLine()) != null) { buf.append(line).append("\n"); } return buf.toString(); } }
3. Arquillian のテストケース
バッチを呼び出す、テスト対象のRESTサービスをInjectし、テストする。
@RunWith(Arquillian.class) public class SimpleJobRestServiceTest { @Deployment public static WebArchive createDeployment() { WebArchive war = ShrinkWrap.create(WebArchive.class, "test.war") .addPackage("info.typea.tallarico.job.simplejob") .addPackage("info.typea.tallarico.rest") .addAsWebInfResource(EmptyAsset.INSTANCE, ArchivePaths.create("beans.xml")) .addAsResource("META-INF/batch-jobs/simpleJob.xml") .addClasses(Resources.class) .addClasses(WebResources.class) ; System.out.println(war.toString(true)); return war; } @Inject Logger logger; @Inject SimpleJobRestService simpleJobService; @Test public void executeTest() throws Exception { assertNotNull(simpleJobService); logger.info("\n== BATCH REST SERVICE RESULT ==\n" + simpleJobService.execute()); } }
4.テスト結果
OK!
5.実際にバッチEARをテストする
Server から、WildFlyを選択し、コンテキストメニュー Add and Remove から、バッチEARを追加する。
起動すると、以下のエラー。
- Producer Method [Logger] with qualifiers [@Any @Default] declared as [[BackedAnnotatedMethod] @Produces public info.typea.tallarico.util.Resources.produceLog(InjectionPoint)]
たしかに、現状、Logger をインジェクションしたときに、EJBプロジェクトに含まれるLoggerと、EJBバッチプロジェクトに含まれるLoggerが曖昧になっている。
特定のBeanに対していくつかの実装が提供される場合(例えば、あるインターフェースに対して複数の実装クラスが存在する場合)、限定子を利用してどの実装をインジェクトするのかを特定できる
うーむ。Logger をProduceしており衝突しているどちらかの実装を削除すればよいのだが、せっかくなので、限定子を試してみる。
CDI関連のエラーのほとんどはデプロイ時に発生するようだ。
ちなみに、CDIについて、以下のスライドがわかりやすかった。
5.1 限定子を利用してみる
ということで、せっかくの機会なので限定子を利用してみる。
限定子アノテーションの宣言
package info.typea.tallarico.job.util; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import javax.inject.Qualifier; import static java.lang.annotation.ElementType.TYPE; import static java.lang.annotation.ElementType.METHOD; import static java.lang.annotation.ElementType.FIELD; import static java.lang.annotation.ElementType.PARAMETER; import java.lang.annotation.Target; @Qualifier @Retention(RetentionPolicy.RUNTIME) @Target({TYPE, METHOD, FIELD, PARAMETER}) public @interface BatchResource {}
Produce メソッドをアノテートする
public class Resources { @Produces @BatchResource public Logger produceLog(InjectionPoint injectionPoint) { return Logger.getLogger(injectionPoint.getMember().getDeclaringClass().getName()); } }
インジェクション箇所をアノテートする
@Inject @BatchResource Logger logger;
これでOKのはず
6.ブラウザからRESTサービスを直接呼び出す
バッチアプリケーションのコンテキストパスは、@ApplicationPath アノテーションで指定する。
@ApplicationPath("/batch") public class JaxRsActivator extends Application { /* class body intentionally left blank */ }
RESTサービスのURIは、上記 2.RESTサービスで掲げたクラスのアノテーションで指定。
OKブラウザから、バッチを呼び出すことが出来た!!
ちなみに、ここまで試したソースコード
https://github.com/pppiroto/Tallarico.git