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>