Publishing a REST services with Spring

In this article we will see how to implement a REST services with Spring with a classic example…the Employees!!
We’ll see a simple application that manages a list of employees and exposes three web services:

  • GetEmployes
  • GetEmployees
  • AddEmployee

1. The POM file

First we see the POM definition with all the needed dependancies.

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>org.madbit.springexample</groupId>
  <artifactId>Ex3-REST</artifactId>
  <version>1.0.0-SNAPSHOT</version>
  <packaging>war</packaging>

	<properties>
		<spring.version>3.1.0.RELEASE</spring.version>
	</properties>

	<dependencies>	
		<!-- Spring Framework -->
		<dependency> <!-- Used for @Autowired annotation -->
			<groupId>org.springframework</groupId>
			<artifactId>spring-beans</artifactId>
			<version>${spring.version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-context</artifactId>
			<version>${spring.version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-web</artifactId>
			<version>${spring.version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-webmvc</artifactId>
			<version>${spring.version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-oxm</artifactId>
			<version>${spring.version}</version>
		</dependency>
	
		<dependency>
			<groupId>javax.servlet</groupId>
			<artifactId>javax.servlet-api</artifactId>
			<version>3.0.1</version>
		</dependency>
		<dependency>
			<groupId>log4j</groupId>
			<artifactId>log4j</artifactId>
			<version>1.2.16</version>
		</dependency>
		<dependency>
			<groupId>javax.xml</groupId>
			<artifactId>jaxb-api</artifactId>
			<version>2.1</version>
		</dependency>		
		<dependency>
			<groupId>jdom</groupId>
			<artifactId>jdom</artifactId>
			<version>1.0</version>
		</dependency>
	</dependencies>

2. Types definition

While SOAP-based services rely on WSDL to describe the format of possible requests for a given web service, REST doen’t need of it. Sometimes can be useful define the types of the exchanced messages in a .xsd file.

Then, let’s create the file EmployeeServices.xsd under /src/resources/xsd.

<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:tns="https://madbit.org/EmployeeService"
targetNamespace="https://madbit.org/EmployeeService"  elementFormDefault="qualified">
		 
	<xs:complexType name="EmployeeData">
		<xs:sequence>
			<xs:element name="id" type="xs:int" minOccurs="0" maxOccurs="1" />
			<xs:element name="firstname" type="xs:string" minOccurs="1" maxOccurs="1" />
			<xs:element name="lastname" type="xs:string" minOccurs="1" maxOccurs="1" />
			<xs:element name="role" type="xs:string" minOccurs="1" maxOccurs="1" />
		</xs:sequence>
	</xs:complexType>
	 
	<xs:element name="GetEmployeeResponse">
		<xs:complexType>
			<xs:sequence>
				<xs:element name="employee" type="tns:EmployeeData" minOccurs="1" maxOccurs="1" />
			</xs:sequence>
		</xs:complexType>
	</xs:element>
		
	<xs:element name="GetEmployeesResponse">
		<xs:complexType>
			<xs:sequence>
				<xs:element name="employee" type="tns:EmployeeData" minOccurs="0" maxOccurs="unbounded" />
			</xs:sequence>
		</xs:complexType>
	</xs:element>
	
	<xs:element name="AddEmployeeRequest">
		<xs:complexType>
			<xs:sequence>
				<xs:element name="employee" type="tns:EmployeeData" minOccurs="1" maxOccurs="1" />
			</xs:sequence>
		</xs:complexType>
	</xs:element>
	
	<xs:element name="AddEmployeeResponse">
		<xs:complexType>
			<xs:sequence>
				<xs:element name="data" type="xs:string" minOccurs="1" maxOccurs="1" />
			</xs:sequence>
		</xs:complexType>
	</xs:element>	
</xs:schema>

It’s possible delegate to Maven the generation of the classes that will be used by Maven to marshall and unmarshall the XML body of the REST requests and responses. To do this it’s enough add in the pom.xml file the following lines:

<build>
	<plugins>
		<plugin>
			<groupId>org.jvnet.jaxb2.maven2</groupId>
			<artifactId>maven-jaxb2-plugin</artifactId>
			<executions>
				<execution>
					<goals>
						<goal>generate</goal>
					</goals>
				</execution>
			</executions>
			<configuration>
				<schemaDirectory>${basedir}/src/main/resources/xsd</schemaDirectory>
				<generateDirectory>${basedir}/target/generated/</generateDirectory>
				<generatePackage>org.madbit.springexample.ws</generatePackage>
				<extension>true</extension>
			</configuration>
		</plugin>
	</plugins>
</build>

In our case will be generated the following classes under target/generated/ directory :

  • AddEmployeeRequest.java
  • AddEmployeeResponse.java
  • EmployeeData.java
  • GetEmployeeRequest.java
  • GetEmployeeResponse.java

Everyone of these classes will be generated with the JAXB2 annotations. The main one is @XmlRootElement. This annotations are used by JAXB2 to understand how to map and convert an XML data format in a class and vice versa.
Below is showed an example of the generated class AddEmployeeRequest.java.

package org.madbit.springexample.ws;

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;

@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "", propOrder = {
    "employee"
})
@XmlRootElement(name = "AddEmployeeRequest")
public class AddEmployeeRequest {

    @XmlElement(required = true)
    protected EmployeeData employee;

    public EmployeeData getEmployee() {
        return employee;
    }

    public void setEmployee(EmployeeData value) {
        this.employee = value;
    }
}

3. Creating configuration files

Web deployment descriptor

The web deployment descriptor web.xml is the essential configuration file for a Java web application. In this file, you define the servlets for your application and how web request are mapped to them. For a Spring MVC application, you only have to define a single DispatcherServlet instance that acts as the front controller for Spring MVC, although you are allowed to define more than one if required.
In large application, it can be convenient to use multiple DispatcherServlet instances. This allows DispatcherServlet instances to be designed to specific URLs, making code management easier and letting individual team members work on an application’s logic without getting in each other’s way.

<web-app version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee

http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">

    <display-name>Calculator Service</display-name>
    
    <!-- REST mapping -->
    <servlet>
        <servlet-name>employee</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
		<load-on-startup>1</load-on-startup>
    </servlet>

    <servlet-mapping>
        <servlet-name>employee</servlet-name>
        <url-pattern>/employee/*</url-pattern>
    </servlet-mapping>
</web-app>

In this web deployment descriptor you define a servlet of type DispatcherServlet. This is the core servlet class in Spring MVC that receives web request and dispatches them to appropriate handlers.
Another purpose of the servlet name is for DispatcherServlet to decide which file to load for Spring MVC configuration. By default, Spring looks for a file [servlet-name]-servlet.xml by joining the servlet name with -servlet.xml as suffix. But in this case, the employee servlet loads the Spring MVC configuration file employee-servlet.xml by default placed under WEB-INF directory.

You can explicitly specify a configuration file in the contextConfiguration servlet parameter as:

<web-app ...>
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/employee-servlet.xml</param-value>
    </context-param>

    <listener>
        <listener-class>
            org.springframework.web.context.ContextLoaderListener
        </listener-class>
    </listener>
...
</web-app>

4. Activating Spring MVC annotation scanning

Before you create an application’s controllers, you have to set up the web application for classes to be scanned for presence of @Controller and @RequestMapping annotations. First, for Spring to auto-detect annotation, you have to enable Spring’s component scanninfeatures through the element.
In addition to this statement, since Spring’s MVC @RequestMapping annotation maps URL request to controller classes and their corresponding handler methods, this requires additional statements in a web application’s context. To male this work, youi have to register a DefaultAnnotationHandlerMapping instance and an AnnotationMethodHandlerAdapter instance in the web application context. These instances process the @RequestMapping annotation at the class level and the method level, respectively.
To enable support for annotation-based controllers, includes the following configuration in the employee-servlet.xml file under WEB-INF directory:

<?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:p="http://www.springframework.org/schema/p"
	xmlns:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="http://www.springframework.org/schema/beans

http://www.springframework.org/schema/beans/spring-beans-3.1.xsd


http://www.springframework.org/schema/context


http://www.springframework.org/schema/context/spring-context-3.1.xsd">

	<bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping" />
	<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
	    <property name="messageConverters">
	      <list>
	        <ref bean="marshallingHttpMessageConverter"/>
	      </list>
	    </property>
	 </bean>

	  <bean id="marshallingHttpMessageConverter"
	    class="org.springframework.http.converter.xml.MarshallingHttpMessageConverter"
	    p:marshaller-ref="jaxb2Marshaller" p:unmarshaller-ref="jaxb2Marshaller" />
	
          <!-- package where are placed the JAXB2 annotated classes -->
	  <bean id="jaxb2Marshaller" class="org.springframework.oxm.jaxb.Jaxb2Marshaller">
	    <property name="classesToBeBound">
	      <list>
	        <value>org.madbit.springexample.ws.GetEmployeeResponse</value>
	        <value>org.madbit.springexample.ws.GetEmployeesResponse</value>
	        <value>org.madbit.springexample.ws.AddEmployeeRequest</value>
	        <value>org.madbit.springexample.ws.AddEmployeeResponse</value>
	      </list>
	    </property>
	  </bean>
	  
          <!-- package where is placed the REST Endpoint -->
	  <context:component-scan base-package="org.madbit.springexample.ws" />
  </beans>

In alternative, if all your JAX2 annotated classes are placed in the same package, you can use the following bean definition to for jaxb2Marshaller:

<bean id="jaxb2Marshaller" class="org.springframework.oxm.jaxb.Jaxb2Marshaller">
	<property name="contextPath" value="org.madbit.springexample.ws" />
</bean>

Notice the element with base-package value of org.madbit.springexample.ws. This package corresponds to the same one used in the Spring MVC controllers, which will be illustrated next.
Next, the DefaultAnnotationHandlerMapping and AnnotationMethodHandlerAdapter bean classes are preregistrered in the web application context by default.
Once you have creating the controller class itself, as well as finishing the configuration set-up for employee-servlet.xml.

5. Create the Employee services

In this example we won’t use any data source like a database for the employees. So, we will use a simple model class Employee.java and a controller class EmployeeService.java to handle a list of employees.

package org.madbit.springexample.model;

public class Employee {
	
	private int id;
	private String firstname;
	private String lastname;
	private String role;
	
	public Employee(int id, String firstname, String lastname, String role) {
		super();
		this.id = id;
		this.firstname = firstname;
		this.lastname = lastname;
		this.role = role;
	}	
	
	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	public String getFirstname() {
		return firstname;
	}
	public void setFirstname(String firstname) {
		this.firstname = firstname;
	}
	public String getLastname() {
		return lastname;
	}
	public void setLastname(String lastname) {
		this.lastname = lastname;
	}
	public String getRole() {
		return role;
	}
	public void setRole(String role) {
		this.role = role;
	}
}

Below the controller interface IEmployeeService.java.

package org.madbit.springexample.controller;

import java.util.List;

import org.madbit.springexample.model.Employee;

public interface IEmployeeService {
	
	public void addEmployee(Employee e);
	
	public void removeEmployee(int id);
	
	public void updateEmployee(int id, Employee e);
	
	public Employee getEmployee(int id);
	
	public List<Employee> getEmployees();
}

And the controller implementation EmployeeService.java.

package org.madbit.springexample.controller;

import java.util.List;

import org.madbit.springexample.model.Employee;

public class EmployeeService implements IEmployeeService {
	
	private List<Employee> employees;
	
	public void setEmployees(List<Employee> employees){
		this.employees = employees;
	}	

	public void addEmployee(Employee e) {
		this.employees.add(e);
	}

	public void removeEmployee(int id) {
		this.employees.remove(id);

	}

	public void updateEmployee(int id, Employee e) {
		this.employees.set(id, e);
	}
	
	public Employee getEmployee(int id){
		return this.employees.get(id);
	}
	
	public List<Employee> getEmployees(){
		return employees;
	}
}

Now it’s time to declare the EmployeeService bean in the Spring IoC container. Let’s create the file applicationContext.xml with the definition unter src/main/resources.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd">

    <bean id="employeeService" class="org.madbit.springexample.controller.EmployeeService">
        <property name="employees">
        	<list>
        		<bean class="org.madbit.springexample.model.Employee">
        			<constructor-arg value="0" />
        			<constructor-arg value="John" />
        			<constructor-arg value="Doe" />
        			<constructor-arg value="Manager" />
        		</bean>
        		<bean class="org.madbit.springexample.model.Employee">
        			<constructor-arg value="1" />
        			<constructor-arg value="Jane" />
        			<constructor-arg value="Doe" />
        			<constructor-arg value="CEO" />
        		</bean>
        	</list>
        </property>
    </bean>    
</beans>

With the configuration above we asked Spring to instantiate a bean employeeService passing via setter injection two objects Employee. In this way we have a starting list of employees.

Now, what we have to do is pass to the ContextLoaderListener the file application.xml with our bean definition through the param contextConfigLocation declared in the web.xml file, as below:

<web-app ...>
    <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>
    ...
</web-app>

6. The REST endpoint

At the end we just need to define the REST endpoint EmployeeEndpoint.java which receive all the defined REST services.

package org.madbit.springexample.ws;

import java.util.ArrayList;
import java.util.List;

import org.madbit.springexample.controller.EmployeeService;
import org.madbit.springexample.model.Employee;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
public class EmployeeEndpoint {

	@Autowired
	private EmployeeService employeeService;

	@RequestMapping(value = "/getEmployee", method = RequestMethod.GET)
	@ResponseBody
	public GetEmployeeResponse getEmployee(@RequestParam("id") int id) 
	{
		Employee e = employeeService.getEmployee(0);
		GetEmployeeResponse response = new GetEmployeeResponse();
		EmployeeData data = new EmployeeData();
		data.setId(e.getId());
		data.setFirstname(e.getFirstname());
		data.setLastname(e.getLastname());
		data.setRole(e.getRole());
		response.setEmployee(data);
		return response;
	}
	
	@RequestMapping(value = "/getEmployees", method = RequestMethod.GET)
	@ResponseBody
	public GetEmployeesResponse getEmployees() 
	{
		List<Employee> employees = employeeService.getEmployees();
		List<EmployeeData> employeeData = new ArrayList<EmployeeData>();
		
		GetEmployeesResponse response = new GetEmployeesResponse();
		EmployeeData data;
		
		for(Employee e: employees) {
			data = new EmployeeData();
			data.setId(e.getId());
			data.setFirstname(e.getFirstname());
			data.setLastname(e.getLastname());
			data.setRole(e.getRole());
			employeeData.add(data);
		}
		
		response.employee = employeeData;
		return response;
	}
	
	@RequestMapping(value = "/addEmployee", method = RequestMethod.POST)
	@ResponseBody
	public AddEmployeeResponse addEmployee(@RequestBody AddEmployeeRequest request) 
	{
		EmployeeData employeData = request.getEmployee();
		Employee employee = new Employee();
		employee.setId(employeeService.getEmployees().size());
		employee.setFirstname(employeData.getFirstname());
		employee.setLastname(employeData.getLastname());
		employee.setRole(employeData.getRole());
		
		employeeService.addEmployee(employee);
		
		AddEmployeeResponse response = new AddEmployeeResponse();
		response.data = "Employee added";
		return response;
	}
}

With the @Autowired annotation, Spring wires automatically the object employeeService to the employeeService bean defined before in applicationContext.xml, which has been inizialized with a list of employees.

7. Invoke the services

Here we’ll see how to invoke the REST services. You can use any REST client or implement a java client which invokes the web service.

GetEmployee

Request:

Method: GET URL: http://localhost:8080/Ex3-REST/employee/getEmployee?id=0

Response:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<GetEmployeeResponse xmlns="https://madbit.org/EmployeeService">
  <employee>
    <id>0</id>
    <firstname>John</firstname>
    <lastname>Doe</lastname>
    <role>Manager</role>
  </employee>
</GetEmployeeResponse>

GetEmployees

Request:

Method: GET URL: http://localhost:8080/Ex3-REST/employee/getEmployees

Response:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<GetEmployeesResponse xmlns="https://madbit.org/EmployeeService">
  <employee>
    <id>0</id>
    <firstname>John</firstname>
    <lastname>Doe</lastname>
    <role>Manager</role>
  </employee>
  <employee>
    <id>1</id>
    <firstname>Jane</firstname>
    <lastname>Doe</lastname>
    <role>CEO</role>
  </employee>
</GetEmployeesResponse>

AddEmployee

Request:

Method: POST URL: http://localhost:8080/Ex3-REST/employee/addEmployee Request Body:

<?xml version="1.0" encoding="UTF-8"?>
<AddEmployeeRequest xmlns="https://madbit.org/EmployeeService">
    <employee>
        <firstname>Mary</firstname>
        <lastname>Doe</lastname>
        <role>Secretary</role>
    </employee>
</AddEmployeeRequest>

Request Header: (“Accept”, “application/xml”) (“Content-Type”, “application/xml”) NOTE: Take care to add the headers above, otherwise you get the error “HTTP/1.1 415 Unsupported Media Type

Response:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<AddEmployeeResponse xmlns="https://madbit.org/EmployeeService">
    <data>Employee added</data>
</AddEmployeeResponse>

If you want to learn more retro games news, read our magazine and subscribe to our newsletter.