This is a continuation of the previous article regarding some migration points from JBoss 4.2.2-GA to JBoss 7.1.1 and, presumably, Tomcat 7.

2. The HAR archive

The HAR archive was a nice mechanism which allowed hibernate integration. A ${name}.har file was created, containing all the mappings (*.hbm.xml) and data classes (*.class), allong with a hibernate-service.xml (later renamed to service-hibernate.xml in JBoss 5). This took care of creating the SessionFactory and making it accessible through JNDI. Creating a session in code become:

<pre lang="java">InitialContext ctx = new InitialContext();
SessionFactory factory = (SessionFactory) ctx.lookup("XName");
return factory.openSession();

2.1 JBoss 4.2.2

This is the hibernate-service.xml from JBoss 4.2.2.

<pre lang="xml">
<server>
	<mbean code="org.jboss.hibernate.jmx.Hibernate" name="com.mccsoft:name=XOracleSessionFactory">
		<attribute name="DatasourceName">java:/XOracleDS</attribute>
		<attribute name="Dialect">org.hibernate.dialect.Oracle10gDialect</attribute>
		<attribute name="SessionFactoryName">java:/hibernate/XOracleSessionFactory</attribute>
		<attribute name="CacheProviderClass">net.sf.ehcache.hibernate.SingletonEhCacheProvider</attribute>

		
		<attribute name="JdbcBatchSize">20</attribute>
		<attribute name="ScanForMappingsEnabled">false</attribute>
		<attribute name="ReflectionOptimizationEnabled">false</attribute>
		<attribute name="ShowSqlEnabled">false</attribute>
		<attribute name="QueryCacheEnabled">true</attribute>
		<attribute name="SecondLevelCacheEnabled">true</attribute>
	</mbean>
</server>

Except for the ScanForMappingsEnabled all of the attributes are hibernate attributes with a name modification. For instance: hibernate.cache.use_query_cache => QueryCacheEnabled. I never understood the reason for this name modification which implied a lot of codding instead of just passing hibernate attributes with the same name.

2.2 JBoss 7.1.1

HAR archives do not exist in JBoss 7 anymore. In fact even the ServiceMBeanSupport does not exist anymore. One possibility is to use some mechanism to create the SessionFactory and inject it into the JNDI. Another possibility is to “use and not use” the new JPA api. By “use” I mean define Hibernate configuration in a persistence.xml file and use the mapping detection feature available. This would allow the plain renaming of the .har to a .jar with an added META-INF/persistence.xml file without the need to hardcode all the mappings and classes in a long list somewhere. By “not use” I mean to have the JPA initialized but use the old SessionFactory instead because there is no reason to change to the new API when the old one works quite well.
However, another problem is that JBoss 7 is bundled with Hibernate 4 and the migration might not be straightforward. However there is still a possibility to bundle a Hibernate as lower as 3.5 in your application. Here is the persistence.xml:

<pre lang="xml">
<persistence version="2.0" 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_2_0.xsd">
  <persistence-unit name="X">
    <description>X</description>
    <jta-data-source>java:/XOracleDS</jta-data-source>
    <properties>
    	
        <property name="jboss.as.jpa.providerModule" value="hibernate3-bundled"></property>
        
        
        <property name="hibernate.session_factory_name" value="java:/hibernate/XOracleSessionFactory"></property>
        <property name="hibernate.dialect" value="org.hibernate.dialect.Oracle10gDialect"></property>
        
        <property name="hibernate.cache.region.factory_class" value="net.sf.ehcache.hibernate.EhCacheRegionFactory"></property>
        
        <property name="hibernate.cache.region_prefix" value=""></property>
        <property name="hibernate.cache.provider_class" value="net.sf.ehcache.hibernate.SingletonEhCacheProvider"></property>
        
        <property name="hibernate.transaction.factory_class" value="org.hibernate.transaction.JTATransactionFactory"></property>
        <property name="hibernate.jdbc.batch_size" value="20"></property>
        <property name="hibernate.show_sql" value="false"></property>
     	<property name="hibernate.format_sql" value="false"></property>
     	<property name="hibernate.cache.use_query_cache" value="true"></property>
     	<property name="hibernate.cache.use_second_level_cache" value="true"></property>
    </properties>
  </persistence-unit>
</persistence>

I spent a lot of time figuring out some of the options above, especially the ones related to the RegionFactory as JBoss seems to default to org.hibernate.cache.infinispan.JndiInfinispanRegionFactory.

2.3 Tomcat 7

I would have expected this to be very simple and to write maybe a ServletListener which initialized Hibernate and put the SessionFactory into JNDI. However Tomcat JNDI is READ-ONLY. The only solution was to implement an ObjectFactory. However I did not wanted to loose the advantages of autodetection of mappings and classes so I ended again with a hybrid solution.

<pre lang="java">public class TomcatHibernateFactory implements ObjectFactory {
	private static Logger log = Logger.getLogger(TomcatHibernateFactory.class);

	@Override
	public Object getObjectInstance(Object obj, Name name, Context nameCtx, Hashtable environment) throws Exception {
		SessionFactory sessionFactory;
		try {
			EntityManagerFactory emf = Persistence.createEntityManagerFactory("x");
			HibernateEntityManagerFactory hemf = (HibernateEntityManagerFactory)emf;
			sessionFactory = hemf.getSessionFactory();
		} catch (Throwable e) {
			log.error("TomcatHibernateFactory.getObjectInstance", e);
			throw new NamingException(e.getMessage());
		}
		return sessionFactory;
	}
}

This creates a JPA EntityManagerFactory and converts it to the underlying hibernate implementation to access the SessionFactory. It is then used (defined) in the application META-INF/context.xml (or other context location) as:

<pre lang="xml">
<context>
        <resource auth="Container" factory="com.x.web.TomcatHibernateFactory" name="hibernate/XOracleSessionFactory" type="org.hibernate.SessionFactory"></resource>
</context>

and then in the WEB-INF/web.xml:

<pre lang="xml">   
    <resource-ref>
        <res-ref-name>hibernate/XOracleSessionFactory</res-ref-name>
        <res-type>org.hibernate.SessionFactory</res-type>
        <res-auth>Container</res-auth>
    </resource-ref>

The only tiny bit of modification in the code is the name of the JNDI resource used to access the SessionFactory with the java:comp/env prefix. For more info on Tomcat JNDI there is always the official doc.
Also the persistence.xml file has to be modified:

<pre lang="xml">
<persistence version="2.0" 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_2_0.xsd">
  <persistence-unit name="X">
    <description>X</description>
    
    <non-jta-data-source>java:/comp/env/jdbc/XOracleDS</non-jta-data-source>
    <properties>
    	
        
        <property name="hibernate.dialect" value="org.hibernate.dialect.Oracle10gDialect"></property>
        <property name="hibernate.cache.region.factory_class" value="net.sf.ehcache.hibernate.EhCacheRegionFactory"></property>
        <property name="hibernate.cache.region_prefix" value=""></property>
        <property name="hibernate.cache.provider_class" value="net.sf.ehcache.hibernate.SingletonEhCacheProvider"></property>
        
        
        <property name="hibernate.jdbc.batch_size" value="20"></property>
        <property name="hibernate.show_sql" value="false"></property>
     	<property name="hibernate.format_sql" value="false"></property>
     	<property name="hibernate.cache.use_query_cache" value="true"></property>
     	<property name="hibernate.cache.use_second_level_cache" value="true"></property>
    </properties>

  </persistence-unit>
</persistence>