Tomcat 7.0.27 Integration with Atomikos 3.7.1
Recently, I upgraded Tomcat to version 7.0.27 and Atomikos to version 3.7.1. I read this page:
Tomcat7Integration35 but not all the mentioned changes were applicable for Tomcat 7.0.27.
Edit 'server.xml'
As it is mentioned in this page:
'Edit server.xml' five listeners should be added to this page. But there are two classes that don't exist in this version of Tomcat:
<Listener className="org.apache.catalina.mbeans.ServerLifecycleListener" />
<Listener className="org.apache.catalina.storeconfig.StoreConfigLifecycleListener"/>
So, by excluding these two listeners and applying all other changes mentioned in
Tomcat7Integration35, this integration works.
But I realized that the auto-publish does not work for my application and none of the JNDI resources defined in 'context.xml' were found.
In order to solve this problem, I made a work around 'EnhancedTomcatAtomikosBeanFactory.java' to check and close if any 'AtomikosConnectionFactoryBean' and 'AbstractDataSourceBean' exists. (Source code is attached to this page:
'atomikos-integration-extension-3.7.1-patch.zip')
'EnhancedTomcatAtomikosBeanFactory.java'
package com.atomikos.tomcat;
import java.util.Enumeration;
import java.util.Hashtable;
import javax.jms.JMSException;
import javax.naming.Context;
import javax.naming.Name;
import javax.naming.NamingException;
import javax.naming.RefAddr;
import javax.naming.Reference;
import javax.naming.spi.ObjectFactory;
import org.apache.naming.ResourceRef;
import org.apache.naming.factory.Constants;
import com.atomikos.beans.PropertyException;
import com.atomikos.beans.PropertyUtils;
import com.atomikos.jdbc.AbstractDataSourceBean;
import com.atomikos.jdbc.AtomikosDataSourceBean;
import com.atomikos.jdbc.AtomikosSQLException;
import com.atomikos.jms.AtomikosConnectionFactoryBean;
import com.atomikos.util.IntraVmObjectRegistry;
public class EnhancedTomcatAtomikosBeanFactory implements ObjectFactory {
public Object getObjectInstance(Object obj, Name name, Context nameCtx, Hashtable<?,?> environment) throws NamingException
{
if (obj instanceof ResourceRef) {
try {
Reference ref = (Reference) obj;
findAndCloseUniqueResource(ref);
String beanClassName = ref.getClassName();
Class<?> beanClass = null;
ClassLoader tcl = Thread.currentThread().getContextClassLoader();
if (tcl != null) {
try {
beanClass = tcl.loadClass(beanClassName);
} catch (ClassNotFoundException e) {
}
} else {
try {
beanClass = Class.forName(beanClassName);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
if (beanClass == null) {
throw new NamingException("Class not found: " + beanClassName);
}
if (AtomikosDataSourceBean.class.isAssignableFrom(beanClass)) {
return createDataSourceBean(ref, beanClass);
} else if (AtomikosConnectionFactoryBean.class.isAssignableFrom(beanClass)) {
return createConnectionFactoryBean(ref, beanClass);
} else {
throw new NamingException("Class is neither an AtomikosDataSourceBean nor an AtomikosConnectionFactoryBean: " + beanClassName);
}
} catch (Exception ex) {
throw (NamingException) new NamingException("error creating AtomikosDataSourceBean").initCause(ex);
}
} else {
return null;
}
}
/**
* create a DataSourceBean for a JMS datasource
*
* @param ref
* @param beanClass
* @return
* @throws InstantiationException
* @throws IllegalAccessException
* @throws PropertyException
* @throws AtomikosSQLException
* @throws JMSException
*/
private Object createConnectionFactoryBean(Reference ref, Class<?> beanClass) throws InstantiationException, IllegalAccessException, PropertyException, JMSException
{
AtomikosConnectionFactoryBean bean = (AtomikosConnectionFactoryBean) beanClass.newInstance();
int i = 0;
Enumeration<RefAddr> en = ref.getAll();
while (en.hasMoreElements()) {
RefAddr ra = en.nextElement();
String propName = ra.getType();
if (propName.equals(Constants.FACTORY) || propName.equals("singleton") || propName.equals("description") || propName.equals("scope") || propName.equals("auth")) {
continue;
}
String value = (String) ra.getContent();
PropertyUtils.setProperty(bean, propName, value);
i++;
}
bean.init();
return bean;
}
/**
* create a DataSourceBean for a JDBC datasource
*
* @param ref
* @param beanClass
* @return
* @throws InstantiationException
* @throws IllegalAccessException
* @throws PropertyException
* @throws AtomikosSQLException
*/
private Object createDataSourceBean(Reference ref, Class<?> beanClass) throws InstantiationException, IllegalAccessException, PropertyException, AtomikosSQLException
{
AtomikosDataSourceBean bean = (AtomikosDataSourceBean) beanClass.newInstance();
int i = 0;
Enumeration<RefAddr> en = ref.getAll();
while (en.hasMoreElements()) {
RefAddr ra = en.nextElement();
String propName = ra.getType();
if (propName.equals(Constants.FACTORY) || propName.equals("singleton") || propName.equals("description") || propName.equals("scope") || propName.equals("auth")) {
continue;
}
String value = (String) ra.getContent();
PropertyUtils.setProperty(bean, propName, value);
i++;
}
bean.init();
return bean;
}
/**
* Check and close if any AtomikosConnectionFactoryBean and AbstractDataSourceBean exists
* @param ref
*/
private void findAndCloseUniqueResource(Reference ref){
Enumeration<RefAddr> all = ref.getAll();
while(all.hasMoreElements()) {
RefAddr refAddrElement = all.nextElement();
if(refAddrElement.getType().equals("uniqueResourceName")){
closeIfExist(refAddrElement.getContent().toString());
}
}
}
/**
* Close the existing AtomikosConnectionFactoryBean and AbstractDataSourceBean.
* @param aName
*/
private void closeIfExist(String aName){
try {
Object o = IntraVmObjectRegistry.getResource(aName);
if (o != null) {
try {
if(o instanceof AtomikosConnectionFactoryBean) {
AtomikosConnectionFactoryBean o1 = (AtomikosConnectionFactoryBean)o;
o1.close();
}
else if (o instanceof AbstractDataSourceBean){
AbstractDataSourceBean o1 = (AbstractDataSourceBean)o;
o1.close();
}
}
catch(Exception se) {
se.printStackTrace();
}
IntraVmObjectRegistry.removeResource(aName);
}
} catch (Exception e) {
}
}
}
Edit 'context.xml'
"com.atomikos.tomcat.EnhancedTomcatAtomikosBeanFactory" should be set as factory for both JDBC and JMS connection factory resources. Below, you can see an example of 'context.xml':
<?xml version="1.0" encoding="UTF-8"?>
<!-- The contents of this file will be loaded for each web application -->
<Context>
<!-- Default set of monitored resources -->
<WatchedResource>WEB-INF/web.xml</WatchedResource>
<!-- Atomikos Support for the Tomcat server - register Atomikos as java:comp/UserTransaction -->
<Transaction factory="com.atomikos.icatch.jta.UserTransactionFactory" />
<!-- Also register Atomikos TransactionManager as java:comp/env/TransactionManager -->
<Resource name="TransactionManager"
auth="Container"
type="com.atomikos.icatch.jta.UserTransactionManager"
factory="org.apache.naming.factory.BeanFactory" />
<!-- Spring LoadTimeWeaver Support for the Tomcat server. -->
<Loader loaderClass="org.springframework.instrument.classloading.tomcat.TomcatInstrumentableClassLoader"
useSystemClassLoaderAsParent="false"/>
<Resource name="jdbc/MyDb"
auth="Container"
type="com.atomikos.jdbc.AtomikosDataSourceBean"
factory="com.atomikos.tomcat.EnhancedTomcatAtomikosBeanFactory"
uniqueResourceName="MyDb_Resource"
maxPoolSize="8"
xaDataSourceClassName="org.apache.derby.jdbc.ClientXADataSource"
xaProperties.databaseName="MyDb"
xaProperties.connectionAttributes="serverName=localhost;portNumber=1527;user=USER;password=PASSWORD;create=true"/>
<Resource name="jms/ConnectionFactory"
auth="Container"
type="com.atomikos.jms.AtomikosConnectionFactoryBean"
factory="com.atomikos.tomcat.EnhancedTomcatAtomikosBeanFactory"
uniqueResourceName="ConnectionFactory_Resource"
xaConnectionFactoryClassName="org.apache.activemq.ActiveMQXAConnectionFactory"
xaProperties.brokerURL="tcp://localhost:61616?daemon=true"/>
</Context>
Copy patched - Atomikos Integration Extension library
Copy
atomikos-integration-extension-3.7.1-20120529.jar into TOMCAT_HOME/lib folder.
