JBoss AS5.1上でJPAを完全にSpring駆動で実行させる方法

JPAプロバイダーを起動するしくみがJavaEE5対応のアプリケーションサーバに組み込まれているため、JPAの仕様に従って、META-DATA/persistence.xmlファイルが格納されていると、アプリケーションサーバーによって読み込まれて処理されます。読み込まれたJPAの永続ユニットはローカルのJNDIコンテキストから参照できます。
しかしながら、

  • persistence.xmlの動作は環境によってかなり差異がある(パスの解決方法など)
  • 開発時は軽量なTomcatのようなサーバーで開発したい

などの理由により、アプリケーションサーバーにJPAを処理させずに、Springの軽量コンテナーに処理させるという方法も時として便利です。このような場合、アプリケーションサーバーにpersistence.xmlを認識させないための、てっとり早い方法としては、persistence.xmlをpersistece-spring.xmlなどにリネームし、これをSpringに読み込ませるという方法があります。

	<!-- JPA EntityManagerFactory の設定 -->
	<bean id="emf"
		class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
		<property name="persistenceUnitName" value="sampleDB" />
		<!-- JBoss が PersistenceUnit をロードしないようファイル名称を persistence.xml 以外に変更 -->
		<property name="persistenceXmlLocation" value="classpath*:META-INF/spring-persistence.xml" />
		<property name="dataSource" ref="dataSource" />
		<property name="jpaVendorAdapter">
			<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
				<property name="database" value="MYSQL" />
				<property name="databasePlatform" value="org.hibernate.dialect.MySQL5Dialect" />
			</bean>
		</property>
		<property name="jpaDialect">
			<bean class="org.springframework.orm.jpa.vendor.HibernateJpaDialect" />
		</property>
	</bean>

しかし、この方法ではJBoss AS5*1上で正しくJPAを動作させることができないようです。実際、以下の例外となりアプリケーションが起動しません。

java.lang.IllegalArgumentException: Can't find a persistence unit named 'null' in AbstractVFSDeploymentContext@2123658583{vfszip:/D:/development/tools/JBoss/jboss-5.1.0.GA/server/default/deploy/sample.war/}
	at org.jboss.jpa.resolvers.BasePersistenceUnitDependencyResolver.resolvePersistenceUnitSupplier(BasePersistenceUnitDependencyResolver.java:107)
	at org.jboss.web.tomcat.service.TomcatInjectionContainer.resolvePersistenceUnitSupplier(TomcatInjectionContainer.java:685)
	at org.jboss.injection.PersistenceUnitHandler.addPUDependency(PersistenceUnitHandler.java:130)

Springのアプリケーションでは部分的にJavaEEの標準アノテーションが利用できるようになっており、例えば、EJBでないPojoのDAOクラスの中で@PersisteceContextアノテーションを利用することができます。しかし、なぜか、このアノテーションJBossに認識されてしまうという問題があるようです。(本来であれば、EJBサーブレットなど特定のJavaEEコンポーネント上でしか認識してほしくないのですが。)今の場合、persistence.xmlを別名にリネームしているため永続ユニットが初期化されていないとJBossに認識されているため、@PersistenceContextアノテーションの読み込みがされた時点でチェックされて上記のエラーとなるようです。この問題について、しばらく悩んでいたのですが、以下に解決策のヒントが書かれていました。
[JBSEAM-3587] Spring example on AS 5 throwns a non-jta-data-source error while deploying - JBoss Issue Tracker
結局、対処としては、web.xmlタグにmetadata-complete="true"をつけてやればよいようです。

<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
xmlns="http://java.sun.com/xml/ns/javaee" 
xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" 
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" 
id="WebApp_ID" version="2.5" metadata-complete="true">

	<display-name>Archetype Created Web Application</display-name>

	<context-param>
		<param-name>contextConfigLocation</param-name>
		<param-value>
			classpath:appCommonContext.xml
			/WEB-INF/applicationContext.xml
		</param-value>
	</context-param>

	<listener>
		<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
	</listener>
...
</web-app>

この属性により、JBossxmlファイルによって設定が完結していると認識し、勝手にPojo内のアノテーションを探しに行く動作が抑制されるようです。さらに嬉しいことに、この属性により、起動時間も多少軽くなるようです。
ちなみに、以前からJBossとSpringの相性はよくなく、VFSから設定ファイルを読み込めず、Spring2.5まではSpring MVCがそのままでは起動すらしないという状態でした。SpringとJBossの連携については以下のプロジェクトも知られています。
Snowdrop - Cloud native Spring Boot applications
Spring3ではかなり相性の問題は解決されているようですが、古いバージョンを利用する場合には以上に紹介した連携モジュールの仕掛けが役に立つかもしれません。

*1:JBoss AS6は今のところ未確認