The old JOTM driver is not longer supported and while other alternatives exist our project did not longer need the complexities of distributed transactions. I thought moving back to pure Spring + JPA + Hibernate was going to be easy however it took a while to get that downgrade right. Here are the steps we followed
Eliminate all dependencies from tomcat:
cd /opt/tomcat/lib
mv -f carol-iiop-delegate.jar carol-interceptors.jar howl.jar jotm-core.jar ow2-connector-1.5-spec.jar ow2-jta-1.1-spec.jar xapool-1.6.beta.jar ~/
Remove dependencies from project:
<!-- Remove the jotm completely when confirmed the new config works
<dependency>
<groupId>org.ow2.jotm</groupId>
<artifactId>jotm-core</artifactId>
<version>2.2.1</version>
<scope>provided</scope>
<exclusions>
<exclusion>
<artifactId>jacorb</artifactId>
<groupId>org.jacorb</groupId>
</exclusion>
<exclusion>
<artifactId>jacorb</artifactId>
<groupId>org.jacorb</groupId>
</exclusion>
<exclusion>
<artifactId>jacorb-idl</artifactId>
<groupId>org.jacorb</groupId>
</exclusion>
<exclusion>
<artifactId>commons-logging</artifactId>
<groupId>commons-logging</groupId>
</exclusion>
<exclusion>
<artifactId>howl</artifactId>
<groupId>org.objectweb.howl</groupId>
</exclusion>
<exclusion>
<artifactId>ow2-jta-1.1-spec</artifactId>
<groupId>org.ow2.spec.ee</groupId>
</exclusion>
</exclusions>
</dependency>
-->
Remove the JotmFactoryBean which for some reason we had to include in our code (Most likely we were using a Spring version where it was not available)
/*
* Copyright 2002-2008 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.transaction.jta;
import javax.naming.NamingException;
import javax.transaction.SystemException;
import org.objectweb.jotm.Current;
import org.objectweb.jotm.Jotm;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.FactoryBean;
/**
* {@link FactoryBean} that retrieves the JTA UserTransaction/TransactionManager
* for ObjectWeb's JOTM. Will retrieve
* an already active JOTM instance if found (e.g. if running in JOnAS),
* else create a new local JOTM instance.
*
* With JOTM, the same object implements both the
* {@link javax.transaction.UserTransaction} and the
* {@link javax.transaction.TransactionManager} interface,
* as returned by this FactoryBean.
*
*
A local JOTM instance is well-suited for working in conjunction with
* ObjectWeb's XAPool, e.g. with bean
* definitions like the following:
*
*
* <bean id="jotm" class="org.springframework.transaction.jta.JotmFactoryBean"/>
*
* <bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager">
* <property name="userTransaction" ref="jotm"/>
* </bean>
*
* <bean id="innerDataSource" class="org.enhydra.jdbc.standard.StandardXADataSource" destroy-method="shutdown">
* <property name="transactionManager" ref="jotm"/>
* <property name="driverName" value="..."/>
* <property name="url" value="..."/>
* <property name="user" value="..."/>
* <property name="password" value="..."/>
* </bean>
*
* <bean id="dataSource" class="org.enhydra.jdbc.pool.StandardXAPoolDataSource" destroy-method="shutdown">
* <property name="dataSource" ref="innerDataSource"/>
* <property name="user" value="..."/>
* <property name="password" value="..."/>
* <property name="maxSize" value="..."/>
* </bean>
*
* Note that Spring's {@link JtaTransactionManager} will automatically detect
* that the passed-in UserTransaction reference also implements the
* TransactionManager interface. Hence, it is not necessary to specify a
* separate reference for JtaTransactionManager's "transactionManager" property.
*
* Implementation note: This FactoryBean uses JOTM's static access method
* to obtain the JOTM {@link org.objectweb.jotm.Current} object, which
* implements both the UserTransaction and the TransactionManager interface,
* as mentioned above.
*
* @author Juergen Hoeller
* @since 21.01.2004
* @see JtaTransactionManager#setUserTransaction
* @see JtaTransactionManager#setTransactionManager
* @see org.objectweb.jotm.Current
*/
public class JotmFactoryBean implements FactoryBean, DisposableBean {
private Current jotmCurrent;
private Jotm jotm;
public JotmFactoryBean() throws NamingException {
// Check for already active JOTM instance.
this.jotmCurrent = Current.getCurrent();
// If none found, create new local JOTM instance.
if (this.jotmCurrent == null) {
// Only for use within the current Spring context:
// local, not bound to registry.
this.jotm = new Jotm(true, false);
this.jotmCurrent = Current.getCurrent();
}
}
/**
* Set the default transaction timeout for the JOTM instance.
*
Should only be called for a local JOTM instance,
* not when accessing an existing (shared) JOTM instance.
*/
public void setDefaultTimeout(int defaultTimeout) {
this.jotmCurrent.setDefaultTimeout(defaultTimeout);
// The following is a JOTM oddity: should be used for demarcation transaction only,
// but is required here in order to actually get rid of JOTM's default (60 seconds).
try {
this.jotmCurrent.setTransactionTimeout(defaultTimeout);
}
catch (SystemException ex) {
// should never happen
}
}
/**
* Return the JOTM instance created by this factory bean, if any.
* Will be null
if an already active JOTM instance is used.
*
Application code should never need to access this.
*/
public Jotm getJotm() {
return this.jotm;
}
public Object getObject() {
return this.jotmCurrent;
}
public Class getObjectType() {
return this.jotmCurrent.getClass();
}
public boolean isSingleton() {
return true;
}
/**
* Stop the local JOTM instance, if created by this FactoryBean.
*/
public void destroy() {
if (this.jotm != null) {
this.jotm.stop();
}
}
}
Include in your project the code for IsolationSupportSessionTransactionData custom class (Just Google it) which is supposed to take care of custom levels of transaction isolations as they are not supported by the JPA specification.
Include JTA dependency in the project:
<dependency>
<groupId>javax.transaction</groupId>
<artifactId>jta</artifactId>
<version>1.1</version>
</dependency>
File persistence.xml changes:
...
<!-- Use RESOURCE_LOCAL instead of JTA -->
<persistence-unit name="persistenceUnit" transaction-type="RESOURCE_LOCAL">
...
<!-- Remove completely the below once JOTM is removed and project is working
<property name="hibernate.transaction.manager_lookup_class" value="org.hibernate.transaction.JOTMTransactionManagerLookup" />
<property name="hibernate.transaction.factory_class" value="org.hibernate.transaction.JTATransactionFactory"/>
<property name="hibernate.transaction.flush_before_completion" value="false" />
<property name="hibernate.transaction.auto_close_session" value="false" />
<property name="hibernate.current_session_context_class" value="jta" />
<property name="hibernate.connection.release_mode" value="auto" />
-->
...
File applicationContext.xml changes (Spring Application Context file):
...
<!-- Remove all XA related configuration as soon as we confirm there are no issues with the new pool -->
<!-- first XA data source -->
<!--
<bean id="innerDataSource" class="org.enhydra.jdbc.standard.StandardXADataSource" destroy-method="shutdown">
<property name="transactionManager" ref="jotm" />
<property name="driverName" value="${jdbc.driverClassName}" />
<property name="url" value="${jdbc.url}" />
<property name="user" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
</bean>
-->
<!-- first XA data source pool -->
<!--
<bean id="dataSource" class="org.enhydra.jdbc.pool.StandardXAPoolDataSource" destroy-method="shutdown">
<property name="transactionManager" ref="jotm" />
<property name="dataSource" ref="innerDataSource" />
<property name="user" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
<property name="minSize" value="${jdbc.minSize}" />
<property name="maxSize" value="${jdbc.maxSize}" />
<property name="checkLevelObject" value="2"/>
<property name="sleepTime" value="${jdbc.sleepTime}" />
<property name="lifeTime" value="${jdbc.lifeTime}" />
<property name="jdbcTestStmt" value="SELECT NOW()"/>
</bean>
-->
...
<!-- Data source not pooled -->
<bean id="dataSourceTemplate" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="${jdbc.driverClassName}" />
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
</bean>
<!-- Connection pool data source. -->
<bean id="dataSource" class="org.apache.tomcat.jdbc.pool.DataSource" destroy-method="close">
<!-- Refer to a separately created bean as a data source template to work around a quirk of Tomcat's class loader. -->
<property name="dataSource" ref="dataSourceTemplate" />
<property name="initialSize" value="${jdbc.minSize}" />
<property name="maxActive" value="${jdbc.maxSize}" />
<property name="timeBetweenEvictionRunsMillis" value="${jdbc.sleepTime}" />
<property name="maxAge" value="${jdbc.lifeTime}" />
<property name="validationQuery" value="SELECT 1"/>
</bean>
...
...
<!-- Use a JPA Dialect able to handle different transaction isolation levels -->
<bean id="Emf" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
...
<property name="jpaDialect">
<!-- <bean class="org.springframework.orm.jpa.vendor.HibernateJpaDialect" /> -->
<bean class="com.sample.jpa.HibernateExtendedJpaDialect" />
</property>
...
<!-- Stop using the JOTM transaction manager for @Transactional -->
<!-- <tx:annotation-driven transaction-manager="jtaTransactionManager" proxy-target-class="false" />
-->
<tx:annotation-driven transaction-manager="transactionManager" />