Here is an example:
package com.noushin.spring.ws.controller;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.PathVariable;
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.ResponseBody;
import com.noushin.spring.ws.model.Student;
import com.noushin.spring.ws.service.StudentService;
/**
*
* This class is used to demonstrate how Spring is used to implement RESTful web services.
*
* @author nbashir
*
*/
@Controller
@RequestMapping("/student")
public class StudentController {
@Autowired
StudentService studentService;
/**
* To test: curl -i -v -X POST -H "Content-Type: application/json;charset=UTF-8" http://localhost:8080/ws/student/add -d '{"name":"John Wait", "grade":1}'
*
* @param student JSON representation of student to be added
*
*/
@RequestMapping(value="/add", method=RequestMethod.POST, consumes="application/json;charset=UTF-8", produces="application/json;charset=UTF-8")
@ResponseBody
public Student addStudent(@RequestBody Student student) {
return studentService.addStudent(student);
}
/**
* To test: curl -i -v -X GET -H "Content-Type: application/json;charset=UTF-8" http://localhost:8080/ws/student/get/16a61b8e-f6ae-459f-bb92-164bd7b72d07
*
* @param id Student ID
* @return A student in JSON format
*/
@RequestMapping(value="/get/{id}", method=RequestMethod.GET, produces="application/json;charset=UTF-8")
@ResponseBody
public Student getStudent(@PathVariable String id) {
return studentService.getStudent(id);
}
/**
* To test: curl -i -v -X GET -H "Content-Type: application/json;charset=UTF-8" http://localhost:8080/ws/student/getall
*
* @return A list of all students in JSON format
*/
@RequestMapping(value="/getall", method=RequestMethod.GET, produces="application/json;charset=UTF-8")
public @ResponseBody List<Student> getStudents() {
return studentService.getStudents();
}
/**
* To test: curl -i -v -X GET http://localhost:8080/ws/student/testexception
* Only for demonstration purposes.
*
* @throws Exception
*/
@RequestMapping(value="/testexception", method=RequestMethod.GET)
public void testException() throws Exception {
System.out.println("Service is available and throws exception.");
studentService.throwException();
}
/**
* Exception Handler for this service.
*
* @param exception Use a different subclass of Exception, if you need a more refined exception handling.
* @return The same student object populated with info on the exception.
*/
@ExceptionHandler(Exception.class)
public @ResponseBody String handleMyException(Exception exception) {
return (new String("Error : " + exception.getMessage()));
}
}
We will need a service class to support our controller methods. Note how service class is accessed in the controller by Spring Autowired feature.
package com.noushin.spring.ws.service;
import java.util.List;
import java.util.UUID;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import com.noushin.spring.ws.dao.StudentDao;
import com.noushin.spring.ws.model.Student;
/**
*
* This class is used to demonstrate how Spring is used to implement RESTful web services.
*
* @author nbashir
*
*/
@Component
public class StudentService {
@Autowired
StudentDao studentDao;
public Student addStudent(Student student) {
student.setId(UUID.randomUUID().toString());
studentDao.addStudent(student);
return student;
}
public Student getStudent(String id) {
return studentDao.getStudent(id);
}
public List<Student> getStudents() {
return studentDao.getStudents();
}
public void updateStudent(Student student) {
studentDao.updateStudent(student);
}
public void deleteStudent(String id) {
studentDao.deleteStudent(id);
}
public void throwException() throws Exception {
throw new Exception("This is an artificial Exception to test WS exception handling.");
}
}
In a typical application, you would need a data layer that manages your data. Here is a typical Data Access Object (DAO) class. Note in this example, I am just using a local static Map to hold the data. In real world applications, you would be accessing some database or similar technology.
package com.noushin.spring.ws.dao;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import org.springframework.stereotype.Component;
import com.noushin.spring.ws.model.Student;
/**
*
* This class is used to demonstrate how Spring is used to implement RESTful web services.
*
* @author nbashir
*
*/
@Component
public class StudentDao {
private static Map<String, Student> students = new HashMap<String, Student>();
public void addStudent(Student student) {
students.put(student.getId(), student);
}
public Student getStudent(String id) {
return students.get(id);
}
public List<Student> getStudents() {
return new ArrayList<Student>(students.values());
}
public void updateStudent(Student student) {
deleteStudent(student.getId());
addStudent(student);
}
public void deleteStudent(String id) {
students.remove(id);
}
}
And finally the business object you are trying to manage:
package com.noushin.spring.ws.model;
/**
*
* This class is used to demonstrate how Spring is used to implement RESTful web services.
*
* @author nbashir
*
*/
public class Student {
private String id;
private int grade;
private String name;
/**
* Need this constructor for JSON mapping
*/
public Student() {
super();
}
public Student(int grade, String name) {
super();
this.grade = grade;
this.name = name;
}
public Student(String id, int grade, String name) {
super();
this.id = id;
this.grade = grade;
this.name = name;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public int getGrade() {
return grade;
}
public void setGrade(int grade) {
this.grade = grade;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
To run your application as a web based application, you'd need a web.xml and servlet configuration file. Add the following files to your application WEB-INF directory.
Example:
~/workspace/ws/src/main/webapp/WEB-INF/web.xml
~/workspace/ws/src/main/webapp/WEB-INF/ws-servlet.xml
web.xml:
<?xml version="1.0" encoding="ISO-8859-1"?>
<web-app version="3.0" metadata-complete="true"
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
<display-name>Spring WS Demo</display-name>
<description>Spring WS Demo</description>
<servlet>
<servlet-name>ws</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>ws</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/ws-servlet.xml</param-value>
</context-param>
<context-param>
<param-name>log4jConfigLocation</param-name>
<param-value>/WEB-INF/classes/log4j.properties</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
</web-app>
ws-servlet.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:p="http://www.springframework.org/schema/p"
xmlns:jms="http://www.springframework.org/schema/jms"
xmlns:amq="http://activemq.apache.org/schema/core"
xmlns:mvc="http://www.springframework.org/schema/mvc"
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.2.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd">
<context:component-scan base-package="com.noushin.spring.ws" />
<mvc:annotation-driven />
</beans>
Since I am using Maven to manage my application build and dependecies, here is the pom.xml you need:
<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>com.noushin.spring</groupId>
<artifactId>ws</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>war</packaging>
<name>ws</name>
<url>http://maven.apache.org</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<junit.version>4.10</junit.version>
<log4j.version>1.2.17</log4j.version>
<spring.version>4.1.0.RELEASE</spring.version>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
<!-- Logging -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>${log4j.version}</version>
</dependency>
<!-- Spring Test -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${spring.version}</version>
<scope>test</scope>
</dependency>
<!-- Spring web -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</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>
<!-- Jackson mapper is used by spring to convert Java POJOs to JSON strings. -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.2.3</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>2.1.1</version>
<configuration>
<failOnMissingWebXml>false</failOnMissingWebXml>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<version>2.1</version>
<executions>
<execution>
<phase>validate</phase>
<goals>
<goal>copy</goal>
</goals>
<configuration>
<outputDirectory>${endorsed.dir}</outputDirectory>
<silent>true</silent>
<artifactItems>
<artifactItem>
<groupId>javax</groupId>
<artifactId>javaee-endorsed-api</artifactId>
<version>6.0</version>
<type>jar</type>
</artifactItem>
</artifactItems>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
And since no application development is ever complete without proper unit testing, here are some basic method level testing of StudentService. Note, I already included the curl commands you need to test your web service methods from command line in Linux in StudentController class.
StudentServiceTest.java:
package com.noushin.spring.ws.service;
import static org.junit.Assert.*;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.UUID;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import com.noushin.spring.ws.model.Student;
import com.noushin.spring.ws.service.StudentService;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
public class StudentServiceTest {
@Autowired
StudentService service;
@Before
public void setUp() throws Exception {
}
@After
public void tearDown() throws Exception {
}
@Test
public void testAddStudent() {
Student student = service.addStudent(new Student(5, "John Wait"));
Student addedStudent = service.getStudent(student.getId());
assertTrue(addedStudent.getGrade() == student.getGrade());
}
@Test
public void testDeleteStudent() {
Student student = service.addStudent(new Student(5, "John Wait"));
student = service.getStudent(student.getId());
service.deleteStudent(student.getId());
Student deletedStudent = service.getStudent(student.getId());
assertTrue(deletedStudent == null);
}
@Test
public void testGetStudents() {
Student student = new Student(1, "Jane Porter");
service.addStudent(student);
student = new Student(2, "John Downy");
service.addStudent(student);
student = new Student(3, "Kate Summerfield");
service.addStudent(student);
ArrayList<Student> students = (ArrayList<Student>) service.getStudents();
assertTrue(students != null);
Iterator<Student> it = (Iterator<Student>) students.iterator();
while (it.hasNext()) {
student = (Student) it.next();
System.out.println(student.getGrade() + ":" + student.getName());
}
}
@Test
public void testUpdateStudent() {
Student student = service.addStudent(new Student(1, "Jane Porter"));
int grade1 = student.getGrade();
System.out.println("Student >>> " + student.getId() + " - Name : " + student.getName() + " - Grade : " + student.getGrade());
student.setGrade(2);
Student updatedStudent = service.getStudent(student.getId());
System.out.println("updatedStudent >>> " + updatedStudent.getId() + " - Name : " + updatedStudent.getName() + " - Grade : " + updatedStudent.getGrade());
int grade2 = updatedStudent.getGrade();
assertTrue(grade1 != grade2);
}
}
Since I am using Spring Test framework, here is the context file you need to run your tests. Place the following file in
~workspace/ws/src/test/resources/com/noushin/spring/ws/service/StudentServiceTest-context.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:amq="http://activemq.apache.org/schema/core" xmlns:context="http://www.springframework.org/schema/context"
xmlns:p="http://www.springframework.org/schema/p" 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-2.0.xsd
http://activemq.apache.org/schema/core http://activemq.apache.org/schema/core/activemq-core.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="com.noushin.spring.ws" />
<context:annotation-config />
</beans>
One more note regarding debugging Spring web services. Add the following lines to your log4j.properties file. They will come handy when you get errors when invoking web service methods:
log4j.category.org.springframework.web.servlet.DispatcherServlet=DEBUG
log4j.category.org.springframework.web.servlet.mvc=TRACE
No comments:
Post a Comment