From 47fbf7ad9541d6e3a29408c2279cb9c7b77539a4 Mon Sep 17 00:00:00 2001 From: Ranga Rao Karanam Date: Fri, 15 Dec 2017 11:50:11 +0530 Subject: [PATCH] I'm too lazy to put in a comment --- .../.gitignore | 24 + .../.mvn/wrapper/maven-wrapper.jar | Bin 0 -> 47610 bytes .../.mvn/wrapper/maven-wrapper.properties | 1 + .../code-21July2017.zip | Bin 0 -> 7267 bytes 01.Spring-Boot-Introduction-In-10-Steps/mvnw | 225 +++++ .../mvnw.cmd | 143 +++ .../notes.txt | 26 + .../pom.xml | 111 +++ .../readme.md | 277 ++++++ .../basics/springbootin10steps/Book.java | 32 + .../springbootin10steps/BooksController.java | 16 + .../SpringbootIn10StepsApplication.java | 19 + .../src/main/resources/application.properties | 2 + .../SpringbootIn10StepsApplicationTests.java | 16 + 02.Spring-Boot-Web-Application/.DS_Store | Bin 0 -> 6148 bytes 02.Spring-Boot-Web-Application/README.md | 133 +++ 02.Spring-Boot-Web-Application/Step01.md | 117 +++ 02.Spring-Boot-Web-Application/Step02.md | 165 ++++ 02.Spring-Boot-Web-Application/Step03.md | 6 + 02.Spring-Boot-Web-Application/Step04.md | 207 ++++ 02.Spring-Boot-Web-Application/Step05.md | 186 ++++ 02.Spring-Boot-Web-Application/Step06.md | 12 + 02.Spring-Boot-Web-Application/Step07.md | 226 +++++ 02.Spring-Boot-Web-Application/Step07.zip | Bin 0 -> 15427 bytes 02.Spring-Boot-Web-Application/Step08.md | 280 ++++++ 02.Spring-Boot-Web-Application/Step08.zip | Bin 0 -> 16275 bytes 02.Spring-Boot-Web-Application/Step09.md | 4 + 02.Spring-Boot-Web-Application/Step10.md | 161 +++ 02.Spring-Boot-Web-Application/Step11.md | 2 + 02.Spring-Boot-Web-Application/Step12.md | 474 +++++++++ 02.Spring-Boot-Web-Application/Step12.zip | Bin 0 -> 19103 bytes 02.Spring-Boot-Web-Application/Step13.md | 502 ++++++++++ 02.Spring-Boot-Web-Application/Step13.zip | Bin 0 -> 19565 bytes 02.Spring-Boot-Web-Application/Step14.md | 12 + 02.Spring-Boot-Web-Application/Step15.md | 566 +++++++++++ 02.Spring-Boot-Web-Application/Step15.zip | Bin 0 -> 19918 bytes 02.Spring-Boot-Web-Application/Step16.md | 555 +++++++++++ 02.Spring-Boot-Web-Application/Step16.zip | Bin 0 -> 19990 bytes 02.Spring-Boot-Web-Application/Step17.md | 576 +++++++++++ 02.Spring-Boot-Web-Application/Step18.md | 626 ++++++++++++ 02.Spring-Boot-Web-Application/Step18.zip | Bin 0 -> 20428 bytes 02.Spring-Boot-Web-Application/Step19.md | 665 +++++++++++++ 02.Spring-Boot-Web-Application/Step19.zip | Bin 0 -> 20595 bytes 02.Spring-Boot-Web-Application/Step20.md | 708 +++++++++++++ 02.Spring-Boot-Web-Application/Step20.zip | Bin 0 -> 20871 bytes 02.Spring-Boot-Web-Application/Step21.md | 702 +++++++++++++ 02.Spring-Boot-Web-Application/Step21.zip | Bin 0 -> 21926 bytes 02.Spring-Boot-Web-Application/Step22.md | 657 ++++++++++++ 02.Spring-Boot-Web-Application/Step22.zip | Bin 0 -> 21441 bytes 02.Spring-Boot-Web-Application/Step23.md | 739 ++++++++++++++ 02.Spring-Boot-Web-Application/Step23.zip | Bin 0 -> 22400 bytes 02.Spring-Boot-Web-Application/Step24.md | 34 + 02.Spring-Boot-Web-Application/Step25.md | 795 +++++++++++++++ 02.Spring-Boot-Web-Application/Step25.zip | Bin 0 -> 23765 bytes 02.Spring-Boot-Web-Application/Step26.md | 729 ++++++++++++++ 02.Spring-Boot-Web-Application/Step27.md | 15 + 02.Spring-Boot-Web-Application/Step27.zip | Bin 0 -> 14664 bytes .../StepReference.md | 16 + 02.Spring-Boot-Web-Application/pom.xml | 89 ++ 02.Spring-Boot-Web-Application/src/.DS_Store | Bin 0 -> 6148 bytes .../web/SpringBootFirstWebApplication.java | 14 + .../web/controller/ErrorController.java | 25 + .../web/controller/LogoutController.java | 33 + .../web/controller/TodoController.java | 106 ++ .../web/controller/WelcomeController.java | 30 + .../springboot/web/model/Todo.java | 104 ++ .../web/security/SecurityConfiguration.java | 25 + .../springboot/web/service/TodoService.java | 63 ++ .../src/main/resources/application.properties | 3 + .../webapp/WEB-INF/jsp/common/footer.jspf | 12 + .../webapp/WEB-INF/jsp/common/header.jspf | 14 + .../webapp/WEB-INF/jsp/common/navigation.jspf | 15 + .../src/main/webapp/WEB-INF/jsp/error.jsp | 6 + .../main/webapp/WEB-INF/jsp/list-todos.jsp | 34 + .../src/main/webapp/WEB-INF/jsp/todo.jsp | 23 + .../src/main/webapp/WEB-INF/jsp/welcome.jsp | 7 + .../SpringBootFirstWebApplicationTests.java | 16 + .../target/classes/META-INF/MANIFEST.MF | 11 + .../pom.properties | 7 + .../pom.xml | 89 ++ .../target/classes/application.properties | 3 + 03.JUnit-Introduction-In-5-Steps/.DS_Store | Bin 0 -> 6148 bytes 03.JUnit-Introduction-In-5-Steps/.classpath | 8 + 03.JUnit-Introduction-In-5-Steps/.gitignore | 1 + 03.JUnit-Introduction-In-5-Steps/.project | 17 + .../code-21July2017.zip | Bin 0 -> 7239 bytes 03.JUnit-Introduction-In-5-Steps/readme.md | 124 +++ .../src/.DS_Store | Bin 0 -> 6148 bytes .../src/com/.DS_Store | Bin 0 -> 6148 bytes .../src/com/in28minutes/.DS_Store | Bin 0 -> 6148 bytes .../src/com/in28minutes/junit/MyMath.java | 11 + 03.JUnit-Introduction-In-5-Steps/step22.md | 383 +++++++ 03.JUnit-Introduction-In-5-Steps/step22.zip | Bin 0 -> 7239 bytes 03.JUnit-Introduction-In-5-Steps/step25.md | 97 ++ 03.JUnit-Introduction-In-5-Steps/step25.zip | Bin 0 -> 7239 bytes .../test/.DS_Store | Bin 0 -> 6148 bytes .../com/in28minutes/junit/AssertTest.java | 18 + .../com/in28minutes/junit/MyMathTest.java | 47 + 04.Mockito-Introduction-In-5-Steps/.DS_Store | Bin 0 -> 6148 bytes 04.Mockito-Introduction-In-5-Steps/.gitignore | 24 + .../.mvn/wrapper/maven-wrapper.jar | Bin 0 -> 47610 bytes .../.mvn/wrapper/maven-wrapper.properties | 1 + .../code-21July2017.zip | Bin 0 -> 12110 bytes 04.Mockito-Introduction-In-5-Steps/mvnw | 225 +++++ 04.Mockito-Introduction-In-5-Steps/mvnw.cmd | 143 +++ 04.Mockito-Introduction-In-5-Steps/pom.xml | 88 ++ 04.Mockito-Introduction-In-5-Steps/readme.md | 366 +++++++ .../src/.DS_Store | Bin 0 -> 6148 bytes .../src/main/.DS_Store | Bin 0 -> 6148 bytes .../src/main/java/.DS_Store | Bin 0 -> 6148 bytes .../src/main/java/com/.DS_Store | Bin 0 -> 6148 bytes .../src/main/java/com/in28minutes/.DS_Store | Bin 0 -> 6148 bytes .../java/com/in28minutes/mockito/.DS_Store | Bin 0 -> 6148 bytes .../mockito/mockitodemo/DataService.java | 5 + .../mockitodemo/MockitoDemoApplication.java | 12 + .../mockito/mockitodemo/SomeBusinessImpl.java | 22 + .../src/main/resources/application.properties | 0 .../src/test/.DS_Store | Bin 0 -> 6148 bytes .../src/test/java/.DS_Store | Bin 0 -> 6148 bytes .../src/test/java/com/.DS_Store | Bin 0 -> 6148 bytes .../src/test/java/com/in28minutes/.DS_Store | Bin 0 -> 6148 bytes .../java/com/in28minutes/mockito/.DS_Store | Bin 0 -> 6148 bytes .../mockito/mockitodemo/ListTest.java | 46 + .../MockitoDemoApplicationTests.java | 16 + .../SomeBusinessMockAnnotationsTest.java | 38 + .../mockitodemo/SomeBusinessMockTest.java | 29 + .../mockitodemo/SomeBusinessStubTest.java | 23 + 05.Spring-Boot-Advanced/.DS_Store | Bin 0 -> 6148 bytes 05.Spring-Boot-Advanced/.classpath | 36 + 05.Spring-Boot-Advanced/.project | 35 + 05.Spring-Boot-Advanced/.settings/.DS_Store | Bin 0 -> 6148 bytes 05.Spring-Boot-Advanced/.settings/.jsdtscope | 13 + .../org.eclipse.core.resources.prefs | 6 + .../.settings/org.eclipse.jdt.core.prefs | 8 + .../.settings/org.eclipse.m2e.core.prefs | 4 + .../org.eclipse.wst.common.component | 10 + ....eclipse.wst.common.project.facet.core.xml | 8 + ...rg.eclipse.wst.jsdt.ui.superType.container | 1 + .../org.eclipse.wst.jsdt.ui.superType.name | 1 + .../org.eclipse.wst.validation.prefs | 2 + 05.Spring-Boot-Advanced/README.md | 143 +++ 05.Spring-Boot-Advanced/Step01.md | 74 ++ 05.Spring-Boot-Advanced/Step01.zip | Bin 0 -> 2337 bytes 05.Spring-Boot-Advanced/Step02.md | 134 +++ 05.Spring-Boot-Advanced/Step02.zip | Bin 0 -> 3433 bytes 05.Spring-Boot-Advanced/Step03.md | 117 +++ 05.Spring-Boot-Advanced/Step04.md | 28 + 05.Spring-Boot-Advanced/Step05.md | 48 + 05.Spring-Boot-Advanced/Step06.md | 344 +++++++ 05.Spring-Boot-Advanced/Step06.zip | Bin 0 -> 6363 bytes 05.Spring-Boot-Advanced/Step07.md | 407 ++++++++ 05.Spring-Boot-Advanced/Step08.md | 401 ++++++++ 05.Spring-Boot-Advanced/Step08.zip | Bin 0 -> 7219 bytes 05.Spring-Boot-Advanced/Step09.md | 24 + 05.Spring-Boot-Advanced/Step10.md | 442 +++++++++ 05.Spring-Boot-Advanced/Step10.zip | Bin 0 -> 7512 bytes 05.Spring-Boot-Advanced/Step11.md | 69 ++ 05.Spring-Boot-Advanced/Step12.md | 7 + 05.Spring-Boot-Advanced/Step13.md | 437 ++++++++ 05.Spring-Boot-Advanced/Step13.zip | Bin 0 -> 8837 bytes 05.Spring-Boot-Advanced/Step14.md | 456 +++++++++ 05.Spring-Boot-Advanced/Step14.zip | Bin 0 -> 7575 bytes 05.Spring-Boot-Advanced/Step15.md | 467 +++++++++ 05.Spring-Boot-Advanced/Step15.zip | Bin 0 -> 7986 bytes 05.Spring-Boot-Advanced/Step16.md | 460 +++++++++ 05.Spring-Boot-Advanced/Step16.zip | Bin 0 -> 8300 bytes 05.Spring-Boot-Advanced/Step17.md | 607 ++++++++++++ 05.Spring-Boot-Advanced/Step17.zip | Bin 0 -> 9213 bytes 05.Spring-Boot-Advanced/Step18.md | 738 ++++++++++++++ 05.Spring-Boot-Advanced/Step18.zip | Bin 0 -> 11011 bytes 05.Spring-Boot-Advanced/Step19.md | 655 ++++++++++++ 05.Spring-Boot-Advanced/Step19.zip | Bin 0 -> 12495 bytes 05.Spring-Boot-Advanced/Step20.md | 686 +++++++++++++ 05.Spring-Boot-Advanced/Step20.zip | Bin 0 -> 11523 bytes 05.Spring-Boot-Advanced/Step21.md | 821 +++++++++++++++ 05.Spring-Boot-Advanced/Step21.zip | Bin 0 -> 13183 bytes 05.Spring-Boot-Advanced/Step22.md | 812 +++++++++++++++ 05.Spring-Boot-Advanced/Step22.zip | Bin 0 -> 13508 bytes 05.Spring-Boot-Advanced/Step23.md | 741 ++++++++++++++ 05.Spring-Boot-Advanced/Step23.zip | Bin 0 -> 13531 bytes 05.Spring-Boot-Advanced/Step24.md | 867 ++++++++++++++++ 05.Spring-Boot-Advanced/Step25.md | 889 +++++++++++++++++ 05.Spring-Boot-Advanced/Step25.zip | Bin 0 -> 14852 bytes 05.Spring-Boot-Advanced/Step26.md | 30 + 05.Spring-Boot-Advanced/Step27.md | 933 ++++++++++++++++++ 05.Spring-Boot-Advanced/Step27.zip | Bin 0 -> 15983 bytes 05.Spring-Boot-Advanced/Step28.md | 899 +++++++++++++++++ 05.Spring-Boot-Advanced/Step28.zip | Bin 0 -> 15983 bytes 05.Spring-Boot-Advanced/StepReference.md | 16 + 05.Spring-Boot-Advanced/pom.xml | 74 ++ .../in28minutes/springboot/Application.java | 22 + .../springboot/WelcomeController.java | 37 + .../springboot/WelcomeService.java | 17 + .../configuration/BasicConfiguration.java | 36 + .../controller/SurveyController.java | 56 ++ .../com/in28minutes/springboot/jpa/User.java | 44 + .../springboot/jpa/UserCommandLineRunner.java | 38 + .../springboot/jpa/UserRepository.java | 9 + .../springboot/jpa/UserRestRepository.java | 13 + .../springboot/model/Question.java | 81 ++ .../in28minutes/springboot/model/Survey.java | 58 ++ .../springboot/security/SecurityConfig.java | 27 + .../springboot/service/SurveyService.java | 95 ++ .../main/resources/application-dev.properties | 1 + .../resources/application-prod.properties | 1 + .../src/main/resources/application.properties | 7 + .../controller/SurveyControllerIT.java | 113 +++ .../controller/SurveyControllerTest.java | 88 ++ .../target/classes/META-INF/MANIFEST.MF | 11 + .../first-springboot-project/pom.properties | 7 + .../first-springboot-project/pom.xml | 74 ++ .../target/classes/application-dev.properties | 1 + .../classes/application-prod.properties | 1 + .../target/classes/application.properties | 7 + .../web-resources/META-INF/MANIFEST.MF | 11 + .../pom.properties | 7 + .../springboot-for-beginners-example/pom.xml | 35 + .../compile/default-compile/createdFiles.lst | 0 .../compile/default-compile/inputFiles.lst | 12 + .../default-testCompile/createdFiles.lst | 0 .../default-testCompile/inputFiles.lst | 2 + ...ngboot.controller.SurveyControllerTest.xml | 69 ++ ...ngboot.controller.SurveyControllerTest.txt | 4 + 06.JPA-Introduction-In-10-Steps/.DS_Store | Bin 0 -> 6148 bytes 06.JPA-Introduction-In-10-Steps/.gitignore | 24 + .../.mvn/.DS_Store | Bin 0 -> 6148 bytes .../.mvn/wrapper/maven-wrapper.jar | Bin 0 -> 47610 bytes .../.mvn/wrapper/maven-wrapper.properties | 1 + .../code-21July2017.zip | Bin 0 -> 11884 bytes .../jpa-in-10-steps-all-code.md | 371 +++++++ 06.JPA-Introduction-In-10-Steps/mvnw | 225 +++++ 06.JPA-Introduction-In-10-Steps/mvnw.cmd | 143 +++ 06.JPA-Introduction-In-10-Steps/notes.txt | 32 + 06.JPA-Introduction-In-10-Steps/pom.xml | 100 ++ 06.JPA-Introduction-In-10-Steps/readme.md | 407 ++++++++ 06.JPA-Introduction-In-10-Steps/src/.DS_Store | Bin 0 -> 6148 bytes .../src/main/.DS_Store | Bin 0 -> 6148 bytes .../jpain10steps/JpaIn10StepsApplication.java | 12 + .../UserDaoServiceCommandLineRunner.java | 28 + .../UserRepositoryCommandLineRunner.java | 37 + .../jpa/jpain10steps/entity/User.java | 45 + .../jpain10steps/service/UserDAOService.java | 29 + .../jpain10steps/service/UserRepository.java | 9 + .../src/main/resources/.DS_Store | Bin 0 -> 6148 bytes .../src/main/resources/application.properties | 3 + .../JpaIn10StepsApplicationTests.java | 16 + .../step-completed.sh | 3 + 06.JPA-Introduction-In-10-Steps/step21.md | 766 ++++++++++++++ 06.JPA-Introduction-In-10-Steps/step21.zip | Bin 0 -> 10214 bytes 06.JPA-Introduction-In-10-Steps/step22.md | 383 +++++++ 06.JPA-Introduction-In-10-Steps/step22.zip | Bin 0 -> 10213 bytes 06.JPA-Introduction-In-10-Steps/step23.md | 383 +++++++ 06.JPA-Introduction-In-10-Steps/step23.zip | Bin 0 -> 10653 bytes .../take-step-backup.sh | 2 + 09.Spring-Introduction-In-10-Steps/.gitignore | 24 + .../.mvn/wrapper/maven-wrapper.jar | Bin 0 -> 47610 bytes .../.mvn/wrapper/maven-wrapper.properties | 1 + .../code-21July2017.zip | Bin 0 -> 7634 bytes 09.Spring-Introduction-In-10-Steps/mvnw | 225 +++++ 09.Spring-Introduction-In-10-Steps/mvnw.cmd | 143 +++ 09.Spring-Introduction-In-10-Steps/pom.xml | 88 ++ 09.Spring-Introduction-In-10-Steps/readme.md | 265 +++++ .../springin5steps/BinarySearchImpl.java | 20 + .../springin5steps/BubbleSortAlgorithm.java | 13 + .../springin5steps/QuickSortAlgorithm.java | 11 + .../basics/springin5steps/SortAlgorithm.java | 5 + .../SpringIn5StepsApplication.java | 27 + .../src/main/resources/application.properties | 1 + .../src/main/resources/log.txt | 15 + .../SpringIn5StepsApplicationTests.java | 16 + README.md | 2 +- Step24.md | 2 +- 272 files changed, 31044 insertions(+), 2 deletions(-) create mode 100644 01.Spring-Boot-Introduction-In-10-Steps/.gitignore create mode 100644 01.Spring-Boot-Introduction-In-10-Steps/.mvn/wrapper/maven-wrapper.jar create mode 100644 01.Spring-Boot-Introduction-In-10-Steps/.mvn/wrapper/maven-wrapper.properties create mode 100644 01.Spring-Boot-Introduction-In-10-Steps/code-21July2017.zip create mode 100755 01.Spring-Boot-Introduction-In-10-Steps/mvnw create mode 100644 01.Spring-Boot-Introduction-In-10-Steps/mvnw.cmd create mode 100644 01.Spring-Boot-Introduction-In-10-Steps/notes.txt create mode 100644 01.Spring-Boot-Introduction-In-10-Steps/pom.xml create mode 100644 01.Spring-Boot-Introduction-In-10-Steps/readme.md create mode 100644 01.Spring-Boot-Introduction-In-10-Steps/src/main/java/com/in28minutes/springboot/basics/springbootin10steps/Book.java create mode 100644 01.Spring-Boot-Introduction-In-10-Steps/src/main/java/com/in28minutes/springboot/basics/springbootin10steps/BooksController.java create mode 100644 01.Spring-Boot-Introduction-In-10-Steps/src/main/java/com/in28minutes/springboot/basics/springbootin10steps/SpringbootIn10StepsApplication.java create mode 100644 01.Spring-Boot-Introduction-In-10-Steps/src/main/resources/application.properties create mode 100644 01.Spring-Boot-Introduction-In-10-Steps/src/test/java/com/in28minutes/springboot/basics/springbootin10steps/SpringbootIn10StepsApplicationTests.java create mode 100644 02.Spring-Boot-Web-Application/.DS_Store create mode 100644 02.Spring-Boot-Web-Application/README.md create mode 100644 02.Spring-Boot-Web-Application/Step01.md create mode 100644 02.Spring-Boot-Web-Application/Step02.md create mode 100644 02.Spring-Boot-Web-Application/Step03.md create mode 100644 02.Spring-Boot-Web-Application/Step04.md create mode 100644 02.Spring-Boot-Web-Application/Step05.md create mode 100644 02.Spring-Boot-Web-Application/Step06.md create mode 100644 02.Spring-Boot-Web-Application/Step07.md create mode 100644 02.Spring-Boot-Web-Application/Step07.zip create mode 100644 02.Spring-Boot-Web-Application/Step08.md create mode 100644 02.Spring-Boot-Web-Application/Step08.zip create mode 100644 02.Spring-Boot-Web-Application/Step09.md create mode 100644 02.Spring-Boot-Web-Application/Step10.md create mode 100644 02.Spring-Boot-Web-Application/Step11.md create mode 100644 02.Spring-Boot-Web-Application/Step12.md create mode 100644 02.Spring-Boot-Web-Application/Step12.zip create mode 100644 02.Spring-Boot-Web-Application/Step13.md create mode 100644 02.Spring-Boot-Web-Application/Step13.zip create mode 100644 02.Spring-Boot-Web-Application/Step14.md create mode 100644 02.Spring-Boot-Web-Application/Step15.md create mode 100644 02.Spring-Boot-Web-Application/Step15.zip create mode 100644 02.Spring-Boot-Web-Application/Step16.md create mode 100644 02.Spring-Boot-Web-Application/Step16.zip create mode 100644 02.Spring-Boot-Web-Application/Step17.md create mode 100644 02.Spring-Boot-Web-Application/Step18.md create mode 100644 02.Spring-Boot-Web-Application/Step18.zip create mode 100644 02.Spring-Boot-Web-Application/Step19.md create mode 100644 02.Spring-Boot-Web-Application/Step19.zip create mode 100644 02.Spring-Boot-Web-Application/Step20.md create mode 100644 02.Spring-Boot-Web-Application/Step20.zip create mode 100644 02.Spring-Boot-Web-Application/Step21.md create mode 100644 02.Spring-Boot-Web-Application/Step21.zip create mode 100644 02.Spring-Boot-Web-Application/Step22.md create mode 100644 02.Spring-Boot-Web-Application/Step22.zip create mode 100644 02.Spring-Boot-Web-Application/Step23.md create mode 100644 02.Spring-Boot-Web-Application/Step23.zip create mode 100644 02.Spring-Boot-Web-Application/Step24.md create mode 100644 02.Spring-Boot-Web-Application/Step25.md create mode 100644 02.Spring-Boot-Web-Application/Step25.zip create mode 100644 02.Spring-Boot-Web-Application/Step26.md create mode 100644 02.Spring-Boot-Web-Application/Step27.md create mode 100644 02.Spring-Boot-Web-Application/Step27.zip create mode 100644 02.Spring-Boot-Web-Application/StepReference.md create mode 100644 02.Spring-Boot-Web-Application/pom.xml create mode 100644 02.Spring-Boot-Web-Application/src/.DS_Store create mode 100644 02.Spring-Boot-Web-Application/src/main/java/com/in28minutes/springboot/web/SpringBootFirstWebApplication.java create mode 100644 02.Spring-Boot-Web-Application/src/main/java/com/in28minutes/springboot/web/controller/ErrorController.java create mode 100644 02.Spring-Boot-Web-Application/src/main/java/com/in28minutes/springboot/web/controller/LogoutController.java create mode 100644 02.Spring-Boot-Web-Application/src/main/java/com/in28minutes/springboot/web/controller/TodoController.java create mode 100644 02.Spring-Boot-Web-Application/src/main/java/com/in28minutes/springboot/web/controller/WelcomeController.java create mode 100644 02.Spring-Boot-Web-Application/src/main/java/com/in28minutes/springboot/web/model/Todo.java create mode 100644 02.Spring-Boot-Web-Application/src/main/java/com/in28minutes/springboot/web/security/SecurityConfiguration.java create mode 100644 02.Spring-Boot-Web-Application/src/main/java/com/in28minutes/springboot/web/service/TodoService.java create mode 100644 02.Spring-Boot-Web-Application/src/main/resources/application.properties create mode 100644 02.Spring-Boot-Web-Application/src/main/webapp/WEB-INF/jsp/common/footer.jspf create mode 100644 02.Spring-Boot-Web-Application/src/main/webapp/WEB-INF/jsp/common/header.jspf create mode 100644 02.Spring-Boot-Web-Application/src/main/webapp/WEB-INF/jsp/common/navigation.jspf create mode 100644 02.Spring-Boot-Web-Application/src/main/webapp/WEB-INF/jsp/error.jsp create mode 100644 02.Spring-Boot-Web-Application/src/main/webapp/WEB-INF/jsp/list-todos.jsp create mode 100644 02.Spring-Boot-Web-Application/src/main/webapp/WEB-INF/jsp/todo.jsp create mode 100644 02.Spring-Boot-Web-Application/src/main/webapp/WEB-INF/jsp/welcome.jsp create mode 100644 02.Spring-Boot-Web-Application/src/test/java/com/in28minutes/springboot/web/SpringBootFirstWebApplicationTests.java create mode 100644 02.Spring-Boot-Web-Application/target/classes/META-INF/MANIFEST.MF create mode 100644 02.Spring-Boot-Web-Application/target/classes/META-INF/maven/com.in28minutes.springboot.web/spring-boot-first-web-application-git/pom.properties create mode 100644 02.Spring-Boot-Web-Application/target/classes/META-INF/maven/com.in28minutes.springboot.web/spring-boot-first-web-application-git/pom.xml create mode 100644 02.Spring-Boot-Web-Application/target/classes/application.properties create mode 100644 03.JUnit-Introduction-In-5-Steps/.DS_Store create mode 100644 03.JUnit-Introduction-In-5-Steps/.classpath create mode 100644 03.JUnit-Introduction-In-5-Steps/.gitignore create mode 100644 03.JUnit-Introduction-In-5-Steps/.project create mode 100644 03.JUnit-Introduction-In-5-Steps/code-21July2017.zip create mode 100644 03.JUnit-Introduction-In-5-Steps/readme.md create mode 100644 03.JUnit-Introduction-In-5-Steps/src/.DS_Store create mode 100644 03.JUnit-Introduction-In-5-Steps/src/com/.DS_Store create mode 100644 03.JUnit-Introduction-In-5-Steps/src/com/in28minutes/.DS_Store create mode 100644 03.JUnit-Introduction-In-5-Steps/src/com/in28minutes/junit/MyMath.java create mode 100644 03.JUnit-Introduction-In-5-Steps/step22.md create mode 100644 03.JUnit-Introduction-In-5-Steps/step22.zip create mode 100644 03.JUnit-Introduction-In-5-Steps/step25.md create mode 100644 03.JUnit-Introduction-In-5-Steps/step25.zip create mode 100644 03.JUnit-Introduction-In-5-Steps/test/.DS_Store create mode 100644 03.JUnit-Introduction-In-5-Steps/test/com/in28minutes/junit/AssertTest.java create mode 100644 03.JUnit-Introduction-In-5-Steps/test/com/in28minutes/junit/MyMathTest.java create mode 100644 04.Mockito-Introduction-In-5-Steps/.DS_Store create mode 100644 04.Mockito-Introduction-In-5-Steps/.gitignore create mode 100644 04.Mockito-Introduction-In-5-Steps/.mvn/wrapper/maven-wrapper.jar create mode 100644 04.Mockito-Introduction-In-5-Steps/.mvn/wrapper/maven-wrapper.properties create mode 100644 04.Mockito-Introduction-In-5-Steps/code-21July2017.zip create mode 100755 04.Mockito-Introduction-In-5-Steps/mvnw create mode 100644 04.Mockito-Introduction-In-5-Steps/mvnw.cmd create mode 100644 04.Mockito-Introduction-In-5-Steps/pom.xml create mode 100644 04.Mockito-Introduction-In-5-Steps/readme.md create mode 100644 04.Mockito-Introduction-In-5-Steps/src/.DS_Store create mode 100644 04.Mockito-Introduction-In-5-Steps/src/main/.DS_Store create mode 100644 04.Mockito-Introduction-In-5-Steps/src/main/java/.DS_Store create mode 100644 04.Mockito-Introduction-In-5-Steps/src/main/java/com/.DS_Store create mode 100644 04.Mockito-Introduction-In-5-Steps/src/main/java/com/in28minutes/.DS_Store create mode 100644 04.Mockito-Introduction-In-5-Steps/src/main/java/com/in28minutes/mockito/.DS_Store create mode 100644 04.Mockito-Introduction-In-5-Steps/src/main/java/com/in28minutes/mockito/mockitodemo/DataService.java create mode 100644 04.Mockito-Introduction-In-5-Steps/src/main/java/com/in28minutes/mockito/mockitodemo/MockitoDemoApplication.java create mode 100644 04.Mockito-Introduction-In-5-Steps/src/main/java/com/in28minutes/mockito/mockitodemo/SomeBusinessImpl.java create mode 100644 04.Mockito-Introduction-In-5-Steps/src/main/resources/application.properties create mode 100644 04.Mockito-Introduction-In-5-Steps/src/test/.DS_Store create mode 100644 04.Mockito-Introduction-In-5-Steps/src/test/java/.DS_Store create mode 100644 04.Mockito-Introduction-In-5-Steps/src/test/java/com/.DS_Store create mode 100644 04.Mockito-Introduction-In-5-Steps/src/test/java/com/in28minutes/.DS_Store create mode 100644 04.Mockito-Introduction-In-5-Steps/src/test/java/com/in28minutes/mockito/.DS_Store create mode 100644 04.Mockito-Introduction-In-5-Steps/src/test/java/com/in28minutes/mockito/mockitodemo/ListTest.java create mode 100644 04.Mockito-Introduction-In-5-Steps/src/test/java/com/in28minutes/mockito/mockitodemo/MockitoDemoApplicationTests.java create mode 100644 04.Mockito-Introduction-In-5-Steps/src/test/java/com/in28minutes/mockito/mockitodemo/SomeBusinessMockAnnotationsTest.java create mode 100644 04.Mockito-Introduction-In-5-Steps/src/test/java/com/in28minutes/mockito/mockitodemo/SomeBusinessMockTest.java create mode 100644 04.Mockito-Introduction-In-5-Steps/src/test/java/com/in28minutes/mockito/mockitodemo/SomeBusinessStubTest.java create mode 100644 05.Spring-Boot-Advanced/.DS_Store create mode 100644 05.Spring-Boot-Advanced/.classpath create mode 100644 05.Spring-Boot-Advanced/.project create mode 100644 05.Spring-Boot-Advanced/.settings/.DS_Store create mode 100644 05.Spring-Boot-Advanced/.settings/.jsdtscope create mode 100644 05.Spring-Boot-Advanced/.settings/org.eclipse.core.resources.prefs create mode 100644 05.Spring-Boot-Advanced/.settings/org.eclipse.jdt.core.prefs create mode 100644 05.Spring-Boot-Advanced/.settings/org.eclipse.m2e.core.prefs create mode 100644 05.Spring-Boot-Advanced/.settings/org.eclipse.wst.common.component create mode 100644 05.Spring-Boot-Advanced/.settings/org.eclipse.wst.common.project.facet.core.xml create mode 100644 05.Spring-Boot-Advanced/.settings/org.eclipse.wst.jsdt.ui.superType.container create mode 100644 05.Spring-Boot-Advanced/.settings/org.eclipse.wst.jsdt.ui.superType.name create mode 100644 05.Spring-Boot-Advanced/.settings/org.eclipse.wst.validation.prefs create mode 100644 05.Spring-Boot-Advanced/README.md create mode 100644 05.Spring-Boot-Advanced/Step01.md create mode 100644 05.Spring-Boot-Advanced/Step01.zip create mode 100644 05.Spring-Boot-Advanced/Step02.md create mode 100644 05.Spring-Boot-Advanced/Step02.zip create mode 100644 05.Spring-Boot-Advanced/Step03.md create mode 100644 05.Spring-Boot-Advanced/Step04.md create mode 100644 05.Spring-Boot-Advanced/Step05.md create mode 100644 05.Spring-Boot-Advanced/Step06.md create mode 100644 05.Spring-Boot-Advanced/Step06.zip create mode 100644 05.Spring-Boot-Advanced/Step07.md create mode 100644 05.Spring-Boot-Advanced/Step08.md create mode 100644 05.Spring-Boot-Advanced/Step08.zip create mode 100644 05.Spring-Boot-Advanced/Step09.md create mode 100644 05.Spring-Boot-Advanced/Step10.md create mode 100644 05.Spring-Boot-Advanced/Step10.zip create mode 100644 05.Spring-Boot-Advanced/Step11.md create mode 100644 05.Spring-Boot-Advanced/Step12.md create mode 100644 05.Spring-Boot-Advanced/Step13.md create mode 100644 05.Spring-Boot-Advanced/Step13.zip create mode 100644 05.Spring-Boot-Advanced/Step14.md create mode 100644 05.Spring-Boot-Advanced/Step14.zip create mode 100644 05.Spring-Boot-Advanced/Step15.md create mode 100644 05.Spring-Boot-Advanced/Step15.zip create mode 100644 05.Spring-Boot-Advanced/Step16.md create mode 100644 05.Spring-Boot-Advanced/Step16.zip create mode 100644 05.Spring-Boot-Advanced/Step17.md create mode 100644 05.Spring-Boot-Advanced/Step17.zip create mode 100644 05.Spring-Boot-Advanced/Step18.md create mode 100644 05.Spring-Boot-Advanced/Step18.zip create mode 100644 05.Spring-Boot-Advanced/Step19.md create mode 100644 05.Spring-Boot-Advanced/Step19.zip create mode 100644 05.Spring-Boot-Advanced/Step20.md create mode 100644 05.Spring-Boot-Advanced/Step20.zip create mode 100644 05.Spring-Boot-Advanced/Step21.md create mode 100644 05.Spring-Boot-Advanced/Step21.zip create mode 100644 05.Spring-Boot-Advanced/Step22.md create mode 100644 05.Spring-Boot-Advanced/Step22.zip create mode 100644 05.Spring-Boot-Advanced/Step23.md create mode 100644 05.Spring-Boot-Advanced/Step23.zip create mode 100644 05.Spring-Boot-Advanced/Step24.md create mode 100644 05.Spring-Boot-Advanced/Step25.md create mode 100644 05.Spring-Boot-Advanced/Step25.zip create mode 100644 05.Spring-Boot-Advanced/Step26.md create mode 100644 05.Spring-Boot-Advanced/Step27.md create mode 100644 05.Spring-Boot-Advanced/Step27.zip create mode 100644 05.Spring-Boot-Advanced/Step28.md create mode 100644 05.Spring-Boot-Advanced/Step28.zip create mode 100644 05.Spring-Boot-Advanced/StepReference.md create mode 100644 05.Spring-Boot-Advanced/pom.xml create mode 100644 05.Spring-Boot-Advanced/src/main/java/com/in28minutes/springboot/Application.java create mode 100644 05.Spring-Boot-Advanced/src/main/java/com/in28minutes/springboot/WelcomeController.java create mode 100644 05.Spring-Boot-Advanced/src/main/java/com/in28minutes/springboot/WelcomeService.java create mode 100644 05.Spring-Boot-Advanced/src/main/java/com/in28minutes/springboot/configuration/BasicConfiguration.java create mode 100644 05.Spring-Boot-Advanced/src/main/java/com/in28minutes/springboot/controller/SurveyController.java create mode 100644 05.Spring-Boot-Advanced/src/main/java/com/in28minutes/springboot/jpa/User.java create mode 100644 05.Spring-Boot-Advanced/src/main/java/com/in28minutes/springboot/jpa/UserCommandLineRunner.java create mode 100644 05.Spring-Boot-Advanced/src/main/java/com/in28minutes/springboot/jpa/UserRepository.java create mode 100644 05.Spring-Boot-Advanced/src/main/java/com/in28minutes/springboot/jpa/UserRestRepository.java create mode 100644 05.Spring-Boot-Advanced/src/main/java/com/in28minutes/springboot/model/Question.java create mode 100644 05.Spring-Boot-Advanced/src/main/java/com/in28minutes/springboot/model/Survey.java create mode 100644 05.Spring-Boot-Advanced/src/main/java/com/in28minutes/springboot/security/SecurityConfig.java create mode 100644 05.Spring-Boot-Advanced/src/main/java/com/in28minutes/springboot/service/SurveyService.java create mode 100644 05.Spring-Boot-Advanced/src/main/resources/application-dev.properties create mode 100644 05.Spring-Boot-Advanced/src/main/resources/application-prod.properties create mode 100644 05.Spring-Boot-Advanced/src/main/resources/application.properties create mode 100644 05.Spring-Boot-Advanced/src/test/java/com/in28minutes/springboot/controller/SurveyControllerIT.java create mode 100644 05.Spring-Boot-Advanced/src/test/java/com/in28minutes/springboot/controller/SurveyControllerTest.java create mode 100644 05.Spring-Boot-Advanced/target/classes/META-INF/MANIFEST.MF create mode 100644 05.Spring-Boot-Advanced/target/classes/META-INF/maven/com.in28minutes.springboot/first-springboot-project/pom.properties create mode 100644 05.Spring-Boot-Advanced/target/classes/META-INF/maven/com.in28minutes.springboot/first-springboot-project/pom.xml create mode 100644 05.Spring-Boot-Advanced/target/classes/application-dev.properties create mode 100644 05.Spring-Boot-Advanced/target/classes/application-prod.properties create mode 100644 05.Spring-Boot-Advanced/target/classes/application.properties create mode 100644 05.Spring-Boot-Advanced/target/m2e-wtp/web-resources/META-INF/MANIFEST.MF create mode 100644 05.Spring-Boot-Advanced/target/m2e-wtp/web-resources/META-INF/maven/com.in28minutes/springboot-for-beginners-example/pom.properties create mode 100644 05.Spring-Boot-Advanced/target/m2e-wtp/web-resources/META-INF/maven/com.in28minutes/springboot-for-beginners-example/pom.xml create mode 100644 05.Spring-Boot-Advanced/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst create mode 100644 05.Spring-Boot-Advanced/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst create mode 100644 05.Spring-Boot-Advanced/target/maven-status/maven-compiler-plugin/testCompile/default-testCompile/createdFiles.lst create mode 100644 05.Spring-Boot-Advanced/target/maven-status/maven-compiler-plugin/testCompile/default-testCompile/inputFiles.lst create mode 100644 05.Spring-Boot-Advanced/target/surefire-reports/TEST-com.in28minutes.springboot.controller.SurveyControllerTest.xml create mode 100644 05.Spring-Boot-Advanced/target/surefire-reports/com.in28minutes.springboot.controller.SurveyControllerTest.txt create mode 100644 06.JPA-Introduction-In-10-Steps/.DS_Store create mode 100644 06.JPA-Introduction-In-10-Steps/.gitignore create mode 100644 06.JPA-Introduction-In-10-Steps/.mvn/.DS_Store create mode 100644 06.JPA-Introduction-In-10-Steps/.mvn/wrapper/maven-wrapper.jar create mode 100644 06.JPA-Introduction-In-10-Steps/.mvn/wrapper/maven-wrapper.properties create mode 100644 06.JPA-Introduction-In-10-Steps/code-21July2017.zip create mode 100644 06.JPA-Introduction-In-10-Steps/jpa-in-10-steps-all-code.md create mode 100755 06.JPA-Introduction-In-10-Steps/mvnw create mode 100644 06.JPA-Introduction-In-10-Steps/mvnw.cmd create mode 100644 06.JPA-Introduction-In-10-Steps/notes.txt create mode 100644 06.JPA-Introduction-In-10-Steps/pom.xml create mode 100644 06.JPA-Introduction-In-10-Steps/readme.md create mode 100644 06.JPA-Introduction-In-10-Steps/src/.DS_Store create mode 100644 06.JPA-Introduction-In-10-Steps/src/main/.DS_Store create mode 100644 06.JPA-Introduction-In-10-Steps/src/main/java/com/in28minutes/learning/jpa/jpain10steps/JpaIn10StepsApplication.java create mode 100644 06.JPA-Introduction-In-10-Steps/src/main/java/com/in28minutes/learning/jpa/jpain10steps/UserDaoServiceCommandLineRunner.java create mode 100644 06.JPA-Introduction-In-10-Steps/src/main/java/com/in28minutes/learning/jpa/jpain10steps/UserRepositoryCommandLineRunner.java create mode 100644 06.JPA-Introduction-In-10-Steps/src/main/java/com/in28minutes/learning/jpa/jpain10steps/entity/User.java create mode 100644 06.JPA-Introduction-In-10-Steps/src/main/java/com/in28minutes/learning/jpa/jpain10steps/service/UserDAOService.java create mode 100644 06.JPA-Introduction-In-10-Steps/src/main/java/com/in28minutes/learning/jpa/jpain10steps/service/UserRepository.java create mode 100644 06.JPA-Introduction-In-10-Steps/src/main/resources/.DS_Store create mode 100644 06.JPA-Introduction-In-10-Steps/src/main/resources/application.properties create mode 100644 06.JPA-Introduction-In-10-Steps/src/test/java/com/in28minutes/learning/jpa/jpain10steps/JpaIn10StepsApplicationTests.java create mode 100755 06.JPA-Introduction-In-10-Steps/step-completed.sh create mode 100644 06.JPA-Introduction-In-10-Steps/step21.md create mode 100644 06.JPA-Introduction-In-10-Steps/step21.zip create mode 100644 06.JPA-Introduction-In-10-Steps/step22.md create mode 100644 06.JPA-Introduction-In-10-Steps/step22.zip create mode 100644 06.JPA-Introduction-In-10-Steps/step23.md create mode 100644 06.JPA-Introduction-In-10-Steps/step23.zip create mode 100755 06.JPA-Introduction-In-10-Steps/take-step-backup.sh create mode 100644 09.Spring-Introduction-In-10-Steps/.gitignore create mode 100644 09.Spring-Introduction-In-10-Steps/.mvn/wrapper/maven-wrapper.jar create mode 100644 09.Spring-Introduction-In-10-Steps/.mvn/wrapper/maven-wrapper.properties create mode 100644 09.Spring-Introduction-In-10-Steps/code-21July2017.zip create mode 100755 09.Spring-Introduction-In-10-Steps/mvnw create mode 100644 09.Spring-Introduction-In-10-Steps/mvnw.cmd create mode 100644 09.Spring-Introduction-In-10-Steps/pom.xml create mode 100644 09.Spring-Introduction-In-10-Steps/readme.md create mode 100644 09.Spring-Introduction-In-10-Steps/src/main/java/com/in28minutes/spring/basics/springin5steps/BinarySearchImpl.java create mode 100644 09.Spring-Introduction-In-10-Steps/src/main/java/com/in28minutes/spring/basics/springin5steps/BubbleSortAlgorithm.java create mode 100644 09.Spring-Introduction-In-10-Steps/src/main/java/com/in28minutes/spring/basics/springin5steps/QuickSortAlgorithm.java create mode 100644 09.Spring-Introduction-In-10-Steps/src/main/java/com/in28minutes/spring/basics/springin5steps/SortAlgorithm.java create mode 100644 09.Spring-Introduction-In-10-Steps/src/main/java/com/in28minutes/spring/basics/springin5steps/SpringIn5StepsApplication.java create mode 100644 09.Spring-Introduction-In-10-Steps/src/main/resources/application.properties create mode 100644 09.Spring-Introduction-In-10-Steps/src/main/resources/log.txt create mode 100644 09.Spring-Introduction-In-10-Steps/src/test/java/com/in28minutes/spring/basics/springin5steps/SpringIn5StepsApplicationTests.java diff --git a/01.Spring-Boot-Introduction-In-10-Steps/.gitignore b/01.Spring-Boot-Introduction-In-10-Steps/.gitignore new file mode 100644 index 0000000..2af7cef --- /dev/null +++ b/01.Spring-Boot-Introduction-In-10-Steps/.gitignore @@ -0,0 +1,24 @@ +target/ +!.mvn/wrapper/maven-wrapper.jar + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr + +### NetBeans ### +nbproject/private/ +build/ +nbbuild/ +dist/ +nbdist/ +.nb-gradle/ \ No newline at end of file diff --git a/01.Spring-Boot-Introduction-In-10-Steps/.mvn/wrapper/maven-wrapper.jar b/01.Spring-Boot-Introduction-In-10-Steps/.mvn/wrapper/maven-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..9cc84ea9b4d95453115d0c26488d6a78694e0bc6 GIT binary patch literal 47610 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 literal 0 HcmV?d00001 diff --git a/01.Spring-Boot-Introduction-In-10-Steps/.mvn/wrapper/maven-wrapper.properties b/01.Spring-Boot-Introduction-In-10-Steps/.mvn/wrapper/maven-wrapper.properties new file mode 100644 index 0000000..c315043 --- /dev/null +++ b/01.Spring-Boot-Introduction-In-10-Steps/.mvn/wrapper/maven-wrapper.properties @@ -0,0 +1 @@ +distributionUrl=https://repo1.maven.org/maven2/org/apache/maven/apache-maven/3.5.0/apache-maven-3.5.0-bin.zip diff --git a/01.Spring-Boot-Introduction-In-10-Steps/code-21July2017.zip b/01.Spring-Boot-Introduction-In-10-Steps/code-21July2017.zip new file mode 100644 index 0000000000000000000000000000000000000000..91201bc6c4e7d07af510b24bc60b13dbe2d117c0 GIT binary patch literal 7267 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 literal 0 HcmV?d00001 diff --git a/01.Spring-Boot-Introduction-In-10-Steps/mvnw b/01.Spring-Boot-Introduction-In-10-Steps/mvnw new file mode 100755 index 0000000..5bf251c --- /dev/null +++ b/01.Spring-Boot-Introduction-In-10-Steps/mvnw @@ -0,0 +1,225 @@ +#!/bin/sh +# ---------------------------------------------------------------------------- +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# ---------------------------------------------------------------------------- + +# ---------------------------------------------------------------------------- +# Maven2 Start Up Batch script +# +# Required ENV vars: +# ------------------ +# JAVA_HOME - location of a JDK home dir +# +# Optional ENV vars +# ----------------- +# M2_HOME - location of maven2's installed home dir +# MAVEN_OPTS - parameters passed to the Java VM when running Maven +# e.g. to debug Maven itself, use +# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +# MAVEN_SKIP_RC - flag to disable loading of mavenrc files +# ---------------------------------------------------------------------------- + +if [ -z "$MAVEN_SKIP_RC" ] ; then + + if [ -f /etc/mavenrc ] ; then + . /etc/mavenrc + fi + + if [ -f "$HOME/.mavenrc" ] ; then + . "$HOME/.mavenrc" + fi + +fi + +# OS specific support. $var _must_ be set to either true or false. +cygwin=false; +darwin=false; +mingw=false +case "`uname`" in + CYGWIN*) cygwin=true ;; + MINGW*) mingw=true;; + Darwin*) darwin=true + # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home + # See https://developer.apple.com/library/mac/qa/qa1170/_index.html + if [ -z "$JAVA_HOME" ]; then + if [ -x "/usr/libexec/java_home" ]; then + export JAVA_HOME="`/usr/libexec/java_home`" + else + export JAVA_HOME="/Library/Java/Home" + fi + fi + ;; +esac + +if [ -z "$JAVA_HOME" ] ; then + if [ -r /etc/gentoo-release ] ; then + JAVA_HOME=`java-config --jre-home` + fi +fi + +if [ -z "$M2_HOME" ] ; then + ## resolve links - $0 may be a link to maven's home + PRG="$0" + + # need this for relative symlinks + while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG="`dirname "$PRG"`/$link" + fi + done + + saveddir=`pwd` + + M2_HOME=`dirname "$PRG"`/.. + + # make it fully qualified + M2_HOME=`cd "$M2_HOME" && pwd` + + cd "$saveddir" + # echo Using m2 at $M2_HOME +fi + +# For Cygwin, ensure paths are in UNIX format before anything is touched +if $cygwin ; then + [ -n "$M2_HOME" ] && + M2_HOME=`cygpath --unix "$M2_HOME"` + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --unix "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --unix "$CLASSPATH"` +fi + +# For Migwn, ensure paths are in UNIX format before anything is touched +if $mingw ; then + [ -n "$M2_HOME" ] && + M2_HOME="`(cd "$M2_HOME"; pwd)`" + [ -n "$JAVA_HOME" ] && + JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" + # TODO classpath? +fi + +if [ -z "$JAVA_HOME" ]; then + javaExecutable="`which javac`" + if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then + # readlink(1) is not available as standard on Solaris 10. + readLink=`which readlink` + if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then + if $darwin ; then + javaHome="`dirname \"$javaExecutable\"`" + javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" + else + javaExecutable="`readlink -f \"$javaExecutable\"`" + fi + javaHome="`dirname \"$javaExecutable\"`" + javaHome=`expr "$javaHome" : '\(.*\)/bin'` + JAVA_HOME="$javaHome" + export JAVA_HOME + fi + fi +fi + +if [ -z "$JAVACMD" ] ; then + if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + else + JAVACMD="`which java`" + fi +fi + +if [ ! -x "$JAVACMD" ] ; then + echo "Error: JAVA_HOME is not defined correctly." >&2 + echo " We cannot execute $JAVACMD" >&2 + exit 1 +fi + +if [ -z "$JAVA_HOME" ] ; then + echo "Warning: JAVA_HOME environment variable is not set." +fi + +CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher + +# traverses directory structure from process work directory to filesystem root +# first directory with .mvn subdirectory is considered project base directory +find_maven_basedir() { + + if [ -z "$1" ] + then + echo "Path not specified to find_maven_basedir" + return 1 + fi + + basedir="$1" + wdir="$1" + while [ "$wdir" != '/' ] ; do + if [ -d "$wdir"/.mvn ] ; then + basedir=$wdir + break + fi + # workaround for JBEAP-8937 (on Solaris 10/Sparc) + if [ -d "${wdir}" ]; then + wdir=`cd "$wdir/.."; pwd` + fi + # end of workaround + done + echo "${basedir}" +} + +# concatenates all lines of a file +concat_lines() { + if [ -f "$1" ]; then + echo "$(tr -s '\n' ' ' < "$1")" + fi +} + +BASE_DIR=`find_maven_basedir "$(pwd)"` +if [ -z "$BASE_DIR" ]; then + exit 1; +fi + +export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} +echo $MAVEN_PROJECTBASEDIR +MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" + +# For Cygwin, switch paths to Windows format before running java +if $cygwin; then + [ -n "$M2_HOME" ] && + M2_HOME=`cygpath --path --windows "$M2_HOME"` + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --windows "$CLASSPATH"` + [ -n "$MAVEN_PROJECTBASEDIR" ] && + MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` +fi + +WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +exec "$JAVACMD" \ + $MAVEN_OPTS \ + -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ + "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ + ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" diff --git a/01.Spring-Boot-Introduction-In-10-Steps/mvnw.cmd b/01.Spring-Boot-Introduction-In-10-Steps/mvnw.cmd new file mode 100644 index 0000000..019bd74 --- /dev/null +++ b/01.Spring-Boot-Introduction-In-10-Steps/mvnw.cmd @@ -0,0 +1,143 @@ +@REM ---------------------------------------------------------------------------- +@REM Licensed to the Apache Software Foundation (ASF) under one +@REM or more contributor license agreements. See the NOTICE file +@REM distributed with this work for additional information +@REM regarding copyright ownership. The ASF licenses this file +@REM to you under the Apache License, Version 2.0 (the +@REM "License"); you may not use this file except in compliance +@REM with the License. You may obtain a copy of the License at +@REM +@REM http://www.apache.org/licenses/LICENSE-2.0 +@REM +@REM Unless required by applicable law or agreed to in writing, +@REM software distributed under the License is distributed on an +@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +@REM KIND, either express or implied. See the License for the +@REM specific language governing permissions and limitations +@REM under the License. +@REM ---------------------------------------------------------------------------- + +@REM ---------------------------------------------------------------------------- +@REM Maven2 Start Up Batch script +@REM +@REM Required ENV vars: +@REM JAVA_HOME - location of a JDK home dir +@REM +@REM Optional ENV vars +@REM M2_HOME - location of maven2's installed home dir +@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands +@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending +@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven +@REM e.g. to debug Maven itself, use +@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files +@REM ---------------------------------------------------------------------------- + +@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' +@echo off +@REM enable echoing my setting MAVEN_BATCH_ECHO to 'on' +@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% + +@REM set %HOME% to equivalent of $HOME +if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") + +@REM Execute a user defined script before this one +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre +@REM check for pre script, once with legacy .bat ending and once with .cmd ending +if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" +if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" +:skipRcPre + +@setlocal + +set ERROR_CODE=0 + +@REM To isolate internal variables from possible post scripts, we use another setlocal +@setlocal + +@REM ==== START VALIDATION ==== +if not "%JAVA_HOME%" == "" goto OkJHome + +echo. +echo Error: JAVA_HOME not found in your environment. >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +:OkJHome +if exist "%JAVA_HOME%\bin\java.exe" goto init + +echo. +echo Error: JAVA_HOME is set to an invalid directory. >&2 +echo JAVA_HOME = "%JAVA_HOME%" >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +@REM ==== END VALIDATION ==== + +:init + +@REM Find the project base dir, i.e. the directory that contains the folder ".mvn". +@REM Fallback to current working directory if not found. + +set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% +IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir + +set EXEC_DIR=%CD% +set WDIR=%EXEC_DIR% +:findBaseDir +IF EXIST "%WDIR%"\.mvn goto baseDirFound +cd .. +IF "%WDIR%"=="%CD%" goto baseDirNotFound +set WDIR=%CD% +goto findBaseDir + +:baseDirFound +set MAVEN_PROJECTBASEDIR=%WDIR% +cd "%EXEC_DIR%" +goto endDetectBaseDir + +:baseDirNotFound +set MAVEN_PROJECTBASEDIR=%EXEC_DIR% +cd "%EXEC_DIR%" + +:endDetectBaseDir + +IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig + +@setlocal EnableExtensions EnableDelayedExpansion +for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a +@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% + +:endReadAdditionalConfig + +SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" + +set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" +set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* +if ERRORLEVEL 1 goto error +goto end + +:error +set ERROR_CODE=1 + +:end +@endlocal & set ERROR_CODE=%ERROR_CODE% + +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost +@REM check for post script, once with legacy .bat ending and once with .cmd ending +if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" +if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" +:skipRcPost + +@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' +if "%MAVEN_BATCH_PAUSE%" == "on" pause + +if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% + +exit /B %ERROR_CODE% diff --git a/01.Spring-Boot-Introduction-In-10-Steps/notes.txt b/01.Spring-Boot-Introduction-In-10-Steps/notes.txt new file mode 100644 index 0000000..f7a73d3 --- /dev/null +++ b/01.Spring-Boot-Introduction-In-10-Steps/notes.txt @@ -0,0 +1,26 @@ +## Spring Boot + +### Goals +- Enable building production ready applications quickly +- Provide common non-functional features + - embedded servers + - metrics + - health checks + - externalized configuration + +### What Spring Boot is NOT! +- ZERO code generation +- Neither an application server nor a web server + +### Features +- Quick Starter Projects with Auto Configuration + - Web + - JPA +- Embedded Servers - Tomcat, Jetty or Undertow +- Production-ready features + - metrics and health checks + - externalized configuration + + +http://localhost:8080/books => Few hardcoded books + \ No newline at end of file diff --git a/01.Spring-Boot-Introduction-In-10-Steps/pom.xml b/01.Spring-Boot-Introduction-In-10-Steps/pom.xml new file mode 100644 index 0000000..ec0eb6d --- /dev/null +++ b/01.Spring-Boot-Introduction-In-10-Steps/pom.xml @@ -0,0 +1,111 @@ + + + 4.0.0 + + com.in28minutes.springboot.basics + springboot-in-10-steps + 0.0.1-SNAPSHOT + jar + + springboot-in-10-steps + Demo project for Spring Boot + + + org.springframework.boot + spring-boot-starter-parent + 2.0.0.BUILD-SNAPSHOT + + + + + UTF-8 + UTF-8 + 1.8 + + + + + org.springframework.boot + spring-boot-starter-web + + + + org.springframework.boot + spring-boot-starter-actuator + + + + org.springframework.data + spring-data-rest-hal-browser + + + + + + org.springframework.boot + spring-boot-starter-test + test + + + + org.springframework.boot + spring-boot-devtools + + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + + + spring-snapshots + Spring Snapshots + https://repo.spring.io/snapshot + + true + + + + spring-milestones + Spring Milestones + https://repo.spring.io/milestone + + false + + + + + + + spring-snapshots + Spring Snapshots + https://repo.spring.io/snapshot + + true + + + + spring-milestones + Spring Milestones + https://repo.spring.io/milestone + + false + + + + + + diff --git a/01.Spring-Boot-Introduction-In-10-Steps/readme.md b/01.Spring-Boot-Introduction-In-10-Steps/readme.md new file mode 100644 index 0000000..95a38ae --- /dev/null +++ b/01.Spring-Boot-Introduction-In-10-Steps/readme.md @@ -0,0 +1,277 @@ +## First 10 Steps in Spring Boot + +- Step 1 : Introduction to Spring Boot - Goals and Important Features +- Step 2 : Developing Spring Applications before Spring Boot +- Step 3 : Using Spring Initializr to create a Spring Boot Application +- Step 4 : Creating a Simple REST Controller +- Step 5 : What is Spring Boot Auto Configuration? +- Step 6 : Spring Boot vs Spring vs Spring MVC +- Step 7 : Spring Boot Starter Projects - Starter Web and Starter JPA +- Step 8 : Overview of different Spring Boot Starter Projects +- Step 9 : Spring Boot Actuator +- Step 10 : Spring Boot Developer Tools +- Spring Boot - Conclusion + +## Complete Code Example + +### /notes.txt + +``` +Goals +Enable building production ready applications quickly +Provide common non-functional features +- embedded servers +- metrics +- health checks +- externalized configuration + +What Spring Boot is NOT! +ZERO code generation +Neither an application server nor a web server + +Features +Quick Starter Projects with Auto Configuration + - Web + - JPA +Embedded Servers - Tomcat, Jetty or Undertow +Production-ready features + - metrics and health checks + - externalized configuration + + +http://localhost:8080/books => Few hardcoded books + +``` +--- + +### /pom.xml + +```xml + + + 4.0.0 + + com.in28minutes.springboot.basics + springboot-in-10-steps + 0.0.1-SNAPSHOT + jar + + springboot-in-10-steps + Demo project for Spring Boot + + + org.springframework.boot + spring-boot-starter-parent + 2.0.0.BUILD-SNAPSHOT + + + + + UTF-8 + UTF-8 + 1.8 + + + + + org.springframework.boot + spring-boot-starter-web + + + + org.springframework.boot + spring-boot-starter-actuator + + + + org.springframework.data + spring-data-rest-hal-browser + + + + + + org.springframework.boot + spring-boot-starter-test + test + + + + org.springframework.boot + spring-boot-devtools + + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + + + spring-snapshots + Spring Snapshots + https://repo.spring.io/snapshot + + true + + + + spring-milestones + Spring Milestones + https://repo.spring.io/milestone + + false + + + + + + + spring-snapshots + Spring Snapshots + https://repo.spring.io/snapshot + + true + + + + spring-milestones + Spring Milestones + https://repo.spring.io/milestone + + false + + + + + + +``` +--- + +### /src/main/java/com/in28minutes/springboot/basics/springbootin10steps/Book.java + +```java +package com.in28minutes.springboot.basics.springbootin10steps; + +public class Book { + long id; + String name; + String author; + + public Book(long id, String name, String author) { + super(); + this.id = id; + this.name = name; + this.author = author; + } + + public long getId() { + return id; + } + + public String getName() { + return name; + } + + public String getAuthor() { + return author; + } + + @Override + public String toString() { + return String.format("Book [id=%s, name=%s, author=%s]", id, name, author); + } + +} +``` +--- + +### /src/main/java/com/in28minutes/springboot/basics/springbootin10steps/BooksController.java + +```java +package com.in28minutes.springboot.basics.springbootin10steps; + +import java.util.Arrays; +import java.util.List; + +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +public class BooksController { + @GetMapping("/books") + public List getAllBooks() { + return Arrays.asList( + new Book(1l, "Mastering Spring 5.2", "Ranga Karanam")); + } +} +``` +--- + +### /src/main/java/com/in28minutes/springboot/basics/springbootin10steps/SpringbootIn10StepsApplication.java + +```java +package com.in28minutes.springboot.basics.springbootin10steps; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ConfigurableApplicationContext; + +@SpringBootApplication +public class SpringbootIn10StepsApplication { + + public static void main(String[] args) { + ApplicationContext applicationContext = + SpringApplication.run(SpringbootIn10StepsApplication.class, args); + + for (String name : applicationContext.getBeanDefinitionNames()) { + System.out.println(name); + } + } +} +``` +--- + +### /src/main/resources/application.properties + +```properties +#logging.level.org.springframework = DEBUG +management.security.enabled=false +``` +--- + +### /src/test/java/com/in28minutes/springboot/basics/springbootin10steps/SpringbootIn10StepsApplicationTests.java + +```java +package com.in28minutes.springboot.basics.springbootin10steps; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; + +@RunWith(SpringRunner.class) +@SpringBootTest +public class SpringbootIn10StepsApplicationTests { + + @Test + public void contextLoads() { + } + +} +``` +--- diff --git a/01.Spring-Boot-Introduction-In-10-Steps/src/main/java/com/in28minutes/springboot/basics/springbootin10steps/Book.java b/01.Spring-Boot-Introduction-In-10-Steps/src/main/java/com/in28minutes/springboot/basics/springbootin10steps/Book.java new file mode 100644 index 0000000..9bccf3b --- /dev/null +++ b/01.Spring-Boot-Introduction-In-10-Steps/src/main/java/com/in28minutes/springboot/basics/springbootin10steps/Book.java @@ -0,0 +1,32 @@ +package com.in28minutes.springboot.basics.springbootin10steps; + +public class Book { + long id; + String name; + String author; + + public Book(long id, String name, String author) { + super(); + this.id = id; + this.name = name; + this.author = author; + } + + public long getId() { + return id; + } + + public String getName() { + return name; + } + + public String getAuthor() { + return author; + } + + @Override + public String toString() { + return String.format("Book [id=%s, name=%s, author=%s]", id, name, author); + } + +} diff --git a/01.Spring-Boot-Introduction-In-10-Steps/src/main/java/com/in28minutes/springboot/basics/springbootin10steps/BooksController.java b/01.Spring-Boot-Introduction-In-10-Steps/src/main/java/com/in28minutes/springboot/basics/springbootin10steps/BooksController.java new file mode 100644 index 0000000..bfdd7c8 --- /dev/null +++ b/01.Spring-Boot-Introduction-In-10-Steps/src/main/java/com/in28minutes/springboot/basics/springbootin10steps/BooksController.java @@ -0,0 +1,16 @@ +package com.in28minutes.springboot.basics.springbootin10steps; + +import java.util.Arrays; +import java.util.List; + +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +public class BooksController { + @GetMapping("/books") + public List getAllBooks() { + return Arrays.asList( + new Book(1l, "Mastering Spring 5.2", "Ranga Karanam")); + } +} \ No newline at end of file diff --git a/01.Spring-Boot-Introduction-In-10-Steps/src/main/java/com/in28minutes/springboot/basics/springbootin10steps/SpringbootIn10StepsApplication.java b/01.Spring-Boot-Introduction-In-10-Steps/src/main/java/com/in28minutes/springboot/basics/springbootin10steps/SpringbootIn10StepsApplication.java new file mode 100644 index 0000000..9ca4508 --- /dev/null +++ b/01.Spring-Boot-Introduction-In-10-Steps/src/main/java/com/in28minutes/springboot/basics/springbootin10steps/SpringbootIn10StepsApplication.java @@ -0,0 +1,19 @@ +package com.in28minutes.springboot.basics.springbootin10steps; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ConfigurableApplicationContext; + +@SpringBootApplication +public class SpringbootIn10StepsApplication { + + public static void main(String[] args) { + ApplicationContext applicationContext = + SpringApplication.run(SpringbootIn10StepsApplication.class, args); + + for (String name : applicationContext.getBeanDefinitionNames()) { + System.out.println(name); + } + } +} diff --git a/01.Spring-Boot-Introduction-In-10-Steps/src/main/resources/application.properties b/01.Spring-Boot-Introduction-In-10-Steps/src/main/resources/application.properties new file mode 100644 index 0000000..4b07b72 --- /dev/null +++ b/01.Spring-Boot-Introduction-In-10-Steps/src/main/resources/application.properties @@ -0,0 +1,2 @@ +#logging.level.org.springframework = DEBUG +management.security.enabled=false \ No newline at end of file diff --git a/01.Spring-Boot-Introduction-In-10-Steps/src/test/java/com/in28minutes/springboot/basics/springbootin10steps/SpringbootIn10StepsApplicationTests.java b/01.Spring-Boot-Introduction-In-10-Steps/src/test/java/com/in28minutes/springboot/basics/springbootin10steps/SpringbootIn10StepsApplicationTests.java new file mode 100644 index 0000000..6cd60dd --- /dev/null +++ b/01.Spring-Boot-Introduction-In-10-Steps/src/test/java/com/in28minutes/springboot/basics/springbootin10steps/SpringbootIn10StepsApplicationTests.java @@ -0,0 +1,16 @@ +package com.in28minutes.springboot.basics.springbootin10steps; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; + +@RunWith(SpringRunner.class) +@SpringBootTest +public class SpringbootIn10StepsApplicationTests { + + @Test + public void contextLoads() { + } + +} diff --git a/02.Spring-Boot-Web-Application/.DS_Store b/02.Spring-Boot-Web-Application/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..5172429f264de2441865cb4700216d4256da9242 GIT binary patch literal 6148 zcmeH~J!%6%427R!7lt%jx}3%b$PET#pTHLgIFQEJ;E>dF^gR7ES*H$5cmnB-G%I%Z zD|S`@Z2$T80!#olbXV*=%*>dt@PRwdU#I)^a=X5>;#J@&VrHyNnC;iLL0pQvfVyTmjO&;ssLc!1UOG})p;=82 zR;?Ceh}WZ?+UmMqI#RP8R>OzYoz15hnq@nzF`-!xQ4j$Um=RcIKKc27r2jVm&svm< zfC&6E0=7P!4tu^-ovjbA=k?dB`g+i*aXG_}p8zI)6mRKa+;6_1_R^8c3Qa!(fk8n8 H{*=HsM+*^= literal 0 HcmV?d00001 diff --git a/02.Spring-Boot-Web-Application/README.md b/02.Spring-Boot-Web-Application/README.md new file mode 100644 index 0000000..ead3000 --- /dev/null +++ b/02.Spring-Boot-Web-Application/README.md @@ -0,0 +1,133 @@ +# Your First Web Application with Spring Boot +Develop your first web application with Spring Boot in more than 25 steps +* [Installing Eclipse, Maven and Java](#installing-tools) +* [Course Overview](#course-overview) +* [About in28Minutes](#about-in28minutes) + - [Our Beliefs](#our-beliefs) + - [Our Approach](#our-approach) + - [Find Us](#useful-links) + - [Other Courses](#other-courses) + +## Installing Tools +- PDF : https://github.com/in28minutes/SpringIn28Minutes/blob/master/InstallationGuide-JavaEclipseAndMaven_v2.pdf +- Video : https://www.youtube.com/playlist?list=PLBBog2r6uMCSmMVTW_QmDLyASBvovyAO3 +- GIT Repository : https://github.com/in28minutes/getting-started-in-5-steps + +## Course Overview + +### Introduction +Developing your first Spring Boot Web Application is fun. + +Spring Boot makes it easy to create stand-alone, production-grade Spring based Applications that you can “just run”. We take an opinionated view of the Spring platform and third-party libraries so you can get started with minimum fuss. Most Spring Boot applications need very little Spring configuration. + +In this course, you will learn the basics developing a Basic Todo Management Application using Spring Boot with Login and Logout functionalities. + +You will build the website step by step - in more than 25 steps. This course would be a perfect first step as an introduction to Java Web Application Development. + +You will be using Spring (Dependency Management), Spring MVC, Spring Boot, Spring Security (Authentication and Authorization), BootStrap (Styling Pages), Maven (dependencies management), Eclipse (IDE) and Tomcat Embedded Web Server. We will help you set up each one of these. + +You will learn about +- Basics of Spring Boot +- Basics of Autoconfiguration and Spring Boot Magic +- DispatcherServlet +- Basic Todo Management Application with Login/Logout +- Model, Controllers, ViewResolver and Filters +- Forms - DataBinding, Validation +- Annotation based approach - @RequestParam, @ModelAttribute, @SessionAttributes etc +- Bootstrap to style the page +- Spring Security +- Exception Handling + +### Step Wise Details +- Step 01: Basic Spring Boot Web Application Setup +- Step 02: First Spring MVC Controller, @ResponseBody, @Controller +- Step 03: Demystifying some of the Spring Boot magic +- Step 04: Redirect to Login JSP - LoginController, @ResponseBody and View Resolver +- Step 05: Show userid and password on the welcome page - ModelMap and @RequestParam +- Step 06: DispatcherServlet and Spring MVC Flow +- Step 07: Your First HTML form +- Step 08: Add hard-coded validation of userid and password +- Step 09: Magic of Spring +- Step 10: Create TodoController and list-todos view. Make TodoService a @Service and inject it. +- Step 11: Architecture of Web Applications +- Step 12: Session vs Model vs Request - @SessionAttributes +- Step 13: Add new todo +- Step 14: Display Todos in a table using JSTL Tags +- Step 15: Bootstrap for Page Formatting using webjars +- Step 16: Let's delete a Todo +- Step 17: Format Add Todo Page and Adding Basic HTML5 form validation +- Step 18: Introduce JSR 349 Validations using Hibernate Validator - First Command Bean. +- Step 19: Updating a todo +- Step 20: Let's add a Target Date for Todo - Use initBinder to Handle Date Fields +- Step 21: JSP Fragments and Navigation Bar +- Step 22: Preparing for Spring Security +- Step 23: Initial Spring Security Setup +- Step 24: Refactor and add Logout Functionality using Spring Security +- Step 25: Exception Handling + +--- + +- We do NOT interact with a Database in this Beginner’s Course. +- We will be building a traditional JSP based web application in this course. + +--- + +### Expectations +- You should know Java. You should understand usage of Annotations. +- You should understand the basics of Spring framework. +- You are NOT expected to have any experience with Eclipse or Maven. +- We will help you install Eclipse and get up and running with Maven. + +## Let's have some fun +- What are we waiting for? +- Let's have some fun building a web application Spring Boot in 25 Steps. +- I had fun creating this course and hope you would too. +- Thanks for your interest in Our Course + - I hope you’re as excited as I am! + - If you’re ready to learn more and sign up for the course, + - go ahead and hit that Enroll button, + - or take a test drive by using the Free Preview feature. +- See you in the course! + +## Getting Started +- Eclipse - https://courses.in28minutes.com/p/eclipse-tutorial-for-beginners +- Maven - https://courses.in28minutes.com/p/maven-tutorial-for-beginners-in-5-steps +- JUnit - https://courses.in28minutes.com/p/junit-tutorial-for-beginners +- Mockito - https://courses.in28minutes.com/p/mockito-for-beginner-in-5-steps + +## About in28Minutes +- At in28Minutes, we ask ourselves one question everyday. How do we help you learn effectively - that is more quickly and retain more of what you have learnt? +- We use Problem-Solution based Step-By-Step Hands-on Approach With Practical, Real World Application Examples. +- Our success on Udemy and Youtube (2 Million Views & 12K Subscribers) speaks volumes about the success of our approach. +- While our primary expertise is on Development, Design & Architecture Java & Related Frameworks (Spring, Struts, Hibernate) we are expanding into the front-end world (Bootstrap, JQuery, Angular JS). + +### Our Beliefs +- Best Courses are interactive and fun. +- Foundations for building high quality applications are best laid down while learning. + +### Our Approach +- Problem Solution based Step by Step Hands-on Learning +- Practical, Real World Application Examples. +- We use 80-20 Rule. We discuss 20% things used 80% of time in depth. We touch upon other things briefly equipping you with enough knowledge to find out more on your own. +- We will be developing a demo application in the course, which could be reused in your projects, saving hours of your effort. +- We love open source and therefore, All our code is open source too and available on Github. + +### Other Courses + +- [Check out all our courses with 100,000 Students](https://courses.in28minutes.com/courses) +- [25 Videos and Articles for Beginners on Spring Boot](http://www.springboottutorial.com/spring-boot-tutorials-for-beginners) +- Our Best Courses with 66,000 Students and 4,000 5-Star Ratings + * [Java Interview Guide : 200+ Interview Questions and Answers](https://www.udemy.com/java-interview-questions-and-answers/?couponCode=JAVA_INTER_GIT) + * [Mockito Tutorial : Learn mocking with 25 Junit Examples](https://www.udemy.com/mockito-tutorial-with-junit-examples/?couponCode=MOCKITO_GIT) + * [Java EE Made Easy - Patterns, Architecture and Frameworks](https://www.udemy.com/java-ee-design-patterns-architecture-and-frameworks/?couponCode=EEPATTERNS-GIT) + * [Spring MVC For Beginners : Build Java Web App in 25 Steps](https://www.udemy.com/spring-mvc-tutorial-for-beginners-step-by-step/?couponCode=SPRINGMVC-GIT) + * [JSP Servlets For Beginners : Build Java Web App in 25 Steps](https://www.udemy.com/learn-java-servlets-and-jsp-web-application-in-25-steps/?couponCode=JSPSRVLT-GIT) + * [Maven Tutorial - Manage Java Dependencies in 25 Steps](https://www.udemy.com/learn-maven-java-dependency-management-in-20-steps/?couponCode=MAVEN_GIT) + * [Java OOPS in 1 Hours](https://www.udemy.com/learn-object-oriented-programming-in-java/?couponCode=OOPS-GIT) + * [C Puzzle for Interview](https://www.udemy.com/c-puzzles-for-beginners/?couponCode=CPUZZLES-GIT) + +### Useful Links +- [Our Website](http://www.in28minutes.com) +- [Facebook](http://facebook.com/in28minutes) +- [Twitter](http://twitter.com/in28minutes) +- [Google Plus](https://plus.google.com/u/3/110861829188024231119) diff --git a/02.Spring-Boot-Web-Application/Step01.md b/02.Spring-Boot-Web-Application/Step01.md new file mode 100644 index 0000000..f877dad --- /dev/null +++ b/02.Spring-Boot-Web-Application/Step01.md @@ -0,0 +1,117 @@ +## What You Will Learn during this Step: +- Lets create a simple web application using Spring Boot +- Lets run the Spring Boot Application +- There is a lot of magic happening in here! We will take a deep dive into the magic in Step 03. + +## Files List + +### pom.xml + +```xml + + + 4.0.0 + + com.in28minutes.springboot.web + spring-boot-first-web-application + 0.0.1-SNAPSHOT + jar + + spring-boot-first-web-application + Demo project for Spring Boot + + + org.springframework.boot + spring-boot-starter-parent + 1.4.3.RELEASE + + + + + UTF-8 + UTF-8 + 1.8 + + + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-devtools + runtime + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + +``` +--- +### src/main/java/com/in28minutes/springboot/web/SpringBootFirstWebApplication.java + +```java +package com.in28minutes.springboot.web; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class SpringBootFirstWebApplication { + + public static void main(String[] args) { + SpringApplication.run(SpringBootFirstWebApplication.class, args); + } +} +``` +--- +### src/main/resources/application.properties + +``` +``` +--- +### src/test/java/com/in28minutes/springboot/web/SpringBootFirstWebApplicationTests.java + +```java +package com.in28minutes.springboot.web; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; + +@RunWith(SpringRunner.class) +@SpringBootTest +public class SpringBootFirstWebApplicationTests { + + @Test + public void contextLoads() { + } + +} +``` +--- +### todo.txt + +``` +Spring Boot Starter Parent +Spring Boot Starter Web +@SpringBootApplication +Auto Configuration +``` +--- diff --git a/02.Spring-Boot-Web-Application/Step02.md b/02.Spring-Boot-Web-Application/Step02.md new file mode 100644 index 0000000..ff0b70e --- /dev/null +++ b/02.Spring-Boot-Web-Application/Step02.md @@ -0,0 +1,165 @@ +## What You Will Learn during this Step: +- @RequestMapping(value = "/login", method = RequestMethod.GET) +- http://localhost:8080/login +- Why @ResponseBody? +- Important of RequestMapping method +- How do web applications work? Request and Response + - Browser sends Http Request to Web Server + - Code in Web Server => Input:HttpRequest, Output: HttpResponse + - Web Server responds with Http Response + +## Useful Snippets and References +First Snippet +``` +@Controller +public class LoginController { + + @RequestMapping(value = "/login") + @ResponseBody + public String sayHello() { + return "Hello World dummy"; + } + +} +``` +### src/main/resources/application.properties + +``` +logging.level.org.springframework.web: DEBUG +``` + + +## Exercises +- Create another method with a different mapping returning some other text! + +## Files List + +### pom.xml + +```xml + + + 4.0.0 + + com.in28minutes.springboot.web + spring-boot-first-web-application + 0.0.1-SNAPSHOT + jar + + spring-boot-first-web-application + Demo project for Spring Boot + + + org.springframework.boot + spring-boot-starter-parent + 1.4.3.RELEASE + + + + + UTF-8 + UTF-8 + 1.8 + + + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-devtools + runtime + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + +``` +--- +### src/main/java/com/in28minutes/springboot/web/controller/LoginController.java + +```java +package com.in28minutes.springboot.web.controller; + +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.RequestMapping; + +@Controller +public class LoginController { + + @RequestMapping("/login") + public String loginMessage(){ + return "Hello World"; + } +} +``` +--- +### src/main/java/com/in28minutes/springboot/web/SpringBootFirstWebApplication.java + +```java +package com.in28minutes.springboot.web; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class SpringBootFirstWebApplication { + + public static void main(String[] args) { + SpringApplication.run(SpringBootFirstWebApplication.class, args); + } +} +``` +--- +### src/main/resources/application.properties + +``` +logging.level.org.springframework.web: DEBUG +``` +--- +### src/test/java/com/in28minutes/springboot/web/SpringBootFirstWebApplicationTests.java + +```java +package com.in28minutes.springboot.web; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; + +@RunWith(SpringRunner.class) +@SpringBootTest +public class SpringBootFirstWebApplicationTests { + + @Test + public void contextLoads() { + } + +} +``` +--- +### todo.txt + +``` +Spring Boot Starter Parent +Spring Boot Starter Web +@SpringBootApplication +Auto Configuration +``` +--- diff --git a/02.Spring-Boot-Web-Application/Step03.md b/02.Spring-Boot-Web-Application/Step03.md new file mode 100644 index 0000000..11a6fb3 --- /dev/null +++ b/02.Spring-Boot-Web-Application/Step03.md @@ -0,0 +1,6 @@ +## What You Will Learn during this Step: +- Demystifying some of the magic + - Spring Boot Starter Parent + - Spring Boot Starter Web + - Embedded Tomcat + - Dev Tools diff --git a/02.Spring-Boot-Web-Application/Step04.md b/02.Spring-Boot-Web-Application/Step04.md new file mode 100644 index 0000000..4cc131f --- /dev/null +++ b/02.Spring-Boot-Web-Application/Step04.md @@ -0,0 +1,207 @@ +## What You Will Learn during this Step: +- Your First JSP + - There is a bit of setup before we get there! +- Introduction to View Resolver + +## Useful Snippets and References +First Snippet - /src/main/webapp/WEB-INF/jsp/login.jsp +``` + + +Yahoo!! + + +My First JSP!!! + + +``` + +Second Snippet - /src/main/resources/application.properties +``` +spring.mvc.view.prefix: /WEB-INF/jsp/ +spring.mvc.view.suffix: .jsp +logging.level.: DEBUG +``` + +Third Snippet : To enable jsp support in embedded tomcat server! +``` + + org.apache.tomcat.embed + tomcat-embed-jasper + provided + + +``` + +## Exercises +- Create a new jsp and a new controller method to redirect to it! +- Play around! + +## Files List + +### pom.xml + +```xml + + + 4.0.0 + + com.in28minutes.springboot.web + spring-boot-first-web-application + 0.0.1-SNAPSHOT + jar + + spring-boot-first-web-application + Demo project for Spring Boot + + + org.springframework.boot + spring-boot-starter-parent + 1.4.3.RELEASE + + + + + UTF-8 + UTF-8 + 1.8 + + + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-devtools + runtime + + + org.apache.tomcat.embed + tomcat-embed-jasper + provided + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + +``` +--- +### src/main/java/com/in28minutes/springboot/web/controller/LoginController.java + +```java +package com.in28minutes.springboot.web.controller; + +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.ResponseBody; + +@Controller +public class LoginController { + + @RequestMapping("/login") + public String loginMessage(){ + return "login"; + } +} +``` +--- +### src/main/java/com/in28minutes/springboot/web/SpringBootFirstWebApplication.java + +```java +package com.in28minutes.springboot.web; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class SpringBootFirstWebApplication { + + public static void main(String[] args) { + SpringApplication.run(SpringBootFirstWebApplication.class, args); + } +} +``` +--- +### src/main/resources/application.properties + +``` +spring.mvc.view.prefix=/WEB-INF/jsp/ +spring.mvc.view.suffix=.jsp +logging.level.org.springframework.web=DEBUG +``` +--- +### src/main/webapp/WEB-INF/jsp/login.jsp + +``` + + + +First Web Application + + + +My First JSP!! + + + +``` +--- +### src/test/java/com/in28minutes/springboot/web/SpringBootFirstWebApplicationTests.java + +```java +package com.in28minutes.springboot.web; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; + +@RunWith(SpringRunner.class) +@SpringBootTest +public class SpringBootFirstWebApplicationTests { + + @Test + public void contextLoads() { + } + +} +``` +--- +### todo.txt + +``` +Spring Boot Starter Parent +Spring Boot Starter Web +@SpringBootApplication +Auto Configuration + +Dispatcher Servlet + +/login => "login" + +"login" => src/main/webapp/WEB-INF/jsp/login.jsp + + +Search for a view named "login" + + + +/login => LoginController +``` +--- diff --git a/02.Spring-Boot-Web-Application/Step05.md b/02.Spring-Boot-Web-Application/Step05.md new file mode 100644 index 0000000..4357f7c --- /dev/null +++ b/02.Spring-Boot-Web-Application/Step05.md @@ -0,0 +1,186 @@ +## What You Will Learn during this Step: +- You first GET Parameter. +- Problem with using GET + +## Snippets +``` +ModelMap model +model.put("name", name); +My First JSP!!! My name is ${name} +``` + +## Files List + +### pom.xml + +```xml + + + 4.0.0 + + com.in28minutes.springboot.web + spring-boot-first-web-application + 0.0.1-SNAPSHOT + jar + + spring-boot-first-web-application + Demo project for Spring Boot + + + org.springframework.boot + spring-boot-starter-parent + 1.4.3.RELEASE + + + + + UTF-8 + UTF-8 + 1.8 + + + + + org.springframework.boot + spring-boot-starter-web + + + + org.apache.tomcat.embed + tomcat-embed-jasper + provided + + + + org.springframework.boot + spring-boot-devtools + runtime + + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + +``` +--- +### src/main/java/com/in28minutes/springboot/web/controller/LoginController.java + +```java +package com.in28minutes.springboot.web.controller; + +import org.springframework.stereotype.Controller; +import org.springframework.ui.ModelMap; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; + +@Controller +public class LoginController { + + //Model + + @RequestMapping("/login") + public String loginMessage(@RequestParam String name, ModelMap model){ + model.put("name", name); + return "login"; + } +} +``` +--- +### src/main/java/com/in28minutes/springboot/web/SpringBootFirstWebApplication.java + +```java +package com.in28minutes.springboot.web; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class SpringBootFirstWebApplication { + + public static void main(String[] args) { + SpringApplication.run(SpringBootFirstWebApplication.class, args); + } +} +``` +--- +### src/main/resources/application.properties + +``` +spring.mvc.view.prefix=/WEB-INF/jsp/ +spring.mvc.view.suffix=.jsp +logging.level.org.springframework.web=DEBUG +``` +--- +### src/main/webapp/WEB-INF/jsp/login.jsp + +``` + + + +First Web Application + + + +My First JSP!! Welcome ${name}! + + + +``` +--- +### src/test/java/com/in28minutes/springboot/web/SpringBootFirstWebApplicationTests.java + +```java +package com.in28minutes.springboot.web; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; + +@RunWith(SpringRunner.class) +@SpringBootTest +public class SpringBootFirstWebApplicationTests { + + @Test + public void contextLoads() { + } + +} +``` +--- +### todo.txt + +``` +Spring Boot Starter Parent +Spring Boot Starter Web +@SpringBootApplication +Auto Configuration + +Dispatcher Servlet + +/login => "login" + +"login" => src/main/webapp/WEB-INF/jsp/login.jsp + + +Search for a view named "login" + + + +/login => LoginController +``` +--- diff --git a/02.Spring-Boot-Web-Application/Step06.md b/02.Spring-Boot-Web-Application/Step06.md new file mode 100644 index 0000000..776452f --- /dev/null +++ b/02.Spring-Boot-Web-Application/Step06.md @@ -0,0 +1,12 @@ +## What You Will Learn during this Step: +- Understand importance of DispatcherServlet. + +## Spring MVC Request Flow +- DispatcherServlet receives HTTP Request. +- DispatcherServlet identifies the right Controller based on the URL. +- Controller executes Business Logic. +- Controller returns a) Model b) View Name Back to DispatcherServlet. +- DispatcherServlet identifies the correct view (ViewResolver). +- DispatcherServlet makes the model available to view and executes it. +- DispatcherServlet returns HTTP Response Back. +- Flow : http://docs.spring.io/spring-framework/docs/2.0.8/reference/images/mvc.png diff --git a/02.Spring-Boot-Web-Application/Step07.md b/02.Spring-Boot-Web-Application/Step07.md new file mode 100644 index 0000000..514ffac --- /dev/null +++ b/02.Spring-Boot-Web-Application/Step07.md @@ -0,0 +1,226 @@ +## What You Will Learn during this Step: +- Lets get the name from the user in a form + +## Useful Snippets and References +First Snippet +``` + @RequestMapping(value = "/login", method = RequestMethod.GET) + public String showLoginPage(ModelMap model, @RequestParam String name) { + return "login"; + } + + @RequestMapping(value = "/login", method = RequestMethod.POST) + public String handleLogin(ModelMap model, @RequestParam String name) { + model.put("name", name); + return "welcome"; + } + +``` +Second Snippet +``` +
+ Name : +
+ +``` + +## Files List + +### pom.xml + +```xml + + + 4.0.0 + + com.in28minutes.springboot.web + spring-boot-first-web-application + 0.0.1-SNAPSHOT + jar + + spring-boot-first-web-application + Demo project for Spring Boot + + + org.springframework.boot + spring-boot-starter-parent + 1.4.3.RELEASE + + + + + UTF-8 + UTF-8 + 1.8 + + + + + org.springframework.boot + spring-boot-starter-web + + + + org.apache.tomcat.embed + tomcat-embed-jasper + provided + + + + org.springframework.boot + spring-boot-devtools + runtime + + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + +``` +--- +### src/main/java/com/in28minutes/springboot/web/controller/LoginController.java + +```java +package com.in28minutes.springboot.web.controller; + +import org.springframework.stereotype.Controller; +import org.springframework.ui.ModelMap; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RequestParam; + +@Controller +public class LoginController { + + @RequestMapping(value="/login", method = RequestMethod.GET) + public String showLoginPage(ModelMap model){ + return "login"; + } + + @RequestMapping(value="/login", method = RequestMethod.POST) + public String showWelcomePage(ModelMap model, @RequestParam String name){ + model.put("name", name); + return "welcome"; + } + +} +``` +--- +### src/main/java/com/in28minutes/springboot/web/SpringBootFirstWebApplication.java + +```java +package com.in28minutes.springboot.web; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class SpringBootFirstWebApplication { + + public static void main(String[] args) { + SpringApplication.run(SpringBootFirstWebApplication.class, args); + } +} +``` +--- +### src/main/resources/application.properties + +``` +spring.mvc.view.prefix=/WEB-INF/jsp/ +spring.mvc.view.suffix=.jsp +logging.level.org.springframework.web=DEBUG +``` +--- +### src/main/webapp/WEB-INF/jsp/login.jsp + +``` + + + +First Web Application + + + +
+ Name : + Password : + +
+ + + +``` +--- +### src/main/webapp/WEB-INF/jsp/welcome.jsp + +``` + + + +First Web Application + + + + Welcome ${name}!! + + + +``` +--- +### src/test/java/com/in28minutes/springboot/web/SpringBootFirstWebApplicationTests.java + +```java +package com.in28minutes.springboot.web; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; + +@RunWith(SpringRunner.class) +@SpringBootTest +public class SpringBootFirstWebApplicationTests { + + @Test + public void contextLoads() { + } + +} +``` +--- +### todo.txt + +``` +Spring Boot Starter Parent +Spring Boot Starter Web +@SpringBootApplication +Auto Configuration + +Dispatcher Servlet + +/login => "login" + +"login" => src/main/webapp/WEB-INF/jsp/login.jsp + + +Search for a view named "login" + + + +/login => LoginController +``` +--- diff --git a/02.Spring-Boot-Web-Application/Step07.zip b/02.Spring-Boot-Web-Application/Step07.zip new file mode 100644 index 0000000000000000000000000000000000000000..0a86c7d330310a9cbfd08fbc25df5a61c4a30347 GIT binary patch literal 15427 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 M2~@gN+rj_-2N4A`$N&HU literal 0 HcmV?d00001 diff --git a/02.Spring-Boot-Web-Application/Step08.md b/02.Spring-Boot-Web-Application/Step08.md new file mode 100644 index 0000000..33c38ce --- /dev/null +++ b/02.Spring-Boot-Web-Application/Step08.md @@ -0,0 +1,280 @@ +## What You Will Learn during this Step: +- Add validation for userid and password + - Hard coded validation!! + +## Useful Snippets and References +First Snippet +``` +package com.in28minutes.springboot.web.service; + +import org.springframework.stereotype.Component; + +@Component +public class LoginService { + public boolean validateUser(String user, String password) { + return user.equalsIgnoreCase("in28Minutes") && password.equals("dummy"); + } +} + +``` +Second Snippet +``` + @Autowired + private LoginService service; + + @RequestMapping(value = "/login", method = RequestMethod.POST) + public String handleLogin(ModelMap model, @RequestParam String name, + @RequestParam String password) { + + boolean isValidUser = service.validateUser(name, password); + + if (isValidUser) { + model.put("name", name); + return "welcome"; + } else { + model.put("errorMessage", "Invalid Credentials!!"); + return "login"; + } + } +``` + +## Files List + +### pom.xml + +```xml + + + 4.0.0 + + com.in28minutes.springboot.web + spring-boot-first-web-application + 0.0.1-SNAPSHOT + jar + + spring-boot-first-web-application + Demo project for Spring Boot + + + org.springframework.boot + spring-boot-starter-parent + 1.4.3.RELEASE + + + + + UTF-8 + UTF-8 + 1.8 + + + + + org.springframework.boot + spring-boot-starter-web + + + + org.apache.tomcat.embed + tomcat-embed-jasper + provided + + + + org.springframework.boot + spring-boot-devtools + runtime + + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + +``` +--- +### src/main/java/com/in28minutes/springboot/web/controller/LoginController.java + +```java +package com.in28minutes.springboot.web.controller; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import org.springframework.ui.ModelMap; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RequestParam; + +import com.in28minutes.springboot.web.service.LoginService; + +@Controller +public class LoginController { + + @Autowired + LoginService service; + + @RequestMapping(value="/login", method = RequestMethod.GET) + public String showLoginPage(ModelMap model){ + return "login"; + } + + @RequestMapping(value="/login", method = RequestMethod.POST) + public String showWelcomePage(ModelMap model, @RequestParam String name, @RequestParam String password){ + boolean isValidUser = service.validateUser(name, password); + + if (!isValidUser) { + model.put("errorMessage", "Invalid Credentials"); + return "login"; + } + + model.put("name", name); + model.put("password", password); + + return "welcome"; + } + +} +``` +--- +### src/main/java/com/in28minutes/springboot/web/service/LoginService.java + +```java +package com.in28minutes.springboot.web.service; + +import org.springframework.stereotype.Component; + +@Component +public class LoginService { + + public boolean validateUser(String userid, String password) { + // in28minutes, dummy + return userid.equalsIgnoreCase("in28minutes") + && password.equalsIgnoreCase("dummy"); + } + +} +``` +--- +### src/main/java/com/in28minutes/springboot/web/SpringBootFirstWebApplication.java + +```java +package com.in28minutes.springboot.web; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class SpringBootFirstWebApplication { + + public static void main(String[] args) { + SpringApplication.run(SpringBootFirstWebApplication.class, args); + } +} +``` +--- +### src/main/resources/application.properties + +``` +spring.mvc.view.prefix=/WEB-INF/jsp/ +spring.mvc.view.suffix=.jsp +logging.level.org.springframework.web=DEBUG +``` +--- +### src/main/webapp/WEB-INF/jsp/login.jsp + +``` + + + +First Web Application + + + + ${errorMessage} +
+ Name : + Password : + +
+ + + +``` +--- +### src/main/webapp/WEB-INF/jsp/welcome.jsp + +``` + + + +First Web Application + + + + Welcome ${name}!! + + + +``` +--- +### src/test/java/com/in28minutes/springboot/web/SpringBootFirstWebApplicationTests.java + +```java +package com.in28minutes.springboot.web; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; + +@RunWith(SpringRunner.class) +@SpringBootTest +public class SpringBootFirstWebApplicationTests { + + @Test + public void contextLoads() { + } + +} +``` +--- +### todo.txt + +``` +Component, Service, Repository, Controller +Autowired +ComponentScan + + +Spring Boot Starter Parent +Spring Boot Starter Web +@SpringBootApplication +Auto Configuration + +Dispatcher Servlet + +/login => "login" + +"login" => src/main/webapp/WEB-INF/jsp/login.jsp + + +Search for a view named "login" + + + +/login => LoginController +``` +--- diff --git a/02.Spring-Boot-Web-Application/Step08.zip b/02.Spring-Boot-Web-Application/Step08.zip new file mode 100644 index 0000000000000000000000000000000000000000..86e7a508bfd9a80f300763511a875b1ee381b863 GIT binary patch literal 16275 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 todos = new ArrayList(); + private static int todoCount = 3; + + static { + todos.add(new Todo(1, "in28Minutes", "Learn Spring MVC", new Date(), + false)); + todos.add(new Todo(2, "in28Minutes", "Learn Struts", new Date(), false)); + todos.add(new Todo(3, "in28Minutes", "Learn Hibernate", new Date(), + false)); + } + + public List retrieveTodos(String user) { + List filteredTodos = new ArrayList(); + for (Todo todo : todos) { + if (todo.getUser().equals(user)) { + filteredTodos.add(todo); + } + } + return filteredTodos; + } + + public void addTodo(String name, String desc, Date targetDate, + boolean isDone) { + todos.add(new Todo(++todoCount, name, desc, targetDate, isDone)); + } + + public void deleteTodo(int id) { + Iterator iterator = todos.iterator(); + while (iterator.hasNext()) { + Todo todo = iterator.next(); + if (todo.getId() == id) { + iterator.remove(); + } + } + } +} +``` diff --git a/02.Spring-Boot-Web-Application/Step11.md b/02.Spring-Boot-Web-Application/Step11.md new file mode 100644 index 0000000..27de8bc --- /dev/null +++ b/02.Spring-Boot-Web-Application/Step11.md @@ -0,0 +1,2 @@ +## What we will do: +- Lets discuss about Architecture of web applications diff --git a/02.Spring-Boot-Web-Application/Step12.md b/02.Spring-Boot-Web-Application/Step12.md new file mode 100644 index 0000000..6296489 --- /dev/null +++ b/02.Spring-Boot-Web-Application/Step12.md @@ -0,0 +1,474 @@ +## What You Will Learn during this Step: +- Session vs Model vs Request. +- Be cautious about what you use Session for. +- @SessionAttributes("name") and how it works? +- Why use Model? "adding elements directly to the HttpServletRequest (as request attributes) would seem to serve the same purpose. The reason to do this is obvious when taking a look at one of the requirements we have set for the MVC framework: It should be as view-agnostic as possible, which means we’d like to be able to incorporate view technologies not bound to the HttpServletRequest as well." - Rod Johnson et. al’s book Professional Java Development with the Spring Framework +- Spring documentation states that the @SessionAttributes annotation “list the names of model attributes which should be transparently stored in the session or some conversational storage.” + +## Useful Snippets and References +First Snippet +``` +@SessionAttributes("name") +``` + +## Files List + +### pom.xml + +```xml + + + 4.0.0 + + com.in28minutes.springboot.web + spring-boot-first-web-application + 0.0.1-SNAPSHOT + jar + + spring-boot-first-web-application + Demo project for Spring Boot + + + org.springframework.boot + spring-boot-starter-parent + 1.4.3.RELEASE + + + + + UTF-8 + UTF-8 + 1.8 + + + + + org.springframework.boot + spring-boot-starter-web + + + + org.apache.tomcat.embed + tomcat-embed-jasper + provided + + + + org.springframework.boot + spring-boot-devtools + runtime + + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + +``` +--- +### src/main/java/com/in28minutes/springboot/web/controller/LoginController.java + +```java +package com.in28minutes.springboot.web.controller; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import org.springframework.ui.ModelMap; +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.SessionAttributes; + +import com.in28minutes.springboot.web.service.LoginService; + +@Controller +@SessionAttributes("name") +public class LoginController { + + @Autowired + LoginService service; + + @RequestMapping(value="/login", method = RequestMethod.GET) + public String showLoginPage(ModelMap model){ + return "login"; + } + + @RequestMapping(value="/login", method = RequestMethod.POST) + public String showWelcomePage(ModelMap model, @RequestParam String name, @RequestParam String password){ + + boolean isValidUser = service.validateUser(name, password); + + if (!isValidUser) { + model.put("errorMessage", "Invalid Credentials"); + return "login"; + } + + model.put("name", name); + model.put("password", password); + + return "welcome"; + } + +} +``` +--- +### src/main/java/com/in28minutes/springboot/web/controller/TodoController.java + +```java +package com.in28minutes.springboot.web.controller; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import org.springframework.ui.ModelMap; +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.SessionAttributes; + +import com.in28minutes.springboot.web.service.LoginService; +import com.in28minutes.springboot.web.service.TodoService; + +@Controller +@SessionAttributes("name") +public class TodoController { + + @Autowired + TodoService service; + + @RequestMapping(value="/list-todos", method = RequestMethod.GET) + public String showTodos(ModelMap model){ + String name = (String) model.get("name"); + model.put("todos", service.retrieveTodos(name)); + return "list-todos"; + } +} +``` +--- +### src/main/java/com/in28minutes/springboot/web/model/Todo.java + +```java +package com.in28minutes.springboot.web.model; + +import java.util.Date; + +public class Todo { + private int id; + private String user; + private String desc; + private Date targetDate; + private boolean isDone; + + public Todo(int id, String user, String desc, Date targetDate, + boolean isDone) { + super(); + this.id = id; + this.user = user; + this.desc = desc; + this.targetDate = targetDate; + this.isDone = isDone; + } + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public String getUser() { + return user; + } + + public void setUser(String user) { + this.user = user; + } + + public String getDesc() { + return desc; + } + + public void setDesc(String desc) { + this.desc = desc; + } + + public Date getTargetDate() { + return targetDate; + } + + public void setTargetDate(Date targetDate) { + this.targetDate = targetDate; + } + + public boolean isDone() { + return isDone; + } + + public void setDone(boolean isDone) { + this.isDone = isDone; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + id; + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + Todo other = (Todo) obj; + if (id != other.id) { + return false; + } + return true; + } + + @Override + public String toString() { + return String.format( + "Todo [id=%s, user=%s, desc=%s, targetDate=%s, isDone=%s]", id, + user, desc, targetDate, isDone); + } + +} +``` +--- +### src/main/java/com/in28minutes/springboot/web/service/LoginService.java + +```java +package com.in28minutes.springboot.web.service; + +import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; + +@Service +public class LoginService { + + public boolean validateUser(String userid, String password) { + // in28minutes, dummy + return userid.equalsIgnoreCase("in28minutes") + && password.equalsIgnoreCase("dummy"); + } + +} +``` +--- +### src/main/java/com/in28minutes/springboot/web/service/TodoService.java + +```java +package com.in28minutes.springboot.web.service; + +import java.util.ArrayList; +import java.util.Date; +import java.util.Iterator; +import java.util.List; + +import org.springframework.stereotype.Service; + +import com.in28minutes.springboot.web.model.Todo; + +@Service +public class TodoService { + private static List todos = new ArrayList(); + private static int todoCount = 3; + + static { + todos.add(new Todo(1, "in28Minutes", "Learn Spring MVC", new Date(), + false)); + todos.add(new Todo(2, "in28Minutes", "Learn Struts", new Date(), false)); + todos.add(new Todo(3, "in28Minutes", "Learn Hibernate", new Date(), + false)); + } + + public List retrieveTodos(String user) { + List filteredTodos = new ArrayList(); + for (Todo todo : todos) { + if (todo.getUser().equals(user)) { + filteredTodos.add(todo); + } + } + return filteredTodos; + } + + public void addTodo(String name, String desc, Date targetDate, + boolean isDone) { + todos.add(new Todo(++todoCount, name, desc, targetDate, isDone)); + } + + public void deleteTodo(int id) { + Iterator iterator = todos.iterator(); + while (iterator.hasNext()) { + Todo todo = iterator.next(); + if (todo.getId() == id) { + iterator.remove(); + } + } + } +} +``` +--- +### src/main/java/com/in28minutes/springboot/web/SpringBootFirstWebApplication.java + +```java +package com.in28minutes.springboot.web; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.ComponentScan; + +@SpringBootApplication +@ComponentScan("com.in28minutes.springboot.web") +public class SpringBootFirstWebApplication { + + public static void main(String[] args) { + SpringApplication.run(SpringBootFirstWebApplication.class, args); + } +} +``` +--- +### src/main/resources/application.properties + +``` +spring.mvc.view.prefix=/WEB-INF/jsp/ +spring.mvc.view.suffix=.jsp +logging.level.org.springframework.web=DEBUG +``` +--- +### src/main/webapp/WEB-INF/jsp/list-todos.jsp + +``` + + + +First Web Application + + + + Here are the list of your todos: + ${todos} +
+ Your Name is : ${name} + + + +``` +--- +### src/main/webapp/WEB-INF/jsp/login.jsp + +``` + + + +First Web Application + + + + ${errorMessage} +
+ Name : + Password : + +
+ + + +``` +--- +### src/main/webapp/WEB-INF/jsp/welcome.jsp + +``` + + + +First Web Application + + + + Welcome ${name}!! Click here to manage your todo's. + + + +``` +--- +### src/test/java/com/in28minutes/springboot/web/SpringBootFirstWebApplicationTests.java + +```java +package com.in28minutes.springboot.web; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; + +@RunWith(SpringRunner.class) +@SpringBootTest +public class SpringBootFirstWebApplicationTests { + + @Test + public void contextLoads() { + } + +} +``` +--- +### todo.txt + +``` +LoginController -> adds name to model +welcome.jsp -> shows ${name} + +TodoController -> redirects to list-todos.jsp +${name} is empty + + +Component, Service, Repository, Controller +Autowired +ComponentScan + +Field dummyService in com.in28minutes.springboot.web.controller.LoginController +required a bean of type 'com.in28minutes.dummy.DummyService' +that could not be found. + +Spring Boot Starter Parent +Spring Boot Starter Web +@SpringBootApplication +Auto Configuration + +Dispatcher Servlet + +/login => "login" + +"login" => src/main/webapp/WEB-INF/jsp/login.jsp + + +Search for a view named "login" + + + +/login => LoginController +``` +--- diff --git a/02.Spring-Boot-Web-Application/Step12.zip b/02.Spring-Boot-Web-Application/Step12.zip new file mode 100644 index 0000000000000000000000000000000000000000..f38ee97c86aa5b50486770b7aaf6f601422ca4f1 GIT binary patch literal 19103 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 + + 4.0.0 + + com.in28minutes.springboot.web + spring-boot-first-web-application + 0.0.1-SNAPSHOT + jar + + spring-boot-first-web-application + Demo project for Spring Boot + + + org.springframework.boot + spring-boot-starter-parent + 1.4.3.RELEASE + + + + + UTF-8 + UTF-8 + 1.8 + + + + + org.springframework.boot + spring-boot-starter-web + + + + org.apache.tomcat.embed + tomcat-embed-jasper + provided + + + + org.springframework.boot + spring-boot-devtools + runtime + + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + +``` +--- +### src/main/java/com/in28minutes/springboot/web/controller/LoginController.java + +```java +package com.in28minutes.springboot.web.controller; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import org.springframework.ui.ModelMap; +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.SessionAttributes; + +import com.in28minutes.springboot.web.service.LoginService; + +@Controller +@SessionAttributes("name") +public class LoginController { + + @Autowired + LoginService service; + + @RequestMapping(value="/login", method = RequestMethod.GET) + public String showLoginPage(ModelMap model){ + return "login"; + } + + @RequestMapping(value="/login", method = RequestMethod.POST) + public String showWelcomePage(ModelMap model, @RequestParam String name, @RequestParam String password){ + + boolean isValidUser = service.validateUser(name, password); + + if (!isValidUser) { + model.put("errorMessage", "Invalid Credentials"); + return "login"; + } + + model.put("name", name); + model.put("password", password); + + return "welcome"; + } + +} +``` +--- +### src/main/java/com/in28minutes/springboot/web/controller/TodoController.java + +```java +package com.in28minutes.springboot.web.controller; + +import java.util.Date; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import org.springframework.ui.ModelMap; +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.SessionAttributes; + +import com.in28minutes.springboot.web.service.LoginService; +import com.in28minutes.springboot.web.service.TodoService; + +@Controller +@SessionAttributes("name") +public class TodoController { + + @Autowired + TodoService service; + + @RequestMapping(value="/list-todos", method = RequestMethod.GET) + public String showTodos(ModelMap model){ + String name = (String) model.get("name"); + model.put("todos", service.retrieveTodos(name)); + return "list-todos"; + } + + @RequestMapping(value="/add-todo", method = RequestMethod.GET) + public String showAddTodoPage(ModelMap model){ + return "todo"; + } + + @RequestMapping(value="/add-todo", method = RequestMethod.POST) + public String addTodo(ModelMap model, @RequestParam String desc){ + service.addTodo((String) model.get("name"), desc, new Date(), false); + return "redirect:/list-todos"; + } + +} +``` +--- +### src/main/java/com/in28minutes/springboot/web/model/Todo.java + +```java +package com.in28minutes.springboot.web.model; + +import java.util.Date; + +public class Todo { + private int id; + private String user; + private String desc; + private Date targetDate; + private boolean isDone; + + public Todo(int id, String user, String desc, Date targetDate, + boolean isDone) { + super(); + this.id = id; + this.user = user; + this.desc = desc; + this.targetDate = targetDate; + this.isDone = isDone; + } + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public String getUser() { + return user; + } + + public void setUser(String user) { + this.user = user; + } + + public String getDesc() { + return desc; + } + + public void setDesc(String desc) { + this.desc = desc; + } + + public Date getTargetDate() { + return targetDate; + } + + public void setTargetDate(Date targetDate) { + this.targetDate = targetDate; + } + + public boolean isDone() { + return isDone; + } + + public void setDone(boolean isDone) { + this.isDone = isDone; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + id; + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + Todo other = (Todo) obj; + if (id != other.id) { + return false; + } + return true; + } + + @Override + public String toString() { + return String.format( + "Todo [id=%s, user=%s, desc=%s, targetDate=%s, isDone=%s]", id, + user, desc, targetDate, isDone); + } + +} +``` +--- +### src/main/java/com/in28minutes/springboot/web/service/LoginService.java + +```java +package com.in28minutes.springboot.web.service; + +import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; + +@Service +public class LoginService { + + public boolean validateUser(String userid, String password) { + // in28minutes, dummy + return userid.equalsIgnoreCase("in28minutes") + && password.equalsIgnoreCase("dummy"); + } + +} +``` +--- +### src/main/java/com/in28minutes/springboot/web/service/TodoService.java + +```java +package com.in28minutes.springboot.web.service; + +import java.util.ArrayList; +import java.util.Date; +import java.util.Iterator; +import java.util.List; + +import org.springframework.stereotype.Service; + +import com.in28minutes.springboot.web.model.Todo; + +@Service +public class TodoService { + private static List todos = new ArrayList(); + private static int todoCount = 3; + + static { + todos.add(new Todo(1, "in28Minutes", "Learn Spring MVC", new Date(), + false)); + todos.add(new Todo(2, "in28Minutes", "Learn Struts", new Date(), false)); + todos.add(new Todo(3, "in28Minutes", "Learn Hibernate", new Date(), + false)); + } + + public List retrieveTodos(String user) { + List filteredTodos = new ArrayList(); + for (Todo todo : todos) { + if (todo.getUser().equals(user)) { + filteredTodos.add(todo); + } + } + return filteredTodos; + } + + public void addTodo(String name, String desc, Date targetDate, + boolean isDone) { + todos.add(new Todo(++todoCount, name, desc, targetDate, isDone)); + } + + public void deleteTodo(int id) { + Iterator iterator = todos.iterator(); + while (iterator.hasNext()) { + Todo todo = iterator.next(); + if (todo.getId() == id) { + iterator.remove(); + } + } + } +} +``` +--- +### src/main/java/com/in28minutes/springboot/web/SpringBootFirstWebApplication.java + +```java +package com.in28minutes.springboot.web; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.ComponentScan; + +@SpringBootApplication +@ComponentScan("com.in28minutes.springboot.web") +public class SpringBootFirstWebApplication { + + public static void main(String[] args) { + SpringApplication.run(SpringBootFirstWebApplication.class, args); + } +} +``` +--- +### src/main/resources/application.properties + +``` +spring.mvc.view.prefix=/WEB-INF/jsp/ +spring.mvc.view.suffix=.jsp +logging.level.org.springframework.web=DEBUG +``` +--- +### src/main/webapp/WEB-INF/jsp/list-todos.jsp + +``` + + + +First Web Application + + + + Here are the list of ${name}'s todos: + ${todos}. +
+ Add a Todo + + + +``` +--- +### src/main/webapp/WEB-INF/jsp/login.jsp + +``` + + + +First Web Application + + + + ${errorMessage} +
+ Name : + Password : + +
+ + + +``` +--- +### src/main/webapp/WEB-INF/jsp/todo.jsp + +``` + + + +First Web Application + + + +ADD Todo Page for ${name} + +
+ Description : + +
+ + + + +``` +--- +### src/main/webapp/WEB-INF/jsp/welcome.jsp + +``` + + + +First Web Application + + + + Welcome ${name}!! Click here to manage your todo's. + + + +``` +--- +### src/test/java/com/in28minutes/springboot/web/SpringBootFirstWebApplicationTests.java + +```java +package com.in28minutes.springboot.web; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; + +@RunWith(SpringRunner.class) +@SpringBootTest +public class SpringBootFirstWebApplicationTests { + + @Test + public void contextLoads() { + } + +} +``` +--- +### todo.txt + +``` +LoginController -> adds name to model +welcome.jsp -> shows ${name} + +TodoController -> redirects to list-todos.jsp +${name} is empty + + +Component, Service, Repository, Controller +Autowired +ComponentScan + +Field dummyService in com.in28minutes.springboot.web.controller.LoginController +required a bean of type 'com.in28minutes.dummy.DummyService' +that could not be found. + +Spring Boot Starter Parent +Spring Boot Starter Web +@SpringBootApplication +Auto Configuration + +Dispatcher Servlet + +/login => "login" + +"login" => src/main/webapp/WEB-INF/jsp/login.jsp + + +Search for a view named "login" + + + +/login => LoginController +``` +--- diff --git a/02.Spring-Boot-Web-Application/Step13.zip b/02.Spring-Boot-Web-Application/Step13.zip new file mode 100644 index 0000000000000000000000000000000000000000..2a78a2af292015b0f812522f3d0159a00ca5c235 GIT binary patch literal 19565 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 lPZ|uIK=ZJEG9a+IXDV +- Add Dependency for jstl + +## Snippet +``` + + javax.servlet + jstl + +``` diff --git a/02.Spring-Boot-Web-Application/Step15.md b/02.Spring-Boot-Web-Application/Step15.md new file mode 100644 index 0000000..0542599 --- /dev/null +++ b/02.Spring-Boot-Web-Application/Step15.md @@ -0,0 +1,566 @@ +## What we will do: +- Add bootstrap to give basic formatting to the page : We use bootstrap classes container,table and table-striped. +- We will use webjars + - Already auto configured by Spring Boot : o.s.w.s.handler.SimpleUrlHandlerMapping : Mapped URL path [/webjars/**] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler] + +## Useful Snippets +``` + + org.webjars + bootstrap + 3.3.6 + + + org.webjars + jquery + 1.9.1 + + + + + + + + +``` + +## Files List + +### pom.xml + +```xml + + + 4.0.0 + + com.in28minutes.springboot.web + spring-boot-first-web-application + 0.0.1-SNAPSHOT + jar + + spring-boot-first-web-application + Demo project for Spring Boot + + + org.springframework.boot + spring-boot-starter-parent + 1.4.3.RELEASE + + + + + UTF-8 + UTF-8 + 1.8 + + + + + org.springframework.boot + spring-boot-starter-web + + + + javax.servlet + jstl + + + + org.webjars + bootstrap + 3.3.6 + + + + org.webjars + jquery + 1.9.1 + + + + org.apache.tomcat.embed + tomcat-embed-jasper + provided + + + + org.springframework.boot + spring-boot-devtools + runtime + + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + +``` +--- +### src/main/java/com/in28minutes/springboot/web/controller/LoginController.java + +```java +package com.in28minutes.springboot.web.controller; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import org.springframework.ui.ModelMap; +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.SessionAttributes; + +import com.in28minutes.springboot.web.service.LoginService; + +@Controller +@SessionAttributes("name") +public class LoginController { + + @Autowired + LoginService service; + + @RequestMapping(value="/login", method = RequestMethod.GET) + public String showLoginPage(ModelMap model){ + return "login"; + } + + @RequestMapping(value="/login", method = RequestMethod.POST) + public String showWelcomePage(ModelMap model, @RequestParam String name, @RequestParam String password){ + + boolean isValidUser = service.validateUser(name, password); + + if (!isValidUser) { + model.put("errorMessage", "Invalid Credentials"); + return "login"; + } + + model.put("name", name); + model.put("password", password); + + return "welcome"; + } + +} +``` +--- +### src/main/java/com/in28minutes/springboot/web/controller/TodoController.java + +```java +package com.in28minutes.springboot.web.controller; + +import java.util.Date; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import org.springframework.ui.ModelMap; +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.SessionAttributes; + +import com.in28minutes.springboot.web.service.LoginService; +import com.in28minutes.springboot.web.service.TodoService; + +@Controller +@SessionAttributes("name") +public class TodoController { + + @Autowired + TodoService service; + + @RequestMapping(value="/list-todos", method = RequestMethod.GET) + public String showTodos(ModelMap model){ + String name = (String) model.get("name"); + model.put("todos", service.retrieveTodos(name)); + return "list-todos"; + } + + @RequestMapping(value="/add-todo", method = RequestMethod.GET) + public String showAddTodoPage(ModelMap model){ + return "todo"; + } + + @RequestMapping(value="/add-todo", method = RequestMethod.POST) + public String addTodo(ModelMap model, @RequestParam String desc){ + service.addTodo((String) model.get("name"), desc, new Date(), false); + return "redirect:/list-todos"; + } + +} +``` +--- +### src/main/java/com/in28minutes/springboot/web/model/Todo.java + +```java +package com.in28minutes.springboot.web.model; + +import java.util.Date; + +public class Todo { + private int id; + private String user; + private String desc; + private Date targetDate; + private boolean isDone; + + public Todo(int id, String user, String desc, Date targetDate, + boolean isDone) { + super(); + this.id = id; + this.user = user; + this.desc = desc; + this.targetDate = targetDate; + this.isDone = isDone; + } + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public String getUser() { + return user; + } + + public void setUser(String user) { + this.user = user; + } + + public String getDesc() { + return desc; + } + + public void setDesc(String desc) { + this.desc = desc; + } + + public Date getTargetDate() { + return targetDate; + } + + public void setTargetDate(Date targetDate) { + this.targetDate = targetDate; + } + + public boolean isDone() { + return isDone; + } + + public void setDone(boolean isDone) { + this.isDone = isDone; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + id; + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + Todo other = (Todo) obj; + if (id != other.id) { + return false; + } + return true; + } + + @Override + public String toString() { + return String.format( + "Todo [id=%s, user=%s, desc=%s, targetDate=%s, isDone=%s]", id, + user, desc, targetDate, isDone); + } + +} +``` +--- +### src/main/java/com/in28minutes/springboot/web/service/LoginService.java + +```java +package com.in28minutes.springboot.web.service; + +import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; + +@Service +public class LoginService { + + public boolean validateUser(String userid, String password) { + // in28minutes, dummy + return userid.equalsIgnoreCase("in28minutes") + && password.equalsIgnoreCase("dummy"); + } + +} +``` +--- +### src/main/java/com/in28minutes/springboot/web/service/TodoService.java + +```java +package com.in28minutes.springboot.web.service; + +import java.util.ArrayList; +import java.util.Date; +import java.util.Iterator; +import java.util.List; + +import org.springframework.stereotype.Service; + +import com.in28minutes.springboot.web.model.Todo; + +@Service +public class TodoService { + private static List todos = new ArrayList(); + private static int todoCount = 3; + + static { + todos.add(new Todo(1, "in28Minutes", "Learn Spring MVC", new Date(), + false)); + todos.add(new Todo(2, "in28Minutes", "Learn Struts", new Date(), false)); + todos.add(new Todo(3, "in28Minutes", "Learn Hibernate", new Date(), + false)); + } + + public List retrieveTodos(String user) { + List filteredTodos = new ArrayList(); + for (Todo todo : todos) { + if (todo.getUser().equals(user)) { + filteredTodos.add(todo); + } + } + return filteredTodos; + } + + public void addTodo(String name, String desc, Date targetDate, + boolean isDone) { + todos.add(new Todo(++todoCount, name, desc, targetDate, isDone)); + } + + public void deleteTodo(int id) { + Iterator iterator = todos.iterator(); + while (iterator.hasNext()) { + Todo todo = iterator.next(); + if (todo.getId() == id) { + iterator.remove(); + } + } + } +} +``` +--- +### src/main/java/com/in28minutes/springboot/web/SpringBootFirstWebApplication.java + +```java +package com.in28minutes.springboot.web; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.ComponentScan; + +@SpringBootApplication +@ComponentScan("com.in28minutes.springboot.web") +public class SpringBootFirstWebApplication { + + public static void main(String[] args) { + SpringApplication.run(SpringBootFirstWebApplication.class, args); + } +} +``` +--- +### src/main/resources/application.properties + +``` +spring.mvc.view.prefix=/WEB-INF/jsp/ +spring.mvc.view.suffix=.jsp +logging.level.org.springframework.web=INFO +``` +--- +### src/main/webapp/WEB-INF/jsp/list-todos.jsp + +``` +<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%> + + + + +Todo's for ${name} + + + + +
+ + + + + + + + + + + + + + + + + + +
Your todos are
DescriptionTarget DateIs it Done?
${todo.desc}${todo.targetDate}${todo.done}
+ + + + +
+ + + +``` +--- +### src/main/webapp/WEB-INF/jsp/login.jsp + +``` + + + +First Web Application + + + + ${errorMessage} +
+ Name : + Password : + +
+ + + +``` +--- +### src/main/webapp/WEB-INF/jsp/todo.jsp + +``` + + + +First Web Application + + + +ADD Todo Page for ${name} + +
+ Description : + +
+ + + + +``` +--- +### src/main/webapp/WEB-INF/jsp/welcome.jsp + +``` + + + +First Web Application + + + + Welcome ${name}!! Click here to manage your todo's. + + + +``` +--- +### src/test/java/com/in28minutes/springboot/web/SpringBootFirstWebApplicationTests.java + +```java +package com.in28minutes.springboot.web; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; + +@RunWith(SpringRunner.class) +@SpringBootTest +public class SpringBootFirstWebApplicationTests { + + @Test + public void contextLoads() { + } + +} +``` +--- +### todo.txt + +``` +LoginController -> adds name to model +welcome.jsp -> shows ${name} + +TodoController -> redirects to list-todos.jsp +${name} is empty + + +Component, Service, Repository, Controller +Autowired +ComponentScan + +Field dummyService in com.in28minutes.springboot.web.controller.LoginController +required a bean of type 'com.in28minutes.dummy.DummyService' +that could not be found. + +Spring Boot Starter Parent +Spring Boot Starter Web +@SpringBootApplication +Auto Configuration + +Dispatcher Servlet + +/login => "login" + +"login" => src/main/webapp/WEB-INF/jsp/login.jsp + + +Search for a view named "login" + + + +/login => LoginController +``` +--- diff --git a/02.Spring-Boot-Web-Application/Step15.zip b/02.Spring-Boot-Web-Application/Step15.zip new file mode 100644 index 0000000000000000000000000000000000000000..14da314e22c8efe3baf99d3227f0bbbe2d1d2aa0 GIT binary patch literal 19918 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 +``` +## Files List + +### pom.xml + +```xml + + + 4.0.0 + + com.in28minutes.springboot.web + spring-boot-first-web-application + 0.0.1-SNAPSHOT + jar + + spring-boot-first-web-application + Demo project for Spring Boot + + + org.springframework.boot + spring-boot-starter-parent + 1.4.3.RELEASE + + + + + UTF-8 + UTF-8 + 1.8 + + + + + org.springframework.boot + spring-boot-starter-web + + + + javax.servlet + jstl + + + + org.webjars + bootstrap + 3.3.6 + + + + org.webjars + jquery + 1.9.1 + + + + org.apache.tomcat.embed + tomcat-embed-jasper + provided + + + + org.springframework.boot + spring-boot-devtools + runtime + + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + +``` +--- +### src/main/java/com/in28minutes/springboot/web/controller/LoginController.java + +```java +package com.in28minutes.springboot.web.controller; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import org.springframework.ui.ModelMap; +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.SessionAttributes; + +import com.in28minutes.springboot.web.service.LoginService; + +@Controller +@SessionAttributes("name") +public class LoginController { + + @Autowired + LoginService service; + + @RequestMapping(value="/login", method = RequestMethod.GET) + public String showLoginPage(ModelMap model){ + return "login"; + } + + @RequestMapping(value="/login", method = RequestMethod.POST) + public String showWelcomePage(ModelMap model, @RequestParam String name, @RequestParam String password){ + + boolean isValidUser = service.validateUser(name, password); + + if (!isValidUser) { + model.put("errorMessage", "Invalid Credentials"); + return "login"; + } + + model.put("name", name); + model.put("password", password); + + return "welcome"; + } + +} +``` +--- +### src/main/java/com/in28minutes/springboot/web/controller/TodoController.java + +```java +package com.in28minutes.springboot.web.controller; + +import java.util.Date; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import org.springframework.ui.ModelMap; +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.SessionAttributes; + +import com.in28minutes.springboot.web.service.LoginService; +import com.in28minutes.springboot.web.service.TodoService; + +@Controller +@SessionAttributes("name") +public class TodoController { + + @Autowired + TodoService service; + + @RequestMapping(value="/list-todos", method = RequestMethod.GET) + public String showTodos(ModelMap model){ + String name = (String) model.get("name"); + model.put("todos", service.retrieveTodos(name)); + return "list-todos"; + } + + @RequestMapping(value="/add-todo", method = RequestMethod.GET) + public String showAddTodoPage(ModelMap model){ + return "todo"; + } + + @RequestMapping(value="/delete-todo", method = RequestMethod.GET) + public String deleteTodo(@RequestParam int id){ + service.deleteTodo(id); + return "redirect:/list-todos"; + } + + @RequestMapping(value="/add-todo", method = RequestMethod.POST) + public String addTodo(ModelMap model, @RequestParam String desc){ + service.addTodo((String) model.get("name"), desc, new Date(), false); + return "redirect:/list-todos"; + } + +} +``` +--- +### src/main/java/com/in28minutes/springboot/web/model/Todo.java + +```java +package com.in28minutes.springboot.web.model; + +import java.util.Date; + +public class Todo { + private int id; + private String user; + private String desc; + private Date targetDate; + private boolean isDone; + + public Todo(int id, String user, String desc, Date targetDate, + boolean isDone) { + super(); + this.id = id; + this.user = user; + this.desc = desc; + this.targetDate = targetDate; + this.isDone = isDone; + } + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public String getUser() { + return user; + } + + public void setUser(String user) { + this.user = user; + } + + public String getDesc() { + return desc; + } + + public void setDesc(String desc) { + this.desc = desc; + } + + public Date getTargetDate() { + return targetDate; + } + + public void setTargetDate(Date targetDate) { + this.targetDate = targetDate; + } + + public boolean isDone() { + return isDone; + } + + public void setDone(boolean isDone) { + this.isDone = isDone; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + id; + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + Todo other = (Todo) obj; + if (id != other.id) { + return false; + } + return true; + } + + @Override + public String toString() { + return String.format( + "Todo [id=%s, user=%s, desc=%s, targetDate=%s, isDone=%s]", id, + user, desc, targetDate, isDone); + } + +} +``` +--- +### src/main/java/com/in28minutes/springboot/web/service/LoginService.java + +```java +package com.in28minutes.springboot.web.service; + +import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; + +@Service +public class LoginService { + + public boolean validateUser(String userid, String password) { + // in28minutes, dummy + return userid.equalsIgnoreCase("in28minutes") + && password.equalsIgnoreCase("dummy"); + } + +} +``` +--- +### src/main/java/com/in28minutes/springboot/web/service/TodoService.java + +```java +package com.in28minutes.springboot.web.service; + +import java.util.ArrayList; +import java.util.Date; +import java.util.Iterator; +import java.util.List; + +import org.springframework.stereotype.Service; + +import com.in28minutes.springboot.web.model.Todo; + +@Service +public class TodoService { + private static List todos = new ArrayList(); + private static int todoCount = 3; + + static { + todos.add(new Todo(1, "in28Minutes", "Learn Spring MVC", new Date(), + false)); + todos.add(new Todo(2, "in28Minutes", "Learn Struts", new Date(), false)); + todos.add(new Todo(3, "in28Minutes", "Learn Hibernate", new Date(), + false)); + } + + public List retrieveTodos(String user) { + List filteredTodos = new ArrayList(); + for (Todo todo : todos) { + if (todo.getUser().equals(user)) { + filteredTodos.add(todo); + } + } + return filteredTodos; + } + + public void addTodo(String name, String desc, Date targetDate, + boolean isDone) { + todos.add(new Todo(++todoCount, name, desc, targetDate, isDone)); + } + + public void deleteTodo(int id) { + Iterator iterator = todos.iterator(); + while (iterator.hasNext()) { + Todo todo = iterator.next(); + if (todo.getId() == id) { + iterator.remove(); + } + } + } +} +``` +--- +### src/main/java/com/in28minutes/springboot/web/SpringBootFirstWebApplication.java + +```java +package com.in28minutes.springboot.web; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.ComponentScan; + +@SpringBootApplication +@ComponentScan("com.in28minutes.springboot.web") +public class SpringBootFirstWebApplication { + + public static void main(String[] args) { + SpringApplication.run(SpringBootFirstWebApplication.class, args); + } +} +``` +--- +### src/main/resources/application.properties + +``` +spring.mvc.view.prefix=/WEB-INF/jsp/ +spring.mvc.view.suffix=.jsp +logging.level.org.springframework.web=INFO +``` +--- +### src/main/webapp/WEB-INF/jsp/list-todos.jsp + +``` +<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%> + + + + +Todo's for ${name} + + + + +
+ + + + + + + + + + + + + + + + + + + + +
Your todos are
DescriptionTarget DateIs it Done?
${todo.desc}${todo.targetDate}${todo.done}Delete
+ + + + +
+ + + +``` +--- +### src/main/webapp/WEB-INF/jsp/login.jsp + +``` + + + +First Web Application + + + + ${errorMessage} +
+ Name : + Password : + +
+ + + +``` +--- +### src/main/webapp/WEB-INF/jsp/todo.jsp + +``` + + + +First Web Application + + + +ADD Todo Page for ${name} + +
+ Description : + +
+ + + + +``` +--- +### src/main/webapp/WEB-INF/jsp/welcome.jsp + +``` + + + +First Web Application + + + + Welcome ${name}!! Click here to manage your todo's. + + + +``` +--- +### src/test/java/com/in28minutes/springboot/web/SpringBootFirstWebApplicationTests.java + +```java +package com.in28minutes.springboot.web; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; + +@RunWith(SpringRunner.class) +@SpringBootTest +public class SpringBootFirstWebApplicationTests { + + @Test + public void contextLoads() { + } + +} +``` +--- +### todo.txt + +``` +LoginController -> adds name to model +welcome.jsp -> shows ${name} + +TodoController -> redirects to list-todos.jsp +${name} is empty + + +Component, Service, Repository, Controller +Autowired +ComponentScan + +Field dummyService in com.in28minutes.springboot.web.controller.LoginController +required a bean of type 'com.in28minutes.dummy.DummyService' +that could not be found. + +Spring Boot Starter Parent +Spring Boot Starter Web +@SpringBootApplication +Auto Configuration + +Dispatcher Servlet + +/login => "login" + +"login" => src/main/webapp/WEB-INF/jsp/login.jsp + + +Search for a view named "login" + + + +/login => LoginController +``` +--- diff --git a/02.Spring-Boot-Web-Application/Step16.zip b/02.Spring-Boot-Web-Application/Step16.zip new file mode 100644 index 0000000000000000000000000000000000000000..603e6c837235a6d07d67e6eb6b557ab68cbfbfdb GIT binary patch literal 19990 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 literal 0 HcmV?d00001 diff --git a/02.Spring-Boot-Web-Application/Step17.md b/02.Spring-Boot-Web-Application/Step17.md new file mode 100644 index 0000000..d6a1cc6 --- /dev/null +++ b/02.Spring-Boot-Web-Application/Step17.md @@ -0,0 +1,576 @@ +## What we will do: +In this short step: +- Format Add Todo Page +- Add Html5 Form Validations + +## Useful Snippets +``` +
+ + +
+ +``` + +## Files List + +### pom.xml + +```xml + + + 4.0.0 + + com.in28minutes.springboot.web + spring-boot-first-web-application + 0.0.1-SNAPSHOT + jar + + spring-boot-first-web-application + Demo project for Spring Boot + + + org.springframework.boot + spring-boot-starter-parent + 1.4.3.RELEASE + + + + + UTF-8 + UTF-8 + 1.8 + + + + + org.springframework.boot + spring-boot-starter-web + + + + javax.servlet + jstl + + + + org.webjars + bootstrap + 3.3.6 + + + + org.webjars + jquery + 1.9.1 + + + + org.apache.tomcat.embed + tomcat-embed-jasper + provided + + + + org.springframework.boot + spring-boot-devtools + runtime + + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + +``` +--- +### src/main/java/com/in28minutes/springboot/web/controller/LoginController.java + +```java +package com.in28minutes.springboot.web.controller; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import org.springframework.ui.ModelMap; +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.SessionAttributes; + +import com.in28minutes.springboot.web.service.LoginService; + +@Controller +@SessionAttributes("name") +public class LoginController { + + @Autowired + LoginService service; + + @RequestMapping(value="/login", method = RequestMethod.GET) + public String showLoginPage(ModelMap model){ + return "login"; + } + + @RequestMapping(value="/login", method = RequestMethod.POST) + public String showWelcomePage(ModelMap model, @RequestParam String name, @RequestParam String password){ + + boolean isValidUser = service.validateUser(name, password); + + if (!isValidUser) { + model.put("errorMessage", "Invalid Credentials"); + return "login"; + } + + model.put("name", name); + model.put("password", password); + + return "welcome"; + } + +} +``` +--- +### src/main/java/com/in28minutes/springboot/web/controller/TodoController.java + +```java +package com.in28minutes.springboot.web.controller; + +import java.util.Date; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import org.springframework.ui.ModelMap; +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.SessionAttributes; + +import com.in28minutes.springboot.web.service.LoginService; +import com.in28minutes.springboot.web.service.TodoService; + +@Controller +@SessionAttributes("name") +public class TodoController { + + @Autowired + TodoService service; + + @RequestMapping(value="/list-todos", method = RequestMethod.GET) + public String showTodos(ModelMap model){ + String name = (String) model.get("name"); + model.put("todos", service.retrieveTodos(name)); + return "list-todos"; + } + + @RequestMapping(value="/add-todo", method = RequestMethod.GET) + public String showAddTodoPage(ModelMap model){ + return "todo"; + } + + @RequestMapping(value="/delete-todo", method = RequestMethod.GET) + public String deleteTodo(@RequestParam int id){ + service.deleteTodo(id); + return "redirect:/list-todos"; + } + + @RequestMapping(value="/add-todo", method = RequestMethod.POST) + public String addTodo(ModelMap model, @RequestParam String desc){ + service.addTodo((String) model.get("name"), desc, new Date(), false); + return "redirect:/list-todos"; + } + +} +``` +--- +### src/main/java/com/in28minutes/springboot/web/model/Todo.java + +```java +package com.in28minutes.springboot.web.model; + +import java.util.Date; + +public class Todo { + private int id; + private String user; + private String desc; + private Date targetDate; + private boolean isDone; + + public Todo(int id, String user, String desc, Date targetDate, + boolean isDone) { + super(); + this.id = id; + this.user = user; + this.desc = desc; + this.targetDate = targetDate; + this.isDone = isDone; + } + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public String getUser() { + return user; + } + + public void setUser(String user) { + this.user = user; + } + + public String getDesc() { + return desc; + } + + public void setDesc(String desc) { + this.desc = desc; + } + + public Date getTargetDate() { + return targetDate; + } + + public void setTargetDate(Date targetDate) { + this.targetDate = targetDate; + } + + public boolean isDone() { + return isDone; + } + + public void setDone(boolean isDone) { + this.isDone = isDone; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + id; + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + Todo other = (Todo) obj; + if (id != other.id) { + return false; + } + return true; + } + + @Override + public String toString() { + return String.format( + "Todo [id=%s, user=%s, desc=%s, targetDate=%s, isDone=%s]", id, + user, desc, targetDate, isDone); + } + +} +``` +--- +### src/main/java/com/in28minutes/springboot/web/service/LoginService.java + +```java +package com.in28minutes.springboot.web.service; + +import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; + +@Service +public class LoginService { + + public boolean validateUser(String userid, String password) { + // in28minutes, dummy + return userid.equalsIgnoreCase("in28minutes") + && password.equalsIgnoreCase("dummy"); + } + +} +``` +--- +### src/main/java/com/in28minutes/springboot/web/service/TodoService.java + +```java +package com.in28minutes.springboot.web.service; + +import java.util.ArrayList; +import java.util.Date; +import java.util.Iterator; +import java.util.List; + +import org.springframework.stereotype.Service; + +import com.in28minutes.springboot.web.model.Todo; + +@Service +public class TodoService { + private static List todos = new ArrayList(); + private static int todoCount = 3; + + static { + todos.add(new Todo(1, "in28Minutes", "Learn Spring MVC", new Date(), + false)); + todos.add(new Todo(2, "in28Minutes", "Learn Struts", new Date(), false)); + todos.add(new Todo(3, "in28Minutes", "Learn Hibernate", new Date(), + false)); + } + + public List retrieveTodos(String user) { + List filteredTodos = new ArrayList(); + for (Todo todo : todos) { + if (todo.getUser().equals(user)) { + filteredTodos.add(todo); + } + } + return filteredTodos; + } + + public void addTodo(String name, String desc, Date targetDate, + boolean isDone) { + todos.add(new Todo(++todoCount, name, desc, targetDate, isDone)); + } + + public void deleteTodo(int id) { + Iterator iterator = todos.iterator(); + while (iterator.hasNext()) { + Todo todo = iterator.next(); + if (todo.getId() == id) { + iterator.remove(); + } + } + } +} +``` +--- +### src/main/java/com/in28minutes/springboot/web/SpringBootFirstWebApplication.java + +```java +package com.in28minutes.springboot.web; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.ComponentScan; + +@SpringBootApplication +@ComponentScan("com.in28minutes.springboot.web") +public class SpringBootFirstWebApplication { + + public static void main(String[] args) { + SpringApplication.run(SpringBootFirstWebApplication.class, args); + } +} +``` +--- +### src/main/resources/application.properties + +``` +spring.mvc.view.prefix=/WEB-INF/jsp/ +spring.mvc.view.suffix=.jsp +logging.level.org.springframework.web=INFO +``` +--- +### src/main/webapp/WEB-INF/jsp/list-todos.jsp + +``` +<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%> + + + + +Todo's for ${name} + + + + +
+ + + + + + + + + + + + + + + + + + + + +
Your todos are
DescriptionTarget DateIs it Done?
${todo.desc}${todo.targetDate}${todo.done}Delete
+ +
+ + + + + + + +``` +--- +### src/main/webapp/WEB-INF/jsp/login.jsp + +``` + + + +First Web Application + + + + ${errorMessage} +
+ Name : + Password : + +
+ + + +``` +--- +### src/main/webapp/WEB-INF/jsp/todo.jsp + +``` + + + +First Web Application + + + + + +
+
+
+ + +
+ + +
+
+ + + + + + + +``` +--- +### src/main/webapp/WEB-INF/jsp/welcome.jsp + +``` + + + +First Web Application + + + + Welcome ${name}!! Click here to manage your todo's. + + + +``` +--- +### src/test/java/com/in28minutes/springboot/web/SpringBootFirstWebApplicationTests.java + +```java +package com.in28minutes.springboot.web; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; + +@RunWith(SpringRunner.class) +@SpringBootTest +public class SpringBootFirstWebApplicationTests { + + @Test + public void contextLoads() { + } + +} +``` +--- +### todo.txt + +``` +LoginController -> adds name to model +welcome.jsp -> shows ${name} + +TodoController -> redirects to list-todos.jsp +${name} is empty + + +Component, Service, Repository, Controller +Autowired +ComponentScan + +Field dummyService in com.in28minutes.springboot.web.controller.LoginController +required a bean of type 'com.in28minutes.dummy.DummyService' +that could not be found. + +Spring Boot Starter Parent +Spring Boot Starter Web +@SpringBootApplication +Auto Configuration + +Dispatcher Servlet + +/login => "login" + +"login" => src/main/webapp/WEB-INF/jsp/login.jsp + + +Search for a view named "login" + + + +/login => LoginController +``` +--- diff --git a/02.Spring-Boot-Web-Application/Step18.md b/02.Spring-Boot-Web-Application/Step18.md new file mode 100644 index 0000000..c507ca6 --- /dev/null +++ b/02.Spring-Boot-Web-Application/Step18.md @@ -0,0 +1,626 @@ +## What we will do: +- Lets use a command bean for Todo +- Add Validations +- The JSR 303 and JSR 349 defines specification for the Bean Validation API (version 1.0 and 1.1, respectively), and Hibernate Validator is the reference implementation. +- org.hibernate:hibernate-validator + +## Useful Snippets +``` + <%@taglib uri="http://www.springframework.org/tags/form" prefix="form"%> + + +
+ Description + +
+
+ + @Size(min = 10, message = "Enter atleast 10 Characters.") + + @Valid Todo todo, BindingResult result + + if (result.hasErrors()) + return "todo"; + + + +``` +## Files List + +### pom.xml + +```xml + + + 4.0.0 + + com.in28minutes.springboot.web + spring-boot-first-web-application + 0.0.1-SNAPSHOT + jar + + spring-boot-first-web-application + Demo project for Spring Boot + + + org.springframework.boot + spring-boot-starter-parent + 1.4.3.RELEASE + + + + + UTF-8 + UTF-8 + 1.8 + + + + + org.springframework.boot + spring-boot-starter-web + + + + javax.servlet + jstl + + + + org.webjars + bootstrap + 3.3.6 + + + + org.webjars + jquery + 1.9.1 + + + + org.apache.tomcat.embed + tomcat-embed-jasper + provided + + + + org.springframework.boot + spring-boot-devtools + runtime + + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + +``` +--- +### src/main/java/com/in28minutes/springboot/web/controller/LoginController.java + +```java +package com.in28minutes.springboot.web.controller; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import org.springframework.ui.ModelMap; +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.SessionAttributes; + +import com.in28minutes.springboot.web.service.LoginService; + +@Controller +@SessionAttributes("name") +public class LoginController { + + @Autowired + LoginService service; + + @RequestMapping(value="/login", method = RequestMethod.GET) + public String showLoginPage(ModelMap model){ + return "login"; + } + + @RequestMapping(value="/login", method = RequestMethod.POST) + public String showWelcomePage(ModelMap model, @RequestParam String name, @RequestParam String password){ + + boolean isValidUser = service.validateUser(name, password); + + if (!isValidUser) { + model.put("errorMessage", "Invalid Credentials"); + return "login"; + } + + model.put("name", name); + model.put("password", password); + + return "welcome"; + } + +} +``` +--- +### src/main/java/com/in28minutes/springboot/web/controller/TodoController.java + +```java +package com.in28minutes.springboot.web.controller; + +import java.util.Date; + +import javax.validation.Valid; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import org.springframework.ui.ModelMap; +import org.springframework.validation.BindingResult; +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.SessionAttributes; + +import com.in28minutes.springboot.web.model.Todo; +import com.in28minutes.springboot.web.service.LoginService; +import com.in28minutes.springboot.web.service.TodoService; + +@Controller +@SessionAttributes("name") +public class TodoController { + + @Autowired + TodoService service; + + @RequestMapping(value = "/list-todos", method = RequestMethod.GET) + public String showTodos(ModelMap model) { + String name = (String) model.get("name"); + model.put("todos", service.retrieveTodos(name)); + return "list-todos"; + } + + @RequestMapping(value = "/add-todo", method = RequestMethod.GET) + public String showAddTodoPage(ModelMap model) { + model.addAttribute("todo", new Todo(0, (String) model.get("name"), "Default Desc", + new Date(), false)); + return "todo"; + } + + @RequestMapping(value = "/delete-todo", method = RequestMethod.GET) + public String deleteTodo(@RequestParam int id) { + service.deleteTodo(id); + return "redirect:/list-todos"; + } + + @RequestMapping(value = "/add-todo", method = RequestMethod.POST) + public String addTodo(ModelMap model, @Valid Todo todo, BindingResult result) { + + if(result.hasErrors()){ + return "todo"; + } + + service.addTodo((String) model.get("name"), todo.getDesc(), new Date(), + false); + return "redirect:/list-todos"; + } +} +``` +--- +### src/main/java/com/in28minutes/springboot/web/model/Todo.java + +```java +package com.in28minutes.springboot.web.model; + +import java.util.Date; + +import javax.validation.constraints.Size; + +public class Todo { + private int id; + private String user; + + @Size(min=10, message="Enter at least 10 Characters...") + private String desc; + + private Date targetDate; + private boolean isDone; + + public Todo() { + super(); + } + + public Todo(int id, String user, String desc, Date targetDate, + boolean isDone) { + super(); + this.id = id; + this.user = user; + this.desc = desc; + this.targetDate = targetDate; + this.isDone = isDone; + } + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public String getUser() { + return user; + } + + public void setUser(String user) { + this.user = user; + } + + public String getDesc() { + return desc; + } + + public void setDesc(String desc) { + this.desc = desc; + } + + public Date getTargetDate() { + return targetDate; + } + + public void setTargetDate(Date targetDate) { + this.targetDate = targetDate; + } + + public boolean isDone() { + return isDone; + } + + public void setDone(boolean isDone) { + this.isDone = isDone; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + id; + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + Todo other = (Todo) obj; + if (id != other.id) { + return false; + } + return true; + } + + @Override + public String toString() { + return String.format( + "Todo [id=%s, user=%s, desc=%s, targetDate=%s, isDone=%s]", id, + user, desc, targetDate, isDone); + } + +} +``` +--- +### src/main/java/com/in28minutes/springboot/web/service/LoginService.java + +```java +package com.in28minutes.springboot.web.service; + +import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; + +@Service +public class LoginService { + + public boolean validateUser(String userid, String password) { + // in28minutes, dummy + return userid.equalsIgnoreCase("in28minutes") + && password.equalsIgnoreCase("dummy"); + } + +} +``` +--- +### src/main/java/com/in28minutes/springboot/web/service/TodoService.java + +```java +package com.in28minutes.springboot.web.service; + +import java.util.ArrayList; +import java.util.Date; +import java.util.Iterator; +import java.util.List; + +import org.springframework.stereotype.Service; + +import com.in28minutes.springboot.web.model.Todo; + +@Service +public class TodoService { + private static List todos = new ArrayList(); + private static int todoCount = 3; + + static { + todos.add(new Todo(1, "in28Minutes", "Learn Spring MVC", new Date(), + false)); + todos.add(new Todo(2, "in28Minutes", "Learn Struts", new Date(), false)); + todos.add(new Todo(3, "in28Minutes", "Learn Hibernate", new Date(), + false)); + } + + public List retrieveTodos(String user) { + List filteredTodos = new ArrayList(); + for (Todo todo : todos) { + if (todo.getUser().equals(user)) { + filteredTodos.add(todo); + } + } + return filteredTodos; + } + + public void addTodo(String name, String desc, Date targetDate, + boolean isDone) { + todos.add(new Todo(++todoCount, name, desc, targetDate, isDone)); + } + + public void deleteTodo(int id) { + Iterator iterator = todos.iterator(); + while (iterator.hasNext()) { + Todo todo = iterator.next(); + if (todo.getId() == id) { + iterator.remove(); + } + } + } +} +``` +--- +### src/main/java/com/in28minutes/springboot/web/SpringBootFirstWebApplication.java + +```java +package com.in28minutes.springboot.web; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.ComponentScan; + +@SpringBootApplication +@ComponentScan("com.in28minutes.springboot.web") +public class SpringBootFirstWebApplication { + + public static void main(String[] args) { + SpringApplication.run(SpringBootFirstWebApplication.class, args); + } +} +``` +--- +### src/main/resources/application.properties + +``` +spring.mvc.view.prefix=/WEB-INF/jsp/ +spring.mvc.view.suffix=.jsp +logging.level.org.springframework.web=INFO +``` +--- +### src/main/webapp/WEB-INF/jsp/list-todos.jsp + +``` +<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%> + + + + +Todo's for ${name} + + + + +
+ + + + + + + + + + + + + + + + + + + + +
Your todos are
DescriptionTarget DateIs it Done?
${todo.desc}${todo.targetDate}${todo.done}Delete
+ +
+ + + + + + + +``` +--- +### src/main/webapp/WEB-INF/jsp/login.jsp + +``` + + + +First Web Application + + + + ${errorMessage} +
+ Name : + Password : + +
+ + + +``` +--- +### src/main/webapp/WEB-INF/jsp/todo.jsp + +``` +<%@taglib uri="http://www.springframework.org/tags/form" prefix="form"%> + + + +First Web Application + + + + + +
+ +
+ Description + + +
+ + +
+
+ + + + + + + +``` +--- +### src/main/webapp/WEB-INF/jsp/welcome.jsp + +``` + + + +First Web Application + + + + Welcome ${name}!! Click here to manage your todo's. + + + +``` +--- +### src/test/java/com/in28minutes/springboot/web/SpringBootFirstWebApplicationTests.java + +```java +package com.in28minutes.springboot.web; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; + +@RunWith(SpringRunner.class) +@SpringBootTest +public class SpringBootFirstWebApplicationTests { + + @Test + public void contextLoads() { + } + +} +``` +--- +### todo.txt + +``` +Implementing Server Side Validation +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Command Bean or Form Backing Bean + +Add Validation +Use Validation on Controller +Display Errors in View + +Command Bean +~~~~~~~~~~~~ +Controller +View - Spring Form Tags + + + +LoginController -> adds name to model +welcome.jsp -> shows ${name} + +TodoController -> redirects to list-todos.jsp +${name} is empty + + +Component, Service, Repository, Controller +Autowired +ComponentScan + +Field dummyService in com.in28minutes.springboot.web.controller.LoginController +required a bean of type 'com.in28minutes.dummy.DummyService' +that could not be found. + +Spring Boot Starter Parent +Spring Boot Starter Web +@SpringBootApplication +Auto Configuration + +Dispatcher Servlet + +/login => "login" + +"login" => src/main/webapp/WEB-INF/jsp/login.jsp + + +Search for a view named "login" + + + +/login => LoginController +``` +--- diff --git a/02.Spring-Boot-Web-Application/Step18.zip b/02.Spring-Boot-Web-Application/Step18.zip new file mode 100644 index 0000000000000000000000000000000000000000..dcbe6af9db9dd9ab7dfd0bf4ce386e5119f36216 GIT binary patch literal 20428 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 +``` + +## Files List + +### pom.xml + +```xml + + + 4.0.0 + + com.in28minutes.springboot.web + spring-boot-first-web-application + 0.0.1-SNAPSHOT + jar + + spring-boot-first-web-application + Demo project for Spring Boot + + + org.springframework.boot + spring-boot-starter-parent + 1.4.3.RELEASE + + + + + UTF-8 + UTF-8 + 1.8 + + + + + org.springframework.boot + spring-boot-starter-web + + + + javax.servlet + jstl + + + + org.webjars + bootstrap + 3.3.6 + + + + org.webjars + jquery + 1.9.1 + + + + org.apache.tomcat.embed + tomcat-embed-jasper + provided + + + + org.springframework.boot + spring-boot-devtools + runtime + + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + +``` +--- +### src/main/java/com/in28minutes/springboot/web/controller/LoginController.java + +```java +package com.in28minutes.springboot.web.controller; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import org.springframework.ui.ModelMap; +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.SessionAttributes; + +import com.in28minutes.springboot.web.service.LoginService; + +@Controller +@SessionAttributes("name") +public class LoginController { + + @Autowired + LoginService service; + + @RequestMapping(value="/login", method = RequestMethod.GET) + public String showLoginPage(ModelMap model){ + return "login"; + } + + @RequestMapping(value="/login", method = RequestMethod.POST) + public String showWelcomePage(ModelMap model, @RequestParam String name, @RequestParam String password){ + + boolean isValidUser = service.validateUser(name, password); + + if (!isValidUser) { + model.put("errorMessage", "Invalid Credentials"); + return "login"; + } + + model.put("name", name); + model.put("password", password); + + return "welcome"; + } + +} +``` +--- +### src/main/java/com/in28minutes/springboot/web/controller/TodoController.java + +```java +package com.in28minutes.springboot.web.controller; + +import java.util.Date; + +import javax.validation.Valid; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import org.springframework.ui.ModelMap; +import org.springframework.validation.BindingResult; +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.SessionAttributes; + +import com.in28minutes.springboot.web.model.Todo; +import com.in28minutes.springboot.web.service.LoginService; +import com.in28minutes.springboot.web.service.TodoService; + +@Controller +@SessionAttributes("name") +public class TodoController { + + @Autowired + TodoService service; + + @RequestMapping(value = "/list-todos", method = RequestMethod.GET) + public String showTodos(ModelMap model) { + String name = (String) model.get("name"); + model.put("todos", service.retrieveTodos(name)); + return "list-todos"; + } + + @RequestMapping(value = "/add-todo", method = RequestMethod.GET) + public String showAddTodoPage(ModelMap model) { + model.addAttribute("todo", new Todo(0, (String) model.get("name"), + "Default Desc", new Date(), false)); + return "todo"; + } + + @RequestMapping(value = "/delete-todo", method = RequestMethod.GET) + public String deleteTodo(@RequestParam int id) { + service.deleteTodo(id); + return "redirect:/list-todos"; + } + + @RequestMapping(value = "/update-todo", method = RequestMethod.GET) + public String showUpdateTodoPage(@RequestParam int id, ModelMap model) { + Todo todo = service.retrieveTodo(id); + model.put("todo", todo); + return "todo"; + } + + @RequestMapping(value = "/update-todo", method = RequestMethod.POST) + public String updateTodo(ModelMap model, @Valid Todo todo, BindingResult result) { + + if (result.hasErrors()) { + return "todo"; + } + + todo.setUser((String) model.get("name")); + + service.updateTodo(todo); + + return "redirect:/list-todos"; + } + + @RequestMapping(value = "/add-todo", method = RequestMethod.POST) + public String addTodo(ModelMap model, @Valid Todo todo, BindingResult result) { + + if (result.hasErrors()) { + return "todo"; + } + + service.addTodo((String) model.get("name"), todo.getDesc(), new Date(), + false); + return "redirect:/list-todos"; + } +} +``` +--- +### src/main/java/com/in28minutes/springboot/web/model/Todo.java + +```java +package com.in28minutes.springboot.web.model; + +import java.util.Date; + +import javax.validation.constraints.Size; + +public class Todo { + private int id; + private String user; + + @Size(min=10, message="Enter at least 10 Characters...") + private String desc; + + private Date targetDate; + private boolean isDone; + + public Todo() { + super(); + } + + public Todo(int id, String user, String desc, Date targetDate, + boolean isDone) { + super(); + this.id = id; + this.user = user; + this.desc = desc; + this.targetDate = targetDate; + this.isDone = isDone; + } + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public String getUser() { + return user; + } + + public void setUser(String user) { + this.user = user; + } + + public String getDesc() { + return desc; + } + + public void setDesc(String desc) { + this.desc = desc; + } + + public Date getTargetDate() { + return targetDate; + } + + public void setTargetDate(Date targetDate) { + this.targetDate = targetDate; + } + + public boolean isDone() { + return isDone; + } + + public void setDone(boolean isDone) { + this.isDone = isDone; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + id; + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + Todo other = (Todo) obj; + if (id != other.id) { + return false; + } + return true; + } + + @Override + public String toString() { + return String.format( + "Todo [id=%s, user=%s, desc=%s, targetDate=%s, isDone=%s]", id, + user, desc, targetDate, isDone); + } + +} +``` +--- +### src/main/java/com/in28minutes/springboot/web/service/LoginService.java + +```java +package com.in28minutes.springboot.web.service; + +import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; + +@Service +public class LoginService { + + public boolean validateUser(String userid, String password) { + // in28minutes, dummy + return userid.equalsIgnoreCase("in28minutes") + && password.equalsIgnoreCase("dummy"); + } + +} +``` +--- +### src/main/java/com/in28minutes/springboot/web/service/TodoService.java + +```java +package com.in28minutes.springboot.web.service; + +import java.util.ArrayList; +import java.util.Date; +import java.util.Iterator; +import java.util.List; + +import org.springframework.stereotype.Service; + +import com.in28minutes.springboot.web.model.Todo; + +@Service +public class TodoService { + private static List todos = new ArrayList(); + private static int todoCount = 3; + + static { + todos.add(new Todo(1, "in28Minutes", "Learn Spring MVC", new Date(), + false)); + todos.add(new Todo(2, "in28Minutes", "Learn Struts", new Date(), false)); + todos.add(new Todo(3, "in28Minutes", "Learn Hibernate", new Date(), + false)); + } + + public List retrieveTodos(String user) { + List filteredTodos = new ArrayList(); + for (Todo todo : todos) { + if (todo.getUser().equalsIgnoreCase(user)) { + filteredTodos.add(todo); + } + } + return filteredTodos; + } + + public Todo retrieveTodo(int id) { + for (Todo todo : todos) { + if (todo.getId()==id) { + return todo; + } + } + return null; + } + + public void updateTodo(Todo todo){ + todos.remove(todo); + todos.add(todo); + } + + public void addTodo(String name, String desc, Date targetDate, + boolean isDone) { + todos.add(new Todo(++todoCount, name, desc, targetDate, isDone)); + } + + public void deleteTodo(int id) { + Iterator iterator = todos.iterator(); + while (iterator.hasNext()) { + Todo todo = iterator.next(); + if (todo.getId() == id) { + iterator.remove(); + } + } + } +} +``` +--- +### src/main/java/com/in28minutes/springboot/web/SpringBootFirstWebApplication.java + +```java +package com.in28minutes.springboot.web; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.ComponentScan; + +@SpringBootApplication +@ComponentScan("com.in28minutes.springboot.web") +public class SpringBootFirstWebApplication { + + public static void main(String[] args) { + SpringApplication.run(SpringBootFirstWebApplication.class, args); + } +} +``` +--- +### src/main/resources/application.properties + +``` +spring.mvc.view.prefix=/WEB-INF/jsp/ +spring.mvc.view.suffix=.jsp +logging.level.org.springframework.web=INFO +``` +--- +### src/main/webapp/WEB-INF/jsp/list-todos.jsp + +``` +<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%> + + + + +Todo's for ${name} + + + + +
+ + + + + + + + + + + + + + + + + + + + + + +
Your todos are
DescriptionTarget DateIs it Done?
${todo.desc}${todo.targetDate}${todo.done}UpdateDelete
+ +
+ + + + + + + +``` +--- +### src/main/webapp/WEB-INF/jsp/login.jsp + +``` + + + +First Web Application + + + + ${errorMessage} +
+ Name : + Password : + +
+ + + +``` +--- +### src/main/webapp/WEB-INF/jsp/todo.jsp + +``` +<%@taglib uri="http://www.springframework.org/tags/form" prefix="form"%> + + + +First Web Application + + + + + +
+ + + + +
+ Description + + +
+ + +
+
+ + + + + + + +``` +--- +### src/main/webapp/WEB-INF/jsp/welcome.jsp + +``` + + + +First Web Application + + + + Welcome ${name}!! Click here to manage your todo's. + + + +``` +--- +### src/test/java/com/in28minutes/springboot/web/SpringBootFirstWebApplicationTests.java + +```java +package com.in28minutes.springboot.web; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; + +@RunWith(SpringRunner.class) +@SpringBootTest +public class SpringBootFirstWebApplicationTests { + + @Test + public void contextLoads() { + } + +} +``` +--- +### todo.txt + +``` +Implementing Server Side Validation +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Command Bean or Form Backing Bean + +Add Validation +Use Validation on Controller +Display Errors in View + +Command Bean +~~~~~~~~~~~~ +Controller +View - Spring Form Tags + + + +LoginController -> adds name to model +welcome.jsp -> shows ${name} + +TodoController -> redirects to list-todos.jsp +${name} is empty + + +Component, Service, Repository, Controller +Autowired +ComponentScan + +Field dummyService in com.in28minutes.springboot.web.controller.LoginController +required a bean of type 'com.in28minutes.dummy.DummyService' +that could not be found. + +Spring Boot Starter Parent +Spring Boot Starter Web +@SpringBootApplication +Auto Configuration + +Dispatcher Servlet + +/login => "login" + +"login" => src/main/webapp/WEB-INF/jsp/login.jsp + + +Search for a view named "login" + + + +/login => LoginController +``` +--- diff --git a/02.Spring-Boot-Web-Application/Step19.zip b/02.Spring-Boot-Web-Application/Step19.zip new file mode 100644 index 0000000000000000000000000000000000000000..d3b9fdf9cab61fa7362622840c60e7e1f92485d3 GIT binary patch literal 20595 zcmdsecR1Dm`#&jCR@otxy^>iZnc16=J;QO1>=0R%k&zve8Ie(hu0x*ZckBa&`5`^Kp;oec#V}y|h#bHdEnkMgF#26j#9m{Se_%;JF~2 z1-zYM`i3NUZM|m=FMJO*{DhB(_ekFq(I!ZB8txzZta{Qjl|s!Zk2U;Acc(m2f4h2b zv#AwBk3)4p%Y`>04oADySz@pDrVhBZEX}C9-VxYdKKOVW`@a4i^pDRss(p}c-q%09 zm@!lRS?Iz}=T|}YWcbG25ua-hp4M=9mT0mw&uHO6n2?)~m#G<5e`_Dd~hE9bes`rSt#M75oW ziRT%96dmI+Ce7~tgckn>G{&)~Caou>^aRg1*5(hGE=iav{El9ELU4%A25 zk`vm_a6G<<01aV&o|@6V{!_miP-0XVy1#| zFJ2dYP%-$JB>(BagW+B~#gXn5|G`{Y^=SKsD`CSj%x5?=O>G8k=>(Wu%0+{$5Mx5Z z4{CIVV%3)3S4iCZ5IDr}gp};AuZP2S;XU?)@qP^E(Ip%Ma8tst_gic#2fdo4ST1D^ zl0MGY8Y^q%D$PpKW*0Ky-W}?ggke1&t+VE+|r+&v9TaF908W2QHdizPFGx|k~CLJ^x;V8I1P#|XB zRg+CR-(gweTh8z?CPX4hw5*qJyFbg+!>WFlm|6>IlCeXQ+`$&Nl`ANniMqpdh8@x^ zeZ&{Y?mEeAO|5^s*3ui3VZYIM9$1Emt}>^}59WALak3vr zTWIXE1^mWiUyG(XzppejJg%^1nVO+9=>0o$-kM_ppSg*KhQB&6+&EQf_EFKY+%(x? zo1eHWpb<4i01aPF8*WFh`4#A(*A(Q$Tv6tp)xR zuIG>%c!m61(S{`*Gz@G;isj~N{ZkS5kV+t9N5z1Z6WcAJCjk8cC!8%E;Fx1?azw}@3T1gW&hrj%p##nia1VD|Hw>2f(C_5n z{wm|N-U@+m$H1-@aRQ4){y%~BvbDlM%dg`2P!#~K81PKMAWNoElyh}oI`$wRrHcEqZE*xL!BV8M zthtf<4Gj$|7K|zoI@(69IpfJfU#F`tjz4C<-Y6#6nK*TDvDb~QWK7Zn&#c8y8;CG1S>h z)8v)#Qnchkc&vRmWz@3W7T(vIGB>TdgP)Wg^1HpbnF_lx_`Cf7w@Wnl8e$<}=?E{)C7I_61v9!0C`!P3J!iU>E| z8~#vD>=T>C6V9jkl}r_%^>%4yr?RyAzWbeOjU`-1OY;-L_S{VGN%^j(&H?*U$~SkO zUZiVRZkSubUN>pOgYu7dcQOOPXmO;=y9$|FWNk(TBa{slYePe`>i=LvdI%fD8YYD3 z0fIXo-aUPFM2{eeR8NiU(`ST-JUSf=_p@~AOx&=NX`3qURLg3QJrt&4K(aKQozr=; zyXgy^Aj_FO;hKYcGe%Sfqpk4yI(P-zs~33-95**Q+vXTIl`Gy4q;I(;o87px$%C;` zKbQ0dvlYMhg&Tv`fu)G$M;%%5p+^d+m#2PAHQzFVm1HoK1ziw}_9K)vI!t@eajrg8 zXBUB|UiMohf{^LJ6@73PdG<&Dm_`@qvQ?U7lcDQ_s~ zltQq4jaEm!Zpfr4qCEu8I^6xTYq3| zvqc3d73t#fv>10s3+ArAYwA8_Z6=D>;6$5c$hc1b=1L0vg7 zdFI&FxpTR?B@S+KKfbfVVWw~UgR?!NUkI)B zrj~p13fkTsa}H7A57`AXjof2kEJqeA?`>D(x)^j}8foq01+t1s@-Hn&++yz~>#LJ_+3-X*m7R7`%*D;4 z`lc^Us@a@6iiybKlAc`OBALdrg9T~hWSMj<>Lx=ixaBHMUZlmH(73N|K38-+Y9Ccf zS>|$1S)$tGg~Mb`pV>df?Y-!hxS7Cwo3C}Gn3%HML9QJ2%Bx{#$@Ovur>`w+ z89kp9H&6THDs1j_O5%0V6P(BBaNd=3(<`RY)fmY`o7 z)h0+Gg{XO`C#%AGqhex9L3p`NV_@5E&x5RF@P4={V|Ay7%#|OK!k%%R%2nGB>y1Xf zYf2ra|Ms+DdLkuGr$aBFoY^UJ*AxC*NB6#(GjKW@@R9$z$D8IF=o)Snm# z$zd4sc3f7_R0|J3m`Tll>qfI+W?F7^zc^e<>^QfIj*1=ssn!IJ0K3gJY=>k7H~EI; z8-z{1WMybIga_3v_gdGJ(9t&bq`m4j&d*5>(1~Jr$QYYubTqNj=AQl81dChotzWEo z+#fj;4_c8YDl!VU^oH_Sb8->9r{3Y2O^t}n6yf7JZqiV7r|fxbDs0Eh@MZtIl2qwW zLWlO>@_Wj$$l9H7!$EgI<>G*=uVq2L(#x=3qy4;{z6p}#I(V8C+_UqM52UFITZT_r zHbobTskd;4J1_>oVwMXeE4gXvbkm1+KB6lhJKr856{o1l z;Cn-~*i-N7&OIfXPYX$XmRgIodrNvu;R5S);*!XfH!eL& zL{U%Qc5NClb@I+o8_gZ;^G&xtF1-9w(BbB}$-tzjkxAAeUQaeTVkK=7)05=M9G6Dj zf(WUC^!I+Co+oQH53L(5ndQf$e&@Zdyz)EZ&~n;|%0;)N_cdD-P1uPuc?EY&WsRoK z?&DSry6e6vsmoo$Pwf+8=0&=gsGE`OmX6+}d40V{$5X!*)(7;-e~b3_`~L35{h2S$ zmj{A{!fDMCXL96AlqG3Rm4|D9x8{UhE9)twPtillfL&Nwk?5bAyqm47r-QXE#;ejm zi5JSN%KV@yk>cNy_?ZvsqUb^VYp5NS3|-K?=SZ-hYq@4&@sY-i;LC^QTxbPurRGtL6VZ%Jw*glDxs1V!oz5Vv36#c##eCcWj{(FeZFw%2ZY5k__#tm8DImE2UL z6CPD@cDF}J+yeaNmKx)>Pno^0S1P*r8AhBO5A%D{r<$Fgq`Ca#%>x~|+vSqh1&q}O z?={;G2Xrb$`lg?hIuv%cY&O*J`tH~JK6|~hOv|~gaw;JJxH|HQ3LczMhwch-dASCG3!hr-q++1d!Ej#ectt88obEKn|_}!-6lgI?&T6^Ll zWgtOV)V?vn6V_HPL3dH<@ZpOsj`FiyueXL4-_c8Ab}BGFm)P@I>6V$}t zo9yGliWr<3ztb?(9_Qr!@tkhfeed;Drtadw;PZmDk zTS*n!EhYMNdci0?KkGHS!FRKyOS1PLk0`eZ@ERS_CU&Z2$j1MneuhQ{mQPZualmzM zv$Su?zP=GBR-(}3bX70948<6>CpLuU-+?SmiC|3U@ zRgv(Rw{qX^I2ooH36TUhEA?KHHd2J0)--N)PAnIlHGkHyz2L62RQ(ZI28$nJ@mn*h zRCHlAgWHnDXGE(q;!|xIm&@w|$_%>uObl%T+x#t*VuiLz&1(^#EB;Z(l<-zZ+N=K? z{0pMHOiQ+#cSw}GG5*CjC)0So#yQh?DNlj#9tb{#Tl%76(-#ui%hi=th;Obm9*uNAu*z1bdl`F)Mho9H1jVl91J&m6K87{DYM zGZ~@u5kIA{Pr_H9FJ@R!APJwNc_!pX24~AMK48I=FD9p@k-?{q5NyHMPD3Mu=AT-y z?$4T17Fkm&I=H&IAG5WRadCk;SX;U~AmES?al7MJh={3GA=poGq(#zvhmWI->xgov zs{RQ1M6KMcAVZsAc?$8f`faTN;-<8E@nfQMZyVW&-{1#}Yhoix!@PhU;!(6s<( zCcBr!mE^513q`?o9Cbq-Jh1yG8y|h@*k=$k)@d^kWf=Yi2IryW}ompq#pna_SNO+q)W!n2-lq62Q;5u53z_B0+C+S@AUbyoN;0ifXI1-}I#L_GZ! zB5*f(NRbN0G>kL*K!r9Aiq#C+7{1tF;iJ1E*fda&2(}9v|1}K(^FQ~-e{o+Br5@~(L;SDQBVdla z1a#*HU3UEAvg2)eT^r^QrjYV_2yT}+RD9O1su@nBQN^J_$WfJ;m={&WK`79iM|c+A zOp|w(f&)4W?-3y;8yVIDIypq~gALC#HHnmm zauRcgMn2Hyj?3s>rDh+>OB|-P9Z$H*(Y5KqN1~sB=u`sBRs&=pazg&C3|ZhOm(n5OGFUw&Ic|%hD^DOmO$gXX>d0nECRxWwuk+yp?fqmRL`?;G#Of}98It#a% z+qSBR=gJ4~717kF_6n4eY=N6y@2*o0kBoN}S&$s(R(fuJg(;pq%Y%%-M>Oo)*nOXU zRWo+iyBQkxZZ@OY{rD2mp(qL;yg@2;o3}a@8qBFTV!9a(#jjCt$=PeuZ@z$y#81o>Wh&%DqU`R@ZQbmiRiE{;oL^zF^Y@Vz~5>5H)Zu*Y&52~WZ<&kgIldH*e z3g+TU!jd`F)l%seUygijvX~DOH>YF^b^MVuu5ij}0{%SLGKANpXY=*j0tqbRu0yI0 zi9Rv(2xFnqAUi`-Aqh_J<|7XGp0$6;5!K}r(`8dX`EkmYjn`b9#&5AY<1DlA=j_{s zhxNRMmB+GJT~wr|PZUp>F7}tgA4vym5!uC-Q4f^#(E{y-^xBdAQ^?kW4rEZt?+dk0 z_TO0*Fwh1Y6j>X}P)%d@J0Tmlp~Ih#ZH;^5`;xxO#%VOMi5Sa}sjvzEWM;gVr?=5WRX~e@o z`^4I93geZ_Y!5#w+&XJsmE|PQ$u7r{et@jPMZa1@{2eQ$A6{N=8r8hciBgpSM@yrG z+6u>7(Rt2SK6YW#-gjD<6YZxD72T)3efe=(IRaLx3@`AxL{qXfNC@afG?|m{7BXeo z1?kL=C_=prcq`ZQTfou4YGHVW@-q(|b*BO3yy!`d>{g#h1#UiAta_t>)Rafd4bd>p z1a=RR;?#f|oxvC7r54drt#`%EHQz~<9h6|Km+gEOcJMIItzxItJ(<$GpEi{e`ZyCX z>659CQJhHJvy*WqJC1g`7%^Xeb3FC?7VDdhPszU0=G6D^Bk4b=crM$L#H`S#>%_Sq z`f4c!al9m)a>}HvE>=R?Qsv~67BVMM&OB^I@A(YaJ>YW_RR-KK0be`UOTSeS{wnQtEIoRE$INxbDA_jpg{Qj~| zwGk)0$JmSuDkL}CQ_hH5hP`3t<|_*lFBc}bO$uYZaAP^0-?Av~OXTPy(ru?rKGU#& zW>j^asr|UKvReK4rQE(87VqOREaIL^Jq>TX1G%)m%BDT}R;WY$amn$iJ(EebUWeFR za)dNrQ!S!B$2PevvZSAAVNcDs^2oh|xu05IJL>NUbsn!szQLDJsc-1PbWTaczoCD= z0x!M&^tYS$-lav*X`~nwhL0=C0|7v0UH6h z!Fd~Nx&ovD>luQVEeu(8*@A7M?$?S5dz8$!hk_Ie9Ya)Ydv3RySdg6WqprFvmQBo& zelap)&)YX=i3#% z+}L^aei!sSsB?iyJ-`VpD*eIc1zk3-jkVJE zSqBz7V?^(FZ3q_K$poZFXOL^B+yI2D_TWoq$tn6;WU+M+%Np?d{2{aYqWCT)j7J!zLK4qhCPkR zS(F9=y1ni7PA@aMm+L5tHEJS1R~+U_>Y1&dD2bYa>0XkPd=nH?EqqYdbZ-2fwp!KV z8;KCBre}7p-`xr`(|JzNhi?uaD_F@WBhe)NyO1^-+p1$VW(Y8J2|9YJOR=qUD&%l=Q) z!MH7B)hu9bssE$10PLr4)hqxiG0s^4`f>OVUF@pf>WY~E-^~JkiuNCx1>kl?97#4H z7&^)R585D*En~_CWcYBk{tpO)zloXvbwR+#WZWq$!UYWRtG2?;3N{W{ zDU&iqt8h?RByKG1QSojFPZKz?pot_37Z?|>Co4Au_rMmEve%xxLvl&ntYdF8@Iy#M z{|O0HI*B3!Y&3u+S^cHWiWN`FjQuGY$agm=ioka_D2gbf2fn%ie@meT5-cZB3%PWL z>AL*16X1TqcLLHKAQ*@u?r(5VTmawTfVlv^+W>E_t3tSlNABJJEf>Is=BlqaP)q<{ zae$SNeaFHeCXlAS{STQyugqiLG)nKl!aR7d_z;L#P9Q1r+W$ym6fr>9UzIkBDzH8e z7IF2?GT6TG$p4V)$_6uv9I(L*zE)s=8N7k32ociyZ^_}H{Le@K4aE(_$^ft5}0wPQbyq##C?WZqJN zUN-czgx+~u4+i`eEXmMQW5HnFr;T9ILRd1wiG_KnHUh{10ld66Ao|7H2q-56ba;JG zaF|@3bKp)}gdhYC(cn_)@8a-AX^BB-rTrBx^kW899xB8E<`2%Nj6w8}IUVia$Oh2+ zO`l{9PeAbm#CY)Lo;ZXjX1c%O=~rI@6$4gYbQD1#e*zN(KPowa)btxB2yF?Vhyis9 zIN$&Y7MK_pL^`5f>k`AN#!(bjQZ3x#FhVFytxe%qA&i@EY$ee_P@gg2;u~FqVk4sb z3cFiQ2|}Edxxxqvy_gjXOgawqmcVKSHlV;;R^a?HC-6OPu(g(;Q2YXGVc^h;-7E&{ zbxGzj6D|!v8Ao>~u^9(@pa@6{Ie{a;L0x;13%3l|rNS-{wMm)xyK(9qvpt1Gy!tfsDhbH69ca zu*L&6=haI*V5DO>kXBZDP>8@v4;XRvLJt@*eH}#9dJhT}SnmNl2(a7(UeMqH89+|p z@NZDDSA9@4fK?wb4Pey+yzIaZp&`Y-TB~SX0Gz{C7C%t%z~ToO9@v-wkI!U-4@+bnpP##PGXmx;1862o{0*`WInKF79jsp8# z(*xQ!q31zy`?H0)+O<$VfE^N`eG@G(1STED)xyEX0q4q-GLfrr&{^n4u%LaDCY)H< z$>&CJ!SC9n3>_~>T^q?cago4&hoOBF#GjBrrS}aittxF7NvGlt<5S-NK1b$tc z!v9b#wF~3od#z$=#Q?!-hrYo@B3Ot(f(p#au!A3NFz^GK6X-7r0{bk+r0%81TRU1JcSdYybcN literal 0 HcmV?d00001 diff --git a/02.Spring-Boot-Web-Application/Step20.md b/02.Spring-Boot-Web-Application/Step20.md new file mode 100644 index 0000000..2edcde3 --- /dev/null +++ b/02.Spring-Boot-Web-Application/Step20.md @@ -0,0 +1,708 @@ +## What we will do: +- Make real use of the Target Date Field +- initBinder method + +## Useful Snippets +``` + <%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt"%> + + + + @InitBinder + protected void initBinder(WebDataBinder binder) { + SimpleDateFormat dateFormat = new SimpleDateFormat("dd/MM/yyyy"); + binder.registerCustomEditor(Date.class, new CustomDateEditor( + dateFormat, false)); + } + + + org.webjars + bootstrap-datepicker + 1.0.1 + + + + + + +``` + +## Files List + +### pom.xml + +```xml + + + 4.0.0 + + com.in28minutes.springboot.web + spring-boot-first-web-application + 0.0.1-SNAPSHOT + jar + + spring-boot-first-web-application + Demo project for Spring Boot + + + org.springframework.boot + spring-boot-starter-parent + 1.4.3.RELEASE + + + + + UTF-8 + UTF-8 + 1.8 + + + + + org.springframework.boot + spring-boot-starter-web + + + + javax.servlet + jstl + + + + org.webjars + bootstrap + 3.3.6 + + + + org.webjars + bootstrap-datepicker + 1.0.1 + + + + org.webjars + jquery + 1.9.1 + + + + org.apache.tomcat.embed + tomcat-embed-jasper + provided + + + + org.springframework.boot + spring-boot-devtools + runtime + + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + +``` +--- +### src/main/java/com/in28minutes/springboot/web/controller/LoginController.java + +```java +package com.in28minutes.springboot.web.controller; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import org.springframework.ui.ModelMap; +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.SessionAttributes; + +import com.in28minutes.springboot.web.service.LoginService; + +@Controller +@SessionAttributes("name") +public class LoginController { + + @Autowired + LoginService service; + + @RequestMapping(value="/login", method = RequestMethod.GET) + public String showLoginPage(ModelMap model){ + return "login"; + } + + @RequestMapping(value="/login", method = RequestMethod.POST) + public String showWelcomePage(ModelMap model, @RequestParam String name, @RequestParam String password){ + + boolean isValidUser = service.validateUser(name, password); + + if (!isValidUser) { + model.put("errorMessage", "Invalid Credentials"); + return "login"; + } + + model.put("name", name); + model.put("password", password); + + return "welcome"; + } + +} +``` +--- +### src/main/java/com/in28minutes/springboot/web/controller/TodoController.java + +```java +package com.in28minutes.springboot.web.controller; + +import java.text.SimpleDateFormat; +import java.util.Date; + +import javax.validation.Valid; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.propertyeditors.CustomDateEditor; +import org.springframework.stereotype.Controller; +import org.springframework.ui.ModelMap; +import org.springframework.validation.BindingResult; +import org.springframework.web.bind.WebDataBinder; +import org.springframework.web.bind.annotation.InitBinder; +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.SessionAttributes; + +import com.in28minutes.springboot.web.model.Todo; +import com.in28minutes.springboot.web.service.LoginService; +import com.in28minutes.springboot.web.service.TodoService; + +@Controller +@SessionAttributes("name") +public class TodoController { + + @Autowired + TodoService service; + + @InitBinder + public void initBinder(WebDataBinder binder) { + // Date - dd/MM/yyyy + SimpleDateFormat dateFormat = new SimpleDateFormat("dd/MM/yyyy"); + binder.registerCustomEditor(Date.class, new CustomDateEditor( + dateFormat, false)); + } + + @RequestMapping(value = "/list-todos", method = RequestMethod.GET) + public String showTodos(ModelMap model) { + String name = (String) model.get("name"); + model.put("todos", service.retrieveTodos(name)); + return "list-todos"; + } + + @RequestMapping(value = "/add-todo", method = RequestMethod.GET) + public String showAddTodoPage(ModelMap model) { + model.addAttribute("todo", new Todo(0, (String) model.get("name"), + "Default Desc", new Date(), false)); + return "todo"; + } + + @RequestMapping(value = "/delete-todo", method = RequestMethod.GET) + public String deleteTodo(@RequestParam int id) { + service.deleteTodo(id); + return "redirect:/list-todos"; + } + + @RequestMapping(value = "/update-todo", method = RequestMethod.GET) + public String showUpdateTodoPage(@RequestParam int id, ModelMap model) { + Todo todo = service.retrieveTodo(id); + model.put("todo", todo); + return "todo"; + } + + @RequestMapping(value = "/update-todo", method = RequestMethod.POST) + public String updateTodo(ModelMap model, @Valid Todo todo, + BindingResult result) { + + if (result.hasErrors()) { + return "todo"; + } + + todo.setUser((String) model.get("name")); + + service.updateTodo(todo); + + return "redirect:/list-todos"; + } + + @RequestMapping(value = "/add-todo", method = RequestMethod.POST) + public String addTodo(ModelMap model, @Valid Todo todo, BindingResult result) { + + if (result.hasErrors()) { + return "todo"; + } + + service.addTodo((String) model.get("name"), todo.getDesc(), todo.getTargetDate(), + false); + return "redirect:/list-todos"; + } +} +``` +--- +### src/main/java/com/in28minutes/springboot/web/model/Todo.java + +```java +package com.in28minutes.springboot.web.model; + +import java.util.Date; + +import javax.validation.constraints.Size; + +public class Todo { + private int id; + private String user; + + @Size(min=10, message="Enter at least 10 Characters...") + private String desc; + + private Date targetDate; + private boolean isDone; + + public Todo() { + super(); + } + + public Todo(int id, String user, String desc, Date targetDate, + boolean isDone) { + super(); + this.id = id; + this.user = user; + this.desc = desc; + this.targetDate = targetDate; + this.isDone = isDone; + } + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public String getUser() { + return user; + } + + public void setUser(String user) { + this.user = user; + } + + public String getDesc() { + return desc; + } + + public void setDesc(String desc) { + this.desc = desc; + } + + public Date getTargetDate() { + return targetDate; + } + + public void setTargetDate(Date targetDate) { + this.targetDate = targetDate; + } + + public boolean isDone() { + return isDone; + } + + public void setDone(boolean isDone) { + this.isDone = isDone; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + id; + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + Todo other = (Todo) obj; + if (id != other.id) { + return false; + } + return true; + } + + @Override + public String toString() { + return String.format( + "Todo [id=%s, user=%s, desc=%s, targetDate=%s, isDone=%s]", id, + user, desc, targetDate, isDone); + } + +} +``` +--- +### src/main/java/com/in28minutes/springboot/web/service/LoginService.java + +```java +package com.in28minutes.springboot.web.service; + +import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; + +@Service +public class LoginService { + + public boolean validateUser(String userid, String password) { + // in28minutes, dummy + return userid.equalsIgnoreCase("in28minutes") + && password.equalsIgnoreCase("dummy"); + } + +} +``` +--- +### src/main/java/com/in28minutes/springboot/web/service/TodoService.java + +```java +package com.in28minutes.springboot.web.service; + +import java.util.ArrayList; +import java.util.Date; +import java.util.Iterator; +import java.util.List; + +import org.springframework.stereotype.Service; + +import com.in28minutes.springboot.web.model.Todo; + +@Service +public class TodoService { + private static List todos = new ArrayList(); + private static int todoCount = 3; + + static { + todos.add(new Todo(1, "in28Minutes", "Learn Spring MVC", new Date(), + false)); + todos.add(new Todo(2, "in28Minutes", "Learn Struts", new Date(), false)); + todos.add(new Todo(3, "in28Minutes", "Learn Hibernate", new Date(), + false)); + } + + public List retrieveTodos(String user) { + List filteredTodos = new ArrayList(); + for (Todo todo : todos) { + if (todo.getUser().equalsIgnoreCase(user)) { + filteredTodos.add(todo); + } + } + return filteredTodos; + } + + public Todo retrieveTodo(int id) { + for (Todo todo : todos) { + if (todo.getId()==id) { + return todo; + } + } + return null; + } + + public void updateTodo(Todo todo){ + todos.remove(todo); + todos.add(todo); + } + + public void addTodo(String name, String desc, Date targetDate, + boolean isDone) { + todos.add(new Todo(++todoCount, name, desc, targetDate, isDone)); + } + + public void deleteTodo(int id) { + Iterator iterator = todos.iterator(); + while (iterator.hasNext()) { + Todo todo = iterator.next(); + if (todo.getId() == id) { + iterator.remove(); + } + } + } +} +``` +--- +### src/main/java/com/in28minutes/springboot/web/SpringBootFirstWebApplication.java + +```java +package com.in28minutes.springboot.web; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.ComponentScan; + +@SpringBootApplication +@ComponentScan("com.in28minutes.springboot.web") +public class SpringBootFirstWebApplication { + + public static void main(String[] args) { + SpringApplication.run(SpringBootFirstWebApplication.class, args); + } +} +``` +--- +### src/main/resources/application.properties + +``` +spring.mvc.view.prefix=/WEB-INF/jsp/ +spring.mvc.view.suffix=.jsp +logging.level.org.springframework.web=INFO +``` +--- +### src/main/webapp/WEB-INF/jsp/list-todos.jsp + +``` +<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%> +<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt"%> + + + + +Todo's for ${name} + + + + +
+ + + + + + + + + + + + + + + + + + + + + + +
Your todos are
DescriptionTarget DateIs it Done?
${todo.desc}${todo.done}UpdateDelete
+ +
+ + + + + + + +``` +--- +### src/main/webapp/WEB-INF/jsp/login.jsp + +``` + + + +First Web Application + + + + ${errorMessage} +
+ Name : + Password : + +
+ + + +``` +--- +### src/main/webapp/WEB-INF/jsp/todo.jsp + +``` +<%@taglib uri="http://www.springframework.org/tags/form" prefix="form"%> + + + +First Web Application + + + + + +
+ + + + +
+ Description + + +
+ +
+ Target Date + + +
+ + +
+
+ + + + + + + + +``` +--- +### src/main/webapp/WEB-INF/jsp/welcome.jsp + +``` + + + +First Web Application + + + + Welcome ${name}!! Click here to manage your todo's. + + + +``` +--- +### src/test/java/com/in28minutes/springboot/web/SpringBootFirstWebApplicationTests.java + +```java +package com.in28minutes.springboot.web; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; + +@RunWith(SpringRunner.class) +@SpringBootTest +public class SpringBootFirstWebApplicationTests { + + @Test + public void contextLoads() { + } + +} +``` +--- +### todo.txt + +``` +Implementing Server Side Validation +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Command Bean or Form Backing Bean + +Add Validation +Use Validation on Controller +Display Errors in View + +Command Bean +~~~~~~~~~~~~ +Controller +View - Spring Form Tags + + + +LoginController -> adds name to model +welcome.jsp -> shows ${name} + +TodoController -> redirects to list-todos.jsp +${name} is empty + + +Component, Service, Repository, Controller +Autowired +ComponentScan + +Field dummyService in com.in28minutes.springboot.web.controller.LoginController +required a bean of type 'com.in28minutes.dummy.DummyService' +that could not be found. + +Spring Boot Starter Parent +Spring Boot Starter Web +@SpringBootApplication +Auto Configuration + +Dispatcher Servlet + +/login => "login" + +"login" => src/main/webapp/WEB-INF/jsp/login.jsp + + +Search for a view named "login" + + + +/login => LoginController +``` +--- diff --git a/02.Spring-Boot-Web-Application/Step20.zip b/02.Spring-Boot-Web-Application/Step20.zip new file mode 100644 index 0000000000000000000000000000000000000000..15de171d1e5bb8286608f786ea065346feeca26a GIT binary patch literal 20871 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 + + + +``` + +## Files List + +### pom.xml + +```xml + + + 4.0.0 + + com.in28minutes.springboot.web + spring-boot-first-web-application + 0.0.1-SNAPSHOT + jar + + spring-boot-first-web-application + Demo project for Spring Boot + + + org.springframework.boot + spring-boot-starter-parent + 1.4.3.RELEASE + + + + + UTF-8 + UTF-8 + 1.8 + + + + + org.springframework.boot + spring-boot-starter-web + + + + javax.servlet + jstl + + + + org.webjars + bootstrap + 3.3.6 + + + + org.webjars + bootstrap-datepicker + 1.0.1 + + + + org.webjars + jquery + 1.9.1 + + + + org.apache.tomcat.embed + tomcat-embed-jasper + provided + + + + org.springframework.boot + spring-boot-devtools + runtime + + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + +``` +--- +### src/main/java/com/in28minutes/springboot/web/controller/LoginController.java + +```java +package com.in28minutes.springboot.web.controller; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import org.springframework.ui.ModelMap; +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.SessionAttributes; + +import com.in28minutes.springboot.web.service.LoginService; + +@Controller +@SessionAttributes("name") +public class LoginController { + + @Autowired + LoginService service; + + @RequestMapping(value="/login", method = RequestMethod.GET) + public String showLoginPage(ModelMap model){ + return "login"; + } + + @RequestMapping(value="/login", method = RequestMethod.POST) + public String showWelcomePage(ModelMap model, @RequestParam String name, @RequestParam String password){ + + boolean isValidUser = service.validateUser(name, password); + + if (!isValidUser) { + model.put("errorMessage", "Invalid Credentials"); + return "login"; + } + + model.put("name", name); + model.put("password", password); + + return "welcome"; + } + +} +``` +--- +### src/main/java/com/in28minutes/springboot/web/controller/TodoController.java + +```java +package com.in28minutes.springboot.web.controller; + +import java.text.SimpleDateFormat; +import java.util.Date; + +import javax.validation.Valid; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.propertyeditors.CustomDateEditor; +import org.springframework.stereotype.Controller; +import org.springframework.ui.ModelMap; +import org.springframework.validation.BindingResult; +import org.springframework.web.bind.WebDataBinder; +import org.springframework.web.bind.annotation.InitBinder; +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.SessionAttributes; + +import com.in28minutes.springboot.web.model.Todo; +import com.in28minutes.springboot.web.service.LoginService; +import com.in28minutes.springboot.web.service.TodoService; + +@Controller +@SessionAttributes("name") +public class TodoController { + + @Autowired + TodoService service; + + @InitBinder + public void initBinder(WebDataBinder binder) { + // Date - dd/MM/yyyy + SimpleDateFormat dateFormat = new SimpleDateFormat("dd/MM/yyyy"); + binder.registerCustomEditor(Date.class, new CustomDateEditor( + dateFormat, false)); + } + + @RequestMapping(value = "/list-todos", method = RequestMethod.GET) + public String showTodos(ModelMap model) { + String name = (String) model.get("name"); + model.put("todos", service.retrieveTodos(name)); + return "list-todos"; + } + + @RequestMapping(value = "/add-todo", method = RequestMethod.GET) + public String showAddTodoPage(ModelMap model) { + model.addAttribute("todo", new Todo(0, (String) model.get("name"), + "Default Desc", new Date(), false)); + return "todo"; + } + + @RequestMapping(value = "/delete-todo", method = RequestMethod.GET) + public String deleteTodo(@RequestParam int id) { + service.deleteTodo(id); + return "redirect:/list-todos"; + } + + @RequestMapping(value = "/update-todo", method = RequestMethod.GET) + public String showUpdateTodoPage(@RequestParam int id, ModelMap model) { + Todo todo = service.retrieveTodo(id); + model.put("todo", todo); + return "todo"; + } + + @RequestMapping(value = "/update-todo", method = RequestMethod.POST) + public String updateTodo(ModelMap model, @Valid Todo todo, + BindingResult result) { + + if (result.hasErrors()) { + return "todo"; + } + + todo.setUser((String) model.get("name")); + + service.updateTodo(todo); + + return "redirect:/list-todos"; + } + + @RequestMapping(value = "/add-todo", method = RequestMethod.POST) + public String addTodo(ModelMap model, @Valid Todo todo, BindingResult result) { + + if (result.hasErrors()) { + return "todo"; + } + + service.addTodo((String) model.get("name"), todo.getDesc(), todo.getTargetDate(), + false); + return "redirect:/list-todos"; + } +} +``` +--- +### src/main/java/com/in28minutes/springboot/web/model/Todo.java + +```java +package com.in28minutes.springboot.web.model; + +import java.util.Date; + +import javax.validation.constraints.Size; + +public class Todo { + private int id; + private String user; + + @Size(min=10, message="Enter at least 10 Characters...") + private String desc; + + private Date targetDate; + private boolean isDone; + + public Todo() { + super(); + } + + public Todo(int id, String user, String desc, Date targetDate, + boolean isDone) { + super(); + this.id = id; + this.user = user; + this.desc = desc; + this.targetDate = targetDate; + this.isDone = isDone; + } + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public String getUser() { + return user; + } + + public void setUser(String user) { + this.user = user; + } + + public String getDesc() { + return desc; + } + + public void setDesc(String desc) { + this.desc = desc; + } + + public Date getTargetDate() { + return targetDate; + } + + public void setTargetDate(Date targetDate) { + this.targetDate = targetDate; + } + + public boolean isDone() { + return isDone; + } + + public void setDone(boolean isDone) { + this.isDone = isDone; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + id; + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + Todo other = (Todo) obj; + if (id != other.id) { + return false; + } + return true; + } + + @Override + public String toString() { + return String.format( + "Todo [id=%s, user=%s, desc=%s, targetDate=%s, isDone=%s]", id, + user, desc, targetDate, isDone); + } + +} +``` +--- +### src/main/java/com/in28minutes/springboot/web/service/LoginService.java + +```java +package com.in28minutes.springboot.web.service; + +import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; + +@Service +public class LoginService { + + public boolean validateUser(String userid, String password) { + // in28minutes, dummy + return userid.equalsIgnoreCase("in28minutes") + && password.equalsIgnoreCase("dummy"); + } + +} +``` +--- +### src/main/java/com/in28minutes/springboot/web/service/TodoService.java + +```java +package com.in28minutes.springboot.web.service; + +import java.util.ArrayList; +import java.util.Date; +import java.util.Iterator; +import java.util.List; + +import org.springframework.stereotype.Service; + +import com.in28minutes.springboot.web.model.Todo; + +@Service +public class TodoService { + private static List todos = new ArrayList(); + private static int todoCount = 3; + + static { + todos.add(new Todo(1, "in28Minutes", "Learn Spring MVC", new Date(), + false)); + todos.add(new Todo(2, "in28Minutes", "Learn Struts", new Date(), false)); + todos.add(new Todo(3, "in28Minutes", "Learn Hibernate", new Date(), + false)); + } + + public List retrieveTodos(String user) { + List filteredTodos = new ArrayList(); + for (Todo todo : todos) { + if (todo.getUser().equalsIgnoreCase(user)) { + filteredTodos.add(todo); + } + } + return filteredTodos; + } + + public Todo retrieveTodo(int id) { + for (Todo todo : todos) { + if (todo.getId()==id) { + return todo; + } + } + return null; + } + + public void updateTodo(Todo todo){ + todos.remove(todo); + todos.add(todo); + } + + public void addTodo(String name, String desc, Date targetDate, + boolean isDone) { + todos.add(new Todo(++todoCount, name, desc, targetDate, isDone)); + } + + public void deleteTodo(int id) { + Iterator iterator = todos.iterator(); + while (iterator.hasNext()) { + Todo todo = iterator.next(); + if (todo.getId() == id) { + iterator.remove(); + } + } + } +} +``` +--- +### src/main/java/com/in28minutes/springboot/web/SpringBootFirstWebApplication.java + +```java +package com.in28minutes.springboot.web; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.ComponentScan; + +@SpringBootApplication +@ComponentScan("com.in28minutes.springboot.web") +public class SpringBootFirstWebApplication { + + public static void main(String[] args) { + SpringApplication.run(SpringBootFirstWebApplication.class, args); + } +} +``` +--- +### src/main/resources/application.properties + +``` +spring.mvc.view.prefix=/WEB-INF/jsp/ +spring.mvc.view.suffix=.jsp +logging.level.org.springframework.web=INFO +``` +--- +### src/main/webapp/WEB-INF/jsp/common/footer.jspf + +``` + + + + + + + +``` +--- +### src/main/webapp/WEB-INF/jsp/common/header.jspf + +``` +<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%> +<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt"%> +<%@taglib uri="http://www.springframework.org/tags/form" prefix="form"%> + + + + +First Web Application + + + + + +``` +--- +### src/main/webapp/WEB-INF/jsp/common/navigation.jspf + +``` + +``` +--- +### src/main/webapp/WEB-INF/jsp/list-todos.jsp + +``` +<%@ include file="common/header.jspf" %> +<%@ include file="common/navigation.jspf" %> + +
+ + + + + + + + + + + + + + + + + + + + + + +
Your todos are
DescriptionTarget DateIs it Done?
${todo.desc}${todo.done}UpdateDelete
+ +
+<%@ include file="common/footer.jspf" %> +``` +--- +### src/main/webapp/WEB-INF/jsp/login.jsp + +``` +<%@ include file="common/header.jspf" %> +<%@ include file="common/navigation.jspf" %> + +
+ + ${errorMessage} +
+ Name : + Password : + +
+
+<%@ include file="common/footer.jspf" %> +``` +--- +### src/main/webapp/WEB-INF/jsp/todo.jsp + +``` +<%@ include file="common/header.jspf" %> +<%@ include file="common/navigation.jspf" %> +
+ + +
+ Description + + +
+ +
+ Target Date + + +
+ + +
+
+<%@ include file="common/footer.jspf" %> +``` +--- +### src/main/webapp/WEB-INF/jsp/welcome.jsp + +``` +<%@ include file="common/header.jspf"%> +<%@ include file="common/navigation.jspf"%> +
+ Welcome ${name}!! Click here to manage your + todo's. +
+<%@ include file="common/footer.jspf"%> +``` +--- +### src/test/java/com/in28minutes/springboot/web/SpringBootFirstWebApplicationTests.java + +```java +package com.in28minutes.springboot.web; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; + +@RunWith(SpringRunner.class) +@SpringBootTest +public class SpringBootFirstWebApplicationTests { + + @Test + public void contextLoads() { + } + +} +``` +--- +### todo.txt + +``` +Implementing Server Side Validation +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Command Bean or Form Backing Bean + +Add Validation +Use Validation on Controller +Display Errors in View + +Command Bean +~~~~~~~~~~~~ +Controller +View - Spring Form Tags + + + +LoginController -> adds name to model +welcome.jsp -> shows ${name} + +TodoController -> redirects to list-todos.jsp +${name} is empty + + +Component, Service, Repository, Controller +Autowired +ComponentScan + +Field dummyService in com.in28minutes.springboot.web.controller.LoginController +required a bean of type 'com.in28minutes.dummy.DummyService' +that could not be found. + +Spring Boot Starter Parent +Spring Boot Starter Web +@SpringBootApplication +Auto Configuration + +Dispatcher Servlet + +/login => "login" + +"login" => src/main/webapp/WEB-INF/jsp/login.jsp + + +Search for a view named "login" + + + +/login => LoginController +``` +--- diff --git a/02.Spring-Boot-Web-Application/Step21.zip b/02.Spring-Boot-Web-Application/Step21.zip new file mode 100644 index 0000000000000000000000000000000000000000..21e955fb0e789b3b6d7b3a7b1ad90d4b2dd6c9bb GIT binary patch literal 21926 zcmdU1cU;Zw`)_Dzr@d(JqohU2sid8Toc7*R6HzLqK|@Q6rb0s^L`X#{X=oRvsijC! z=yyJy&gpYbN8CW+D2xn8bU6%WIS>Z>f{TMwdsIieSClG%_X%gi!)f~Fnj^$kM){h-K?ai3 z?V824t~CDT@kxPC%GzRSllC-Us~my6JG0ntIzw~SJyNz1%EHl5=Ady{huBt7TnEyW z?S60POI7bH+e%wKNv@Mqo2HhHHyQcV!>XM51&+y9;tPHW?l&^*L! zS;>c`x*>|gIbzGlePn8%2Jg^eZfKy*9>BrZy0~aopI}@v(d{xwWo3Q)xvFe*HXBbB z)TEB=(?`wrh(NkBHuJNYp-G8L={(nk3-~Fux9b?tlS!yq``PIlRfHKf8nK3XxYnlU z(-#pQV!hB9Dw(wFvPYh0%Nx2fc4q5)iYn<%VkrR+?xzG)Jicf+d6<~K{5@%|W|GN^ zLh+2gOANDohH?~hOqcaZN77#s@!NDYoq+gAe~}&ZAC55G$#jB=SuKjyH0D-!@8d>^ zPbGO~Eh&^U72;j&tdGkxRjZc^cAHv1pTErBQWD?rvevuxV*|91%^UG^7Jmd9OIfx z3u;*wJwubsc)fujG4W_38z*y(%x#_$ndyok%l+);S|xil;wow<#Q*IDTcuSoYcOx#=0H zI$y7!a&+87^a(1*GS3rS>~H^iFE~kFsCjXj`%$mZpbT#BLs#3N{740{5Eqz62q*iY zq`rc85-)P^latISiR?Mt(Y2d?VX3w*vs`!7P9?QiO=$a9%KDzf+MPY|QN1^u-;%3L zrcFPXv%mGg|755r^&}%=$<4#9N$sk1%fO|xph&VH# z4_b(agM&|kgL4EP@Y`3zJjWV6;7Q;CZ*Hy$Rn*pl?FK@AA3WrN-xplk)z7f+6B_Vm z5c6Mb8EXqVYkLbTi&;$#gIP+0@FQpOVj1!Is9134ICl}wm55RiuqrA9XKKVoGaYmZ zxpt141CpA4^=2~cNNoI#W3W_M!sTO^Qj+VB6u4VhSh%=4Sz0=|+1R+(AB-a{spNe`WJxzfG=Dv9o1$>VZx%#cOyXz+3FNLD3%#QVYN4GuFu8wX#-nL#SEc4Od*&*;-EH4jF zM>l&bcXw|T>_!u(FjN%z6Ief6D-^WS28~<7(5;vLQ)p{ZHAsqfCC$hz_IYuaOzii;F?(p>v^;2i)Ac-+ z?L4$safG#=UC?lUR237xW$k6RO42%0Y1yRd5T`7^K`nz!mjwT#Ii!oahLcCKd=1Qe z$4{|k8osLHQouRC$UfYjJ4BY<<~hVH$}(a<*-DjI{z|{i?`u0$DfrZlANJA%MQJ;o z2`i84f9V`xt5Nx=UXytd=OkS@ndxC_N^4YUcDw{c9nlb8orN!ahkF*YEx+9yKziEl*!~ z;?1qnfnZl($YlNZjm-k;;sxh<_@U);Gy)#@p8>i~5jAztO!$U~A- zaoK{A!rDs|>bM;DZ)J6<%4_a^+@;Y_!hiTyL1F}FSw?TlcMVNmmv6U(KZi5z?Pku4 z@Ji(Jl84DC)v|Z(1d`F>c=w44cuA4H4UvoxF~oyWi>Ut(F{JNqrFz9iJO?Q(>P?lSEq$EPoJ=$RHa*0|ALE~~0#ME2ujPVQGO$I-prp2syN z<4zBAO-I^Nx{z?k6VQ$H`i1g4sR=N)E#9P9!YwN(9XWJJdtNZP%n$iFs|GdWyE6GHC&u-+Z-yHc`!A9`}m;} zcKmRo=9J;s*D#3_#m6FIvWLG;!B6irXza9lWc~+Al{nn8I{a^UV$HriPI#71Tp468 z;Se$frU9Dz9edlCkIfaYj{sJ^MvTLZ|sE6HT4~{sQL-F zU(49{%wou$hk!5p?hE5jT-P6RGwvdOZf$iqDxJFNMOpV(l0b*IrVQu&0_9_AI&2n| zSbZq6ZGsP+X1rk`Fa=wZSrBJ6X7wpN+|)Z0`y!k&7&0C%3@hd+jlL@K!PWGIbcTe5 zU5oS!3wuKKyc}wQbhnnrdDctcCL|r5C8WwTl-@PmExHw*=5sJT`gDrqd6&*+<0Al;E=kjhR z7xJC`X2D6WZ3W?s~@clr{3Zq)M$^EiE)WVb|24i7I7+48h`vs z`z1Aj@8RJ)%;OkNvt~VgJIF5O2h02V@wV1fg{wrX`boXwz@(Rhac_-fN5{T-jMS}& z80$sw{(P$7F;`SO6MWwfcXS(q+Sq+%I4IoSyP2{+?9zf}{@gF_8{ef`L3%*{!-aQ^X=6;wb@d;oQsQ+x z^$RF>I%hGz5V?Dp>&<5a=fi=YM2dWBN1P;ge9>V!OfgmCFmx`LW%Pv8${}ry$Vizi zI+44#n?$qH^J3mhxyeZy3#jR;*@>KNP2>%<+eXhV3l+ulk0>yRm~P=`>;fdy9uq|tOUJlT}g(mD3g>~#b5V@ z3R?5=;}6nN`{vNO$7Ssm7Bn`guXs@UDlW~1dUotez@z=N881Rd#qOT2<6Y$FNxbc- zH>7rH$kX5QdVxwyM4zFUP?vw=eo9>&Z7PAe`3q0wX>YzBJ89VzWdelrFSjCziVm6SamvfQsUN(832B3ma*fcROybKixI{T96kLuSNr!C*C2^>&bjtMyZ{ay3p*>A2_hC=p4GMFXJ<|>t_ z?q@Jn9eV=oDF?e&j+5}dgozlurJbAc`sW}Uq@@()m_C2uawe3l^|kQm7Yeg z(UkvI#A<=2hmeQbUx3Wfc0_e{QM=L!UySU^bUu~TIo54Nd+6K*Yy`bY&){6umf$TP zsrOajKe@QC>KqwUh3Zm3;vE^MC<}gm20!0O8R@1XN}ZfH9kFD|Y6L4gx; z&}IKvn$J`5Ih~Q<3;Ga}_AAUHR>E!a1m_Pw_1|fGqs))+LZ?hMpCQA3yeGPm(dA+6 zKIh^W+*$~p7`1)L>hsJj?-pShcXm1;;>(m~7CudXUB&x%_FG?PeP}SK-F_gj zOXY%p#&J2>h_KSRP*@Rr$L_Cw?<~`E@2j0m3ra;jVqXrMgcwF}S^A^h5^gPyGp z{jFbte1*-eWxZ=@OTWqus92=Tq4V51L1*tKTc(^R|6qyRW7;!a+yyUKlHdi40ojj5 zo!e7_5p8$&FPcY2m8V?LpuuNddNxr)-*dWu7<<0eBDxzZ)Mu2B>`>9k`@DZEf{{N^jJd$bpN zO%`2xH%?Q)6LDgwe<1K3mx>*Noox7S6Q&!KK8i0x47JE z-`h;l?K28-9?Q;JesBBzGHly$3(bXe3>!JixC|jNcE_*z_)1l$QI$k<1SG5Pjf{P( zxctBwmTtI*ETl=LFH_!7+2xG3QLAgx9f>*fm-VFAAIZzr98_Sj_$iq{e6>PN&*jPR z_RCVU5*1ey(rj5*?$iX98uSd9z-)rr0xVSG_H37%KSFZ8_-8d+;#*yLzxT^--`smj zk0|sAjY*y$*h zPq-;J2ln|N6^FIZ>82(K{(5FKf2921=PS=qF4_e16-&P127WBBhnhWA;w@{f)<6m-ZZIJoM(u z_b&V+8kgEtC_cMf6 zwUFGSZ1q?u45{X=9_2Ko1th}L0fsa4L&@bQ@yG2RnUs5u z9vbgf;x?l=CFZA1=*53C%(p)#u+`ne96DUM~-PQWgrd_%q?j2*D@gHf4&?4#e+(OdoWuL zslRd$#2mf|$Waoq?fA!S$06vbig1iLl+rQkW|uTt9A;P1ExLl2 z5YlvwFwCuq{#qE>>UcK%GgD3wXgrIs8gudqG+JnBV}0)e+YY>2Am;0#jkFj!cl;My zj5(|2-`rw|Iml+DnaHW_zt~L7g=PJ*?)i>jkVo z0Gst40V@RIuWwZEY!FfJ5Ngm6=@jBu(`+cb)lTW>)B0uJtL?QQqS%6K-st!UL*67*Kc9|g^jgvwgY9HuK5sYP#ZN@5iRfhFE@A_CB65cR z)^B|<>lf_tx^4xaFZOc|(+zvEhQ~S*#C&TW`Nuc=|MyiJGxh-T@`l&UhzmGi!I3*- zz1zgxU);ZSAOaE@LP*4a++qL3v4_}em?pAeca^uTtA~pv^1g0s-ojpQg2N4k`k!zZ zqE5g}PJqKU)(H@El$}W1kZI~)91K(#7tU1RV1So%@brM1d5vwjcc#bq8aRzmX?yMK zV}}$(RJD{*D4#c=Ojx2%c!2cxD-PpFB>36y>W;Wr2LfE~ z>pCD}>4h->AzK5qgSYCoo)EaQcF0)pU&Ho^*4+L~luEhtrGOe;E2I&&4O(P)GVV@9 zkAp^rQf|1PRQ3^u;|8A>+0Cbor&3=*e_ zp_8;d(hR2$c2@`Q{z}vMdg4jQr8_p)Y?{w>UfKJ6?`OZ%d5I#r1PxM25*o^VQ}4xv zzSYrEs@1*CO5`xM*ATcx9|pT{$^JoB4y62|p5pv7v97WctAh;JGrXU&;b)Wt+9-|) zqtD)=Hyms&Z7{q+x3o5MixG3H79~40HbEPFw(vbwrXmN+bgPhe5^GNqmm%KEH?O9J zIu*seKOCgGIvlvuJO7wSfSTAiOKnpl=NAHtxvOtKx!zTLdCS9WxAK#|;$w1B`u37!QG8)XB5%c(c$C{Oli|xZx|gSD)p=C092qTe=b527j|L-@fHr!wC|`?BH%nhf`?Wz2 zD4aCb!pSuTN)fV)R;lv~?YYWJ-tBwqD$-_71(js~941vhQeyt56(*F%5iix~5l6@? z32T+1lyZ5RBaxoCt*OWW7r&Rg$eWz&Wbq|Z(!&PQheS`^q%36f&iLH1{1Z|m`Qh2R8!e?Z}IiB&;P_#8V@^i>k79ID6T?y>@ zv-N(JyLvMRCoK0@7RCk7%0C`!`Szx_eqXU!RC;bMSZ0?$MfAFcZ8EXzpVBe<_3 z(R_uE5om(H@s*3Cm$!&Fe7@@i>^bC?BVP-$P(8$8_n|ol@ub*X<&i$}Itsb@4mMvK zrF}Y<>S0Ru#9`l;`Mb498LG&-0^a1jHXAQ1S@eSa{7e*hTRmp^(j}XPDUCP)Qht2GrBLp|623?IN`HPN0~?! z4O|1xiwRONUJ6#ZX4aujGq#VEmX$B|SehJHp$n*NT^U6OjE^m&Ol z$A|2j?#S7XuuJ=K8uhD0eN9r2Nuf%^y)3Ylp z=iRx{Gbg4=?|!Nv*J^fEYT!6oQM|G3ILF1wKNDr+ec_c(qtEy- z`R&Z2@Li7Ce8t7pTTh<*s)SMS<<5ZLe6AN>B^cX-=XXmE&{)8$ef%3DrJRXX*QyaE z3+v3#gVC4q_$L^c;MCA53^i?&I6izIpZK^RD?9VS;EO!VE8mxX{AAWm=@g+6cFd!; zZ%T>UuN%&tOj!gWUUnQ?!BdycQ*5poA^Uh*gYR-@v(Z>&n7&H$xvJes&l-gq!WF3V zqZnu`A7phr3o@&mteMwR3)46?ENwM%P11tVO_BFL-c_}J+W7}P0&3ckM>Lp94{z&i z+26ZTM-w>orR-5S~a_9WgnVDRTl1hO7$xbU6K2SPixj|&rdm6 zI`Pif*O-BlSz-<+NL1WCOrxxlZv0{cy;2OtLTmHhxMN%nq;pFGp=Ob;4;>%lojOwk z6~9#Sls@K}bcSxw$&PW3dS9>hdD{H6+YuxuxF045(WvXx+_^`2Dm5`hm~I|#XN%D+ zrvv*UO%d(H_pkvMJToG?DYx*1>WGK288c5sOh{kWEr@=kRy^KUKzi+cCS{6q^uWC% zr>mSxjV=lea>^LqKSq5;T=}wiQGV?NC#jgO##L`kwMF{}*_Hk+Ejc?o&N*G|a{Ebt z*@yKkWh%5hCclh7d1p=W25`qlg z*HDUQyQm17;RM_S2NOfc!H9_S9&$mBQFzv8CB>dU>@BWn{jvOQWpaq%^leMuZ6TCz z)EmS+b$4Xt^z}D*#UAFG-<|9+$7S%^@m|wz3;d+p*Y#WY>RZD{l`S=?S>KXyo#~<9 z`z*n6Ja7BsH(s_c4y8KJF5vF4ZQV|;9~Mq>IGEwJ;oh`;iCPYK>3^h1igx~Z#sHZ9 zIfqd&2_A|Q@Zb8b#pEjjeLGrd3^50WXh22=_+?1u;=i~c#_W!e3`}?>@e4>UQCsd3{3RzL8H7sz(USZevP$02;Br0 zvxP!3Ft-GY9PI<%L$*}wA&6;)#T?x)j63(T=pE9@_11RgXuHKwIl^uJtqE6o&pqLekf zZ0VeU()-6dbRI3a5gb&EEl{?KxtgLn*Ux7v*B~ao9NydL-x{MjkpG!+dZ2_x8`j%! zIeUjepk7~ljdRP@o|Wny#ad4;d@Vn~pWHiFGgT5jg_yf4+z{ZXv_c~Pr-_-VNXAB~q zF-KBBOrmY<)s+MGHRd1+`}Z5}#nxAT>mVEM#nwTPJ7jCxY?xD^1A&8O>)HVY{YDq9 z?tMTzFeoSjw~oKzUhLOOgYc1?2h%awuRzTI$_7BjZ|=qPf4PtT@w4Ec8!ciV|7^G% z+tNCqF2}%G>>s!NpO_M2kBkkMW52c<^KuNFHUC2vZ@3&ow|K+l*niZ_4VMk;W}+^~ z{-dUCxExzGZT)iWKWY}1A{#IaI7$Aa76H{V>ga^04^L~r81NIRe_`Ih#iPSVi~X*MuVmUKfGZ@L4Y3AVOIyUq}~R?nue~xv4Hjj&;+?eU}G?QLK3vE#o&)pP|YzC z0_Y3D5SR*3pg}l*A=Y{;?5bemfSoc8&E0^5s3NgrVR9$f(R3QdiUqFukhTK+iOp;k zK*JR!0J-b;wIp`cG3h-bXd=%4i3mhFi4X%&e884$d<(Z8#naIDe<}v;WgJ2x@G=e| z5fSvjyEyP`Ck>Ecxj`y$7A7jxe&;*DFpxy-ujCL~0I%f0S^zJrz)v~U(X_Y#C!+tY z764^=!#k_BHHN>l0y`gbjm1GqOhYB$EAdB4Am5N+t~A2$zzY&E(OwoLEH^0W?}P!y zu65c7sla;@u!$Q<{NVVCp!`D<*D3wPtLG*_=?5<>KH zgg>~RG6KoV4T|_{dB2&H{9-2{bOLfbn93@Jrc(jq-}qo{E`dGI1N9SFAox(p4Z6a# zWr64;fwdD1{(nCJCmf)_0t@3uvo4l-Q(+k0I6}gDsfF+#ra7!=68uv_WPja)=3ij&{C?7jE51=cKZ3hC1gLmLwkQZ`;j{gSr*UU!jHej|2%phsg zP^X<(eU73NA~1og8L;ayBZC`+nHzK)j*05YP{rB0U*LOYm8}S5L}XtN_By7_Q;A!TTJncW%&g4r~I@$aM&Jtj9TG z(`;n2fvdx8PLMm`c^|=RLm&aXHn3wivf98%16YvOIc^9)D4M< z;JYDE0lpg;btBsijOy_lRCuq8;JzWi0qz?D9I;9O^9{@bgMgwdH>hfB;AlKJgdBhe z2bKeH%D{vvbUClNH+RjN5oioEgdBij29^WRufSv@aIKOXM8>-%IUC4W2w4Cb3oHwu zFM(Muf@r>z=G$BrCYcE#10XYjWdM8`Fg=6^O@@%bZ)B{qXAtlJdj<>-a7@5#2lUis zAhk=g6Eh9vg@9B+4Lr806Z$~Q z4l5S8klrdTxZs$E-u^8vs6hXf2+RTreV|3M8=D)zs)IxzdjM>u6R>LF>v4UwJ!bQ# zV)Qn9#;XsyAVtLV`oWBdAbF|>i`fGW=X<#>tW9;=GdKX(O{x^<5b+NzN#^A#My^DRgcgq4XyIAxLz3z6f zF!V0ANMciA8@kx7NC0 + + 4.0.0 + + com.in28minutes.springboot.web + spring-boot-first-web-application + 0.0.1-SNAPSHOT + jar + + spring-boot-first-web-application + Demo project for Spring Boot + + + org.springframework.boot + spring-boot-starter-parent + 1.4.3.RELEASE + + + + + UTF-8 + UTF-8 + 1.8 + + + + + org.springframework.boot + spring-boot-starter-web + + + + javax.servlet + jstl + + + + org.webjars + bootstrap + 3.3.6 + + + + org.webjars + bootstrap-datepicker + 1.0.1 + + + + org.webjars + jquery + 1.9.1 + + + + org.apache.tomcat.embed + tomcat-embed-jasper + provided + + + + org.springframework.boot + spring-boot-devtools + runtime + + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + +``` +--- +### src/main/java/com/in28minutes/springboot/web/controller/LoginController.java + +```java +package com.in28minutes.springboot.web.controller; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import org.springframework.ui.ModelMap; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.SessionAttributes; + +import com.in28minutes.springboot.web.service.LoginService; + +@Controller +@SessionAttributes("name") +public class LoginController { + + @Autowired + LoginService service; + + @RequestMapping(value = "/", method = RequestMethod.GET) + public String showLoginPage(ModelMap model) { + model.put("name", "in28Minutes"); + return "welcome"; + } + +} +``` +--- +### src/main/java/com/in28minutes/springboot/web/controller/TodoController.java + +```java +package com.in28minutes.springboot.web.controller; + +import java.text.SimpleDateFormat; +import java.util.Date; + +import javax.validation.Valid; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.propertyeditors.CustomDateEditor; +import org.springframework.stereotype.Controller; +import org.springframework.ui.ModelMap; +import org.springframework.validation.BindingResult; +import org.springframework.web.bind.WebDataBinder; +import org.springframework.web.bind.annotation.InitBinder; +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.SessionAttributes; + +import com.in28minutes.springboot.web.model.Todo; +import com.in28minutes.springboot.web.service.LoginService; +import com.in28minutes.springboot.web.service.TodoService; + +@Controller +@SessionAttributes("name") +public class TodoController { + + @Autowired + TodoService service; + + @InitBinder + public void initBinder(WebDataBinder binder) { + // Date - dd/MM/yyyy + SimpleDateFormat dateFormat = new SimpleDateFormat("dd/MM/yyyy"); + binder.registerCustomEditor(Date.class, new CustomDateEditor( + dateFormat, false)); + } + + @RequestMapping(value = "/list-todos", method = RequestMethod.GET) + public String showTodos(ModelMap model) { + String name = getLoggedInUserName(model); + model.put("todos", service.retrieveTodos(name)); + return "list-todos"; + } + + private String getLoggedInUserName(ModelMap model) { + return (String) model.get("name"); + } + + @RequestMapping(value = "/add-todo", method = RequestMethod.GET) + public String showAddTodoPage(ModelMap model) { + model.addAttribute("todo", new Todo(0, getLoggedInUserName(model), + "Default Desc", new Date(), false)); + return "todo"; + } + + @RequestMapping(value = "/delete-todo", method = RequestMethod.GET) + public String deleteTodo(@RequestParam int id) { + service.deleteTodo(id); + return "redirect:/list-todos"; + } + + @RequestMapping(value = "/update-todo", method = RequestMethod.GET) + public String showUpdateTodoPage(@RequestParam int id, ModelMap model) { + Todo todo = service.retrieveTodo(id); + model.put("todo", todo); + return "todo"; + } + + @RequestMapping(value = "/update-todo", method = RequestMethod.POST) + public String updateTodo(ModelMap model, @Valid Todo todo, + BindingResult result) { + + if (result.hasErrors()) { + return "todo"; + } + + todo.setUser(getLoggedInUserName(model)); + + service.updateTodo(todo); + + return "redirect:/list-todos"; + } + + @RequestMapping(value = "/add-todo", method = RequestMethod.POST) + public String addTodo(ModelMap model, @Valid Todo todo, BindingResult result) { + + if (result.hasErrors()) { + return "todo"; + } + + service.addTodo(getLoggedInUserName(model), todo.getDesc(), todo.getTargetDate(), + false); + return "redirect:/list-todos"; + } +} +``` +--- +### src/main/java/com/in28minutes/springboot/web/model/Todo.java + +```java +package com.in28minutes.springboot.web.model; + +import java.util.Date; + +import javax.validation.constraints.Size; + +public class Todo { + private int id; + private String user; + + @Size(min=10, message="Enter at least 10 Characters...") + private String desc; + + private Date targetDate; + private boolean isDone; + + public Todo() { + super(); + } + + public Todo(int id, String user, String desc, Date targetDate, + boolean isDone) { + super(); + this.id = id; + this.user = user; + this.desc = desc; + this.targetDate = targetDate; + this.isDone = isDone; + } + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public String getUser() { + return user; + } + + public void setUser(String user) { + this.user = user; + } + + public String getDesc() { + return desc; + } + + public void setDesc(String desc) { + this.desc = desc; + } + + public Date getTargetDate() { + return targetDate; + } + + public void setTargetDate(Date targetDate) { + this.targetDate = targetDate; + } + + public boolean isDone() { + return isDone; + } + + public void setDone(boolean isDone) { + this.isDone = isDone; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + id; + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + Todo other = (Todo) obj; + if (id != other.id) { + return false; + } + return true; + } + + @Override + public String toString() { + return String.format( + "Todo [id=%s, user=%s, desc=%s, targetDate=%s, isDone=%s]", id, + user, desc, targetDate, isDone); + } + +} +``` +--- +### src/main/java/com/in28minutes/springboot/web/service/LoginService.java + +```java +package com.in28minutes.springboot.web.service; + +import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; + +@Service +public class LoginService { + + public boolean validateUser(String userid, String password) { + // in28minutes, dummy + return userid.equalsIgnoreCase("in28minutes") + && password.equalsIgnoreCase("dummy"); + } + +} +``` +--- +### src/main/java/com/in28minutes/springboot/web/service/TodoService.java + +```java +package com.in28minutes.springboot.web.service; + +import java.util.ArrayList; +import java.util.Date; +import java.util.Iterator; +import java.util.List; + +import org.springframework.stereotype.Service; + +import com.in28minutes.springboot.web.model.Todo; + +@Service +public class TodoService { + private static List todos = new ArrayList(); + private static int todoCount = 3; + + static { + todos.add(new Todo(1, "in28Minutes", "Learn Spring MVC", new Date(), + false)); + todos.add(new Todo(2, "in28Minutes", "Learn Struts", new Date(), false)); + todos.add(new Todo(3, "in28Minutes", "Learn Hibernate", new Date(), + false)); + } + + public List retrieveTodos(String user) { + List filteredTodos = new ArrayList(); + for (Todo todo : todos) { + if (todo.getUser().equalsIgnoreCase(user)) { + filteredTodos.add(todo); + } + } + return filteredTodos; + } + + public Todo retrieveTodo(int id) { + for (Todo todo : todos) { + if (todo.getId()==id) { + return todo; + } + } + return null; + } + + public void updateTodo(Todo todo){ + todos.remove(todo); + todos.add(todo); + } + + public void addTodo(String name, String desc, Date targetDate, + boolean isDone) { + todos.add(new Todo(++todoCount, name, desc, targetDate, isDone)); + } + + public void deleteTodo(int id) { + Iterator iterator = todos.iterator(); + while (iterator.hasNext()) { + Todo todo = iterator.next(); + if (todo.getId() == id) { + iterator.remove(); + } + } + } +} +``` +--- +### src/main/java/com/in28minutes/springboot/web/SpringBootFirstWebApplication.java + +```java +package com.in28minutes.springboot.web; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.ComponentScan; + +@SpringBootApplication +@ComponentScan("com.in28minutes.springboot.web") +public class SpringBootFirstWebApplication { + + public static void main(String[] args) { + SpringApplication.run(SpringBootFirstWebApplication.class, args); + } +} +``` +--- +### src/main/resources/application.properties + +``` +spring.mvc.view.prefix=/WEB-INF/jsp/ +spring.mvc.view.suffix=.jsp +logging.level.org.springframework.web=INFO +``` +--- +### src/main/webapp/WEB-INF/jsp/common/footer.jspf + +``` + + + + + + + +``` +--- +### src/main/webapp/WEB-INF/jsp/common/header.jspf + +``` +<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%> +<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt"%> +<%@taglib uri="http://www.springframework.org/tags/form" prefix="form"%> + + + + +First Web Application + + + + + +``` +--- +### src/main/webapp/WEB-INF/jsp/common/navigation.jspf + +``` + +``` +--- +### src/main/webapp/WEB-INF/jsp/list-todos.jsp + +``` +<%@ include file="common/header.jspf" %> +<%@ include file="common/navigation.jspf" %> + +
+ + + + + + + + + + + + + + + + + + + + + + +
Your todos are
DescriptionTarget DateIs it Done?
${todo.desc}${todo.done}UpdateDelete
+ +
+<%@ include file="common/footer.jspf" %> +``` +--- +### src/main/webapp/WEB-INF/jsp/todo.jsp + +``` +<%@ include file="common/header.jspf" %> +<%@ include file="common/navigation.jspf" %> +
+ + +
+ Description + + +
+ +
+ Target Date + + +
+ + +
+
+<%@ include file="common/footer.jspf" %> +``` +--- +### src/main/webapp/WEB-INF/jsp/welcome.jsp + +``` +<%@ include file="common/header.jspf"%> +<%@ include file="common/navigation.jspf"%> +
+ Welcome ${name}!! Click here to manage your + todo's. +
+<%@ include file="common/footer.jspf"%> +``` +--- +### src/test/java/com/in28minutes/springboot/web/SpringBootFirstWebApplicationTests.java + +```java +package com.in28minutes.springboot.web; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; + +@RunWith(SpringRunner.class) +@SpringBootTest +public class SpringBootFirstWebApplicationTests { + + @Test + public void contextLoads() { + } + +} +``` +--- +### todo.txt + +``` +Implementing Server Side Validation +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Command Bean or Form Backing Bean + +Add Validation +Use Validation on Controller +Display Errors in View + +Command Bean +~~~~~~~~~~~~ +Controller +View - Spring Form Tags + + + +LoginController -> adds name to model +welcome.jsp -> shows ${name} + +TodoController -> redirects to list-todos.jsp +${name} is empty + + +Component, Service, Repository, Controller +Autowired +ComponentScan + +Field dummyService in com.in28minutes.springboot.web.controller.LoginController +required a bean of type 'com.in28minutes.dummy.DummyService' +that could not be found. + +Spring Boot Starter Parent +Spring Boot Starter Web +@SpringBootApplication +Auto Configuration + +Dispatcher Servlet + +/login => "login" + +"login" => src/main/webapp/WEB-INF/jsp/login.jsp + + +Search for a view named "login" + + + +/login => LoginController +``` +--- diff --git a/02.Spring-Boot-Web-Application/Step22.zip b/02.Spring-Boot-Web-Application/Step22.zip new file mode 100644 index 0000000000000000000000000000000000000000..0cd9766c4bcfc09e032c7b57de995496c7f4d1c3 GIT binary patch literal 21441 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 R6A-8)ezkypSr+);{{bz7Fm3<< literal 0 HcmV?d00001 diff --git a/02.Spring-Boot-Web-Application/Step23.md b/02.Spring-Boot-Web-Application/Step23.md new file mode 100644 index 0000000..0a78acd --- /dev/null +++ b/02.Spring-Boot-Web-Application/Step23.md @@ -0,0 +1,739 @@ +## What we will do: +- Get Setup for Spring Security + + +## Useful Snippets +``` + + org.springframework.boot + spring-boot-starter-security + + +package com.in28minutes.security; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; + +@Configuration +@EnableWebSecurity +public class SecurityConfiguration extends WebSecurityConfigurerAdapter { + + @Autowired + public void configureGlobalSecurity(AuthenticationManagerBuilder auth) + throws Exception { + auth.inMemoryAuthentication().withUser("in28Minutes").password("dummy") + .roles("USER", "ADMIN"); + } + + @Override + protected void configure(HttpSecurity http) throws Exception { + http.authorizeRequests().antMatchers("/login").permitAll() + .antMatchers("/", "/*todo*/**").access("hasRole('USER')").and() + .formLogin(); + } +} + +``` + +Not Needed anymore with Spring Boot Auto Configuration +``` + + springSecurityFilterChain + org.springframework.web.filter.DelegatingFilterProxy + + + + springSecurityFilterChain + /* + +``` + +## Files List + +### pom.xml + +```xml + + + 4.0.0 + + com.in28minutes.springboot.web + spring-boot-first-web-application + 0.0.1-SNAPSHOT + jar + + spring-boot-first-web-application + Demo project for Spring Boot + + + org.springframework.boot + spring-boot-starter-parent + 1.4.3.RELEASE + + + + + UTF-8 + UTF-8 + 1.8 + + + + + org.springframework.boot + spring-boot-starter-web + + + + org.springframework.boot + spring-boot-starter-security + + + + javax.servlet + jstl + + + + org.webjars + bootstrap + 3.3.6 + + + + org.webjars + bootstrap-datepicker + 1.0.1 + + + + org.webjars + jquery + 1.9.1 + + + + org.apache.tomcat.embed + tomcat-embed-jasper + provided + + + + org.springframework.boot + spring-boot-devtools + runtime + + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + +``` +--- +### src/main/java/com/in28minutes/springboot/web/controller/LoginController.java + +```java +package com.in28minutes.springboot.web.controller; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import org.springframework.ui.ModelMap; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.SessionAttributes; + +import com.in28minutes.springboot.web.service.LoginService; + +@Controller +@SessionAttributes("name") +public class LoginController { + + @Autowired + LoginService service; + + @RequestMapping(value = "/", method = RequestMethod.GET) + public String showLoginPage(ModelMap model) { + model.put("name", "in28Minutes"); + return "welcome"; + } + +} +``` +--- +### src/main/java/com/in28minutes/springboot/web/controller/TodoController.java + +```java +package com.in28minutes.springboot.web.controller; + +import java.text.SimpleDateFormat; +import java.util.Date; + +import javax.validation.Valid; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.propertyeditors.CustomDateEditor; +import org.springframework.stereotype.Controller; +import org.springframework.ui.ModelMap; +import org.springframework.validation.BindingResult; +import org.springframework.web.bind.WebDataBinder; +import org.springframework.web.bind.annotation.InitBinder; +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.SessionAttributes; + +import com.in28minutes.springboot.web.model.Todo; +import com.in28minutes.springboot.web.service.LoginService; +import com.in28minutes.springboot.web.service.TodoService; + +@Controller +@SessionAttributes("name") +public class TodoController { + + @Autowired + TodoService service; + + @InitBinder + public void initBinder(WebDataBinder binder) { + // Date - dd/MM/yyyy + SimpleDateFormat dateFormat = new SimpleDateFormat("dd/MM/yyyy"); + binder.registerCustomEditor(Date.class, new CustomDateEditor( + dateFormat, false)); + } + + @RequestMapping(value = "/list-todos", method = RequestMethod.GET) + public String showTodos(ModelMap model) { + String name = getLoggedInUserName(model); + model.put("todos", service.retrieveTodos(name)); + return "list-todos"; + } + + private String getLoggedInUserName(ModelMap model) { + return (String) model.get("name"); + } + + @RequestMapping(value = "/add-todo", method = RequestMethod.GET) + public String showAddTodoPage(ModelMap model) { + model.addAttribute("todo", new Todo(0, getLoggedInUserName(model), + "Default Desc", new Date(), false)); + return "todo"; + } + + @RequestMapping(value = "/delete-todo", method = RequestMethod.GET) + public String deleteTodo(@RequestParam int id) { + service.deleteTodo(id); + return "redirect:/list-todos"; + } + + @RequestMapping(value = "/update-todo", method = RequestMethod.GET) + public String showUpdateTodoPage(@RequestParam int id, ModelMap model) { + Todo todo = service.retrieveTodo(id); + model.put("todo", todo); + return "todo"; + } + + @RequestMapping(value = "/update-todo", method = RequestMethod.POST) + public String updateTodo(ModelMap model, @Valid Todo todo, + BindingResult result) { + + if (result.hasErrors()) { + return "todo"; + } + + todo.setUser(getLoggedInUserName(model)); + + service.updateTodo(todo); + + return "redirect:/list-todos"; + } + + @RequestMapping(value = "/add-todo", method = RequestMethod.POST) + public String addTodo(ModelMap model, @Valid Todo todo, BindingResult result) { + + if (result.hasErrors()) { + return "todo"; + } + + service.addTodo(getLoggedInUserName(model), todo.getDesc(), todo.getTargetDate(), + false); + return "redirect:/list-todos"; + } +} +``` +--- +### src/main/java/com/in28minutes/springboot/web/model/Todo.java + +```java +package com.in28minutes.springboot.web.model; + +import java.util.Date; + +import javax.validation.constraints.Size; + +public class Todo { + private int id; + private String user; + + @Size(min=10, message="Enter at least 10 Characters...") + private String desc; + + private Date targetDate; + private boolean isDone; + + public Todo() { + super(); + } + + public Todo(int id, String user, String desc, Date targetDate, + boolean isDone) { + super(); + this.id = id; + this.user = user; + this.desc = desc; + this.targetDate = targetDate; + this.isDone = isDone; + } + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public String getUser() { + return user; + } + + public void setUser(String user) { + this.user = user; + } + + public String getDesc() { + return desc; + } + + public void setDesc(String desc) { + this.desc = desc; + } + + public Date getTargetDate() { + return targetDate; + } + + public void setTargetDate(Date targetDate) { + this.targetDate = targetDate; + } + + public boolean isDone() { + return isDone; + } + + public void setDone(boolean isDone) { + this.isDone = isDone; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + id; + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + Todo other = (Todo) obj; + if (id != other.id) { + return false; + } + return true; + } + + @Override + public String toString() { + return String.format( + "Todo [id=%s, user=%s, desc=%s, targetDate=%s, isDone=%s]", id, + user, desc, targetDate, isDone); + } + +} +``` +--- +### src/main/java/com/in28minutes/springboot/web/security/SecurityConfiguration.java + +```java +package com.in28minutes.springboot.web.security; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; + +@Configuration +public class SecurityConfiguration extends WebSecurityConfigurerAdapter{ + //Create User - in28Minutes/dummy + @Autowired + public void configureGlobalSecurity(AuthenticationManagerBuilder auth) + throws Exception { + auth.inMemoryAuthentication().withUser("in28Minutes").password("dummy") + .roles("USER", "ADMIN"); + } + + @Override + protected void configure(HttpSecurity http) throws Exception { + http.authorizeRequests().antMatchers("/login").permitAll() + .antMatchers("/", "/*todo*/**").access("hasRole('USER')").and() + .formLogin(); + } +} +``` +--- +### src/main/java/com/in28minutes/springboot/web/service/LoginService.java + +```java +package com.in28minutes.springboot.web.service; + +import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; + +@Service +public class LoginService { + + public boolean validateUser(String userid, String password) { + // in28minutes, dummy + return userid.equalsIgnoreCase("in28minutes") + && password.equalsIgnoreCase("dummy"); + } + +} +``` +--- +### src/main/java/com/in28minutes/springboot/web/service/TodoService.java + +```java +package com.in28minutes.springboot.web.service; + +import java.util.ArrayList; +import java.util.Date; +import java.util.Iterator; +import java.util.List; + +import org.springframework.stereotype.Service; + +import com.in28minutes.springboot.web.model.Todo; + +@Service +public class TodoService { + private static List todos = new ArrayList(); + private static int todoCount = 3; + + static { + todos.add(new Todo(1, "in28Minutes", "Learn Spring MVC", new Date(), + false)); + todos.add(new Todo(2, "in28Minutes", "Learn Struts", new Date(), false)); + todos.add(new Todo(3, "in28Minutes", "Learn Hibernate", new Date(), + false)); + } + + public List retrieveTodos(String user) { + List filteredTodos = new ArrayList(); + for (Todo todo : todos) { + if (todo.getUser().equalsIgnoreCase(user)) { + filteredTodos.add(todo); + } + } + return filteredTodos; + } + + public Todo retrieveTodo(int id) { + for (Todo todo : todos) { + if (todo.getId()==id) { + return todo; + } + } + return null; + } + + public void updateTodo(Todo todo){ + todos.remove(todo); + todos.add(todo); + } + + public void addTodo(String name, String desc, Date targetDate, + boolean isDone) { + todos.add(new Todo(++todoCount, name, desc, targetDate, isDone)); + } + + public void deleteTodo(int id) { + Iterator iterator = todos.iterator(); + while (iterator.hasNext()) { + Todo todo = iterator.next(); + if (todo.getId() == id) { + iterator.remove(); + } + } + } +} +``` +--- +### src/main/java/com/in28minutes/springboot/web/SpringBootFirstWebApplication.java + +```java +package com.in28minutes.springboot.web; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.ComponentScan; + +@SpringBootApplication +@ComponentScan("com.in28minutes.springboot.web") +public class SpringBootFirstWebApplication { + + public static void main(String[] args) { + SpringApplication.run(SpringBootFirstWebApplication.class, args); + } +} +``` +--- +### src/main/resources/application.properties + +``` +spring.mvc.view.prefix=/WEB-INF/jsp/ +spring.mvc.view.suffix=.jsp +logging.level.org.springframework.web=INFO +``` +--- +### src/main/webapp/WEB-INF/jsp/common/footer.jspf + +``` + + + + + + + +``` +--- +### src/main/webapp/WEB-INF/jsp/common/header.jspf + +``` +<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%> +<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt"%> +<%@taglib uri="http://www.springframework.org/tags/form" prefix="form"%> + + + + +First Web Application + + + + + +``` +--- +### src/main/webapp/WEB-INF/jsp/common/navigation.jspf + +``` + +``` +--- +### src/main/webapp/WEB-INF/jsp/list-todos.jsp + +``` +<%@ include file="common/header.jspf" %> +<%@ include file="common/navigation.jspf" %> + +
+ + + + + + + + + + + + + + + + + + + + + + +
Your todos are
DescriptionTarget DateIs it Done?
${todo.desc}${todo.done}UpdateDelete
+ +
+<%@ include file="common/footer.jspf" %> +``` +--- +### src/main/webapp/WEB-INF/jsp/todo.jsp + +``` +<%@ include file="common/header.jspf" %> +<%@ include file="common/navigation.jspf" %> +
+ + +
+ Description + + +
+ +
+ Target Date + + +
+ + +
+
+<%@ include file="common/footer.jspf" %> +``` +--- +### src/main/webapp/WEB-INF/jsp/welcome.jsp + +``` +<%@ include file="common/header.jspf"%> +<%@ include file="common/navigation.jspf"%> +
+ Welcome ${name}!! Click here to manage your + todo's. +
+<%@ include file="common/footer.jspf"%> +``` +--- +### src/test/java/com/in28minutes/springboot/web/SpringBootFirstWebApplicationTests.java + +```java +package com.in28minutes.springboot.web; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; + +@RunWith(SpringRunner.class) +@SpringBootTest +public class SpringBootFirstWebApplicationTests { + + @Test + public void contextLoads() { + } + +} +``` +--- +### todo.txt + +``` +Implementing Server Side Validation +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Command Bean or Form Backing Bean + +Add Validation +Use Validation on Controller +Display Errors in View + +Command Bean +~~~~~~~~~~~~ +Controller +View - Spring Form Tags + + + +LoginController -> adds name to model +welcome.jsp -> shows ${name} + +TodoController -> redirects to list-todos.jsp +${name} is empty + + +Component, Service, Repository, Controller +Autowired +ComponentScan + +Field dummyService in com.in28minutes.springboot.web.controller.LoginController +required a bean of type 'com.in28minutes.dummy.DummyService' +that could not be found. + +Spring Boot Starter Parent +Spring Boot Starter Web +@SpringBootApplication +Auto Configuration + +Dispatcher Servlet + +/login => "login" + +"login" => src/main/webapp/WEB-INF/jsp/login.jsp + + +Search for a view named "login" + + + +/login => LoginController +``` +--- diff --git a/02.Spring-Boot-Web-Application/Step23.zip b/02.Spring-Boot-Web-Application/Step23.zip new file mode 100644 index 0000000000000000000000000000000000000000..bdeb466b44c54e5710105dc0fb2c8d1c63d40b7f GIT binary patch literal 22400 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 literal 0 HcmV?d00001 diff --git a/02.Spring-Boot-Web-Application/Step24.md b/02.Spring-Boot-Web-Application/Step24.md new file mode 100644 index 0000000..e3b786a --- /dev/null +++ b/02.Spring-Boot-Web-Application/Step24.md @@ -0,0 +1,34 @@ +## What we will do: +- Remove Hardcoding of User Name +- Remove LoginService +- Rename LoginController to WelcomeController +- Add Logout Functionality + +## Useful Snippets +``` + private String getLoggedInUserName(ModelMap model) { + Object principal = SecurityContextHolder.getContext() + .getAuthentication().getPrincipal(); + + if (principal instanceof UserDetails) + return ((UserDetails) principal).getUsername(); + + return principal.toString(); + } + + + + @RequestMapping(value = "/logout", method = RequestMethod.GET) + public String logout(HttpServletRequest request, + HttpServletResponse response) { + Authentication auth = SecurityContextHolder.getContext() + .getAuthentication(); + if (auth != null) { + new SecurityContextLogoutHandler().logout(request, response, auth); + } + return "redirect:/"; + } + +``` diff --git a/02.Spring-Boot-Web-Application/Step25.md b/02.Spring-Boot-Web-Application/Step25.md new file mode 100644 index 0000000..93b8dd3 --- /dev/null +++ b/02.Spring-Boot-Web-Application/Step25.md @@ -0,0 +1,795 @@ +## What we will do: +- Basic Exception Handling +- Exception Handling is a cross cutting concern +- Do not handle exceptions in Controllers or Services, if you cannot add value to them. +- Bit of refactoring on the controllers +- Whitelabel Error Page provided by default by Spring Boot + - You can see a few details of the errors +- We can customize if we would want to +- @ControllerAdvice and Controller Specific Exception Handling +- Handling Errors thrown from Views + +## Useful Snippets +``` +@Controller("error") +public class ExceptionController { + + private Log logger = LogFactory.getLog(ExceptionController.class); + + @ExceptionHandler(Exception.class) + public ModelAndView handleError(HttpServletRequest req, Exception ex) { + logger.error("Request: " + req.getRequestURL() + " raised " + ex); + + ModelAndView mav = new ModelAndView(); + mav.addObject("exception", ex); + mav.addObject("url", req.getRequestURL()); + mav.setViewName("error"); + return mav; + } +} + +``` + +## Files List + +### pom.xml + +```xml + + + 4.0.0 + + com.in28minutes.springboot.web + spring-boot-first-web-application + 0.0.1-SNAPSHOT + jar + + spring-boot-first-web-application + Demo project for Spring Boot + + + org.springframework.boot + spring-boot-starter-parent + 1.4.3.RELEASE + + + + + UTF-8 + UTF-8 + 1.8 + + + + + org.springframework.boot + spring-boot-starter-web + + + + org.springframework.boot + spring-boot-starter-security + + + + javax.servlet + jstl + + + + org.webjars + bootstrap + 3.3.6 + + + + org.webjars + bootstrap-datepicker + 1.0.1 + + + + org.webjars + jquery + 1.9.1 + + + + org.apache.tomcat.embed + tomcat-embed-jasper + provided + + + + org.springframework.boot + spring-boot-devtools + runtime + + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + +``` +--- +### src/main/java/com/in28minutes/springboot/web/controller/ErrorController.java + +```java +package com.in28minutes.springboot.web.controller; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.servlet.ModelAndView; + +@Controller("error") +public class ErrorController { + + @ExceptionHandler(Exception.class) + public ModelAndView handleException + (HttpServletRequest request, Exception ex){ + ModelAndView mv = new ModelAndView(); + + mv.addObject("exception", ex.getLocalizedMessage()); + mv.addObject("url", request.getRequestURL()); + + mv.setViewName("error"); + return mv; + } + +} +``` +--- +### src/main/java/com/in28minutes/springboot/web/controller/LogoutController.java + +```java +package com.in28minutes.springboot.web.controller; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.web.authentication.logout.LogoutHandler; +import org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler; +import org.springframework.stereotype.Controller; +import org.springframework.ui.ModelMap; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; + +@Controller +public class LogoutController { + + @RequestMapping(value = "/logout", method = RequestMethod.GET) + public String logout(HttpServletRequest request, + HttpServletResponse response) { + + Authentication authentication = SecurityContextHolder.getContext() + .getAuthentication(); + + if (authentication != null) { + new SecurityContextLogoutHandler().logout(request, response, + authentication); + } + + return "redirect:/"; + } +} +``` +--- +### src/main/java/com/in28minutes/springboot/web/controller/TodoController.java + +```java +package com.in28minutes.springboot.web.controller; + +import java.text.SimpleDateFormat; +import java.util.Date; + +import javax.validation.Valid; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.propertyeditors.CustomDateEditor; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.stereotype.Controller; +import org.springframework.ui.ModelMap; +import org.springframework.validation.BindingResult; +import org.springframework.web.bind.WebDataBinder; +import org.springframework.web.bind.annotation.InitBinder; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RequestParam; + +import com.in28minutes.springboot.web.model.Todo; +import com.in28minutes.springboot.web.service.TodoService; + +@Controller +public class TodoController { + + @Autowired + TodoService service; + + @InitBinder + public void initBinder(WebDataBinder binder) { + // Date - dd/MM/yyyy + SimpleDateFormat dateFormat = new SimpleDateFormat("dd/MM/yyyy"); + binder.registerCustomEditor(Date.class, new CustomDateEditor( + dateFormat, false)); + } + + @RequestMapping(value = "/list-todos", method = RequestMethod.GET) + public String showTodos(ModelMap model) { + String name = getLoggedInUserName(model); + model.put("todos", service.retrieveTodos(name)); + return "list-todos"; + } + + private String getLoggedInUserName(ModelMap model) { + Object principal = SecurityContextHolder.getContext() + .getAuthentication().getPrincipal(); + + if (principal instanceof UserDetails) { + return ((UserDetails) principal).getUsername(); + } + + return principal.toString(); + } + + @RequestMapping(value = "/add-todo", method = RequestMethod.GET) + public String showAddTodoPage(ModelMap model) { + model.addAttribute("todo", new Todo(0, getLoggedInUserName(model), + "Default Desc", new Date(), false)); + return "todo"; + } + + @RequestMapping(value = "/delete-todo", method = RequestMethod.GET) + public String deleteTodo(@RequestParam int id) { + + if(id==1) + throw new RuntimeException("Something went wrong"); + + service.deleteTodo(id); + return "redirect:/list-todos"; + } + + @RequestMapping(value = "/update-todo", method = RequestMethod.GET) + public String showUpdateTodoPage(@RequestParam int id, ModelMap model) { + Todo todo = service.retrieveTodo(id); + model.put("todo", todo); + return "todo"; + } + + @RequestMapping(value = "/update-todo", method = RequestMethod.POST) + public String updateTodo(ModelMap model, @Valid Todo todo, + BindingResult result) { + + if (result.hasErrors()) { + return "todo"; + } + + todo.setUser(getLoggedInUserName(model)); + + service.updateTodo(todo); + + return "redirect:/list-todos"; + } + + @RequestMapping(value = "/add-todo", method = RequestMethod.POST) + public String addTodo(ModelMap model, @Valid Todo todo, BindingResult result) { + + if (result.hasErrors()) { + return "todo"; + } + + service.addTodo(getLoggedInUserName(model), todo.getDesc(), todo.getTargetDate(), + false); + return "redirect:/list-todos"; + } +} +``` +--- +### src/main/java/com/in28minutes/springboot/web/controller/WelcomeController.java + +```java +package com.in28minutes.springboot.web.controller; + +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.stereotype.Controller; +import org.springframework.ui.ModelMap; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; + +@Controller +public class WelcomeController { + + @RequestMapping(value = "/", method = RequestMethod.GET) + public String showWelcomePage(ModelMap model) { + model.put("name", getLoggedinUserName()); + return "welcome"; + } + + private String getLoggedinUserName() { + Object principal = SecurityContextHolder.getContext() + .getAuthentication().getPrincipal(); + + if (principal instanceof UserDetails) { + return ((UserDetails) principal).getUsername(); + } + + return principal.toString(); + } + +} +``` +--- +### src/main/java/com/in28minutes/springboot/web/model/Todo.java + +```java +package com.in28minutes.springboot.web.model; + +import java.util.Date; + +import javax.validation.constraints.Size; + +public class Todo { + private int id; + private String user; + + @Size(min=10, message="Enter at least 10 Characters...") + private String desc; + + private Date targetDate; + private boolean isDone; + + public Todo() { + super(); + } + + public Todo(int id, String user, String desc, Date targetDate, + boolean isDone) { + super(); + this.id = id; + this.user = user; + this.desc = desc; + this.targetDate = targetDate; + this.isDone = isDone; + } + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public String getUser() { + return user; + } + + public void setUser(String user) { + this.user = user; + } + + public String getDesc() { + return desc; + } + + public void setDesc(String desc) { + this.desc = desc; + } + + public Date getTargetDate() { + return targetDate; + } + + public void setTargetDate(Date targetDate) { + this.targetDate = targetDate; + } + + public boolean isDone() { + return isDone; + } + + public void setDone(boolean isDone) { + this.isDone = isDone; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + id; + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + Todo other = (Todo) obj; + if (id != other.id) { + return false; + } + return true; + } + + @Override + public String toString() { + return String.format( + "Todo [id=%s, user=%s, desc=%s, targetDate=%s, isDone=%s]", id, + user, desc, targetDate, isDone); + } + +} +``` +--- +### src/main/java/com/in28minutes/springboot/web/security/SecurityConfiguration.java + +```java +package com.in28minutes.springboot.web.security; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; + +@Configuration +public class SecurityConfiguration extends WebSecurityConfigurerAdapter{ + //Create User - in28Minutes/dummy + @Autowired + public void configureGlobalSecurity(AuthenticationManagerBuilder auth) + throws Exception { + auth.inMemoryAuthentication().withUser("in28Minutes").password("dummy") + .roles("USER", "ADMIN"); + } + + @Override + protected void configure(HttpSecurity http) throws Exception { + http.authorizeRequests().antMatchers("/login").permitAll() + .antMatchers("/", "/*todo*/**").access("hasRole('USER')").and() + .formLogin(); + } +} +``` +--- +### src/main/java/com/in28minutes/springboot/web/service/TodoService.java + +```java +package com.in28minutes.springboot.web.service; + +import java.util.ArrayList; +import java.util.Date; +import java.util.Iterator; +import java.util.List; + +import org.springframework.stereotype.Service; + +import com.in28minutes.springboot.web.model.Todo; + +@Service +public class TodoService { + private static List todos = new ArrayList(); + private static int todoCount = 3; + + static { + todos.add(new Todo(1, "in28Minutes", "Learn Spring MVC", new Date(), + false)); + todos.add(new Todo(2, "in28Minutes", "Learn Struts", new Date(), false)); + todos.add(new Todo(3, "in28Minutes", "Learn Hibernate", new Date(), + false)); + } + + public List retrieveTodos(String user) { + List filteredTodos = new ArrayList(); + for (Todo todo : todos) { + if (todo.getUser().equalsIgnoreCase(user)) { + filteredTodos.add(todo); + } + } + return filteredTodos; + } + + public Todo retrieveTodo(int id) { + for (Todo todo : todos) { + if (todo.getId()==id) { + return todo; + } + } + return null; + } + + public void updateTodo(Todo todo){ + todos.remove(todo); + todos.add(todo); + } + + public void addTodo(String name, String desc, Date targetDate, + boolean isDone) { + todos.add(new Todo(++todoCount, name, desc, targetDate, isDone)); + } + + public void deleteTodo(int id) { + Iterator iterator = todos.iterator(); + while (iterator.hasNext()) { + Todo todo = iterator.next(); + if (todo.getId() == id) { + iterator.remove(); + } + } + } +} +``` +--- +### src/main/java/com/in28minutes/springboot/web/SpringBootFirstWebApplication.java + +```java +package com.in28minutes.springboot.web; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.ComponentScan; + +@SpringBootApplication +@ComponentScan("com.in28minutes.springboot.web") +public class SpringBootFirstWebApplication { + + public static void main(String[] args) { + SpringApplication.run(SpringBootFirstWebApplication.class, args); + } +} +``` +--- +### src/main/resources/application.properties + +``` +spring.mvc.view.prefix=/WEB-INF/jsp/ +spring.mvc.view.suffix=.jsp +logging.level.org.springframework.web=INFO +``` +--- +### src/main/webapp/WEB-INF/jsp/common/footer.jspf + +``` + + + + + + + +``` +--- +### src/main/webapp/WEB-INF/jsp/common/header.jspf + +``` +<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%> +<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt"%> +<%@taglib uri="http://www.springframework.org/tags/form" prefix="form"%> + + + + +First Web Application + + + + + +``` +--- +### src/main/webapp/WEB-INF/jsp/common/navigation.jspf + +``` + + +``` +--- +### src/main/webapp/WEB-INF/jsp/error.jsp + +``` +<%@ include file="common/header.jspf"%> +<%@ include file="common/navigation.jspf"%> +
+An exception occurred! Please contact Support! +
+<%@ include file="common/footer.jspf"%> +``` +--- +### src/main/webapp/WEB-INF/jsp/list-todos.jsp + +``` +<%@ include file="common/header.jspf" %> +<%@ include file="common/navigation.jspf" %> + +
+ + + + + + + + + + + + + + + + + + + + + + +
Your todos are
DescriptionTarget DateIs it Done?
${todo.desc}${todo.done}UpdateDelete
+ +
+<%@ include file="common/footer.jspf" %> +``` +--- +### src/main/webapp/WEB-INF/jsp/todo.jsp + +``` +<%@ include file="common/header.jspf" %> +<%@ include file="common/navigation.jspf" %> +
+ + +
+ Description + + +
+ +
+ Target Date + + +
+ + +
+
+<%@ include file="common/footer.jspf" %> +``` +--- +### src/main/webapp/WEB-INF/jsp/welcome.jsp + +``` +<%@ include file="common/header.jspf"%> +<%@ include file="common/navigation.jspf"%> +
+ Welcome ${name}!! Click here to manage your + todo's. +
+<%@ include file="common/footer.jspf"%> +``` +--- +### src/test/java/com/in28minutes/springboot/web/SpringBootFirstWebApplicationTests.java + +```java +package com.in28minutes.springboot.web; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; + +@RunWith(SpringRunner.class) +@SpringBootTest +public class SpringBootFirstWebApplicationTests { + + @Test + public void contextLoads() { + } + +} +``` +--- +### todo.txt + +``` +Implementing Server Side Validation +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Command Bean or Form Backing Bean + +Add Validation +Use Validation on Controller +Display Errors in View + +Command Bean +~~~~~~~~~~~~ +Controller +View - Spring Form Tags + + + +LoginController -> adds name to model +welcome.jsp -> shows ${name} + +TodoController -> redirects to list-todos.jsp +${name} is empty + + +Component, Service, Repository, Controller +Autowired +ComponentScan + +Field dummyService in com.in28minutes.springboot.web.controller.LoginController +required a bean of type 'com.in28minutes.dummy.DummyService' +that could not be found. + +Spring Boot Starter Parent +Spring Boot Starter Web +@SpringBootApplication +Auto Configuration + +Dispatcher Servlet + +/login => "login" + +"login" => src/main/webapp/WEB-INF/jsp/login.jsp + + +Search for a view named "login" + + + +/login => LoginController +``` +--- diff --git a/02.Spring-Boot-Web-Application/Step25.zip b/02.Spring-Boot-Web-Application/Step25.zip new file mode 100644 index 0000000000000000000000000000000000000000..22cf7f910d0978f41efc718c45dc73ea73218fda GIT binary patch literal 23765 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 literal 0 HcmV?d00001 diff --git a/02.Spring-Boot-Web-Application/Step26.md b/02.Spring-Boot-Web-Application/Step26.md new file mode 100644 index 0000000..79cfed6 --- /dev/null +++ b/02.Spring-Boot-Web-Application/Step26.md @@ -0,0 +1,729 @@ +## What we will do: +- Basic Spring Rest Services. +- We use com.fasterxml.jackson.core:jackson-databind +- http://localhost:8080/rest/todos +- How does the magic happen? + +## Useful Snippets +``` +@RestController +public class TodoRestController { + @Autowired + private TodoService service; + + @RequestMapping(value = "/rest/todos", method = RequestMethod.GET) + public List listAllTodos() { + List users = service.retrieveTodos("in28Minutes"); + return users; + } + +} +``` + +## Files List +### /pom.xml +``` + + 4.0.0 + com.in28minutes.springboot.web + Spring-Boot-First-Web-Application + 0.0.1-SNAPSHOT + jar + + + org.springframework.boot + spring-boot-starter-parent + 1.4.0.RELEASE + + + + + + org.springframework.boot + spring-boot-starter-web + + + + org.springframework.boot + spring-boot-starter-security + + + + org.springframework.boot + spring-boot-starter-tomcat + provided + + + + org.apache.tomcat.embed + tomcat-embed-jasper + provided + + + + javax.servlet + jstl + + + + org.webjars + bootstrap + 3.3.6 + + + + org.webjars + jquery + 1.9.1 + + + + org.webjars + bootstrap-datepicker + 1.0.1 + + + + org.springframework.boot + spring-boot-devtools + true + + + + + + + 1.8 + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + +``` +### /src/main/java/com/in28minutes/springboot/web/controller/ExceptionController.java +``` +package com.in28minutes.springboot.web.controller; + +import javax.servlet.http.HttpServletRequest; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.servlet.ModelAndView; + +@Controller("error") +public class ExceptionController { + + private Log logger = LogFactory.getLog(ExceptionController.class); + + @ExceptionHandler(Exception.class) + public ModelAndView handleError(HttpServletRequest req, Exception ex) { + logger.error("Request: " + req.getRequestURL() + " raised " + ex); + + ModelAndView mav = new ModelAndView(); + mav.addObject("exception", ex); + mav.addObject("url", req.getRequestURL()); + mav.setViewName("error"); + return mav; + } +} +``` +### /src/main/java/com/in28minutes/springboot/web/controller/LogoutController.java +``` +package com.in28minutes.springboot.web.controller; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; + +@Controller +public class LogoutController { + + @RequestMapping(value = "/logout", method = RequestMethod.GET) + public String logout(HttpServletRequest request, + HttpServletResponse response) { + Authentication auth = SecurityContextHolder.getContext() + .getAuthentication(); + if (auth != null) { + new SecurityContextLogoutHandler().logout(request, response, auth); + } + return "redirect:/"; + } +} +``` +### /src/main/java/com/in28minutes/springboot/web/controller/TodoController.java +``` +package com.in28minutes.springboot.web.controller; + +import java.text.SimpleDateFormat; +import java.util.Date; + +import javax.validation.Valid; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.propertyeditors.CustomDateEditor; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.stereotype.Controller; +import org.springframework.ui.ModelMap; +import org.springframework.validation.BindingResult; +import org.springframework.web.bind.WebDataBinder; +import org.springframework.web.bind.annotation.InitBinder; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RequestParam; + +import com.in28minutes.springboot.web.model.Todo; +import com.in28minutes.springboot.web.service.TodoService; + +@Controller +public class TodoController { + + @Autowired + private TodoService service; + + @InitBinder + protected void initBinder(WebDataBinder binder) { + SimpleDateFormat dateFormat = new SimpleDateFormat("dd/MM/yyyy"); + binder.registerCustomEditor(Date.class, new CustomDateEditor( + dateFormat, false)); + } + + @RequestMapping(value = "/list-todos", method = RequestMethod.GET) + public String showTodosList(ModelMap model) { + String user = getLoggedInUserName(); + model.addAttribute("todos", service.retrieveTodos(user)); + return "list-todos"; + } + + @RequestMapping(value = "/add-todo", method = RequestMethod.GET) + public String showAddTodoPage(ModelMap model) { + model.addAttribute("todo", new Todo()); + return "todo"; + } + + @RequestMapping(value = "/add-todo", method = RequestMethod.POST) + public String addTodo(ModelMap model, @Valid Todo todo, BindingResult result) { + + if (result.hasErrors()) { + return "todo"; + } + + service.addTodo(getLoggedInUserName(), todo.getDesc(), + todo.getTargetDate(), false); + model.clear();// to prevent request parameter "name" to be passed + return "redirect:/list-todos"; + } + + private String getLoggedInUserName() { + Object principal = SecurityContextHolder.getContext() + .getAuthentication().getPrincipal(); + + if (principal instanceof UserDetails) { + return ((UserDetails) principal).getUsername(); + } + + return principal.toString(); + } + + @RequestMapping(value = "/update-todo", method = RequestMethod.GET) + public String showUpdateTodoPage(ModelMap model, @RequestParam int id) { + model.addAttribute("todo", service.retrieveTodo(id)); + return "todo"; + } + + @RequestMapping(value = "/update-todo", method = RequestMethod.POST) + public String updateTodo(ModelMap model, @Valid Todo todo, + BindingResult result) { + if (result.hasErrors()) { + return "todo"; + } + + todo.setUser(getLoggedInUserName()); + service.updateTodo(todo); + + model.clear();// to prevent request parameter "name" to be passed + return "redirect:/list-todos"; + } + + @RequestMapping(value = "/delete-todo", method = RequestMethod.GET) + public String deleteTodo(@RequestParam int id) { + service.deleteTodo(id); + + return "redirect:/list-todos"; + } + +} +``` +### /src/main/java/com/in28minutes/springboot/web/controller/TodoRestController.java +``` +package com.in28minutes.springboot.web.controller; + +import java.util.List; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RestController; + +import com.in28minutes.springboot.web.model.Todo; +import com.in28minutes.springboot.web.service.TodoService; + +@RestController +public class TodoRestController { + @Autowired + private TodoService service; + + @RequestMapping(value = "/rest/todos", method = RequestMethod.GET) + public List listAllTodos() { + List users = service.retrieveTodos("in28Minutes"); + return users; + } + +} +``` +### /src/main/java/com/in28minutes/springboot/web/controller/WelcomeController.java +``` +package com.in28minutes.springboot.web.controller; + +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.stereotype.Controller; +import org.springframework.ui.ModelMap; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; + +@Controller +public class WelcomeController { + + @RequestMapping(value = "/", method = RequestMethod.GET) + public String showWelcomePage(ModelMap model) { + model.put("name", getLoggedInUserName()); + return "welcome"; + } + + private String getLoggedInUserName() { + Object principal = SecurityContextHolder.getContext() + .getAuthentication().getPrincipal(); + + if (principal instanceof UserDetails) { + return ((UserDetails) principal).getUsername(); + } + + return principal.toString(); + } + +} +``` +### /src/main/java/com/in28minutes/springboot/web/model/Todo.java +``` +package com.in28minutes.springboot.web.model; + +import java.util.Date; + +import javax.validation.constraints.Size; + +public class Todo { + + private int id; + + private String user; + + @Size(min = 10, message = "Enter atleast 10 Characters.") + private String desc; + + private Date targetDate; + + private boolean isDone; + + public Todo() { + super(); + } + + public Todo(int id, String user, String desc, Date targetDate, + boolean isDone) { + super(); + this.id = id; + this.user = user; + this.desc = desc; + this.targetDate = targetDate; + this.isDone = isDone; + } + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public String getUser() { + return user; + } + + public void setUser(String user) { + this.user = user; + } + + public String getDesc() { + return desc; + } + + public void setDesc(String desc) { + this.desc = desc; + } + + public Date getTargetDate() { + return targetDate; + } + + public void setTargetDate(Date targetDate) { + this.targetDate = targetDate; + } + + public boolean isDone() { + return isDone; + } + + public void setDone(boolean isDone) { + this.isDone = isDone; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + id; + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + Todo other = (Todo) obj; + if (id != other.id) { + return false; + } + return true; + } + + @Override + public String toString() { + return String.format( + "Todo [id=%s, user=%s, desc=%s, targetDate=%s, isDone=%s]", id, + user, desc, targetDate, isDone); + } + +} +``` +### /src/main/java/com/in28minutes/springboot/web/security/SecurityConfiguration.java +``` +package com.in28minutes.springboot.web.security; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; + +@Configuration +@EnableWebSecurity +public class SecurityConfiguration extends WebSecurityConfigurerAdapter { + + @Autowired + public void configureGlobalSecurity(AuthenticationManagerBuilder auth) + throws Exception { + auth.inMemoryAuthentication().withUser("in28Minutes").password("dummy") + .roles("USER", "ADMIN"); + } + + @Override + protected void configure(HttpSecurity http) throws Exception { + http.authorizeRequests().antMatchers("/login").permitAll() + .antMatchers("/", "/*todo*/**").access("hasRole('USER')").and() + .formLogin(); + } +} +``` +### /src/main/java/com/in28minutes/springboot/web/service/LoginService.java +``` +package com.in28minutes.springboot.web.service; + +import org.springframework.stereotype.Component; + +@Component +public class LoginService { + public boolean validateUser(String user, String password) { + return user.equalsIgnoreCase("in28Minutes") && password.equals("dummy"); + } +} +``` +### /src/main/java/com/in28minutes/springboot/web/service/TodoService.java +``` +package com.in28minutes.springboot.web.service; + +import java.util.ArrayList; +import java.util.Date; +import java.util.Iterator; +import java.util.List; + +import org.springframework.stereotype.Service; + +import com.in28minutes.springboot.web.model.Todo; + +@Service +public class TodoService { + private static List todos = new ArrayList(); + private static int todoCount = 3; + + static { + todos.add(new Todo(1, "in28Minutes", "Learn Spring MVC", new Date(), + false)); + todos.add(new Todo(2, "in28Minutes", "Learn Struts", new Date(), false)); + todos.add(new Todo(3, "in28Minutes", "Learn Hibernate", new Date(), + false)); + } + + public List retrieveTodos(String user) { + List filteredTodos = new ArrayList(); + for (Todo todo : todos) { + if (todo.getUser().equalsIgnoreCase(user)) { + filteredTodos.add(todo); + } + } + return filteredTodos; + } + + public Todo retrieveTodo(int id) { + for (Todo todo : todos) { + if (todo.getId() == id) { + return todo; + } + } + return null; + } + + public void updateTodo(Todo todo) { + todos.remove(todo); + todos.add(todo); + } + + public void addTodo(String name, String desc, Date targetDate, + boolean isDone) { + todos.add(new Todo(++todoCount, name, desc, targetDate, isDone)); + } + + public void deleteTodo(int id) { + Iterator iterator = todos.iterator(); + while (iterator.hasNext()) { + Todo todo = iterator.next(); + if (todo.getId() == id) { + iterator.remove(); + } + } + } +} +``` +### /src/main/java/com/in28minutes/springboot/web/WebApplication.java +``` +package com.in28minutes.springboot.web; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.ApplicationContext; + +@SpringBootApplication +public class WebApplication { + + public static void main(String[] args) { + ApplicationContext ctx = SpringApplication.run(WebApplication.class, args); + } +} +``` +### /src/main/resources/application.properties +``` +spring.mvc.view.prefix: /WEB-INF/jsp/ +spring.mvc.view.suffix: .jsp +logging.level.: DEBUG +``` +### /src/main/webapp/WEB-INF/jsp/common/footer.jspf +``` + + + + + + +``` +### /src/main/webapp/WEB-INF/jsp/common/header.jspf +``` +<%@taglib uri="http://www.springframework.org/tags/form" prefix="form"%> +<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%> +<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt"%> + + +Todos Application + + + + +``` +### /src/main/webapp/WEB-INF/jsp/common/navigation.jspf +``` + +``` +### /src/main/webapp/WEB-INF/jsp/error.jsp +``` +<%@ include file="common/header.jspf"%> +<%@ include file="common/navigation.jspf"%> +
+ Application has encountered an error. Please contact support on ... +
+ +<%@ include file="common/footer.jspf"%> +``` +### /src/main/webapp/WEB-INF/jsp/list-todos.jsp +``` +<%@ include file="common/header.jspf"%> +<%@ include file="common/navigation.jspf"%> + +
+ + + + + + + + + + + + + + + + + + + + +
Your Todos are
DescriptionDateCompleted
${todo.desc}${todo.done}Edit Delete +
+
+ Add +
+
+<%@ include file="common/footer.jspf"%> +``` +### /src/main/webapp/WEB-INF/jsp/login.jsp +``` +<%@ include file="common/header.jspf"%> +<%@ include file="common/navigation.jspf"%> +
+

+ ${errorMessage} +

+
+
+ +
+
+ +
+ +
+ +
+ +<%@ include file="common/footer.jspf"%> +``` +### /src/main/webapp/WEB-INF/jsp/todo.jsp +``` +<%@ include file="common/header.jspf"%> +<%@ include file="common/navigation.jspf"%> +
+ + +
+ Description + + +
+
+ Target Date + + +
+ +
+
+ +<%@ include file="common/footer.jspf"%> + + +``` +### /src/main/webapp/WEB-INF/jsp/welcome.jsp +``` +<%@ include file="common/header.jspf"%> +<%@ include file="common/navigation.jspf"%> +
+ Welcome ${name}. You are now authenticated. +
+ +<%@ include file="common/footer.jspf"%> +``` diff --git a/02.Spring-Boot-Web-Application/Step27.md b/02.Spring-Boot-Web-Application/Step27.md new file mode 100644 index 0000000..445fb3b --- /dev/null +++ b/02.Spring-Boot-Web-Application/Step27.md @@ -0,0 +1,15 @@ +## What we will do: +- One More Spring Rest Services. +- @PathVariable("id") int id +- http://localhost:8080/rest/todos/1 + +## Useful Snippets + +``` + @RequestMapping(value = "/rest/todos/{id}", method = RequestMethod.GET) + public Todo retrieveTodo(@PathVariable("id") int id) { + return service.retrieveTodo(id); + } + +``` +## Files List diff --git a/02.Spring-Boot-Web-Application/Step27.zip b/02.Spring-Boot-Web-Application/Step27.zip new file mode 100644 index 0000000000000000000000000000000000000000..8a4c9f672b46b782a0640ec5c5ff9097d1a95432 GIT binary patch literal 14664 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 + + 4.0.0 + + com.in28minutes.springboot.web + spring-boot-first-web-application-git + 0.0.1-SNAPSHOT + jar + + spring-boot-first-web-application + Demo project for Spring Boot + + + org.springframework.boot + spring-boot-starter-parent + 1.4.3.RELEASE + + + + + UTF-8 + UTF-8 + 1.8 + + + + + org.springframework.boot + spring-boot-starter-web + + + + org.springframework.boot + spring-boot-starter-security + + + + javax.servlet + jstl + + + + org.webjars + bootstrap + 3.3.6 + + + + org.webjars + bootstrap-datepicker + 1.0.1 + + + + org.webjars + jquery + 1.9.1 + + + + org.apache.tomcat.embed + tomcat-embed-jasper + provided + + + + org.springframework.boot + spring-boot-devtools + runtime + + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + diff --git a/02.Spring-Boot-Web-Application/src/.DS_Store b/02.Spring-Boot-Web-Application/src/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..0787fb7b15229e3b1308635a538cf207523385ad GIT binary patch literal 6148 zcmeH~K?=e^3`G;|LU7Zi%h`AUZ!na40xzH-h@ce&UC+_|$ppdbT10*z`IF3q(zoa| zBBI@GzY=LhWCk~trG=3x@0kh68F=pTt>P!&@2c$2NroNPq-LfCNb3 zhX~lc4V%rYGLirZkie6GeIF9sG>4X|{^>yQ5dhkt?1r_^63}D?Xbvq^QGsbp4;roN zV~Ewg9h%}?4lPw{yJ!p_8c$Z6VqjX^MH3R3W)}t$Ab}BqY0X=^|JU$O^Z%%YDG89k zpApce-}D_`D(=>|*R%RIs todos = new ArrayList(); + private static int todoCount = 3; + + static { + todos.add(new Todo(1, "in28Minutes", "Learn Spring MVC", new Date(), + false)); + todos.add(new Todo(2, "in28Minutes", "Learn Struts", new Date(), false)); + todos.add(new Todo(3, "in28Minutes", "Learn Hibernate", new Date(), + false)); + } + + public List retrieveTodos(String user) { + List filteredTodos = new ArrayList(); + for (Todo todo : todos) { + if (todo.getUser().equalsIgnoreCase(user)) { + filteredTodos.add(todo); + } + } + return filteredTodos; + } + + public Todo retrieveTodo(int id) { + for (Todo todo : todos) { + if (todo.getId()==id) { + return todo; + } + } + return null; + } + + public void updateTodo(Todo todo){ + todos.remove(todo); + todos.add(todo); + } + + public void addTodo(String name, String desc, Date targetDate, + boolean isDone) { + todos.add(new Todo(++todoCount, name, desc, targetDate, isDone)); + } + + public void deleteTodo(int id) { + Iterator iterator = todos.iterator(); + while (iterator.hasNext()) { + Todo todo = iterator.next(); + if (todo.getId() == id) { + iterator.remove(); + } + } + } +} \ No newline at end of file diff --git a/02.Spring-Boot-Web-Application/src/main/resources/application.properties b/02.Spring-Boot-Web-Application/src/main/resources/application.properties new file mode 100644 index 0000000..bc7d47b --- /dev/null +++ b/02.Spring-Boot-Web-Application/src/main/resources/application.properties @@ -0,0 +1,3 @@ +spring.mvc.view.prefix=/WEB-INF/jsp/ +spring.mvc.view.suffix=.jsp +logging.level.org.springframework.web=INFO \ No newline at end of file diff --git a/02.Spring-Boot-Web-Application/src/main/webapp/WEB-INF/jsp/common/footer.jspf b/02.Spring-Boot-Web-Application/src/main/webapp/WEB-INF/jsp/common/footer.jspf new file mode 100644 index 0000000..879b276 --- /dev/null +++ b/02.Spring-Boot-Web-Application/src/main/webapp/WEB-INF/jsp/common/footer.jspf @@ -0,0 +1,12 @@ + + + + + + + \ No newline at end of file diff --git a/02.Spring-Boot-Web-Application/src/main/webapp/WEB-INF/jsp/common/header.jspf b/02.Spring-Boot-Web-Application/src/main/webapp/WEB-INF/jsp/common/header.jspf new file mode 100644 index 0000000..6c8761f --- /dev/null +++ b/02.Spring-Boot-Web-Application/src/main/webapp/WEB-INF/jsp/common/header.jspf @@ -0,0 +1,14 @@ +<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%> +<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt"%> +<%@taglib uri="http://www.springframework.org/tags/form" prefix="form"%> + + + + +First Web Application + + + + + diff --git a/02.Spring-Boot-Web-Application/src/main/webapp/WEB-INF/jsp/common/navigation.jspf b/02.Spring-Boot-Web-Application/src/main/webapp/WEB-INF/jsp/common/navigation.jspf new file mode 100644 index 0000000..6af8ff1 --- /dev/null +++ b/02.Spring-Boot-Web-Application/src/main/webapp/WEB-INF/jsp/common/navigation.jspf @@ -0,0 +1,15 @@ + + diff --git a/02.Spring-Boot-Web-Application/src/main/webapp/WEB-INF/jsp/error.jsp b/02.Spring-Boot-Web-Application/src/main/webapp/WEB-INF/jsp/error.jsp new file mode 100644 index 0000000..d44949d --- /dev/null +++ b/02.Spring-Boot-Web-Application/src/main/webapp/WEB-INF/jsp/error.jsp @@ -0,0 +1,6 @@ +<%@ include file="common/header.jspf"%> +<%@ include file="common/navigation.jspf"%> +
+An exception occurred! Please contact Support! +
+<%@ include file="common/footer.jspf"%> \ No newline at end of file diff --git a/02.Spring-Boot-Web-Application/src/main/webapp/WEB-INF/jsp/list-todos.jsp b/02.Spring-Boot-Web-Application/src/main/webapp/WEB-INF/jsp/list-todos.jsp new file mode 100644 index 0000000..9e86ebe --- /dev/null +++ b/02.Spring-Boot-Web-Application/src/main/webapp/WEB-INF/jsp/list-todos.jsp @@ -0,0 +1,34 @@ +<%@ include file="common/header.jspf" %> +<%@ include file="common/navigation.jspf" %> + +
+ + + + + + + + + + + + + + + + + + + + + + +
Your todos are
DescriptionTarget DateIs it Done?
${todo.desc}${todo.done}UpdateDelete
+ +
+<%@ include file="common/footer.jspf" %> \ No newline at end of file diff --git a/02.Spring-Boot-Web-Application/src/main/webapp/WEB-INF/jsp/todo.jsp b/02.Spring-Boot-Web-Application/src/main/webapp/WEB-INF/jsp/todo.jsp new file mode 100644 index 0000000..9860549 --- /dev/null +++ b/02.Spring-Boot-Web-Application/src/main/webapp/WEB-INF/jsp/todo.jsp @@ -0,0 +1,23 @@ +<%@ include file="common/header.jspf" %> +<%@ include file="common/navigation.jspf" %> +
+ + +
+ Description + + +
+ +
+ Target Date + + +
+ + +
+
+<%@ include file="common/footer.jspf" %> \ No newline at end of file diff --git a/02.Spring-Boot-Web-Application/src/main/webapp/WEB-INF/jsp/welcome.jsp b/02.Spring-Boot-Web-Application/src/main/webapp/WEB-INF/jsp/welcome.jsp new file mode 100644 index 0000000..51f67df --- /dev/null +++ b/02.Spring-Boot-Web-Application/src/main/webapp/WEB-INF/jsp/welcome.jsp @@ -0,0 +1,7 @@ +<%@ include file="common/header.jspf"%> +<%@ include file="common/navigation.jspf"%> +
+ Welcome ${name}!! Click here to manage your + todo's. +
+<%@ include file="common/footer.jspf"%> \ No newline at end of file diff --git a/02.Spring-Boot-Web-Application/src/test/java/com/in28minutes/springboot/web/SpringBootFirstWebApplicationTests.java b/02.Spring-Boot-Web-Application/src/test/java/com/in28minutes/springboot/web/SpringBootFirstWebApplicationTests.java new file mode 100644 index 0000000..f207136 --- /dev/null +++ b/02.Spring-Boot-Web-Application/src/test/java/com/in28minutes/springboot/web/SpringBootFirstWebApplicationTests.java @@ -0,0 +1,16 @@ +package com.in28minutes.springboot.web; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; + +@RunWith(SpringRunner.class) +@SpringBootTest +public class SpringBootFirstWebApplicationTests { + + @Test + public void contextLoads() { + } + +} diff --git a/02.Spring-Boot-Web-Application/target/classes/META-INF/MANIFEST.MF b/02.Spring-Boot-Web-Application/target/classes/META-INF/MANIFEST.MF new file mode 100644 index 0000000..2aec69c --- /dev/null +++ b/02.Spring-Boot-Web-Application/target/classes/META-INF/MANIFEST.MF @@ -0,0 +1,11 @@ +Manifest-Version: 1.0 +Implementation-Title: spring-boot-first-web-application +Implementation-Version: 0.0.1-SNAPSHOT +Built-By: rangaraokaranam +Implementation-Vendor-Id: com.in28minutes.springboot.web +Build-Jdk: 1.8.0_31 +Implementation-URL: http://projects.spring.io/spring-boot/spring-boot- + first-web-application-git/ +Created-By: Maven Integration for Eclipse +Implementation-Vendor: Pivotal Software, Inc. + diff --git a/02.Spring-Boot-Web-Application/target/classes/META-INF/maven/com.in28minutes.springboot.web/spring-boot-first-web-application-git/pom.properties b/02.Spring-Boot-Web-Application/target/classes/META-INF/maven/com.in28minutes.springboot.web/spring-boot-first-web-application-git/pom.properties new file mode 100644 index 0000000..b7fed34 --- /dev/null +++ b/02.Spring-Boot-Web-Application/target/classes/META-INF/maven/com.in28minutes.springboot.web/spring-boot-first-web-application-git/pom.properties @@ -0,0 +1,7 @@ +#Generated by Maven Integration for Eclipse +#Sun Jul 30 12:42:04 IST 2017 +version=0.0.1-SNAPSHOT +groupId=com.in28minutes.springboot.web +m2e.projectName=spring-boot-first-web-application-git +m2e.projectLocation=/in28Minutes/git/SpringBootWebApplicationStepByStep +artifactId=spring-boot-first-web-application-git diff --git a/02.Spring-Boot-Web-Application/target/classes/META-INF/maven/com.in28minutes.springboot.web/spring-boot-first-web-application-git/pom.xml b/02.Spring-Boot-Web-Application/target/classes/META-INF/maven/com.in28minutes.springboot.web/spring-boot-first-web-application-git/pom.xml new file mode 100644 index 0000000..660b6d1 --- /dev/null +++ b/02.Spring-Boot-Web-Application/target/classes/META-INF/maven/com.in28minutes.springboot.web/spring-boot-first-web-application-git/pom.xml @@ -0,0 +1,89 @@ + + + 4.0.0 + + com.in28minutes.springboot.web + spring-boot-first-web-application-git + 0.0.1-SNAPSHOT + jar + + spring-boot-first-web-application + Demo project for Spring Boot + + + org.springframework.boot + spring-boot-starter-parent + 1.4.3.RELEASE + + + + + UTF-8 + UTF-8 + 1.8 + + + + + org.springframework.boot + spring-boot-starter-web + + + + org.springframework.boot + spring-boot-starter-security + + + + javax.servlet + jstl + + + + org.webjars + bootstrap + 3.3.6 + + + + org.webjars + bootstrap-datepicker + 1.0.1 + + + + org.webjars + jquery + 1.9.1 + + + + org.apache.tomcat.embed + tomcat-embed-jasper + provided + + + + org.springframework.boot + spring-boot-devtools + runtime + + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + diff --git a/02.Spring-Boot-Web-Application/target/classes/application.properties b/02.Spring-Boot-Web-Application/target/classes/application.properties new file mode 100644 index 0000000..bc7d47b --- /dev/null +++ b/02.Spring-Boot-Web-Application/target/classes/application.properties @@ -0,0 +1,3 @@ +spring.mvc.view.prefix=/WEB-INF/jsp/ +spring.mvc.view.suffix=.jsp +logging.level.org.springframework.web=INFO \ No newline at end of file diff --git a/03.JUnit-Introduction-In-5-Steps/.DS_Store b/03.JUnit-Introduction-In-5-Steps/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..13215867a9da5d78738959a78848db941cfa2478 GIT binary patch literal 6148 zcmeHK!A`?440X0121uyK9m5~6Kd7c6aYX75AUZUV)^-{l61Vab`~x3^#GN1EIWb+b zvMZX9Ejcf><2ZS(;)aOKe7l$tr9?D=GLBBr{37gU9mq&5=Rm`A%!;z9)`M|sI9>+$ z-Hqvv9;sd}!}oW3v-4C3zWpe)b>u1`PDR zcomjcuA&Sm1IoZLFul!1T6fJyQqpJPi>TL+utT5F-VP!=B7dF(^baithzxfD;KPGGm( W0cL`oM_3^GBj9MzMj7~12EGB!@?4Do literal 0 HcmV?d00001 diff --git a/03.JUnit-Introduction-In-5-Steps/.classpath b/03.JUnit-Introduction-In-5-Steps/.classpath new file mode 100644 index 0000000..e72ef7c --- /dev/null +++ b/03.JUnit-Introduction-In-5-Steps/.classpath @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/03.JUnit-Introduction-In-5-Steps/.gitignore b/03.JUnit-Introduction-In-5-Steps/.gitignore new file mode 100644 index 0000000..ae3c172 --- /dev/null +++ b/03.JUnit-Introduction-In-5-Steps/.gitignore @@ -0,0 +1 @@ +/bin/ diff --git a/03.JUnit-Introduction-In-5-Steps/.project b/03.JUnit-Introduction-In-5-Steps/.project new file mode 100644 index 0000000..a22f67d --- /dev/null +++ b/03.JUnit-Introduction-In-5-Steps/.project @@ -0,0 +1,17 @@ + + + junit-in-5-steps + + + + + + org.eclipse.jdt.core.javabuilder + + + + + + org.eclipse.jdt.core.javanature + + diff --git a/03.JUnit-Introduction-In-5-Steps/code-21July2017.zip b/03.JUnit-Introduction-In-5-Steps/code-21July2017.zip new file mode 100644 index 0000000000000000000000000000000000000000..f503f51e01e1a0d70d2fd5ba5788ad7bf36852c5 GIT binary patch literal 7239 zcmbVQ2|SeT*B-kejJ2^P36XX3M#wVAk}X@v(%AQuHA^W=-a(cZ4T+E?YYK_%OPkj& zWl1PYA!|aK|MP^*Wa<6B$8VVNoBKNFzRtPNeVwCynuwSJ0zR7FV`@L&Z2TgIut6;B zU4-@Zpb#PfeRHD|sTYkryeT0B#ES$F2r>9IZD2J6@R$7O>bFN^@@9cg#Bd0NYGbvK zqOQ5F`-Mx^zhKZt=D)*CVMJZ(L>NM_`w@!Pvv1lf3Hoc0sKN#ker`JU&^m3>Qm2zB zts148Md1g;%$V$Ptg6=F%y%c-qBX)ArV3AL2?%4%Y{YH7@S_yv(!q@j) z=6tQ$c31ha(b%H!ms{#RJsuUS4go1^eM92@EyY|3Q?@dFlLZ=3&LEgpIJbW1GZzK& zS+c*!0<(xt9%h{^g=t_w||J&VEr&$l6tb2P<8u%l1(b`BM2F%1uj zs9u`=VQx{3-H((OHF%xtbInb`Rq3hY8lTKuTC}oU^cAKmn-OXDaZV!|0?q@Cv>ASzEdvyLz~u8`9qWYF6{-n=wGz#1cXv{Ci@`ySZ6ka@VtVa~HC7LbiJkR4L zy??ma;-AoBVZ_Jps%?)W`FSTBu(wB^cR!?yFQjztR{5%5fVr4fQdGyEp3oVp@l|9( zu)@I(#aY9PbX%dxYPzR04hyrRzs?)YH1{dFZMxrc#LK$buud(&x=CwdW8I0&YgZ;Wr}~QopQVouaoL{i;?<`z zAS)lmJnNl{T(cEByX@a&8^6d`LQ?(VvU;Me=06n!(5Z`KABZE<``+-8t?I0pn-gr+ zg%FQ^H75HmEoUJ1Sm4F8XY4gDYoOfiaN=&|wqxA=BU{yw@(fMYPp)=;H$O`$5DM$6 zp`@jYbGa_pW(-G`**`VCq+Xc1L^|b*_RQ}swr4D!A@E6fcTmbLE}p7+d9*oc*?-J$ z&94Q*cD_ESF3PKk>zw!M4Xz=QDyb5(YrK9Hi*JUn33*!i@M9UjAQhtZ5s-AXzufial z#Ic`;FeaI`q4L6>tQn+%6T#ofh7I0urMC~U^JPV^2@5vBbf4frWyDfEQ+jh`|o2J^+!h7^r(Xc`j* z+0$!ix_d)7RFrGaJR_S)Wm`(hEN)b>O|)8?{83ezGIqkmn4!yO@6(nyD$47vf({>!EM77$$V?!6qVrLu5CM%YCy$QeHw${M`vEQg zl?YnfypSB!mbiH8EKJcPI6OQ2G~Fp7OVko!svG z^K%UPI-LUhl8-e|Qz_Sd3tpUMx*WmiKlqT(ekRxvkzVkH`KEmAb$erV%MfcxWTVW? z!?Q|$G9mK&Uco=X+gpn27s}DJ;X0l1r#n9#MO)UtbnXjz;fcw2T=ZkGJUk%BSB7>( zG;uF{3(6_G35Cv`o6bofZf58sW`065=U0w$PMIhTDkwFk>{qhrbT_eMd&as}&@Fa@ zh4ovO=tVicM|52wyi<+*$eLp-t+`_JqpC=WM0?d|`PDzpKd4vI6}VZ^8`H>=R;I)_ z*SajiQcZ6D!<2DJ?lc8mAR(ggc&0c*|1$+<_uLp~Zi$;N#~35E@0S0C%CB}SNv!WY zF1{Q<_b+mkHCS=>4ZW!yn)LoF_E#mjEA)Zp_Y^_FE;>c+1e`xk zVMyl$8{eJt%Om1>NrNzsw+uW{_MflLIo-{zLiZ(GppOGNA4HqXz~d?H(ta+3agZ?3&#K?Dju8 z)4nACjiOMsQ)o_{;5^S~hkb?+<}{76GGvpdJd#FXOr)vo$Za1F|5!5aX8!;>s!xqi zrLapWZaU(Z&GM^HzxR^rWgn@&_es?tWuRR~@MIXIC$s+NsHLTB^6xxp>GQdZ&Zg*x@=j*Z_a++LSV`cTX>df z*cz_*RvVkOwtAJbf!?y?>2uz3*_#w4ui1Miy9n||%f3TZhEYT0WBu*PF6oP02OBEi z5bYiUP+@^2{!&&Drrx)5l?uFb02)edf^#8Xc390ua{8EYli-=dcK_5w2^IUVx~iwa#p-J&7>QvY?p)$ zhRKF!)X%yGj5?O)^sFFwnopQgvQiVOJ{6gKA3p{zm)u7_3vJor+5Wdw2X*Ks&)amU z>7P;`M3X2Z8S38PoP8HGeEvb$9r+gp`B%a=kgxOq?mX>Dd7<;1p@(P{^K~x^McTx# zrCAUb=`6^?%{J-3ec%>-4Z)G9@MFe&aAB+B47=A7NiY3N2J~~k#uW1K{l(l`F4g2Yq$h64HHIK`*j(<;WYEPzizvI;WYCK8E4vc~aLR@fU z2IcqYB2pCi{B-dZ@;g=F;r**vwmb9Hu;Cj;eg-X><5ic!Dqo8iop1SQ@a7`yc}MUN zd`TLFeT|X})p8}-VxIoD9s<&M6*jIH=bRz$=werjx> z{8puJnP73lEg{(HulM~abf+WUTARJ7%|Ff^{-u4COeT}FMTL&K`vSdT-K3@Dr@7lA z@B4eooM_S4T-V50Gc<`*igXzLK_ycVpLZN_I6%jEC1?F<=5z_uV z-f<=Ltgr(VA#$c2`%iM!(DK_Bi&c^U!KDGh8Hj_`NWkr`@T2^_cj`LW!t|vUD*7I& zM>*KqiQc_B7`r(A?e@ft%!#iYf-{TDEB>11hyg{L$AY$$Ue0Pjrg~cln z_p1{BpL<W`92X5)jvtmc-RaQo6!_%nc{tfd8N|xZe_F5xB?Ch6}^pFc9Unxvhr)Ax~ zTo(unyMIfD-K6fWrQ!H^6Pr4*EMzjqtmaDmV|}is0=@2N#Bsx1Zt>ZpHe%kVdZ|TA zzZi1QMhSXcyRWO?P}-O`XlX@e5WrZqbymQJfj3KgMGWAYA zIp-KC!f@qg^>7<6iRziueQM8yCX2NC`rusU#sAu*y|T_=z^=v|c^z+@f^6}sdi6q8 z1I_U;LtBsSSoFJ#JmHxXS6OQp5ajT}qxa?Q(~Jn>`b){L=%Ljj{+2bnLdDm8-?y!_ zT~A2)Q)@z*Up z6&zuAHAf09%%fI~;U@?de6$lUnJ`B;fwBvn&~2Fk=HhVGZ`ubaQT6l=&lU8tJW#GE&cLOBmmv zPZJOxAX+bi+oyfN(C+60gG|YTZ`NmjB9d43v%7L z-zx<3;<|5k5CJh*$faNE-E4;RDl<(m}`dVV)(oTT^<^&4Lb;wFU(Ee;j9 xzV9LofpCJtaE~P1F1K*)>Cx?JZ=3i6?Cn@nNr2i6f$Rr 6 + @Test + public void sum_with3numbers() { + System.out.println("Test1"); + assertEquals(6, myMath.sum(new int[] { 1, 2, 3 })); + } + + @Test + public void sum_with1number() { + System.out.println("Test2"); + assertEquals(3, myMath.sum(new int[] { 3 })); + } +} +``` +--- diff --git a/03.JUnit-Introduction-In-5-Steps/src/.DS_Store b/03.JUnit-Introduction-In-5-Steps/src/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..082d232892e8b08564ed7c7799ec50cb88f26105 GIT binary patch literal 6148 zcmeHK!D_=W43*jr3cd8$qd#H&15>j5AU`ljpgS~m8Ewzr$Mlm1{hU5o8f#c~4P{7R zKUuQm_(5!oh%@~IyOXKpQ~%xjKy@R{ljy3zt8q6K^AkaKlZz{-@*dcT$}-Cz!`7`oPm)U zu-j+87kUqJ|S&@xiw8ck#kyb>t6aI&oC=-WhNP`V35UxRm?<1;5N-k>3yTku%^7 z{4)l4R@K!8o3gw0*Y@PD4QOv@A~G+F0)gIp1YjWN$f`KiA4JEzXqXixi`Yvz&>sSY L5bvCU5g51uPAe!o literal 0 HcmV?d00001 diff --git a/03.JUnit-Introduction-In-5-Steps/src/com/.DS_Store b/03.JUnit-Introduction-In-5-Steps/src/com/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..aa7381caa0b72e7ca9e81cdb9756c22adf91b160 GIT binary patch literal 6148 zcmeHKyG{c^3>-s>NNG}1?k_+^xr&l6$PZ9}5E2{`kknW4U3^-`4m^m8d8Mq<|FoR=~dxjqcbB$He$_FvJKz zTreHRb<7gP<_Tgi921$LSyG8fwHh%j>CCsP>xE-t(qT1xSUuTlLa}%{?{85K>xqg| zKnhG1xXkU^`~Qyq!~8!bX(t7wz`s(!7Tf)H!&j=_I(s?qwT*s9_nI%d8`nW$h;~ej icFc{p0>b literal 0 HcmV?d00001 diff --git a/03.JUnit-Introduction-In-5-Steps/src/com/in28minutes/.DS_Store b/03.JUnit-Introduction-In-5-Steps/src/com/in28minutes/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..4c188fe7fd3a333020ac199eed6e43082c005e96 GIT binary patch literal 6148 zcmeHKI|>3Z5S{S@f{mqRuHX%V=n1@lg&>NoAZopp=kjR2`83O7r-kwcCNG)HOUNsB zc0@#%x7|WyCL$BKp*(Epo9&zTY>*KJ!g0p+zPU7~bF=T|z6%(4EJwLXBZm&(c4$<9 z3Qz$mKn1A4rxnNwJDGm^V4g<>sKC!FVBd!VH>`Bxq5@RluN2VHYO`A6Nm*NakF#1^;9I!mJmF@TI|YN6W1yE~EUX;QJt^{v&9Pq- U+d!uy?sOo3222+k75KISFL=5X + + 4.0.0 + + com.in28minutes.learning.jpa + jpa-in-10-steps + 0.0.1-SNAPSHOT + jar + + jpa-in-10-steps + Demo project for Spring Boot + + + org.springframework.boot + spring-boot-starter-parent + 2.0.0.BUILD-SNAPSHOT + + + + + UTF-8 + UTF-8 + 1.8 + + + + + + org.springframework.boot + spring-boot-starter-data-jpa + + + + org.springframework.boot + spring-boot-starter-web + + + + com.h2database + h2 + runtime + + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + + + spring-snapshots + Spring Snapshots + https://repo.spring.io/snapshot + + true + + + + spring-milestones + Spring Milestones + https://repo.spring.io/milestone + + false + + + + + + + spring-snapshots + Spring Snapshots + https://repo.spring.io/snapshot + + true + + + + spring-milestones + Spring Milestones + https://repo.spring.io/milestone + + false + + + + + + +``` +--- + +### /src/main/java/com/in28minutes/learning/jpa/jpain10steps/entity/User.java + +```java +package com.in28minutes.learning.jpa.jpain10steps.entity; + +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; + +//Table - User +@Entity +public class User { + + @Id + @GeneratedValue + private long id; + + private String name; + + private String role; + + protected User() { + + } + + public User(String name, String role) { + super(); + this.name = name; + this.role = role; + } + + public long getId() { + return id; + } + + public String getName() { + return name; + } + + public String getRole() { + return role; + } + + @Override + public String toString() { + return String.format("User [id=%s, name=%s, role=%s]", id, name, role); + } +} +``` +--- + +### /src/main/java/com/in28minutes/learning/jpa/jpain10steps/JpaIn10StepsApplication.java + +```java +package com.in28minutes.learning.jpa.jpain10steps; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class JpaIn10StepsApplication { + + public static void main(String[] args) { + SpringApplication.run(JpaIn10StepsApplication.class, args); + } +} +``` +--- + +### /src/main/java/com/in28minutes/learning/jpa/jpain10steps/service/UserDAOService.java + +```java +package com.in28minutes.learning.jpa.jpain10steps.service; + +import javax.persistence.EntityManager; +import javax.persistence.PersistenceContext; +import javax.transaction.Transactional; + +import org.springframework.stereotype.Repository; + +import com.in28minutes.learning.jpa.jpain10steps.entity.User; + +@Repository +@Transactional +public class UserDAOService { + + @PersistenceContext + private EntityManager entityManager; + + public long insert(User user){ + entityManager.persist(user); + return user.getId(); + } +} + +/* + * Spring Data JPA + * + * + * + */ +``` +--- + +### /src/main/java/com/in28minutes/learning/jpa/jpain10steps/service/UserRepository.java + +```java +package com.in28minutes.learning.jpa.jpain10steps.service; + +import org.springframework.data.jpa.repository.JpaRepository; + +import com.in28minutes.learning.jpa.jpain10steps.entity.User; + +public interface UserRepository extends JpaRepository{ + +} +``` +--- + +### /src/main/java/com/in28minutes/learning/jpa/jpain10steps/UserDaoServiceCommandLineRunner.java + +```java +package com.in28minutes.learning.jpa.jpain10steps; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.CommandLineRunner; +import org.springframework.stereotype.Component; + +import com.in28minutes.learning.jpa.jpain10steps.entity.User; +import com.in28minutes.learning.jpa.jpain10steps.service.UserDAOService; + +@Component +public class UserDaoServiceCommandLineRunner implements CommandLineRunner{ + + private static final Logger log = + LoggerFactory.getLogger(UserDaoServiceCommandLineRunner.class); + + @Autowired + private UserDAOService userDaoService; + + @Override + public void run(String... arg0) throws Exception { + User user = new User("Jack", "Admin"); + //New User is created : User [id=1, name=Jack, role=Admin] + long insert = userDaoService.insert(user); + log.info("New User is created : " + user); + } +} +``` +--- + +### /src/main/java/com/in28minutes/learning/jpa/jpain10steps/UserRepositoryCommandLineRunner.java + +```java +package com.in28minutes.learning.jpa.jpain10steps; + +import java.util.List; +import java.util.Optional; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.CommandLineRunner; +import org.springframework.stereotype.Component; + +import com.in28minutes.learning.jpa.jpain10steps.entity.User; +import com.in28minutes.learning.jpa.jpain10steps.service.UserRepository; + +@Component +public class UserRepositoryCommandLineRunner implements CommandLineRunner{ + + private static final Logger log = + LoggerFactory.getLogger(UserRepositoryCommandLineRunner.class); + + @Autowired + private UserRepository userRepository; + + @Override + public void run(String... arg0) throws Exception { + User user = new User("Jill", "Admin"); + userRepository.save(user); + log.info("New User is created : " + user); + + Optional userWithIdOne = userRepository.findById(1L); + log.info("User is retrived : " + userWithIdOne); + + List users = userRepository.findAll(); + log.info("All Users : " + users); + } + +} +``` +--- + +### /src/main/resources/application.properties + +``` +spring.jpa.show-sql=true +spring.h2.console.enabled=true +logging.level.org.springframework=debug +``` +--- + +### /src/test/java/com/in28minutes/learning/jpa/jpain10steps/JpaIn10StepsApplicationTests.java + +```java +package com.in28minutes.learning.jpa.jpain10steps; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; + +@RunWith(SpringRunner.class) +@SpringBootTest +public class JpaIn10StepsApplicationTests { + + @Test + public void contextLoads() { + } + +} +``` +--- + +### /step-completed.sh + +``` +java -cp /ProgrammingExcellence/Workspaces/Rithus.com/ListDirectoryContentInGitFormat/bin test.ListDirectoryContentInGitFormat >> $1.md +zip -r $1.zip . -x "target/*" -x ".*/*" -x ".*" -x "*.md" -x "mvn*" -x "*.zip" +git add *; git commit -m "$1"; git push; +``` +--- diff --git a/03.JUnit-Introduction-In-5-Steps/step22.zip b/03.JUnit-Introduction-In-5-Steps/step22.zip new file mode 100644 index 0000000000000000000000000000000000000000..3a7fec65ad3d7f81746627c0083ee7362017bd6f GIT binary patch literal 7239 zcmbVQ2|SeT*B<*821C{)A+k>12w4VMvSkZd8vEE%)@&(D-a(cZ4T&O4))W%imo~3m z%92o)Le_*d|L19_vG#u7_}$FR&wZV9U+3KCxz5o!O+-uy0bg3)W9mQOtp6g0utO|S zE+Pi{6c8do0}JC8V0m~`K?sN!2_O(+@ZWTR-7w%U`On>NkI3fD0-uPPArR{I-NH(G z7JBX%F4_Eo0im|QOku=a>O^6o*!^%No7p$*l?44Y2xMUci2x5hM_8Q>X{pmmq;`#R z&7#NyVirvHI95%2aOS&{UC|n04O4|DjU=Q9^Y@>#OK z$AYqmP9A2PCo!}A>&932JJ+p8%B~4dpgoJh@8{d#$2ptgBRS9~dOL>-w3&tnMb$3N z{;;qt#_mVRh#9`l^||V%=&JnGagASgE-gkyJ|=*v%63GCW1P#FmVoO(BOS;aj&*nU z{!Oftuk&Vg!5PQA)kVGCnBN%zhKGQ1flE50Ts+)u-2NjPhu3C*Sa(5l@Nhx7<1j5f zcl8jL8#3Pg>eln;n=wGz#1TRu0()XAxVhO}a@V(Ua~HO9Lb|#AOq>WWJ!5cfNt}?F zS6Y4YK;l>>V@NwqDfySqo%;)Um8XG_LZ1SfCm4P3<+Xu^eWEeP(tEAm<>nq@@(+~N zZ)D-qJ#5q!+xo?Ab@W^F;1!W85cc=yAW;wEF=y^A{1{T5sCy~H%4G6({BWy{tuOpB zfgi%^NQzP%tCIuu?M{HbWTjx#t>>#_DsYxC_qBq=9CUx%m5D&wP#80+&qKGOS# zi!J{ND;9x&{I1saI7)zTvH^Q*Cxj{- z?2%kGd2v=niSD zaadf|w|9RJ3Qvyv-|O>w9|^tn=1^Btn7LtD{>;IrUFwz#w|U!y#x7{Y@XR5e zqhFl~)urtW#2yPQ-aTWld07+bZjTptYd1aPTJLRf_m65-M<~FWs-Ilx{BCiUN-zxC zRYOHbAMbKazRiRgQHFYIc1fe~?h@&gFWNJ|w;07(JVW4<`0k*zTYLg_^YUnO^0NP! z-q3D08gl(37#+>@h9j%NNGs)=5^^X{G%RSyw&n`d)>B zJ4s+a4`EC*Yr_;oJlQfxgC;`GuVlOP+gzdw_3j&MF$*8O?#h4)wfAM$U|-Tuebh}- za44&mCXDh4OzKQ4O!K9a?x~!cau4PX$g1$O4rQPX&_&7wC>i3z(WU_bnGW|!N=m&v ztebPnO}fovdc_6vbz3i(^`wZ~q7r_Tb+)LAdq2!99jJ#dj}yIc!ffJWNH6LLjlS-N_}AglczAH5dH+Z(4rAe zEfb<(6oaOgyElYWRi*aKGqRbx>`Td+#f_?VN!Ck~KdLH|2d`9N2_FPgvk*{B8v;KPTbikD0ZG84(3=zdf!gj2+nlgC60mlHypR&ymb7^4EL6!fBqBTFH2o=I#1S33sH-_Tbf=UMY4(^Ex5wt~Q)c

Q}bxbT_qUf5x^} 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 Gr~d(_@S9Ws literal 0 HcmV?d00001 diff --git a/03.JUnit-Introduction-In-5-Steps/step25.md b/03.JUnit-Introduction-In-5-Steps/step25.md new file mode 100644 index 0000000..f1192f8 --- /dev/null +++ b/03.JUnit-Introduction-In-5-Steps/step25.md @@ -0,0 +1,97 @@ + +## Complete Code Example + + +### /in28Minutes/git/getting-started-in-5-steps/junit-in-5-steps/src/com/in28minutes/junit/MyMath.java + +```java +package com.in28minutes.junit; + +public class MyMath { + int sum(int[] numbers) { + int sum = 0; + for (int i : numbers) { + sum += i; + } + return sum; + } +} +``` +--- + +### /in28Minutes/git/getting-started-in-5-steps/junit-in-5-steps/test/com/in28minutes/junit/AssertTest.java + +```java +package com.in28minutes.junit; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +public class AssertTest { + + @Test + public void test() { + boolean condn = true; + assertEquals(true, condn); + assertTrue(condn); + // assertFalse(condn); + } + +} +``` +--- + +### /in28Minutes/git/getting-started-in-5-steps/junit-in-5-steps/test/com/in28minutes/junit/MyMathTest.java + +```java +package com.in28minutes.junit; + +import static org.junit.Assert.assertEquals; + +import org.junit.After; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +public class MyMathTest { + MyMath myMath = new MyMath(); + + @Before + public void before() { + System.out.println("Before"); + } + + @After + public void after() { + System.out.println("After"); + } + + @BeforeClass + public static void beforeClass() { + System.out.println("Before Class"); + } + + @AfterClass + public static void afterClass() { + System.out.println("After Class"); + } + + // MyMath.sum + // 1,2,3 => 6 + @Test + public void sum_with3numbers() { + System.out.println("Test1"); + assertEquals(6, myMath.sum(new int[] { 1, 2, 3 })); + } + + @Test + public void sum_with1number() { + System.out.println("Test2"); + assertEquals(3, myMath.sum(new int[] { 3 })); + } +} +``` +--- diff --git a/03.JUnit-Introduction-In-5-Steps/step25.zip b/03.JUnit-Introduction-In-5-Steps/step25.zip new file mode 100644 index 0000000000000000000000000000000000000000..190decf65b673b62ec5b496aa384e2a452dd6315 GIT binary patch literal 7239 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 literal 0 HcmV?d00001 diff --git a/03.JUnit-Introduction-In-5-Steps/test/.DS_Store b/03.JUnit-Introduction-In-5-Steps/test/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..788768eb44ad2aa2f00b9a31abff35e47e6510a9 GIT binary patch literal 6148 zcmeHKJ5Iwu5S<||BGIHwxf~%kSViUpxd4(aL4oBIq@Z)h4Y(O6;3B;F1RPnRq(E<^ zndhCIx0YYw*%1-lpZcB1Mnu+dLwU2XG}|{{*iB{{pxPO`LpKh`Mcr=+=`EUM^Y#8L0pjxKv;j%Z>H_d-#?4|B}QN6`%tDN&#)!L%YW(Wp7=4 wob}oQe}JD2wO)?ktr+O77z=B~+gV+)XYAL+DbVT2I~~Z2fayY`0)L^v7bDFh#{d8T literal 0 HcmV?d00001 diff --git a/03.JUnit-Introduction-In-5-Steps/test/com/in28minutes/junit/AssertTest.java b/03.JUnit-Introduction-In-5-Steps/test/com/in28minutes/junit/AssertTest.java new file mode 100644 index 0000000..29135c0 --- /dev/null +++ b/03.JUnit-Introduction-In-5-Steps/test/com/in28minutes/junit/AssertTest.java @@ -0,0 +1,18 @@ +package com.in28minutes.junit; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +public class AssertTest { + + @Test + public void test() { + boolean condn = true; + assertEquals(true, condn); + assertTrue(condn); + // assertFalse(condn); + } + +} diff --git a/03.JUnit-Introduction-In-5-Steps/test/com/in28minutes/junit/MyMathTest.java b/03.JUnit-Introduction-In-5-Steps/test/com/in28minutes/junit/MyMathTest.java new file mode 100644 index 0000000..705445a --- /dev/null +++ b/03.JUnit-Introduction-In-5-Steps/test/com/in28minutes/junit/MyMathTest.java @@ -0,0 +1,47 @@ +package com.in28minutes.junit; + +import static org.junit.Assert.assertEquals; + +import org.junit.After; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +public class MyMathTest { + MyMath myMath = new MyMath(); + + @Before + public void before() { + System.out.println("Before"); + } + + @After + public void after() { + System.out.println("After"); + } + + @BeforeClass + public static void beforeClass() { + System.out.println("Before Class"); + } + + @AfterClass + public static void afterClass() { + System.out.println("After Class"); + } + + // MyMath.sum + // 1,2,3 => 6 + @Test + public void sum_with3numbers() { + System.out.println("Test1"); + assertEquals(6, myMath.sum(new int[] { 1, 2, 3 })); + } + + @Test + public void sum_with1number() { + System.out.println("Test2"); + assertEquals(3, myMath.sum(new int[] { 3 })); + } +} diff --git a/04.Mockito-Introduction-In-5-Steps/.DS_Store b/04.Mockito-Introduction-In-5-Steps/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..0c7f5fbe01439d16a41e5c909fbad6f276789f24 GIT binary patch literal 6148 zcmeHK%TB{U3>?!66>;g2<9-1Wf3T{`7vu*(fFdLmq`uC%@!O2OrlO==IiPW5Pcm!o zDszZq0Jhpc-vLVi3%VmdJWS2k-6wXD%#3KU#}2O;&|`b(CfVl+uf4)8ws^sg*Rg&x zyuA;7+Zi*iZ}4>JPS{K4Ka%gg_Q%@!)5J;vDIf);fE17d?AdOM>xPO_Knh5KqXPbY zD0Ih~I5vz=2Sbbi#5w6O-p4FKEKU$>;@FT3%~~n3m0FG%*2>A3%&UoG!&VN<;luJ| z%L&Eebnagw9abAEN&zWws({blw59j|d-^Z)zvsxLfE4(r3fO$ztyg@x?5&g6^j=%& t5A-i%Zlp6rD<(!O=7YE5n{Ro=@3~$R$A(c(KFW#u5pZ2(Qs6HX_yqoHA5Q=P literal 0 HcmV?d00001 diff --git a/04.Mockito-Introduction-In-5-Steps/.gitignore b/04.Mockito-Introduction-In-5-Steps/.gitignore new file mode 100644 index 0000000..2af7cef --- /dev/null +++ b/04.Mockito-Introduction-In-5-Steps/.gitignore @@ -0,0 +1,24 @@ +target/ +!.mvn/wrapper/maven-wrapper.jar + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr + +### NetBeans ### +nbproject/private/ +build/ +nbbuild/ +dist/ +nbdist/ +.nb-gradle/ \ No newline at end of file diff --git a/04.Mockito-Introduction-In-5-Steps/.mvn/wrapper/maven-wrapper.jar b/04.Mockito-Introduction-In-5-Steps/.mvn/wrapper/maven-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..9cc84ea9b4d95453115d0c26488d6a78694e0bc6 GIT binary patch literal 47610 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 literal 0 HcmV?d00001 diff --git a/04.Mockito-Introduction-In-5-Steps/.mvn/wrapper/maven-wrapper.properties b/04.Mockito-Introduction-In-5-Steps/.mvn/wrapper/maven-wrapper.properties new file mode 100644 index 0000000..c315043 --- /dev/null +++ b/04.Mockito-Introduction-In-5-Steps/.mvn/wrapper/maven-wrapper.properties @@ -0,0 +1 @@ +distributionUrl=https://repo1.maven.org/maven2/org/apache/maven/apache-maven/3.5.0/apache-maven-3.5.0-bin.zip diff --git a/04.Mockito-Introduction-In-5-Steps/code-21July2017.zip b/04.Mockito-Introduction-In-5-Steps/code-21July2017.zip new file mode 100644 index 0000000000000000000000000000000000000000..2353384e0330c762510e96a69b8c68024d27f1e1 GIT binary patch literal 12110 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 TiUXQZCg5K#aF__E2fzIfc5##^ literal 0 HcmV?d00001 diff --git a/04.Mockito-Introduction-In-5-Steps/mvnw b/04.Mockito-Introduction-In-5-Steps/mvnw new file mode 100755 index 0000000..5bf251c --- /dev/null +++ b/04.Mockito-Introduction-In-5-Steps/mvnw @@ -0,0 +1,225 @@ +#!/bin/sh +# ---------------------------------------------------------------------------- +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# ---------------------------------------------------------------------------- + +# ---------------------------------------------------------------------------- +# Maven2 Start Up Batch script +# +# Required ENV vars: +# ------------------ +# JAVA_HOME - location of a JDK home dir +# +# Optional ENV vars +# ----------------- +# M2_HOME - location of maven2's installed home dir +# MAVEN_OPTS - parameters passed to the Java VM when running Maven +# e.g. to debug Maven itself, use +# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +# MAVEN_SKIP_RC - flag to disable loading of mavenrc files +# ---------------------------------------------------------------------------- + +if [ -z "$MAVEN_SKIP_RC" ] ; then + + if [ -f /etc/mavenrc ] ; then + . /etc/mavenrc + fi + + if [ -f "$HOME/.mavenrc" ] ; then + . "$HOME/.mavenrc" + fi + +fi + +# OS specific support. $var _must_ be set to either true or false. +cygwin=false; +darwin=false; +mingw=false +case "`uname`" in + CYGWIN*) cygwin=true ;; + MINGW*) mingw=true;; + Darwin*) darwin=true + # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home + # See https://developer.apple.com/library/mac/qa/qa1170/_index.html + if [ -z "$JAVA_HOME" ]; then + if [ -x "/usr/libexec/java_home" ]; then + export JAVA_HOME="`/usr/libexec/java_home`" + else + export JAVA_HOME="/Library/Java/Home" + fi + fi + ;; +esac + +if [ -z "$JAVA_HOME" ] ; then + if [ -r /etc/gentoo-release ] ; then + JAVA_HOME=`java-config --jre-home` + fi +fi + +if [ -z "$M2_HOME" ] ; then + ## resolve links - $0 may be a link to maven's home + PRG="$0" + + # need this for relative symlinks + while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG="`dirname "$PRG"`/$link" + fi + done + + saveddir=`pwd` + + M2_HOME=`dirname "$PRG"`/.. + + # make it fully qualified + M2_HOME=`cd "$M2_HOME" && pwd` + + cd "$saveddir" + # echo Using m2 at $M2_HOME +fi + +# For Cygwin, ensure paths are in UNIX format before anything is touched +if $cygwin ; then + [ -n "$M2_HOME" ] && + M2_HOME=`cygpath --unix "$M2_HOME"` + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --unix "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --unix "$CLASSPATH"` +fi + +# For Migwn, ensure paths are in UNIX format before anything is touched +if $mingw ; then + [ -n "$M2_HOME" ] && + M2_HOME="`(cd "$M2_HOME"; pwd)`" + [ -n "$JAVA_HOME" ] && + JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" + # TODO classpath? +fi + +if [ -z "$JAVA_HOME" ]; then + javaExecutable="`which javac`" + if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then + # readlink(1) is not available as standard on Solaris 10. + readLink=`which readlink` + if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then + if $darwin ; then + javaHome="`dirname \"$javaExecutable\"`" + javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" + else + javaExecutable="`readlink -f \"$javaExecutable\"`" + fi + javaHome="`dirname \"$javaExecutable\"`" + javaHome=`expr "$javaHome" : '\(.*\)/bin'` + JAVA_HOME="$javaHome" + export JAVA_HOME + fi + fi +fi + +if [ -z "$JAVACMD" ] ; then + if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + else + JAVACMD="`which java`" + fi +fi + +if [ ! -x "$JAVACMD" ] ; then + echo "Error: JAVA_HOME is not defined correctly." >&2 + echo " We cannot execute $JAVACMD" >&2 + exit 1 +fi + +if [ -z "$JAVA_HOME" ] ; then + echo "Warning: JAVA_HOME environment variable is not set." +fi + +CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher + +# traverses directory structure from process work directory to filesystem root +# first directory with .mvn subdirectory is considered project base directory +find_maven_basedir() { + + if [ -z "$1" ] + then + echo "Path not specified to find_maven_basedir" + return 1 + fi + + basedir="$1" + wdir="$1" + while [ "$wdir" != '/' ] ; do + if [ -d "$wdir"/.mvn ] ; then + basedir=$wdir + break + fi + # workaround for JBEAP-8937 (on Solaris 10/Sparc) + if [ -d "${wdir}" ]; then + wdir=`cd "$wdir/.."; pwd` + fi + # end of workaround + done + echo "${basedir}" +} + +# concatenates all lines of a file +concat_lines() { + if [ -f "$1" ]; then + echo "$(tr -s '\n' ' ' < "$1")" + fi +} + +BASE_DIR=`find_maven_basedir "$(pwd)"` +if [ -z "$BASE_DIR" ]; then + exit 1; +fi + +export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} +echo $MAVEN_PROJECTBASEDIR +MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" + +# For Cygwin, switch paths to Windows format before running java +if $cygwin; then + [ -n "$M2_HOME" ] && + M2_HOME=`cygpath --path --windows "$M2_HOME"` + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --windows "$CLASSPATH"` + [ -n "$MAVEN_PROJECTBASEDIR" ] && + MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` +fi + +WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +exec "$JAVACMD" \ + $MAVEN_OPTS \ + -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ + "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ + ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" diff --git a/04.Mockito-Introduction-In-5-Steps/mvnw.cmd b/04.Mockito-Introduction-In-5-Steps/mvnw.cmd new file mode 100644 index 0000000..019bd74 --- /dev/null +++ b/04.Mockito-Introduction-In-5-Steps/mvnw.cmd @@ -0,0 +1,143 @@ +@REM ---------------------------------------------------------------------------- +@REM Licensed to the Apache Software Foundation (ASF) under one +@REM or more contributor license agreements. See the NOTICE file +@REM distributed with this work for additional information +@REM regarding copyright ownership. The ASF licenses this file +@REM to you under the Apache License, Version 2.0 (the +@REM "License"); you may not use this file except in compliance +@REM with the License. You may obtain a copy of the License at +@REM +@REM http://www.apache.org/licenses/LICENSE-2.0 +@REM +@REM Unless required by applicable law or agreed to in writing, +@REM software distributed under the License is distributed on an +@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +@REM KIND, either express or implied. See the License for the +@REM specific language governing permissions and limitations +@REM under the License. +@REM ---------------------------------------------------------------------------- + +@REM ---------------------------------------------------------------------------- +@REM Maven2 Start Up Batch script +@REM +@REM Required ENV vars: +@REM JAVA_HOME - location of a JDK home dir +@REM +@REM Optional ENV vars +@REM M2_HOME - location of maven2's installed home dir +@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands +@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending +@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven +@REM e.g. to debug Maven itself, use +@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files +@REM ---------------------------------------------------------------------------- + +@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' +@echo off +@REM enable echoing my setting MAVEN_BATCH_ECHO to 'on' +@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% + +@REM set %HOME% to equivalent of $HOME +if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") + +@REM Execute a user defined script before this one +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre +@REM check for pre script, once with legacy .bat ending and once with .cmd ending +if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" +if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" +:skipRcPre + +@setlocal + +set ERROR_CODE=0 + +@REM To isolate internal variables from possible post scripts, we use another setlocal +@setlocal + +@REM ==== START VALIDATION ==== +if not "%JAVA_HOME%" == "" goto OkJHome + +echo. +echo Error: JAVA_HOME not found in your environment. >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +:OkJHome +if exist "%JAVA_HOME%\bin\java.exe" goto init + +echo. +echo Error: JAVA_HOME is set to an invalid directory. >&2 +echo JAVA_HOME = "%JAVA_HOME%" >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +@REM ==== END VALIDATION ==== + +:init + +@REM Find the project base dir, i.e. the directory that contains the folder ".mvn". +@REM Fallback to current working directory if not found. + +set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% +IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir + +set EXEC_DIR=%CD% +set WDIR=%EXEC_DIR% +:findBaseDir +IF EXIST "%WDIR%"\.mvn goto baseDirFound +cd .. +IF "%WDIR%"=="%CD%" goto baseDirNotFound +set WDIR=%CD% +goto findBaseDir + +:baseDirFound +set MAVEN_PROJECTBASEDIR=%WDIR% +cd "%EXEC_DIR%" +goto endDetectBaseDir + +:baseDirNotFound +set MAVEN_PROJECTBASEDIR=%EXEC_DIR% +cd "%EXEC_DIR%" + +:endDetectBaseDir + +IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig + +@setlocal EnableExtensions EnableDelayedExpansion +for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a +@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% + +:endReadAdditionalConfig + +SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" + +set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" +set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* +if ERRORLEVEL 1 goto error +goto end + +:error +set ERROR_CODE=1 + +:end +@endlocal & set ERROR_CODE=%ERROR_CODE% + +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost +@REM check for post script, once with legacy .bat ending and once with .cmd ending +if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" +if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" +:skipRcPost + +@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' +if "%MAVEN_BATCH_PAUSE%" == "on" pause + +if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% + +exit /B %ERROR_CODE% diff --git a/04.Mockito-Introduction-In-5-Steps/pom.xml b/04.Mockito-Introduction-In-5-Steps/pom.xml new file mode 100644 index 0000000..a088b5b --- /dev/null +++ b/04.Mockito-Introduction-In-5-Steps/pom.xml @@ -0,0 +1,88 @@ + + + 4.0.0 + + com.in28minutes.mockito + mockito-demo + 0.0.1-SNAPSHOT + jar + + mockito-demo + Demo project for Spring Boot + + + org.springframework.boot + spring-boot-starter-parent + 2.0.0.BUILD-SNAPSHOT + + + + + UTF-8 + UTF-8 + 1.8 + + + + + org.springframework.boot + spring-boot-starter + + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + + + spring-snapshots + Spring Snapshots + https://repo.spring.io/snapshot + + true + + + + spring-milestones + Spring Milestones + https://repo.spring.io/milestone + + false + + + + + + + spring-snapshots + Spring Snapshots + https://repo.spring.io/snapshot + + true + + + + spring-milestones + Spring Milestones + https://repo.spring.io/milestone + + false + + + + + + diff --git a/04.Mockito-Introduction-In-5-Steps/readme.md b/04.Mockito-Introduction-In-5-Steps/readme.md new file mode 100644 index 0000000..535ff02 --- /dev/null +++ b/04.Mockito-Introduction-In-5-Steps/readme.md @@ -0,0 +1,366 @@ +## First 5 Steps in Mockito + +Mockito is the most famous mocking framework in Java. + +- Git Repository - https://github.com/in28minutes/getting-started-in-5-steps +- Pre-requisites + - Java & Eclipse - https://www.youtube.com/playlist?list=PLBBog2r6uMCSmMVTW_QmDLyASBvovyAO3 + - JUnit - https://courses.in28minutes.com/p/junit-tutorial-for-beginners +- Easier Static Imports + - Window > Preferences > Java > Editor > Content Assist > Favorites + - org.junit.Assert + - org.mockito.BDDMockito + - org.mockito.Mockito + - org.hamcrest.Matchers + - org.hamcrest.CoreMatchers +- More information + - Visit Mockito Official Documentation - [Mockito Documentation] (http://site.mockito.org/mockito/docs/current/org/mockito/Mockito.html) + +- Step 1 : Setting up an example using http://start.spring.io. +- Step 2 : Using a Stubs - Disadvantages +- Step 3 : Your first mock. +- Step 4 : Using Mockito Annotations - @Mock, @InjectMocks, @RunWith(MockitoJUnitRunner.class) +- Step 5 : Mocking List interface +- Next Steps + +## Complete Code Example + + +### /pom.xml + +```xml + + + 4.0.0 + + com.in28minutes.mockito + mockito-demo + 0.0.1-SNAPSHOT + jar + + mockito-demo + Demo project for Spring Boot + + + org.springframework.boot + spring-boot-starter-parent + 2.0.0.BUILD-SNAPSHOT + + + + + UTF-8 + UTF-8 + 1.8 + + + + + org.springframework.boot + spring-boot-starter + + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + + + spring-snapshots + Spring Snapshots + https://repo.spring.io/snapshot + + true + + + + spring-milestones + Spring Milestones + https://repo.spring.io/milestone + + false + + + + + + + spring-snapshots + Spring Snapshots + https://repo.spring.io/snapshot + + true + + + + spring-milestones + Spring Milestones + https://repo.spring.io/milestone + + false + + + + + + +``` +--- + +### /src/main/java/com/in28minutes/mockito/mockitodemo/DataService.java + +```java +package com.in28minutes.mockito.mockitodemo; + +public interface DataService { + int[] retrieveAllData(); +} +``` +--- + +### /src/main/java/com/in28minutes/mockito/mockitodemo/MockitoDemoApplication.java + +```java +package com.in28minutes.mockito.mockitodemo; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class MockitoDemoApplication { + + public static void main(String[] args) { + SpringApplication.run(MockitoDemoApplication.class, args); + } +} +``` +--- + +### /src/main/java/com/in28minutes/mockito/mockitodemo/SomeBusinessImpl.java + +```java +package com.in28minutes.mockito.mockitodemo; + +public class SomeBusinessImpl { + private DataService dataService; + + public SomeBusinessImpl(DataService dataService) { + super(); + this.dataService = dataService; + } + + int findTheGreatestFromAllData() { + int[] data = dataService.retrieveAllData(); + int greatest = Integer.MIN_VALUE; + + for (int value : data) { + if (value > greatest) { + greatest = value; + } + } + return greatest; + } +} +``` +--- + +### /src/main/resources/application.properties + +```properties +``` +--- + +### /src/test/java/com/in28minutes/mockito/mockitodemo/ListTest.java + +```java +package com.in28minutes.mockito.mockitodemo; + +import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.util.List; + +import org.junit.Test; +import org.mockito.Mockito; + +public class ListTest { + + @Test + public void testSize() { + List listMock = mock(List.class); + when(listMock.size()).thenReturn(10); + assertEquals(10, listMock.size()); + assertEquals(10, listMock.size()); + } + + @Test + public void testSize_multipleReturns() { + List listMock = mock(List.class); + when(listMock.size()).thenReturn(10).thenReturn(20); + assertEquals(10, listMock.size()); + assertEquals(20, listMock.size()); + assertEquals(20, listMock.size()); + } + + @Test + public void testGet_SpecificParameter() { + List listMock = mock(List.class); + when(listMock.get(0)).thenReturn("SomeString"); + assertEquals("SomeString", listMock.get(0)); + assertEquals(null, listMock.get(1)); + } + + @Test + public void testGet_GenericParameter() { + List listMock = mock(List.class); + when(listMock.get(Mockito.anyInt())).thenReturn("SomeString"); + assertEquals("SomeString", listMock.get(0)); + assertEquals("SomeString", listMock.get(1)); + } +} +``` +--- + +### /src/test/java/com/in28minutes/mockito/mockitodemo/MockitoDemoApplicationTests.java + +```java +package com.in28minutes.mockito.mockitodemo; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; + +@RunWith(SpringRunner.class) +@SpringBootTest +public class MockitoDemoApplicationTests { + + @Test + public void contextLoads() { + } + +} +``` +--- + +### /src/test/java/com/in28minutes/mockito/mockitodemo/SomeBusinessMockAnnotationsTest.java + +```java +package com.in28minutes.mockito.mockitodemo; + +import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.when; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; + +@RunWith(MockitoJUnitRunner.class) +public class SomeBusinessMockAnnotationsTest { + + @Mock + DataService dataServiceMock; + + @InjectMocks + SomeBusinessImpl businessImpl; + + @Test + public void testFindTheGreatestFromAllData() { + when(dataServiceMock.retrieveAllData()).thenReturn(new int[] { 24, 15, 3 }); + assertEquals(24, businessImpl.findTheGreatestFromAllData()); + } + + @Test + public void testFindTheGreatestFromAllData_ForOneValue() { + when(dataServiceMock.retrieveAllData()).thenReturn(new int[] { 15 }); + assertEquals(15, businessImpl.findTheGreatestFromAllData()); + } + + @Test + public void testFindTheGreatestFromAllData_NoValues() { + when(dataServiceMock.retrieveAllData()).thenReturn(new int[] {}); + assertEquals(Integer.MIN_VALUE, businessImpl.findTheGreatestFromAllData()); + } +} +``` +--- + +### /src/test/java/com/in28minutes/mockito/mockitodemo/SomeBusinessMockTest.java + +```java +package com.in28minutes.mockito.mockitodemo; + +import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import org.junit.Test; + +public class SomeBusinessMockTest { + + @Test + public void testFindTheGreatestFromAllData() { + DataService dataServiceMock = mock(DataService.class); + when(dataServiceMock.retrieveAllData()).thenReturn(new int[] { 24, 15, 3 }); + SomeBusinessImpl businessImpl = new SomeBusinessImpl(dataServiceMock); + int result = businessImpl.findTheGreatestFromAllData(); + assertEquals(24, result); + } + + @Test + public void testFindTheGreatestFromAllData_ForOneValue() { + DataService dataServiceMock = mock(DataService.class); + when(dataServiceMock.retrieveAllData()).thenReturn(new int[] { 15 }); + SomeBusinessImpl businessImpl = new SomeBusinessImpl(dataServiceMock); + int result = businessImpl.findTheGreatestFromAllData(); + assertEquals(15, result); + } + +} +``` +--- + +### /src/test/java/com/in28minutes/mockito/mockitodemo/SomeBusinessStubTest.java + +```java +package com.in28minutes.mockito.mockitodemo; + +import static org.junit.Assert.assertEquals; + +import org.junit.Test; + +public class SomeBusinessStubTest { + @Test + public void testFindTheGreatestFromAllData() { + SomeBusinessImpl businessImpl = new SomeBusinessImpl(new DataServiceStub()); + int result = businessImpl.findTheGreatestFromAllData(); + assertEquals(24, result); + + } + +} + +class DataServiceStub implements DataService { + @Override + public int[] retrieveAllData() { + return new int[] { 24, 6, 15 }; + } +} +``` +--- diff --git a/04.Mockito-Introduction-In-5-Steps/src/.DS_Store b/04.Mockito-Introduction-In-5-Steps/src/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..7b5c75f2e0a9c2dff938e1cb0d1b7044fac7b894 GIT binary patch literal 6148 zcmeHK%Sr=55Ukcc1ia+vael!+7()Dl{6GSV2)cqs&wI*u`Ds=^5R#4H#fwx!cg@sx z&#-ma-UeW+_q!Wl31C5Y#KFVd{JHzgE{bt9KjUNbIzGLO54oS^pCi0>z%zDu#CTFa zV2^veVSmD(J4c)pkOERb3P=Gd@H++Av)vX~iHcG{3P^!(1^oNa=#IT`N{mkjLyQ2# zfpi$xF-s7e6U1IPC6b|8mJ+knYQ(TCC*LZs7fy*;4vU+4PTg!Zp;+8bzC}8$Cn`z- zDR8X7Wp0<=|8MC(%>Tzl+DQQ^@UIlGVZB+e_)67VXD{cyw$ZnA&-tXgaUCTL(T<7H ij=Ax6d>u)d*L=Lp5a`I75)X#wHB9j7tt-u#C&KU~; literal 0 HcmV?d00001 diff --git a/04.Mockito-Introduction-In-5-Steps/src/main/.DS_Store b/04.Mockito-Introduction-In-5-Steps/src/main/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..0ea0534fce4609c5de7b2e35dab2d81543b609bf GIT binary patch literal 6148 zcmeH~Jx&8b427Rziy)emlye1cFp9znaseVhNC+iTP*QKlwR!#lk!+x(K+lrr&3Nq@ z?N_XK0N7&xcniz{%;>Io_hD-O+&A+VNgfCz|y2#A0P{6>I1+iiB8s!;?)Km@)B`1hgFU3=-68lMgh zaRs0b42N+ZcL{3q0JWEnsf^GpOQ~6Eb;YnOXS`KjFC9~}92O^YpE}ve+vxZ7Ph)PRbBI<< ijaJNsx8j>`dClj1-b=^SC}%v%srnIcE;14L3j!agQ5tCg literal 0 HcmV?d00001 diff --git a/04.Mockito-Introduction-In-5-Steps/src/main/java/.DS_Store b/04.Mockito-Introduction-In-5-Steps/src/main/java/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..0a8bc21eef2735eaef7786d6a2b9eb2fa2ef83ae GIT binary patch literal 6148 zcmeH~J!%6%427R!7lt%jx}3%b$PET#pTHLgIFQCa5J>7ddY*ootW$?2Jc0B^niaeI z6+0^cw*P#40VV(&x+``bW@gM+IOC4%*XiSSx!>Md@hb2XF*8;s%=T+rA_5{H0wN#+ zBCsF=d5UB6yr5^&qlkbAEQ5f59~#}YmyXo-s>NNG}1?k_+^xr&l6$PZ9}5E2{`kknW4U3^-`4m^m8d8Mq<|FoR=~dxjqcbB$He$_FvJKz zTreHRb<7gP<_Tgi921$LSyG8fwHh%j>CCsP>xE-t(qT1xSUuTlLa}%{?{85K>xqg| zKnhG1xXkU^`~Qyq!~8!bX(t7wz`s(!7Tf)H!&j=_I(s?qwT*s9_nI%d8`nW$h;~ej icFc{p0>b literal 0 HcmV?d00001 diff --git a/04.Mockito-Introduction-In-5-Steps/src/main/java/com/in28minutes/.DS_Store b/04.Mockito-Introduction-In-5-Steps/src/main/java/com/in28minutes/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..cd0e671fbc7a84a6500e566aaac48395c5afcb54 GIT binary patch literal 6148 zcmeHK%Sr=55UkdKfn0L*IKSW@3?Y6&ejs2#M9qSz=RNsd`Lt9&2!@Rp!HZNwcg^&4 z&9Zga-UeW+kKG-x1Td#N;>*L-{M>zFSCug$op(Ip9mD75ZT~V%sxK#;dxKXDc);^_ z{{AszE~o%@f34I3_Yfv!oJ}YBge5(wT2n*9*tQq{C|XuzIr9gkte@-ru4e))N(_fD|}a z;5xTU@BjDoALjpal6F!+3j8YtY_{I4SA3=Ft&^AYUfbwfy4M`&Zd?b2A=)u9+A%lY ej&GwV>zc25-V4XXpfewIqJ9Qk7nv0JYXy#iOck{N literal 0 HcmV?d00001 diff --git a/04.Mockito-Introduction-In-5-Steps/src/main/java/com/in28minutes/mockito/.DS_Store b/04.Mockito-Introduction-In-5-Steps/src/main/java/com/in28minutes/mockito/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..fb9ab742e21cb0a51a829e3218fdb55ca6c9cbdf GIT binary patch literal 6148 zcmeHK%Sr=55Ukc50=eYqael!+7()Dl{6HWP5j6{Ad3+Zy0dIJzi$jmlMuiv3kG(PZR#| zv3or|52uH`{W4uyDIf);fE17dQs9>gc<-exSBZ*JKnh5KZw37O(CCi6a7>I(2Sbbi z#0Aq~T*oXyY@Q(Y!ZDE greatest) { + greatest = value; + } + } + return greatest; + } +} \ No newline at end of file diff --git a/04.Mockito-Introduction-In-5-Steps/src/main/resources/application.properties b/04.Mockito-Introduction-In-5-Steps/src/main/resources/application.properties new file mode 100644 index 0000000..e69de29 diff --git a/04.Mockito-Introduction-In-5-Steps/src/test/.DS_Store b/04.Mockito-Introduction-In-5-Steps/src/test/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..63b728b450e912f8fa3bc98e04a9fc67ff4fcf03 GIT binary patch literal 6148 zcmeH~K?=e^3`G;|qTr@Wm$UHz-e4$t0xzH-2qF~(yPl)_lL>;=wTS#c@+X-IrEk$` zL`3`haV^q`NDDWWm4%5Z@&JhP8{Tj3ht;B=96)--iS@&7q~Le>xC+1b{XuyJ79K1T}(S!u1*@b}wNMJ%>Uh~fG|1JE}{6A@7N&+PC zX9Tp_>^41KD(=>|*R%RQs7ddY*ootW$?2Jc0B^niaeI z6+0^cw*P#40VV(&x+``bW@gM+IOC4%*XiSSx!>Md@hb2XF*8;s%=T+rA_5{H0wN#+ zBCsF=d5UB6yr5^&qlkbAEQ5f59~#}YmyXo-s>NNG}1?k_+^xr&l6$PZ9}5E2{`kknW4U3^-`4m^m8d8Mq<|FoR=~dxjqcbB$He$_FvJKz zTreHRb<7gP<_Tgi921$LSyG8fwHh%j>CCsP>xE-t(qT1xSUuTlLa}%{?{85K>xqg| zKnhG1xXkU^`~Qyq!~8!bX(t7wz`s(!7Tf)H!&j=_I(s?qwT*s9_nI%d8`nW$h;~ej icFc{p0>b literal 0 HcmV?d00001 diff --git a/04.Mockito-Introduction-In-5-Steps/src/test/java/com/in28minutes/.DS_Store b/04.Mockito-Introduction-In-5-Steps/src/test/java/com/in28minutes/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..cd0e671fbc7a84a6500e566aaac48395c5afcb54 GIT binary patch literal 6148 zcmeHK%Sr=55UkdKfn0L*IKSW@3?Y6&ejs2#M9qSz=RNsd`Lt9&2!@Rp!HZNwcg^&4 z&9Zga-UeW+kKG-x1Td#N;>*L-{M>zFSCug$op(Ip9mD75ZT~V%sxK#;dxKXDc);^_ z{{AszE~o%@f34I3_Yfv!oJ}YBge5(wT2n*9*tQq{C|XuzIr9gkte@-ru4e))N(_fD|}a z;5xTU@BjDoALjpal6F!+3j8YtY_{I4SA3=Ft&^AYUfbwfy4M`&Zd?b2A=)u9+A%lY ej&GwV>zc25-V4XXpfewIqJ9Qk7nv0JYXy#iOck{N literal 0 HcmV?d00001 diff --git a/04.Mockito-Introduction-In-5-Steps/src/test/java/com/in28minutes/mockito/.DS_Store b/04.Mockito-Introduction-In-5-Steps/src/test/java/com/in28minutes/mockito/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..fb9ab742e21cb0a51a829e3218fdb55ca6c9cbdf GIT binary patch literal 6148 zcmeHK%Sr=55Ukc50=eYqael!+7()Dl{6HWP5j6{Ad3+Zy0dIJzi$jmlMuiv3kG(PZR#| zv3or|52uH`{W4uyDIf);fE17dQs9>gc<-exSBZ*JKnh5KZw37O(CCi6a7>I(2Sbbi z#0Aq~T*oXyY@Q(Y!ZDE?!66>;g2<9-1Wf3T{`7vu*(fFdLmq`uC%@!O2OrlO==IiPW5Pcm!o zDszZq0Jhpc-vLVi3%VmdJWS2k-6wXD%#3KU#}2O;&|`b(CfVl+uf4)8ws^sg*Rg&x zyuA;7+Zi*iZ}4>JPS{K4Ka%gg_Q%@!)5J;vDIf);fE17d?AdOM>xPO_Knh5KqXPbY zD0Ih~I5vz=2Sbbi#5w6O-p4FKEKU$>;@FT3%~~n3m0FG%*2>A3%&UoG!&VN<;luJ| z%L&Eebnagw9abAEN&zWws({blw59j|d-^Z)zvsxLfE4(r3fO$ztyg@x?5&g6^j=%& t5A-i%Zlp6rD<(!O=7YE5n{Ro=@3~$R$A(c(KFW#u5pZ2(Qs6HX_yqoHA5Q=P literal 0 HcmV?d00001 diff --git a/05.Spring-Boot-Advanced/.classpath b/05.Spring-Boot-Advanced/.classpath new file mode 100644 index 0000000..fae1a2b --- /dev/null +++ b/05.Spring-Boot-Advanced/.classpath @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/05.Spring-Boot-Advanced/.project b/05.Spring-Boot-Advanced/.project new file mode 100644 index 0000000..45b2121 --- /dev/null +++ b/05.Spring-Boot-Advanced/.project @@ -0,0 +1,35 @@ + + + first-springboot-project + + + + + + org.eclipse.wst.common.project.facet.core.builder + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.m2e.core.maven2Builder + + + + + org.springframework.ide.eclipse.core.springbuilder + + + + + + org.springframework.ide.eclipse.core.springnature + org.eclipse.jdt.core.javanature + org.eclipse.m2e.core.maven2Nature + org.eclipse.wst.common.project.facet.core.nature + + diff --git a/05.Spring-Boot-Advanced/.settings/.DS_Store b/05.Spring-Boot-Advanced/.settings/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..cb3f4e649c28e3915f0ceb54a5936c2a0f26e607 GIT binary patch literal 6148 zcmeHKOHRWu5Pbths>G)3So#X(1W^@E&?g(c0FXtO=fDiWltoe088Pi19lG$~2~o-%E!KEMM#9s`M1QeM*M5TzZ)ow# z^1c3se&%jIxSPB6^L)8#Hrt~uoWa^Ob)CC*1?+cM?}d97EB}DCeK}d-(PG051ww&P zAQT7%4hrzjR-0ZMMjZ--0-?Y`0X-iQi(=*28zI>eI?@h^WRFH*kF`BMppREAN90-?ZAflF)7^!~r+FEg6t z4=K?K1ww&;rhrW9Wxe30{BAwjp5C>I<$*;_<9c;y^aqasHuN01ErT|m)Ms4f*c<9B S+F$6zcnB0hq6!6mL4i+ivo>S^ literal 0 HcmV?d00001 diff --git a/05.Spring-Boot-Advanced/.settings/.jsdtscope b/05.Spring-Boot-Advanced/.settings/.jsdtscope new file mode 100644 index 0000000..b72a6a4 --- /dev/null +++ b/05.Spring-Boot-Advanced/.settings/.jsdtscope @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/05.Spring-Boot-Advanced/.settings/org.eclipse.core.resources.prefs b/05.Spring-Boot-Advanced/.settings/org.eclipse.core.resources.prefs new file mode 100644 index 0000000..29abf99 --- /dev/null +++ b/05.Spring-Boot-Advanced/.settings/org.eclipse.core.resources.prefs @@ -0,0 +1,6 @@ +eclipse.preferences.version=1 +encoding//src/main/java=UTF-8 +encoding//src/main/resources=UTF-8 +encoding//src/test/java=UTF-8 +encoding//src/test/resources=UTF-8 +encoding/=UTF-8 diff --git a/05.Spring-Boot-Advanced/.settings/org.eclipse.jdt.core.prefs b/05.Spring-Boot-Advanced/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 0000000..6e80039 --- /dev/null +++ b/05.Spring-Boot-Advanced/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,8 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 +org.eclipse.jdt.core.compiler.compliance=1.8 +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning +org.eclipse.jdt.core.compiler.source=1.8 diff --git a/05.Spring-Boot-Advanced/.settings/org.eclipse.m2e.core.prefs b/05.Spring-Boot-Advanced/.settings/org.eclipse.m2e.core.prefs new file mode 100644 index 0000000..f897a7f --- /dev/null +++ b/05.Spring-Boot-Advanced/.settings/org.eclipse.m2e.core.prefs @@ -0,0 +1,4 @@ +activeProfiles= +eclipse.preferences.version=1 +resolveWorkspaceProjects=true +version=1 diff --git a/05.Spring-Boot-Advanced/.settings/org.eclipse.wst.common.component b/05.Spring-Boot-Advanced/.settings/org.eclipse.wst.common.component new file mode 100644 index 0000000..dd59085 --- /dev/null +++ b/05.Spring-Boot-Advanced/.settings/org.eclipse.wst.common.component @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/05.Spring-Boot-Advanced/.settings/org.eclipse.wst.common.project.facet.core.xml b/05.Spring-Boot-Advanced/.settings/org.eclipse.wst.common.project.facet.core.xml new file mode 100644 index 0000000..efa5461 --- /dev/null +++ b/05.Spring-Boot-Advanced/.settings/org.eclipse.wst.common.project.facet.core.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/05.Spring-Boot-Advanced/.settings/org.eclipse.wst.jsdt.ui.superType.container b/05.Spring-Boot-Advanced/.settings/org.eclipse.wst.jsdt.ui.superType.container new file mode 100644 index 0000000..3bd5d0a --- /dev/null +++ b/05.Spring-Boot-Advanced/.settings/org.eclipse.wst.jsdt.ui.superType.container @@ -0,0 +1 @@ +org.eclipse.wst.jsdt.launching.baseBrowserLibrary \ No newline at end of file diff --git a/05.Spring-Boot-Advanced/.settings/org.eclipse.wst.jsdt.ui.superType.name b/05.Spring-Boot-Advanced/.settings/org.eclipse.wst.jsdt.ui.superType.name new file mode 100644 index 0000000..05bd71b --- /dev/null +++ b/05.Spring-Boot-Advanced/.settings/org.eclipse.wst.jsdt.ui.superType.name @@ -0,0 +1 @@ +Window \ No newline at end of file diff --git a/05.Spring-Boot-Advanced/.settings/org.eclipse.wst.validation.prefs b/05.Spring-Boot-Advanced/.settings/org.eclipse.wst.validation.prefs new file mode 100644 index 0000000..04cad8c --- /dev/null +++ b/05.Spring-Boot-Advanced/.settings/org.eclipse.wst.validation.prefs @@ -0,0 +1,2 @@ +disabled=06target +eclipse.preferences.version=1 diff --git a/05.Spring-Boot-Advanced/README.md b/05.Spring-Boot-Advanced/README.md new file mode 100644 index 0000000..999d6eb --- /dev/null +++ b/05.Spring-Boot-Advanced/README.md @@ -0,0 +1,143 @@ +# SpringBootForBeginners +Spring Boot Tutorial For Beginners +* [Installing Eclipse, Maven and Java](#installing-tools) +* [Running Examples](#running-examples) +* [Course Overview](#course-overview) + - [Course Steps](#step-list) + - [Expectations](#expectations) +* [About in28Minutes](#about-in28minutes) + - [Our Beliefs](#our-beliefs) + - [Our Approach](#our-approach) + - [Find Us](#useful-links) + - [Other Courses](#other-courses) + +## Installing Tools +- PDF : https://github.com/in28minutes/SpringIn28Minutes/blob/master/InstallationGuide-JavaEclipseAndMaven_v2.pdf +- Video : https://www.youtube.com/playlist?list=PLBBog2r6uMCSmMVTW_QmDLyASBvovyAO3 +- GIT Repository : https://github.com/in28minutes/getting-started-in-5-steps + +## Course Overview + +### Introduction + +Spring Boot has a lot of magic going for it. Developing Services with it is cool and fun. + +Spring Boot makes it easy to create stand-alone, production-grade Spring based Applications that you can “just run”. Most Spring Boot applications need very little Spring configuration. + +In this course, you will learn the cool things about Spring Boot and Spring Boot Starter Projects. We will develop a basic REST Service to manage questions of a survey. + +You will learn about Spring Boot step by step - in more than 25 steps. This course would be a perfect first step as an introduction to Spring Boot. + +You will be using Spring Boot and get introduced to REST Services, Spring Security (Authentication and Authorization), Maven (dependencies management), Eclipse (IDE) and Tomcat Embedded Web Server. We will help you set up each one of these. + +You will learn about + +- Basics of Spring Boot +- Basics of Auto Configuration and Spring Boot Magic +- Spring Boot Starter Projects +- Spring Initializr +- Basic REST Services using Spring Boot Starter Web +- REST Service Content Negotiation with JSON and XML +- Embedded servlet containers : Tomcat, Jetty and Undertow +- Writing Unit and Integration tests using Spring Boot Starter Test +- Profiles and Dynamic Configuration with Spring Boot +- Spring Boot Data JPA +- Spring Boot Actuator +- Spring Security +- Spring Boot Developer Tools and LiveReload + +Spring Boot makes it easy to create stand-alone, production-grade Spring based Applications that you can “just run”. We take an opinionated view of the Spring platform and third-party libraries so you can get started with minimum fuss. Most Spring Boot applications need very little Spring configuration. + +With Microservices, focus is shifting to getting started with developing web applications quickly. Spring Boot enables this. + + +### Goals +- Provide quick start for projects with Spring. +- Be opinionated but provide options. +- Provide a range of non-functional features that are common to large classes of projects (e.g. embedded servers, security, metrics, health checks, externalized configuration). +- Absolutely no code generation and no requirement for XML configuration. + +### Step List +- Step 01: Setup and Launch Spring Boot Application with Maven and Eclipse +- Step 02: Creating your first RestController +- Step 03: Understanding Spring Boot Magic : Spring Boot Starter Web +- Step 04: Understanding Spring Boot Magic : Spring Boot Starter Parent +- Step 05: Spring Boot vs Spring +- Step 06: Create all Services for Survey and Questions +- Step 07: What is REST? Creating REST Service with @GetMapping and @PathVariable +- Step 08: Second REST Service to retrieve a specific question +- Step 09: Spring Boot Developer Tools and LiveReload : Develop faster! +- Step 10: Create a REST Service to add a new question to survey : @PostMapping, Postman +- Step 11: Understand Content Negotiation. Deliver XML Responses from the REST Services +- Step 12: Spring Initializr : Create Spring Boot Projects on the fly! +- Step 13: Spring Boot Actuator : Monitor your Spring Boot applications! +- Step 14: Understanding Embedded servlet containers : Switch to Jetty or Undertow +- Step 15: Adding Dynamic Configuration to your application : YAML & More.. +- Step 16: Basics of Profiles +- Step 17: Advanced Application Configuration with Type Safe Configuration Properties +- Step 18: Spring Boot Starter : Spring Data JPA with CommandLineRunner +- Step 19: In Memory Database H2 Console and add a new JPA Repository Method +- Step 20: Spring Boot Starter : Introduction to Spring Data Rest +- Step 21: Spring Boot Integration Test +- Step 22: Adding Integration Test for POST Request +- Step 23: Small Refactoring to organise ourselves +- Step 24: Writing Unit Tests with Spring Boot and Mockito +- Step 25: Writing Unit test for createTodo +- Step 26: Securing our services with Basic Authentication using Spring Security +- Step 27: Configure Spring Security user roles for survey and other services +- Step 28: A Deep Dive into Spring Boot Auto Configuration + +### Expectations +- You should know Java. You should understand usage of Annotations. +- You should understand the basics of Spring framework. +- A basic understanding of JPA, Spring Security will be useful. +- You are NOT expected to have any experience with Eclipse or Maven. +- We will help you install Eclipse and get up and running with Maven. + +## Let's have some fun +- What are we waiting for? +- Let's have some fun with Spring Boot in 25 Steps. +- I had fun creating this course and hope you would too. +- Thanks for your interest in Our Course + - I hope you’re as excited as I am! + - If you’re ready to learn more and sign up for the course, + - go ahead and hit that Enroll button, + - or take a test drive by using the Free Preview feature. +- See you in the course! + +## About in28Minutes +- At in28Minutes, we ask ourselves one question everyday. How do we help you learn effectively - that is more quickly and retain more of what you have learnt? +- We use Problem-Solution based Step-By-Step Hands-on Approach With Practical, Real World Application Examples. +- Our success on Udemy and Youtube (2 Million Views & 12K Subscribers) speaks volumes about the success of our approach. +- While our primary expertise is on Development, Design & Architecture Java & Related Frameworks (Spring, Struts, Hibernate) we are expanding into the front-end world (Bootstrap, JQuery, Angular JS). + +### Our Beliefs +- Best Courses are interactive and fun. +- Foundations for building high quality applications are best laid down while learning. + +### Our Approach +- Problem Solution based Step by Step Hands-on Learning +- Practical, Real World Application Examples. +- We use 80-20 Rule. We discuss 20% things used 80% of time in depth. We touch upon other things briefly equipping you with enough knowledge to find out more on your own. +- We will be developing a demo application in the course, which could be reused in your projects, saving hours of your effort. +- We love open source and therefore, All our code is open source too and available on Github. + +### Other Courses + +- [Check out all our courses with 100,000 Students](https://courses.in28minutes.com/courses) +- [25 Videos and Articles for Beginners on Spring Boot](http://www.springboottutorial.com/spring-boot-tutorials-for-beginners) +- Our Best Courses with 66,000 Students and 4,000 5-Star Ratings + * [Java Interview Guide : 200+ Interview Questions and Answers](https://www.udemy.com/java-interview-questions-and-answers/?couponCode=JAVA_INTER_GIT) + * [Mockito Tutorial : Learn mocking with 25 Junit Examples](https://www.udemy.com/mockito-tutorial-with-junit-examples/?couponCode=MOCKITO_GIT) + * [Java EE Made Easy - Patterns, Architecture and Frameworks](https://www.udemy.com/java-ee-design-patterns-architecture-and-frameworks/?couponCode=EEPATTERNS-GIT) + * [Spring MVC For Beginners : Build Java Web App in 25 Steps](https://www.udemy.com/spring-mvc-tutorial-for-beginners-step-by-step/?couponCode=SPRINGMVC-GIT) + * [JSP Servlets For Beginners : Build Java Web App in 25 Steps](https://www.udemy.com/learn-java-servlets-and-jsp-web-application-in-25-steps/?couponCode=JSPSRVLT-GIT) + * [Maven Tutorial - Manage Java Dependencies in 25 Steps](https://www.udemy.com/learn-maven-java-dependency-management-in-20-steps/?couponCode=MAVEN_GIT) + * [Java OOPS in 1 Hours](https://www.udemy.com/learn-object-oriented-programming-in-java/?couponCode=OOPS-GIT) + * [C Puzzle for Interview](https://www.udemy.com/c-puzzles-for-beginners/?couponCode=CPUZZLES-GIT) + +### Useful Links +- [Our Website](http://www.in28minutes.com) +- [Facebook](http://facebook.com/in28minutes) +- [Twitter](http://twitter.com/in28minutes) +- [Google Plus](https://plus.google.com/u/3/110861829188024231119) diff --git a/05.Spring-Boot-Advanced/Step01.md b/05.Spring-Boot-Advanced/Step01.md new file mode 100644 index 0000000..34fea76 --- /dev/null +++ b/05.Spring-Boot-Advanced/Step01.md @@ -0,0 +1,74 @@ +## What You Will Learn during this Step: +- Set up an Maven Project with Eclipse. + - Intellij Link : https://www.jetbrains.com/help/idea/2016.2/getting-started-with-maven.html#create_maven_project +- Copy Two Files pom.xml and Application.java +- Launch Your First Spring Boot Application. +- You will be introduced to Maven + - Dependency Management + +## Cool thing to note! +- Without a lot of configuration, we are up and running with a web application + - Refer https://github.com/in28minutes/SpringMvcStepByStep/blob/master/Step15.md to understand the sort of stuff - web.xml, dispatcher servlet configuration, maven dependency management and plugins - that are need to launch a typical web application without Spring Boot! + +## What You Will NOT Learn during this Step: +- Spring Boot does a lot of magic. This magic is called Auto Configuration. We will discuss about different terms related to Spring Boot - Starter Parent, Starter projects, Auto configuration - in depth during our first 10 steps. +- As far as this step is concerned, we will focus on getting up and running with Spring Boot. We will understand all the magic a little later. +- We will copy a lot of code in this step - just to avoid typos + +## Exercises +- If you are comfortable with Spring, try to create a few dependencies and see if are automatically auto-wired! + +## Files List +### pom.xml +``` + + 4.0.0 + com.in28minutes.springboot + first-springboot-project + 0.0.1-SNAPSHOT + + org.springframework.boot + spring-boot-starter-parent + 1.4.0.RELEASE + + + + 1.8 + + + + + org.springframework.boot + spring-boot-starter-web + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + +``` +### src/main/java/com/in28minutes/springboot/Application.java +``` +package com.in28minutes.springboot; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.ApplicationContext; + +@SpringBootApplication +public class Application { + + public static void main(String[] args) { + ApplicationContext ctx = SpringApplication.run(Application.class, args); + + } + +} +``` diff --git a/05.Spring-Boot-Advanced/Step01.zip b/05.Spring-Boot-Advanced/Step01.zip new file mode 100644 index 0000000000000000000000000000000000000000..54dc6115c61449db4bf31a5b952e6f3f4c9b9105 GIT binary patch literal 2337 zcmWIWW@Zs#-~d8R7eg-wB*4xfz)+B%t5=bm6B@z8u)e1@qO7wv;wvKq!_-L!^9~vC zw4MJTvf{g1>)VEa@J{ASY6l%&o4l2Z3O;m8#p}|vy?-yY1f6Oz%lkL?{ zYmaZDW6h~4YC4Sh76z=wshhp$3HVz6KECCm?(x8-dM~5pjc#?fHE(#J`9xk}>Pz{X z%LTp{9h>YK*EQY5tD;e3wh#9yFJ3N9Ma|RO=DZfzE|;txbXnGOOQ5yZ)Nj`F>vzR< z==&^NV#S+%;#l1utNTaJzy2-|F599YG{Y?Lc=9(Z!#66L3!Z)Aj=lV)V9w3$D$mXZ zHGhe{uf3ozqi5FQJ-XAnEBCx$d^l}Ii?_C6*<^^>PBO1tk%LmFRkfF!Uzp=aQgb z4y*pmJR^(T%)HW))M66M(j;hBaY0dLUV2h~ehF4%Az26AisU~blbM`Yl9`{U z2MS71qZyYnf5KUX zJm(#Y!g`*B+q;LPU3zjpqu4|_A-tzvF?E{n@uErh_b)Aa|4iGz<<2yzFQH3Wgqh`> zLKe^5;USgRqF%sNd$*w9O2*N3cA9Eb=zMF(V)pDGKbm`PUcX#-!?u3&sfAaAJDzic zQf^4Z=CiwjskIp;Q52!2Tw&x8D@rZSFD*(&ib;hI^i+kO^kE4Z82^Zv!c70wX!gPq zGFTf*LN>&(va~`CrGSLF8LSu6&7~>%(44@?B+85^W0A8mI|C1_#C2HG2x7uZTn>1N z8{iEx09TGj81U9{Gcb%H`JV%x|MBVOM6&*^<5Q?^Xjy>W2=t`Rhhl^b6T|^vBcQlo7<4C5K|x46J6MmKVgCtBGRn6N2WVlpW|+AW9D89Ab%N z#gayAU}+DEPegEnf&tm-NLFB$BOLH@1Ucj}vmSC1%4I>a5;;*r+z-l#$cABNLuA9& zK@EduN{A7lOo?m+W|l-Y;wiEbC@Bh*L6ObD%$~^RD6&HQg^^hyjsj)Y0B=?{P!{9? O!j-^~PXcOTU;qGP5RV4{ literal 0 HcmV?d00001 diff --git a/05.Spring-Boot-Advanced/Step02.md b/05.Spring-Boot-Advanced/Step02.md new file mode 100644 index 0000000..092555c --- /dev/null +++ b/05.Spring-Boot-Advanced/Step02.md @@ -0,0 +1,134 @@ +## What You Will Learn during this Step: +- Lets add a RestController with a dependency and see Spring Boot Magic live + +## Theory Break : Quick Spring and Spring MVC Primer +- What is dependency? +- @Component +- @Autowired +- @RestController + +## Useful Snippets and References +``` +package com.in28minutes.springboot; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +public class WelcomeController { + + //Auto wiring + @Autowired + private WelcomeService service; + + @RequestMapping("/welcome") + public String welcome() { + return service.retrieveWelcomeMessage(); + } +} + +@Component +class WelcomeService { + + public String retrieveWelcomeMessage() { + //Complex Method + return "Good Morning updated"; + } +} +``` +## Files List +### pom.xml +``` + + 4.0.0 + com.in28minutes.springboot + first-springboot-project + 0.0.1-SNAPSHOT + + org.springframework.boot + spring-boot-starter-parent + 1.4.0.RELEASE + + + + 1.8 + + + + + org.springframework.boot + spring-boot-starter-web + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + +``` +### src/main/java/com/in28minutes/service/WelcomeService.java +``` +package com.in28minutes.service; + +import org.springframework.stereotype.Component; + +@Component +public class WelcomeService { + + public String retrieveWelcomeMessage() { + //Complex Method + return "Good Morning updated"; + } +} +``` +### src/main/java/com/in28minutes/springboot/Application.java +``` +package com.in28minutes.springboot; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.ApplicationContext; +import org.springframework.context.annotation.ComponentScan; + +@SpringBootApplication +@ComponentScan("com.in28minutes") +public class Application { + + public static void main(String[] args) { + ApplicationContext ctx = SpringApplication.run(Application.class, args); + + } + +} +``` +### src/main/java/com/in28minutes/springboot/WelcomeController.java +``` +package com.in28minutes.springboot; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import com.in28minutes.service.WelcomeService; + +@RestController +public class WelcomeController { + + //Auto wiring + @Autowired + private WelcomeService service; + + @RequestMapping("/welcome") + public String welcome() { + return service.retrieveWelcomeMessage(); + } +} +``` diff --git a/05.Spring-Boot-Advanced/Step02.zip b/05.Spring-Boot-Advanced/Step02.zip new file mode 100644 index 0000000000000000000000000000000000000000..ab760d7e5772cd9cedc83f7ad6b919acf0a25ce1 GIT binary patch literal 3433 zcmb7`dpOg39LIl%&3z|bJjtb#)>(uh1Qt#kb<*S zw0}%8-0v718pk0HZeCP+EvCKCr<2oA6XCa!Fkpymc^7jb%Zr3N(Dnz~SdMOErf5Oo zcod_&)_-h0-?_Hchix(%q;KAznR>YPF=j~LAa_*1uN*bWXvuLS9?7?Kk4yZi&{MU| zU0nsU0(0vkYYbJVM_%pQt?PE)$8JsTTf34&^~7{j&%euT)gx}U9GtQpXzm!BL-_;r4Lt6HP*s^4YpLS6akSFiazvOB2-j&ZEyzgK~^iKC{Pr49q=IsvZ zdJyPL3>_}7_wnw1N4#Cg;+NOA_mtjw*}n2VA#h0R6qoku&R_a?4+L_eruqpDRK7+5 z0Px_*RAvT+c9I;~0&rw846-RSuRw5K$I=Az`f`P`up){?rOi{8;Vjum16~$R+Cvi6 zDrZTzl&)PUQ%j~t%~Rj%D%o3a&Uz}%+$M@jW5!ZqL@X0f0_eDkx8EkkLVuMNLt*Tp zk|`plC5$P!H`2`Y-^>i4M1otVY!$RHfpml>X9c#HZR2pXK?A&L&QFJpjL`(&4CM^P zjKs>Vh~aFa`zD>u-c3#wpS{B_efZP5hR*ja6f@bLb%ThpR?6FC z%sq-@JwLnC#XgX4i$>aT60u` zD&s=rbeahVgXk*T{Fx3Bh|3kY`rQF8DIHI!%$@uS(o2qETDxIvHMa`AE?y2grRFK4 z&#J(a`563tS#-?T1&U@+X<<9*^w@cHzbiG%Z_r(g#YIO)Qpu!PDxD_M-4WCs`awtL z_iS8*JQkJGQMdyex9!x>-5dkOphHc1>@;i(%F<%R$qIw%y__+0oKpua31&FTv_@aW6e@9a>9B zR_}h)aAI8hg+JnKs^8h(%n{|Q2VG73vePNiqw4}##%^J?d#di0l=xKZ2itvQ*7gP8 zICsXfVCArymQiqScngW|q0Z`kZcti&G3QF;EA@il;(+ti;sE)TOW!4wTa5Wt6kSQ< z*KPh~JNpub*`NRoNq*V0;vR57PlPu^aEjZd)ZF+zLlPcjCps;bL63~2FhrW#gL>Kt zH7)YthF>>B6|>7xC&!&ry%kMOia3@|VQ!bSe+VjPd0VlE9#)4~S7KsE;yz6KSn?x( z!)t8jUGIK1;uPv?>pXF)F8zV~Xlr`2O687$ExTL_j<@$GDZ7to!esyNge;Gz=RJB3@O*v zf=kVy#L$@xGR(yc(xqKY+V*En9P~c%g^~6C{Zf5rO?;+I!o;yMEtzp>iO0e0nrCWd z*Ua2S7do$;{9*<1`vM+g2tW;#X6EBqmI(ceUK>c!YXk2A0lt)WVgL`v0W}t6A5xTk z^HnRu+VgP#39E%UI5$E%$uuMo%jE?Nm_Z2T6mXzSgfB&!vrvLVkl_gOac-7uI>JRM zn3E-$l>#o5mGCu6ry5#fjYD7MigQ!IhXPH&7pEpXF4n{NWr;T6GY1-`SW8F)__}BL zb7m+~l%ex&2?J}%!v!w*YD;l^_!H*d6_iPEf0f)-?f0~hO(-NVn^15eA>)So%tqL* z$`Yp)zhhTlvT-CZYvXV^Wo-<8@~8+{W4J*Ggo8{C4wJPsIIKzt6B?WV0T~<|A!~1N u#2_3YaXTTigL7o94$jd<3ic&qe1b(m#)tP%RD$eG6a2mbM@|Gw0N{TW10b~k literal 0 HcmV?d00001 diff --git a/05.Spring-Boot-Advanced/Step03.md b/05.Spring-Boot-Advanced/Step03.md new file mode 100644 index 0000000..c917669 --- /dev/null +++ b/05.Spring-Boot-Advanced/Step03.md @@ -0,0 +1,117 @@ +## What You Will Learn during this Step: +- First installment of revealing how magic happens with Spring Boot. As a Spring Boot developer, you need to understand what's happening beneath the hood of Spring Boot! +- spring-boot-starter-web : starter for building applications with Spring MVC. Tomcat is default embedded container. +- We already added this starter in the first step! Now we will explore the features it provides +- We will enable logging in DEBUG mode to understand further + +##spring-boot-starter-web +- Spring Boot Starter Web brings all dependencies needed to build normal and RESTful web applications. Look at the dependency tree. + - All the dependencies are added in because of spring-boot-starter-web + - Also look at /META-INF/spring.provides inside the spring-boot-starter-web.jar +- Spring Boot Starter Web auto configures things needed to startup a web application. Look at the log + - Mapping servlet: 'dispatcherServlet' to [/] + - Mapped "{[/error]}" onto public org.springframework.http.ResponseEntity> org.springframework.boot.autoconfigure.web.BasicErrorController.error(javax.servlet.http.HttpServletRequest) + - Mapped URL path [/webjars/**] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler] + - Look at package org.springframework.boot.autoconfigure.web in spring-boot-autoconfigure-*.jar +- Go to url http://localhost:8080/some-non-existing-url + +## Useful Snippets +/src/main/resources/application.properties +``` +logging.level.org.springframework: DEBUG +``` + +## Files List +### pom.xml +``` + + 4.0.0 + com.in28minutes.springboot + first-springboot-project + 0.0.1-SNAPSHOT + + org.springframework.boot + spring-boot-starter-parent + 1.4.0.RELEASE + + + + 1.8 + + + + + org.springframework.boot + spring-boot-starter-web + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + +``` +### src/main/java/com/in28minutes/springboot/Application.java +``` +package com.in28minutes.springboot; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.ApplicationContext; + +@SpringBootApplication +public class Application { + + public static void main(String[] args) { + ApplicationContext ctx = SpringApplication.run(Application.class, args); + + } + +} +``` +### src/main/java/com/in28minutes/springboot/WelcomeController.java +``` +package com.in28minutes.springboot; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +public class WelcomeController { + + //Auto wiring + @Autowired + private WelcomeService service; + + @RequestMapping("/welcome") + public String welcome() { + return service.retrieveWelcomeMessage(); + } +} +``` +### src/main/java/com/in28minutes/springboot/WelcomeService.java +``` +package com.in28minutes.springboot; + +import org.springframework.stereotype.Component; + +@Component +public class WelcomeService { + + public String retrieveWelcomeMessage() { + //Complex Method + return "Good Morning updated"; + } +} +``` +### src/main/resources/application.properties +``` +logging.level.org.springframework: DEBUG +``` diff --git a/05.Spring-Boot-Advanced/Step04.md b/05.Spring-Boot-Advanced/Step04.md new file mode 100644 index 0000000..30f4e37 --- /dev/null +++ b/05.Spring-Boot-Advanced/Step04.md @@ -0,0 +1,28 @@ +## What You Will Learn during this Step: +- Understand Starter Parent +- How to override things defined in Starter Parent +- Other starter projects + +## Starter Parent +- Dependency Versions +- Java Versions +- Default Plugins + +## Other Starter Projects +- spring-boot-starter-web-services +- spring-boot-starter-test +- spring-boot-starter-jdbc +- spring-boot-starter-security +- spring-boot-starter-data-jpa +- spring-boot-starter-data-rest +- More at https://docs.spring.io/spring-boot/docs/current-SNAPSHOT/reference/htmlsingle/#using-boot-starter + +## Useful Snippets and References +First Snippet +``` + + org.springframework.boot + spring-boot-starter-parent + 1.4.0.RELEASE + +``` diff --git a/05.Spring-Boot-Advanced/Step05.md b/05.Spring-Boot-Advanced/Step05.md new file mode 100644 index 0000000..ea4164e --- /dev/null +++ b/05.Spring-Boot-Advanced/Step05.md @@ -0,0 +1,48 @@ +## What You Will Learn during this Step: +- Spring Boot vs Spring +- What Spring Boot is Not! + +## Spring Boot vs Spring + +### Spring +- Spring is just a dependency injection framework. Spring focuses on the "plumbing" of enterprise applications so that teams can focus on application-level business logic, without unnecessary ties to specific deployment environments. +- First half of the 2000 decade! EJBs +- EJBs were NOT easy to develop. + - Write a lot of xml and plumbing code to get EJBs running + - Impossible to Unit Test + - Alternative - Writing simple JDBC Code involved a lot of plumbing +- Spring framework started with aim of making Java EE development simpler. + - Goals + - Make applications testable. i.e. easier to write unit tests + - Reduce plumbing code of JDBC and JMS + - Simple architecture. Minus EJB. + - Integrates well with other popular frameworks. + +### Applications with Spring Framework +- Over the next few years, a number of applications were developed with Spring Framework + - Testable but + - Lot of configuration (XML and Java) + - Developing Spring Based application need configuration of a lot of beans! + - Integration with other frameworks need configuration as well! +- In the last few years, focus is moving from monolith applications to microservices. We need to be able to start project quickly. Minimum or Zero start up time + - Framework Setup + - Deployment - Configurability + - Logging, Transaction Management + - Monitoring + - Web Server Configuration + +### Spring Boot +- Spring Boot makes it easy to create stand-alone, production-grade Spring based Applications that you can “just run”. +- We take an opinionated view of the Spring platform and third-party libraries so you can get started with minimum fuss. +- Example Problem Statements + - You want to add Hibernate to your project. You dont worry about configuring a data source and a session factory. I will do if for you! +- Goals + - Provide quick start for projects with Spring. + - Be opinionated but provide options. + - Provide a range of non-functional features that are common to large classes of projects (e.g. embedded servers, security, metrics, health checks, externalized configuration). + +#### What Spring Boot is NOT? +- It’s not an app or a web server +- Does not implement any specific framework - for example, JPA or JMS +- Does not generate code + diff --git a/05.Spring-Boot-Advanced/Step06.md b/05.Spring-Boot-Advanced/Step06.md new file mode 100644 index 0000000..2d6e809 --- /dev/null +++ b/05.Spring-Boot-Advanced/Step06.md @@ -0,0 +1,344 @@ +## What You Will Learn during this Step: +- We want to prepare for creating a Rest Service + - Survey + - Question + - SurveyService +- We use hard-coded data to get started + +## Files List +### pom.xml +``` + + 4.0.0 + com.in28minutes.springboot + first-springboot-project + 0.0.1-SNAPSHOT + + org.springframework.boot + spring-boot-starter-parent + 1.4.0.RELEASE + + + + 1.8 + + + + + org.springframework.boot + spring-boot-starter-web + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + +``` +### src/main/java/com/in28minutes/springboot/Application.java +``` +package com.in28minutes.springboot; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.ApplicationContext; + +@SpringBootApplication +public class Application { + + public static void main(String[] args) { + ApplicationContext ctx = SpringApplication.run(Application.class, args); + + } + +} +``` +### 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 void setId(String id) { + this.id = id; + } + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public List getQuestions() { + return questions; + } + + public void setQuestions(List questions) { + this.questions = questions; + } + + @Override + public String toString() { + return "Survey [id=" + id + ", title=" + title + ", description=" + + description + ", questions=" + 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/java/com/in28minutes/springboot/WelcomeController.java +``` +package com.in28minutes.springboot; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +public class WelcomeController { + + //Auto wiring + @Autowired + private WelcomeService service; + + @RequestMapping("/welcome") + public String welcome() { + return service.retrieveWelcomeMessage(); + } +} +``` +### src/main/java/com/in28minutes/springboot/WelcomeService.java +``` +package com.in28minutes.springboot; + +import org.springframework.stereotype.Component; + +@Component +public class WelcomeService { + + public String retrieveWelcomeMessage() { + //Complex Method + return "Good Morning updated"; + } +} +``` +### src/main/resources/application.properties +``` +logging.level.org.springframework: DEBUG +``` diff --git a/05.Spring-Boot-Advanced/Step06.zip b/05.Spring-Boot-Advanced/Step06.zip new file mode 100644 index 0000000000000000000000000000000000000000..0ce0f7397d798c1c49363713a2fd859292383964 GIT binary patch literal 6363 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 literal 0 HcmV?d00001 diff --git a/05.Spring-Boot-Advanced/Step07.md b/05.Spring-Boot-Advanced/Step07.md new file mode 100644 index 0000000..e955ea0 --- /dev/null +++ b/05.Spring-Boot-Advanced/Step07.md @@ -0,0 +1,407 @@ +## What You Will Learn during this Step: +- Create a REST Service for Retrieving all questions for a survey + - 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') + +## Some Theory +- 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. + +## Useful Snippets and References +- JSON View : https://jsonview.com/ +First Snippet +``` +@RestController +class SurveyController { + @Autowired + private SurveyService surveyService; + + @GetMapping("/surveys/{surveyId}/questions") + public List retrieveQuestions(@PathVariable String surveyId) { + return surveyService.retrieveQuestions(surveyId); + } +} +``` + +## Exercise +- Try to think about how the URI for retrieving the details of a specific question should be! + +## Files List +### pom.xml +``` + + 4.0.0 + com.in28minutes.springboot + first-springboot-project + 0.0.1-SNAPSHOT + + org.springframework.boot + spring-boot-starter-parent + 1.4.0.RELEASE + + + + 1.8 + + + + + org.springframework.boot + spring-boot-starter-web + + + + org.springframework.boot + spring-boot-devtools + true + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + +``` +### src/main/java/com/in28minutes/springboot/Application.java +``` +package com.in28minutes.springboot; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.ApplicationContext; + +@SpringBootApplication +public class Application { + + public static void main(String[] args) { + ApplicationContext ctx = SpringApplication.run(Application.class, args); + + } + +} +``` +### src/main/java/com/in28minutes/springboot/controller/SurveyController.java +``` +package com.in28minutes.springboot.controller; + +import java.util.List; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RestController; + +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); + } +} +``` +### 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 void setId(String id) { + this.id = id; + } + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public List getQuestions() { + return questions; + } + + public void setQuestions(List questions) { + this.questions = questions; + } + + @Override + public String toString() { + return "Survey [id=" + id + ", title=" + title + ", description=" + + description + ", questions=" + 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/java/com/in28minutes/springboot/WelcomeController.java +``` +package com.in28minutes.springboot; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +public class WelcomeController { + + //Auto wiring + @Autowired + private WelcomeService service; + + @RequestMapping("/welcome") + public String welcome() { + return service.retrieveWelcomeMessage(); + } +} +``` +### src/main/java/com/in28minutes/springboot/WelcomeService.java +``` +package com.in28minutes.springboot; + +import org.springframework.stereotype.Component; + +@Component +public class WelcomeService { + + public String retrieveWelcomeMessage() { + //Complex Method + return "Good Morning updated"; + } +} +``` +### src/main/resources/application.properties +``` +logging.level.org.springframework: DEBUG +``` diff --git a/05.Spring-Boot-Advanced/Step08.md b/05.Spring-Boot-Advanced/Step08.md new file mode 100644 index 0000000..5ecb3c2 --- /dev/null +++ b/05.Spring-Boot-Advanced/Step08.md @@ -0,0 +1,401 @@ +## What You Will Learn during this Step: +- 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 +``` + @GetMapping(path = "/surveys/{surveyId}/questions/{questionId}") + public Question retrieveQuestion(@PathVariable String surveyId, + @PathVariable String questionId) { + return surveyService.retrieveQuestion(surveyId, questionId); + } + +``` + +## Exercises +- Write the method to retrieve all surveys! + +## Files List +### pom.xml +``` + + 4.0.0 + com.in28minutes.springboot + first-springboot-project + 0.0.1-SNAPSHOT + + org.springframework.boot + spring-boot-starter-parent + 1.4.0.RELEASE + + + + 1.8 + + + + + org.springframework.boot + spring-boot-starter-web + + + + org.springframework.boot + spring-boot-devtools + true + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + +``` +### src/main/java/com/in28minutes/springboot/Application.java +``` +package com.in28minutes.springboot; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.ApplicationContext; + +@SpringBootApplication +public class Application { + + public static void main(String[] args) { + ApplicationContext ctx = SpringApplication.run(Application.class, args); + + } + +} +``` +### src/main/java/com/in28minutes/springboot/controller/SurveyController.java +``` +package com.in28minutes.springboot.controller; + +import java.util.List; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RestController; + +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); + } + + // GET "/surveys/{surveyId}/questions/{questionId}" + @GetMapping("/surveys/{surveyId}/questions/{questionId}") + public Question retrieveDetailsForQuestion(@PathVariable String surveyId, + @PathVariable String questionId) { + return surveyService.retrieveQuestion(surveyId, questionId); + } + +} +``` +### 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 void setId(String id) { + this.id = id; + } + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public List getQuestions() { + return questions; + } + + public void setQuestions(List questions) { + this.questions = questions; + } + + @Override + public String toString() { + return "Survey [id=" + id + ", title=" + title + ", description=" + + description + ", questions=" + 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/java/com/in28minutes/springboot/WelcomeController.java +``` +package com.in28minutes.springboot; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +public class WelcomeController { + + //Auto wiring + @Autowired + private WelcomeService service; + + @RequestMapping("/welcome") + public String welcome() { + return service.retrieveWelcomeMessage(); + } +} +``` +### src/main/java/com/in28minutes/springboot/WelcomeService.java +``` +package com.in28minutes.springboot; + +import org.springframework.stereotype.Component; + +@Component +public class WelcomeService { + + public String retrieveWelcomeMessage() { + //Complex Method + return "Good Morning updated"; + } +} +``` +### src/main/resources/application.properties +``` +logging.level.org.springframework: DEBUG +``` diff --git a/05.Spring-Boot-Advanced/Step08.zip b/05.Spring-Boot-Advanced/Step08.zip new file mode 100644 index 0000000000000000000000000000000000000000..2e1072cac0b49fa28ed7d20171c9de0f6efea65b GIT binary patch literal 7219 zcmbVQcRbYpAHPCowz5OkB{Rz2lzsMS$T(c~tcijFYl=wlcpqQO?RHnPq*8 z_}z8V=cw=R>+2rwasRxYulH-bU+?GZ2~okqrUc<3Kiq2k${@fWE{GK5U~kKF%hpEM zfCzLX&&$9z*2`e+2nZDYNYmbzkF@F8nl|+a?A^i}YQ=HC=9rIyW`^rVWA&m|@g?4y z>s;C1c~8_5-J5pqZdCYvll?*=@9xr}^*pihVs^vPi4e-yq-3n3N5=);R4g2mO2Ll{ zNpN!1E7E)n4Q|2Bj8|jV-YPQ;YNk0>5o{zVTnxduDlGTct7wMDC5mHo# zKgT79Q82vF8L)Q!0QHcpCZ|zxgy}`7RV}-g5k7mhB*|lhC&S8TtIi!BJ6G%Qkkv1; zmA5y2c2qduQmHL5?bvKtxkOTo)%}dL%Ix=<(9%z!vA7q$kFI!wK`n6_aHbd8d>f7& zX6^dK6irOt4X;zmQ!8eE@?AmyR1M7VS+qhm z=|D|tIr|$Dhzc%VBX<^26tX^c*dP!DSy+M}I!3kz(X=-opvIonwdsk3dMPD$IeMb~I46KZUgAL5g z#06$=$Ad~Gs(3rOUIrz*rw@JDNl?p2)Yv(vJt-{XzRVgl`s^Y?Q{1IECdpPg{*$!l z&5E)*X&L+G4X>|ktejq&@4eYMx_M)Wy(}J?6OdKe2H#@CE|-q*Nnu!X*6A1mUo(I9 zu${?Ht~5n2WKv)1qK?Je$xbV0!PD3EL+&y|W7L|QB1dY{oR+4!H2jBRXjgU9uqm(@ zWpz?xOE1yd`ExiBcnv#*i_*&|#9Oc#={^&aamKa&`)|LH{=S|&2#LFeEy?e+gJuX( zovUL|*j|C`RzGC-{^~=VeT5J_OvcRY?OdGfZET=Udo^J`P?O_Em~Erngk6oFZ|ql627h0) zQ|b{k@-18F=vZp6e1*fxWsq`7l_$Eov@M#4Cc6j+Ton>(5=-4~UO)t1^@=_jVBjf( zQ(zw5%mFRSrZZyf!jTEHmgAy1ZN+u+VP0}5cJ^^BuGA^DW{YgRo;(s`v$JMtJ=#tI zTSN+D-;Sm7zJ7K}nqZs=x6a<`>$)nv#OxG%gKBk5mHm1FvSlKuIUPOK5(Ub=skY4 z%RL>H@E$%=$@CgiH#RtB#@TSf0j(1{!rtCg<3YlupUvmS5pTi^2nTjL5b8ap3->c{ zEg45VSK1K_nRg55{Ec2kLpqI1WN!zS6eN&+vPdh=G-}uE;GpPs8xTh1ac5Ri6ntaN zI_)u|uhk1tvEcSxvr6b9*j6X-MF<2gx8s~8^}J&~VP$=~{GsD~Rx!Up#jBQW#dA)9 z<+)mI)?NJl}QN#jM|<=3ot)AF?B+o};uN$gN^tvw3Uj zgNWKS&ndBHLR~0iWJnKkIg0&UXn^}GU4lYoe8Zd+ybMLy#Aa?d}{r5W7zB9DUS+*c`vw z@RKPI@qV&`0LgZT&FGoIRhKI(BXeSgRXyjgsJ@u54LEi+J!39>cBEaZvPjFC)aeG@ z8Bza&@rj*vVpE2dw88w%dT{^9>7s{>t5*z~hU+XG(;TI@i-l zJ&gRwiWB5sKI#%o&Sv&G|9mxTKCJIX(OX3lT;6XeV~qjdW-tA8WGWSB)BeK^bz|7ACC!Rb;~_XkqlrVT=rOphf} zecLW&Y}~raI~uWkCCck$D!A@~R^)dH-EUE94E(otN_ox6u3w0ZT0S#*PLGxquPPTb zLNM3pbCYG8rI=ELZ)@25t@ky}Vpp{kyZV1K!=7 zWgG>0mp|%J=S(qbhOM6H(qJ}BRMq@q6_rw-T*O2<8dgr$Oo!-Gg_zj#qZn z%gL**bxmGb%*S^o=w1mHFSYJv_!*|ob@IWAg-@5$?+Y`YmsXtK@h|RP>`+Xc3;2qt zT6~;d)_zUa+4;Hc&4DR}zor8@+SD(NZM?0&Ybi8V$LPf_W^#%HZ`$C67$g=RotcX$ zbyEzC8J;1)y04U&e&_0|nfAEau8l@R-xx|ahq@;tTC??`xkA*<6Qvs(cuuA^kb?JX zbrdXmWl|QWS(P`xNY%?#H3iv0Kn*w_braS3DyeIA2I#58Hz8|jg*w~32BjwRK?5b- z5h9B(CJ=Q2$KmHYa>lbkF0NCgN9|9wXs)sK`d*Hdc^2nGD?k#F&S_I0?{J4ICmWYy zq{uBPv*+VSQ5#oWudaEk*`m!`HFe>78~O}NJNOiR`Ed2syti%^>L{+7zX?bR136@tz zfner*8QPL>!9&=DZ~NCrO-ndtXlComsUsM0L)O#yczi|?+ib>3ue^eqMp`bEQvOwW z6Wly6H5qHnFEA#2GuxY8ayjye z1yn491aV2&b8|;^HmH*Zgjz)_G8L+8NjwK_N)&Yob-_n@D3>y`Ji)Jt=wKuwt!WwM z?>=SZ8ly%dq?zmdC*B!f30B@Z8}^0H`J&K+LDs@07x~BfXD+|{ z_Jf?aovXY-_WIMrWH!U#k+gN_%%JuW}z359d(=I2GIU^O%{F(97V`Qqg%4--D6CaAE|sUugL$n;_MOU%J3SwZ&tfKJ!yH`C*13oA3wyN&x}VH3;N- zi(UE0kKM1IIs9S!D+NEL9#?bwAH)fwa^xr#7P?hYx*t$D!UYliM3jd6pnmSDfOvZ< zAQ}LDhW%k<0{|9e`jPhntv2E9sZIZ6jgMhXh#X)j>s{sP_YnKFBVr5)Ss=u2I?*^N z6$*Hi{W#V{fHHH zMj$SRQiXPbQmW7{&<%mosepQn2p|n`ACtqlIH+3T?WtD4s16D}z^EP}S?9lw3Pbim z2Z)k=fB~XJAD~{Kgc0D{fAsbT2?!l9N&*4~yq`Mr0*u%5DZkGxiopR%hZKhMX_7<4`6oVyc8ub4 zz@nl!4e%w$Y$Y|a!#l=gIB2gZh660?L6!g<4UXghufX>XT{skH0EU2K48XS=jFG#7 z--G>$KcLTx;t#;{?tcw2-!oYX^m%tF0s53EN&tj7NC Roles + protected void configure(AuthenticationManagerBuilder auth) + throws Exception { + auth.inMemoryAuthentication().withUser("user1").password("secret1") + .roles("USER").and().withUser("admin1").password("secret1") + .roles("USER", "ADMIN"); + } + + // Authorization : Role -> Access + // survey -> USER + protected void configure(HttpSecurity http) throws Exception { + http.httpBasic().and().authorizeRequests().antMatchers("/surveys/**") + .hasRole("USER").antMatchers("/users/**").hasRole("USER") + .antMatchers("/**").hasRole("ADMIN").and().csrf().disable() + .headers().frameOptions().disable(); + } + +} +``` +### 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/java/com/in28minutes/springboot/WelcomeController.java +``` +package com.in28minutes.springboot; + +import java.util.HashMap; +import java.util.Map; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import com.in28minutes.springboot.configuration.BasicConfiguration; + +@RestController +public class WelcomeController { + + //Auto wiring + @Autowired + private WelcomeService service; + + @Autowired + private BasicConfiguration configuration; + + @RequestMapping("/welcome") + public String welcome() { + return service.retrieveWelcomeMessage(); + } + + @RequestMapping("/dynamic-configuration") + public Map dynamicConfiguration() { + Map map = new HashMap(); + map.put("message", configuration.getMessage()); + map.put("number", configuration.getNumber()); + map.put("value", configuration.isValue()); + + return map; + } + +} +``` +### src/main/java/com/in28minutes/springboot/WelcomeService.java +``` +package com.in28minutes.springboot; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +//Spring to manage this bean and create an instance of this +@Component +public class WelcomeService { + + @Value("${welcome.message}") + private String welcomeMessage; + + public String retrieveWelcomeMessage() { + //Complex Method + return welcomeMessage; + } +} +``` +### src/main/resources/application-dev.properties +``` +logging.level.org.springframework: TRACE +``` +### src/main/resources/application-prod.properties +``` +logging.level.org.springframework: INFO +``` +### src/main/resources/application.properties +``` +logging.level.org.springframework: DEBUG +app.name=in28Minutes +welcome.message=Welcome message from property file! Welcome to ${app.name} + +basic.value=true +basic.message=Welcome to in28minutes +basic.number=200 +``` +### src/test/java/com/in28minutes/springboot/controller/SurveyControllerIT.java +``` +package com.in28minutes.springboot.controller; + +import static org.junit.Assert.assertTrue; + +import java.nio.charset.Charset; +import java.util.Arrays; +import java.util.List; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.skyscreamer.jsonassert.JSONAssert; +import org.springframework.boot.context.embedded.LocalServerPort; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.web.client.TestRestTemplate; +import org.springframework.core.ParameterizedTypeReference; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.security.crypto.codec.Base64; +import org.springframework.test.context.junit4.SpringRunner; + +import com.in28minutes.springboot.Application; +import com.in28minutes.springboot.model.Question; + +@RunWith(SpringRunner.class) +@SpringBootTest(classes = Application.class, + webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) +public class SurveyControllerIT { + + @LocalServerPort + private int port; + + TestRestTemplate restTemplate = new TestRestTemplate(); + + HttpHeaders headers = new HttpHeaders(); + + @Before + public void before() { + headers.add("Authorization", createHttpAuthenticationHeaderValue( + "user1", "secret1")); + headers.setAccept(Arrays.asList(MediaType.APPLICATION_JSON)); + } + + @Test + public void testRetrieveSurveyQuestion() { + + HttpEntity entity = new HttpEntity(null, headers); + + ResponseEntity response = restTemplate.exchange( + createURLWithPort("/surveys/Survey1/questions/Question1"), + HttpMethod.GET, entity, String.class); + + String expected = "{id:Question1,description:Largest Country in the World,correctAnswer:Russia}"; + + JSONAssert.assertEquals(expected, response.getBody(), false); + } + + @Test + public void retrieveAllSurveyQuestions() throws Exception { + + ResponseEntity> response = restTemplate.exchange( + createURLWithPort("/surveys/Survey1/questions"), + HttpMethod.GET, new HttpEntity("DUMMY_DOESNT_MATTER", + headers), + new ParameterizedTypeReference>() { + }); + + Question sampleQuestion = new Question("Question1", + "Largest Country in the World", "Russia", Arrays.asList( + "India", "Russia", "United States", "China")); + + assertTrue(response.getBody().contains(sampleQuestion)); + } + + @Test + public void addQuestion() { + + Question question = new Question("DOESNTMATTER", "Question1", "Russia", + Arrays.asList("India", "Russia", "United States", "China")); + + HttpEntity entity = new HttpEntity(question, headers); + + ResponseEntity response = restTemplate.exchange( + createURLWithPort("/surveys/Survey1/questions"), + HttpMethod.POST, entity, String.class); + + String actual = response.getHeaders().get(HttpHeaders.LOCATION).get(0); + + assertTrue(actual.contains("/surveys/Survey1/questions/")); + + } + + private String createURLWithPort(final String uri) { + return "http://localhost:" + port + uri; + } + + private String createHttpAuthenticationHeaderValue(String userId, + String password) { + + String auth = userId + ":" + password; + + byte[] encodedAuth = Base64.encode(auth.getBytes(Charset + .forName("US-ASCII"))); + + String headerValue = "Basic " + new String(encodedAuth); + + return headerValue; + } + +} +``` +### src/test/java/com/in28minutes/springboot/controller/SurveyControllerTest.java +``` +package com.in28minutes.springboot.controller; + +import static org.junit.Assert.assertEquals; + +import java.util.Arrays; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mockito; +import org.skyscreamer.jsonassert.JSONAssert; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.mock.web.MockHttpServletResponse; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.MvcResult; +import org.springframework.test.web.servlet.RequestBuilder; +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; + +import com.in28minutes.springboot.model.Question; +import com.in28minutes.springboot.service.SurveyService; + +@RunWith(SpringRunner.class) +@WebMvcTest(value = SurveyController.class, secure = false) +public class SurveyControllerTest { + + @Autowired + private MockMvc mockMvc; + + // Mock @Autowired + @MockBean + private SurveyService surveyService; + + @Test + public void retrieveDetailsForQuestion() throws Exception { + Question mockQuestion = new Question("Question1", + "Largest Country in the World", "Russia", Arrays.asList( + "India", "Russia", "United States", "China")); + + Mockito.when( + surveyService.retrieveQuestion(Mockito.anyString(), Mockito + .anyString())).thenReturn(mockQuestion); + + RequestBuilder requestBuilder = MockMvcRequestBuilders.get( + "/surveys/Survey1/questions/Question1").accept( + MediaType.APPLICATION_JSON); + + MvcResult result = mockMvc.perform(requestBuilder).andReturn(); + + String expected = "{id:Question1,description:Largest Country in the World,correctAnswer:Russia}"; + + JSONAssert.assertEquals(expected, result.getResponse() + .getContentAsString(), false); + + // Assert + } + + @Test + public void createSurveyQuestion() throws Exception { + Question mockQuestion = new Question("1", "Smallest Number", "1", + Arrays.asList("1", "2", "3", "4")); + + String questionJson = "{\"description\":\"Smallest Number\",\"correctAnswer\":\"1\",\"options\":[\"1\",\"2\",\"3\",\"4\"]}"; + //surveyService.addQuestion to respond back with mockQuestion + Mockito.when( + surveyService.addQuestion(Mockito.anyString(), Mockito + .any(Question.class))).thenReturn(mockQuestion); + + //Send question as body to /surveys/Survey1/questions + RequestBuilder requestBuilder = MockMvcRequestBuilders.post( + "/surveys/Survey1/questions") + .accept(MediaType.APPLICATION_JSON).content(questionJson) + .contentType(MediaType.APPLICATION_JSON); + + MvcResult result = mockMvc.perform(requestBuilder).andReturn(); + + MockHttpServletResponse response = result.getResponse(); + + assertEquals(HttpStatus.CREATED.value(), response.getStatus()); + + assertEquals("http://localhost/surveys/Survey1/questions/1", response + .getHeader(HttpHeaders.LOCATION)); + } +} +``` diff --git a/05.Spring-Boot-Advanced/Step27.zip b/05.Spring-Boot-Advanced/Step27.zip new file mode 100644 index 0000000000000000000000000000000000000000..05cb15e3f9508298eb7211b5baadc02417c8c95d GIT binary patch literal 15983 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 LOch)%fq?jbZL&a- literal 0 HcmV?d00001 diff --git a/05.Spring-Boot-Advanced/Step28.md b/05.Spring-Boot-Advanced/Step28.md new file mode 100644 index 0000000..cbaf406 --- /dev/null +++ b/05.Spring-Boot-Advanced/Step28.md @@ -0,0 +1,899 @@ +## What You Will Learn during this Step: +- A Deep Dive into Autoconfiguration + - spring-boot-autoconfigure-1.4.0.RELEASE.jar + - /META-INF/spring.factories + - Package org.springframework.boot.autoconfigure +- Lets look at the log in debug mode! +- Examples + - JdbcTemplateAutoConfiguration + - HttpMessageConvertersAutoConfiguration +- Programming Tips + - Understand Design Patterns + - https://www.youtube.com/watch?v=Vp7q_pE7Fzg + - Understand Modern Development Practices + - https://www.youtube.com/watch?v=0Kqzfyp-w4s + +## Useful Snippets and References +``` + String[] beanNames = ctx.getBeanDefinitionNames(); + Arrays.sort(beanNames); + + for (String beanName : beanNames) { + System.out.println(beanName); + } +``` +## Files List +### pom.xml +``` + + 4.0.0 + com.in28minutes.springboot + first-springboot-project + 0.0.1-SNAPSHOT + + org.springframework.boot + spring-boot-starter-parent + 1.4.0.RELEASE + + + + 1.8 + + + + + org.springframework.boot + spring-boot-starter-web + + + + org.springframework.boot + spring-boot-starter-security + + + + org.springframework.boot + spring-boot-starter-data-jpa + + + + org.springframework.boot + spring-boot-starter-data-rest + + + + com.h2database + h2 + + + + org.springframework.boot + spring-boot-devtools + true + + + + org.springframework.boot + spring-boot-starter-actuator + + + + org.springframework.data + spring-data-rest-hal-browser + + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + +``` +### src/main/java/com/in28minutes/springboot/Application.java +``` +package com.in28minutes.springboot; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.ApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Profile; + +@SpringBootApplication +public class Application { + + public static void main(String[] args) { + ApplicationContext ctx = SpringApplication.run(Application.class, args); + + } + + @Profile("prod") + @Bean + public String dummy() { + return "something"; + } +} +``` +### src/main/java/com/in28minutes/springboot/configuration/BasicConfiguration.java +``` +package com.in28minutes.springboot.configuration; + +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +@Component +@ConfigurationProperties("basic") +public class BasicConfiguration { + private boolean value; + private String message; + private int number; + + public boolean isValue() { + return value; + } + + public void setValue(boolean value) { + this.value = value; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public int getNumber() { + return number; + } + + public void setNumber(int number) { + this.number = number; + } +} +``` +### 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); + } + + // GET "/surveys/{surveyId}/questions/{questionId}" + @GetMapping("/surveys/{surveyId}/questions/{questionId}") + public Question retrieveDetailsForQuestion(@PathVariable String surveyId, + @PathVariable String questionId) { + return surveyService.retrieveQuestion(surveyId, questionId); + } + + // /surveys/{surveyId}/questions + @PostMapping("/surveys/{surveyId}/questions") + public ResponseEntity addQuestionToSurvey( + @PathVariable String surveyId, @RequestBody Question newQuestion) { + + Question question = surveyService.addQuestion(surveyId, newQuestion); + + if (question == null) + return ResponseEntity.noContent().build(); + + // Success - URI of the new resource in Response Header + // Status - created + // URI -> /surveys/{surveyId}/questions/{questionId} + // question.getQuestionId() + URI location = ServletUriComponentsBuilder.fromCurrentRequest().path( + "/{id}").buildAndExpand(question.getId()).toUri(); + + // Status + return ResponseEntity.created(location).build(); + } + +} +``` +### src/main/java/com/in28minutes/springboot/jpa/User.java +``` +package com.in28minutes.springboot.jpa; + +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; + +@Entity +public class User { + + @Id + @GeneratedValue(strategy = GenerationType.AUTO) + private Long id; + + private String name; + private String role; + + protected User() { + } + + public User(String name, String role) { + super(); + this.name = name; + this.role = role; + } + + public Long getId() { + return id; + } + + public String getName() { + return name; + } + + public String getRole() { + return role; + } + + @Override + public String toString() { + return "User [id=" + id + ", name=" + name + ", role=" + role + "]"; + } + +} +``` +### src/main/java/com/in28minutes/springboot/jpa/UserCommandLineRunner.java +``` +package com.in28minutes.springboot.jpa; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.CommandLineRunner; +import org.springframework.stereotype.Component; + +@Component +public class UserCommandLineRunner implements CommandLineRunner { + + private static final Logger log = LoggerFactory + .getLogger(UserCommandLineRunner.class); + + @Autowired + private UserRepository repository; + + @Override + public void run(String... args) throws Exception { + + repository.save(new User("Ranga", "Admin")); + repository.save(new User("Ravi", "User")); + repository.save(new User("Satish", "Admin")); + repository.save(new User("Raghu", "User")); + + for (User user : repository.findAll()) { + log.info(user.toString()); + } + + log.info("Admin users are....."); + log.info("____________________"); + for (User user : repository.findByRole("Admin")) { + log.info(user.toString()); + } + + } + +} +``` +### src/main/java/com/in28minutes/springboot/jpa/UserRepository.java +``` +package com.in28minutes.springboot.jpa; + +import java.util.List; + +import org.springframework.data.repository.CrudRepository; + +public interface UserRepository extends CrudRepository { + List findByRole(String role); +} +``` +### src/main/java/com/in28minutes/springboot/jpa/UserRestRepository.java +``` +package com.in28minutes.springboot.jpa; + +import java.util.List; + +import org.springframework.data.repository.PagingAndSortingRepository; +import org.springframework.data.repository.query.Param; +import org.springframework.data.rest.core.annotation.RepositoryRestResource; + +@RepositoryRestResource(path = "users", collectionResourceRel = "users") +public interface UserRestRepository extends + PagingAndSortingRepository { + List findByRole(@Param("role") String role); +} +``` +### 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 void setId(String id) { + this.id = id; + } + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public List getQuestions() { + return questions; + } + + public void setQuestions(List questions) { + this.questions = questions; + } + + @Override + public String toString() { + return "Survey [id=" + id + ", title=" + title + ", description=" + + description + ", questions=" + questions + "]"; + } + +} +``` +### src/main/java/com/in28minutes/springboot/security/SecurityConfig.java +``` +package com.in28minutes.springboot.security; + +import org.springframework.context.annotation.Configuration; +import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; + +@Configuration +public class SecurityConfig extends WebSecurityConfigurerAdapter { + // Authentication : User --> Roles + protected void configure(AuthenticationManagerBuilder auth) + throws Exception { + auth.inMemoryAuthentication().withUser("user1").password("secret1") + .roles("USER").and().withUser("admin1").password("secret1") + .roles("USER", "ADMIN"); + } + + // Authorization : Role -> Access + // survey -> USER + protected void configure(HttpSecurity http) throws Exception { + http.httpBasic().and().authorizeRequests().antMatchers("/surveys/**") + .hasRole("USER").antMatchers("/users/**").hasRole("USER") + .antMatchers("/**").hasRole("ADMIN").and().csrf().disable() + .headers().frameOptions().disable(); + } + +} +``` +### 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/java/com/in28minutes/springboot/WelcomeController.java +``` +package com.in28minutes.springboot; + +import java.util.HashMap; +import java.util.Map; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import com.in28minutes.springboot.configuration.BasicConfiguration; + +@RestController +public class WelcomeController { + + //Auto wiring + @Autowired + private WelcomeService service; + + @Autowired + private BasicConfiguration configuration; + + @RequestMapping("/welcome") + public String welcome() { + return service.retrieveWelcomeMessage(); + } + + @RequestMapping("/dynamic-configuration") + public Map dynamicConfiguration() { + Map map = new HashMap(); + map.put("message", configuration.getMessage()); + map.put("number", configuration.getNumber()); + map.put("value", configuration.isValue()); + + return map; + } + +} +``` +### src/main/java/com/in28minutes/springboot/WelcomeService.java +``` +package com.in28minutes.springboot; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +//Spring to manage this bean and create an instance of this +@Component +public class WelcomeService { + + @Value("${welcome.message}") + private String welcomeMessage; + + public String retrieveWelcomeMessage() { + //Complex Method + return welcomeMessage; + } +} +``` +### src/main/resources/application-dev.properties +``` +logging.level.org.springframework: TRACE +``` +### src/main/resources/application-prod.properties +``` +logging.level.org.springframework: INFO +``` +### src/main/resources/application.properties +``` +logging.level.org.springframework: DEBUG +app.name=in28Minutes +welcome.message=Welcome message from property file! Welcome to ${app.name} + +basic.value=true +basic.message=Welcome to in28minutes +basic.number=200 +``` +### src/test/java/com/in28minutes/springboot/controller/SurveyControllerIT.java +``` +package com.in28minutes.springboot.controller; + +import static org.junit.Assert.assertTrue; + +import java.nio.charset.Charset; +import java.util.Arrays; +import java.util.List; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.skyscreamer.jsonassert.JSONAssert; +import org.springframework.boot.context.embedded.LocalServerPort; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.web.client.TestRestTemplate; +import org.springframework.core.ParameterizedTypeReference; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.security.crypto.codec.Base64; +import org.springframework.test.context.junit4.SpringRunner; + +import com.in28minutes.springboot.Application; +import com.in28minutes.springboot.model.Question; + +@RunWith(SpringRunner.class) +@SpringBootTest(classes = Application.class, + webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) +public class SurveyControllerIT { + + @LocalServerPort + private int port; + + TestRestTemplate restTemplate = new TestRestTemplate(); + + HttpHeaders headers = new HttpHeaders(); + + @Before + public void before() { + headers.add("Authorization", createHttpAuthenticationHeaderValue( + "user1", "secret1")); + headers.setAccept(Arrays.asList(MediaType.APPLICATION_JSON)); + } + + @Test + public void testRetrieveSurveyQuestion() { + + HttpEntity entity = new HttpEntity(null, headers); + + ResponseEntity response = restTemplate.exchange( + createURLWithPort("/surveys/Survey1/questions/Question1"), + HttpMethod.GET, entity, String.class); + + String expected = "{id:Question1,description:Largest Country in the World,correctAnswer:Russia}"; + + JSONAssert.assertEquals(expected, response.getBody(), false); + } + + @Test + public void retrieveAllSurveyQuestions() throws Exception { + + ResponseEntity> response = restTemplate.exchange( + createURLWithPort("/surveys/Survey1/questions"), + HttpMethod.GET, new HttpEntity("DUMMY_DOESNT_MATTER", + headers), + new ParameterizedTypeReference>() { + }); + + Question sampleQuestion = new Question("Question1", + "Largest Country in the World", "Russia", Arrays.asList( + "India", "Russia", "United States", "China")); + + assertTrue(response.getBody().contains(sampleQuestion)); + } + + @Test + public void addQuestion() { + + Question question = new Question("DOESNTMATTER", "Question1", "Russia", + Arrays.asList("India", "Russia", "United States", "China")); + + HttpEntity entity = new HttpEntity(question, headers); + + ResponseEntity response = restTemplate.exchange( + createURLWithPort("/surveys/Survey1/questions"), + HttpMethod.POST, entity, String.class); + + String actual = response.getHeaders().get(HttpHeaders.LOCATION).get(0); + + assertTrue(actual.contains("/surveys/Survey1/questions/")); + + } + + private String createURLWithPort(final String uri) { + return "http://localhost:" + port + uri; + } + + private String createHttpAuthenticationHeaderValue(String userId, + String password) { + + String auth = userId + ":" + password; + + byte[] encodedAuth = Base64.encode(auth.getBytes(Charset + .forName("US-ASCII"))); + + String headerValue = "Basic " + new String(encodedAuth); + + return headerValue; + } + +} +``` +### src/test/java/com/in28minutes/springboot/controller/SurveyControllerTest.java +``` +package com.in28minutes.springboot.controller; + +import static org.junit.Assert.assertEquals; + +import java.util.Arrays; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mockito; +import org.skyscreamer.jsonassert.JSONAssert; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.mock.web.MockHttpServletResponse; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.MvcResult; +import org.springframework.test.web.servlet.RequestBuilder; +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; + +import com.in28minutes.springboot.model.Question; +import com.in28minutes.springboot.service.SurveyService; + +@RunWith(SpringRunner.class) +@WebMvcTest(value = SurveyController.class, secure = false) +public class SurveyControllerTest { + + @Autowired + private MockMvc mockMvc; + + // Mock @Autowired + @MockBean + private SurveyService surveyService; + + @Test + public void retrieveDetailsForQuestion() throws Exception { + Question mockQuestion = new Question("Question1", + "Largest Country in the World", "Russia", Arrays.asList( + "India", "Russia", "United States", "China")); + + Mockito.when( + surveyService.retrieveQuestion(Mockito.anyString(), Mockito + .anyString())).thenReturn(mockQuestion); + + RequestBuilder requestBuilder = MockMvcRequestBuilders.get( + "/surveys/Survey1/questions/Question1").accept( + MediaType.APPLICATION_JSON); + + MvcResult result = mockMvc.perform(requestBuilder).andReturn(); + + String expected = "{id:Question1,description:Largest Country in the World,correctAnswer:Russia}"; + + JSONAssert.assertEquals(expected, result.getResponse() + .getContentAsString(), false); + + // Assert + } + + @Test + public void createSurveyQuestion() throws Exception { + Question mockQuestion = new Question("1", "Smallest Number", "1", + Arrays.asList("1", "2", "3", "4")); + + String questionJson = "{\"description\":\"Smallest Number\",\"correctAnswer\":\"1\",\"options\":[\"1\",\"2\",\"3\",\"4\"]}"; + //surveyService.addQuestion to respond back with mockQuestion + Mockito.when( + surveyService.addQuestion(Mockito.anyString(), Mockito + .any(Question.class))).thenReturn(mockQuestion); + + //Send question as body to /surveys/Survey1/questions + RequestBuilder requestBuilder = MockMvcRequestBuilders.post( + "/surveys/Survey1/questions") + .accept(MediaType.APPLICATION_JSON).content(questionJson) + .contentType(MediaType.APPLICATION_JSON); + + MvcResult result = mockMvc.perform(requestBuilder).andReturn(); + + MockHttpServletResponse response = result.getResponse(); + + assertEquals(HttpStatus.CREATED.value(), response.getStatus()); + + assertEquals("http://localhost/surveys/Survey1/questions/1", response + .getHeader(HttpHeaders.LOCATION)); + } +} +``` diff --git a/05.Spring-Boot-Advanced/Step28.zip b/05.Spring-Boot-Advanced/Step28.zip new file mode 100644 index 0000000000000000000000000000000000000000..05cb15e3f9508298eb7211b5baadc02417c8c95d GIT binary patch literal 15983 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 LOch)%fq?jbZL&a- literal 0 HcmV?d00001 diff --git a/05.Spring-Boot-Advanced/StepReference.md b/05.Spring-Boot-Advanced/StepReference.md new file mode 100644 index 0000000..f409414 --- /dev/null +++ b/05.Spring-Boot-Advanced/StepReference.md @@ -0,0 +1,16 @@ +## What You Will Learn during this Step: +- First +- Second +- Third + +## Useful Snippets and References +First Snippet +``` +``` +Second Snippet +``` +``` + +## Exercises + +## Files List diff --git a/05.Spring-Boot-Advanced/pom.xml b/05.Spring-Boot-Advanced/pom.xml new file mode 100644 index 0000000..06c481e --- /dev/null +++ b/05.Spring-Boot-Advanced/pom.xml @@ -0,0 +1,74 @@ + + 4.0.0 + com.in28minutes.springboot + first-springboot-project + 0.0.1-SNAPSHOT + + org.springframework.boot + spring-boot-starter-parent + 1.4.0.RELEASE + + + + 1.8 + + + + + org.springframework.boot + spring-boot-starter-web + + + + org.springframework.boot + spring-boot-starter-security + + + + org.springframework.boot + spring-boot-starter-data-jpa + + + + org.springframework.boot + spring-boot-starter-data-rest + + + + com.h2database + h2 + + + + org.springframework.boot + spring-boot-devtools + true + + + + org.springframework.boot + spring-boot-starter-actuator + + + + org.springframework.data + spring-data-rest-hal-browser + + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + \ No newline at end of file diff --git a/05.Spring-Boot-Advanced/src/main/java/com/in28minutes/springboot/Application.java b/05.Spring-Boot-Advanced/src/main/java/com/in28minutes/springboot/Application.java new file mode 100644 index 0000000..b972283 --- /dev/null +++ b/05.Spring-Boot-Advanced/src/main/java/com/in28minutes/springboot/Application.java @@ -0,0 +1,22 @@ +package com.in28minutes.springboot; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.ApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Profile; + +@SpringBootApplication +public class Application { + + public static void main(String[] args) { + ApplicationContext ctx = SpringApplication.run(Application.class, args); + + } + + @Profile("prod") + @Bean + public String dummy() { + return "something"; + } +} \ No newline at end of file diff --git a/05.Spring-Boot-Advanced/src/main/java/com/in28minutes/springboot/WelcomeController.java b/05.Spring-Boot-Advanced/src/main/java/com/in28minutes/springboot/WelcomeController.java new file mode 100644 index 0000000..fcb9f38 --- /dev/null +++ b/05.Spring-Boot-Advanced/src/main/java/com/in28minutes/springboot/WelcomeController.java @@ -0,0 +1,37 @@ +package com.in28minutes.springboot; + +import java.util.HashMap; +import java.util.Map; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import com.in28minutes.springboot.configuration.BasicConfiguration; + +@RestController +public class WelcomeController { + + //Auto wiring + @Autowired + private WelcomeService service; + + @Autowired + private BasicConfiguration configuration; + + @RequestMapping("/welcome") + public String welcome() { + return service.retrieveWelcomeMessage(); + } + + @RequestMapping("/dynamic-configuration") + public Map dynamicConfiguration() { + Map map = new HashMap(); + map.put("message", configuration.getMessage()); + map.put("number", configuration.getNumber()); + map.put("value", configuration.isValue()); + + return map; + } + +} diff --git a/05.Spring-Boot-Advanced/src/main/java/com/in28minutes/springboot/WelcomeService.java b/05.Spring-Boot-Advanced/src/main/java/com/in28minutes/springboot/WelcomeService.java new file mode 100644 index 0000000..8f4353b --- /dev/null +++ b/05.Spring-Boot-Advanced/src/main/java/com/in28minutes/springboot/WelcomeService.java @@ -0,0 +1,17 @@ +package com.in28minutes.springboot; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +//Spring to manage this bean and create an instance of this +@Component +public class WelcomeService { + + @Value("${welcome.message}") + private String welcomeMessage; + + public String retrieveWelcomeMessage() { + //Complex Method + return welcomeMessage; + } +} \ No newline at end of file diff --git a/05.Spring-Boot-Advanced/src/main/java/com/in28minutes/springboot/configuration/BasicConfiguration.java b/05.Spring-Boot-Advanced/src/main/java/com/in28minutes/springboot/configuration/BasicConfiguration.java new file mode 100644 index 0000000..af16020 --- /dev/null +++ b/05.Spring-Boot-Advanced/src/main/java/com/in28minutes/springboot/configuration/BasicConfiguration.java @@ -0,0 +1,36 @@ +package com.in28minutes.springboot.configuration; + +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +@Component +@ConfigurationProperties("basic") +public class BasicConfiguration { + private boolean value; + private String message; + private int number; + + public boolean isValue() { + return value; + } + + public void setValue(boolean value) { + this.value = value; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public int getNumber() { + return number; + } + + public void setNumber(int number) { + this.number = number; + } +} diff --git a/05.Spring-Boot-Advanced/src/main/java/com/in28minutes/springboot/controller/SurveyController.java b/05.Spring-Boot-Advanced/src/main/java/com/in28minutes/springboot/controller/SurveyController.java new file mode 100644 index 0000000..dcc5be3 --- /dev/null +++ b/05.Spring-Boot-Advanced/src/main/java/com/in28minutes/springboot/controller/SurveyController.java @@ -0,0 +1,56 @@ +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); + } + + // GET "/surveys/{surveyId}/questions/{questionId}" + @GetMapping("/surveys/{surveyId}/questions/{questionId}") + public Question retrieveDetailsForQuestion(@PathVariable String surveyId, + @PathVariable String questionId) { + return surveyService.retrieveQuestion(surveyId, questionId); + } + + // /surveys/{surveyId}/questions + @PostMapping("/surveys/{surveyId}/questions") + public ResponseEntity addQuestionToSurvey( + @PathVariable String surveyId, @RequestBody Question newQuestion) { + + Question question = surveyService.addQuestion(surveyId, newQuestion); + + if (question == null) + return ResponseEntity.noContent().build(); + + // Success - URI of the new resource in Response Header + // Status - created + // URI -> /surveys/{surveyId}/questions/{questionId} + // question.getQuestionId() + URI location = ServletUriComponentsBuilder.fromCurrentRequest().path( + "/{id}").buildAndExpand(question.getId()).toUri(); + + // Status + return ResponseEntity.created(location).build(); + } + +} diff --git a/05.Spring-Boot-Advanced/src/main/java/com/in28minutes/springboot/jpa/User.java b/05.Spring-Boot-Advanced/src/main/java/com/in28minutes/springboot/jpa/User.java new file mode 100644 index 0000000..ae622c9 --- /dev/null +++ b/05.Spring-Boot-Advanced/src/main/java/com/in28minutes/springboot/jpa/User.java @@ -0,0 +1,44 @@ +package com.in28minutes.springboot.jpa; + +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; + +@Entity +public class User { + + @Id + @GeneratedValue(strategy = GenerationType.AUTO) + private Long id; + + private String name; + private String role; + + protected User() { + } + + public User(String name, String role) { + super(); + this.name = name; + this.role = role; + } + + public Long getId() { + return id; + } + + public String getName() { + return name; + } + + public String getRole() { + return role; + } + + @Override + public String toString() { + return "User [id=" + id + ", name=" + name + ", role=" + role + "]"; + } + +} diff --git a/05.Spring-Boot-Advanced/src/main/java/com/in28minutes/springboot/jpa/UserCommandLineRunner.java b/05.Spring-Boot-Advanced/src/main/java/com/in28minutes/springboot/jpa/UserCommandLineRunner.java new file mode 100644 index 0000000..af728d1 --- /dev/null +++ b/05.Spring-Boot-Advanced/src/main/java/com/in28minutes/springboot/jpa/UserCommandLineRunner.java @@ -0,0 +1,38 @@ +package com.in28minutes.springboot.jpa; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.CommandLineRunner; +import org.springframework.stereotype.Component; + +@Component +public class UserCommandLineRunner implements CommandLineRunner { + + private static final Logger log = LoggerFactory + .getLogger(UserCommandLineRunner.class); + + @Autowired + private UserRepository repository; + + @Override + public void run(String... args) throws Exception { + + repository.save(new User("Ranga", "Admin")); + repository.save(new User("Ravi", "User")); + repository.save(new User("Satish", "Admin")); + repository.save(new User("Raghu", "User")); + + for (User user : repository.findAll()) { + log.info(user.toString()); + } + + log.info("Admin users are....."); + log.info("____________________"); + for (User user : repository.findByRole("Admin")) { + log.info(user.toString()); + } + + } + +} diff --git a/05.Spring-Boot-Advanced/src/main/java/com/in28minutes/springboot/jpa/UserRepository.java b/05.Spring-Boot-Advanced/src/main/java/com/in28minutes/springboot/jpa/UserRepository.java new file mode 100644 index 0000000..d5daf67 --- /dev/null +++ b/05.Spring-Boot-Advanced/src/main/java/com/in28minutes/springboot/jpa/UserRepository.java @@ -0,0 +1,9 @@ +package com.in28minutes.springboot.jpa; + +import java.util.List; + +import org.springframework.data.repository.CrudRepository; + +public interface UserRepository extends CrudRepository { + List findByRole(String role); +} diff --git a/05.Spring-Boot-Advanced/src/main/java/com/in28minutes/springboot/jpa/UserRestRepository.java b/05.Spring-Boot-Advanced/src/main/java/com/in28minutes/springboot/jpa/UserRestRepository.java new file mode 100644 index 0000000..bb67556 --- /dev/null +++ b/05.Spring-Boot-Advanced/src/main/java/com/in28minutes/springboot/jpa/UserRestRepository.java @@ -0,0 +1,13 @@ +package com.in28minutes.springboot.jpa; + +import java.util.List; + +import org.springframework.data.repository.PagingAndSortingRepository; +import org.springframework.data.repository.query.Param; +import org.springframework.data.rest.core.annotation.RepositoryRestResource; + +@RepositoryRestResource(path = "users", collectionResourceRel = "users") +public interface UserRestRepository extends + PagingAndSortingRepository { + List findByRole(@Param("role") String role); +} diff --git a/05.Spring-Boot-Advanced/src/main/java/com/in28minutes/springboot/model/Question.java b/05.Spring-Boot-Advanced/src/main/java/com/in28minutes/springboot/model/Question.java new file mode 100644 index 0000000..9c83090 --- /dev/null +++ b/05.Spring-Boot-Advanced/src/main/java/com/in28minutes/springboot/model/Question.java @@ -0,0 +1,81 @@ +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; + } + +} \ No newline at end of file diff --git a/05.Spring-Boot-Advanced/src/main/java/com/in28minutes/springboot/model/Survey.java b/05.Spring-Boot-Advanced/src/main/java/com/in28minutes/springboot/model/Survey.java new file mode 100644 index 0000000..6a650aa --- /dev/null +++ b/05.Spring-Boot-Advanced/src/main/java/com/in28minutes/springboot/model/Survey.java @@ -0,0 +1,58 @@ +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 void setId(String id) { + this.id = id; + } + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public List getQuestions() { + return questions; + } + + public void setQuestions(List questions) { + this.questions = questions; + } + + @Override + public String toString() { + return "Survey [id=" + id + ", title=" + title + ", description=" + + description + ", questions=" + questions + "]"; + } + +} \ No newline at end of file diff --git a/05.Spring-Boot-Advanced/src/main/java/com/in28minutes/springboot/security/SecurityConfig.java b/05.Spring-Boot-Advanced/src/main/java/com/in28minutes/springboot/security/SecurityConfig.java new file mode 100644 index 0000000..7547748 --- /dev/null +++ b/05.Spring-Boot-Advanced/src/main/java/com/in28minutes/springboot/security/SecurityConfig.java @@ -0,0 +1,27 @@ +package com.in28minutes.springboot.security; + +import org.springframework.context.annotation.Configuration; +import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; + +@Configuration +public class SecurityConfig extends WebSecurityConfigurerAdapter { + // Authentication : User --> Roles + protected void configure(AuthenticationManagerBuilder auth) + throws Exception { + auth.inMemoryAuthentication().withUser("user1").password("secret1") + .roles("USER").and().withUser("admin1").password("secret1") + .roles("USER", "ADMIN"); + } + + // Authorization : Role -> Access + // survey -> USER + protected void configure(HttpSecurity http) throws Exception { + http.httpBasic().and().authorizeRequests().antMatchers("/surveys/**") + .hasRole("USER").antMatchers("/users/**").hasRole("USER") + .antMatchers("/**").hasRole("ADMIN").and().csrf().disable() + .headers().frameOptions().disable(); + } + +} diff --git a/05.Spring-Boot-Advanced/src/main/java/com/in28minutes/springboot/service/SurveyService.java b/05.Spring-Boot-Advanced/src/main/java/com/in28minutes/springboot/service/SurveyService.java new file mode 100644 index 0000000..b3f3ccb --- /dev/null +++ b/05.Spring-Boot-Advanced/src/main/java/com/in28minutes/springboot/service/SurveyService.java @@ -0,0 +1,95 @@ +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; + } +} \ No newline at end of file diff --git a/05.Spring-Boot-Advanced/src/main/resources/application-dev.properties b/05.Spring-Boot-Advanced/src/main/resources/application-dev.properties new file mode 100644 index 0000000..9fe78d9 --- /dev/null +++ b/05.Spring-Boot-Advanced/src/main/resources/application-dev.properties @@ -0,0 +1 @@ +logging.level.org.springframework: TRACE diff --git a/05.Spring-Boot-Advanced/src/main/resources/application-prod.properties b/05.Spring-Boot-Advanced/src/main/resources/application-prod.properties new file mode 100644 index 0000000..d71ef41 --- /dev/null +++ b/05.Spring-Boot-Advanced/src/main/resources/application-prod.properties @@ -0,0 +1 @@ +logging.level.org.springframework: INFO diff --git a/05.Spring-Boot-Advanced/src/main/resources/application.properties b/05.Spring-Boot-Advanced/src/main/resources/application.properties new file mode 100644 index 0000000..68b4ac4 --- /dev/null +++ b/05.Spring-Boot-Advanced/src/main/resources/application.properties @@ -0,0 +1,7 @@ +logging.level.org.springframework: DEBUG +app.name=in28Minutes +welcome.message=Welcome message from property file! Welcome to ${app.name} + +basic.value=true +basic.message=Welcome to in28minutes +basic.number=200 diff --git a/05.Spring-Boot-Advanced/src/test/java/com/in28minutes/springboot/controller/SurveyControllerIT.java b/05.Spring-Boot-Advanced/src/test/java/com/in28minutes/springboot/controller/SurveyControllerIT.java new file mode 100644 index 0000000..85557ff --- /dev/null +++ b/05.Spring-Boot-Advanced/src/test/java/com/in28minutes/springboot/controller/SurveyControllerIT.java @@ -0,0 +1,113 @@ +package com.in28minutes.springboot.controller; + +import static org.junit.Assert.assertTrue; + +import java.nio.charset.Charset; +import java.util.Arrays; +import java.util.List; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.skyscreamer.jsonassert.JSONAssert; +import org.springframework.boot.context.embedded.LocalServerPort; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.web.client.TestRestTemplate; +import org.springframework.core.ParameterizedTypeReference; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.security.crypto.codec.Base64; +import org.springframework.test.context.junit4.SpringRunner; + +import com.in28minutes.springboot.Application; +import com.in28minutes.springboot.model.Question; + +@RunWith(SpringRunner.class) +@SpringBootTest(classes = Application.class, + webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) +public class SurveyControllerIT { + + @LocalServerPort + private int port; + + TestRestTemplate restTemplate = new TestRestTemplate(); + + HttpHeaders headers = new HttpHeaders(); + + @Before + public void before() { + headers.add("Authorization", createHttpAuthenticationHeaderValue( + "user1", "secret1")); + headers.setAccept(Arrays.asList(MediaType.APPLICATION_JSON)); + } + + @Test + public void testRetrieveSurveyQuestion() { + + HttpEntity entity = new HttpEntity(null, headers); + + ResponseEntity response = restTemplate.exchange( + createURLWithPort("/surveys/Survey1/questions/Question1"), + HttpMethod.GET, entity, String.class); + + String expected = "{id:Question1,description:Largest Country in the World,correctAnswer:Russia}"; + + JSONAssert.assertEquals(expected, response.getBody(), false); + } + + @Test + public void retrieveAllSurveyQuestions() throws Exception { + + ResponseEntity> response = restTemplate.exchange( + createURLWithPort("/surveys/Survey1/questions"), + HttpMethod.GET, new HttpEntity("DUMMY_DOESNT_MATTER", + headers), + new ParameterizedTypeReference>() { + }); + + Question sampleQuestion = new Question("Question1", + "Largest Country in the World", "Russia", Arrays.asList( + "India", "Russia", "United States", "China")); + + assertTrue(response.getBody().contains(sampleQuestion)); + } + + @Test + public void addQuestion() { + + Question question = new Question("DOESNTMATTER", "Question1", "Russia", + Arrays.asList("India", "Russia", "United States", "China")); + + HttpEntity entity = new HttpEntity(question, headers); + + ResponseEntity response = restTemplate.exchange( + createURLWithPort("/surveys/Survey1/questions"), + HttpMethod.POST, entity, String.class); + + String actual = response.getHeaders().get(HttpHeaders.LOCATION).get(0); + + assertTrue(actual.contains("/surveys/Survey1/questions/")); + + } + + private String createURLWithPort(final String uri) { + return "http://localhost:" + port + uri; + } + + private String createHttpAuthenticationHeaderValue(String userId, + String password) { + + String auth = userId + ":" + password; + + byte[] encodedAuth = Base64.encode(auth.getBytes(Charset + .forName("US-ASCII"))); + + String headerValue = "Basic " + new String(encodedAuth); + + return headerValue; + } + +} diff --git a/05.Spring-Boot-Advanced/src/test/java/com/in28minutes/springboot/controller/SurveyControllerTest.java b/05.Spring-Boot-Advanced/src/test/java/com/in28minutes/springboot/controller/SurveyControllerTest.java new file mode 100644 index 0000000..c98f5d0 --- /dev/null +++ b/05.Spring-Boot-Advanced/src/test/java/com/in28minutes/springboot/controller/SurveyControllerTest.java @@ -0,0 +1,88 @@ +package com.in28minutes.springboot.controller; + +import static org.junit.Assert.assertEquals; + +import java.util.Arrays; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mockito; +import org.skyscreamer.jsonassert.JSONAssert; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.mock.web.MockHttpServletResponse; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.MvcResult; +import org.springframework.test.web.servlet.RequestBuilder; +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; + +import com.in28minutes.springboot.model.Question; +import com.in28minutes.springboot.service.SurveyService; + +@RunWith(SpringRunner.class) +@WebMvcTest(value = SurveyController.class, secure = false) +public class SurveyControllerTest { + + @Autowired + private MockMvc mockMvc; + + // Mock @Autowired + @MockBean + private SurveyService surveyService; + + @Test + public void retrieveDetailsForQuestion() throws Exception { + Question mockQuestion = new Question("Question1", + "Largest Country in the World", "Russia", Arrays.asList( + "India", "Russia", "United States", "China")); + + Mockito.when( + surveyService.retrieveQuestion(Mockito.anyString(), Mockito + .anyString())).thenReturn(mockQuestion); + + RequestBuilder requestBuilder = MockMvcRequestBuilders.get( + "/surveys/Survey1/questions/Question1").accept( + MediaType.APPLICATION_JSON); + + MvcResult result = mockMvc.perform(requestBuilder).andReturn(); + + String expected = "{id:Question1,description:Largest Country in the World,correctAnswer:Russia}"; + + JSONAssert.assertEquals(expected, result.getResponse() + .getContentAsString(), false); + + // Assert + } + + @Test + public void createSurveyQuestion() throws Exception { + Question mockQuestion = new Question("1", "Smallest Number", "1", + Arrays.asList("1", "2", "3", "4")); + + String questionJson = "{\"description\":\"Smallest Number\",\"correctAnswer\":\"1\",\"options\":[\"1\",\"2\",\"3\",\"4\"]}"; + //surveyService.addQuestion to respond back with mockQuestion + Mockito.when( + surveyService.addQuestion(Mockito.anyString(), Mockito + .any(Question.class))).thenReturn(mockQuestion); + + //Send question as body to /surveys/Survey1/questions + RequestBuilder requestBuilder = MockMvcRequestBuilders.post( + "/surveys/Survey1/questions") + .accept(MediaType.APPLICATION_JSON).content(questionJson) + .contentType(MediaType.APPLICATION_JSON); + + MvcResult result = mockMvc.perform(requestBuilder).andReturn(); + + MockHttpServletResponse response = result.getResponse(); + + assertEquals(HttpStatus.CREATED.value(), response.getStatus()); + + assertEquals("http://localhost/surveys/Survey1/questions/1", response + .getHeader(HttpHeaders.LOCATION)); + } +} diff --git a/05.Spring-Boot-Advanced/target/classes/META-INF/MANIFEST.MF b/05.Spring-Boot-Advanced/target/classes/META-INF/MANIFEST.MF new file mode 100644 index 0000000..980cfa4 --- /dev/null +++ b/05.Spring-Boot-Advanced/target/classes/META-INF/MANIFEST.MF @@ -0,0 +1,11 @@ +Manifest-Version: 1.0 +Implementation-Title: first-springboot-project +Implementation-Version: 0.0.1-SNAPSHOT +Built-By: rangaraokaranam +Implementation-Vendor-Id: com.in28minutes.springboot +Build-Jdk: 1.8.0_31 +Implementation-URL: http://projects.spring.io/spring-boot/first-spring + boot-project/ +Created-By: Maven Integration for Eclipse +Implementation-Vendor: Pivotal Software, Inc. + diff --git a/05.Spring-Boot-Advanced/target/classes/META-INF/maven/com.in28minutes.springboot/first-springboot-project/pom.properties b/05.Spring-Boot-Advanced/target/classes/META-INF/maven/com.in28minutes.springboot/first-springboot-project/pom.properties new file mode 100644 index 0000000..907440b --- /dev/null +++ b/05.Spring-Boot-Advanced/target/classes/META-INF/maven/com.in28minutes.springboot/first-springboot-project/pom.properties @@ -0,0 +1,7 @@ +#Generated by Maven Integration for Eclipse +#Sun Jul 30 12:40:01 IST 2017 +version=0.0.1-SNAPSHOT +groupId=com.in28minutes.springboot +m2e.projectName=first-springboot-project +m2e.projectLocation=/in28Minutes/git/SpringBootForBeginners +artifactId=first-springboot-project diff --git a/05.Spring-Boot-Advanced/target/classes/META-INF/maven/com.in28minutes.springboot/first-springboot-project/pom.xml b/05.Spring-Boot-Advanced/target/classes/META-INF/maven/com.in28minutes.springboot/first-springboot-project/pom.xml new file mode 100644 index 0000000..06c481e --- /dev/null +++ b/05.Spring-Boot-Advanced/target/classes/META-INF/maven/com.in28minutes.springboot/first-springboot-project/pom.xml @@ -0,0 +1,74 @@ + + 4.0.0 + com.in28minutes.springboot + first-springboot-project + 0.0.1-SNAPSHOT + + org.springframework.boot + spring-boot-starter-parent + 1.4.0.RELEASE + + + + 1.8 + + + + + org.springframework.boot + spring-boot-starter-web + + + + org.springframework.boot + spring-boot-starter-security + + + + org.springframework.boot + spring-boot-starter-data-jpa + + + + org.springframework.boot + spring-boot-starter-data-rest + + + + com.h2database + h2 + + + + org.springframework.boot + spring-boot-devtools + true + + + + org.springframework.boot + spring-boot-starter-actuator + + + + org.springframework.data + spring-data-rest-hal-browser + + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + \ No newline at end of file diff --git a/05.Spring-Boot-Advanced/target/classes/application-dev.properties b/05.Spring-Boot-Advanced/target/classes/application-dev.properties new file mode 100644 index 0000000..9fe78d9 --- /dev/null +++ b/05.Spring-Boot-Advanced/target/classes/application-dev.properties @@ -0,0 +1 @@ +logging.level.org.springframework: TRACE diff --git a/05.Spring-Boot-Advanced/target/classes/application-prod.properties b/05.Spring-Boot-Advanced/target/classes/application-prod.properties new file mode 100644 index 0000000..d71ef41 --- /dev/null +++ b/05.Spring-Boot-Advanced/target/classes/application-prod.properties @@ -0,0 +1 @@ +logging.level.org.springframework: INFO diff --git a/05.Spring-Boot-Advanced/target/classes/application.properties b/05.Spring-Boot-Advanced/target/classes/application.properties new file mode 100644 index 0000000..68b4ac4 --- /dev/null +++ b/05.Spring-Boot-Advanced/target/classes/application.properties @@ -0,0 +1,7 @@ +logging.level.org.springframework: DEBUG +app.name=in28Minutes +welcome.message=Welcome message from property file! Welcome to ${app.name} + +basic.value=true +basic.message=Welcome to in28minutes +basic.number=200 diff --git a/05.Spring-Boot-Advanced/target/m2e-wtp/web-resources/META-INF/MANIFEST.MF b/05.Spring-Boot-Advanced/target/m2e-wtp/web-resources/META-INF/MANIFEST.MF new file mode 100644 index 0000000..ea381f6 --- /dev/null +++ b/05.Spring-Boot-Advanced/target/m2e-wtp/web-resources/META-INF/MANIFEST.MF @@ -0,0 +1,11 @@ +Manifest-Version: 1.0 +Implementation-Title: Your First Spring Boot Example +Implementation-Version: 0.0.1-SNAPSHOT +Built-By: rangaraokaranam +Implementation-Vendor-Id: com.in28minutes +Build-Jdk: 1.8.0_31 +Implementation-URL: http://projects.spring.io/spring-boot/springboot-f + or-beginners-example/ +Created-By: Maven Integration for Eclipse +Implementation-Vendor: Pivotal Software, Inc. + diff --git a/05.Spring-Boot-Advanced/target/m2e-wtp/web-resources/META-INF/maven/com.in28minutes/springboot-for-beginners-example/pom.properties b/05.Spring-Boot-Advanced/target/m2e-wtp/web-resources/META-INF/maven/com.in28minutes/springboot-for-beginners-example/pom.properties new file mode 100644 index 0000000..0cf6b50 --- /dev/null +++ b/05.Spring-Boot-Advanced/target/m2e-wtp/web-resources/META-INF/maven/com.in28minutes/springboot-for-beginners-example/pom.properties @@ -0,0 +1,7 @@ +#Generated by Maven Integration for Eclipse +#Tue Jan 03 17:15:01 IST 2017 +version=0.0.1-SNAPSHOT +groupId=com.in28minutes +m2e.projectName=springboot-for-beginners-example +m2e.projectLocation=/in28Minutes/git/SpringBootForBeginners +artifactId=springboot-for-beginners-example diff --git a/05.Spring-Boot-Advanced/target/m2e-wtp/web-resources/META-INF/maven/com.in28minutes/springboot-for-beginners-example/pom.xml b/05.Spring-Boot-Advanced/target/m2e-wtp/web-resources/META-INF/maven/com.in28minutes/springboot-for-beginners-example/pom.xml new file mode 100644 index 0000000..e21e191 --- /dev/null +++ b/05.Spring-Boot-Advanced/target/m2e-wtp/web-resources/META-INF/maven/com.in28minutes/springboot-for-beginners-example/pom.xml @@ -0,0 +1,35 @@ + + 4.0.0 + com.in28minutes + springboot-for-beginners-example + 0.0.1-SNAPSHOT + Your First Spring Boot Example + war + + + org.springframework.boot + spring-boot-starter-parent + 1.4.0.RELEASE + + + + + org.springframework.boot + spring-boot-starter-web + + + + + 1.8 + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + \ No newline at end of file diff --git a/05.Spring-Boot-Advanced/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst b/05.Spring-Boot-Advanced/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst new file mode 100644 index 0000000..e69de29 diff --git a/05.Spring-Boot-Advanced/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst b/05.Spring-Boot-Advanced/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst new file mode 100644 index 0000000..76def39 --- /dev/null +++ b/05.Spring-Boot-Advanced/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst @@ -0,0 +1,12 @@ +/in28Minutes/git/SpringBootForBeginners/src/main/java/com/in28minutes/springboot/jpa/User.java +/in28Minutes/git/SpringBootForBeginners/src/main/java/com/in28minutes/springboot/Application.java +/in28Minutes/git/SpringBootForBeginners/src/main/java/com/in28minutes/springboot/jpa/UserRestRepository.java +/in28Minutes/git/SpringBootForBeginners/src/main/java/com/in28minutes/springboot/security/SecurityConfig.java +/in28Minutes/git/SpringBootForBeginners/src/main/java/com/in28minutes/springboot/jpa/UserRepository.java +/in28Minutes/git/SpringBootForBeginners/src/main/java/com/in28minutes/springboot/model/Survey.java +/in28Minutes/git/SpringBootForBeginners/src/main/java/com/in28minutes/springboot/jpa/UserCommandLineRunner.java +/in28Minutes/git/SpringBootForBeginners/src/main/java/com/in28minutes/springboot/controller/SurveyController.java +/in28Minutes/git/SpringBootForBeginners/src/main/java/com/in28minutes/springboot/model/Question.java +/in28Minutes/git/SpringBootForBeginners/src/main/java/com/in28minutes/springboot/service/SurveyService.java +/in28Minutes/git/SpringBootForBeginners/src/main/java/com/in28minutes/springboot/WelcomeController.java +/in28Minutes/git/SpringBootForBeginners/src/main/java/com/in28minutes/springboot/configuration/BasicConfiguration.java diff --git a/05.Spring-Boot-Advanced/target/maven-status/maven-compiler-plugin/testCompile/default-testCompile/createdFiles.lst b/05.Spring-Boot-Advanced/target/maven-status/maven-compiler-plugin/testCompile/default-testCompile/createdFiles.lst new file mode 100644 index 0000000..e69de29 diff --git a/05.Spring-Boot-Advanced/target/maven-status/maven-compiler-plugin/testCompile/default-testCompile/inputFiles.lst b/05.Spring-Boot-Advanced/target/maven-status/maven-compiler-plugin/testCompile/default-testCompile/inputFiles.lst new file mode 100644 index 0000000..b5b8817 --- /dev/null +++ b/05.Spring-Boot-Advanced/target/maven-status/maven-compiler-plugin/testCompile/default-testCompile/inputFiles.lst @@ -0,0 +1,2 @@ +/in28Minutes/git/SpringBootForBeginners/src/test/java/com/in28minutes/springboot/controller/SurveyControllerTest.java +/in28Minutes/git/SpringBootForBeginners/src/test/java/com/in28minutes/springboot/controller/SurveyControllerIT.java diff --git a/05.Spring-Boot-Advanced/target/surefire-reports/TEST-com.in28minutes.springboot.controller.SurveyControllerTest.xml b/05.Spring-Boot-Advanced/target/surefire-reports/TEST-com.in28minutes.springboot.controller.SurveyControllerTest.xml new file mode 100644 index 0000000..2b167c1 --- /dev/null +++ b/05.Spring-Boot-Advanced/target/surefire-reports/TEST-com.in28minutes.springboot.controller.SurveyControllerTest.xml @@ -0,0 +1,69 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/05.Spring-Boot-Advanced/target/surefire-reports/com.in28minutes.springboot.controller.SurveyControllerTest.txt b/05.Spring-Boot-Advanced/target/surefire-reports/com.in28minutes.springboot.controller.SurveyControllerTest.txt new file mode 100644 index 0000000..e9d4bbe --- /dev/null +++ b/05.Spring-Boot-Advanced/target/surefire-reports/com.in28minutes.springboot.controller.SurveyControllerTest.txt @@ -0,0 +1,4 @@ +------------------------------------------------------------------------------- +Test set: com.in28minutes.springboot.controller.SurveyControllerTest +------------------------------------------------------------------------------- +Tests run: 3, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 4.782 sec - in com.in28minutes.springboot.controller.SurveyControllerTest diff --git a/06.JPA-Introduction-In-10-Steps/.DS_Store b/06.JPA-Introduction-In-10-Steps/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..9437bd40f4b74c5930c9aaa3158328373380eae1 GIT binary patch literal 6148 zcmeHKyH3L}6uoW(DzJ1yjQN2AMM6UA5>;UXmM$zvl(s5GO;WW<2c`(ABVWJ=u_Lkb z348+Q+EyAT4I@I}TiM6C59jzuv1=j{-DKD%Y7$WcjnUafx4}5htz)j4 z22n68Wm<<-z$)+?72t2zp+gF2Nul}uzD@J7AoBfT5)X*pSJaV9$?Ae=SQ(q$=l6t9 zF3jqdy?Ho|!ZCcEIOGT6>4@S_TR0r?BAt^fc4 literal 0 HcmV?d00001 diff --git a/06.JPA-Introduction-In-10-Steps/.gitignore b/06.JPA-Introduction-In-10-Steps/.gitignore new file mode 100644 index 0000000..2af7cef --- /dev/null +++ b/06.JPA-Introduction-In-10-Steps/.gitignore @@ -0,0 +1,24 @@ +target/ +!.mvn/wrapper/maven-wrapper.jar + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr + +### NetBeans ### +nbproject/private/ +build/ +nbbuild/ +dist/ +nbdist/ +.nb-gradle/ \ No newline at end of file diff --git a/06.JPA-Introduction-In-10-Steps/.mvn/.DS_Store b/06.JPA-Introduction-In-10-Steps/.mvn/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..cee77800bbeb074655ec66f220c8f19d98f551e4 GIT binary patch literal 6148 zcmeHK%}T>S5T4PB1us2%+*jxugc6@1XHjXSJv2=SR1?|n>tm0mEz5fMvLyO?f4En_gB(p;|KicDg{A!}pOWMt$7|JZH|=(X z8L%4?3?`k!7uMC0KdC!$VHkZd5DW|%xU}I+ z?*BV{GNVQQFeFC7KrnD-4Dh6^$^~A^@7AN≤#dJ)((-Uls)deee^2ft(|^I%)Gs ZbodpH*-)~GUBiL#BcOyt7YzIX1K&7dGQ9u* literal 0 HcmV?d00001 diff --git a/06.JPA-Introduction-In-10-Steps/.mvn/wrapper/maven-wrapper.jar b/06.JPA-Introduction-In-10-Steps/.mvn/wrapper/maven-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..9cc84ea9b4d95453115d0c26488d6a78694e0bc6 GIT binary patch literal 47610 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 literal 0 HcmV?d00001 diff --git a/06.JPA-Introduction-In-10-Steps/.mvn/wrapper/maven-wrapper.properties b/06.JPA-Introduction-In-10-Steps/.mvn/wrapper/maven-wrapper.properties new file mode 100644 index 0000000..c315043 --- /dev/null +++ b/06.JPA-Introduction-In-10-Steps/.mvn/wrapper/maven-wrapper.properties @@ -0,0 +1 @@ +distributionUrl=https://repo1.maven.org/maven2/org/apache/maven/apache-maven/3.5.0/apache-maven-3.5.0-bin.zip diff --git a/06.JPA-Introduction-In-10-Steps/code-21July2017.zip b/06.JPA-Introduction-In-10-Steps/code-21July2017.zip new file mode 100644 index 0000000000000000000000000000000000000000..0b8b4ac64e3ec51b0d31fe4346db1c3ac63f9f35 GIT binary patch literal 11884 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 aM$X{-fU$w;4g_KY{!9T)jWrYUyZ-@vlJOV- literal 0 HcmV?d00001 diff --git a/06.JPA-Introduction-In-10-Steps/jpa-in-10-steps-all-code.md b/06.JPA-Introduction-In-10-Steps/jpa-in-10-steps-all-code.md new file mode 100644 index 0000000..322559b --- /dev/null +++ b/06.JPA-Introduction-In-10-Steps/jpa-in-10-steps-all-code.md @@ -0,0 +1,371 @@ +## Complete Code Example +### /notes.txt + +``` +Questions +- Where is the database created? +- What schema is used to create the tables? +- Where are the tables created? +- Can I see the data in the database? +- Where is Hibernate coming in from? +- How is a datasource created? + +Magic of Spring Boot and in Memory Database +- Zero project setup or infrastructure +- Zero Configuration +- Zero Maintainance +- Easy to use for Learning and Unit Tests +- Simple Configuration to switch to a real database + +# Restrictions of using in-memory database +- Data is not persisted between restarts + +Spring Boot chooses a default value for you based on whether it thinks your database is embedded (default create-drop) or not (default none). + +HibernateJpaAutoConfiguration matched: + - @ConditionalOnClass found required classes 'org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean', 'javax.persistence.EntityManager'; @ConditionalOnMissingClass did not find unwanted class (OnClassCondition) + - HibernateEntityManager found class 'org.hibernate.ejb.HibernateEntityManager' (HibernateJpaAutoConfiguration.HibernateEntityManagerCondition) + +DataSourceAutoConfiguration matched: + - @ConditionalOnClass found required classes 'javax.sql.DataSource', 'org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType'; @ConditionalOnMissingClass did not find unwanted class (OnClassCondition) + +JpaBaseConfiguration#entityManagerFactory matched: + - @ConditionalOnMissingBean (types: org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean,javax.persistence.EntityManagerFactory; SearchStrategy: all) did not find any beans (OnBeanCondition) + +JpaBaseConfiguration#transactionManager matched: + - @ConditionalOnMissingBean (types: org.springframework.transaction.PlatformTransactionManager; SearchStrategy: all) did not find any beans (OnBeanCondition) +``` +--- + +### /pom.xml + +```xml + + + 4.0.0 + + com.in28minutes.learning.jpa + jpa-in-10-steps + 0.0.1-SNAPSHOT + jar + + jpa-in-10-steps + Demo project for Spring Boot + + + org.springframework.boot + spring-boot-starter-parent + 2.0.0.BUILD-SNAPSHOT + + + + + UTF-8 + UTF-8 + 1.8 + + + + + + org.springframework.boot + spring-boot-starter-data-jpa + + + + org.springframework.boot + spring-boot-starter-web + + + + com.h2database + h2 + runtime + + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + + + spring-snapshots + Spring Snapshots + https://repo.spring.io/snapshot + + true + + + + spring-milestones + Spring Milestones + https://repo.spring.io/milestone + + false + + + + + + + spring-snapshots + Spring Snapshots + https://repo.spring.io/snapshot + + true + + + + spring-milestones + Spring Milestones + https://repo.spring.io/milestone + + false + + + + + + +``` +--- + +### /src/main/java/com/in28minutes/learning/jpa/jpain10steps/entity/User.java + +```java +package com.in28minutes.learning.jpa.jpain10steps.entity; + +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; + +//Table - User +@Entity +public class User { + + @Id + @GeneratedValue + private long id; + + private String name; + + private String role; + + protected User() { + + } + + public User(String name, String role) { + super(); + this.name = name; + this.role = role; + } + + public long getId() { + return id; + } + + public String getName() { + return name; + } + + public String getRole() { + return role; + } + + @Override + public String toString() { + return String.format("User [id=%s, name=%s, role=%s]", id, name, role); + } +} +``` +--- + +### /src/main/java/com/in28minutes/learning/jpa/jpain10steps/JpaIn10StepsApplication.java + +```java +package com.in28minutes.learning.jpa.jpain10steps; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class JpaIn10StepsApplication { + + public static void main(String[] args) { + SpringApplication.run(JpaIn10StepsApplication.class, args); + } +} +``` +--- + +### /src/main/java/com/in28minutes/learning/jpa/jpain10steps/service/UserDAOService.java + +```java +package com.in28minutes.learning.jpa.jpain10steps.service; + +import javax.persistence.EntityManager; +import javax.persistence.PersistenceContext; +import javax.transaction.Transactional; + +import org.springframework.stereotype.Repository; + +import com.in28minutes.learning.jpa.jpain10steps.entity.User; + +@Repository +@Transactional +public class UserDAOService { + + @PersistenceContext + private EntityManager entityManager; + + public long insert(User user){ + entityManager.persist(user); + return user.getId(); + } +} + +/* + * Spring Data JPA + * + * + * + */ +``` +--- + +### /src/main/java/com/in28minutes/learning/jpa/jpain10steps/service/UserRepository.java + +```java +package com.in28minutes.learning.jpa.jpain10steps.service; + +import org.springframework.data.jpa.repository.JpaRepository; + +import com.in28minutes.learning.jpa.jpain10steps.entity.User; + +public interface UserRepository extends JpaRepository{ + +} +``` +--- + +### /src/main/java/com/in28minutes/learning/jpa/jpain10steps/UserDaoServiceCommandLineRunner.java + +```java +package com.in28minutes.learning.jpa.jpain10steps; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.CommandLineRunner; +import org.springframework.stereotype.Component; + +import com.in28minutes.learning.jpa.jpain10steps.entity.User; +import com.in28minutes.learning.jpa.jpain10steps.service.UserDAOService; + +@Component +public class UserDaoServiceCommandLineRunner implements CommandLineRunner{ + + private static final Logger log = + LoggerFactory.getLogger(UserDaoServiceCommandLineRunner.class); + + @Autowired + private UserDAOService userDaoService; + + @Override + public void run(String... arg0) throws Exception { + User user = new User("Jack", "Admin"); + //New User is created : User [id=1, name=Jack, role=Admin] + long insert = userDaoService.insert(user); + log.info("New User is created : " + user); + } +} +``` +--- + +### /src/main/java/com/in28minutes/learning/jpa/jpain10steps/UserRepositoryCommandLineRunner.java + +```java +package com.in28minutes.learning.jpa.jpain10steps; + +import java.util.List; +import java.util.Optional; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.CommandLineRunner; +import org.springframework.stereotype.Component; + +import com.in28minutes.learning.jpa.jpain10steps.entity.User; +import com.in28minutes.learning.jpa.jpain10steps.service.UserRepository; + +@Component +public class UserRepositoryCommandLineRunner implements CommandLineRunner{ + + private static final Logger log = + LoggerFactory.getLogger(UserRepositoryCommandLineRunner.class); + + @Autowired + private UserRepository userRepository; + + @Override + public void run(String... arg0) throws Exception { + User user = new User("Jill", "Admin"); + userRepository.save(user); + log.info("New User is created : " + user); + + Optional userWithIdOne = userRepository.findById(1L); + log.info("User is retrived : " + userWithIdOne); + + List users = userRepository.findAll(); + log.info("All Users : " + users); + } + +} +``` +--- + +### /src/main/resources/application.properties + +``` +spring.jpa.show-sql=true +spring.h2.console.enabled=true +logging.level.org.springframework=debug +``` +--- + +### /src/test/java/com/in28minutes/learning/jpa/jpain10steps/JpaIn10StepsApplicationTests.java + +```java +package com.in28minutes.learning.jpa.jpain10steps; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; + +@RunWith(SpringRunner.class) +@SpringBootTest +public class JpaIn10StepsApplicationTests { + + @Test + public void contextLoads() { + } + +} +``` +--- \ No newline at end of file diff --git a/06.JPA-Introduction-In-10-Steps/mvnw b/06.JPA-Introduction-In-10-Steps/mvnw new file mode 100755 index 0000000..5bf251c --- /dev/null +++ b/06.JPA-Introduction-In-10-Steps/mvnw @@ -0,0 +1,225 @@ +#!/bin/sh +# ---------------------------------------------------------------------------- +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# ---------------------------------------------------------------------------- + +# ---------------------------------------------------------------------------- +# Maven2 Start Up Batch script +# +# Required ENV vars: +# ------------------ +# JAVA_HOME - location of a JDK home dir +# +# Optional ENV vars +# ----------------- +# M2_HOME - location of maven2's installed home dir +# MAVEN_OPTS - parameters passed to the Java VM when running Maven +# e.g. to debug Maven itself, use +# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +# MAVEN_SKIP_RC - flag to disable loading of mavenrc files +# ---------------------------------------------------------------------------- + +if [ -z "$MAVEN_SKIP_RC" ] ; then + + if [ -f /etc/mavenrc ] ; then + . /etc/mavenrc + fi + + if [ -f "$HOME/.mavenrc" ] ; then + . "$HOME/.mavenrc" + fi + +fi + +# OS specific support. $var _must_ be set to either true or false. +cygwin=false; +darwin=false; +mingw=false +case "`uname`" in + CYGWIN*) cygwin=true ;; + MINGW*) mingw=true;; + Darwin*) darwin=true + # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home + # See https://developer.apple.com/library/mac/qa/qa1170/_index.html + if [ -z "$JAVA_HOME" ]; then + if [ -x "/usr/libexec/java_home" ]; then + export JAVA_HOME="`/usr/libexec/java_home`" + else + export JAVA_HOME="/Library/Java/Home" + fi + fi + ;; +esac + +if [ -z "$JAVA_HOME" ] ; then + if [ -r /etc/gentoo-release ] ; then + JAVA_HOME=`java-config --jre-home` + fi +fi + +if [ -z "$M2_HOME" ] ; then + ## resolve links - $0 may be a link to maven's home + PRG="$0" + + # need this for relative symlinks + while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG="`dirname "$PRG"`/$link" + fi + done + + saveddir=`pwd` + + M2_HOME=`dirname "$PRG"`/.. + + # make it fully qualified + M2_HOME=`cd "$M2_HOME" && pwd` + + cd "$saveddir" + # echo Using m2 at $M2_HOME +fi + +# For Cygwin, ensure paths are in UNIX format before anything is touched +if $cygwin ; then + [ -n "$M2_HOME" ] && + M2_HOME=`cygpath --unix "$M2_HOME"` + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --unix "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --unix "$CLASSPATH"` +fi + +# For Migwn, ensure paths are in UNIX format before anything is touched +if $mingw ; then + [ -n "$M2_HOME" ] && + M2_HOME="`(cd "$M2_HOME"; pwd)`" + [ -n "$JAVA_HOME" ] && + JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" + # TODO classpath? +fi + +if [ -z "$JAVA_HOME" ]; then + javaExecutable="`which javac`" + if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then + # readlink(1) is not available as standard on Solaris 10. + readLink=`which readlink` + if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then + if $darwin ; then + javaHome="`dirname \"$javaExecutable\"`" + javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" + else + javaExecutable="`readlink -f \"$javaExecutable\"`" + fi + javaHome="`dirname \"$javaExecutable\"`" + javaHome=`expr "$javaHome" : '\(.*\)/bin'` + JAVA_HOME="$javaHome" + export JAVA_HOME + fi + fi +fi + +if [ -z "$JAVACMD" ] ; then + if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + else + JAVACMD="`which java`" + fi +fi + +if [ ! -x "$JAVACMD" ] ; then + echo "Error: JAVA_HOME is not defined correctly." >&2 + echo " We cannot execute $JAVACMD" >&2 + exit 1 +fi + +if [ -z "$JAVA_HOME" ] ; then + echo "Warning: JAVA_HOME environment variable is not set." +fi + +CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher + +# traverses directory structure from process work directory to filesystem root +# first directory with .mvn subdirectory is considered project base directory +find_maven_basedir() { + + if [ -z "$1" ] + then + echo "Path not specified to find_maven_basedir" + return 1 + fi + + basedir="$1" + wdir="$1" + while [ "$wdir" != '/' ] ; do + if [ -d "$wdir"/.mvn ] ; then + basedir=$wdir + break + fi + # workaround for JBEAP-8937 (on Solaris 10/Sparc) + if [ -d "${wdir}" ]; then + wdir=`cd "$wdir/.."; pwd` + fi + # end of workaround + done + echo "${basedir}" +} + +# concatenates all lines of a file +concat_lines() { + if [ -f "$1" ]; then + echo "$(tr -s '\n' ' ' < "$1")" + fi +} + +BASE_DIR=`find_maven_basedir "$(pwd)"` +if [ -z "$BASE_DIR" ]; then + exit 1; +fi + +export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} +echo $MAVEN_PROJECTBASEDIR +MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" + +# For Cygwin, switch paths to Windows format before running java +if $cygwin; then + [ -n "$M2_HOME" ] && + M2_HOME=`cygpath --path --windows "$M2_HOME"` + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --windows "$CLASSPATH"` + [ -n "$MAVEN_PROJECTBASEDIR" ] && + MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` +fi + +WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +exec "$JAVACMD" \ + $MAVEN_OPTS \ + -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ + "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ + ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" diff --git a/06.JPA-Introduction-In-10-Steps/mvnw.cmd b/06.JPA-Introduction-In-10-Steps/mvnw.cmd new file mode 100644 index 0000000..019bd74 --- /dev/null +++ b/06.JPA-Introduction-In-10-Steps/mvnw.cmd @@ -0,0 +1,143 @@ +@REM ---------------------------------------------------------------------------- +@REM Licensed to the Apache Software Foundation (ASF) under one +@REM or more contributor license agreements. See the NOTICE file +@REM distributed with this work for additional information +@REM regarding copyright ownership. The ASF licenses this file +@REM to you under the Apache License, Version 2.0 (the +@REM "License"); you may not use this file except in compliance +@REM with the License. You may obtain a copy of the License at +@REM +@REM http://www.apache.org/licenses/LICENSE-2.0 +@REM +@REM Unless required by applicable law or agreed to in writing, +@REM software distributed under the License is distributed on an +@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +@REM KIND, either express or implied. See the License for the +@REM specific language governing permissions and limitations +@REM under the License. +@REM ---------------------------------------------------------------------------- + +@REM ---------------------------------------------------------------------------- +@REM Maven2 Start Up Batch script +@REM +@REM Required ENV vars: +@REM JAVA_HOME - location of a JDK home dir +@REM +@REM Optional ENV vars +@REM M2_HOME - location of maven2's installed home dir +@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands +@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending +@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven +@REM e.g. to debug Maven itself, use +@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files +@REM ---------------------------------------------------------------------------- + +@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' +@echo off +@REM enable echoing my setting MAVEN_BATCH_ECHO to 'on' +@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% + +@REM set %HOME% to equivalent of $HOME +if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") + +@REM Execute a user defined script before this one +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre +@REM check for pre script, once with legacy .bat ending and once with .cmd ending +if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" +if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" +:skipRcPre + +@setlocal + +set ERROR_CODE=0 + +@REM To isolate internal variables from possible post scripts, we use another setlocal +@setlocal + +@REM ==== START VALIDATION ==== +if not "%JAVA_HOME%" == "" goto OkJHome + +echo. +echo Error: JAVA_HOME not found in your environment. >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +:OkJHome +if exist "%JAVA_HOME%\bin\java.exe" goto init + +echo. +echo Error: JAVA_HOME is set to an invalid directory. >&2 +echo JAVA_HOME = "%JAVA_HOME%" >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +@REM ==== END VALIDATION ==== + +:init + +@REM Find the project base dir, i.e. the directory that contains the folder ".mvn". +@REM Fallback to current working directory if not found. + +set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% +IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir + +set EXEC_DIR=%CD% +set WDIR=%EXEC_DIR% +:findBaseDir +IF EXIST "%WDIR%"\.mvn goto baseDirFound +cd .. +IF "%WDIR%"=="%CD%" goto baseDirNotFound +set WDIR=%CD% +goto findBaseDir + +:baseDirFound +set MAVEN_PROJECTBASEDIR=%WDIR% +cd "%EXEC_DIR%" +goto endDetectBaseDir + +:baseDirNotFound +set MAVEN_PROJECTBASEDIR=%EXEC_DIR% +cd "%EXEC_DIR%" + +:endDetectBaseDir + +IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig + +@setlocal EnableExtensions EnableDelayedExpansion +for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a +@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% + +:endReadAdditionalConfig + +SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" + +set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" +set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* +if ERRORLEVEL 1 goto error +goto end + +:error +set ERROR_CODE=1 + +:end +@endlocal & set ERROR_CODE=%ERROR_CODE% + +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost +@REM check for post script, once with legacy .bat ending and once with .cmd ending +if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" +if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" +:skipRcPost + +@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' +if "%MAVEN_BATCH_PAUSE%" == "on" pause + +if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% + +exit /B %ERROR_CODE% diff --git a/06.JPA-Introduction-In-10-Steps/notes.txt b/06.JPA-Introduction-In-10-Steps/notes.txt new file mode 100644 index 0000000..77a081d --- /dev/null +++ b/06.JPA-Introduction-In-10-Steps/notes.txt @@ -0,0 +1,32 @@ +Questions +- Where is the database created? +- What schema is used to create the tables? +- Where are the tables created? +- Can I see the data in the database? +- Where is Hibernate coming in from? +- How is a datasource created? + +Magic of Spring Boot and in Memory Database +- Zero project setup or infrastructure +- Zero Configuration +- Zero Maintainance +- Easy to use for Learning and Unit Tests +- Simple Configuration to switch to a real database + +# Restrictions of using in-memory database +- Data is not persisted between restarts + +Spring Boot chooses a default value for you based on whether it thinks your database is embedded (default create-drop) or not (default none). + +HibernateJpaAutoConfiguration matched: + - @ConditionalOnClass found required classes 'org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean', 'javax.persistence.EntityManager'; @ConditionalOnMissingClass did not find unwanted class (OnClassCondition) + - HibernateEntityManager found class 'org.hibernate.ejb.HibernateEntityManager' (HibernateJpaAutoConfiguration.HibernateEntityManagerCondition) + +DataSourceAutoConfiguration matched: + - @ConditionalOnClass found required classes 'javax.sql.DataSource', 'org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType'; @ConditionalOnMissingClass did not find unwanted class (OnClassCondition) + +JpaBaseConfiguration#entityManagerFactory matched: + - @ConditionalOnMissingBean (types: org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean,javax.persistence.EntityManagerFactory; SearchStrategy: all) did not find any beans (OnBeanCondition) + +JpaBaseConfiguration#transactionManager matched: + - @ConditionalOnMissingBean (types: org.springframework.transaction.PlatformTransactionManager; SearchStrategy: all) did not find any beans (OnBeanCondition) diff --git a/06.JPA-Introduction-In-10-Steps/pom.xml b/06.JPA-Introduction-In-10-Steps/pom.xml new file mode 100644 index 0000000..fe27988 --- /dev/null +++ b/06.JPA-Introduction-In-10-Steps/pom.xml @@ -0,0 +1,100 @@ + + + 4.0.0 + + com.in28minutes.learning.jpa + jpa-in-10-steps + 0.0.1-SNAPSHOT + jar + + jpa-in-10-steps + Demo project for Spring Boot + + + org.springframework.boot + spring-boot-starter-parent + 2.0.0.BUILD-SNAPSHOT + + + + + UTF-8 + UTF-8 + 1.8 + + + + + + org.springframework.boot + spring-boot-starter-data-jpa + + + + org.springframework.boot + spring-boot-starter-web + + + + com.h2database + h2 + runtime + + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + + + spring-snapshots + Spring Snapshots + https://repo.spring.io/snapshot + + true + + + + spring-milestones + Spring Milestones + https://repo.spring.io/milestone + + false + + + + + + + spring-snapshots + Spring Snapshots + https://repo.spring.io/snapshot + + true + + + + spring-milestones + Spring Milestones + https://repo.spring.io/milestone + + false + + + + + + diff --git a/06.JPA-Introduction-In-10-Steps/readme.md b/06.JPA-Introduction-In-10-Steps/readme.md new file mode 100644 index 0000000..56d2a9f --- /dev/null +++ b/06.JPA-Introduction-In-10-Steps/readme.md @@ -0,0 +1,407 @@ +## First 10 Steps in JPA with H2 in-memory database + +- Step 1 : Object Relational Impedence Mismatch - Understanding the problem that JPA solves +- Step 2 : World before JPA - JDBC, Spring JDBC and myBatis +- Step 3 : Introduction to JPA +- Step 4 : Creating a JPA Project using Spring Initializr +- Step 5 : Defining a JPA Entity - User +- Step 6 : Defining a Service to manage the Entity - UserService and EntityManager +- Step 7 : Using a Command Line Runner to save the User to database. +- Step 8 : Magic of Spring Boot and In Memory Database H2 +- Step 9 : Introduction to Spring Data JPA +- Step 10 : More JPA Repository : findById and findAll +- Next Steps + +Notes +- http://localhost:8080/h2-console +- Use db url jdbc:h2:mem:testdb +## Complete Code Example + + +### /notes.txt + +``` +Questions +- Where is the database created? +- What schema is used to create the tables? +- Where are the tables created? +- Can I see the data in the database? +- Where is Hibernate coming in from? +- How is a datasource created? + +Magic of Spring Boot and in Memory Database +- Zero project setup or infrastructure +- Zero Configuration +- Zero Maintainance +- Easy to use for Learning and Unit Tests +- Simple Configuration to switch to a real database + +# Restrictions of using in-memory database +- Data is not persisted between restarts + +Spring Boot chooses a default value for you based on whether it thinks your database is embedded (default create-drop) or not (default none). + +HibernateJpaAutoConfiguration matched: + - @ConditionalOnClass found required classes 'org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean', 'javax.persistence.EntityManager'; @ConditionalOnMissingClass did not find unwanted class (OnClassCondition) + - HibernateEntityManager found class 'org.hibernate.ejb.HibernateEntityManager' (HibernateJpaAutoConfiguration.HibernateEntityManagerCondition) + +DataSourceAutoConfiguration matched: + - @ConditionalOnClass found required classes 'javax.sql.DataSource', 'org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType'; @ConditionalOnMissingClass did not find unwanted class (OnClassCondition) + +JpaBaseConfiguration#entityManagerFactory matched: + - @ConditionalOnMissingBean (types: org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean,javax.persistence.EntityManagerFactory; SearchStrategy: all) did not find any beans (OnBeanCondition) + +JpaBaseConfiguration#transactionManager matched: + - @ConditionalOnMissingBean (types: org.springframework.transaction.PlatformTransactionManager; SearchStrategy: all) did not find any beans (OnBeanCondition) +``` +--- + +### /pom.xml + +```xml + + + 4.0.0 + + com.in28minutes.learning.jpa + jpa-in-10-steps + 0.0.1-SNAPSHOT + jar + + jpa-in-10-steps + Demo project for Spring Boot + + + org.springframework.boot + spring-boot-starter-parent + 2.0.0.BUILD-SNAPSHOT + + + + + UTF-8 + UTF-8 + 1.8 + + + + + + org.springframework.boot + spring-boot-starter-data-jpa + + + + org.springframework.boot + spring-boot-starter-web + + + + com.h2database + h2 + runtime + + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + + + spring-snapshots + Spring Snapshots + https://repo.spring.io/snapshot + + true + + + + spring-milestones + Spring Milestones + https://repo.spring.io/milestone + + false + + + + + + + spring-snapshots + Spring Snapshots + https://repo.spring.io/snapshot + + true + + + + spring-milestones + Spring Milestones + https://repo.spring.io/milestone + + false + + + + + + +``` +--- + +### /src/main/java/com/in28minutes/learning/jpa/jpain10steps/entity/User.java + +```java +package com.in28minutes.learning.jpa.jpain10steps.entity; + +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; + +//Table - User +@Entity +public class User { + + @Id + @GeneratedValue + private long id; + + private String name; + + private String role; + + protected User() { + + } + + public User(String name, String role) { + super(); + this.name = name; + this.role = role; + } + + public long getId() { + return id; + } + + public String getName() { + return name; + } + + public String getRole() { + return role; + } + + @Override + public String toString() { + return String.format("User [id=%s, name=%s, role=%s]", id, name, role); + } +} +``` +--- + +### /src/main/java/com/in28minutes/learning/jpa/jpain10steps/JpaIn10StepsApplication.java + +```java +package com.in28minutes.learning.jpa.jpain10steps; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class JpaIn10StepsApplication { + + public static void main(String[] args) { + SpringApplication.run(JpaIn10StepsApplication.class, args); + } +} +``` +--- + +### /src/main/java/com/in28minutes/learning/jpa/jpain10steps/service/UserDAOService.java + +```java +package com.in28minutes.learning.jpa.jpain10steps.service; + +import javax.persistence.EntityManager; +import javax.persistence.PersistenceContext; +import javax.transaction.Transactional; + +import org.springframework.stereotype.Repository; + +import com.in28minutes.learning.jpa.jpain10steps.entity.User; + +@Repository +@Transactional +public class UserDAOService { + + @PersistenceContext + private EntityManager entityManager; + + public long insert(User user){ + entityManager.persist(user); + return user.getId(); + } +} + +/* + * Spring Data JPA + * + * + * + */ +``` +--- + +### /src/main/java/com/in28minutes/learning/jpa/jpain10steps/service/UserRepository.java + +```java +package com.in28minutes.learning.jpa.jpain10steps.service; + +import org.springframework.data.jpa.repository.JpaRepository; + +import com.in28minutes.learning.jpa.jpain10steps.entity.User; + +public interface UserRepository extends JpaRepository{ + +} +``` +--- + +### /src/main/java/com/in28minutes/learning/jpa/jpain10steps/UserDaoServiceCommandLineRunner.java + +```java +package com.in28minutes.learning.jpa.jpain10steps; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.CommandLineRunner; +import org.springframework.stereotype.Component; + +import com.in28minutes.learning.jpa.jpain10steps.entity.User; +import com.in28minutes.learning.jpa.jpain10steps.service.UserDAOService; + +@Component +public class UserDaoServiceCommandLineRunner implements CommandLineRunner{ + + private static final Logger log = + LoggerFactory.getLogger(UserDaoServiceCommandLineRunner.class); + + @Autowired + private UserDAOService userDaoService; + + @Override + public void run(String... arg0) throws Exception { + User user = new User("Jack", "Admin"); + //New User is created : User [id=1, name=Jack, role=Admin] + long insert = userDaoService.insert(user); + log.info("New User is created : " + user); + } +} +``` +--- + +### /src/main/java/com/in28minutes/learning/jpa/jpain10steps/UserRepositoryCommandLineRunner.java + +```java +package com.in28minutes.learning.jpa.jpain10steps; + +import java.util.List; +import java.util.Optional; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.CommandLineRunner; +import org.springframework.stereotype.Component; + +import com.in28minutes.learning.jpa.jpain10steps.entity.User; +import com.in28minutes.learning.jpa.jpain10steps.service.UserRepository; + +@Component +public class UserRepositoryCommandLineRunner implements CommandLineRunner{ + + private static final Logger log = + LoggerFactory.getLogger(UserRepositoryCommandLineRunner.class); + + @Autowired + private UserRepository userRepository; + + @Override + public void run(String... arg0) throws Exception { + User user = new User("Jill", "Admin"); + userRepository.save(user); + log.info("New User is created : " + user); + + Optional userWithIdOne = userRepository.findById(1L); + log.info("User is retrived : " + userWithIdOne); + + List users = userRepository.findAll(); + log.info("All Users : " + users); + } + +} +``` +--- + +### /src/main/resources/application.properties + +```properties +spring.jpa.show-sql=true +spring.h2.console.enabled=true +logging.level.org.springframework=debug +``` +--- + +### /src/test/java/com/in28minutes/learning/jpa/jpain10steps/JpaIn10StepsApplicationTests.java + +```java +package com.in28minutes.learning.jpa.jpain10steps; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; + +@RunWith(SpringRunner.class) +@SpringBootTest +public class JpaIn10StepsApplicationTests { + + @Test + public void contextLoads() { + } + +} +``` +--- + +### /step-completed.sh + +``` +java -cp /ProgrammingExcellence/Workspaces/Rithus.com/ListDirectoryContentInGitFormat/bin test.ListDirectoryContentInGitFormat $PWD >> $1.md +zip -r $1.zip . -x "target/*" -x ".*/*" -x ".*" -x "*.md" -x "mvn*" -x "*.zip" +git add *; git commit -m "$1"; git push; +``` +--- + +### /take-step-backup.sh + +``` +java -cp /ProgrammingExcellence/Workspaces/Rithus.com/ListDirectoryContentInGitFormat/bin test.ListDirectoryContentInGitFormat $PWD >> $1.md +zip -r $1.zip . -x "target/*" -x ".*/*" -x ".*" -x "*.md" -x "mvn*" -x "*.zip" +``` +--- diff --git a/06.JPA-Introduction-In-10-Steps/src/.DS_Store b/06.JPA-Introduction-In-10-Steps/src/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..c47a126a4cc0e72651e9c5976cd60d86a3f5ed38 GIT binary patch literal 6148 zcmeHK%}&EG47Q<%ptGQ1C;V^gJCxgrF% zlzfRDXU0alD>quq1v-97nck9P+(goB?OR8E^*vDFc#oAd97<_s)Pb;0%lm$o&w|1ared zF2#@W&Y7tSE~O4rRIZ=y;NA1KKT` uh^!lA0Kqt>Vqi?gn8-bH5shjOGKM`j928|2(MNZnKLjEn-Z=w5z`!R6DL|Y6 literal 0 HcmV?d00001 diff --git a/06.JPA-Introduction-In-10-Steps/src/main/.DS_Store b/06.JPA-Introduction-In-10-Steps/src/main/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..a13d2e6ce992d946642a1cd989e7ea526abdbc50 GIT binary patch literal 6148 zcmeHK%}&EG47Q;{LtJ*;F^|CBAezb(^gftcG=#Qw8g%Dq@5uY`IW~xn95^9_Y$^Gi z_$SFHHK~b+7ehBEniA0jO^`(?h)fqv7v_8gWXW-C4!cdiTkF`Ye;Z`)6+P36jjHABXtJ8E^*v83SC^O})UO>}~yaJb7ya+5?)1_$5&w&__Q37|1;`%SN>a a(cu>jlcHpi`5X@PAAwAWch0~sFz^mInmVum literal 0 HcmV?d00001 diff --git a/06.JPA-Introduction-In-10-Steps/src/main/java/com/in28minutes/learning/jpa/jpain10steps/JpaIn10StepsApplication.java b/06.JPA-Introduction-In-10-Steps/src/main/java/com/in28minutes/learning/jpa/jpain10steps/JpaIn10StepsApplication.java new file mode 100644 index 0000000..7aff7a2 --- /dev/null +++ b/06.JPA-Introduction-In-10-Steps/src/main/java/com/in28minutes/learning/jpa/jpain10steps/JpaIn10StepsApplication.java @@ -0,0 +1,12 @@ +package com.in28minutes.learning.jpa.jpain10steps; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class JpaIn10StepsApplication { + + public static void main(String[] args) { + SpringApplication.run(JpaIn10StepsApplication.class, args); + } +} diff --git a/06.JPA-Introduction-In-10-Steps/src/main/java/com/in28minutes/learning/jpa/jpain10steps/UserDaoServiceCommandLineRunner.java b/06.JPA-Introduction-In-10-Steps/src/main/java/com/in28minutes/learning/jpa/jpain10steps/UserDaoServiceCommandLineRunner.java new file mode 100644 index 0000000..bae155f --- /dev/null +++ b/06.JPA-Introduction-In-10-Steps/src/main/java/com/in28minutes/learning/jpa/jpain10steps/UserDaoServiceCommandLineRunner.java @@ -0,0 +1,28 @@ +package com.in28minutes.learning.jpa.jpain10steps; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.CommandLineRunner; +import org.springframework.stereotype.Component; + +import com.in28minutes.learning.jpa.jpain10steps.entity.User; +import com.in28minutes.learning.jpa.jpain10steps.service.UserDAOService; + +@Component +public class UserDaoServiceCommandLineRunner implements CommandLineRunner{ + + private static final Logger log = + LoggerFactory.getLogger(UserDaoServiceCommandLineRunner.class); + + @Autowired + private UserDAOService userDaoService; + + @Override + public void run(String... arg0) throws Exception { + User user = new User("Jack", "Admin"); + //New User is created : User [id=1, name=Jack, role=Admin] + long insert = userDaoService.insert(user); + log.info("New User is created : " + user); + } +} \ No newline at end of file diff --git a/06.JPA-Introduction-In-10-Steps/src/main/java/com/in28minutes/learning/jpa/jpain10steps/UserRepositoryCommandLineRunner.java b/06.JPA-Introduction-In-10-Steps/src/main/java/com/in28minutes/learning/jpa/jpain10steps/UserRepositoryCommandLineRunner.java new file mode 100644 index 0000000..3cdf7e7 --- /dev/null +++ b/06.JPA-Introduction-In-10-Steps/src/main/java/com/in28minutes/learning/jpa/jpain10steps/UserRepositoryCommandLineRunner.java @@ -0,0 +1,37 @@ +package com.in28minutes.learning.jpa.jpain10steps; + +import java.util.List; +import java.util.Optional; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.CommandLineRunner; +import org.springframework.stereotype.Component; + +import com.in28minutes.learning.jpa.jpain10steps.entity.User; +import com.in28minutes.learning.jpa.jpain10steps.service.UserRepository; + +@Component +public class UserRepositoryCommandLineRunner implements CommandLineRunner{ + + private static final Logger log = + LoggerFactory.getLogger(UserRepositoryCommandLineRunner.class); + + @Autowired + private UserRepository userRepository; + + @Override + public void run(String... arg0) throws Exception { + User user = new User("Jill", "Admin"); + userRepository.save(user); + log.info("New User is created : " + user); + + Optional userWithIdOne = userRepository.findById(1L); + log.info("User is retrived : " + userWithIdOne); + + List users = userRepository.findAll(); + log.info("All Users : " + users); + } + +} \ No newline at end of file diff --git a/06.JPA-Introduction-In-10-Steps/src/main/java/com/in28minutes/learning/jpa/jpain10steps/entity/User.java b/06.JPA-Introduction-In-10-Steps/src/main/java/com/in28minutes/learning/jpa/jpain10steps/entity/User.java new file mode 100644 index 0000000..d5bab5c --- /dev/null +++ b/06.JPA-Introduction-In-10-Steps/src/main/java/com/in28minutes/learning/jpa/jpain10steps/entity/User.java @@ -0,0 +1,45 @@ +package com.in28minutes.learning.jpa.jpain10steps.entity; + +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; + +//Table - User +@Entity +public class User { + + @Id + @GeneratedValue + private long id; + + private String name; + + private String role; + + protected User() { + + } + + public User(String name, String role) { + super(); + this.name = name; + this.role = role; + } + + public long getId() { + return id; + } + + public String getName() { + return name; + } + + public String getRole() { + return role; + } + + @Override + public String toString() { + return String.format("User [id=%s, name=%s, role=%s]", id, name, role); + } +} diff --git a/06.JPA-Introduction-In-10-Steps/src/main/java/com/in28minutes/learning/jpa/jpain10steps/service/UserDAOService.java b/06.JPA-Introduction-In-10-Steps/src/main/java/com/in28minutes/learning/jpa/jpain10steps/service/UserDAOService.java new file mode 100644 index 0000000..0c6fccc --- /dev/null +++ b/06.JPA-Introduction-In-10-Steps/src/main/java/com/in28minutes/learning/jpa/jpain10steps/service/UserDAOService.java @@ -0,0 +1,29 @@ +package com.in28minutes.learning.jpa.jpain10steps.service; + +import javax.persistence.EntityManager; +import javax.persistence.PersistenceContext; +import javax.transaction.Transactional; + +import org.springframework.stereotype.Repository; + +import com.in28minutes.learning.jpa.jpain10steps.entity.User; + +@Repository +@Transactional +public class UserDAOService { + + @PersistenceContext + private EntityManager entityManager; + + public long insert(User user){ + entityManager.persist(user); + return user.getId(); + } +} + +/* + * Spring Data JPA + * + * + * + */ diff --git a/06.JPA-Introduction-In-10-Steps/src/main/java/com/in28minutes/learning/jpa/jpain10steps/service/UserRepository.java b/06.JPA-Introduction-In-10-Steps/src/main/java/com/in28minutes/learning/jpa/jpain10steps/service/UserRepository.java new file mode 100644 index 0000000..9fc309f --- /dev/null +++ b/06.JPA-Introduction-In-10-Steps/src/main/java/com/in28minutes/learning/jpa/jpain10steps/service/UserRepository.java @@ -0,0 +1,9 @@ +package com.in28minutes.learning.jpa.jpain10steps.service; + +import org.springframework.data.jpa.repository.JpaRepository; + +import com.in28minutes.learning.jpa.jpain10steps.entity.User; + +public interface UserRepository extends JpaRepository{ + +} diff --git a/06.JPA-Introduction-In-10-Steps/src/main/resources/.DS_Store b/06.JPA-Introduction-In-10-Steps/src/main/resources/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..97d947d6e1495e62fff3f9e843aa4d37f42f14b9 GIT binary patch literal 6148 zcmeHKOHRW;47E#zTCu5Ev79UP22mAGkPCp4svsq8L|fJocjP`ie^e=CV2J?PlKqmg zoy@$9#xW7`=G?4_Rz$Rb3eJ`=84Sf;^_B%Wody1Br)s{Nysnr>7 z>6qDJnW)S>vr&`TW}-54)`f=&27-ZL;EysO=PP7!V;D9V2nK?I3j=aKB&cBFI2h{D z0i!Jd(0&BXC%uH^B*(&WFoXxfmI|~~_7;P+6%l{(xWaKTv~*%`KG@&P-l4F*9r;Pa zi3`K9!9XxDX5h+hS5p7)zwZCXNpTkp1Oxwy0iKstxxtdWwoVo&wKhQ?pdu1C7+z9v hp`{qHQi|`ODi}|40W2H`LySP;M?h%^6Ab()10M>qOdS9K literal 0 HcmV?d00001 diff --git a/06.JPA-Introduction-In-10-Steps/src/main/resources/application.properties b/06.JPA-Introduction-In-10-Steps/src/main/resources/application.properties new file mode 100644 index 0000000..7716fe8 --- /dev/null +++ b/06.JPA-Introduction-In-10-Steps/src/main/resources/application.properties @@ -0,0 +1,3 @@ +spring.jpa.show-sql=true +spring.h2.console.enabled=true +logging.level.org.springframework=debug \ No newline at end of file diff --git a/06.JPA-Introduction-In-10-Steps/src/test/java/com/in28minutes/learning/jpa/jpain10steps/JpaIn10StepsApplicationTests.java b/06.JPA-Introduction-In-10-Steps/src/test/java/com/in28minutes/learning/jpa/jpain10steps/JpaIn10StepsApplicationTests.java new file mode 100644 index 0000000..770aad7 --- /dev/null +++ b/06.JPA-Introduction-In-10-Steps/src/test/java/com/in28minutes/learning/jpa/jpain10steps/JpaIn10StepsApplicationTests.java @@ -0,0 +1,16 @@ +package com.in28minutes.learning.jpa.jpain10steps; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; + +@RunWith(SpringRunner.class) +@SpringBootTest +public class JpaIn10StepsApplicationTests { + + @Test + public void contextLoads() { + } + +} diff --git a/06.JPA-Introduction-In-10-Steps/step-completed.sh b/06.JPA-Introduction-In-10-Steps/step-completed.sh new file mode 100755 index 0000000..084e06f --- /dev/null +++ b/06.JPA-Introduction-In-10-Steps/step-completed.sh @@ -0,0 +1,3 @@ +java -cp /ProgrammingExcellence/Workspaces/Rithus.com/ListDirectoryContentInGitFormat/bin test.ListDirectoryContentInGitFormat $PWD >> $1.md +zip -r $1.zip . -x "target/*" -x ".*/*" -x ".*" -x "*.md" -x "mvn*" -x "*.zip" +git add *; git commit -m "$1"; git push; diff --git a/06.JPA-Introduction-In-10-Steps/step21.md b/06.JPA-Introduction-In-10-Steps/step21.md new file mode 100644 index 0000000..71b3f64 --- /dev/null +++ b/06.JPA-Introduction-In-10-Steps/step21.md @@ -0,0 +1,766 @@ + +## Complete Code Example + + +### /example.sh + +``` +java -cp /ProgrammingExcellence/Workspaces/Rithus.com/ListDirectoryContentInGitFormat/bin test.ListDirectoryContentInGitFormat >> $1.md +git add *; git commit -m "$1"; git push; +zip -r $1.zip . -x "target/*" -x ".*/*" -x ".*" -x "*.md" -x "mvn*" +``` +--- + +### /notes.txt + +``` +Questions +- Where is the database created? +- What schema is used to create the tables? +- Where are the tables created? +- Can I see the data in the database? +- Where is Hibernate coming in from? +- How is a datasource created? + +Magic of Spring Boot and in Memory Database +- Zero project setup or infrastructure +- Zero Configuration +- Zero Maintainance +- Easy to use for Learning and Unit Tests +- Simple Configuration to switch to a real database + +# Restrictions of using in-memory database +- Data is not persisted between restarts + +Spring Boot chooses a default value for you based on whether it thinks your database is embedded (default create-drop) or not (default none). + +HibernateJpaAutoConfiguration matched: + - @ConditionalOnClass found required classes 'org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean', 'javax.persistence.EntityManager'; @ConditionalOnMissingClass did not find unwanted class (OnClassCondition) + - HibernateEntityManager found class 'org.hibernate.ejb.HibernateEntityManager' (HibernateJpaAutoConfiguration.HibernateEntityManagerCondition) + +DataSourceAutoConfiguration matched: + - @ConditionalOnClass found required classes 'javax.sql.DataSource', 'org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType'; @ConditionalOnMissingClass did not find unwanted class (OnClassCondition) + +JpaBaseConfiguration#entityManagerFactory matched: + - @ConditionalOnMissingBean (types: org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean,javax.persistence.EntityManagerFactory; SearchStrategy: all) did not find any beans (OnBeanCondition) + +JpaBaseConfiguration#transactionManager matched: + - @ConditionalOnMissingBean (types: org.springframework.transaction.PlatformTransactionManager; SearchStrategy: all) did not find any beans (OnBeanCondition) +``` +--- + +### /pom.xml + +```xml + + + 4.0.0 + + com.in28minutes.learning.jpa + jpa-in-10-steps + 0.0.1-SNAPSHOT + jar + + jpa-in-10-steps + Demo project for Spring Boot + + + org.springframework.boot + spring-boot-starter-parent + 2.0.0.BUILD-SNAPSHOT + + + + + UTF-8 + UTF-8 + 1.8 + + + + + + org.springframework.boot + spring-boot-starter-data-jpa + + + + org.springframework.boot + spring-boot-starter-web + + + + com.h2database + h2 + runtime + + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + + + spring-snapshots + Spring Snapshots + https://repo.spring.io/snapshot + + true + + + + spring-milestones + Spring Milestones + https://repo.spring.io/milestone + + false + + + + + + + spring-snapshots + Spring Snapshots + https://repo.spring.io/snapshot + + true + + + + spring-milestones + Spring Milestones + https://repo.spring.io/milestone + + false + + + + + + +``` +--- + +### /src/main/java/com/in28minutes/learning/jpa/jpain10steps/entity/User.java + +```java +package com.in28minutes.learning.jpa.jpain10steps.entity; + +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; + +//Table - User +@Entity +public class User { + + @Id + @GeneratedValue + private long id; + + private String name; + + private String role; + + protected User() { + + } + + public User(String name, String role) { + super(); + this.name = name; + this.role = role; + } + + public long getId() { + return id; + } + + public String getName() { + return name; + } + + public String getRole() { + return role; + } + + @Override + public String toString() { + return String.format("User [id=%s, name=%s, role=%s]", id, name, role); + } +} +``` +--- + +### /src/main/java/com/in28minutes/learning/jpa/jpain10steps/JpaIn10StepsApplication.java + +```java +package com.in28minutes.learning.jpa.jpain10steps; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class JpaIn10StepsApplication { + + public static void main(String[] args) { + SpringApplication.run(JpaIn10StepsApplication.class, args); + } +} +``` +--- + +### /src/main/java/com/in28minutes/learning/jpa/jpain10steps/service/UserDAOService.java + +```java +package com.in28minutes.learning.jpa.jpain10steps.service; + +import javax.persistence.EntityManager; +import javax.persistence.PersistenceContext; +import javax.transaction.Transactional; + +import org.springframework.stereotype.Repository; + +import com.in28minutes.learning.jpa.jpain10steps.entity.User; + +@Repository +@Transactional +public class UserDAOService { + + @PersistenceContext + private EntityManager entityManager; + + public long insert(User user){ + entityManager.persist(user); + return user.getId(); + } +} + +/* + * Spring Data JPA + * + * + * + */ +``` +--- + +### /src/main/java/com/in28minutes/learning/jpa/jpain10steps/service/UserRepository.java + +```java +package com.in28minutes.learning.jpa.jpain10steps.service; + +import org.springframework.data.jpa.repository.JpaRepository; + +import com.in28minutes.learning.jpa.jpain10steps.entity.User; + +public interface UserRepository extends JpaRepository{ + +} +``` +--- + +### /src/main/java/com/in28minutes/learning/jpa/jpain10steps/UserDaoServiceCommandLineRunner.java + +```java +package com.in28minutes.learning.jpa.jpain10steps; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.CommandLineRunner; +import org.springframework.stereotype.Component; + +import com.in28minutes.learning.jpa.jpain10steps.entity.User; +import com.in28minutes.learning.jpa.jpain10steps.service.UserDAOService; + +@Component +public class UserDaoServiceCommandLineRunner implements CommandLineRunner{ + + private static final Logger log = + LoggerFactory.getLogger(UserDaoServiceCommandLineRunner.class); + + @Autowired + private UserDAOService userDaoService; + + @Override + public void run(String... arg0) throws Exception { + User user = new User("Jack", "Admin"); + //New User is created : User [id=1, name=Jack, role=Admin] + long insert = userDaoService.insert(user); + log.info("New User is created : " + user); + } +} +``` +--- + +### /src/main/java/com/in28minutes/learning/jpa/jpain10steps/UserRepositoryCommandLineRunner.java + +```java +package com.in28minutes.learning.jpa.jpain10steps; + +import java.util.List; +import java.util.Optional; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.CommandLineRunner; +import org.springframework.stereotype.Component; + +import com.in28minutes.learning.jpa.jpain10steps.entity.User; +import com.in28minutes.learning.jpa.jpain10steps.service.UserRepository; + +@Component +public class UserRepositoryCommandLineRunner implements CommandLineRunner{ + + private static final Logger log = + LoggerFactory.getLogger(UserRepositoryCommandLineRunner.class); + + @Autowired + private UserRepository userRepository; + + @Override + public void run(String... arg0) throws Exception { + User user = new User("Jill", "Admin"); + userRepository.save(user); + log.info("New User is created : " + user); + + Optional userWithIdOne = userRepository.findById(1L); + log.info("User is retrived : " + userWithIdOne); + + List users = userRepository.findAll(); + log.info("All Users : " + users); + } + +} +``` +--- + +### /src/main/resources/application.properties + +``` +spring.jpa.show-sql=true +spring.h2.console.enabled=true +logging.level.org.springframework=debug +``` +--- + +### /src/test/java/com/in28minutes/learning/jpa/jpain10steps/JpaIn10StepsApplicationTests.java + +```java +package com.in28minutes.learning.jpa.jpain10steps; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; + +@RunWith(SpringRunner.class) +@SpringBootTest +public class JpaIn10StepsApplicationTests { + + @Test + public void contextLoads() { + } + +} +``` +--- + +## Complete Code Example + + +### /example.sh + +``` +java -cp /ProgrammingExcellence/Workspaces/Rithus.com/ListDirectoryContentInGitFormat/bin test.ListDirectoryContentInGitFormat >> $1.md +git add *; git commit -m "$1"; git push; +zip -r $1.zip . -x "target/*" -x ".*/*" -x ".*" -x "*.md" -x "mvn*" -x "*.zip" +``` +--- + +### /notes.txt + +``` +Questions +- Where is the database created? +- What schema is used to create the tables? +- Where are the tables created? +- Can I see the data in the database? +- Where is Hibernate coming in from? +- How is a datasource created? + +Magic of Spring Boot and in Memory Database +- Zero project setup or infrastructure +- Zero Configuration +- Zero Maintainance +- Easy to use for Learning and Unit Tests +- Simple Configuration to switch to a real database + +# Restrictions of using in-memory database +- Data is not persisted between restarts + +Spring Boot chooses a default value for you based on whether it thinks your database is embedded (default create-drop) or not (default none). + +HibernateJpaAutoConfiguration matched: + - @ConditionalOnClass found required classes 'org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean', 'javax.persistence.EntityManager'; @ConditionalOnMissingClass did not find unwanted class (OnClassCondition) + - HibernateEntityManager found class 'org.hibernate.ejb.HibernateEntityManager' (HibernateJpaAutoConfiguration.HibernateEntityManagerCondition) + +DataSourceAutoConfiguration matched: + - @ConditionalOnClass found required classes 'javax.sql.DataSource', 'org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType'; @ConditionalOnMissingClass did not find unwanted class (OnClassCondition) + +JpaBaseConfiguration#entityManagerFactory matched: + - @ConditionalOnMissingBean (types: org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean,javax.persistence.EntityManagerFactory; SearchStrategy: all) did not find any beans (OnBeanCondition) + +JpaBaseConfiguration#transactionManager matched: + - @ConditionalOnMissingBean (types: org.springframework.transaction.PlatformTransactionManager; SearchStrategy: all) did not find any beans (OnBeanCondition) +``` +--- + +### /pom.xml + +```xml + + + 4.0.0 + + com.in28minutes.learning.jpa + jpa-in-10-steps + 0.0.1-SNAPSHOT + jar + + jpa-in-10-steps + Demo project for Spring Boot + + + org.springframework.boot + spring-boot-starter-parent + 2.0.0.BUILD-SNAPSHOT + + + + + UTF-8 + UTF-8 + 1.8 + + + + + + org.springframework.boot + spring-boot-starter-data-jpa + + + + org.springframework.boot + spring-boot-starter-web + + + + com.h2database + h2 + runtime + + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + + + spring-snapshots + Spring Snapshots + https://repo.spring.io/snapshot + + true + + + + spring-milestones + Spring Milestones + https://repo.spring.io/milestone + + false + + + + + + + spring-snapshots + Spring Snapshots + https://repo.spring.io/snapshot + + true + + + + spring-milestones + Spring Milestones + https://repo.spring.io/milestone + + false + + + + + + +``` +--- + +### /src/main/java/com/in28minutes/learning/jpa/jpain10steps/entity/User.java + +```java +package com.in28minutes.learning.jpa.jpain10steps.entity; + +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; + +//Table - User +@Entity +public class User { + + @Id + @GeneratedValue + private long id; + + private String name; + + private String role; + + protected User() { + + } + + public User(String name, String role) { + super(); + this.name = name; + this.role = role; + } + + public long getId() { + return id; + } + + public String getName() { + return name; + } + + public String getRole() { + return role; + } + + @Override + public String toString() { + return String.format("User [id=%s, name=%s, role=%s]", id, name, role); + } +} +``` +--- + +### /src/main/java/com/in28minutes/learning/jpa/jpain10steps/JpaIn10StepsApplication.java + +```java +package com.in28minutes.learning.jpa.jpain10steps; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class JpaIn10StepsApplication { + + public static void main(String[] args) { + SpringApplication.run(JpaIn10StepsApplication.class, args); + } +} +``` +--- + +### /src/main/java/com/in28minutes/learning/jpa/jpain10steps/service/UserDAOService.java + +```java +package com.in28minutes.learning.jpa.jpain10steps.service; + +import javax.persistence.EntityManager; +import javax.persistence.PersistenceContext; +import javax.transaction.Transactional; + +import org.springframework.stereotype.Repository; + +import com.in28minutes.learning.jpa.jpain10steps.entity.User; + +@Repository +@Transactional +public class UserDAOService { + + @PersistenceContext + private EntityManager entityManager; + + public long insert(User user){ + entityManager.persist(user); + return user.getId(); + } +} + +/* + * Spring Data JPA + * + * + * + */ +``` +--- + +### /src/main/java/com/in28minutes/learning/jpa/jpain10steps/service/UserRepository.java + +```java +package com.in28minutes.learning.jpa.jpain10steps.service; + +import org.springframework.data.jpa.repository.JpaRepository; + +import com.in28minutes.learning.jpa.jpain10steps.entity.User; + +public interface UserRepository extends JpaRepository{ + +} +``` +--- + +### /src/main/java/com/in28minutes/learning/jpa/jpain10steps/UserDaoServiceCommandLineRunner.java + +```java +package com.in28minutes.learning.jpa.jpain10steps; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.CommandLineRunner; +import org.springframework.stereotype.Component; + +import com.in28minutes.learning.jpa.jpain10steps.entity.User; +import com.in28minutes.learning.jpa.jpain10steps.service.UserDAOService; + +@Component +public class UserDaoServiceCommandLineRunner implements CommandLineRunner{ + + private static final Logger log = + LoggerFactory.getLogger(UserDaoServiceCommandLineRunner.class); + + @Autowired + private UserDAOService userDaoService; + + @Override + public void run(String... arg0) throws Exception { + User user = new User("Jack", "Admin"); + //New User is created : User [id=1, name=Jack, role=Admin] + long insert = userDaoService.insert(user); + log.info("New User is created : " + user); + } +} +``` +--- + +### /src/main/java/com/in28minutes/learning/jpa/jpain10steps/UserRepositoryCommandLineRunner.java + +```java +package com.in28minutes.learning.jpa.jpain10steps; + +import java.util.List; +import java.util.Optional; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.CommandLineRunner; +import org.springframework.stereotype.Component; + +import com.in28minutes.learning.jpa.jpain10steps.entity.User; +import com.in28minutes.learning.jpa.jpain10steps.service.UserRepository; + +@Component +public class UserRepositoryCommandLineRunner implements CommandLineRunner{ + + private static final Logger log = + LoggerFactory.getLogger(UserRepositoryCommandLineRunner.class); + + @Autowired + private UserRepository userRepository; + + @Override + public void run(String... arg0) throws Exception { + User user = new User("Jill", "Admin"); + userRepository.save(user); + log.info("New User is created : " + user); + + Optional userWithIdOne = userRepository.findById(1L); + log.info("User is retrived : " + userWithIdOne); + + List users = userRepository.findAll(); + log.info("All Users : " + users); + } + +} +``` +--- + +### /src/main/resources/application.properties + +``` +spring.jpa.show-sql=true +spring.h2.console.enabled=true +logging.level.org.springframework=debug +``` +--- + +### /src/test/java/com/in28minutes/learning/jpa/jpain10steps/JpaIn10StepsApplicationTests.java + +```java +package com.in28minutes.learning.jpa.jpain10steps; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; + +@RunWith(SpringRunner.class) +@SpringBootTest +public class JpaIn10StepsApplicationTests { + + @Test + public void contextLoads() { + } + +} +``` +--- diff --git a/06.JPA-Introduction-In-10-Steps/step21.zip b/06.JPA-Introduction-In-10-Steps/step21.zip new file mode 100644 index 0000000000000000000000000000000000000000..539a5bae6502621da7eac02718d366c7e919bb7a GIT binary patch literal 10214 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 $1.md +zip -r $1.zip . -x "target/*" -x ".*/*" -x ".*" -x "*.md" -x "mvn*" -x "*.zip" +git add *; git commit -m "$1"; git push; +``` +--- + +### /notes.txt + +``` +Questions +- Where is the database created? +- What schema is used to create the tables? +- Where are the tables created? +- Can I see the data in the database? +- Where is Hibernate coming in from? +- How is a datasource created? + +Magic of Spring Boot and in Memory Database +- Zero project setup or infrastructure +- Zero Configuration +- Zero Maintainance +- Easy to use for Learning and Unit Tests +- Simple Configuration to switch to a real database + +# Restrictions of using in-memory database +- Data is not persisted between restarts + +Spring Boot chooses a default value for you based on whether it thinks your database is embedded (default create-drop) or not (default none). + +HibernateJpaAutoConfiguration matched: + - @ConditionalOnClass found required classes 'org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean', 'javax.persistence.EntityManager'; @ConditionalOnMissingClass did not find unwanted class (OnClassCondition) + - HibernateEntityManager found class 'org.hibernate.ejb.HibernateEntityManager' (HibernateJpaAutoConfiguration.HibernateEntityManagerCondition) + +DataSourceAutoConfiguration matched: + - @ConditionalOnClass found required classes 'javax.sql.DataSource', 'org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType'; @ConditionalOnMissingClass did not find unwanted class (OnClassCondition) + +JpaBaseConfiguration#entityManagerFactory matched: + - @ConditionalOnMissingBean (types: org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean,javax.persistence.EntityManagerFactory; SearchStrategy: all) did not find any beans (OnBeanCondition) + +JpaBaseConfiguration#transactionManager matched: + - @ConditionalOnMissingBean (types: org.springframework.transaction.PlatformTransactionManager; SearchStrategy: all) did not find any beans (OnBeanCondition) +``` +--- + +### /pom.xml + +```xml + + + 4.0.0 + + com.in28minutes.learning.jpa + jpa-in-10-steps + 0.0.1-SNAPSHOT + jar + + jpa-in-10-steps + Demo project for Spring Boot + + + org.springframework.boot + spring-boot-starter-parent + 2.0.0.BUILD-SNAPSHOT + + + + + UTF-8 + UTF-8 + 1.8 + + + + + + org.springframework.boot + spring-boot-starter-data-jpa + + + + org.springframework.boot + spring-boot-starter-web + + + + com.h2database + h2 + runtime + + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + + + spring-snapshots + Spring Snapshots + https://repo.spring.io/snapshot + + true + + + + spring-milestones + Spring Milestones + https://repo.spring.io/milestone + + false + + + + + + + spring-snapshots + Spring Snapshots + https://repo.spring.io/snapshot + + true + + + + spring-milestones + Spring Milestones + https://repo.spring.io/milestone + + false + + + + + + +``` +--- + +### /src/main/java/com/in28minutes/learning/jpa/jpain10steps/entity/User.java + +```java +package com.in28minutes.learning.jpa.jpain10steps.entity; + +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; + +//Table - User +@Entity +public class User { + + @Id + @GeneratedValue + private long id; + + private String name; + + private String role; + + protected User() { + + } + + public User(String name, String role) { + super(); + this.name = name; + this.role = role; + } + + public long getId() { + return id; + } + + public String getName() { + return name; + } + + public String getRole() { + return role; + } + + @Override + public String toString() { + return String.format("User [id=%s, name=%s, role=%s]", id, name, role); + } +} +``` +--- + +### /src/main/java/com/in28minutes/learning/jpa/jpain10steps/JpaIn10StepsApplication.java + +```java +package com.in28minutes.learning.jpa.jpain10steps; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class JpaIn10StepsApplication { + + public static void main(String[] args) { + SpringApplication.run(JpaIn10StepsApplication.class, args); + } +} +``` +--- + +### /src/main/java/com/in28minutes/learning/jpa/jpain10steps/service/UserDAOService.java + +```java +package com.in28minutes.learning.jpa.jpain10steps.service; + +import javax.persistence.EntityManager; +import javax.persistence.PersistenceContext; +import javax.transaction.Transactional; + +import org.springframework.stereotype.Repository; + +import com.in28minutes.learning.jpa.jpain10steps.entity.User; + +@Repository +@Transactional +public class UserDAOService { + + @PersistenceContext + private EntityManager entityManager; + + public long insert(User user){ + entityManager.persist(user); + return user.getId(); + } +} + +/* + * Spring Data JPA + * + * + * + */ +``` +--- + +### /src/main/java/com/in28minutes/learning/jpa/jpain10steps/service/UserRepository.java + +```java +package com.in28minutes.learning.jpa.jpain10steps.service; + +import org.springframework.data.jpa.repository.JpaRepository; + +import com.in28minutes.learning.jpa.jpain10steps.entity.User; + +public interface UserRepository extends JpaRepository{ + +} +``` +--- + +### /src/main/java/com/in28minutes/learning/jpa/jpain10steps/UserDaoServiceCommandLineRunner.java + +```java +package com.in28minutes.learning.jpa.jpain10steps; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.CommandLineRunner; +import org.springframework.stereotype.Component; + +import com.in28minutes.learning.jpa.jpain10steps.entity.User; +import com.in28minutes.learning.jpa.jpain10steps.service.UserDAOService; + +@Component +public class UserDaoServiceCommandLineRunner implements CommandLineRunner{ + + private static final Logger log = + LoggerFactory.getLogger(UserDaoServiceCommandLineRunner.class); + + @Autowired + private UserDAOService userDaoService; + + @Override + public void run(String... arg0) throws Exception { + User user = new User("Jack", "Admin"); + //New User is created : User [id=1, name=Jack, role=Admin] + long insert = userDaoService.insert(user); + log.info("New User is created : " + user); + } +} +``` +--- + +### /src/main/java/com/in28minutes/learning/jpa/jpain10steps/UserRepositoryCommandLineRunner.java + +```java +package com.in28minutes.learning.jpa.jpain10steps; + +import java.util.List; +import java.util.Optional; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.CommandLineRunner; +import org.springframework.stereotype.Component; + +import com.in28minutes.learning.jpa.jpain10steps.entity.User; +import com.in28minutes.learning.jpa.jpain10steps.service.UserRepository; + +@Component +public class UserRepositoryCommandLineRunner implements CommandLineRunner{ + + private static final Logger log = + LoggerFactory.getLogger(UserRepositoryCommandLineRunner.class); + + @Autowired + private UserRepository userRepository; + + @Override + public void run(String... arg0) throws Exception { + User user = new User("Jill", "Admin"); + userRepository.save(user); + log.info("New User is created : " + user); + + Optional userWithIdOne = userRepository.findById(1L); + log.info("User is retrived : " + userWithIdOne); + + List users = userRepository.findAll(); + log.info("All Users : " + users); + } + +} +``` +--- + +### /src/main/resources/application.properties + +``` +spring.jpa.show-sql=true +spring.h2.console.enabled=true +logging.level.org.springframework=debug +``` +--- + +### /src/test/java/com/in28minutes/learning/jpa/jpain10steps/JpaIn10StepsApplicationTests.java + +```java +package com.in28minutes.learning.jpa.jpain10steps; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; + +@RunWith(SpringRunner.class) +@SpringBootTest +public class JpaIn10StepsApplicationTests { + + @Test + public void contextLoads() { + } + +} +``` +--- diff --git a/06.JPA-Introduction-In-10-Steps/step22.zip b/06.JPA-Introduction-In-10-Steps/step22.zip new file mode 100644 index 0000000000000000000000000000000000000000..97c8f12c27b3e24f31c0d9689f3661524dbdcc2d GIT binary patch literal 10213 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 EKPj;$PXGV_ literal 0 HcmV?d00001 diff --git a/06.JPA-Introduction-In-10-Steps/step23.md b/06.JPA-Introduction-In-10-Steps/step23.md new file mode 100644 index 0000000..05f7cdf --- /dev/null +++ b/06.JPA-Introduction-In-10-Steps/step23.md @@ -0,0 +1,383 @@ + +## Complete Code Example + + +### /notes.txt + +``` +Questions +- Where is the database created? +- What schema is used to create the tables? +- Where are the tables created? +- Can I see the data in the database? +- Where is Hibernate coming in from? +- How is a datasource created? + +Magic of Spring Boot and in Memory Database +- Zero project setup or infrastructure +- Zero Configuration +- Zero Maintainance +- Easy to use for Learning and Unit Tests +- Simple Configuration to switch to a real database + +# Restrictions of using in-memory database +- Data is not persisted between restarts + +Spring Boot chooses a default value for you based on whether it thinks your database is embedded (default create-drop) or not (default none). + +HibernateJpaAutoConfiguration matched: + - @ConditionalOnClass found required classes 'org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean', 'javax.persistence.EntityManager'; @ConditionalOnMissingClass did not find unwanted class (OnClassCondition) + - HibernateEntityManager found class 'org.hibernate.ejb.HibernateEntityManager' (HibernateJpaAutoConfiguration.HibernateEntityManagerCondition) + +DataSourceAutoConfiguration matched: + - @ConditionalOnClass found required classes 'javax.sql.DataSource', 'org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType'; @ConditionalOnMissingClass did not find unwanted class (OnClassCondition) + +JpaBaseConfiguration#entityManagerFactory matched: + - @ConditionalOnMissingBean (types: org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean,javax.persistence.EntityManagerFactory; SearchStrategy: all) did not find any beans (OnBeanCondition) + +JpaBaseConfiguration#transactionManager matched: + - @ConditionalOnMissingBean (types: org.springframework.transaction.PlatformTransactionManager; SearchStrategy: all) did not find any beans (OnBeanCondition) +``` +--- + +### /pom.xml + +```xml + + + 4.0.0 + + com.in28minutes.learning.jpa + jpa-in-10-steps + 0.0.1-SNAPSHOT + jar + + jpa-in-10-steps + Demo project for Spring Boot + + + org.springframework.boot + spring-boot-starter-parent + 2.0.0.BUILD-SNAPSHOT + + + + + UTF-8 + UTF-8 + 1.8 + + + + + + org.springframework.boot + spring-boot-starter-data-jpa + + + + org.springframework.boot + spring-boot-starter-web + + + + com.h2database + h2 + runtime + + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + + + spring-snapshots + Spring Snapshots + https://repo.spring.io/snapshot + + true + + + + spring-milestones + Spring Milestones + https://repo.spring.io/milestone + + false + + + + + + + spring-snapshots + Spring Snapshots + https://repo.spring.io/snapshot + + true + + + + spring-milestones + Spring Milestones + https://repo.spring.io/milestone + + false + + + + + + +``` +--- + +### /src/main/java/com/in28minutes/learning/jpa/jpain10steps/entity/User.java + +```java +package com.in28minutes.learning.jpa.jpain10steps.entity; + +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; + +//Table - User +@Entity +public class User { + + @Id + @GeneratedValue + private long id; + + private String name; + + private String role; + + protected User() { + + } + + public User(String name, String role) { + super(); + this.name = name; + this.role = role; + } + + public long getId() { + return id; + } + + public String getName() { + return name; + } + + public String getRole() { + return role; + } + + @Override + public String toString() { + return String.format("User [id=%s, name=%s, role=%s]", id, name, role); + } +} +``` +--- + +### /src/main/java/com/in28minutes/learning/jpa/jpain10steps/JpaIn10StepsApplication.java + +```java +package com.in28minutes.learning.jpa.jpain10steps; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class JpaIn10StepsApplication { + + public static void main(String[] args) { + SpringApplication.run(JpaIn10StepsApplication.class, args); + } +} +``` +--- + +### /src/main/java/com/in28minutes/learning/jpa/jpain10steps/service/UserDAOService.java + +```java +package com.in28minutes.learning.jpa.jpain10steps.service; + +import javax.persistence.EntityManager; +import javax.persistence.PersistenceContext; +import javax.transaction.Transactional; + +import org.springframework.stereotype.Repository; + +import com.in28minutes.learning.jpa.jpain10steps.entity.User; + +@Repository +@Transactional +public class UserDAOService { + + @PersistenceContext + private EntityManager entityManager; + + public long insert(User user){ + entityManager.persist(user); + return user.getId(); + } +} + +/* + * Spring Data JPA + * + * + * + */ +``` +--- + +### /src/main/java/com/in28minutes/learning/jpa/jpain10steps/service/UserRepository.java + +```java +package com.in28minutes.learning.jpa.jpain10steps.service; + +import org.springframework.data.jpa.repository.JpaRepository; + +import com.in28minutes.learning.jpa.jpain10steps.entity.User; + +public interface UserRepository extends JpaRepository{ + +} +``` +--- + +### /src/main/java/com/in28minutes/learning/jpa/jpain10steps/UserDaoServiceCommandLineRunner.java + +```java +package com.in28minutes.learning.jpa.jpain10steps; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.CommandLineRunner; +import org.springframework.stereotype.Component; + +import com.in28minutes.learning.jpa.jpain10steps.entity.User; +import com.in28minutes.learning.jpa.jpain10steps.service.UserDAOService; + +@Component +public class UserDaoServiceCommandLineRunner implements CommandLineRunner{ + + private static final Logger log = + LoggerFactory.getLogger(UserDaoServiceCommandLineRunner.class); + + @Autowired + private UserDAOService userDaoService; + + @Override + public void run(String... arg0) throws Exception { + User user = new User("Jack", "Admin"); + //New User is created : User [id=1, name=Jack, role=Admin] + long insert = userDaoService.insert(user); + log.info("New User is created : " + user); + } +} +``` +--- + +### /src/main/java/com/in28minutes/learning/jpa/jpain10steps/UserRepositoryCommandLineRunner.java + +```java +package com.in28minutes.learning.jpa.jpain10steps; + +import java.util.List; +import java.util.Optional; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.CommandLineRunner; +import org.springframework.stereotype.Component; + +import com.in28minutes.learning.jpa.jpain10steps.entity.User; +import com.in28minutes.learning.jpa.jpain10steps.service.UserRepository; + +@Component +public class UserRepositoryCommandLineRunner implements CommandLineRunner{ + + private static final Logger log = + LoggerFactory.getLogger(UserRepositoryCommandLineRunner.class); + + @Autowired + private UserRepository userRepository; + + @Override + public void run(String... arg0) throws Exception { + User user = new User("Jill", "Admin"); + userRepository.save(user); + log.info("New User is created : " + user); + + Optional userWithIdOne = userRepository.findById(1L); + log.info("User is retrived : " + userWithIdOne); + + List users = userRepository.findAll(); + log.info("All Users : " + users); + } + +} +``` +--- + +### /src/main/resources/application.properties + +``` +spring.jpa.show-sql=true +spring.h2.console.enabled=true +logging.level.org.springframework=debug +``` +--- + +### /src/test/java/com/in28minutes/learning/jpa/jpain10steps/JpaIn10StepsApplicationTests.java + +```java +package com.in28minutes.learning.jpa.jpain10steps; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; + +@RunWith(SpringRunner.class) +@SpringBootTest +public class JpaIn10StepsApplicationTests { + + @Test + public void contextLoads() { + } + +} +``` +--- + +### /step-completed.sh + +``` +java -cp /ProgrammingExcellence/Workspaces/Rithus.com/ListDirectoryContentInGitFormat/bin test.ListDirectoryContentInGitFormat $PWD >> $1.md +zip -r $1.zip . -x "target/*" -x ".*/*" -x ".*" -x "*.md" -x "mvn*" -x "*.zip" +git add *; git commit -m "$1"; git push; +``` +--- diff --git a/06.JPA-Introduction-In-10-Steps/step23.zip b/06.JPA-Introduction-In-10-Steps/step23.zip new file mode 100644 index 0000000000000000000000000000000000000000..351bc9d692694e607eb1238a5331add4ca8eb926 GIT binary patch literal 10653 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 literal 0 HcmV?d00001 diff --git a/06.JPA-Introduction-In-10-Steps/take-step-backup.sh b/06.JPA-Introduction-In-10-Steps/take-step-backup.sh new file mode 100755 index 0000000..587e7d0 --- /dev/null +++ b/06.JPA-Introduction-In-10-Steps/take-step-backup.sh @@ -0,0 +1,2 @@ +java -cp /ProgrammingExcellence/Workspaces/Rithus.com/ListDirectoryContentInGitFormat/bin test.ListDirectoryContentInGitFormat $PWD >> $1.md +zip -r $1.zip . -x "target/*" -x ".*/*" -x ".*" -x "*.md" -x "mvn*" -x "*.zip" \ No newline at end of file diff --git a/09.Spring-Introduction-In-10-Steps/.gitignore b/09.Spring-Introduction-In-10-Steps/.gitignore new file mode 100644 index 0000000..2af7cef --- /dev/null +++ b/09.Spring-Introduction-In-10-Steps/.gitignore @@ -0,0 +1,24 @@ +target/ +!.mvn/wrapper/maven-wrapper.jar + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr + +### NetBeans ### +nbproject/private/ +build/ +nbbuild/ +dist/ +nbdist/ +.nb-gradle/ \ No newline at end of file diff --git a/09.Spring-Introduction-In-10-Steps/.mvn/wrapper/maven-wrapper.jar b/09.Spring-Introduction-In-10-Steps/.mvn/wrapper/maven-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..9cc84ea9b4d95453115d0c26488d6a78694e0bc6 GIT binary patch literal 47610 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 literal 0 HcmV?d00001 diff --git a/09.Spring-Introduction-In-10-Steps/.mvn/wrapper/maven-wrapper.properties b/09.Spring-Introduction-In-10-Steps/.mvn/wrapper/maven-wrapper.properties new file mode 100644 index 0000000..c315043 --- /dev/null +++ b/09.Spring-Introduction-In-10-Steps/.mvn/wrapper/maven-wrapper.properties @@ -0,0 +1 @@ +distributionUrl=https://repo1.maven.org/maven2/org/apache/maven/apache-maven/3.5.0/apache-maven-3.5.0-bin.zip diff --git a/09.Spring-Introduction-In-10-Steps/code-21July2017.zip b/09.Spring-Introduction-In-10-Steps/code-21July2017.zip new file mode 100644 index 0000000000000000000000000000000000000000..826a060a6d0f1d1b9e86e18804496b868df05bc8 GIT binary patch literal 7634 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 ZbRfZ)W&{T(O|Oarza2oHywi1Knp*$> literal 0 HcmV?d00001 diff --git a/09.Spring-Introduction-In-10-Steps/mvnw b/09.Spring-Introduction-In-10-Steps/mvnw new file mode 100755 index 0000000..5bf251c --- /dev/null +++ b/09.Spring-Introduction-In-10-Steps/mvnw @@ -0,0 +1,225 @@ +#!/bin/sh +# ---------------------------------------------------------------------------- +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# ---------------------------------------------------------------------------- + +# ---------------------------------------------------------------------------- +# Maven2 Start Up Batch script +# +# Required ENV vars: +# ------------------ +# JAVA_HOME - location of a JDK home dir +# +# Optional ENV vars +# ----------------- +# M2_HOME - location of maven2's installed home dir +# MAVEN_OPTS - parameters passed to the Java VM when running Maven +# e.g. to debug Maven itself, use +# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +# MAVEN_SKIP_RC - flag to disable loading of mavenrc files +# ---------------------------------------------------------------------------- + +if [ -z "$MAVEN_SKIP_RC" ] ; then + + if [ -f /etc/mavenrc ] ; then + . /etc/mavenrc + fi + + if [ -f "$HOME/.mavenrc" ] ; then + . "$HOME/.mavenrc" + fi + +fi + +# OS specific support. $var _must_ be set to either true or false. +cygwin=false; +darwin=false; +mingw=false +case "`uname`" in + CYGWIN*) cygwin=true ;; + MINGW*) mingw=true;; + Darwin*) darwin=true + # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home + # See https://developer.apple.com/library/mac/qa/qa1170/_index.html + if [ -z "$JAVA_HOME" ]; then + if [ -x "/usr/libexec/java_home" ]; then + export JAVA_HOME="`/usr/libexec/java_home`" + else + export JAVA_HOME="/Library/Java/Home" + fi + fi + ;; +esac + +if [ -z "$JAVA_HOME" ] ; then + if [ -r /etc/gentoo-release ] ; then + JAVA_HOME=`java-config --jre-home` + fi +fi + +if [ -z "$M2_HOME" ] ; then + ## resolve links - $0 may be a link to maven's home + PRG="$0" + + # need this for relative symlinks + while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG="`dirname "$PRG"`/$link" + fi + done + + saveddir=`pwd` + + M2_HOME=`dirname "$PRG"`/.. + + # make it fully qualified + M2_HOME=`cd "$M2_HOME" && pwd` + + cd "$saveddir" + # echo Using m2 at $M2_HOME +fi + +# For Cygwin, ensure paths are in UNIX format before anything is touched +if $cygwin ; then + [ -n "$M2_HOME" ] && + M2_HOME=`cygpath --unix "$M2_HOME"` + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --unix "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --unix "$CLASSPATH"` +fi + +# For Migwn, ensure paths are in UNIX format before anything is touched +if $mingw ; then + [ -n "$M2_HOME" ] && + M2_HOME="`(cd "$M2_HOME"; pwd)`" + [ -n "$JAVA_HOME" ] && + JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" + # TODO classpath? +fi + +if [ -z "$JAVA_HOME" ]; then + javaExecutable="`which javac`" + if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then + # readlink(1) is not available as standard on Solaris 10. + readLink=`which readlink` + if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then + if $darwin ; then + javaHome="`dirname \"$javaExecutable\"`" + javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" + else + javaExecutable="`readlink -f \"$javaExecutable\"`" + fi + javaHome="`dirname \"$javaExecutable\"`" + javaHome=`expr "$javaHome" : '\(.*\)/bin'` + JAVA_HOME="$javaHome" + export JAVA_HOME + fi + fi +fi + +if [ -z "$JAVACMD" ] ; then + if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + else + JAVACMD="`which java`" + fi +fi + +if [ ! -x "$JAVACMD" ] ; then + echo "Error: JAVA_HOME is not defined correctly." >&2 + echo " We cannot execute $JAVACMD" >&2 + exit 1 +fi + +if [ -z "$JAVA_HOME" ] ; then + echo "Warning: JAVA_HOME environment variable is not set." +fi + +CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher + +# traverses directory structure from process work directory to filesystem root +# first directory with .mvn subdirectory is considered project base directory +find_maven_basedir() { + + if [ -z "$1" ] + then + echo "Path not specified to find_maven_basedir" + return 1 + fi + + basedir="$1" + wdir="$1" + while [ "$wdir" != '/' ] ; do + if [ -d "$wdir"/.mvn ] ; then + basedir=$wdir + break + fi + # workaround for JBEAP-8937 (on Solaris 10/Sparc) + if [ -d "${wdir}" ]; then + wdir=`cd "$wdir/.."; pwd` + fi + # end of workaround + done + echo "${basedir}" +} + +# concatenates all lines of a file +concat_lines() { + if [ -f "$1" ]; then + echo "$(tr -s '\n' ' ' < "$1")" + fi +} + +BASE_DIR=`find_maven_basedir "$(pwd)"` +if [ -z "$BASE_DIR" ]; then + exit 1; +fi + +export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} +echo $MAVEN_PROJECTBASEDIR +MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" + +# For Cygwin, switch paths to Windows format before running java +if $cygwin; then + [ -n "$M2_HOME" ] && + M2_HOME=`cygpath --path --windows "$M2_HOME"` + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --windows "$CLASSPATH"` + [ -n "$MAVEN_PROJECTBASEDIR" ] && + MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` +fi + +WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +exec "$JAVACMD" \ + $MAVEN_OPTS \ + -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ + "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ + ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" diff --git a/09.Spring-Introduction-In-10-Steps/mvnw.cmd b/09.Spring-Introduction-In-10-Steps/mvnw.cmd new file mode 100644 index 0000000..019bd74 --- /dev/null +++ b/09.Spring-Introduction-In-10-Steps/mvnw.cmd @@ -0,0 +1,143 @@ +@REM ---------------------------------------------------------------------------- +@REM Licensed to the Apache Software Foundation (ASF) under one +@REM or more contributor license agreements. See the NOTICE file +@REM distributed with this work for additional information +@REM regarding copyright ownership. The ASF licenses this file +@REM to you under the Apache License, Version 2.0 (the +@REM "License"); you may not use this file except in compliance +@REM with the License. You may obtain a copy of the License at +@REM +@REM http://www.apache.org/licenses/LICENSE-2.0 +@REM +@REM Unless required by applicable law or agreed to in writing, +@REM software distributed under the License is distributed on an +@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +@REM KIND, either express or implied. See the License for the +@REM specific language governing permissions and limitations +@REM under the License. +@REM ---------------------------------------------------------------------------- + +@REM ---------------------------------------------------------------------------- +@REM Maven2 Start Up Batch script +@REM +@REM Required ENV vars: +@REM JAVA_HOME - location of a JDK home dir +@REM +@REM Optional ENV vars +@REM M2_HOME - location of maven2's installed home dir +@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands +@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending +@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven +@REM e.g. to debug Maven itself, use +@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files +@REM ---------------------------------------------------------------------------- + +@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' +@echo off +@REM enable echoing my setting MAVEN_BATCH_ECHO to 'on' +@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% + +@REM set %HOME% to equivalent of $HOME +if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") + +@REM Execute a user defined script before this one +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre +@REM check for pre script, once with legacy .bat ending and once with .cmd ending +if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" +if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" +:skipRcPre + +@setlocal + +set ERROR_CODE=0 + +@REM To isolate internal variables from possible post scripts, we use another setlocal +@setlocal + +@REM ==== START VALIDATION ==== +if not "%JAVA_HOME%" == "" goto OkJHome + +echo. +echo Error: JAVA_HOME not found in your environment. >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +:OkJHome +if exist "%JAVA_HOME%\bin\java.exe" goto init + +echo. +echo Error: JAVA_HOME is set to an invalid directory. >&2 +echo JAVA_HOME = "%JAVA_HOME%" >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +@REM ==== END VALIDATION ==== + +:init + +@REM Find the project base dir, i.e. the directory that contains the folder ".mvn". +@REM Fallback to current working directory if not found. + +set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% +IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir + +set EXEC_DIR=%CD% +set WDIR=%EXEC_DIR% +:findBaseDir +IF EXIST "%WDIR%"\.mvn goto baseDirFound +cd .. +IF "%WDIR%"=="%CD%" goto baseDirNotFound +set WDIR=%CD% +goto findBaseDir + +:baseDirFound +set MAVEN_PROJECTBASEDIR=%WDIR% +cd "%EXEC_DIR%" +goto endDetectBaseDir + +:baseDirNotFound +set MAVEN_PROJECTBASEDIR=%EXEC_DIR% +cd "%EXEC_DIR%" + +:endDetectBaseDir + +IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig + +@setlocal EnableExtensions EnableDelayedExpansion +for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a +@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% + +:endReadAdditionalConfig + +SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" + +set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" +set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* +if ERRORLEVEL 1 goto error +goto end + +:error +set ERROR_CODE=1 + +:end +@endlocal & set ERROR_CODE=%ERROR_CODE% + +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost +@REM check for post script, once with legacy .bat ending and once with .cmd ending +if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" +if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" +:skipRcPost + +@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' +if "%MAVEN_BATCH_PAUSE%" == "on" pause + +if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% + +exit /B %ERROR_CODE% diff --git a/09.Spring-Introduction-In-10-Steps/pom.xml b/09.Spring-Introduction-In-10-Steps/pom.xml new file mode 100644 index 0000000..737356c --- /dev/null +++ b/09.Spring-Introduction-In-10-Steps/pom.xml @@ -0,0 +1,88 @@ + + + 4.0.0 + + com.in28minutes.spring.basics + spring-in-5-steps + 0.0.1-SNAPSHOT + jar + + spring-in-5-steps + Demo project for Spring Boot + + + org.springframework.boot + spring-boot-starter-parent + 2.0.0.BUILD-SNAPSHOT + + + + + UTF-8 + UTF-8 + 1.8 + + + + + org.springframework.boot + spring-boot-starter + + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + + + spring-snapshots + Spring Snapshots + https://repo.spring.io/snapshot + + true + + + + spring-milestones + Spring Milestones + https://repo.spring.io/milestone + + false + + + + + + + spring-snapshots + Spring Snapshots + https://repo.spring.io/snapshot + + true + + + + spring-milestones + Spring Milestones + https://repo.spring.io/milestone + + false + + + + + + diff --git a/09.Spring-Introduction-In-10-Steps/readme.md b/09.Spring-Introduction-In-10-Steps/readme.md new file mode 100644 index 0000000..a9efb4f --- /dev/null +++ b/09.Spring-Introduction-In-10-Steps/readme.md @@ -0,0 +1,265 @@ +## First 10 Steps in Spring + +- Step 1 : Setting up a Spring Project using htttp://start.spring.io +- Step 2 : Understanding Tight Coupling using the Binary Search Algorithm Example +- Step 3 : Making the Binary Search Algorithm Example Loosely Coupled +- Step 4 : Using Spring to Manage Dependencies - @Component, @Autowired +- Step 5 : What is happening in the background? +- Step 6 : Dynamic auto wiring and Troubleshooting - @Primary +- Step 7 : Constructor and Setter Injection +- Step 8 : Spring Modules +- Step 9 : Spring Projects +- Step 10 : Why is Spring Popular? + +## Complete Code Example + + +### /pom.xml + +```xml + + + 4.0.0 + + com.in28minutes.spring.basics + spring-in-5-steps + 0.0.1-SNAPSHOT + jar + + spring-in-5-steps + Demo project for Spring Boot + + + org.springframework.boot + spring-boot-starter-parent + 2.0.0.BUILD-SNAPSHOT + + + + + UTF-8 + UTF-8 + 1.8 + + + + + org.springframework.boot + spring-boot-starter + + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + + + spring-snapshots + Spring Snapshots + https://repo.spring.io/snapshot + + true + + + + spring-milestones + Spring Milestones + https://repo.spring.io/milestone + + false + + + + + + + spring-snapshots + Spring Snapshots + https://repo.spring.io/snapshot + + true + + + + spring-milestones + Spring Milestones + https://repo.spring.io/milestone + + false + + + + + + +``` +--- + +### /src/main/java/com/in28minutes/spring/basics/springin5steps/BinarySearchImpl.java + +```java +package com.in28minutes.spring.basics.springin5steps; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +@Component +public class BinarySearchImpl { + + @Autowired + private SortAlgorithm sortAlgorithm; + + public int binarySearch(int[] numbers, int numberToSearchFor) { + + int[] sortedNumbers = sortAlgorithm.sort(numbers); + System.out.println(sortAlgorithm); + // Search the array + return 3; + } + +} +``` +--- + +### /src/main/java/com/in28minutes/spring/basics/springin5steps/BubbleSortAlgorithm.java + +```java +package com.in28minutes.spring.basics.springin5steps; + +import org.springframework.context.annotation.Primary; +import org.springframework.stereotype.Component; + +@Component +@Primary +public class BubbleSortAlgorithm implements SortAlgorithm { + public int[] sort(int[] numbers) { + // Logic for Bubble Sort + return numbers; + } +} +``` +--- + +### /src/main/java/com/in28minutes/spring/basics/springin5steps/QuickSortAlgorithm.java + +```java +package com.in28minutes.spring.basics.springin5steps; + +import org.springframework.stereotype.Component; + +@Component +public class QuickSortAlgorithm implements SortAlgorithm { + public int[] sort(int[] numbers) { + // Logic for Quick Sort + return numbers; + } +} +``` +--- + +### /src/main/java/com/in28minutes/spring/basics/springin5steps/SortAlgorithm.java + +```java +package com.in28minutes.spring.basics.springin5steps; + +public interface SortAlgorithm { + public int[] sort(int[] numbers); +} +``` +--- + +### /src/main/java/com/in28minutes/spring/basics/springin5steps/SpringIn5StepsApplication.java + +```java +package com.in28minutes.spring.basics.springin5steps; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.ApplicationContext; + +@SpringBootApplication +public class SpringIn5StepsApplication { + + // What are the beans? + // What are the dependencies of a bean? + // Where to search for beans? => No need + + public static void main(String[] args) { + + // BinarySearchImpl binarySearch = + // new BinarySearchImpl(new QuickSortAlgorithm()); + // Application Context + ApplicationContext applicationContext = + SpringApplication.run(SpringIn5StepsApplication.class, args); + BinarySearchImpl binarySearch = + applicationContext.getBean(BinarySearchImpl.class); + int result = + binarySearch.binarySearch(new int[] { 12, 4, 6 }, 3); + System.out.println(result); + } +} +``` +--- + +### /src/main/resources/application.properties + +```properties +logging.level.org.springframework = debug +``` +--- + +### /src/main/resources/log.txt + +``` +Searching directory [/in28Minutes/git/getting-started-in-5-steps/spring-in-5-steps/target/classes/com/in28minutes/spring/basics/springin5steps] for files matching pattern [/in28Minutes/git/getting-started-in-5-steps/spring-in-5-steps/target/classes/com/in28minutes/spring/basics/springin5steps/**/*.class] +Identified candidate component class: file [/in28Minutes/git/getting-started-in-5-steps/spring-in-5-steps/target/classes/com/in28minutes/spring/basics/springin5steps/BinarySearchImpl.class] +Identified candidate component class: file [/in28Minutes/git/getting-started-in-5-steps/spring-in-5-steps/target/classes/com/in28minutes/spring/basics/springin5steps/BubbleSortAlgorithm.class] + +Creating instance of bean 'binarySearchImpl' +Creating instance of bean 'bubbleSortAlgorithm' +Finished creating instance of bean 'bubbleSortAlgorithm' + +Constuctor - Autowiring by type from bean name 'binarySearchImpl' via constructor +to bean named 'bubbleSortAlgorithm' +Setter - Autowiring by type from bean name 'binarySearchImpl' to bean named 'bubbleSortAlgorithm' +No Setter or Constructor - Autowiring by type from bean name 'binarySearchImpl' to bean named 'bubbleSortAlgorithm' + + +Finished creating instance of bean 'binarySearchImpl' +``` +--- + +### /src/test/java/com/in28minutes/spring/basics/springin5steps/SpringIn5StepsApplicationTests.java + +```java +package com.in28minutes.spring.basics.springin5steps; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; + +@RunWith(SpringRunner.class) +@SpringBootTest +public class SpringIn5StepsApplicationTests { + + @Test + public void contextLoads() { + } + +} +``` +--- diff --git a/09.Spring-Introduction-In-10-Steps/src/main/java/com/in28minutes/spring/basics/springin5steps/BinarySearchImpl.java b/09.Spring-Introduction-In-10-Steps/src/main/java/com/in28minutes/spring/basics/springin5steps/BinarySearchImpl.java new file mode 100644 index 0000000..b3e2e57 --- /dev/null +++ b/09.Spring-Introduction-In-10-Steps/src/main/java/com/in28minutes/spring/basics/springin5steps/BinarySearchImpl.java @@ -0,0 +1,20 @@ +package com.in28minutes.spring.basics.springin5steps; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +@Component +public class BinarySearchImpl { + + @Autowired + private SortAlgorithm sortAlgorithm; + + public int binarySearch(int[] numbers, int numberToSearchFor) { + + int[] sortedNumbers = sortAlgorithm.sort(numbers); + System.out.println(sortAlgorithm); + // Search the array + return 3; + } + +} diff --git a/09.Spring-Introduction-In-10-Steps/src/main/java/com/in28minutes/spring/basics/springin5steps/BubbleSortAlgorithm.java b/09.Spring-Introduction-In-10-Steps/src/main/java/com/in28minutes/spring/basics/springin5steps/BubbleSortAlgorithm.java new file mode 100644 index 0000000..66bb71e --- /dev/null +++ b/09.Spring-Introduction-In-10-Steps/src/main/java/com/in28minutes/spring/basics/springin5steps/BubbleSortAlgorithm.java @@ -0,0 +1,13 @@ +package com.in28minutes.spring.basics.springin5steps; + +import org.springframework.context.annotation.Primary; +import org.springframework.stereotype.Component; + +@Component +@Primary +public class BubbleSortAlgorithm implements SortAlgorithm { + public int[] sort(int[] numbers) { + // Logic for Bubble Sort + return numbers; + } +} diff --git a/09.Spring-Introduction-In-10-Steps/src/main/java/com/in28minutes/spring/basics/springin5steps/QuickSortAlgorithm.java b/09.Spring-Introduction-In-10-Steps/src/main/java/com/in28minutes/spring/basics/springin5steps/QuickSortAlgorithm.java new file mode 100644 index 0000000..b495569 --- /dev/null +++ b/09.Spring-Introduction-In-10-Steps/src/main/java/com/in28minutes/spring/basics/springin5steps/QuickSortAlgorithm.java @@ -0,0 +1,11 @@ +package com.in28minutes.spring.basics.springin5steps; + +import org.springframework.stereotype.Component; + +@Component +public class QuickSortAlgorithm implements SortAlgorithm { + public int[] sort(int[] numbers) { + // Logic for Quick Sort + return numbers; + } +} diff --git a/09.Spring-Introduction-In-10-Steps/src/main/java/com/in28minutes/spring/basics/springin5steps/SortAlgorithm.java b/09.Spring-Introduction-In-10-Steps/src/main/java/com/in28minutes/spring/basics/springin5steps/SortAlgorithm.java new file mode 100644 index 0000000..cb49da6 --- /dev/null +++ b/09.Spring-Introduction-In-10-Steps/src/main/java/com/in28minutes/spring/basics/springin5steps/SortAlgorithm.java @@ -0,0 +1,5 @@ +package com.in28minutes.spring.basics.springin5steps; + +public interface SortAlgorithm { + public int[] sort(int[] numbers); +} diff --git a/09.Spring-Introduction-In-10-Steps/src/main/java/com/in28minutes/spring/basics/springin5steps/SpringIn5StepsApplication.java b/09.Spring-Introduction-In-10-Steps/src/main/java/com/in28minutes/spring/basics/springin5steps/SpringIn5StepsApplication.java new file mode 100644 index 0000000..cbcd806 --- /dev/null +++ b/09.Spring-Introduction-In-10-Steps/src/main/java/com/in28minutes/spring/basics/springin5steps/SpringIn5StepsApplication.java @@ -0,0 +1,27 @@ +package com.in28minutes.spring.basics.springin5steps; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.ApplicationContext; + +@SpringBootApplication +public class SpringIn5StepsApplication { + + // What are the beans? + // What are the dependencies of a bean? + // Where to search for beans? => No need + + public static void main(String[] args) { + + // BinarySearchImpl binarySearch = + // new BinarySearchImpl(new QuickSortAlgorithm()); + // Application Context + ApplicationContext applicationContext = + SpringApplication.run(SpringIn5StepsApplication.class, args); + BinarySearchImpl binarySearch = + applicationContext.getBean(BinarySearchImpl.class); + int result = + binarySearch.binarySearch(new int[] { 12, 4, 6 }, 3); + System.out.println(result); + } +} \ No newline at end of file diff --git a/09.Spring-Introduction-In-10-Steps/src/main/resources/application.properties b/09.Spring-Introduction-In-10-Steps/src/main/resources/application.properties new file mode 100644 index 0000000..35c0162 --- /dev/null +++ b/09.Spring-Introduction-In-10-Steps/src/main/resources/application.properties @@ -0,0 +1 @@ +logging.level.org.springframework = debug \ No newline at end of file diff --git a/09.Spring-Introduction-In-10-Steps/src/main/resources/log.txt b/09.Spring-Introduction-In-10-Steps/src/main/resources/log.txt new file mode 100644 index 0000000..bd755b2 --- /dev/null +++ b/09.Spring-Introduction-In-10-Steps/src/main/resources/log.txt @@ -0,0 +1,15 @@ +Searching directory [/in28Minutes/git/getting-started-in-5-steps/spring-in-5-steps/target/classes/com/in28minutes/spring/basics/springin5steps] for files matching pattern [/in28Minutes/git/getting-started-in-5-steps/spring-in-5-steps/target/classes/com/in28minutes/spring/basics/springin5steps/**/*.class] +Identified candidate component class: file [/in28Minutes/git/getting-started-in-5-steps/spring-in-5-steps/target/classes/com/in28minutes/spring/basics/springin5steps/BinarySearchImpl.class] +Identified candidate component class: file [/in28Minutes/git/getting-started-in-5-steps/spring-in-5-steps/target/classes/com/in28minutes/spring/basics/springin5steps/BubbleSortAlgorithm.class] + +Creating instance of bean 'binarySearchImpl' +Creating instance of bean 'bubbleSortAlgorithm' +Finished creating instance of bean 'bubbleSortAlgorithm' + +Constuctor - Autowiring by type from bean name 'binarySearchImpl' via constructor +to bean named 'bubbleSortAlgorithm' +Setter - Autowiring by type from bean name 'binarySearchImpl' to bean named 'bubbleSortAlgorithm' +No Setter or Constructor - Autowiring by type from bean name 'binarySearchImpl' to bean named 'bubbleSortAlgorithm' + + +Finished creating instance of bean 'binarySearchImpl' diff --git a/09.Spring-Introduction-In-10-Steps/src/test/java/com/in28minutes/spring/basics/springin5steps/SpringIn5StepsApplicationTests.java b/09.Spring-Introduction-In-10-Steps/src/test/java/com/in28minutes/spring/basics/springin5steps/SpringIn5StepsApplicationTests.java new file mode 100644 index 0000000..c5e11af --- /dev/null +++ b/09.Spring-Introduction-In-10-Steps/src/test/java/com/in28minutes/spring/basics/springin5steps/SpringIn5StepsApplicationTests.java @@ -0,0 +1,16 @@ +package com.in28minutes.spring.basics.springin5steps; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; + +@RunWith(SpringRunner.class) +@SpringBootTest +public class SpringIn5StepsApplicationTests { + + @Test + public void contextLoads() { + } + +} diff --git a/README.md b/README.md index 132b9b5..999d6eb 100644 --- a/README.md +++ b/README.md @@ -124,7 +124,7 @@ With Microservices, focus is shifting to getting started with developing web app ### Other Courses -- [Most Watched Courses on YouTube - 30,000 Subscribers](https://www.youtube.com/watch?v=bNFoN956P2A&list=PLBBog2r6uMCQhZaQ9vUT5zJWXzz-f49k1) +- [Check out all our courses with 100,000 Students](https://courses.in28minutes.com/courses) - [25 Videos and Articles for Beginners on Spring Boot](http://www.springboottutorial.com/spring-boot-tutorials-for-beginners) - Our Best Courses with 66,000 Students and 4,000 5-Star Ratings * [Java Interview Guide : 200+ Interview Questions and Answers](https://www.udemy.com/java-interview-questions-and-answers/?couponCode=JAVA_INTER_GIT) diff --git a/Step24.md b/Step24.md index c06afb0..e6f90b9 100644 --- a/Step24.md +++ b/Step24.md @@ -5,7 +5,7 @@ - MockMvc framework - @MockBean - Programming Tip - - Be an expert at Mockito - https://www.youtube.com/watch?v=d2KwvXQgQx4 + - Be an expert at Mockito - https://courses.in28minutes.com/p/mockito-for-beginner-in-5-steps ## Useful Snippets and References First Snippet