7.2. Migrating Spring to Red Hat JBoss Enterprise Application Platform from a Servlet Container

This section explains how Spring applications developed for servlet containers can be migrated to take advantage of the Red Hat JBoss middleware service support and infrastructure. This is explained by using sample applications provided by Spring.
Migrating applications from a servlet container to JBoss Enterprise Application Platform addresses the following concerns:
  • Ensures that applications are compatible with the requirements of Java EE 6 standard
  • Enables integration with the Red Hat JBoss middleware services provided by the container
  • Avoids the inclusion of libraries that are provided by the application server
Except for rare situations, migrating applications does not involve changes to the Java code or the Spring bean configuration definitions that define the business logic of the application (bean wiring, aspect definitions, and controllers). The definitions that relate to the infrastructure are:
  • Session factories
  • Datasources
  • Entity managers

7.2.1. Avoiding the Inclusion of Server-provided Dependencies

Red Hat JBoss Enterprise Application Platform already provides a number of dependencies that are required by applications. This is unlike servlet containers, where applications need to package a significant number of libraries in order to provide access to certain Java EE 6 technologies. During migration, change the deployable build by removing the libraries provided by the application server. This is not only an improvement that reduces the size of the final build, but also a requirement, as the inclusion of those libraries results in classloading errors.
Some examples of libraries provided by JBoss Enterprise Application Platform and that do not need to be included in the application are:
  • The Java EE 6 APIs (like JPA, JSTL, and JMS)
  • Hibernate (including Hibernate as a JPA provider)
  • JSF
These components may be required as compile-time dependencies, so include them in the project, but do not package them in the final build. In Maven builds, you can achieve this by setting the scope to provided and in Red Hat JBoss Developer Studio by unchecking the Exported flag for the dependency. Other build systems have their own respective mechanisms of achieving the same goal.
However, components and frameworks that are not part of the Java EE 6 implementation but are certified for use with JBoss Enterprise Application Platform 6 must be included in the application or installed in the application server as subsystems. These components include:
  • Spring Framework
  • Snowdrop
  • Facelets
  • RichFaces

7.2.2. Migrating Datasource Definitions

Most servlet containers support JNDI-bound resources, and binding Datasources in JNDI allows managing connectivity parameters such as URLs, credentials, and pool sizes independent of the application code. However, Spring applications often rely on independent connection pool bean definitions, like those used by commons-dbcp or c3po (see Example 7.1, “Example commons-dbcp DataSource definition in a servlet container”).

Example 7.1. Example commons-dbcp DataSource definition in a servlet container

<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
	<property name="driverClassName" value="org.postgresql.Driver" />
	<property name="url" value="jdbc:postgresql://exampleHost/exampleDatabase" />
	<property name="username" value="user" />
	<property name="password" value="password" />
</bean>
Red Hat JBoss Enterprise Application Platform provides an efficient way of deploying and maintaining managed datasources (see Example 7.2, “Using a JBoss Enterprise Application Platform managed datasource in Spring” for more details, and JBoss Enterprise Application Platform Administration and Configuration Guide for information on deploying them) that are JNDI-accessible. In order to replace the datasource defined in the commons-dbcp example with a managed JNDI-accessible datasource, replace the bean definition with a JNDI object reference as in the following example.

Example 7.2. Using a JBoss Enterprise Application Platform managed datasource in Spring

<jee:jndi-lookup id="dataSource" jndi-name="java:jboss/datasources/ExampleDS" expected-type="javax.sql.DataSource"/>
Preserving the bean id is important when the bean is injected by name. Indicating the expected type specifically is important for @Autowired scenarios.

7.2.3. Migrating Hibernate SessionFactories to JTA

To run applications on JBoss Enterprise Application Platform use JTA for transaction management. The infrastructure changes consist of altering the session factory definition to allow JTA-backed session context and replacing the local Spring-based transaction manager with a HibernateTransactionManager.
Listing Example 7.3, “SessionFactory and transaction manager definitions in a servlet environment” contains typical bean definitions for SessionFactory and transaction manager when used in a servlet container with local Hibernate-based transactions enabled.

Example 7.3. SessionFactory and transaction manager definitions in a servlet environment

<bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
	<property name="dataSource" ref="dataSource"/>
	<property name="mappingLocations" value="classpath:**/*.hbm.xml"/>
	<property name="hibernateProperties">
		<value>
			hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect
			hibernate.show_sql=true
		</value>
	</property>
</bean>

<bean id="transactionManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager">
	<property name="sessionFactory" ref="sessionFactory"/>
</bean>
You can migrate these definitions to use JTA (the datasource is assumed to be a managed datasource, as shown previously).

Example 7.4. JTA-based SessionFactory and transaction manager

<bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
	<property name="dataSource" ref="dataSource"/>
	<property name="mappingLocations" value="classpath:**/*.hbm.xml"/>
	<property name="hibernateProperties">
		<value>
			hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect
			hibernate.show_sql=true
			hibernate.current_session_context_class=jta
			hibernate.transaction.jta.platform=org.hibernate.service.jta.platform.internal.JBossAppServerJtaPlatform
			hibernate.transaction.factory_class=org.hibernate.transaction.JTATransactionFactory
		</value>
	</property>
</bean>

<tx:jta-transaction-manager id="transactionManager"/>

