Wednesday, July 28, 2010

Jersey and Spring Injection

On the project I am on we use iBatis, Spring, and Jersey. We have been using spring injection for our DAOs since the beginning, but our version of Jersey at the time did not support spring injection. To get around this we created factories allowing us to inject beans into the factories. Our REST classes then used the factory to get the beans they were interested in. As the project grew, so did the factory. When we upgraded to Jersey 1.0.3, we were able to take advantage of a Jersey-Spring jar, that would allow us to spring inject beans directly into our REST classes.

In our Maven pom.xml file we added the following dependency.

<dependency>
<groupId>com.sun.jersey.contribs</groupId>
<artifactId>jersey-spring</artifactId>
<version>1.0.3</version>
</dependency>

Then in the constructor of the REST Classes we were able to do the following: (NOTE: as of Jersey 1.4, @Inject becomes @InjectParam)

public class CompanyWS {
private ICompanyManager _companyManager;
private ICompanyTypeManager _companyTypeManager;
public CompanyWS (@InjectParam("companyManager") ICompanyManager companyManager,
@InjectParam("companyTypeManager") ICompanyTypeManager companyTypeManager) {
_companyManager= companyManager;
_companyTypeManager= companyTypeManager;
}
...


The web.xml file also needs to be modified to call out the SpringServlet, instead of the ServletContainer:


<servlet>
<servlet-name>REST Application</servlet-name>
<servlet-class>com.sun.jersey.spi.spring.container.servlet.SpringServlet</servlet-class>
<init-param>
<param-name>javax.ws.rs.Application</param-name>
<param-value>com.accenture.netcds.api.rest.RegisterResources</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>


One downside of the 1.0.3 Spring-Jersey library is that the automatic finding of the REST classes does not work in websphere 6.1. This has been reported as a bug to Jersey and is supposed to be fixed in the more recent versions. As a result the REST classes need to be registered in an Application class.


public class RegisterResources extends Application {
public Set<Class<?>> getClasses() {
Set<Class<?>> s = new HashSet<Class<?>>();
s.add(CompanyWS.class);
return s;
}
}


Spring successfully injected the beans into the REST classes, allowing us to delete the factories we had been using that had grown quite large. Another simplification to make it easier to maintain the software over time.

Nested iBatis Result Mappings

Although, this is not an earth shattering iBatis discovery, it is a time saver, both in development and maintenance so thought I would share it.

Let's assume we have a class called CompanyType that contains type_id and type name and another class called Company that among other properties has a CompanyType object companyType included.

In the iBatis xml file the result map for CompanyType would look something like

<sqlMap namespace="CompanyType">
<resultMap id="companyTypeResultMap" class="com.sample.CompanyType">
<result column="company_type_id" property="typeId"/>
<result column="company_type_nm" property="typeName"/>
</resultMap>
...

In the ibatis xml file for Company the result map would look like:

<sqlMap namespace="Company">
<resultMap id="companyResultMap" class="com.sample.Company">
<!-- other company properties here -->
<result column="company_type_id" property="companyType.typeId"/>
<result column="company_type_nm" property="companyType.typeName"/>
</resultMap>


The problem with this is that if something changes in company type, you have to know to change it in two locations. Makes it harder to maintain. Instead though you could do the following:

<sqlMap namespace="Company">
<resultMap id="companyResultMap" class="com.sample.Company">
<!-- other company properties here -->
<result property="companyType" resultMap="CompanyType.companyTypeResultMap"/>
</resultMap>


As you can see, now if the result map is updated in CompanyType, the changes are automatically reflected in the Company ibatis file. Hope you find this helpful. I know I did.