Just REST, it’s Easy With Spring and Jersey

In one of the products that we are working on, there is a need to expose the API as REST so that it can be consumed by the developer community and foster innovation. I did a few prototypes for the combination of dependency injection and REST with Restlet, Guice, Jersey and Spring and finally settled with Jersey and Spring primarily due to the easy configuration and tooling available around the two. Let us see how to easily build a REST enabled service with Jersey and Spring.

First, let us quickly create a simple webapp using mvn archetype:generate, if you are using maven 3 then you would see a huge number of options. Pick up 104 😉 ( at least this is the number for mvn 3.0.2)

[sourcecode language=”bash”]
104: remote -> maven-archetype-webapp (An archetype which contains a sample Maven Webapp project.)
[/sourcecode]

Ok, once we have the web app, then we need to add the relevant dependencies to the pom.xml. These are the ones that I added,

[sourcecode language=”xml”]
<repositories>
<repository>
<id>maven2-repository.dev.java.net</id>
<name>Java.net Repository for Maven</name>
<url>http://download.java.net/maven/2/</url>
<layout>default</layout>
</repository>
<repository>
<id>maven-repository.dev.java.net</id>
<name>Java.net Maven 1 Repository (legacy)</name>
<url>http://download.java.net/maven/1</url>
<layout>legacy</layout>
</repository>
</repositories>

<!– Shared version number properties –>
<properties>
<org.springframework.version>3.0.5.RELEASE</org.springframework.version>
</properties>

