Wednesday 19 September 2012

Glassfish 3.1 + Spring 3.0 + JNDI

I am writing a web application deployed in Glassfish application server 3.1. The service bean is exposed as a Spring managed bean instead of an EJB. The service bean is injected with an EntityManager, the data source of which is obtained from JNDI.

Wiring Spring with JNDI in Glassfish is more difficult than I thought.

I come across some common exceptions on which there are tons of discussions online. However, I fail to find helpful answers.

Therefore I feel necessary to write this blog to provide a simple solution.

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="jta" transaction-type="JTA">
         <provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
         <jta-data-source>jdbc/myderby</jta-data-source>
         <properties>
             <property name="eclipselink.ddl-generation" value="create-tables" />
             <property name="eclipselink.ddl-generation.output-mode" value="database" />
             <property name="eclipselink.logging.parameters" value="true" />
             <property name="eclipselink.logging.level" value="FINE" />
         </properties>
     </persistence-unit>
</persistence>


applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:jee="http://www.springframework.org/schema/jee"
     xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx"
     xsi:schemaLocation="
        http://www.springframework.org/schema/beans 
        http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
        http://www.springframework.org/schema/jee 
        http://www.springframework.org/schema/jee/spring-jee-3.0.xsd
        http://www.springframework.org/schema/context 
        http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/tx  
        http://www.springframework.org/schema/tx/spring-tx.xsd">

     <context:component-scan base-package="com.demo.controller" />
     <context:component-scan base-package="com.demo.dao" />

     <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
         <property name="persistenceUnitName" value="jta"/>
     </bean>

     <tx:annotation-driven />
 
     <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
         <property name="entityManagerFactory" ref="entityManagerFactory" />
     </bean>
  
</beans>

UserDao.java
@Repository("userDao")
public class UserDaoImpl implements UserDao {

     @PersistenceContext 
     protected EntityManager entityManager;
 
     @Transactional
     public void save(User user) {
         entityManager.persist(user);
     }
}

Deployment is fine. When dao is accessed at runtime, an exception is thrown:


java.lang.IllegalStateException:
Exception Description: Cannot use an EntityTransaction while using JTA.
     at org.eclipse.persistence.internal.jpa.transaction.JTATransactionWrapper.getTransaction(JTATransactionWrapper.java:65)
     at org.eclipse.persistence.internal.jpa.EntityManagerImpl.getTransaction(EntityManagerImpl.java:1153)
     at org.springframework.orm.jpa.DefaultJpaDialect.beginTransaction(DefaultJpaDialect.java:70)
     at org.springframework.orm.jpa.JpaTransactionManager.doBegin(JpaTransactionManager.java:332)
I change the transaction-type from "JTA" to "RESOURCE_LOCAL" in persistence.xml.

The result is even worse. Deployment is complaining:

SEVERE: Exception while preparing the app : The persistence-context-ref-name [com.demo.dao.UserDaoImpl/entityManager] in module [spring-mvc-jpa-jndi] resolves to a persistence unit called [jta] which is of type RESOURCE_LOCAL. Only persistence units with transaction type JTA can be used as a container managed entity manager. Please verify your application.

I am not surprised to see this exception though. Since this is a Java EE environment, the transaction type should only be JTA. So I change the transaction type back to JTA.

I then try a lot of approaches in vain until I see this article:

http://java.dzone.com/tips/how-use-glassfish-managed-jpa

The core value of the article is this single line:

<tx:jta-transaction-manager/>


I replace my transactionManager bean definition with this line and it works perfectly.

After some investigation, it turns out this line creates the transactionMananger instance from this class: org.springframework.transaction.jta.JtaTransactionManager.

So it also works if I replace this line with:

<bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager"/>


Now it is easy to explain why the previous transactionManager causes an exception.  Clearly the org.springframework.orm.jpa.JpaTransactionManager is the transaction manager for RESOURCE_LOCAL persistence unit while org.springframework.transaction.jta.JtaTransactionManager is the transaction manager for JTA persistence unit. The latter probably delegates all the hassle stuff such as "beginning transaction","committing" and "closing transaction" to the Java EE container while the former needs to do all this chore.

With only one persistence unit defined, I can further strip the applicationContext.xml down to the following final version.

<context:component-scan base-package="com.demo.controller" />
<context:component-scan base-package="com.demo.dao" />

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

<tx:annotation-driven />
 
<tx:jta-transaction-manager/>
I am using Spring 3.0.5.

1 comment: