From bc29d58e2c7d8b06de93781efb3a4cc9e7171ee2 Mon Sep 17 00:00:00 2001 From: Matt Warman Date: Wed, 11 Feb 2015 16:35:33 -0500 Subject: [PATCH 1/7] Added AbstractTest and AbstractControllerTest classes to provide base JUnit test capabilities. Created tests for GreetingService and GreetingController. --- pom.xml | 7 + .../ws/service/GreetingService.java | 5 + .../ws/service/GreetingServiceBean.java | 9 + .../ws/web/api/GreetingController.java | 7 +- .../leanstacks/ws/AbstractControllerTest.java | 69 +++++++ .../java/com/leanstacks/ws/AbstractTest.java | 22 +++ .../ws/service/GreetingServiceTest.java | 119 ++++++++++++ .../ws/web/api/GreetingControllerTest.java | 177 ++++++++++++++++++ 8 files changed, 411 insertions(+), 4 deletions(-) create mode 100644 src/test/java/com/leanstacks/ws/AbstractControllerTest.java create mode 100644 src/test/java/com/leanstacks/ws/AbstractTest.java create mode 100644 src/test/java/com/leanstacks/ws/service/GreetingServiceTest.java create mode 100644 src/test/java/com/leanstacks/ws/web/api/GreetingControllerTest.java diff --git a/pom.xml b/pom.xml index cae194f..34cb609 100644 --- a/pom.xml +++ b/pom.xml @@ -39,6 +39,13 @@ guava 18.0 + + + + org.springframework.boot + spring-boot-starter-test + test + diff --git a/src/main/java/com/leanstacks/ws/service/GreetingService.java b/src/main/java/com/leanstacks/ws/service/GreetingService.java index e13bab4..db21330 100644 --- a/src/main/java/com/leanstacks/ws/service/GreetingService.java +++ b/src/main/java/com/leanstacks/ws/service/GreetingService.java @@ -50,4 +50,9 @@ public interface GreetingService { */ void delete(Long id); + /** + * Evicts all members of the "greetings" cache. + */ + void evictCache(); + } diff --git a/src/main/java/com/leanstacks/ws/service/GreetingServiceBean.java b/src/main/java/com/leanstacks/ws/service/GreetingServiceBean.java index c1dda9f..4a36dce 100644 --- a/src/main/java/com/leanstacks/ws/service/GreetingServiceBean.java +++ b/src/main/java/com/leanstacks/ws/service/GreetingServiceBean.java @@ -111,4 +111,13 @@ public void delete(Long id) { logger.info("< delete {}", id); } + @CacheEvict( + value = "greetings", + allEntries = true) + @Override + public void evictCache() { + logger.info("> evictCache"); + logger.info("< evictCache"); + } + } diff --git a/src/main/java/com/leanstacks/ws/web/api/GreetingController.java b/src/main/java/com/leanstacks/ws/web/api/GreetingController.java index 988a4a4..75d336f 100644 --- a/src/main/java/com/leanstacks/ws/web/api/GreetingController.java +++ b/src/main/java/com/leanstacks/ws/web/api/GreetingController.java @@ -116,10 +116,9 @@ public ResponseEntity updateGreeting( @RequestMapping( value = "/api/greetings/{id}", - method = RequestMethod.DELETE, - consumes = MediaType.APPLICATION_JSON_VALUE) - public ResponseEntity deleteGreeting(@PathVariable("id") Long id, - @RequestBody Greeting greeting) throws Exception { + method = RequestMethod.DELETE) + public ResponseEntity deleteGreeting(@PathVariable("id") Long id) + throws Exception { logger.info("> deleteGreeting"); greetingService.delete(id); diff --git a/src/test/java/com/leanstacks/ws/AbstractControllerTest.java b/src/test/java/com/leanstacks/ws/AbstractControllerTest.java new file mode 100644 index 0000000..87da316 --- /dev/null +++ b/src/test/java/com/leanstacks/ws/AbstractControllerTest.java @@ -0,0 +1,69 @@ +package com.leanstacks.ws; + +import java.io.IOException; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.context.web.WebAppConfiguration; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; +import org.springframework.web.context.WebApplicationContext; + +import com.fasterxml.jackson.core.JsonParseException; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonMappingException; +import com.fasterxml.jackson.databind.ObjectMapper; + +/** + * This class extends the functionality of AbstractTest. AbstractControllerTest + * is the parent of all web controller unit test classes. The class ensures that + * a type of WebApplicationContext is built and prepares a MockMvc instance for + * use in test methods. + * + * @author Matt Warman + */ +@WebAppConfiguration +public abstract class AbstractControllerTest extends AbstractTest { + + protected MockMvc mvc; + + @Autowired + protected WebApplicationContext webApplicationContext; + + /** + * Prepares the test class for execution of web tests. Builds a MockMvc + * instance. Call this method from the concrete JUnit test class in the + * @Before setup method. + */ + protected void setUp() { + mvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build(); + } + + /** + * Maps an Object into a JSON String. Uses a Jackson ObjectMapper. + * @param obj The Object to map. + * @return A String of JSON. + * @throws JsonProcessingException Thrown if an error occurs while mapping. + */ + protected String mapToJson(Object obj) throws JsonProcessingException { + ObjectMapper mapper = new ObjectMapper(); + return mapper.writeValueAsString(obj); + } + + /** + * Maps a String of JSON into an instance of a Class of type T. Uses a + * Jackson ObjectMapper. + * @param json A String of JSON. + * @param clazz A Class of type T. The mapper will attempt to convert the + * JSON into an Object of this Class type. + * @return An Object of type T. + * @throws JsonParseException Thrown if an error occurs while mapping. + * @throws JsonMappingException Thrown if an error occurs while mapping. + * @throws IOException Thrown if an error occurs while mapping. + */ + protected T mapFromJson(String json, Class clazz) + throws JsonParseException, JsonMappingException, IOException { + ObjectMapper mapper = new ObjectMapper(); + return mapper.readValue(json, clazz); + } + +} diff --git a/src/test/java/com/leanstacks/ws/AbstractTest.java b/src/test/java/com/leanstacks/ws/AbstractTest.java new file mode 100644 index 0000000..f691879 --- /dev/null +++ b/src/test/java/com/leanstacks/ws/AbstractTest.java @@ -0,0 +1,22 @@ +package com.leanstacks.ws; + +import org.junit.runner.RunWith; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.boot.test.SpringApplicationConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +/** + * The AbstractTest class is the parent of all JUnit test classes. This class + * configures the test ApplicationContext and test runner environment. + * + * @author Matt Warman + */ +@RunWith(SpringJUnit4ClassRunner.class) +@SpringApplicationConfiguration( + classes = Application.class) +public abstract class AbstractTest { + + protected Logger logger = LoggerFactory.getLogger(this.getClass()); + +} diff --git a/src/test/java/com/leanstacks/ws/service/GreetingServiceTest.java b/src/test/java/com/leanstacks/ws/service/GreetingServiceTest.java new file mode 100644 index 0000000..1ba0fa7 --- /dev/null +++ b/src/test/java/com/leanstacks/ws/service/GreetingServiceTest.java @@ -0,0 +1,119 @@ +package com.leanstacks.ws.service; + +import java.util.Collection; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.transaction.annotation.Transactional; + +import com.leanstacks.ws.AbstractTest; +import com.leanstacks.ws.model.Greeting; + +@Transactional +public class GreetingServiceTest extends AbstractTest { + + @Autowired + private GreetingService greetingService; + + @Before + public void setUp() { + greetingService.evictCache(); + } + + @Test + public void testGetGreetings() { + + Collection greetings = greetingService.findAll(); + + Assert.assertNotNull("failure - expected not null", greetings); + Assert.assertEquals("failure - expected 2 greetings", 2, + greetings.size()); + + } + + @Test + public void testGetGreeting() { + + Collection greetings = greetingService.findAll(); + logger.info("Number of greetings is: {}", greetings.size()); + + Long id = new Long(1); + + Greeting greeting = greetingService.findOne(id); + + Assert.assertNotNull("failure - expected not null", greeting); + Assert.assertEquals("failure - expected greeting.id match", id, + greeting.getId()); + + } + + @Test + public void testCreateGreeting() { + + Greeting greeting = new Greeting(); + greeting.setText("test"); + + Greeting createdGreeting = greetingService.create(greeting); + + Assert.assertNotNull("failure - expected greeting not null", + createdGreeting); + Assert.assertNotNull("failure - expected greeting.id not null", + createdGreeting.getId()); + Assert.assertEquals("failure - expected greeting.text match", "test", + createdGreeting.getText()); + + Collection greetings = greetingService.findAll(); + + Assert.assertEquals("failure - expected 3 greetings", 3, + greetings.size()); + + } + + @Test + public void testUpdateGreeting() { + + Long id = new Long(1); + + Greeting greeting = greetingService.findOne(id); + + Assert.assertNotNull("failure - expected greeting not null", greeting); + + String updatedText = greeting.getText() + " test"; + greeting.setText(updatedText); + Greeting updatedGreeting = greetingService.update(greeting); + + Assert.assertNotNull("failure - expected updated greeting not null", + updatedGreeting); + Assert.assertEquals("failure - expected updated greeting id unchanged", + id, updatedGreeting.getId()); + Assert.assertEquals("failure - expected updated greeting text match", + updatedText, updatedGreeting.getText()); + + } + + @Test + public void testDeleteGreeting() { + + Long id = new Long(1); + + Greeting greeting = greetingService.findOne(id); + + Assert.assertNotNull("failure - expected greeting not null", greeting); + + greetingService.delete(id); + + Collection greetings = greetingService.findAll(); + + Assert.assertEquals("failure - expected 1 greeting", 1, + greetings.size()); + + Greeting deletedGreeting = greetingService.findOne(id); + + Assert.assertNull("failure - expected greeting to be deleted", + deletedGreeting); + + } + +} diff --git a/src/test/java/com/leanstacks/ws/web/api/GreetingControllerTest.java b/src/test/java/com/leanstacks/ws/web/api/GreetingControllerTest.java new file mode 100644 index 0000000..3f7f20d --- /dev/null +++ b/src/test/java/com/leanstacks/ws/web/api/GreetingControllerTest.java @@ -0,0 +1,177 @@ +package com.leanstacks.ws.web.api; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.MediaType; +import org.springframework.test.web.servlet.MvcResult; +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; +import org.springframework.transaction.annotation.Transactional; + +import com.leanstacks.ws.AbstractControllerTest; +import com.leanstacks.ws.model.Greeting; +import com.leanstacks.ws.service.GreetingService; + +@Transactional +public class GreetingControllerTest extends AbstractControllerTest { + + @Autowired + private GreetingService greetingService; + + @Before + public void setUp() { + super.setUp(); + greetingService.evictCache(); + + } + + @Test + public void testGetGreetings() throws Exception { + + String uri = "/api/greetings"; + + MvcResult result = mvc.perform( + MockMvcRequestBuilders.get(uri).accept( + MediaType.APPLICATION_JSON)).andReturn(); + + String content = result.getResponse().getContentAsString(); + int status = result.getResponse().getStatus(); + + Assert.assertEquals("failure - expected HTTP status 200", 200, status); + Assert.assertTrue( + "failure - expected HTTP response body to have a value", + content.trim().length() > 0); + + } + + @Test + public void testGetGreeting() throws Exception { + + String uri = "/api/greetings/{id}"; + Long id = new Long(1); + + MvcResult result = mvc.perform( + MockMvcRequestBuilders.get(uri, id).accept( + MediaType.APPLICATION_JSON)).andReturn(); + + String content = result.getResponse().getContentAsString(); + int status = result.getResponse().getStatus(); + + Assert.assertEquals("failure - expected HTTP status 200", 200, status); + Assert.assertTrue( + "failure - expected HTTP response body to have a value", + content.trim().length() > 0); + + } + + @Test + public void testGetGreetingNotFound() throws Exception { + + String uri = "/api/greetings/{id}"; + Long id = Long.MAX_VALUE; + + MvcResult result = mvc.perform( + MockMvcRequestBuilders.get(uri, id).accept( + MediaType.APPLICATION_JSON)).andReturn(); + + String content = result.getResponse().getContentAsString(); + int status = result.getResponse().getStatus(); + + Assert.assertEquals("failure - expected HTTP status 404", 404, status); + Assert.assertTrue("failure - expected HTTP response body to be empty", + content.trim().length() == 0); + + } + + @Test + public void testCreateGreeting() throws Exception { + + String uri = "/api/greetings"; + Greeting greeting = new Greeting(); + greeting.setText("test"); + String inputJson = super.mapToJson(greeting); + + MvcResult result = mvc.perform( + MockMvcRequestBuilders.post(uri) + .contentType(MediaType.APPLICATION_JSON) + .accept(MediaType.APPLICATION_JSON).content(inputJson)) + .andReturn(); + + String content = result.getResponse().getContentAsString(); + int status = result.getResponse().getStatus(); + + Assert.assertEquals("failure - expected HTTP status 201", 201, status); + Assert.assertTrue( + "failure - expected HTTP response body to have a value", + content.trim().length() > 0); + + Greeting createdGreeting = super.mapFromJson(content, Greeting.class); + + Assert.assertNotNull("failure - expected greeting not null", + createdGreeting); + Assert.assertNotNull("failure - expected greeting.id not null", + createdGreeting.getId()); + Assert.assertEquals("failure - expected greeting.text match", "test", + createdGreeting.getText()); + + } + + @Test + public void testUpdateGreeting() throws Exception { + + String uri = "/api/greetings/{id}"; + Long id = new Long(1); + Greeting greeting = greetingService.findOne(id); + String updatedText = greeting.getText() + " test"; + greeting.setText(updatedText); + String inputJson = super.mapToJson(greeting); + + MvcResult result = mvc.perform( + MockMvcRequestBuilders.put(uri, id) + .contentType(MediaType.APPLICATION_JSON) + .accept(MediaType.APPLICATION_JSON).content(inputJson)) + .andReturn(); + + String content = result.getResponse().getContentAsString(); + int status = result.getResponse().getStatus(); + + Assert.assertEquals("failure - expected HTTP status 200", 200, status); + Assert.assertTrue( + "failure - expected HTTP response body to have a value", + content.trim().length() > 0); + + Greeting updatedGreeting = super.mapFromJson(content, Greeting.class); + + Assert.assertNotNull("failure - expected greeting not null", + updatedGreeting); + Assert.assertEquals("failure - expected greeting.id unchanged", + greeting.getId(), updatedGreeting.getId()); + Assert.assertEquals("failure - expected updated greeting text match", + updatedText, updatedGreeting.getText()); + + } + + @Test + public void testDeleteGreeting() throws Exception { + + String uri = "/api/greetings/{id}"; + Long id = new Long(1); + + MvcResult result = mvc.perform(MockMvcRequestBuilders.delete(uri, id)) + .andReturn(); + + String content = result.getResponse().getContentAsString(); + int status = result.getResponse().getStatus(); + + Assert.assertEquals("failure - expected HTTP status 204", 204, status); + Assert.assertTrue("failure - expected HTTP response body to be empty", + content.trim().length() == 0); + + Greeting deletedGreeting = greetingService.findOne(id); + + Assert.assertNull("failure - expected greeting to be null", + deletedGreeting); + + } +} From 9bba91bf18545e8cccc1f388ee9c0c49f66c5ab6 Mon Sep 17 00:00:00 2001 From: Matt Warman Date: Thu, 12 Feb 2015 11:22:43 -0500 Subject: [PATCH 2/7] Added initial GreetingService unit tests. --- .../java/com/leanstacks/ws/service/GreetingServiceTest.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/test/java/com/leanstacks/ws/service/GreetingServiceTest.java b/src/test/java/com/leanstacks/ws/service/GreetingServiceTest.java index 1ba0fa7..566ad0a 100644 --- a/src/test/java/com/leanstacks/ws/service/GreetingServiceTest.java +++ b/src/test/java/com/leanstacks/ws/service/GreetingServiceTest.java @@ -11,6 +11,11 @@ import com.leanstacks.ws.AbstractTest; import com.leanstacks.ws.model.Greeting; +/** + * Unit test methods for the GreetingService and GreetingServiceBean. + * + * @author Matt Warman + */ @Transactional public class GreetingServiceTest extends AbstractTest { From b6e185639bf195fd448919bb75150ed9c72f138a Mon Sep 17 00:00:00 2001 From: Matt Warman Date: Thu, 12 Feb 2015 11:23:03 -0500 Subject: [PATCH 3/7] Added initial GreetingController unit tests. --- .../com/leanstacks/ws/web/api/GreetingControllerTest.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/test/java/com/leanstacks/ws/web/api/GreetingControllerTest.java b/src/test/java/com/leanstacks/ws/web/api/GreetingControllerTest.java index 3f7f20d..f08bdac 100644 --- a/src/test/java/com/leanstacks/ws/web/api/GreetingControllerTest.java +++ b/src/test/java/com/leanstacks/ws/web/api/GreetingControllerTest.java @@ -13,6 +13,11 @@ import com.leanstacks.ws.model.Greeting; import com.leanstacks.ws.service.GreetingService; +/** + * Unit tests for the GreetingController. + * + * @author Matt Warman + */ @Transactional public class GreetingControllerTest extends AbstractControllerTest { From d6c331506621bf87861b694881306a5cf8f4cacf Mon Sep 17 00:00:00 2001 From: Matt Warman Date: Thu, 12 Feb 2015 16:23:03 -0500 Subject: [PATCH 4/7] Additional GreetingService unit tests. --- .../ws/service/GreetingServiceBean.java | 8 ++- .../ws/web/api/GreetingController.java | 10 ---- .../ws/service/GreetingServiceTest.java | 59 ++++++++++++++++++- 3 files changed, 62 insertions(+), 15 deletions(-) diff --git a/src/main/java/com/leanstacks/ws/service/GreetingServiceBean.java b/src/main/java/com/leanstacks/ws/service/GreetingServiceBean.java index 4a36dce..4a44b9f 100644 --- a/src/main/java/com/leanstacks/ws/service/GreetingServiceBean.java +++ b/src/main/java/com/leanstacks/ws/service/GreetingServiceBean.java @@ -2,6 +2,9 @@ import java.util.Collection; +import javax.persistence.EntityExistsException; +import javax.persistence.NoResultException; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -65,7 +68,8 @@ public Greeting create(Greeting greeting) { if (greeting.getId() != null) { logger.error("Attempted to create a Greeting, but id attribute was not null."); logger.info("< create"); - return null; + throw new EntityExistsException( + "Cannot create new Greeting with supplied id. The id attribute must be null to create an entity."); } Greeting savedGreeting = greetingRepository.save(greeting); @@ -89,7 +93,7 @@ public Greeting update(Greeting greeting) { if (greetingToUpdate == null) { logger.error("Attempted to update a Greeting, but the entity does not exist."); logger.info("< update {}", greeting.getId()); - return null; + throw new NoResultException("Requested Greeting not found."); } Greeting updatedGreeting = greetingRepository.save(greeting); diff --git a/src/main/java/com/leanstacks/ws/web/api/GreetingController.java b/src/main/java/com/leanstacks/ws/web/api/GreetingController.java index 75d336f..480554d 100644 --- a/src/main/java/com/leanstacks/ws/web/api/GreetingController.java +++ b/src/main/java/com/leanstacks/ws/web/api/GreetingController.java @@ -84,11 +84,6 @@ public ResponseEntity createGreeting( logger.info("> createGreeting"); Greeting savedGreeting = greetingService.create(greeting); - if (savedGreeting == null) { - logger.info("< createGreeting"); - return new ResponseEntity( - HttpStatus.INTERNAL_SERVER_ERROR); - } logger.info("< createGreeting"); return new ResponseEntity(savedGreeting, HttpStatus.CREATED); @@ -104,11 +99,6 @@ public ResponseEntity updateGreeting( logger.info("> updateGreeting"); Greeting updatedGreeting = greetingService.update(greeting); - if (updatedGreeting == null) { - logger.info("< updateGreeting"); - return new ResponseEntity( - HttpStatus.INTERNAL_SERVER_ERROR); - } logger.info("< updateGreeting"); return new ResponseEntity(updatedGreeting, HttpStatus.OK); diff --git a/src/test/java/com/leanstacks/ws/service/GreetingServiceTest.java b/src/test/java/com/leanstacks/ws/service/GreetingServiceTest.java index 566ad0a..08ea8d2 100644 --- a/src/test/java/com/leanstacks/ws/service/GreetingServiceTest.java +++ b/src/test/java/com/leanstacks/ws/service/GreetingServiceTest.java @@ -2,6 +2,9 @@ import java.util.Collection; +import javax.persistence.EntityExistsException; +import javax.persistence.NoResultException; + import org.junit.Assert; import org.junit.Before; import org.junit.Test; @@ -41,9 +44,6 @@ public void testGetGreetings() { @Test public void testGetGreeting() { - Collection greetings = greetingService.findAll(); - logger.info("Number of greetings is: {}", greetings.size()); - Long id = new Long(1); Greeting greeting = greetingService.findOne(id); @@ -54,6 +54,17 @@ public void testGetGreeting() { } + @Test + public void testGetGreetingNotFound() { + + Long id = Long.MAX_VALUE; + + Greeting greeting = greetingService.findOne(id); + + Assert.assertNull("failure - expected null", greeting); + + } + @Test public void testCreateGreeting() { @@ -76,6 +87,27 @@ public void testCreateGreeting() { } + @Test + public void testCreateGreetingWithId() { + + Exception e = null; + + Greeting greeting = new Greeting(); + greeting.setId(Long.MAX_VALUE); + greeting.setText("test"); + + try { + Greeting createdGreeting = greetingService.create(greeting); + } catch (EntityExistsException eee) { + e = eee; + } + + Assert.assertNotNull("failure - expected exception", e); + Assert.assertTrue("failure - expected EntityExistsException", + e instanceof EntityExistsException); + + } + @Test public void testUpdateGreeting() { @@ -98,6 +130,27 @@ public void testUpdateGreeting() { } + @Test + public void testUpdateGreetingNotFound() { + + Exception e = null; + + Greeting greeting = new Greeting(); + greeting.setId(Long.MAX_VALUE); + greeting.setText("test"); + + try { + Greeting updatedGreeting = greetingService.update(greeting); + } catch (NoResultException nre) { + e = nre; + } + + Assert.assertNotNull("failure - expected exception", e); + Assert.assertTrue("failure - expected NoResultException", + e instanceof NoResultException); + + } + @Test public void testDeleteGreeting() { From 66007c043f941e2800430f381d9cac8028849bd8 Mon Sep 17 00:00:00 2001 From: Matt Warman Date: Fri, 13 Feb 2015 12:16:01 -0500 Subject: [PATCH 5/7] Created BaseController for extension by web layer Controller classes. --- .../leanstacks/ws/web/api/BaseController.java | 33 +++++++++++++++++++ .../ws/web/api/GreetingController.java | 16 +-------- 2 files changed, 34 insertions(+), 15 deletions(-) create mode 100644 src/main/java/com/leanstacks/ws/web/api/BaseController.java diff --git a/src/main/java/com/leanstacks/ws/web/api/BaseController.java b/src/main/java/com/leanstacks/ws/web/api/BaseController.java new file mode 100644 index 0000000..c728601 --- /dev/null +++ b/src/main/java/com/leanstacks/ws/web/api/BaseController.java @@ -0,0 +1,33 @@ +package com.leanstacks.ws.web.api; + +import javax.persistence.NoResultException; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.ExceptionHandler; + +public class BaseController { + + protected Logger logger = LoggerFactory.getLogger(this.getClass()); + + @ExceptionHandler(NoResultException.class) + public ResponseEntity handleNoResultException( + NoResultException nre) { + logger.error("> handleNoResultException"); + logger.error("- NoResultException: ", nre); + logger.error("< handleNoResultException"); + return new ResponseEntity(HttpStatus.NOT_FOUND); + } + + @ExceptionHandler(Exception.class) + public ResponseEntity handleException(Exception e) { + logger.error("> handleException"); + logger.error("- Exception: ", e); + logger.error("< handleException"); + return new ResponseEntity(e, + HttpStatus.INTERNAL_SERVER_ERROR); + } + +} diff --git a/src/main/java/com/leanstacks/ws/web/api/GreetingController.java b/src/main/java/com/leanstacks/ws/web/api/GreetingController.java index 480554d..a779275 100644 --- a/src/main/java/com/leanstacks/ws/web/api/GreetingController.java +++ b/src/main/java/com/leanstacks/ws/web/api/GreetingController.java @@ -2,13 +2,10 @@ import java.util.Collection; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; -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; @@ -26,22 +23,11 @@ * @author Matt Warman */ @RestController -public class GreetingController { - - private Logger logger = LoggerFactory.getLogger(this.getClass()); +public class GreetingController extends BaseController { @Autowired private GreetingService greetingService; - @ExceptionHandler(Exception.class) - public ResponseEntity handleException(Exception e) { - logger.error("> handleException"); - logger.error("- Exception: ", e); - logger.error("< handleException"); - return new ResponseEntity(e, - HttpStatus.INTERNAL_SERVER_ERROR); - } - @RequestMapping( value = "/api/greetings", method = RequestMethod.GET, From 730da80979839dc4899eb1c59f2b5b7089692c4b Mon Sep 17 00:00:00 2001 From: Matt Warman Date: Fri, 13 Feb 2015 16:05:51 -0500 Subject: [PATCH 6/7] Added Actuator for monitoring and management. Configured attributes for 'info' endpoint. --- pom.xml | 10 ++++++++-- src/main/resources/config/application.properties | 11 +++++++++++ 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 34cb609..ae7ddc3 100644 --- a/pom.xml +++ b/pom.xml @@ -5,19 +5,25 @@ com.leanstacks skeleton-ws-spring-boot - 0.2.0 + 0.3.0 + Web Services Project Skeleton + Skeleton project for RESTful web services using Spring Boot. org.springframework.boot spring-boot-starter-parent 1.2.1.RELEASE - + org.springframework.boot spring-boot-starter-web + + org.springframework.boot + spring-boot-starter-actuator + org.springframework.boot spring-boot-starter-data-jpa diff --git a/src/main/resources/config/application.properties b/src/main/resources/config/application.properties index e244104..3c3679b 100644 --- a/src/main/resources/config/application.properties +++ b/src/main/resources/config/application.properties @@ -14,6 +14,17 @@ server.port= # Initialization spring.datasource.data=classpath:/data/hsqldb/data.sql +## +# Actuator Configuration +## +management.context-path=/actuators + +info.build.groupId=@project.groupId@ +info.build.artifact=@project.artifactId@ +info.build.name=@project.name@ +info.build.description=@project.description@ +info.build.version=@project.version@ + ## # Batch Configuration ## From d8d5221206a1e4ee6c09f153ee2ecb7699a118b4 Mon Sep 17 00:00:00 2001 From: Matt Warman Date: Sat, 14 Feb 2015 08:24:43 -0500 Subject: [PATCH 7/7] Added Actuator CounterService to business service and batch beans. --- .../leanstacks/ws/batch/GreetingBatchBean.java | 18 ++++++++++++++++++ .../ws/service/GreetingServiceBean.java | 18 ++++++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/src/main/java/com/leanstacks/ws/batch/GreetingBatchBean.java b/src/main/java/com/leanstacks/ws/batch/GreetingBatchBean.java index 0171543..355e5d4 100644 --- a/src/main/java/com/leanstacks/ws/batch/GreetingBatchBean.java +++ b/src/main/java/com/leanstacks/ws/batch/GreetingBatchBean.java @@ -5,6 +5,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.actuate.metrics.CounterService; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; @@ -22,6 +23,9 @@ public class GreetingBatchBean { private Logger logger = LoggerFactory.getLogger(this.getClass()); + @Autowired + private CounterService counterService; + @Autowired private GreetingService greetingService; @@ -38,6 +42,8 @@ public class GreetingBatchBean { public void cronJob() { logger.info("> cronJob"); + counterService.increment("method.invoked.greetingBatchBean.cronJob"); + // Add scheduled logic here Collection greetings = greetingService.findAll(); @@ -57,6 +63,9 @@ public void cronJob() { public void fixedRateJob() { logger.info("> fixedRateJob"); + counterService + .increment("method.invoked.greetingBatchBean.fixedRateJob"); + // Add scheduled logic here Collection greetings = greetingService.findAll(); @@ -79,6 +88,9 @@ public void fixedRateJob() { public void fixedRateJobWithInitialDelay() { logger.info("> fixedRateJobWithInitialDelay"); + counterService + .increment("method.invoked.greetingBatchBean.fixedRateJobWithInitialDelay"); + // Add scheduled logic here Collection greetings = greetingService.findAll(); @@ -98,6 +110,9 @@ public void fixedRateJobWithInitialDelay() { public void fixedDelayJob() { logger.info("> fixedDelayJob"); + counterService + .increment("method.invoked.greetingBatchBean.fixedDelayJob"); + // Add scheduled logic here Collection greetings = greetingService.findAll(); @@ -120,6 +135,9 @@ public void fixedDelayJob() { public void fixedDelayJobWithInitialDelay() { logger.info("> fixedDelayJobWithInitialDelay"); + counterService + .increment("method.invoked.greetingBatchBean.fixedDelayJobWithInitialDelay"); + // Add scheduled logic here Collection greetings = greetingService.findAll(); diff --git a/src/main/java/com/leanstacks/ws/service/GreetingServiceBean.java b/src/main/java/com/leanstacks/ws/service/GreetingServiceBean.java index 4a44b9f..d307346 100644 --- a/src/main/java/com/leanstacks/ws/service/GreetingServiceBean.java +++ b/src/main/java/com/leanstacks/ws/service/GreetingServiceBean.java @@ -8,6 +8,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.actuate.metrics.CounterService; import org.springframework.cache.annotation.CacheEvict; import org.springframework.cache.annotation.CachePut; import org.springframework.cache.annotation.Cacheable; @@ -28,6 +29,9 @@ public class GreetingServiceBean implements GreetingService { private Logger logger = LoggerFactory.getLogger(this.getClass()); + @Autowired + private CounterService counterService; + @Autowired private GreetingRepository greetingRepository; @@ -35,6 +39,8 @@ public class GreetingServiceBean implements GreetingService { public Collection findAll() { logger.info("> findAll"); + counterService.increment("method.invoked.greetingServiceBean.findAll"); + Collection greetings = greetingRepository.findAll(); logger.info("< findAll"); @@ -48,6 +54,8 @@ public Collection findAll() { public Greeting findOne(Long id) { logger.info("> findOne {}", id); + counterService.increment("method.invoked.greetingServiceBean.findOne"); + Greeting greeting = greetingRepository.findOne(id); logger.info("< findOne {}", id); @@ -62,6 +70,8 @@ public Greeting findOne(Long id) { public Greeting create(Greeting greeting) { logger.info("> create"); + counterService.increment("method.invoked.greetingServiceBean.create"); + // Ensure the entity object to be created does NOT exist in the // repository. Prevent the default behavior of save() which will update // an existing entity if the entity matching the supplied id exists. @@ -86,6 +96,8 @@ public Greeting create(Greeting greeting) { public Greeting update(Greeting greeting) { logger.info("> update {}", greeting.getId()); + counterService.increment("method.invoked.greetingServiceBean.update"); + // Ensure the entity object to be updated exists in the repository to // prevent the default behavior of save() which will persist a new // entity if the entity matching the id does not exist @@ -110,6 +122,8 @@ public Greeting update(Greeting greeting) { public void delete(Long id) { logger.info("> delete {}", id); + counterService.increment("method.invoked.greetingServiceBean.delete"); + greetingRepository.delete(id); logger.info("< delete {}", id); @@ -121,6 +135,10 @@ public void delete(Long id) { @Override public void evictCache() { logger.info("> evictCache"); + + counterService + .increment("method.invoked.greetingServiceBean.evictCache"); + logger.info("< evictCache"); }