JBossESB上でJPAを実現する方法

JBossESB上でDBアクセスってどうやるんだろうと思って調べてみた。
単純にDBアクセスするならNotifySqlTableとかを使えばいいのかもしれないけど、トランザクション管理もしたいしな…と思い良い方法ないのかなと思って調べてみた。

するとドンピシャ、まさに求めている情報を発見。
Combining ESB, JPA, Hibernate, JTA and Spring |JBoss Developer

JBossESBには「AbstractSpringAction」なる基底クラスが用意されているので、それを継承してSpring駆動なJPA設定をしなさいとのこと。(POJOアノテーションベースでないのが少し残念だけど。。。)

さっそくやってみたらいきなり問題に直面した。
どうやら「jbossesb-rosetta.jar」の中にAbstractSpringActionが存在しないらしくコンパイルが通らない。。
マジかよ〜と思って調べて
AbstractSpringAction location in jboss |JBoss Developer
を発見。
AbstractSpringActionは「${JBOSS_HOME}/server//deploy/spring.esb/jbossesb-spring.jar」にあるよ、とのことだったので以下のようにpom.xmlに定義を追加した。

◆pom.xml

	<properties>
		<jboss.rosetta.version>4.10-SNAPSHOT</jboss.rosetta.version>
		<jboss.spring.lib>${JBOSS_HOME}/server/sample/deploy/spring.esb</jboss.spring.lib>
	</properties>

	<dependencies>
		<dependency>
			<groupId>jboss.soa.spring</groupId>
			<artifactId>jbossesb-spring</artifactId>
			<version>${jboss.rosetta.version}</version>
			<type>jar</type>
			<scope>system</scope>
			<systemPath>${jboss.spring.lib}/jbossesb-spring.jar</systemPath>
		</dependency>
	<dependencies>


さてここからは本題のJPA設定。
今回はサンプルとして「Id, Data」の2カラムあって「Id」がPrimaryキーというエンティティを作成した。

◆SampleEntity.java

package org.sample.soa.esb.entity;

import javax.persistence.Entity;
import javax.persistence.Id;

@Entity(org.sample.soa.esb.entity.SampleEntity)
public class SampleEntity {

	@Id
	private int id;

	private String data;

	public int getId() {
		return id;
	}

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

	public String getData() {
		return data;
	}

	public void setData(String data) {
		this.data = data;
	}

}

エンティティにアクセスするためにサービスクラスを用意。

◆SampleService.java

package org.sample.soa.esb.service;

import org.sample.soa.esb.entity.SampleEntity;

public interface SampleService {

	public void insert(int id, String data);

	public void delete(int id);

	public SampleEntity find(int id);
}

◆SampleServiceImpl.java

package org.sample.soa.esb.service.impl;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.PostPersist;
import javax.persistence.PrePersist;

import org.sample.soa.esb.entity.SampleEntity;
import org.sample.soa.esb.service.SampleService;
import org.springframework.transaction.annotation.Transactional;

@Transactional
public class SampleServiceImpl implements SampleService {

	@PersistenceContext
	private EntityManager em;

	public void insert(int id, String data) {
		SampleEntity he = new SampleEntity();
		he.setId(id);
		he.setData(data);
		em.persist(he);
	}

	public void delete(int id) {
		SampleEntity he = new SampleEntity();
		he.setId(id);
		em.remove(he);
	}

	public SampleEntity find(int id) {
		return em.find(SampleEntity.class, id);
	}
}

アクションクラスはHTTPでリクエストを受け、パラメータで指定されたデータをINSERTするような仕様とした。

◆SampleAction.java

package org.sample.soa.esb.actions;

import java.util.Map;

import org.jboss.soa.esb.actions.AbstractSpringAction;
import org.jboss.soa.esb.actions.ActionLifecycleException;
import org.jboss.soa.esb.helpers.ConfigTree;
import org.jboss.soa.esb.http.HttpRequest;
import org.jboss.soa.esb.message.Message;
import org.sample.soa.esb.entity.SampleEntity;
import org.sample.soa.esb.service.SampleService;
import org.springframework.beans.BeansException;