<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${org.springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>${org.springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>${org.springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${org.springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring</artifactId>
<version>${org.springframework.version}</version>
</dependency>
</dependencies>
</dependencyManagement>

<dependencies>
<dependency>
<groupId>com.sun.jersey</groupId>
<artifactId>jersey-server</artifactId>
<version>1.5</version>
</dependency>
<dependency>
<groupId>com.sun.jersey</groupId>
<artifactId>jersey-json</artifactId>
<version>1.5</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.8.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.sun.grizzly</groupId>
<artifactId>grizzly-servlet-webserver</artifactId>
<version>1.9.8</version>
</dependency>
<dependency>
<groupId>com.sun.jersey</groupId>
<artifactId>jersey-client</artifactId>
<version>1.5</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.sun.jersey.contribs</groupId>
<artifactId>jersey-spring</artifactId>
<version>1.2</version>
<exclusions>
<exclusion>
<groupId>org.springframework</groupId>
<artifactId>spring</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>

<build>
<finalName>justrest</finalName>
<plugins>
<plugin>
<groupId>org.mortbay.jetty</groupId>
<artifactId>maven-jetty-plugin</artifactId>
<version>6.1.17</version>
</plugin>

</plugins>
</build>
[/sourcecode]

Notice, that we need to include some repositories from java.net. Also, notice that we have a dependency on jersey-spring. As of now, jersey-spring depends on the Spring 2.5.6 framework, hence we are excluding it so that we can continue to use 3.0.5.RELEASE without getting into any conflicts

[sourcecode language=”xml”]
<dependency>
<groupId>com.sun.jersey.contribs</groupId>
<artifactId>jersey-spring</artifactId>
<version>1.2</version>
<exclusions>
<exclusion>
<groupId>org.springframework</groupId>
<artifactId>spring</artifactId>
</exclusion>
</exclusions>
</dependency>
[/sourcecode]

Now, let us look at the changes that we would have to make in web.xml. The most significant portion of the web.xml is the servlet mapping for the jersey spring servlet which looks like this

[sourcecode language=”xml”]
<servlet>
<servlet-name>REST</servlet-name>
<servlet-class>
com.sun.jersey.spi.spring.container.servlet.SpringServlet</servlet-class>
<init-param>
<param-name>com.sun.jersey.config.property.packages</param-name>
<param-value>com.inphina.sample</param-value>
</init-param>
<init-param>
<param-name>com.sun.jersey.api.json.POJOMappingFeature</param-name>
<param-value>true</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<!– Map /rest/* to Jersey –>
<servlet-mapping>
<servlet-name>REST</servlet-name>
<url-pattern>/rest/*</url-pattern>
</servlet-mapping>
[/sourcecode]

Next, we also include the standard listeners for Spring, so that it can create the beans, which are the following

[sourcecode language=”xml”]
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>

<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<listener>
<listener-class>org.springframework.web.context.request.RequestContextListener</listener-class>
</listener>
[/sourcecode]

All right, so far so good. It seems that we have all the infrastructure for REST in place. Now, assume that you have a service with the following definition which you want to expose as REST

[sourcecode language=”java”]
package com.inphina.sample;

public interface SpringService {

public void helloMe();

}
[/sourcecode]

And, the implementation looks like this

[sourcecode language=”java”]

package com.inphina.sample;

public class SpringServiceImpl implements SpringService {

public void helloMe() {
System.out.println("The spring service is being invoked");

}

}
[/sourcecode]

Let us put this as a bean in the applicationContext.xml

[sourcecode language=”xml”]

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd">
<bean id="springService" class="com.inphina.sample.SpringServiceImpl"> </bean>
</beans>
[/sourcecode]

If you do a mvn jetty:run at this stage then may be it is all good (if you have been following the example closely) or you might get the following message

[sourcecode language=”text”]

SEVERE: Exception occurred when intialization
com.sun.jersey.api.container.ContainerException: The ResourceConfig instance does not contain any root resource classes.
at com.sun.jersey.server.impl.application.RootResourceUriRules.(RootResourceUriRules.java:103)
at com.sun.jersey.server.impl.application.WebApplicationImpl._initiate(WebApplicationImpl.java:1184)
at com.sun.jersey.server.impl.application.WebApplicationImpl.access$600(WebApplicationImpl.java:159)
at com.sun.jersey.server.impl.application.WebApplicationImpl$12.f(WebApplicationImpl.java:700)
at com.sun.jersey.server.impl.application.WebApplicationImpl$12.f(WebApplicationImpl.java:697)
at com.sun.jersey.spi.inject.Errors.processWithErrors(Errors.java:193)
[/sourcecode]

If you get the above error, then it means that you have not pointed the servlet property

[sourcecode language=”xml”]
<init-param>
<param-name>com.sun.jersey.config.property.packages</param-name>
<param-value>com.inphina.sample</param-value>
</init-param>
[/sourcecode]

to the right location. The right location is the one which has a jersey based REST class. Let us write the root resource now

The class looks like this

[sourcecode language=”java”]

package com.inphina.sample;

import java.util.logging.Logger;

import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;

import com.sun.jersey.spi.inject.Inject;

@Path("/helloworld")
public class HelloWorldResource {

@Inject
private SpringService springService;

private final Logger LOGGER = Logger.getLogger(HelloWorldResource.class.getName());

@GET
@Produces("application/json")
public Person getSomeMessage() {
springService.helloMe();
return new Person("Vikas Hazrati", 36, "vhazrati@inphina.com");
}

@PUT
@Consumes()
public void setNewText(String newText) {
LOGGER.info("do something interesting |" + newText + "|");
}
}
[/sourcecode]

A few things here. Look at the @Path annotation, this is the rest path through which we would be able to call the REST service.

The @GET annotation shows the method which would be invoked for a GET call. The @Produces(“application/json”) annotation shows that the returned value would be in JSON format so that clients can consume it as a JSON object.

If you notice that we have included the following dependency in our pom.xml

[sourcecode language=”xml”]
<dependency>
<groupId>com.sun.jersey</groupId>
<artifactId>jersey-json</artifactId>
<version>1.5</version>
</dependency>
[/sourcecode]

The @Inject annotation is used to inject the SpringService into the REST root resource.

So that is it, we are good. Let us give it a test run and see how it works.

Execute mvn jetty:run and access the URL at the following location

http://localhost:8080/justrest/rest/helloworld

we get the response back as a JSON object

{“name”:”Vikas Hazrati”,”email”:”vhazrati@inphina.com”,”age”:36}

and on the console we would see that our spring service is being called

[sourcecode language=”bash”]

28 Feb, 2011 11:12:32 PM com.sun.jersey.server.impl.application.WebApplicationImpl _initiate
INFO: Initiating Jersey application, version ‘Jersey: 1.5 01/14/2011 12:36 PM’
2011-02-28 23:12:33.464::INFO: Started SelectChannelConnector@0.0.0.0:8080
[INFO] Started Jetty Server
The spring service is being invoked
[/sourcecode]

Entire source code for this project is present here.

Written by 

Vikas is the CEO and Co-Founder of Knoldus Inc. Knoldus does niche Reactive and Big Data product development on Scala, Spark, and Functional Java. Knoldus has a strong focus on software craftsmanship which ensures high-quality software development. It partners with the best in the industry like Lightbend (Scala Ecosystem), Databricks (Spark Ecosystem), Confluent (Kafka) and Datastax (Cassandra). Vikas has been working in the cutting edge tech industry for 20+ years. He was an ardent fan of Java with multiple high load enterprise systems to boast of till he met Scala. His current passions include utilizing the power of Scala, Akka and Play to make Reactive and Big Data systems for niche startups and enterprises who would like to change the way software is developed. To know more, send a mail to hello@knoldus.com or visit www.knoldus.com

4 thoughts on “Just REST, it’s Easy With Spring and Jersey

    1. Not sure if I can answer this one with conviction 🙂 I have not tried the Spring REST support yet. That is certainly worth a try and I would link my thoughts soon …

  1. Pingback: Inphina Thoughts

Leave a Reply

%d bloggers like this: