diff --git a/Step07.md b/Step07.md index 9af8fb7..30e563a 100644 --- a/Step07.md +++ b/Step07.md @@ -1,12 +1,22 @@ ##What You Will Learn during this Step: -- Create a Rest Service +- What is REST? + - Architectural style for the web. REST specifies a set of constraints. + - Client - Server : Server (service provider) should be different from a client (service consumer). + - Enables loose coupling and independent evolution of server and client as new technologies emerge. + - Each service should be stateless. + - Each Resource has a resource identifier. + - It should be possible to cache response. + - Consumer of the service may not have a direct connection to the Service Provider. Response might be sent from a middle layer cache. + - A resource can have multiple representations. Resource can modified through a message in any of the these representations. + +- Create a REST Service - Autowire SurveyService - Create @GetMapping("/surveys/{surveyId}/questions") - Use @PathVariable String surveyId - http://localhost:8080/surveys/Survey1/questions/ - How does the Bean get converted to a JSON? - Auto Configuration : If Jackson jar is on the class path, message converters are auto created! (Search in log :Creating shared instance of singleton bean 'mappingJackson2HttpMessageConverter') - + ## Useful Snippets and References First Snippet ``` diff --git a/Step08.md b/Step08.md index e4221f1..59f965c 100644 --- a/Step08.md +++ b/Step08.md @@ -2,7 +2,12 @@ - Adding the second method to rest service to retrieve a specific question - This will be a very short step - http://localhost:8080/surveys/Survey1/questions/Question1 - +- Different Request Methods + - GET - Retrieve details of a resource + - POST - Create a new resource + - PUT - Update an existing resource + - PATCH - Update part of a resource + - DELETE - Delete a resource ## Useful Snippets and References First Snippet ``` diff --git a/Step09.md b/Step09.md index 54b14d5..1d95b66 100644 --- a/Step09.md +++ b/Step09.md @@ -1,33 +1,25 @@ ##What You Will Learn during this Step: -- Create a REST Service to add a new question to survey - - @PostMapping("/surveys/{surveyId}/questions") - - @RequestBody Question question - - ResponseEntity.created(location).build() - - ResponseEntity.noContent().build() +- I hate the fact that I've to stop and start the server each time. Can somebody save me? + - Yeah. Spring Boot Developer Tools + - By default, any entry on the classpath that points to a folder will be monitored for changes. + - These will not trigger restart - /META-INF/maven, /META-INF/resources ,/resources ,/static ,/public or /templates + - Folders can be configured : spring.devtools.restart.exclude=static/**,public/** + - Additional Paths : spring.devtools.restart.additional-paths + - LiveReload livereload.com + - Technology in progress!! So, expect a few problems! ## Useful Snippets and References First Snippet ``` - @PostMapping("/surveys/{surveyId}/questions") - ResponseEntity add(@PathVariable String surveyId, - @RequestBody Question question) { - - Question createdTodo = surveyService.addQuestion(surveyId, question); - - if (createdTodo == null) { - return ResponseEntity.noContent().build(); - } - - URI location = ServletUriComponentsBuilder.fromCurrentRequest() - .path("/{id}").buildAndExpand(createdTodo.getId()).toUri(); - - return ResponseEntity.created(location).build(); - - } + + org.springframework.boot + spring-boot-devtools + true + ``` ## Exercises -- Create more REST services of your choice +- Make changes and see if they reflect immediately ## Files List ### /pom.xml @@ -52,8 +44,16 @@ First Snippet org.springframework.boot spring-boot-starter-web + + + org.springframework.boot + spring-boot-devtools + true + + + 1.8 diff --git a/Step10.md b/Step10.md new file mode 100644 index 0000000..deb6a59 --- /dev/null +++ b/Step10.md @@ -0,0 +1,419 @@ +##What You Will Learn during this Step: +- Create a REST Service to add a new question to survey + - @PostMapping("/surveys/{surveyId}/questions") + - @RequestBody Question question + - What should be Response Status for create? + - ResponseEntity.created(location).build() + - ResponseEntity.noContent().build() + - Using Postman : https://www.getpostman.com + +## Useful Snippets and References +First Snippet +``` + @PostMapping("/surveys/{surveyId}/questions") + ResponseEntity add(@PathVariable String surveyId, + @RequestBody Question question) { + + Question createdTodo = surveyService.addQuestion(surveyId, question); + + if (createdTodo == null) { + return ResponseEntity.noContent().build(); + } + + URI location = ServletUriComponentsBuilder.fromCurrentRequest() + .path("/{id}").buildAndExpand(createdTodo.getId()).toUri(); + + return ResponseEntity.created(location).build(); + + } +``` +Second Snippet +``` +{"description":"Second Most Populous Country in the World","correctAnswer":"India","options":["India","Russia","United States","China"]} +``` + +## Exercises +- Create more REST services of your choice + +## Files List +### /pom.xml +``` + + 4.0.0 + com.in28minutes + springboot-for-beginners-example + 0.0.1-SNAPSHOT + Your First Spring Boot Example + jar + + + org.springframework.boot + spring-boot-starter-parent + 1.4.0.RELEASE + + + + + org.springframework.boot + spring-boot-starter-web + + + + org.springframework.boot + spring-boot-devtools + true + + + + + + + 1.8 + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + +``` +### /src/main/java/com/in28minutes/springboot/Application.java +``` +package com.in28minutes.springboot; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.ApplicationContext; +import org.springframework.stereotype.Component; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@SpringBootApplication +public class Application { + + public static void main(String[] args) { + ApplicationContext ctx = SpringApplication.run(Application.class, args); + + } + + @RestController + class SomeBean { + + @Autowired + private SomeDependency someDependency; + + @RequestMapping("/") + public String index() { + return someDependency.getSomething(); + } + + } + + @Component + class SomeDependency { + + public String getSomething() { + return "Hello! Welcome!"; + } + + } + +} +``` +### /src/main/java/com/in28minutes/springboot/controller/SurveyController.java +``` +package com.in28minutes.springboot.controller; + +import java.net.URI; +import java.util.List; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.servlet.support.ServletUriComponentsBuilder; + +import com.in28minutes.springboot.model.Question; +import com.in28minutes.springboot.service.SurveyService; + +@RestController +class SurveyController { + @Autowired + private SurveyService surveyService; + + @GetMapping("/surveys/{surveyId}/questions") + public List retrieveQuestions(@PathVariable String surveyId) { + return surveyService.retrieveQuestions(surveyId); + } + + @GetMapping(path = "/surveys/{surveyId}/questions/{questionId}") + public Question retrieveQuestion(@PathVariable String surveyId, + @PathVariable String questionId) { + return surveyService.retrieveQuestion(surveyId, questionId); + } + + @PostMapping("/surveys/{surveyId}/questions") + ResponseEntity add(@PathVariable String surveyId, + @RequestBody Question question) { + + Question createdTodo = surveyService.addQuestion(surveyId, question); + + if (createdTodo == null) { + return ResponseEntity.noContent().build(); + } + + URI location = ServletUriComponentsBuilder.fromCurrentRequest() + .path("/{id}").buildAndExpand(createdTodo.getId()).toUri(); + + return ResponseEntity.created(location).build(); + + } + +} +``` +### /src/main/java/com/in28minutes/springboot/model/Question.java +``` +package com.in28minutes.springboot.model; + +import java.util.List; + +public class Question { + private String id; + private String description; + private String correctAnswer; + private List options; + + // Needed by Caused by: com.fasterxml.jackson.databind.JsonMappingException: + // Can not construct instance of com.in28minutes.springboot.model.Question: + // no suitable constructor found, can not deserialize from Object value + // (missing default constructor or creator, or perhaps need to add/enable + // type information?) + public Question() { + + } + + public Question(String id, String description, String correctAnswer, + List options) { + super(); + this.id = id; + this.description = description; + this.correctAnswer = correctAnswer; + this.options = options; + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getDescription() { + return description; + } + + public String getCorrectAnswer() { + return correctAnswer; + } + + public List getOptions() { + return options; + } + + @Override + public String toString() { + return String + .format("Question [id=%s, description=%s, correctAnswer=%s, options=%s]", + id, description, correctAnswer, options); + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + (id == null ? 0 : id.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + Question other = (Question) obj; + if (id == null) { + if (other.id != null) { + return false; + } + } else if (!id.equals(other.id)) { + return false; + } + return true; + } + +} +``` +### /src/main/java/com/in28minutes/springboot/model/Survey.java +``` +package com.in28minutes.springboot.model; + +import java.util.List; + +public class Survey { + private String id; + private String title; + private String description; + private List questions; + + public Survey(String id, String title, String description, + List questions) { + super(); + this.id = id; + this.title = title; + this.description = description; + this.questions = questions; + } + + public String getId() { + return id; + } + + public String getTitle() { + return title; + } + + public String getDescription() { + return description; + } + + public List getQuestions() { + return questions; + } + + @Override + public String toString() { + return String.format( + "Survey [id=%s, title=%s, description=%s, questions=%s]", id, + title, description, questions); + } + +} +``` +### /src/main/java/com/in28minutes/springboot/service/SurveyService.java +``` +package com.in28minutes.springboot.service; + +import java.math.BigInteger; +import java.security.SecureRandom; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import org.springframework.stereotype.Component; + +import com.in28minutes.springboot.model.Question; +import com.in28minutes.springboot.model.Survey; + +@Component +public class SurveyService { + private static List surveys = new ArrayList<>(); + static { + Question question1 = new Question("Question1", + "Largest Country in the World", "Russia", Arrays.asList( + "India", "Russia", "United States", "China")); + Question question2 = new Question("Question2", + "Most Populus Country in the World", "China", Arrays.asList( + "India", "Russia", "United States", "China")); + Question question3 = new Question("Question3", + "Highest GDP in the World", "United States", Arrays.asList( + "India", "Russia", "United States", "China")); + Question question4 = new Question("Question4", + "Second largest english speaking country", "India", + Arrays.asList("India", "Russia", "United States", "China")); + + List questions = new ArrayList<>(Arrays.asList(question1, + question2, question3, question4)); + + Survey survey = new Survey("Survey1", "My Favorite Survey", + "Description of the Survey", questions); + + surveys.add(survey); + } + + public List retrieveAllSurveys() { + return surveys; + } + + public Survey retrieveSurvey(String surveyId) { + for (Survey survey : surveys) { + if (survey.getId().equals(surveyId)) { + return survey; + } + } + return null; + } + + public List retrieveQuestions(String surveyId) { + Survey survey = retrieveSurvey(surveyId); + + if (survey == null) { + return null; + } + + return survey.getQuestions(); + } + + public Question retrieveQuestion(String surveyId, String questionId) { + Survey survey = retrieveSurvey(surveyId); + + if (survey == null) { + return null; + } + + for (Question question : survey.getQuestions()) { + if (question.getId().equals(questionId)) { + return question; + } + } + + return null; + } + + private SecureRandom random = new SecureRandom(); + + public Question addQuestion(String surveyId, Question question) { + Survey survey = retrieveSurvey(surveyId); + + if (survey == null) { + return null; + } + + String randomId = new BigInteger(130, random).toString(32); + question.setId(randomId); + + survey.getQuestions().add(question); + + return question; + } +} +``` +### /src/main/resources/application.properties +``` +logging.level.org.springframework: DEBUG +``` diff --git a/Step10.zip b/Step10.zip new file mode 100644 index 0000000..741dc8b Binary files /dev/null and b/Step10.zip differ diff --git a/Step11.md b/Step11.md new file mode 100644 index 0000000..c644219 --- /dev/null +++ b/Step11.md @@ -0,0 +1,456 @@ +##What You Will Learn during this Step: +- Understand Content Negotiation + - Accept:application/xml +- http://localhost:8080/surveys/Survey1/questions/ + +## Useful Snippets and References +First Snippet +``` + + com.fasterxml.jackson.dataformat + jackson-dataformat-xml + + +``` +Second Snippet +``` + + + Question1 + Largest Country in the World + Russia + + India + Russia + United States + China + + + + Question2 + Most Populus Country in the World + China + + India + Russia + United States + China + + + + Question3 + Highest GDP in the World + United States + + India + Russia + United States + China + + + + Question4 + Second largest english speaking country + India + + India + Russia + United States + China + + + +``` + + + +## Exercises +- Execute other services using xml and see what happens! + +## Files List +### /pom.xml +``` + + 4.0.0 + com.in28minutes + springboot-for-beginners-example + 0.0.1-SNAPSHOT + Your First Spring Boot Example + jar + + + org.springframework.boot + spring-boot-starter-parent + 1.4.0.RELEASE + + + + + org.springframework.boot + spring-boot-starter-web + + + + com.fasterxml.jackson.dataformat + jackson-dataformat-xml + + + + org.springframework.boot + spring-boot-devtools + true + + + + + + + 1.8 + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + +``` +### /src/main/java/com/in28minutes/springboot/Application.java +``` +package com.in28minutes.springboot; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.ApplicationContext; +import org.springframework.stereotype.Component; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@SpringBootApplication +public class Application { + + public static void main(String[] args) { + ApplicationContext ctx = SpringApplication.run(Application.class, args); + + } + + @RestController + class SomeBean { + + @Autowired + private SomeDependency someDependency; + + @RequestMapping("/") + public String index() { + return someDependency.getSomething(); + } + + } + + @Component + class SomeDependency { + + public String getSomething() { + return "Hello! Welcome!"; + } + + } + +} +``` +### /src/main/java/com/in28minutes/springboot/controller/SurveyController.java +``` +package com.in28minutes.springboot.controller; + +import java.net.URI; +import java.util.List; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.servlet.support.ServletUriComponentsBuilder; + +import com.in28minutes.springboot.model.Question; +import com.in28minutes.springboot.service.SurveyService; + +@RestController +class SurveyController { + @Autowired + private SurveyService surveyService; + + @GetMapping("/surveys/{surveyId}/questions") + public List retrieveQuestions(@PathVariable String surveyId) { + return surveyService.retrieveQuestions(surveyId); + } + + @GetMapping(path = "/surveys/{surveyId}/questions/{questionId}") + public Question retrieveQuestion(@PathVariable String surveyId, + @PathVariable String questionId) { + return surveyService.retrieveQuestion(surveyId, questionId); + } + + @PostMapping("/surveys/{surveyId}/questions") + ResponseEntity add(@PathVariable String surveyId, + @RequestBody Question question) { + + Question createdTodo = surveyService.addQuestion(surveyId, question); + + if (createdTodo == null) { + return ResponseEntity.noContent().build(); + } + + URI location = ServletUriComponentsBuilder.fromCurrentRequest() + .path("/{id}").buildAndExpand(createdTodo.getId()).toUri(); + + return ResponseEntity.created(location).build(); + + } + +} +``` +### /src/main/java/com/in28minutes/springboot/model/Question.java +``` +package com.in28minutes.springboot.model; + +import java.util.List; + +public class Question { + private String id; + private String description; + private String correctAnswer; + private List options; + + // Needed by Caused by: com.fasterxml.jackson.databind.JsonMappingException: + // Can not construct instance of com.in28minutes.springboot.model.Question: + // no suitable constructor found, can not deserialize from Object value + // (missing default constructor or creator, or perhaps need to add/enable + // type information?) + public Question() { + + } + + public Question(String id, String description, String correctAnswer, + List options) { + super(); + this.id = id; + this.description = description; + this.correctAnswer = correctAnswer; + this.options = options; + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getDescription() { + return description; + } + + public String getCorrectAnswer() { + return correctAnswer; + } + + public List getOptions() { + return options; + } + + @Override + public String toString() { + return String + .format("Question [id=%s, description=%s, correctAnswer=%s, options=%s]", + id, description, correctAnswer, options); + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + (id == null ? 0 : id.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + Question other = (Question) obj; + if (id == null) { + if (other.id != null) { + return false; + } + } else if (!id.equals(other.id)) { + return false; + } + return true; + } + +} +``` +### /src/main/java/com/in28minutes/springboot/model/Survey.java +``` +package com.in28minutes.springboot.model; + +import java.util.List; + +public class Survey { + private String id; + private String title; + private String description; + private List questions; + + public Survey(String id, String title, String description, + List questions) { + super(); + this.id = id; + this.title = title; + this.description = description; + this.questions = questions; + } + + public String getId() { + return id; + } + + public String getTitle() { + return title; + } + + public String getDescription() { + return description; + } + + public List getQuestions() { + return questions; + } + + @Override + public String toString() { + return String.format( + "Survey [id=%s, title=%s, description=%s, questions=%s]", id, + title, description, questions); + } + +} +``` +### /src/main/java/com/in28minutes/springboot/service/SurveyService.java +``` +package com.in28minutes.springboot.service; + +import java.math.BigInteger; +import java.security.SecureRandom; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import org.springframework.stereotype.Component; + +import com.in28minutes.springboot.model.Question; +import com.in28minutes.springboot.model.Survey; + +@Component +public class SurveyService { + private static List surveys = new ArrayList<>(); + static { + Question question1 = new Question("Question1", + "Largest Country in the World", "Russia", Arrays.asList( + "India", "Russia", "United States", "China")); + Question question2 = new Question("Question2", + "Most Populus Country in the World", "China", Arrays.asList( + "India", "Russia", "United States", "China")); + Question question3 = new Question("Question3", + "Highest GDP in the World", "United States", Arrays.asList( + "India", "Russia", "United States", "China")); + Question question4 = new Question("Question4", + "Second largest english speaking country", "India", + Arrays.asList("India", "Russia", "United States", "China")); + + List questions = new ArrayList<>(Arrays.asList(question1, + question2, question3, question4)); + + Survey survey = new Survey("Survey1", "My Favorite Survey", + "Description of the Survey", questions); + + surveys.add(survey); + } + + public List retrieveAllSurveys() { + return surveys; + } + + public Survey retrieveSurvey(String surveyId) { + for (Survey survey : surveys) { + if (survey.getId().equals(surveyId)) { + return survey; + } + } + return null; + } + + public List retrieveQuestions(String surveyId) { + Survey survey = retrieveSurvey(surveyId); + + if (survey == null) { + return null; + } + + return survey.getQuestions(); + } + + public Question retrieveQuestion(String surveyId, String questionId) { + Survey survey = retrieveSurvey(surveyId); + + if (survey == null) { + return null; + } + + for (Question question : survey.getQuestions()) { + if (question.getId().equals(questionId)) { + return question; + } + } + + return null; + } + + private SecureRandom random = new SecureRandom(); + + public Question addQuestion(String surveyId, Question question) { + Survey survey = retrieveSurvey(surveyId); + + if (survey == null) { + return null; + } + + String randomId = new BigInteger(130, random).toString(32); + question.setId(randomId); + + survey.getQuestions().add(question); + + return question; + } +} +``` +### /src/main/resources/application.properties +``` +logging.level.org.springframework: DEBUG +``` diff --git a/Step12.md b/Step12.md new file mode 100644 index 0000000..360b578 --- /dev/null +++ b/Step12.md @@ -0,0 +1,7 @@ +##What You Will Learn during this Step: +- Spring Initializr +- https://start.spring.io +- Create a few projects! + +## Exercises +- Create more projects with Spring Initializr and play around with it! diff --git a/Step13.md b/Step13.md new file mode 100644 index 0000000..f3366b0 --- /dev/null +++ b/Step13.md @@ -0,0 +1,22 @@ +##What You Will Learn during this Step: +- Spring Boot Actuator + - /env, /metrics, /trace, /dump, /shutdown, /beans, / autoconfig, /configprops, /mappings +- http://localhost:8080/actuator/ +- Execute individual REST Services for each of above + +## Useful Snippets and References +First Snippet +``` + + org.springframework.boot + spring-boot-starter-actuator + + + + org.springframework.data + spring-data-rest-hal-browser + + +``` + +## Files List diff --git a/pom.xml b/pom.xml index ee2ff20..3686c8e 100644 --- a/pom.xml +++ b/pom.xml @@ -18,8 +18,31 @@ org.springframework.boot spring-boot-starter-web + + + com.fasterxml.jackson.dataformat + jackson-dataformat-xml + + + + org.springframework.boot + spring-boot-starter-actuator + + + + org.springframework.data + spring-data-rest-hal-browser + + + + org.springframework.boot + spring-boot-devtools + true + + + 1.8 diff --git a/src/main/java/com/in28minutes/springboot/controller/SurveyController.java b/src/main/java/com/in28minutes/springboot/controller/SurveyController.java index abb9b05..4c98353 100644 --- a/src/main/java/com/in28minutes/springboot/controller/SurveyController.java +++ b/src/main/java/com/in28minutes/springboot/controller/SurveyController.java @@ -1,11 +1,16 @@ package com.in28minutes.springboot.controller; +import java.net.URI; import java.util.List; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.servlet.support.ServletUriComponentsBuilder; import com.in28minutes.springboot.model.Question; import com.in28minutes.springboot.service.SurveyService; @@ -26,4 +31,21 @@ public Question retrieveQuestion(@PathVariable String surveyId, return surveyService.retrieveQuestion(surveyId, questionId); } + @PostMapping("/surveys/{surveyId}/questions") + ResponseEntity add(@PathVariable String surveyId, + @RequestBody Question question) { + + Question createdTodo = surveyService.addQuestion(surveyId, question); + + if (createdTodo == null) { + return ResponseEntity.noContent().build(); + } + + URI location = ServletUriComponentsBuilder.fromCurrentRequest() + .path("/{id}").buildAndExpand(createdTodo.getId()).toUri(); + + return ResponseEntity.created(location).build(); + + } + }