public class SampleAction extends AbstractSpringAction {

	public SampleAction(ConfigTree configTree) {
		super(configTree);
	}

	public Message process(Message message) throws BeansException,
			ActionLifecycleException {

		HttpRequest request = HttpRequest.getRequest(message);
		Map<String, String[]> params = request.getQueryParams();
		String tmp = ((String[]) params.get("id"))[0];
		String data = ((String[]) params.get("data"))[0];

		if (tmp == null || data == null) {
			throw new RuntimeException("can't insert data.");
		}

		SampleService service = (SampleService) getBeanFactory().getBean(
				"sampleService");

		int id = Integer.parseInt(tmp);
		service.insert(id, data);
		SampleEntity entity = service.find(id);
		message.getBody().add(
				"Result : id = [" + entity.getId() + "], data = [" + entity.getData()
						+ "]");
		// service.delete(100);
		return message;
	}
}

jboss-esb.xml

<?xml version="1.0"?>
<jbossesb parameterReloadSecs="5"
	xmlns="http://anonsvn.labs.jboss.com/labs/jbossesb/trunk/product/etc/schemas/xml/jbossesb-1.2.0.xsd"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://anonsvn.labs.jboss.com/labs/jbossesb/trunk/product/etc/schemas/xml/jbossesb-1.2.0.xsd http://anonsvn.jboss.org/repos/labs/labs/jbossesb/trunk/product/etc/schemas/xml/jbossesb-1.2.0.xsd">

	<services>
		<service name="JPA" description="" category="JPAService" invmScope="GLOBAL">
			<listeners>
				<http-gateway name="jpa-gw" />
			</listeners>
			<actions>
				<action name="jpa" class="org.sample.soa.esb.actions.SampleAction">
					<property name="springContextXml" value="applicationContext.xml" />
				</action>
			</actions>
		</service>
	</services>
</jbossesb>

◆persistence.xml

<persistence xmlns="http://java.sun.com/xml/ns/persistence"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd"
	version="1.0">

	<persistence-unit name="sample">
		<provider>org.hibernate.ejb.HibernatePersistence</provider>
		<jta-data-source>java:/DefaultDS</jta-data-source>
		<properties>
			<property name="jboss.entity.manager.factory.jndi.name"
				value="jndi-name/sample" />
			<property name="hibernate.dialect" value="org.hibernate.dialect.HSQLDialect" />
			<property name="hibernate.hbm2ddl.auto" value="create-drop" />
			<property name="hibernate.transaction.flush_before_completion"
				value="true" />
			<property name="hibernate.show_sql" value="true" />
			<property name="hibernate.format_sql" value="true" />
			<property name="hibernate.use_sql_comments" value="false" />
			<property name="hibernate.max_fetch_depth" value="3" />
		</properties>
	</persistence-unit>
</persistence>


◆applicationContext.xml

<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
	xmlns:tx="http://www.springframework.org/schema/tx" xmlns:jee="http://www.springframework.org/schema/jee"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
           http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
           http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd
           http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-2.5.xsd">

	<jee:jndi-lookup id="datasource" jndi-name="jndi-name/sample"
		lookup-on-startup="false" expected-type="javax.persistence.EntityManagerFactory" />

	<tx:annotation-driven />
	<bean id="transactionManager"
		class="org.springframework.transaction.jta.JtaTransactionManager" />

	<bean
		class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor" />

	<bean id="sampleService"
		class="org.sample.soa.esb.service.impl.SampleServiceImpl" />

</beans>

ここで大事なのがこの設定。

<jee:jndi-lookup id="datasource" jndi-name="jndi-name/sample"
	lookup-on-startup="false" expected-type="javax.persistence.EntityManagerFactory" />

Combining ESB, JPA, Hibernate, JTA and Spring |JBoss Developer
ここにもコメントされているんだが、「lookup-on-startup」と「expected-type」がないとアプリケーションのロード順の問題でデプロイに失敗することがある。(自分の場合はホットデプロイ時に必ずエラーが発生した。)
そのため、この2つを忘れず設定することが必要。


設定はここまで。
するとこんな感じで

うん、うまくいった。