├── src ├── main │ ├── java │ │ └── com │ │ │ └── choudhury │ │ │ ├── service │ │ │ └── BookService.java │ │ │ ├── domain │ │ │ ├── BookList.java │ │ │ ├── ObjectWithId.java │ │ │ └── Book.java │ │ │ ├── impl │ │ │ └── BookServiceImpl.java │ │ │ └── controller │ │ │ └── BookRestController.java │ └── webapp │ │ └── WEB-INF │ │ ├── spring-servlet.xml │ │ ├── web.xml │ │ └── applicationContext.xml └── test │ └── java │ └── com │ └── choudhury │ └── controller │ ├── BaseWebApplicationContextTests.java │ └── TestRestService.java ├── README.md └── pom.xml /src/main/java/com/choudhury/service/BookService.java: -------------------------------------------------------------------------------- 1 | package com.choudhury.service; 2 | 3 | import com.choudhury.domain.Book; 4 | 5 | public interface BookService { 6 | public Book getBook(long id); 7 | 8 | public long addBook(Book book); 9 | 10 | public long getBookCount(); 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/com/choudhury/domain/BookList.java: -------------------------------------------------------------------------------- 1 | package com.choudhury.domain; 2 | 3 | import java.util.List; 4 | 5 | public class BookList { 6 | 7 | private List data; 8 | 9 | public List getData() { 10 | return data; 11 | } 12 | 13 | public void setData(List data) { 14 | this.data = data; 15 | } 16 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | spring-rest-example 2 | =================== 3 | 4 | Simple Spring Rest MVC Example to demonstrate a simple bookservice example which allows Get and Add of a book via 5 | Rest EndPoints. This example can produce JSON or XML based on the Accept-Header of the client. 6 | 7 | Project packaging is war file. To run this, deploy to an application container (such as Apache Tomcat) 8 | -------------------------------------------------------------------------------- /src/main/webapp/WEB-INF/spring-servlet.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /src/main/java/com/choudhury/domain/ObjectWithId.java: -------------------------------------------------------------------------------- 1 | package com.choudhury.domain; 2 | 3 | public class ObjectWithId { 4 | protected long id; 5 | 6 | public ObjectWithId() { 7 | } 8 | 9 | public ObjectWithId(long id) { 10 | this.id = id; 11 | } 12 | 13 | public long getId() { 14 | return id; 15 | } 16 | 17 | public void setId(long id) { 18 | this.id = id; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/main/webapp/WEB-INF/web.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | spring 8 | org.springframework.web.servlet.DispatcherServlet 9 | 1 10 | 11 | 12 | 13 | spring 14 | /rest/* 15 | 16 | 17 | 18 | org.springframework.web.context.ContextLoaderListener 19 | 20 | 21 | -------------------------------------------------------------------------------- /src/main/java/com/choudhury/domain/Book.java: -------------------------------------------------------------------------------- 1 | package com.choudhury.domain; 2 | 3 | public class Book extends ObjectWithId{ 4 | private String author; 5 | private String title; 6 | 7 | public Book() { 8 | } 9 | 10 | public Book(long id,String author, String title) 11 | { 12 | super(id); 13 | this.author = author; 14 | this.title = title; 15 | } 16 | 17 | 18 | public String getAuthor() { 19 | return author; 20 | } 21 | 22 | public String getTitle() { 23 | return title; 24 | } 25 | 26 | 27 | 28 | @Override 29 | public boolean equals(Object o) { 30 | if (this == o) return true; 31 | if (o == null || getClass() != o.getClass()) return false; 32 | 33 | Book book = (Book) o; 34 | 35 | return id == book.id; 36 | 37 | } 38 | 39 | public void setAuthor(String author) { 40 | this.author = author; 41 | } 42 | 43 | public void setTitle(String title) { 44 | this.title = title; 45 | } 46 | 47 | @Override 48 | public int hashCode() { 49 | return (int) (id ^ (id >>> 32)); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/com/choudhury/impl/BookServiceImpl.java: -------------------------------------------------------------------------------- 1 | package com.choudhury.impl; 2 | 3 | import com.choudhury.domain.Book; 4 | import com.choudhury.service.BookService; 5 | import org.slf4j.Logger; 6 | import org.slf4j.LoggerFactory; 7 | 8 | import java.util.ArrayList; 9 | import java.util.List; 10 | import java.util.concurrent.atomic.AtomicInteger; 11 | import javax.inject.Named; 12 | 13 | @Named("bookService") 14 | public class BookServiceImpl implements BookService{ 15 | 16 | private static Logger logger= LoggerFactory.getLogger(BookServiceImpl.class); 17 | private AtomicInteger atomicInteger=new AtomicInteger(0); 18 | 19 | public BookServiceImpl() 20 | { 21 | init(); 22 | } 23 | 24 | // In-memory list 25 | private List books = new ArrayList<>(); 26 | 27 | private void init() 28 | { 29 | addBook("John Smith","Spring Framework intro"); 30 | addBook("William Smith","Advanced Java"); 31 | } 32 | 33 | public Book getBook(long id) { 34 | logger.info("Retrieving id {}",id); 35 | for (Book book : books) { 36 | if (book.getId()==id) 37 | { 38 | return book; 39 | } 40 | } 41 | return null; 42 | } 43 | 44 | public long addBook(Book book) 45 | { 46 | int idTodSet=atomicInteger.getAndIncrement(); 47 | book.setId(idTodSet); 48 | books.add(book); 49 | return idTodSet; 50 | } 51 | 52 | public long addBook(String author, String title) { 53 | Book book = new Book(-1, author, title); 54 | return addBook(book); 55 | } 56 | 57 | public long getBookCount() { 58 | return books.size(); 59 | } 60 | } 61 | 62 | 63 | -------------------------------------------------------------------------------- /src/main/java/com/choudhury/controller/BookRestController.java: -------------------------------------------------------------------------------- 1 | package com.choudhury.controller; 2 | 3 | import com.choudhury.domain.Book; 4 | import com.choudhury.domain.ObjectWithId; 5 | import com.choudhury.service.BookService; 6 | import org.slf4j.Logger; 7 | import org.slf4j.LoggerFactory; 8 | import org.springframework.http.HttpStatus; 9 | import org.springframework.web.bind.annotation.PathVariable; 10 | import org.springframework.web.bind.annotation.RequestBody; 11 | import org.springframework.web.bind.annotation.RequestMapping; 12 | import org.springframework.web.bind.annotation.RequestMethod; 13 | import org.springframework.web.bind.annotation.ResponseStatus; 14 | import org.springframework.web.bind.annotation.RestController; 15 | 16 | import javax.inject.Inject; 17 | import javax.inject.Named; 18 | 19 | 20 | 21 | @RestController 22 | @RequestMapping("/book") 23 | public class BookRestController { 24 | 25 | private BookService bookService; 26 | 27 | private Logger logger=LoggerFactory.getLogger(BookRestController.class); 28 | 29 | 30 | //Note: The @Named("bookService") is not required in this example (as there only instance of BookService around) 31 | @Inject 32 | public BookRestController(@Named("bookService") BookService bookService ) 33 | { 34 | this.bookService=bookService; 35 | } 36 | 37 | 38 | @RequestMapping(value = "/{id}",method = RequestMethod.GET) 39 | @ResponseStatus(HttpStatus.OK) 40 | public Book getBook(@PathVariable("id") Long id) { 41 | logger.debug("Provider has received request to get person with id: " + id); 42 | return bookService.getBook(id); 43 | } 44 | 45 | @RequestMapping(value = "/add", method = RequestMethod.POST) 46 | @ResponseStatus(HttpStatus.CREATED) 47 | public ObjectWithId addBook(@RequestBody Book book) 48 | { 49 | return new ObjectWithId(bookService.addBook(book)); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/test/java/com/choudhury/controller/BaseWebApplicationContextTests.java: -------------------------------------------------------------------------------- 1 | package com.choudhury.controller; 2 | 3 | import com.choudhury.service.BookService; 4 | import org.junit.Before; 5 | import org.junit.runner.RunWith; 6 | import org.springframework.beans.BeansException; 7 | import org.springframework.context.ApplicationContext; 8 | import org.springframework.core.io.FileSystemResourceLoader; 9 | import org.springframework.mock.web.MockHttpServletRequest; 10 | import org.springframework.mock.web.MockHttpServletResponse; 11 | import org.springframework.mock.web.MockServletConfig; 12 | import org.springframework.mock.web.MockServletContext; 13 | import org.springframework.test.context.ContextConfiguration; 14 | import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; 15 | import org.springframework.web.context.WebApplicationContext; 16 | import org.springframework.web.context.support.GenericWebApplicationContext; 17 | import org.springframework.web.servlet.DispatcherServlet; 18 | 19 | import javax.annotation.Resource; 20 | 21 | @RunWith(SpringJUnit4ClassRunner.class) 22 | @ContextConfiguration({"file:src/main/webapp/WEB-INF/applicationContext.xml"}) 23 | public abstract class BaseWebApplicationContextTests { 24 | 25 | 26 | // this servlet is going to be instantiated by ourselves 27 | // so that we can test the servlet behaviour w/o actual web container 28 | // deployment 29 | protected DispatcherServlet servlet; 30 | 31 | 32 | // we need to get at the context already loaded via the @ContextConfiguration annotation. 33 | @Resource 34 | protected ApplicationContext applicationContext; 35 | 36 | protected MockHttpServletRequest request; 37 | protected MockHttpServletResponse response; 38 | protected BookRestController controller; 39 | protected BookService bookService; 40 | 41 | 42 | @Before 43 | public void initDispatcherServlet() throws Exception{ 44 | servlet = new DispatcherServlet() { 45 | 46 | @Override 47 | protected WebApplicationContext createWebApplicationContext( 48 | WebApplicationContext parent) throws BeansException { 49 | 50 | GenericWebApplicationContext gwac = new GenericWebApplicationContext(); 51 | gwac.setParent(applicationContext); 52 | gwac.refresh(); 53 | return gwac; 54 | } 55 | }; 56 | 57 | request = new MockHttpServletRequest(); 58 | response = new MockHttpServletResponse(); 59 | controller = (BookRestController) applicationContext.getBean("bookRestController"); 60 | bookService = (BookService)applicationContext.getBean("bookService"); 61 | MockServletContext servletContext = new MockServletContext("src/main/webapp", new FileSystemResourceLoader()); 62 | 63 | servlet.init(new MockServletConfig(servletContext)); 64 | } 65 | } -------------------------------------------------------------------------------- /src/main/webapp/WEB-INF/applicationContext.xml: -------------------------------------------------------------------------------- 1 | 2 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | com.choudhury.domain.BookList 38 | com.choudhury.domain.Book 39 | com.choudhury.domain.ObjectWithId 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | json=application/json 52 | xml=application/xml 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | -------------------------------------------------------------------------------- /src/test/java/com/choudhury/controller/TestRestService.java: -------------------------------------------------------------------------------- 1 | package com.choudhury.controller; 2 | 3 | 4 | 5 | import com.fasterxml.jackson.databind.JsonNode; 6 | import com.fasterxml.jackson.databind.ObjectMapper; 7 | import org.junit.Assert; 8 | import org.junit.Test; 9 | 10 | import java.io.IOException; 11 | 12 | 13 | public class TestRestService extends BaseWebApplicationContextTests { 14 | 15 | @Test 16 | public void testGetBookAsXML() throws Exception { 17 | request.setMethod("GET"); 18 | request.addHeader("Accept", "application/xml"); 19 | request.addHeader("Content-Type", "application/xml"); 20 | request.setRequestURI("/book/1"); 21 | request.setContentType("application/xml"); 22 | request.setMethod("GET"); 23 | 24 | servlet.service(request, response); 25 | String result = response.getContentAsString(); 26 | Assert.assertEquals(200, response.getStatus()); 27 | String expectedXML = "1William SmithAdvanced Java"; 28 | Assert.assertEquals(expectedXML, result); 29 | } 30 | 31 | @Test 32 | public void testGetBookAsJSon() throws Exception { 33 | request.setMethod("GET"); 34 | request.addHeader("Accept", "application/json"); 35 | request.addHeader("Content-Type", "application/xml"); 36 | request.setRequestURI("/book/1"); 37 | request.setContentType("application/xml"); 38 | request.setMethod("GET"); 39 | 40 | servlet.service(request, response); 41 | String result = response.getContentAsString(); 42 | Assert.assertEquals(200, response.getStatus()); 43 | String expectedJSON = "{\"author\":\"William Smith\",\"title\":\"Advanced Java\",\"id\":1}"; 44 | Assert.assertEquals(createTree(expectedJSON), createTree(result)); 45 | } 46 | 47 | private JsonNode createTree(String jsonString) throws IOException { 48 | ObjectMapper mapper = new ObjectMapper(); 49 | return mapper.readValue(jsonString, JsonNode.class); 50 | } 51 | 52 | @Test 53 | public void testAddBookUsingXML() throws Exception { 54 | request.setMethod("POST"); 55 | request.addHeader("Accept", "application/xml"); 56 | request.setContentType("application/xml"); 57 | request.setRequestURI("/book/add"); 58 | request.addHeader("Content-Type", "application/xml"); 59 | request.setContent("-1William SmithAdvanced Java".getBytes("utf-8")); 60 | servlet.service(request, response); 61 | String result = response.getContentAsString(); 62 | int status = response.getStatus(); 63 | Assert.assertEquals(201, status); 64 | long expectedId=bookService.getBookCount()-1; 65 | String expectedXML = ""+expectedId+""; 66 | Assert.assertEquals(expectedXML, result); 67 | } 68 | 69 | @Test 70 | public void testAddBookUsingJSON() throws Exception { 71 | request.setMethod("POST"); 72 | request.addHeader("Accept", "application/json"); 73 | request.setContentType("application/json;charset=UTF-8"); 74 | request.setRequestURI("/book/add"); 75 | request.addHeader("Content-Type", "application/json;charset=UTF-8"); 76 | request.setContent("{\"author\":\"William Smith\",\"title\":\"Advanced Java\",\"id\":-1}".getBytes("utf-8")); 77 | servlet.service(request, response); 78 | String result = response.getContentAsString(); 79 | int status = response.getStatus(); 80 | Assert.assertEquals(201, status); 81 | long expectedId=bookService.getBookCount()-1; 82 | String expectedJSON = "{\"id\":"+expectedId+"}"; 83 | Assert.assertEquals(createTree(expectedJSON), createTree(result)); 84 | } 85 | 86 | } 87 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | com.choudhury 5 | spring-rest 6 | war 7 | 1.0.0-SNAPSHOT 8 | Choudhury.com Spring Rest Webapp 9 | http://maven.apache.org 10 | 11 | 12 | 4.3.20.RELEASE 13 | 2.13.4.1 14 | 15 | 16 | 17 | 18 | org.springframework.maven.snapshot 19 | Spring Maven Snapshot Repository 20 | http://maven.springframework.org/snapshot 21 | 22 | true 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | junit 34 | junit 35 | 4.13.1 36 | jar 37 | test 38 | 39 | 40 | 41 | org.springframework 42 | spring-test 43 | ${spring.framework.version} 44 | test 45 | 46 | 47 | 48 | org.springframework 49 | spring-web 50 | ${spring.framework.version} 51 | 52 | 53 | 54 | org.springframework 55 | spring-core 56 | ${spring.framework.version} 57 | 58 | 59 | 60 | org.springframework 61 | spring-webmvc 62 | ${spring.framework.version} 63 | 64 | 65 | 66 | javax.servlet 67 | javax.servlet-api 68 | 3.1.0 69 | provided 70 | 71 | 72 | 73 | org.springframework 74 | spring-oxm 75 | ${spring.framework.version} 76 | 77 | 78 | 79 | 80 | log4j 81 | log4j 82 | 1.2.17 83 | 84 | 85 | 86 | org.slf4j 87 | slf4j-log4j12 88 | 1.7.25 89 | 90 | 91 | 92 | jstl 93 | jstl 94 | 1.1.2 95 | 96 | 97 | 98 | taglibs 99 | standard 100 | 1.1.2 101 | 102 | 103 | 104 | com.thoughtworks.xstream 105 | xstream 106 | 1.4.18 107 | 108 | 109 | 110 | xpp3 111 | xpp3_min 112 | 1.1.4c 113 | 114 | 115 | 116 | net.sf.kxml 117 | kxml2 118 | 2.3.0 119 | 120 | 121 | 122 | com.fasterxml.jackson.core 123 | jackson-core 124 | ${jackson.version} 125 | 126 | 127 | 128 | 129 | com.fasterxml.jackson.core 130 | jackson-databind 131 | ${jackson.version} 132 | 133 | 134 | 135 | javax.inject 136 | javax.inject 137 | 1 138 | 139 | 140 | 141 | javax.annotation 142 | javax.annotation-api 143 | 1.3.2 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | org.apache.maven.plugins 154 | maven-compiler-plugin 155 | 3.8.0 156 | 157 | 1.7 158 | 1.7 159 | 160 | 161 | 162 | 163 | 164 | 165 | maven-surefire-plugin 166 | 2.22.0 167 | 168 | 169 | 170 | 171 | choudhury-rest 172 | 173 | 174 | --------------------------------------------------------------------------------