DeltaSpike DataをJava EEアプリケーションサーバー上で動かす(1)

前回のブログで紹介したDeltaSpike DataモジュールをJava EEアプリケーションサーバー上で動かしてみましょう。どのアプリケーションサーバーを使ってもよいのですが、このブログではWildFlyを使った手順を紹介します。WildFlyにはCDIコンテナとしてWeldが組み込まれていますし、H2データベースが最初からインストールされているので、DeltaSpike Dataモジュールをすぐに試してみることができます。このページの最後にサンプルプログラムのリンクをつけていますので実際にプログラムを動かしながら確認してください。

WildFlyのインストール

WildFlyはJBoss.orgにおいてオープンソースで開発されているJava EE 7に対応したアプリケーションサーバーです(以前はJBossプロジェクトのアプリケーションサーバーはJBoss ASと呼ばれていましたが、JBoss ASはVersion 7までで、Version 8からWildFlyはという名前に変更されました)。

WildFlyの最新安定バージョンは9.0.1.Finalですが、今回は10.0.0.CR4を使うことにします。以下のサイトからwildfly-10.0.0.CR4.zipをダウンロードして、適当なディレクトリで解凍してください。

WildFlyの起動と停止

WildFlyの起動方法にはStandaloneモードとDomainモードの二つがあります。ここでは、単一サーバーで確認するためStandaloneモードで起動します。

$ cd wildfly-10.0.0.CR4/bin
$ ./standalone.sh

 

以後、WildFlyを起動したままの状態で作業を進めますが、サーバーの停止の際は起動したコンソール上でCntrl-Cを押してください。

ベースとなるアプリケーションの準備

DeltaSpike Dataモジュールのサンプルはスクラッチから作るのではなくて、ありもののJava EEアプリケーションを修正して作ることにします。WildFly用のJava EE 7 Webアプリケーションを作成するMavenアーキタイプを使うことにしましょう。以下のコマンドを叩くと、tanoseam-webappという名前のMavenプロジェクトが作成されます。

$ mvn archetype:generate -DarchetypeGroupId=org.wildfly.archetype -DarchetypeArtifactId=wildfly-javaee7-webapp-archetype -DarchetypeVersion=8.2.0.Final -DgroupId=org.tanoseam.examples -DartifactId=tanoseam-webapp -Dversion=1.0.0-SNAPSHOT -DinteractiveMode=false

 

このアプリケーションはkitchensinkと呼ばれるJSF/REST/EJB/JPAを使ったWebアプリケーションです。このJPAの部分をDeltaSpike Dataモジュールを使って置き換えます。

DeltaSpikeに対応させるためのアプリケーションの修正

アプリケーション内で直接EntityManagerを操作している箇所をDeltaSpike Dataを使うように修正します。このアプリケーションでは、メンバーの検索はMemberRepository、メンバー登録はMemberRegistrationというクラスが担当していますので、それぞれ修正が必要です。

1. アプリケーションのpom.xmlにDeltaSpike Dataの依存ライブラリを追加します。バージョンは最新版の1.5.2を指定しましょう。

		<dependency>
			<groupId>org.apache.deltaspike.modules</groupId>
			<artifactId>deltaspike-data-module-api</artifactId>
			<version>${version.deltaspike}</version>
		</dependency>

		<dependency>
			<groupId>org.apache.deltaspike.modules</groupId>
			<artifactId>deltaspike-data-module-impl</artifactId>
			<version>${version.deltaspike}</version>
		</dependency>

2. tanoseam-webappプロジェクトのソースを修正して、データベースの検索部分をDeltaSpike Dataを使うように変更します。修正箇所は3つのファイルです。

  • org.tanoseam.examples.data.data.MemberRepository.javaを(前回のブログで紹介した)DeltaSpike Dataモジュールを使ったリポジトリ実装で置き換えてください。
package org.tanoseam.examples.data;

import org.tanoseam.examples.model.Member;
import org.apache.deltaspike.data.api.EntityRepository;
import org.apache.deltaspike.data.api.Query;
import org.apache.deltaspike.data.api.Repository;
import java.util.List;

@Repository
public interface MemberRepository extends EntityRepository<Member, Long> {

    @Query("select m from Member m where m.email = ?1")
	public Member findByEmail(String email);

    @Query("select m from Member m order by name")
	public List<Member> findAllOrderedByName();
}
  • 次に、RESTサービスを上記リポジトリが提供するインタフェースに合わせて修正します。
    org.tanoseam.examples.rest.MemberResourceRESTService.javaのlookupMemberById()メソッドにおいてfindById(id)をfindBy(id)へ変更してください。
@GET
@Path("/{id:[0-9][0-9]*}")
@Produces(MediaType.APPLICATION_JSON)
public Member lookupMemberById(@PathParam("id") long id) {
    //Member member = repository.findById(id);
    Member member = repository.findBy(id);
    if (member == null) {
        throw new WebApplicationException(Response.Status.NOT_FOUND);
    }
    return member;
}
  • 最後に、org.tanoseam.examples.service.MemberRegistration.javaを修正します。MemberRepositoryのBeanを変数repositoryにインジェクトして、em.persist(member)をrepository.save(member)に置き換えてください。
@Inject
private MemberRepository repository;

public void register(Member member) throws Exception {
    log.info("Registering " + member.getName());
    //em.persist(member);
    repository.save(member);
    memberEventSrc.fire(member);
}

WildFlyへのデプロイ