Note

Definitions of the sessionFactory bean are virtually unchanged; the only difference is the addition of the properties required for setting up JTA-based context management and the transaction manager change.

7.2.4. Migrating JPA-based Applications

Java EE 6 environments have restrictions regarding what is deployable by a container that fully supports the JPA API. Specifically, a persistence unit definition contained in a META-INF/persistence.xml file is automatically deployed by the container, but the container cannot declare a RESOURCE_LOCAL transaction type, and must include a JTA datasource reference. Also, the container may not specify a transaction type (which is equivalent to setting a JTA transaction type, but is ignored when the persistence unit is initialized by Spring and a resource-local model is used instead).
However, it is common for servlet-container based applications to use RESOURCE_LOCAL transactions and JTA datasources as shown in example Example 7.5, “A sample persistence unit definition for a servlet-container based application”.

Example 7.5. A sample persistence unit definition for a servlet-container based application

<persistence-unit name="examplePU" transaction-type="RESOURCE_LOCAL">
	<provider>org.hibernate.ejb.HibernatePersistence</provider>
		<properties>
			<property name="hibernate.dialect" value="org.hibernate.dialect.PostgreSQLDialect"/>
			<property name="hibernate.show_sql" value="true"/>
			<property name="hibernate.cache.provider_class" value="org.hibernate.cache.HashtableCacheProvider"/>
	</properties>
</persistence-unit>
A servlet container does not deploy a persistence unit automatically, as a Java EE 6 application does. Therefore, Spring applications using JPA rely on Spring to create the persistence unit by using one of its JPA support factory beans. Also, in a Java EE 6 application, server enrollment in JTA transactions is a requirement, Spring applications running outside Java EE are required to set up a resource-local transaction manager as described in Example 7.6, “JPA EntityManagerFactory and transaction setup in a servlet-container based application”.

Example 7.6. JPA EntityManagerFactory and transaction setup in a servlet-container based application

<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"
p:dataSource-ref="dataSource">    
	<property name="jpaVendorAdapter">
		<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"
			p:database="${jpa.database}" p:showSql="${jpa.showSql}"/>
	</property>
</bean>

<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager"
p:entityManagerFactory-ref="entityManagerFactory"/>
This persistence unit definition in Example 7.6, “JPA EntityManagerFactory and transaction setup in a servlet-container based application” is not compatible with the requirements of Java EE 6 for application containers. It leads to deployment failure if found in the META-INF/persistence.xml file.
There are two ways to solve this problem and to make the application deployable on Red Hat JBoss Enterprise Application Platform:
  • rename the persistence unit definition file
  • leave persistence-unit deployment to JBoss Enterprise Application Platform and use JNDI lookup for retrieving entity managers and entity manager factories.
To rename the persistence unit, you can provide the alternate location as a property to the LocalContainerEntityManagerFactoryBean as described in Example 7.7, “LocalContainerEntityManagerFactoryBean with alternate persistence.xml location”.

Example 7.7. LocalContainerEntityManagerFactoryBean with alternate persistence.xml location

<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
	<property name="persistenceXmlLocation" value="classpath*:META-INF/jpa-persistence.xml"/>
		<!-- other properties (ommitted) -->
</bean>
A more effective approach is converting the persistence.xml file definition to a JTA-based model and using JNDI lookup for retrieving the entity manager. For this, convert the persistence unit definition as in the following example (note that it is not necessary to provide values for both jboss.entity.manager.jndi.name and jboss.entity.manager.factory.jndi.name, but one must be specified).

Example 7.8. Changing the persistence.xml definition to be Java EE 6 compatible

<persistence-unit name="examplePU">
	<jta-data-source>java:jboss/datasources/ExampleDS</jta-data-source>
		<properties>
			<property name="hibernate.show_sql" value="true"/>
			<property name="jboss.entity.manager.jndi.name" value="java:jboss/example/EntityManager"/>
			<property name="jboss.entity.manager.factory.jndi.name" value="java:jboss/example/EntityManagerFactory"/>
		</properties>
</persistence-unit>
You can retrieve the EntityManagerFactory or JTA-synchronized EntityManager from JNDI as follows:

Example 7.9. EntityManager retrieved by JNDI lookup and JTA transaction manager (works with @Autowired)

<jee:jndi-lookup id="entityManager" jndi-name="java:jboss/example/EntityManager" expected-type="javax.persistence.EntityManager"/>

<tx:jta-transaction-manager/>
If the original application relied on Spring to inject the EntityManager using the @PersistenceContext annotation into services and DAOs, change the annotation to @Autowired (as the bean is an EntityManager). In such cases, look for the EntityManagerFactory instead, and allow Spring to create the EntityManager.

Example 7.10. EntityManagerFactory retrieved by JNDI lookup and JTA transaction manager (works with @PersistenceContext)

<jee:jndi-lookup id="entityManagerFactory" jndi-name="java:jboss/example/EntityManagerFactory" expected-type="javax.persistence.EntityManagerFactory"/>

<tx:jta-transaction-manager/>
A particular concern when migrating JPA-based application is the choice of a JPA provider. Servlet-container-based applications include a JPA implementation and specify the nature of the provider explicitly, whereas, JBoss Enterprise Application Platform uses Hibernate as a JPA provider.