Jersey + Spring and Jackson too. Using Maven.

This article is like a tutorial for creating Jersey apps. Technologies used are
  1. Jersey
  2. Spring (for DI)
  3. Jackson (for JSON serialization and de-serialization)
  4. Maven (as build tool)
I use eclipse luna as my IDE.

 This article is divided into following sections
  1. Spring setup using maven
  2. Starting Jersey using Jackson
  3. Configuring Jackson
  4. Integrating Jersey with Spring 3
We assume that you have fair enough knowledge of all the four technologies used.
Note: Jersey runs as a web servlet, and hence we need to create a maven web project. A link that I usually follow is: this.

 Spring setup using maven

pom.xml

Add a spring version number as
<properties>
    <spring.version>4.1.2.RELEASE</spring.version>
</properties>

And spring dependencies as
<dependencies>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>${spring.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-webmvc</artifactId>
        <version>${spring.version}</version>
    </dependency>
</dependencies>

We use spring-context for DI and web-mvc because umm...jersey is a web application. So we could use some features of a web app.

web.xml

We add following to web.xml to springify our web project
<listener>
    <listener-class>
        org.springframework.web.context.ContextLoaderListener
    </listener-class>
</listener>

<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:spring/applicationContext.xml</param-value>
</context-param>

Note: we point to spring/applicationContext.xml here. 

Spring applicationContext.xml

Note: This goes is src/main/resources folder.
For me, file looks as below

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc"
    xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx"
    xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.2.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd">


    <!-- For Spring MVC -->
    <!-- Enable annotation-based Spring MVC controllers (eg: @Controller annotation) -->
    <mvc:annotation-driven />

    <!-- Classpath scanning of @Component, @Service, etc annotated class -->
    <context:component-scan base-package="com.ego" />

    <!-- Resolve view name into jsp file located on /WEB-INF -->
    <bean
        class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/" />
        <property name="suffix" value=".jsp" />
    </bean>

    <!-- Enable Annotation based spring configurations -->
    <context:annotation-config />

    <context:component-scan base-package="com.ego" />
</beans>

Starting Jersey

pom.xml changes

To add Jersey, we add following dependencies to pom.xml.
<dependency>
    <groupId>org.glassfish.jersey.containers</groupId>
    <artifactId>jersey-container-servlet-core</artifactId>
    <version>2.13</version>
</dependency>
<dependency>
    <groupId>org.glassfish.jersey.media</groupId>
    <artifactId>jersey-media-json-jackson</artifactId>
    <version>2.13</version>
</dependency>
<dependency>
    <groupId>org.glassfish.jersey.ext</groupId>
    <artifactId>jersey-spring3</artifactId>
    <version>2.13</version>
    <exclusions>
    <exclusion>
        <groupId>org.springframework</groupId>
        <artifactId>spring-core</artifactId>
    </exclusion>
    <exclusion>
        <groupId>org.springframework</groupId>
        <artifactId>spring-web</artifactId>
    </exclusion>
    <exclusion>
        <groupId>org.springframework</groupId>
        <artifactId>spring-beans</artifactId>
    </exclusion>
    </exclusions>
</dependency>

Note: We exclude spring JARs as they are already included.

web.xml changes

With both Jersey and Jackson integrated into our app through maven, we move forward modifying web.xml.
With this, we create a Jackson servlet. And init param to servlet tells, which packages to scan for Jersey services.

So we add the following to web.xml

<servlet>
    <servlet-name>jersey-serlvet</servlet-name>
    <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
    <init-param>
        <param-name>jersey.config.server.provider.packages</param-name>
        <param-value>com.ego.jerseyapp.services</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
</servlet>

<servlet-mapping>
    <servlet-name>jersey-serlvet</servlet-name>
    <url-pattern>/rest/*</url-pattern>
</servlet-mapping>

Note: Init parameter "jersey.config.server.provider.packages" lists the packages to scan for Jersey resources like jersey services.

Jersey service

Finally we write our service class. Mine is in package "com.ego.jerseyapp.services" and looks like below

package com.ego.jerseyapp.services;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.QueryParam;

@Path("/add")
public class AdditionService {
    @GET
    public Integer add(@QueryParam("arg01") Integer arg01, @QueryParam("arg02") Integer arg02) {
        return arg01 + arg02;
    }
}


A simple one though.

Run the app

URL: http://localhost:8080/jerseyapp/rest/add?arg01=5&arg02=7
And output I get is "12", indicating that jersey is working as expected.

Configuring Jackson

If we note, we do not have a handle to ObjectMapper. Nor do we have any way of configuring Jackson.
To see this working, lets create a response class as below.

public class ServiceResponse {
    private final Boolean status;
    private final Object data;

    public ServiceResponse(Boolean status, Object data) {
        this.status = status;
        this.data = data;
    }

    public Boolean getStatus() {
        return status;
    }

    public Object getData() {
        return data;
    }

}


This response is returned by AdditionService as
return new ServiceResponse(null, arg01 + arg02);

Simple right? But on hitting the URL, we see a "HTTP Status 500 - Internal Server Error".
On investigating in logs, I see

SEVERE: MessageBodyWriter not found for media type=application/json, type=class com.ego.jerseyapp.services.helpers.ServiceResponse, genericType=class com.ego.jerseyapp.services.helpers.ServiceResponse.

This clearly indicates that no MessageBodyWriter could be found. Why? Because we didn't tell Jersey which one to use. So now we add an object mapper to help Jersey serialize / de-serialize
This class is in scanned packages (mentioned in web.xml jersey servlet init param) and looks like below
import javax.ws.rs.ext.ContextResolver;
import javax.ws.rs.ext.Provider;

import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;

@Provider
public class ObjectMapperProvider implements ContextResolver {

    private ObjectMapper objectMapper;

    public ObjectMapperProvider() {
        objectMapper = new ObjectMapper();
        objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
       
        // Do not serialize map key-value where value is null
        objectMapper.configure(SerializationFeature.WRITE_NULL_MAP_VALUES, false);
       
        // Trying to ignore fields that have null values.
        objectMapper.setSerializationInclusion(Include.NON_NULL);
    }

    public ObjectMapper getContext(Class type) {
        return objectMapper;
    }
}


Now when we run our URL, we get JSON response as
{ 
    "data": 12
}
Cool right?

Note: MessageBodyWriter exception occurs when either ObjectMapper is missing, or "jersey-media-json-jackson" JAR is missing.

Note: jersey-spring3 JAR is used to integrate Jersey with Spring 3.

Note: any Jackson configuration goes here.

Integrating Jersey with Spring 3

To demonstrate this, lets use Spring DI through Context and Dependency Injection (CDI) API.

We create a calculator and inject it into our service.
So here is how our classes look like

Calculator
package com.ego.jerseyapp.calculators;

import org.springframework.stereotype.Service;

@Service
public class Calculator {
    public int add(int a, int b) {
        return a + b;
    }
}


Service
package com.ego.jerseyapp.services;

import javax.inject.Inject;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;

import com.ego.jerseyapp.calculators.Calculator;
import com.ego.jerseyapp.services.helpers.ServiceResponse;

@Path("/add")
public class AdditionService {
   
    @Inject
    private Calculator calculator;
   
    @GET
    @Produces("application/json")
    public ServiceResponse add(@QueryParam("arg01") Integer arg01, @QueryParam("arg02") Integer arg02) {
        return new ServiceResponse(null, calculator.add(arg01, arg02));
    }
}


When I run the URL, I get correct results, demonstrating how Spring 3 is integrated with Jersey and Jackson.

I hope this article helps you get started.

Thanks.

Comments