ソースを修正したtanoseam-webappプロジェクトをビルドし、WildFlyへデプロイします。WildFlyへのデプロイには管理コンソールを使うことも可能ですが、WildFlyへのデプロイ用のMavenプラグインがありますので、今回はそれを使います。

$ cd tanoseam-webapp
$ mvn clean package wildfly:deploy

 

アプリケーションの実行

デプロイしたアプリケーションを実行するにはWebブラウザで以下を指定します。

このアプリケーションはメンバー情報(Name, Emai, Phone#)を入力する画面を表示します。各入力項目を入力してRegisterボタンを押下するとメンバーが登録されるはずです。しかし、今回ソースを修正したものを実行すると下図のようにボタンの右側に”A JTA EntityManager cannot use getTransaction()”というエラーメッセージが表示されてしまいます。

tanoseam-webapp

WildFlyのコンソールログを見ると、DeltaSpike Dataモジュールの処理の途中でIllegalStateException例外がスローされてEJB呼び出しが失敗しているようです。スタックトレースをよく見ると、この例外はResourceLocalTransactionStrategyというクラスのgetTransaction()メソッドで発生しています。

Caused by: java.lang.IllegalStateException: A JTA EntityManager cannot use getTransaction()
at org.hibernate.jpa.spi.AbstractEntityManagerImpl.getTransaction(AbstractEntityManagerImpl.java:1333)
at org.jboss.as.jpa.container.AbstractEntityManager.getTransaction(AbstractEntityManager.java:518)
at org.apache.deltaspike.jpa.impl.transaction.ResourceLocalTransactionStrategy.getTransaction(ResourceLocalTransactionStrategy.java:370)
at org.apache.deltaspike.jpa.impl.transaction.ResourceLocalTransactionStrategy.rollbackAllTransactions(ResourceLocalTransactionStrategy.java:336)
at org.apache.deltaspike.jpa.impl.transaction.ResourceLocalTransactionStrategy.execute(ResourceLocalTransactionStrategy.java:154)
at org.apache.deltaspike.data.impl.tx.TransactionalQueryRunner.executeTransactional(TransactionalQueryRunner.java:72)
at org.apache.deltaspike.data.impl.tx.TransactionalQueryRunner.executeQuery(TransactionalQueryRunner.java:54)
at org.apache.deltaspike.data.impl.handler.QueryHandler.process(QueryHandler.java:122)
… 127 more

 

実行時エラーの回避

DeltaSpike DataはDeltaSpike JPAモジュールを使ってトランザクションを制御しています。このエラーは、DeltaSpike JPAモジュールにおけるデフォルトのトランザクションタイプがRESOURCE_LOCALであることが原因です。JPAではRESOURCE_LOCALなトランザクションの場合はEntityManager APIを使ってEntityTransactionを取得し、明示的にトランザクションを開始する必要があります。

しかし、今回のサンプルではMemberRegistryというEJB内部でDeltaSpike Dataのリポジトリ呼び出していますので、リポジトリの処理を開始する前にEJBによってトランザクションはすでに開始されています。そこで、DeltaSpike JPAモジュールはRESOURCE_LOCALなトランザクションを開始することはできず、今回のエラーに至ったと考えられます。

このエラーを回避するには、DeltaSpike Dataにおけるトランザクションの振る舞いをCMT(Container Managed Transaction)に対応させるように変更する必要があります。これを行うには、META-INF/apache-deltaspike.propertiesに以下のようにTransactionStrategyとしてContainerManagedTransactionStrategyを設定します。

globalAlternatives.org.apache.deltaspike.jpa.spi.transaction.TransactionStrategy=org.apache.deltaspike.jpa.impl.transaction.ContainerManagedTransactionStrategy

 

動作確認

エラーを回避する設定をした後にアプリケーションを再デプロイして、いくつかメンバーを登録してみましょう。表のメンバーの表示はMemberListProducerによって行なわれ、その中でMemberRepositoryのfindAllOrderedByName()メソッドが呼び出されています。表のメンバーが名前の順(order by name)になっていることを確認してください。下図の例では(idから類推できるように)David, Bob, Andyの順に登録しましたが、表示上は名前でソートされてAndy, Bob, Davidの順になっています。

members

注意:今回のプログラムではH2データベースを使っていますが、これはデモやテスト用途に適したインメモリの設定になっているため、アプリケーションの再デプロイやサーバーの再起動によってDBに登録済みのデータは消えてしまいます。本番環境でDBを利用する場合は、H2の設定を変更するか、PostgreSQLやMySQLなどの他のデータベースを検討してみてください。

サンプルプログラム

この修正済みのプログラムはこちらからダウンロードできます。

少し手間がかかりましたが、WildFlyという実行環境が準備できるとDeltaSpike Dataのさまざまな機能を実機で試すことができるようになります。次回は、Arquillianを使ってDeltaSpike Dataの機能をテストする方法を説明します。

広告

コメントを残す

以下に詳細を記入するか、アイコンをクリックしてログインしてください。

WordPress.com ロゴ

WordPress.com アカウントを使ってコメントしています。 ログアウト / 変更 )

Twitter 画像

Twitter アカウントを使ってコメントしています。 ログアウト / 変更 )

Facebook の写真

Facebook アカウントを使ってコメントしています。 ログアウト / 変更 )

Google+ フォト

Google+ アカウントを使ってコメントしています。 ログアウト / 変更 )

%s と連携中

%d人のブロガーが「いいね」をつけました。