├── .gitignore ├── README.md ├── Step01.md ├── Step02.md ├── Step03.md ├── Step04.md ├── Step05.md ├── Step06.md ├── Step07.md ├── Step07.zip ├── Step08.md ├── Step08.zip ├── Step09.md ├── Step10.md ├── Step11.md ├── Step12.md ├── Step12.zip ├── Step13.md ├── Step13.zip ├── Step14.md ├── Step15.md ├── Step15.zip ├── Step16.md ├── Step16.zip ├── Step17.md ├── Step18.md ├── Step18.zip ├── Step19.md ├── Step19.zip ├── Step20.md ├── Step20.zip ├── Step21.md ├── Step21.zip ├── Step22.md ├── Step22.zip ├── Step23.md ├── Step23.zip ├── Step24.md ├── Step25.md ├── Step25.zip ├── Step26.md ├── Step27.md ├── Step27.zip ├── StepReference.md ├── pom.xml └── src ├── main ├── java │ └── com │ │ └── in28minutes │ │ └── springboot │ │ └── web │ │ ├── SpringBootFirstWebApplication.java │ │ ├── controller │ │ ├── ErrorController.java │ │ ├── LogoutController.java │ │ ├── TodoController.java │ │ └── WelcomeController.java │ │ ├── model │ │ └── Todo.java │ │ ├── security │ │ └── SecurityConfiguration.java │ │ └── service │ │ └── TodoService.java ├── resources │ └── application.properties └── webapp │ └── WEB-INF │ └── jsp │ ├── common │ ├── footer.jspf │ ├── header.jspf │ └── navigation.jspf │ ├── error.jsp │ ├── list-todos.jsp │ ├── todo.jsp │ └── welcome.jsp └── test └── java └── com └── in28minutes └── springboot └── web └── SpringBootFirstWebApplicationTests.java /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled class file 2 | *.class 3 | 4 | # Log file 5 | *.log 6 | 7 | # BlueJ files 8 | *.ctxt 9 | 10 | # Mobile Tools for Java (J2ME) 11 | .mtj.tmp/ 12 | 13 | # Package Files # 14 | *.jar 15 | *.war 16 | *.ear 17 | *.zip 18 | *.tar.gz 19 | *.rar 20 | *.cmd 21 | *.classpath 22 | *.settings 23 | *.project 24 | *.mvn 25 | mvnw 26 | target 27 | *.DS_Store 28 | 29 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 30 | hs_err_pid* 31 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Your First Web Application with Spring Boot 2 | Develop your first web application with Spring Boot in more than 25 steps 3 | * [Installing Eclipse, Maven and Java](#installing-tools) 4 | * [Course Overview](#course-overview) 5 | * [About in28Minutes](#about-in28minutes) 6 | - [Our Beliefs](#our-beliefs) 7 | - [Our Approach](#our-approach) 8 | - [Find Us](#useful-links) 9 | - [Other Courses](#other-courses) 10 | 11 | ## Installing Tools 12 | - PDF : https://github.com/in28minutes/SpringIn28Minutes/blob/master/InstallationGuide-JavaEclipseAndMaven_v2.pdf 13 | - Video : https://www.youtube.com/playlist?list=PLBBog2r6uMCSmMVTW_QmDLyASBvovyAO3 14 | - GIT Repository : https://github.com/in28minutes/getting-started-in-5-steps 15 | 16 | ## Course Overview 17 | 18 | ### Introduction 19 | Developing your first Spring Boot Web Application is fun. 20 | 21 | 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. 22 | 23 | In this course, you will learn the basics developing a Basic Todo Management Application using Spring Boot with Login and Logout functionalities. 24 | 25 | 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. 26 | 27 | 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. 28 | 29 | You will learn about 30 | - Basics of Spring Boot 31 | - Basics of Autoconfiguration and Spring Boot Magic 32 | - DispatcherServlet 33 | - Basic Todo Management Application with Login/Logout 34 | - Model, Controllers, ViewResolver and Filters 35 | - Forms - DataBinding, Validation 36 | - Annotation based approach - @RequestParam, @ModelAttribute, @SessionAttributes etc 37 | - Bootstrap to style the page 38 | - Spring Security 39 | - Exception Handling 40 | 41 | ### Step Wise Details 42 | - Step 01: Basic Spring Boot Web Application Setup 43 | - Step 02: First Spring MVC Controller, @ResponseBody, @Controller 44 | - Step 03: Demystifying some of the Spring Boot magic 45 | - Step 04: Redirect to Login JSP - LoginController, @ResponseBody and View Resolver 46 | - Step 05: Show userid and password on the welcome page - ModelMap and @RequestParam 47 | - Step 06: DispatcherServlet and Spring MVC Flow 48 | - Step 07: Your First HTML form 49 | - Step 08: Add hard-coded validation of userid and password 50 | - Step 09: Magic of Spring 51 | - Step 10: Create TodoController and list-todos view. Make TodoService a @Service and inject it. 52 | - Step 11: Architecture of Web Applications 53 | - Step 12: Session vs Model vs Request - @SessionAttributes 54 | - Step 13: Add new todo 55 | - Step 14: Display Todos in a table using JSTL Tags 56 | - Step 15: Bootstrap for Page Formatting using webjars 57 | - Step 16: Let's delete a Todo 58 | - Step 17: Format Add Todo Page and Adding Basic HTML5 form validation 59 | - Step 18: Introduce JSR 349 Validations using Hibernate Validator - First Command Bean. 60 | - Step 19: Updating a todo 61 | - Step 20: Let's add a Target Date for Todo - Use initBinder to Handle Date Fields 62 | - Step 21: JSP Fragments and Navigation Bar 63 | - Step 22: Preparing for Spring Security 64 | - Step 23: Initial Spring Security Setup 65 | - Step 24: Refactor and add Logout Functionality using Spring Security 66 | - Step 25: Exception Handling 67 | 68 | --- 69 | 70 | - We do NOT interact with a Database in this Beginner’s Course. 71 | - We will be building a traditional JSP based web application in this course. 72 | 73 | --- 74 | 75 | ### Expectations 76 | - You should know Java. You should understand usage of Annotations. 77 | - You should understand the basics of Spring framework. 78 | - You are NOT expected to have any experience with Eclipse or Maven. 79 | - We will help you install Eclipse and get up and running with Maven. 80 | 81 | ## Let's have some fun 82 | - What are we waiting for? 83 | - Let's have some fun building a web application Spring Boot in 25 Steps. 84 | - I had fun creating this course and hope you would too. 85 | - Thanks for your interest in Our Course 86 | - I hope you’re as excited as I am! 87 | - If you’re ready to learn more and sign up for the course, 88 | - go ahead and hit that Enroll button, 89 | - or take a test drive by using the Free Preview feature. 90 | - See you in the course! 91 | 92 | ## Getting Started 93 | - Eclipse - https://courses.in28minutes.com/p/eclipse-tutorial-for-beginners 94 | - Maven - https://courses.in28minutes.com/p/maven-tutorial-for-beginners-in-5-steps 95 | - JUnit - https://courses.in28minutes.com/p/junit-tutorial-for-beginners 96 | - Mockito - https://courses.in28minutes.com/p/mockito-for-beginner-in-5-steps 97 | 98 | ## About in28Minutes 99 | 100 | At in28Minutes, we ask ourselves one question everyday 101 | > How do we create more amazing course experiences? 102 | > We use 80-20 Rule. We discuss 20% things used 80% of time in depth. 103 | 104 | We are creating amazing learning experiences for learning Spring Boot with AWS, Azure, GCP, Docker, Kubernetes and Full Stack. 300,000 Learners rely on our expertise. [Find out more.... ](https://github.com/in28minutes/learn#best-selling-courses) 105 | 106 | ![in28MinutesLearningRoadmap-July2019.png](https://github.com/in28minutes/in28Minutes-Course-Roadmap/raw/master/in28MinutesLearningRoadmap-July2019.png) 107 | -------------------------------------------------------------------------------- /Step01.md: -------------------------------------------------------------------------------- 1 | ## What You Will Learn during this Step: 2 | - Lets create a simple web application using Spring Boot 3 | - Lets run the Spring Boot Application 4 | - There is a lot of magic happening in here! We will take a deep dive into the magic in Step 03. 5 | 6 | ## Files List 7 | 8 | ### pom.xml 9 | 10 | ```xml 11 | 12 | 14 | 4.0.0 15 | 16 | com.in28minutes.springboot.web 17 | spring-boot-first-web-application 18 | 0.0.1-SNAPSHOT 19 | jar 20 | 21 | spring-boot-first-web-application 22 | Demo project for Spring Boot 23 | 24 | 25 | org.springframework.boot 26 | spring-boot-starter-parent 27 | 1.4.3.RELEASE 28 | 29 | 30 | 31 | 32 | UTF-8 33 | UTF-8 34 | 1.8 35 | 36 | 37 | 38 | 39 | org.springframework.boot 40 | spring-boot-starter-web 41 | 42 | 43 | org.springframework.boot 44 | spring-boot-devtools 45 | runtime 46 | 47 | 48 | org.springframework.boot 49 | spring-boot-starter-test 50 | test 51 | 52 | 53 | 54 | 55 | 56 | 57 | org.springframework.boot 58 | spring-boot-maven-plugin 59 | 60 | 61 | 62 | 63 | 64 | ``` 65 | --- 66 | ### src/main/java/com/in28minutes/springboot/web/SpringBootFirstWebApplication.java 67 | 68 | ```java 69 | package com.in28minutes.springboot.web; 70 | 71 | import org.springframework.boot.SpringApplication; 72 | import org.springframework.boot.autoconfigure.SpringBootApplication; 73 | 74 | @SpringBootApplication 75 | public class SpringBootFirstWebApplication { 76 | 77 | public static void main(String[] args) { 78 | SpringApplication.run(SpringBootFirstWebApplication.class, args); 79 | } 80 | } 81 | ``` 82 | --- 83 | ### src/main/resources/application.properties 84 | 85 | ``` 86 | ``` 87 | --- 88 | ### src/test/java/com/in28minutes/springboot/web/SpringBootFirstWebApplicationTests.java 89 | 90 | ```java 91 | package com.in28minutes.springboot.web; 92 | 93 | import org.junit.Test; 94 | import org.junit.runner.RunWith; 95 | import org.springframework.boot.test.context.SpringBootTest; 96 | import org.springframework.test.context.junit4.SpringRunner; 97 | 98 | @RunWith(SpringRunner.class) 99 | @SpringBootTest 100 | public class SpringBootFirstWebApplicationTests { 101 | 102 | @Test 103 | public void contextLoads() { 104 | } 105 | 106 | } 107 | ``` 108 | --- 109 | ### todo.txt 110 | 111 | ``` 112 | Spring Boot Starter Parent 113 | Spring Boot Starter Web 114 | @SpringBootApplication 115 | Auto Configuration 116 | ``` 117 | --- 118 | -------------------------------------------------------------------------------- /Step02.md: -------------------------------------------------------------------------------- 1 | ## What You Will Learn during this Step: 2 | - @RequestMapping(value = "/login", method = RequestMethod.GET) 3 | - http://localhost:8080/login 4 | - Why @ResponseBody? 5 | - Important of RequestMapping method 6 | - How do web applications work? Request and Response 7 | - Browser sends Http Request to Web Server 8 | - Code in Web Server => Input:HttpRequest, Output: HttpResponse 9 | - Web Server responds with Http Response 10 | 11 | ## Useful Snippets and References 12 | First Snippet 13 | ``` 14 | @Controller 15 | public class LoginController { 16 | 17 | @RequestMapping(value = "/login") 18 | @ResponseBody 19 | public String sayHello() { 20 | return "Hello World dummy"; 21 | } 22 | 23 | } 24 | ``` 25 | ### src/main/resources/application.properties 26 | 27 | ``` 28 | logging.level.org.springframework.web: DEBUG 29 | ``` 30 | 31 | 32 | ## Exercises 33 | - Create another method with a different mapping returning some other text! 34 | 35 | ## Files List 36 | 37 | ### pom.xml 38 | 39 | ```xml 40 | 41 | 43 | 4.0.0 44 | 45 | com.in28minutes.springboot.web 46 | spring-boot-first-web-application 47 | 0.0.1-SNAPSHOT 48 | jar 49 | 50 | spring-boot-first-web-application 51 | Demo project for Spring Boot 52 | 53 | 54 | org.springframework.boot 55 | spring-boot-starter-parent 56 | 1.4.3.RELEASE 57 | 58 | 59 | 60 | 61 | UTF-8 62 | UTF-8 63 | 1.8 64 | 65 | 66 | 67 | 68 | org.springframework.boot 69 | spring-boot-starter-web 70 | 71 | 72 | org.springframework.boot 73 | spring-boot-devtools 74 | runtime 75 | 76 | 77 | org.springframework.boot 78 | spring-boot-starter-test 79 | test 80 | 81 | 82 | 83 | 84 | 85 | 86 | org.springframework.boot 87 | spring-boot-maven-plugin 88 | 89 | 90 | 91 | 92 | 93 | ``` 94 | --- 95 | ### src/main/java/com/in28minutes/springboot/web/controller/LoginController.java 96 | 97 | ```java 98 | package com.in28minutes.springboot.web.controller; 99 | 100 | import org.springframework.stereotype.Controller; 101 | import org.springframework.web.bind.annotation.RequestMapping; 102 | 103 | @Controller 104 | public class LoginController { 105 | 106 | @RequestMapping("/login") 107 | public String loginMessage(){ 108 | return "Hello World"; 109 | } 110 | } 111 | ``` 112 | --- 113 | ### src/main/java/com/in28minutes/springboot/web/SpringBootFirstWebApplication.java 114 | 115 | ```java 116 | package com.in28minutes.springboot.web; 117 | 118 | import org.springframework.boot.SpringApplication; 119 | import org.springframework.boot.autoconfigure.SpringBootApplication; 120 | 121 | @SpringBootApplication 122 | public class SpringBootFirstWebApplication { 123 | 124 | public static void main(String[] args) { 125 | SpringApplication.run(SpringBootFirstWebApplication.class, args); 126 | } 127 | } 128 | ``` 129 | --- 130 | ### src/main/resources/application.properties 131 | 132 | ``` 133 | logging.level.org.springframework.web: DEBUG 134 | ``` 135 | --- 136 | ### src/test/java/com/in28minutes/springboot/web/SpringBootFirstWebApplicationTests.java 137 | 138 | ```java 139 | package com.in28minutes.springboot.web; 140 | 141 | import org.junit.Test; 142 | import org.junit.runner.RunWith; 143 | import org.springframework.boot.test.context.SpringBootTest; 144 | import org.springframework.test.context.junit4.SpringRunner; 145 | 146 | @RunWith(SpringRunner.class) 147 | @SpringBootTest 148 | public class SpringBootFirstWebApplicationTests { 149 | 150 | @Test 151 | public void contextLoads() { 152 | } 153 | 154 | } 155 | ``` 156 | --- 157 | ### todo.txt 158 | 159 | ``` 160 | Spring Boot Starter Parent 161 | Spring Boot Starter Web 162 | @SpringBootApplication 163 | Auto Configuration 164 | ``` 165 | --- 166 | -------------------------------------------------------------------------------- /Step03.md: -------------------------------------------------------------------------------- 1 | ## What You Will Learn during this Step: 2 | - Demystifying some of the magic 3 | - Spring Boot Starter Parent 4 | - Spring Boot Starter Web 5 | - Embedded Tomcat 6 | - Dev Tools 7 | -------------------------------------------------------------------------------- /Step04.md: -------------------------------------------------------------------------------- 1 | ## What You Will Learn during this Step: 2 | - Your First JSP 3 | - There is a bit of setup before we get there! 4 | - Introduction to View Resolver 5 | 6 | ## Useful Snippets and References 7 | First Snippet - /src/main/webapp/WEB-INF/jsp/login.jsp 8 | ``` 9 | 10 | 11 | Yahoo!! 12 | 13 | 14 | My First JSP!!! 15 | 16 | 17 | ``` 18 | 19 | Second Snippet - /src/main/resources/application.properties 20 | ``` 21 | spring.mvc.view.prefix: /WEB-INF/jsp/ 22 | spring.mvc.view.suffix: .jsp 23 | logging.level.: DEBUG 24 | ``` 25 | 26 | Third Snippet : To enable jsp support in embedded tomcat server! 27 | ``` 28 | 29 | org.apache.tomcat.embed 30 | tomcat-embed-jasper 31 | provided 32 | 33 | 34 | ``` 35 | 36 | ## Exercises 37 | - Create a new jsp and a new controller method to redirect to it! 38 | - Play around! 39 | 40 | ## Files List 41 | 42 | ### pom.xml 43 | 44 | ```xml 45 | 46 | 48 | 4.0.0 49 | 50 | com.in28minutes.springboot.web 51 | spring-boot-first-web-application 52 | 0.0.1-SNAPSHOT 53 | jar 54 | 55 | spring-boot-first-web-application 56 | Demo project for Spring Boot 57 | 58 | 59 | org.springframework.boot 60 | spring-boot-starter-parent 61 | 1.4.3.RELEASE 62 | 63 | 64 | 65 | 66 | UTF-8 67 | UTF-8 68 | 1.8 69 | 70 | 71 | 72 | 73 | org.springframework.boot 74 | spring-boot-starter-web 75 | 76 | 77 | org.springframework.boot 78 | spring-boot-devtools 79 | runtime 80 | 81 | 82 | org.apache.tomcat.embed 83 | tomcat-embed-jasper 84 | provided 85 | 86 | 87 | org.springframework.boot 88 | spring-boot-starter-test 89 | test 90 | 91 | 92 | 93 | 94 | 95 | 96 | org.springframework.boot 97 | spring-boot-maven-plugin 98 | 99 | 100 | 101 | 102 | 103 | ``` 104 | --- 105 | ### src/main/java/com/in28minutes/springboot/web/controller/LoginController.java 106 | 107 | ```java 108 | package com.in28minutes.springboot.web.controller; 109 | 110 | import org.springframework.stereotype.Controller; 111 | import org.springframework.web.bind.annotation.RequestMapping; 112 | import org.springframework.web.bind.annotation.ResponseBody; 113 | 114 | @Controller 115 | public class LoginController { 116 | 117 | @RequestMapping("/login") 118 | public String loginMessage(){ 119 | return "login"; 120 | } 121 | } 122 | ``` 123 | --- 124 | ### src/main/java/com/in28minutes/springboot/web/SpringBootFirstWebApplication.java 125 | 126 | ```java 127 | package com.in28minutes.springboot.web; 128 | 129 | import org.springframework.boot.SpringApplication; 130 | import org.springframework.boot.autoconfigure.SpringBootApplication; 131 | 132 | @SpringBootApplication 133 | public class SpringBootFirstWebApplication { 134 | 135 | public static void main(String[] args) { 136 | SpringApplication.run(SpringBootFirstWebApplication.class, args); 137 | } 138 | } 139 | ``` 140 | --- 141 | ### src/main/resources/application.properties 142 | 143 | ``` 144 | spring.mvc.view.prefix=/WEB-INF/jsp/ 145 | spring.mvc.view.suffix=.jsp 146 | logging.level.org.springframework.web=DEBUG 147 | ``` 148 | --- 149 | ### src/main/webapp/WEB-INF/jsp/login.jsp 150 | 151 | ``` 152 | 153 | 154 | 155 | First Web Application 156 | 157 | 158 | 159 | My First JSP!! 160 | 161 | 162 | 163 | ``` 164 | --- 165 | ### src/test/java/com/in28minutes/springboot/web/SpringBootFirstWebApplicationTests.java 166 | 167 | ```java 168 | package com.in28minutes.springboot.web; 169 | 170 | import org.junit.Test; 171 | import org.junit.runner.RunWith; 172 | import org.springframework.boot.test.context.SpringBootTest; 173 | import org.springframework.test.context.junit4.SpringRunner; 174 | 175 | @RunWith(SpringRunner.class) 176 | @SpringBootTest 177 | public class SpringBootFirstWebApplicationTests { 178 | 179 | @Test 180 | public void contextLoads() { 181 | } 182 | 183 | } 184 | ``` 185 | --- 186 | ### todo.txt 187 | 188 | ``` 189 | Spring Boot Starter Parent 190 | Spring Boot Starter Web 191 | @SpringBootApplication 192 | Auto Configuration 193 | 194 | Dispatcher Servlet 195 | 196 | /login => "login" 197 | 198 | "login" => src/main/webapp/WEB-INF/jsp/login.jsp 199 | 200 | 201 | Search for a view named "login" 202 | 203 | 204 | 205 | /login => LoginController 206 | ``` 207 | --- 208 | -------------------------------------------------------------------------------- /Step05.md: -------------------------------------------------------------------------------- 1 | ## What You Will Learn during this Step: 2 | - You first GET Parameter. 3 | - Problem with using GET 4 | 5 | ## Snippets 6 | ``` 7 | ModelMap model 8 | model.put("name", name); 9 | My First JSP!!! My name is ${name} 10 | ``` 11 | 12 | ## Files List 13 | 14 | ### pom.xml 15 | 16 | ```xml 17 | 18 | 20 | 4.0.0 21 | 22 | com.in28minutes.springboot.web 23 | spring-boot-first-web-application 24 | 0.0.1-SNAPSHOT 25 | jar 26 | 27 | spring-boot-first-web-application 28 | Demo project for Spring Boot 29 | 30 | 31 | org.springframework.boot 32 | spring-boot-starter-parent 33 | 1.4.3.RELEASE 34 | 35 | 36 | 37 | 38 | UTF-8 39 | UTF-8 40 | 1.8 41 | 42 | 43 | 44 | 45 | org.springframework.boot 46 | spring-boot-starter-web 47 | 48 | 49 | 50 | org.apache.tomcat.embed 51 | tomcat-embed-jasper 52 | provided 53 | 54 | 55 | 56 | org.springframework.boot 57 | spring-boot-devtools 58 | runtime 59 | 60 | 61 | 62 | org.springframework.boot 63 | spring-boot-starter-test 64 | test 65 | 66 | 67 | 68 | 69 | 70 | 71 | org.springframework.boot 72 | spring-boot-maven-plugin 73 | 74 | 75 | 76 | 77 | 78 | ``` 79 | --- 80 | ### src/main/java/com/in28minutes/springboot/web/controller/LoginController.java 81 | 82 | ```java 83 | package com.in28minutes.springboot.web.controller; 84 | 85 | import org.springframework.stereotype.Controller; 86 | import org.springframework.ui.ModelMap; 87 | import org.springframework.web.bind.annotation.RequestMapping; 88 | import org.springframework.web.bind.annotation.RequestParam; 89 | 90 | @Controller 91 | public class LoginController { 92 | 93 | //Model 94 | 95 | @RequestMapping("/login") 96 | public String loginMessage(@RequestParam String name, ModelMap model){ 97 | model.put("name", name); 98 | return "login"; 99 | } 100 | } 101 | ``` 102 | --- 103 | ### src/main/java/com/in28minutes/springboot/web/SpringBootFirstWebApplication.java 104 | 105 | ```java 106 | package com.in28minutes.springboot.web; 107 | 108 | import org.springframework.boot.SpringApplication; 109 | import org.springframework.boot.autoconfigure.SpringBootApplication; 110 | 111 | @SpringBootApplication 112 | public class SpringBootFirstWebApplication { 113 | 114 | public static void main(String[] args) { 115 | SpringApplication.run(SpringBootFirstWebApplication.class, args); 116 | } 117 | } 118 | ``` 119 | --- 120 | ### src/main/resources/application.properties 121 | 122 | ``` 123 | spring.mvc.view.prefix=/WEB-INF/jsp/ 124 | spring.mvc.view.suffix=.jsp 125 | logging.level.org.springframework.web=DEBUG 126 | ``` 127 | --- 128 | ### src/main/webapp/WEB-INF/jsp/login.jsp 129 | 130 | ``` 131 | 132 | 133 | 134 | First Web Application 135 | 136 | 137 | 138 | My First JSP!! Welcome ${name}! 139 | 140 | 141 | 142 | ``` 143 | --- 144 | ### src/test/java/com/in28minutes/springboot/web/SpringBootFirstWebApplicationTests.java 145 | 146 | ```java 147 | package com.in28minutes.springboot.web; 148 | 149 | import org.junit.Test; 150 | import org.junit.runner.RunWith; 151 | import org.springframework.boot.test.context.SpringBootTest; 152 | import org.springframework.test.context.junit4.SpringRunner; 153 | 154 | @RunWith(SpringRunner.class) 155 | @SpringBootTest 156 | public class SpringBootFirstWebApplicationTests { 157 | 158 | @Test 159 | public void contextLoads() { 160 | } 161 | 162 | } 163 | ``` 164 | --- 165 | ### todo.txt 166 | 167 | ``` 168 | Spring Boot Starter Parent 169 | Spring Boot Starter Web 170 | @SpringBootApplication 171 | Auto Configuration 172 | 173 | Dispatcher Servlet 174 | 175 | /login => "login" 176 | 177 | "login" => src/main/webapp/WEB-INF/jsp/login.jsp 178 | 179 | 180 | Search for a view named "login" 181 | 182 | 183 | 184 | /login => LoginController 185 | ``` 186 | --- 187 | -------------------------------------------------------------------------------- /Step06.md: -------------------------------------------------------------------------------- 1 | ## What You Will Learn during this Step: 2 | - Understand importance of DispatcherServlet. 3 | 4 | ## Spring MVC Request Flow 5 | - DispatcherServlet receives HTTP Request. 6 | - DispatcherServlet identifies the right Controller based on the URL. 7 | - Controller executes Business Logic. 8 | - Controller returns a) Model b) View Name Back to DispatcherServlet. 9 | - DispatcherServlet identifies the correct view (ViewResolver). 10 | - DispatcherServlet makes the model available to view and executes it. 11 | - DispatcherServlet returns HTTP Response Back. 12 | - Flow : http://docs.spring.io/spring-framework/docs/2.0.8/reference/images/mvc.png 13 | -------------------------------------------------------------------------------- /Step07.md: -------------------------------------------------------------------------------- 1 | ## What You Will Learn during this Step: 2 | - Lets get the name from the user in a form 3 | 4 | ## Useful Snippets and References 5 | First Snippet 6 | ``` 7 | @RequestMapping(value = "/login", method = RequestMethod.GET) 8 | public String showLoginPage(ModelMap model, @RequestParam String name) { 9 | return "login"; 10 | } 11 | 12 | @RequestMapping(value = "/login", method = RequestMethod.POST) 13 | public String handleLogin(ModelMap model, @RequestParam String name) { 14 | model.put("name", name); 15 | return "welcome"; 16 | } 17 | 18 | ``` 19 | Second Snippet 20 | ``` 21 |
22 | Name : 23 |
24 | 25 | ``` 26 | 27 | ## Files List 28 | 29 | ### pom.xml 30 | 31 | ```xml 32 | 33 | 35 | 4.0.0 36 | 37 | com.in28minutes.springboot.web 38 | spring-boot-first-web-application 39 | 0.0.1-SNAPSHOT 40 | jar 41 | 42 | spring-boot-first-web-application 43 | Demo project for Spring Boot 44 | 45 | 46 | org.springframework.boot 47 | spring-boot-starter-parent 48 | 1.4.3.RELEASE 49 | 50 | 51 | 52 | 53 | UTF-8 54 | UTF-8 55 | 1.8 56 | 57 | 58 | 59 | 60 | org.springframework.boot 61 | spring-boot-starter-web 62 | 63 | 64 | 65 | org.apache.tomcat.embed 66 | tomcat-embed-jasper 67 | provided 68 | 69 | 70 | 71 | org.springframework.boot 72 | spring-boot-devtools 73 | runtime 74 | 75 | 76 | 77 | org.springframework.boot 78 | spring-boot-starter-test 79 | test 80 | 81 | 82 | 83 | 84 | 85 | 86 | org.springframework.boot 87 | spring-boot-maven-plugin 88 | 89 | 90 | 91 | 92 | 93 | ``` 94 | --- 95 | ### src/main/java/com/in28minutes/springboot/web/controller/LoginController.java 96 | 97 | ```java 98 | package com.in28minutes.springboot.web.controller; 99 | 100 | import org.springframework.stereotype.Controller; 101 | import org.springframework.ui.ModelMap; 102 | import org.springframework.web.bind.annotation.RequestMapping; 103 | import org.springframework.web.bind.annotation.RequestMethod; 104 | import org.springframework.web.bind.annotation.RequestParam; 105 | 106 | @Controller 107 | public class LoginController { 108 | 109 | @RequestMapping(value="/login", method = RequestMethod.GET) 110 | public String showLoginPage(ModelMap model){ 111 | return "login"; 112 | } 113 | 114 | @RequestMapping(value="/login", method = RequestMethod.POST) 115 | public String showWelcomePage(ModelMap model, @RequestParam String name){ 116 | model.put("name", name); 117 | return "welcome"; 118 | } 119 | 120 | } 121 | ``` 122 | --- 123 | ### src/main/java/com/in28minutes/springboot/web/SpringBootFirstWebApplication.java 124 | 125 | ```java 126 | package com.in28minutes.springboot.web; 127 | 128 | import org.springframework.boot.SpringApplication; 129 | import org.springframework.boot.autoconfigure.SpringBootApplication; 130 | 131 | @SpringBootApplication 132 | public class SpringBootFirstWebApplication { 133 | 134 | public static void main(String[] args) { 135 | SpringApplication.run(SpringBootFirstWebApplication.class, args); 136 | } 137 | } 138 | ``` 139 | --- 140 | ### src/main/resources/application.properties 141 | 142 | ``` 143 | spring.mvc.view.prefix=/WEB-INF/jsp/ 144 | spring.mvc.view.suffix=.jsp 145 | logging.level.org.springframework.web=DEBUG 146 | ``` 147 | --- 148 | ### src/main/webapp/WEB-INF/jsp/login.jsp 149 | 150 | ``` 151 | 152 | 153 | 154 | First Web Application 155 | 156 | 157 | 158 |
159 | Name : 160 | Password : 161 | 162 |
163 | 164 | 165 | 166 | ``` 167 | --- 168 | ### src/main/webapp/WEB-INF/jsp/welcome.jsp 169 | 170 | ``` 171 | 172 | 173 | 174 | First Web Application 175 | 176 | 177 | 178 | Welcome ${name}!! 179 | 180 | 181 | 182 | ``` 183 | --- 184 | ### src/test/java/com/in28minutes/springboot/web/SpringBootFirstWebApplicationTests.java 185 | 186 | ```java 187 | package com.in28minutes.springboot.web; 188 | 189 | import org.junit.Test; 190 | import org.junit.runner.RunWith; 191 | import org.springframework.boot.test.context.SpringBootTest; 192 | import org.springframework.test.context.junit4.SpringRunner; 193 | 194 | @RunWith(SpringRunner.class) 195 | @SpringBootTest 196 | public class SpringBootFirstWebApplicationTests { 197 | 198 | @Test 199 | public void contextLoads() { 200 | } 201 | 202 | } 203 | ``` 204 | --- 205 | ### todo.txt 206 | 207 | ``` 208 | Spring Boot Starter Parent 209 | Spring Boot Starter Web 210 | @SpringBootApplication 211 | Auto Configuration 212 | 213 | Dispatcher Servlet 214 | 215 | /login => "login" 216 | 217 | "login" => src/main/webapp/WEB-INF/jsp/login.jsp 218 | 219 | 220 | Search for a view named "login" 221 | 222 | 223 | 224 | /login => LoginController 225 | ``` 226 | --- 227 | -------------------------------------------------------------------------------- /Step07.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/in28minutes/SpringBootWebApplicationStepByStep/7611e5a098e49023ee03ce1446e465a4c567da45/Step07.zip -------------------------------------------------------------------------------- /Step08.md: -------------------------------------------------------------------------------- 1 | ## What You Will Learn during this Step: 2 | - Add validation for userid and password 3 | - Hard coded validation!! 4 | 5 | ## Useful Snippets and References 6 | First Snippet 7 | ``` 8 | package com.in28minutes.springboot.web.service; 9 | 10 | import org.springframework.stereotype.Component; 11 | 12 | @Component 13 | public class LoginService { 14 | public boolean validateUser(String user, String password) { 15 | return user.equalsIgnoreCase("in28Minutes") && password.equals("dummy"); 16 | } 17 | } 18 | 19 | ``` 20 | Second Snippet 21 | ``` 22 | @Autowired 23 | private LoginService service; 24 | 25 | @RequestMapping(value = "/login", method = RequestMethod.POST) 26 | public String handleLogin(ModelMap model, @RequestParam String name, 27 | @RequestParam String password) { 28 | 29 | boolean isValidUser = service.validateUser(name, password); 30 | 31 | if (isValidUser) { 32 | model.put("name", name); 33 | return "welcome"; 34 | } else { 35 | model.put("errorMessage", "Invalid Credentials!!"); 36 | return "login"; 37 | } 38 | } 39 | ``` 40 | 41 | ## Files List 42 | 43 | ### pom.xml 44 | 45 | ```xml 46 | 47 | 49 | 4.0.0 50 | 51 | com.in28minutes.springboot.web 52 | spring-boot-first-web-application 53 | 0.0.1-SNAPSHOT 54 | jar 55 | 56 | spring-boot-first-web-application 57 | Demo project for Spring Boot 58 | 59 | 60 | org.springframework.boot 61 | spring-boot-starter-parent 62 | 1.4.3.RELEASE 63 | 64 | 65 | 66 | 67 | UTF-8 68 | UTF-8 69 | 1.8 70 | 71 | 72 | 73 | 74 | org.springframework.boot 75 | spring-boot-starter-web 76 | 77 | 78 | 79 | org.apache.tomcat.embed 80 | tomcat-embed-jasper 81 | provided 82 | 83 | 84 | 85 | org.springframework.boot 86 | spring-boot-devtools 87 | runtime 88 | 89 | 90 | 91 | org.springframework.boot 92 | spring-boot-starter-test 93 | test 94 | 95 | 96 | 97 | 98 | 99 | 100 | org.springframework.boot 101 | spring-boot-maven-plugin 102 | 103 | 104 | 105 | 106 | 107 | ``` 108 | --- 109 | ### src/main/java/com/in28minutes/springboot/web/controller/LoginController.java 110 | 111 | ```java 112 | package com.in28minutes.springboot.web.controller; 113 | 114 | import org.springframework.beans.factory.annotation.Autowired; 115 | import org.springframework.stereotype.Controller; 116 | import org.springframework.ui.ModelMap; 117 | import org.springframework.web.bind.annotation.RequestMapping; 118 | import org.springframework.web.bind.annotation.RequestMethod; 119 | import org.springframework.web.bind.annotation.RequestParam; 120 | 121 | import com.in28minutes.springboot.web.service.LoginService; 122 | 123 | @Controller 124 | public class LoginController { 125 | 126 | @Autowired 127 | LoginService service; 128 | 129 | @RequestMapping(value="/login", method = RequestMethod.GET) 130 | public String showLoginPage(ModelMap model){ 131 | return "login"; 132 | } 133 | 134 | @RequestMapping(value="/login", method = RequestMethod.POST) 135 | public String showWelcomePage(ModelMap model, @RequestParam String name, @RequestParam String password){ 136 | boolean isValidUser = service.validateUser(name, password); 137 | 138 | if (!isValidUser) { 139 | model.put("errorMessage", "Invalid Credentials"); 140 | return "login"; 141 | } 142 | 143 | model.put("name", name); 144 | model.put("password", password); 145 | 146 | return "welcome"; 147 | } 148 | 149 | } 150 | ``` 151 | --- 152 | ### src/main/java/com/in28minutes/springboot/web/service/LoginService.java 153 | 154 | ```java 155 | package com.in28minutes.springboot.web.service; 156 | 157 | import org.springframework.stereotype.Component; 158 | 159 | @Component 160 | public class LoginService { 161 | 162 | public boolean validateUser(String userid, String password) { 163 | // in28minutes, dummy 164 | return userid.equalsIgnoreCase("in28minutes") 165 | && password.equalsIgnoreCase("dummy"); 166 | } 167 | 168 | } 169 | ``` 170 | --- 171 | ### src/main/java/com/in28minutes/springboot/web/SpringBootFirstWebApplication.java 172 | 173 | ```java 174 | package com.in28minutes.springboot.web; 175 | 176 | import org.springframework.boot.SpringApplication; 177 | import org.springframework.boot.autoconfigure.SpringBootApplication; 178 | 179 | @SpringBootApplication 180 | public class SpringBootFirstWebApplication { 181 | 182 | public static void main(String[] args) { 183 | SpringApplication.run(SpringBootFirstWebApplication.class, args); 184 | } 185 | } 186 | ``` 187 | --- 188 | ### src/main/resources/application.properties 189 | 190 | ``` 191 | spring.mvc.view.prefix=/WEB-INF/jsp/ 192 | spring.mvc.view.suffix=.jsp 193 | logging.level.org.springframework.web=DEBUG 194 | ``` 195 | --- 196 | ### src/main/webapp/WEB-INF/jsp/login.jsp 197 | 198 | ``` 199 | 200 | 201 | 202 | First Web Application 203 | 204 | 205 | 206 | ${errorMessage} 207 |
208 | Name : 209 | Password : 210 | 211 |
212 | 213 | 214 | 215 | ``` 216 | --- 217 | ### src/main/webapp/WEB-INF/jsp/welcome.jsp 218 | 219 | ``` 220 | 221 | 222 | 223 | First Web Application 224 | 225 | 226 | 227 | Welcome ${name}!! 228 | 229 | 230 | 231 | ``` 232 | --- 233 | ### src/test/java/com/in28minutes/springboot/web/SpringBootFirstWebApplicationTests.java 234 | 235 | ```java 236 | package com.in28minutes.springboot.web; 237 | 238 | import org.junit.Test; 239 | import org.junit.runner.RunWith; 240 | import org.springframework.boot.test.context.SpringBootTest; 241 | import org.springframework.test.context.junit4.SpringRunner; 242 | 243 | @RunWith(SpringRunner.class) 244 | @SpringBootTest 245 | public class SpringBootFirstWebApplicationTests { 246 | 247 | @Test 248 | public void contextLoads() { 249 | } 250 | 251 | } 252 | ``` 253 | --- 254 | ### todo.txt 255 | 256 | ``` 257 | Component, Service, Repository, Controller 258 | Autowired 259 | ComponentScan 260 | 261 | 262 | Spring Boot Starter Parent 263 | Spring Boot Starter Web 264 | @SpringBootApplication 265 | Auto Configuration 266 | 267 | Dispatcher Servlet 268 | 269 | /login => "login" 270 | 271 | "login" => src/main/webapp/WEB-INF/jsp/login.jsp 272 | 273 | 274 | Search for a view named "login" 275 | 276 | 277 | 278 | /login => LoginController 279 | ``` 280 | --- 281 | -------------------------------------------------------------------------------- /Step08.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/in28minutes/SpringBootWebApplicationStepByStep/7611e5a098e49023ee03ce1446e465a4c567da45/Step08.zip -------------------------------------------------------------------------------- /Step09.md: -------------------------------------------------------------------------------- 1 | ## What You Will Learn during this Step: 2 | - Magic of Spring 3 | - Learn about Spring Auto-wiring and Dependency Management. 4 | - @Autowired, @Component 5 | -------------------------------------------------------------------------------- /Step10.md: -------------------------------------------------------------------------------- 1 | ## What we will do: 2 | - Create TodoController and list-todos.jsp 3 | - Make TodoService a @Service and inject it 4 | 5 | ## Pending for Next Step 6 | - ${name} is not available in list-todos.jsp 7 | - in28Minutes is hardcoded in TodoController 8 | 9 | ## Snippets 10 | 11 | Snippet - /src/main/java/com/in28minutes/springboot/web/model/Todo.java 12 | ``` 13 | package com.in28minutes.springboot.web.model; 14 | 15 | import java.util.Date; 16 | 17 | public class Todo { 18 | private int id; 19 | private String user; 20 | private String desc; 21 | private Date targetDate; 22 | private boolean isDone; 23 | 24 | public Todo(int id, String user, String desc, Date targetDate, 25 | boolean isDone) { 26 | super(); 27 | this.id = id; 28 | this.user = user; 29 | this.desc = desc; 30 | this.targetDate = targetDate; 31 | this.isDone = isDone; 32 | } 33 | 34 | public int getId() { 35 | return id; 36 | } 37 | 38 | public void setId(int id) { 39 | this.id = id; 40 | } 41 | 42 | public String getUser() { 43 | return user; 44 | } 45 | 46 | public void setUser(String user) { 47 | this.user = user; 48 | } 49 | 50 | public String getDesc() { 51 | return desc; 52 | } 53 | 54 | public void setDesc(String desc) { 55 | this.desc = desc; 56 | } 57 | 58 | public Date getTargetDate() { 59 | return targetDate; 60 | } 61 | 62 | public void setTargetDate(Date targetDate) { 63 | this.targetDate = targetDate; 64 | } 65 | 66 | public boolean isDone() { 67 | return isDone; 68 | } 69 | 70 | public void setDone(boolean isDone) { 71 | this.isDone = isDone; 72 | } 73 | 74 | @Override 75 | public int hashCode() { 76 | final int prime = 31; 77 | int result = 1; 78 | result = prime * result + id; 79 | return result; 80 | } 81 | 82 | @Override 83 | public boolean equals(Object obj) { 84 | if (this == obj) { 85 | return true; 86 | } 87 | if (obj == null) { 88 | return false; 89 | } 90 | if (getClass() != obj.getClass()) { 91 | return false; 92 | } 93 | Todo other = (Todo) obj; 94 | if (id != other.id) { 95 | return false; 96 | } 97 | return true; 98 | } 99 | 100 | @Override 101 | public String toString() { 102 | return String.format( 103 | "Todo [id=%s, user=%s, desc=%s, targetDate=%s, isDone=%s]", id, 104 | user, desc, targetDate, isDone); 105 | } 106 | 107 | } 108 | ``` 109 | 110 | Snippet - /src/main/java/com/in28minutes/springboot/web/service/TodoService.java 111 | ``` 112 | package com.in28minutes.springboot.web.service; 113 | 114 | import java.util.ArrayList; 115 | import java.util.Date; 116 | import java.util.Iterator; 117 | import java.util.List; 118 | 119 | import org.springframework.stereotype.Service; 120 | 121 | import com.in28minutes.springboot.web.model.Todo; 122 | 123 | @Service 124 | public class TodoService { 125 | private static List todos = new ArrayList(); 126 | private static int todoCount = 3; 127 | 128 | static { 129 | todos.add(new Todo(1, "in28Minutes", "Learn Spring MVC", new Date(), 130 | false)); 131 | todos.add(new Todo(2, "in28Minutes", "Learn Struts", new Date(), false)); 132 | todos.add(new Todo(3, "in28Minutes", "Learn Hibernate", new Date(), 133 | false)); 134 | } 135 | 136 | public List retrieveTodos(String user) { 137 | List filteredTodos = new ArrayList(); 138 | for (Todo todo : todos) { 139 | if (todo.getUser().equals(user)) { 140 | filteredTodos.add(todo); 141 | } 142 | } 143 | return filteredTodos; 144 | } 145 | 146 | public void addTodo(String name, String desc, Date targetDate, 147 | boolean isDone) { 148 | todos.add(new Todo(++todoCount, name, desc, targetDate, isDone)); 149 | } 150 | 151 | public void deleteTodo(int id) { 152 | Iterator iterator = todos.iterator(); 153 | while (iterator.hasNext()) { 154 | Todo todo = iterator.next(); 155 | if (todo.getId() == id) { 156 | iterator.remove(); 157 | } 158 | } 159 | } 160 | } 161 | ``` 162 | -------------------------------------------------------------------------------- /Step11.md: -------------------------------------------------------------------------------- 1 | ## What we will do: 2 | - Lets discuss about Architecture of web applications 3 | -------------------------------------------------------------------------------- /Step12.md: -------------------------------------------------------------------------------- 1 | ## What You Will Learn during this Step: 2 | - Session vs Model vs Request. 3 | - Be cautious about what you use Session for. 4 | - @SessionAttributes("name") and how it works? 5 | - 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 6 | - 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.” 7 | 8 | ## Useful Snippets and References 9 | First Snippet 10 | ``` 11 | @SessionAttributes("name") 12 | ``` 13 | 14 | ## Files List 15 | 16 | ### pom.xml 17 | 18 | ```xml 19 | 20 | 22 | 4.0.0 23 | 24 | com.in28minutes.springboot.web 25 | spring-boot-first-web-application 26 | 0.0.1-SNAPSHOT 27 | jar 28 | 29 | spring-boot-first-web-application 30 | Demo project for Spring Boot 31 | 32 | 33 | org.springframework.boot 34 | spring-boot-starter-parent 35 | 1.4.3.RELEASE 36 | 37 | 38 | 39 | 40 | UTF-8 41 | UTF-8 42 | 1.8 43 | 44 | 45 | 46 | 47 | org.springframework.boot 48 | spring-boot-starter-web 49 | 50 | 51 | 52 | org.apache.tomcat.embed 53 | tomcat-embed-jasper 54 | provided 55 | 56 | 57 | 58 | org.springframework.boot 59 | spring-boot-devtools 60 | runtime 61 | 62 | 63 | 64 | org.springframework.boot 65 | spring-boot-starter-test 66 | test 67 | 68 | 69 | 70 | 71 | 72 | 73 | org.springframework.boot 74 | spring-boot-maven-plugin 75 | 76 | 77 | 78 | 79 | 80 | ``` 81 | --- 82 | ### src/main/java/com/in28minutes/springboot/web/controller/LoginController.java 83 | 84 | ```java 85 | package com.in28minutes.springboot.web.controller; 86 | 87 | import org.springframework.beans.factory.annotation.Autowired; 88 | import org.springframework.stereotype.Controller; 89 | import org.springframework.ui.ModelMap; 90 | import org.springframework.web.bind.annotation.RequestMapping; 91 | import org.springframework.web.bind.annotation.RequestMethod; 92 | import org.springframework.web.bind.annotation.RequestParam; 93 | import org.springframework.web.bind.annotation.SessionAttributes; 94 | 95 | import com.in28minutes.springboot.web.service.LoginService; 96 | 97 | @Controller 98 | @SessionAttributes("name") 99 | public class LoginController { 100 | 101 | @Autowired 102 | LoginService service; 103 | 104 | @RequestMapping(value="/login", method = RequestMethod.GET) 105 | public String showLoginPage(ModelMap model){ 106 | return "login"; 107 | } 108 | 109 | @RequestMapping(value="/login", method = RequestMethod.POST) 110 | public String showWelcomePage(ModelMap model, @RequestParam String name, @RequestParam String password){ 111 | 112 | boolean isValidUser = service.validateUser(name, password); 113 | 114 | if (!isValidUser) { 115 | model.put("errorMessage", "Invalid Credentials"); 116 | return "login"; 117 | } 118 | 119 | model.put("name", name); 120 | model.put("password", password); 121 | 122 | return "welcome"; 123 | } 124 | 125 | } 126 | ``` 127 | --- 128 | ### src/main/java/com/in28minutes/springboot/web/controller/TodoController.java 129 | 130 | ```java 131 | package com.in28minutes.springboot.web.controller; 132 | 133 | import org.springframework.beans.factory.annotation.Autowired; 134 | import org.springframework.stereotype.Controller; 135 | import org.springframework.ui.ModelMap; 136 | import org.springframework.web.bind.annotation.RequestMapping; 137 | import org.springframework.web.bind.annotation.RequestMethod; 138 | import org.springframework.web.bind.annotation.RequestParam; 139 | import org.springframework.web.bind.annotation.SessionAttributes; 140 | 141 | import com.in28minutes.springboot.web.service.LoginService; 142 | import com.in28minutes.springboot.web.service.TodoService; 143 | 144 | @Controller 145 | @SessionAttributes("name") 146 | public class TodoController { 147 | 148 | @Autowired 149 | TodoService service; 150 | 151 | @RequestMapping(value="/list-todos", method = RequestMethod.GET) 152 | public String showTodos(ModelMap model){ 153 | String name = (String) model.get("name"); 154 | model.put("todos", service.retrieveTodos(name)); 155 | return "list-todos"; 156 | } 157 | } 158 | ``` 159 | --- 160 | ### src/main/java/com/in28minutes/springboot/web/model/Todo.java 161 | 162 | ```java 163 | package com.in28minutes.springboot.web.model; 164 | 165 | import java.util.Date; 166 | 167 | public class Todo { 168 | private int id; 169 | private String user; 170 | private String desc; 171 | private Date targetDate; 172 | private boolean isDone; 173 | 174 | public Todo(int id, String user, String desc, Date targetDate, 175 | boolean isDone) { 176 | super(); 177 | this.id = id; 178 | this.user = user; 179 | this.desc = desc; 180 | this.targetDate = targetDate; 181 | this.isDone = isDone; 182 | } 183 | 184 | public int getId() { 185 | return id; 186 | } 187 | 188 | public void setId(int id) { 189 | this.id = id; 190 | } 191 | 192 | public String getUser() { 193 | return user; 194 | } 195 | 196 | public void setUser(String user) { 197 | this.user = user; 198 | } 199 | 200 | public String getDesc() { 201 | return desc; 202 | } 203 | 204 | public void setDesc(String desc) { 205 | this.desc = desc; 206 | } 207 | 208 | public Date getTargetDate() { 209 | return targetDate; 210 | } 211 | 212 | public void setTargetDate(Date targetDate) { 213 | this.targetDate = targetDate; 214 | } 215 | 216 | public boolean isDone() { 217 | return isDone; 218 | } 219 | 220 | public void setDone(boolean isDone) { 221 | this.isDone = isDone; 222 | } 223 | 224 | @Override 225 | public int hashCode() { 226 | final int prime = 31; 227 | int result = 1; 228 | result = prime * result + id; 229 | return result; 230 | } 231 | 232 | @Override 233 | public boolean equals(Object obj) { 234 | if (this == obj) { 235 | return true; 236 | } 237 | if (obj == null) { 238 | return false; 239 | } 240 | if (getClass() != obj.getClass()) { 241 | return false; 242 | } 243 | Todo other = (Todo) obj; 244 | if (id != other.id) { 245 | return false; 246 | } 247 | return true; 248 | } 249 | 250 | @Override 251 | public String toString() { 252 | return String.format( 253 | "Todo [id=%s, user=%s, desc=%s, targetDate=%s, isDone=%s]", id, 254 | user, desc, targetDate, isDone); 255 | } 256 | 257 | } 258 | ``` 259 | --- 260 | ### src/main/java/com/in28minutes/springboot/web/service/LoginService.java 261 | 262 | ```java 263 | package com.in28minutes.springboot.web.service; 264 | 265 | import org.springframework.stereotype.Component; 266 | import org.springframework.stereotype.Service; 267 | 268 | @Service 269 | public class LoginService { 270 | 271 | public boolean validateUser(String userid, String password) { 272 | // in28minutes, dummy 273 | return userid.equalsIgnoreCase("in28minutes") 274 | && password.equalsIgnoreCase("dummy"); 275 | } 276 | 277 | } 278 | ``` 279 | --- 280 | ### src/main/java/com/in28minutes/springboot/web/service/TodoService.java 281 | 282 | ```java 283 | package com.in28minutes.springboot.web.service; 284 | 285 | import java.util.ArrayList; 286 | import java.util.Date; 287 | import java.util.Iterator; 288 | import java.util.List; 289 | 290 | import org.springframework.stereotype.Service; 291 | 292 | import com.in28minutes.springboot.web.model.Todo; 293 | 294 | @Service 295 | public class TodoService { 296 | private static List todos = new ArrayList(); 297 | private static int todoCount = 3; 298 | 299 | static { 300 | todos.add(new Todo(1, "in28Minutes", "Learn Spring MVC", new Date(), 301 | false)); 302 | todos.add(new Todo(2, "in28Minutes", "Learn Struts", new Date(), false)); 303 | todos.add(new Todo(3, "in28Minutes", "Learn Hibernate", new Date(), 304 | false)); 305 | } 306 | 307 | public List retrieveTodos(String user) { 308 | List filteredTodos = new ArrayList(); 309 | for (Todo todo : todos) { 310 | if (todo.getUser().equals(user)) { 311 | filteredTodos.add(todo); 312 | } 313 | } 314 | return filteredTodos; 315 | } 316 | 317 | public void addTodo(String name, String desc, Date targetDate, 318 | boolean isDone) { 319 | todos.add(new Todo(++todoCount, name, desc, targetDate, isDone)); 320 | } 321 | 322 | public void deleteTodo(int id) { 323 | Iterator iterator = todos.iterator(); 324 | while (iterator.hasNext()) { 325 | Todo todo = iterator.next(); 326 | if (todo.getId() == id) { 327 | iterator.remove(); 328 | } 329 | } 330 | } 331 | } 332 | ``` 333 | --- 334 | ### src/main/java/com/in28minutes/springboot/web/SpringBootFirstWebApplication.java 335 | 336 | ```java 337 | package com.in28minutes.springboot.web; 338 | 339 | import org.springframework.boot.SpringApplication; 340 | import org.springframework.boot.autoconfigure.SpringBootApplication; 341 | import org.springframework.context.annotation.ComponentScan; 342 | 343 | @SpringBootApplication 344 | @ComponentScan("com.in28minutes.springboot.web") 345 | public class SpringBootFirstWebApplication { 346 | 347 | public static void main(String[] args) { 348 | SpringApplication.run(SpringBootFirstWebApplication.class, args); 349 | } 350 | } 351 | ``` 352 | --- 353 | ### src/main/resources/application.properties 354 | 355 | ``` 356 | spring.mvc.view.prefix=/WEB-INF/jsp/ 357 | spring.mvc.view.suffix=.jsp 358 | logging.level.org.springframework.web=DEBUG 359 | ``` 360 | --- 361 | ### src/main/webapp/WEB-INF/jsp/list-todos.jsp 362 | 363 | ``` 364 | 365 | 366 | 367 | First Web Application 368 | 369 | 370 | 371 | Here are the list of your todos: 372 | ${todos} 373 |
374 | Your Name is : ${name} 375 | 376 | 377 | 378 | ``` 379 | --- 380 | ### src/main/webapp/WEB-INF/jsp/login.jsp 381 | 382 | ``` 383 | 384 | 385 | 386 | First Web Application 387 | 388 | 389 | 390 | ${errorMessage} 391 |
392 | Name : 393 | Password : 394 | 395 |
396 | 397 | 398 | 399 | ``` 400 | --- 401 | ### src/main/webapp/WEB-INF/jsp/welcome.jsp 402 | 403 | ``` 404 | 405 | 406 | 407 | First Web Application 408 | 409 | 410 | 411 | Welcome ${name}!! Click here to manage your todo's. 412 | 413 | 414 | 415 | ``` 416 | --- 417 | ### src/test/java/com/in28minutes/springboot/web/SpringBootFirstWebApplicationTests.java 418 | 419 | ```java 420 | package com.in28minutes.springboot.web; 421 | 422 | import org.junit.Test; 423 | import org.junit.runner.RunWith; 424 | import org.springframework.boot.test.context.SpringBootTest; 425 | import org.springframework.test.context.junit4.SpringRunner; 426 | 427 | @RunWith(SpringRunner.class) 428 | @SpringBootTest 429 | public class SpringBootFirstWebApplicationTests { 430 | 431 | @Test 432 | public void contextLoads() { 433 | } 434 | 435 | } 436 | ``` 437 | --- 438 | ### todo.txt 439 | 440 | ``` 441 | LoginController -> adds name to model 442 | welcome.jsp -> shows ${name} 443 | 444 | TodoController -> redirects to list-todos.jsp 445 | ${name} is empty 446 | 447 | 448 | Component, Service, Repository, Controller 449 | Autowired 450 | ComponentScan 451 | 452 | Field dummyService in com.in28minutes.springboot.web.controller.LoginController 453 | required a bean of type 'com.in28minutes.dummy.DummyService' 454 | that could not be found. 455 | 456 | Spring Boot Starter Parent 457 | Spring Boot Starter Web 458 | @SpringBootApplication 459 | Auto Configuration 460 | 461 | Dispatcher Servlet 462 | 463 | /login => "login" 464 | 465 | "login" => src/main/webapp/WEB-INF/jsp/login.jsp 466 | 467 | 468 | Search for a view named "login" 469 | 470 | 471 | 472 | /login => LoginController 473 | ``` 474 | --- 475 | -------------------------------------------------------------------------------- /Step12.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/in28minutes/SpringBootWebApplicationStepByStep/7611e5a098e49023ee03ce1446e465a4c567da45/Step12.zip -------------------------------------------------------------------------------- /Step13.md: -------------------------------------------------------------------------------- 1 | ## What we will do: 2 | - Add Facility to add New Todo 3 | - todo.jsp 4 | - Importance of redirect:/list-todos 5 | 6 | ## Files List 7 | 8 | ### pom.xml 9 | 10 | ```xml 11 | 12 | 14 | 4.0.0 15 | 16 | com.in28minutes.springboot.web 17 | spring-boot-first-web-application 18 | 0.0.1-SNAPSHOT 19 | jar 20 | 21 | spring-boot-first-web-application 22 | Demo project for Spring Boot 23 | 24 | 25 | org.springframework.boot 26 | spring-boot-starter-parent 27 | 1.4.3.RELEASE 28 | 29 | 30 | 31 | 32 | UTF-8 33 | UTF-8 34 | 1.8 35 | 36 | 37 | 38 | 39 | org.springframework.boot 40 | spring-boot-starter-web 41 | 42 | 43 | 44 | org.apache.tomcat.embed 45 | tomcat-embed-jasper 46 | provided 47 | 48 | 49 | 50 | org.springframework.boot 51 | spring-boot-devtools 52 | runtime 53 | 54 | 55 | 56 | org.springframework.boot 57 | spring-boot-starter-test 58 | test 59 | 60 | 61 | 62 | 63 | 64 | 65 | org.springframework.boot 66 | spring-boot-maven-plugin 67 | 68 | 69 | 70 | 71 | 72 | ``` 73 | --- 74 | ### src/main/java/com/in28minutes/springboot/web/controller/LoginController.java 75 | 76 | ```java 77 | package com.in28minutes.springboot.web.controller; 78 | 79 | import org.springframework.beans.factory.annotation.Autowired; 80 | import org.springframework.stereotype.Controller; 81 | import org.springframework.ui.ModelMap; 82 | import org.springframework.web.bind.annotation.RequestMapping; 83 | import org.springframework.web.bind.annotation.RequestMethod; 84 | import org.springframework.web.bind.annotation.RequestParam; 85 | import org.springframework.web.bind.annotation.SessionAttributes; 86 | 87 | import com.in28minutes.springboot.web.service.LoginService; 88 | 89 | @Controller 90 | @SessionAttributes("name") 91 | public class LoginController { 92 | 93 | @Autowired 94 | LoginService service; 95 | 96 | @RequestMapping(value="/login", method = RequestMethod.GET) 97 | public String showLoginPage(ModelMap model){ 98 | return "login"; 99 | } 100 | 101 | @RequestMapping(value="/login", method = RequestMethod.POST) 102 | public String showWelcomePage(ModelMap model, @RequestParam String name, @RequestParam String password){ 103 | 104 | boolean isValidUser = service.validateUser(name, password); 105 | 106 | if (!isValidUser) { 107 | model.put("errorMessage", "Invalid Credentials"); 108 | return "login"; 109 | } 110 | 111 | model.put("name", name); 112 | model.put("password", password); 113 | 114 | return "welcome"; 115 | } 116 | 117 | } 118 | ``` 119 | --- 120 | ### src/main/java/com/in28minutes/springboot/web/controller/TodoController.java 121 | 122 | ```java 123 | package com.in28minutes.springboot.web.controller; 124 | 125 | import java.util.Date; 126 | 127 | import org.springframework.beans.factory.annotation.Autowired; 128 | import org.springframework.stereotype.Controller; 129 | import org.springframework.ui.ModelMap; 130 | import org.springframework.web.bind.annotation.RequestMapping; 131 | import org.springframework.web.bind.annotation.RequestMethod; 132 | import org.springframework.web.bind.annotation.RequestParam; 133 | import org.springframework.web.bind.annotation.SessionAttributes; 134 | 135 | import com.in28minutes.springboot.web.service.LoginService; 136 | import com.in28minutes.springboot.web.service.TodoService; 137 | 138 | @Controller 139 | @SessionAttributes("name") 140 | public class TodoController { 141 | 142 | @Autowired 143 | TodoService service; 144 | 145 | @RequestMapping(value="/list-todos", method = RequestMethod.GET) 146 | public String showTodos(ModelMap model){ 147 | String name = (String) model.get("name"); 148 | model.put("todos", service.retrieveTodos(name)); 149 | return "list-todos"; 150 | } 151 | 152 | @RequestMapping(value="/add-todo", method = RequestMethod.GET) 153 | public String showAddTodoPage(ModelMap model){ 154 | return "todo"; 155 | } 156 | 157 | @RequestMapping(value="/add-todo", method = RequestMethod.POST) 158 | public String addTodo(ModelMap model, @RequestParam String desc){ 159 | service.addTodo((String) model.get("name"), desc, new Date(), false); 160 | return "redirect:/list-todos"; 161 | } 162 | 163 | } 164 | ``` 165 | --- 166 | ### src/main/java/com/in28minutes/springboot/web/model/Todo.java 167 | 168 | ```java 169 | package com.in28minutes.springboot.web.model; 170 | 171 | import java.util.Date; 172 | 173 | public class Todo { 174 | private int id; 175 | private String user; 176 | private String desc; 177 | private Date targetDate; 178 | private boolean isDone; 179 | 180 | public Todo(int id, String user, String desc, Date targetDate, 181 | boolean isDone) { 182 | super(); 183 | this.id = id; 184 | this.user = user; 185 | this.desc = desc; 186 | this.targetDate = targetDate; 187 | this.isDone = isDone; 188 | } 189 | 190 | public int getId() { 191 | return id; 192 | } 193 | 194 | public void setId(int id) { 195 | this.id = id; 196 | } 197 | 198 | public String getUser() { 199 | return user; 200 | } 201 | 202 | public void setUser(String user) { 203 | this.user = user; 204 | } 205 | 206 | public String getDesc() { 207 | return desc; 208 | } 209 | 210 | public void setDesc(String desc) { 211 | this.desc = desc; 212 | } 213 | 214 | public Date getTargetDate() { 215 | return targetDate; 216 | } 217 | 218 | public void setTargetDate(Date targetDate) { 219 | this.targetDate = targetDate; 220 | } 221 | 222 | public boolean isDone() { 223 | return isDone; 224 | } 225 | 226 | public void setDone(boolean isDone) { 227 | this.isDone = isDone; 228 | } 229 | 230 | @Override 231 | public int hashCode() { 232 | final int prime = 31; 233 | int result = 1; 234 | result = prime * result + id; 235 | return result; 236 | } 237 | 238 | @Override 239 | public boolean equals(Object obj) { 240 | if (this == obj) { 241 | return true; 242 | } 243 | if (obj == null) { 244 | return false; 245 | } 246 | if (getClass() != obj.getClass()) { 247 | return false; 248 | } 249 | Todo other = (Todo) obj; 250 | if (id != other.id) { 251 | return false; 252 | } 253 | return true; 254 | } 255 | 256 | @Override 257 | public String toString() { 258 | return String.format( 259 | "Todo [id=%s, user=%s, desc=%s, targetDate=%s, isDone=%s]", id, 260 | user, desc, targetDate, isDone); 261 | } 262 | 263 | } 264 | ``` 265 | --- 266 | ### src/main/java/com/in28minutes/springboot/web/service/LoginService.java 267 | 268 | ```java 269 | package com.in28minutes.springboot.web.service; 270 | 271 | import org.springframework.stereotype.Component; 272 | import org.springframework.stereotype.Service; 273 | 274 | @Service 275 | public class LoginService { 276 | 277 | public boolean validateUser(String userid, String password) { 278 | // in28minutes, dummy 279 | return userid.equalsIgnoreCase("in28minutes") 280 | && password.equalsIgnoreCase("dummy"); 281 | } 282 | 283 | } 284 | ``` 285 | --- 286 | ### src/main/java/com/in28minutes/springboot/web/service/TodoService.java 287 | 288 | ```java 289 | package com.in28minutes.springboot.web.service; 290 | 291 | import java.util.ArrayList; 292 | import java.util.Date; 293 | import java.util.Iterator; 294 | import java.util.List; 295 | 296 | import org.springframework.stereotype.Service; 297 | 298 | import com.in28minutes.springboot.web.model.Todo; 299 | 300 | @Service 301 | public class TodoService { 302 | private static List todos = new ArrayList(); 303 | private static int todoCount = 3; 304 | 305 | static { 306 | todos.add(new Todo(1, "in28Minutes", "Learn Spring MVC", new Date(), 307 | false)); 308 | todos.add(new Todo(2, "in28Minutes", "Learn Struts", new Date(), false)); 309 | todos.add(new Todo(3, "in28Minutes", "Learn Hibernate", new Date(), 310 | false)); 311 | } 312 | 313 | public List retrieveTodos(String user) { 314 | List filteredTodos = new ArrayList(); 315 | for (Todo todo : todos) { 316 | if (todo.getUser().equals(user)) { 317 | filteredTodos.add(todo); 318 | } 319 | } 320 | return filteredTodos; 321 | } 322 | 323 | public void addTodo(String name, String desc, Date targetDate, 324 | boolean isDone) { 325 | todos.add(new Todo(++todoCount, name, desc, targetDate, isDone)); 326 | } 327 | 328 | public void deleteTodo(int id) { 329 | Iterator iterator = todos.iterator(); 330 | while (iterator.hasNext()) { 331 | Todo todo = iterator.next(); 332 | if (todo.getId() == id) { 333 | iterator.remove(); 334 | } 335 | } 336 | } 337 | } 338 | ``` 339 | --- 340 | ### src/main/java/com/in28minutes/springboot/web/SpringBootFirstWebApplication.java 341 | 342 | ```java 343 | package com.in28minutes.springboot.web; 344 | 345 | import org.springframework.boot.SpringApplication; 346 | import org.springframework.boot.autoconfigure.SpringBootApplication; 347 | import org.springframework.context.annotation.ComponentScan; 348 | 349 | @SpringBootApplication 350 | @ComponentScan("com.in28minutes.springboot.web") 351 | public class SpringBootFirstWebApplication { 352 | 353 | public static void main(String[] args) { 354 | SpringApplication.run(SpringBootFirstWebApplication.class, args); 355 | } 356 | } 357 | ``` 358 | --- 359 | ### src/main/resources/application.properties 360 | 361 | ``` 362 | spring.mvc.view.prefix=/WEB-INF/jsp/ 363 | spring.mvc.view.suffix=.jsp 364 | logging.level.org.springframework.web=DEBUG 365 | ``` 366 | --- 367 | ### src/main/webapp/WEB-INF/jsp/list-todos.jsp 368 | 369 | ``` 370 | 371 | 372 | 373 | First Web Application 374 | 375 | 376 | 377 | Here are the list of ${name}'s todos: 378 | ${todos}. 379 |
380 | Add a Todo 381 | 382 | 383 | 384 | ``` 385 | --- 386 | ### src/main/webapp/WEB-INF/jsp/login.jsp 387 | 388 | ``` 389 | 390 | 391 | 392 | First Web Application 393 | 394 | 395 | 396 | ${errorMessage} 397 |
398 | Name : 399 | Password : 400 | 401 |
402 | 403 | 404 | 405 | ``` 406 | --- 407 | ### src/main/webapp/WEB-INF/jsp/todo.jsp 408 | 409 | ``` 410 | 411 | 412 | 413 | First Web Application 414 | 415 | 416 | 417 | ADD Todo Page for ${name} 418 | 419 |
420 | Description : 421 | 422 |
423 | 424 | 425 | 426 | 427 | ``` 428 | --- 429 | ### src/main/webapp/WEB-INF/jsp/welcome.jsp 430 | 431 | ``` 432 | 433 | 434 | 435 | First Web Application 436 | 437 | 438 | 439 | Welcome ${name}!! Click here to manage your todo's. 440 | 441 | 442 | 443 | ``` 444 | --- 445 | ### src/test/java/com/in28minutes/springboot/web/SpringBootFirstWebApplicationTests.java 446 | 447 | ```java 448 | package com.in28minutes.springboot.web; 449 | 450 | import org.junit.Test; 451 | import org.junit.runner.RunWith; 452 | import org.springframework.boot.test.context.SpringBootTest; 453 | import org.springframework.test.context.junit4.SpringRunner; 454 | 455 | @RunWith(SpringRunner.class) 456 | @SpringBootTest 457 | public class SpringBootFirstWebApplicationTests { 458 | 459 | @Test 460 | public void contextLoads() { 461 | } 462 | 463 | } 464 | ``` 465 | --- 466 | ### todo.txt 467 | 468 | ``` 469 | LoginController -> adds name to model 470 | welcome.jsp -> shows ${name} 471 | 472 | TodoController -> redirects to list-todos.jsp 473 | ${name} is empty 474 | 475 | 476 | Component, Service, Repository, Controller 477 | Autowired 478 | ComponentScan 479 | 480 | Field dummyService in com.in28minutes.springboot.web.controller.LoginController 481 | required a bean of type 'com.in28minutes.dummy.DummyService' 482 | that could not be found. 483 | 484 | Spring Boot Starter Parent 485 | Spring Boot Starter Web 486 | @SpringBootApplication 487 | Auto Configuration 488 | 489 | Dispatcher Servlet 490 | 491 | /login => "login" 492 | 493 | "login" => src/main/webapp/WEB-INF/jsp/login.jsp 494 | 495 | 496 | Search for a view named "login" 497 | 498 | 499 | 500 | /login => LoginController 501 | ``` 502 | --- 503 | -------------------------------------------------------------------------------- /Step13.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/in28minutes/SpringBootWebApplicationStepByStep/7611e5a098e49023ee03ce1446e465a4c567da45/Step13.zip -------------------------------------------------------------------------------- /Step14.md: -------------------------------------------------------------------------------- 1 | ## What we will do: 2 | - Display Todos in a table using JSTL Tags 3 | - <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%> 4 | - Add Dependency for jstl 5 | 6 | ## Snippet 7 | ``` 8 | 9 | javax.servlet 10 | jstl 11 | 12 | ``` 13 | -------------------------------------------------------------------------------- /Step15.md: -------------------------------------------------------------------------------- 1 | ## What we will do: 2 | - Add bootstrap to give basic formatting to the page : We use bootstrap classes container,table and table-striped. 3 | - We will use webjars 4 | - 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] 5 | 6 | ## Useful Snippets 7 | ``` 8 | 9 | org.webjars 10 | bootstrap 11 | 3.3.6 12 | 13 | 14 | org.webjars 15 | jquery 16 | 1.9.1 17 | 18 | 19 | 20 | 21 | 22 | 23 | 25 | 26 | ``` 27 | 28 | ## Files List 29 | 30 | ### pom.xml 31 | 32 | ```xml 33 | 34 | 36 | 4.0.0 37 | 38 | com.in28minutes.springboot.web 39 | spring-boot-first-web-application 40 | 0.0.1-SNAPSHOT 41 | jar 42 | 43 | spring-boot-first-web-application 44 | Demo project for Spring Boot 45 | 46 | 47 | org.springframework.boot 48 | spring-boot-starter-parent 49 | 1.4.3.RELEASE 50 | 51 | 52 | 53 | 54 | UTF-8 55 | UTF-8 56 | 1.8 57 | 58 | 59 | 60 | 61 | org.springframework.boot 62 | spring-boot-starter-web 63 | 64 | 65 | 66 | javax.servlet 67 | jstl 68 | 69 | 70 | 71 | org.webjars 72 | bootstrap 73 | 3.3.6 74 | 75 | 76 | 77 | org.webjars 78 | jquery 79 | 1.9.1 80 | 81 | 82 | 83 | org.apache.tomcat.embed 84 | tomcat-embed-jasper 85 | provided 86 | 87 | 88 | 89 | org.springframework.boot 90 | spring-boot-devtools 91 | runtime 92 | 93 | 94 | 95 | org.springframework.boot 96 | spring-boot-starter-test 97 | test 98 | 99 | 100 | 101 | 102 | 103 | 104 | org.springframework.boot 105 | spring-boot-maven-plugin 106 | 107 | 108 | 109 | 110 | 111 | ``` 112 | --- 113 | ### src/main/java/com/in28minutes/springboot/web/controller/LoginController.java 114 | 115 | ```java 116 | package com.in28minutes.springboot.web.controller; 117 | 118 | import org.springframework.beans.factory.annotation.Autowired; 119 | import org.springframework.stereotype.Controller; 120 | import org.springframework.ui.ModelMap; 121 | import org.springframework.web.bind.annotation.RequestMapping; 122 | import org.springframework.web.bind.annotation.RequestMethod; 123 | import org.springframework.web.bind.annotation.RequestParam; 124 | import org.springframework.web.bind.annotation.SessionAttributes; 125 | 126 | import com.in28minutes.springboot.web.service.LoginService; 127 | 128 | @Controller 129 | @SessionAttributes("name") 130 | public class LoginController { 131 | 132 | @Autowired 133 | LoginService service; 134 | 135 | @RequestMapping(value="/login", method = RequestMethod.GET) 136 | public String showLoginPage(ModelMap model){ 137 | return "login"; 138 | } 139 | 140 | @RequestMapping(value="/login", method = RequestMethod.POST) 141 | public String showWelcomePage(ModelMap model, @RequestParam String name, @RequestParam String password){ 142 | 143 | boolean isValidUser = service.validateUser(name, password); 144 | 145 | if (!isValidUser) { 146 | model.put("errorMessage", "Invalid Credentials"); 147 | return "login"; 148 | } 149 | 150 | model.put("name", name); 151 | model.put("password", password); 152 | 153 | return "welcome"; 154 | } 155 | 156 | } 157 | ``` 158 | --- 159 | ### src/main/java/com/in28minutes/springboot/web/controller/TodoController.java 160 | 161 | ```java 162 | package com.in28minutes.springboot.web.controller; 163 | 164 | import java.util.Date; 165 | 166 | import org.springframework.beans.factory.annotation.Autowired; 167 | import org.springframework.stereotype.Controller; 168 | import org.springframework.ui.ModelMap; 169 | import org.springframework.web.bind.annotation.RequestMapping; 170 | import org.springframework.web.bind.annotation.RequestMethod; 171 | import org.springframework.web.bind.annotation.RequestParam; 172 | import org.springframework.web.bind.annotation.SessionAttributes; 173 | 174 | import com.in28minutes.springboot.web.service.LoginService; 175 | import com.in28minutes.springboot.web.service.TodoService; 176 | 177 | @Controller 178 | @SessionAttributes("name") 179 | public class TodoController { 180 | 181 | @Autowired 182 | TodoService service; 183 | 184 | @RequestMapping(value="/list-todos", method = RequestMethod.GET) 185 | public String showTodos(ModelMap model){ 186 | String name = (String) model.get("name"); 187 | model.put("todos", service.retrieveTodos(name)); 188 | return "list-todos"; 189 | } 190 | 191 | @RequestMapping(value="/add-todo", method = RequestMethod.GET) 192 | public String showAddTodoPage(ModelMap model){ 193 | return "todo"; 194 | } 195 | 196 | @RequestMapping(value="/add-todo", method = RequestMethod.POST) 197 | public String addTodo(ModelMap model, @RequestParam String desc){ 198 | service.addTodo((String) model.get("name"), desc, new Date(), false); 199 | return "redirect:/list-todos"; 200 | } 201 | 202 | } 203 | ``` 204 | --- 205 | ### src/main/java/com/in28minutes/springboot/web/model/Todo.java 206 | 207 | ```java 208 | package com.in28minutes.springboot.web.model; 209 | 210 | import java.util.Date; 211 | 212 | public class Todo { 213 | private int id; 214 | private String user; 215 | private String desc; 216 | private Date targetDate; 217 | private boolean isDone; 218 | 219 | public Todo(int id, String user, String desc, Date targetDate, 220 | boolean isDone) { 221 | super(); 222 | this.id = id; 223 | this.user = user; 224 | this.desc = desc; 225 | this.targetDate = targetDate; 226 | this.isDone = isDone; 227 | } 228 | 229 | public int getId() { 230 | return id; 231 | } 232 | 233 | public void setId(int id) { 234 | this.id = id; 235 | } 236 | 237 | public String getUser() { 238 | return user; 239 | } 240 | 241 | public void setUser(String user) { 242 | this.user = user; 243 | } 244 | 245 | public String getDesc() { 246 | return desc; 247 | } 248 | 249 | public void setDesc(String desc) { 250 | this.desc = desc; 251 | } 252 | 253 | public Date getTargetDate() { 254 | return targetDate; 255 | } 256 | 257 | public void setTargetDate(Date targetDate) { 258 | this.targetDate = targetDate; 259 | } 260 | 261 | public boolean isDone() { 262 | return isDone; 263 | } 264 | 265 | public void setDone(boolean isDone) { 266 | this.isDone = isDone; 267 | } 268 | 269 | @Override 270 | public int hashCode() { 271 | final int prime = 31; 272 | int result = 1; 273 | result = prime * result + id; 274 | return result; 275 | } 276 | 277 | @Override 278 | public boolean equals(Object obj) { 279 | if (this == obj) { 280 | return true; 281 | } 282 | if (obj == null) { 283 | return false; 284 | } 285 | if (getClass() != obj.getClass()) { 286 | return false; 287 | } 288 | Todo other = (Todo) obj; 289 | if (id != other.id) { 290 | return false; 291 | } 292 | return true; 293 | } 294 | 295 | @Override 296 | public String toString() { 297 | return String.format( 298 | "Todo [id=%s, user=%s, desc=%s, targetDate=%s, isDone=%s]", id, 299 | user, desc, targetDate, isDone); 300 | } 301 | 302 | } 303 | ``` 304 | --- 305 | ### src/main/java/com/in28minutes/springboot/web/service/LoginService.java 306 | 307 | ```java 308 | package com.in28minutes.springboot.web.service; 309 | 310 | import org.springframework.stereotype.Component; 311 | import org.springframework.stereotype.Service; 312 | 313 | @Service 314 | public class LoginService { 315 | 316 | public boolean validateUser(String userid, String password) { 317 | // in28minutes, dummy 318 | return userid.equalsIgnoreCase("in28minutes") 319 | && password.equalsIgnoreCase("dummy"); 320 | } 321 | 322 | } 323 | ``` 324 | --- 325 | ### src/main/java/com/in28minutes/springboot/web/service/TodoService.java 326 | 327 | ```java 328 | package com.in28minutes.springboot.web.service; 329 | 330 | import java.util.ArrayList; 331 | import java.util.Date; 332 | import java.util.Iterator; 333 | import java.util.List; 334 | 335 | import org.springframework.stereotype.Service; 336 | 337 | import com.in28minutes.springboot.web.model.Todo; 338 | 339 | @Service 340 | public class TodoService { 341 | private static List todos = new ArrayList(); 342 | private static int todoCount = 3; 343 | 344 | static { 345 | todos.add(new Todo(1, "in28Minutes", "Learn Spring MVC", new Date(), 346 | false)); 347 | todos.add(new Todo(2, "in28Minutes", "Learn Struts", new Date(), false)); 348 | todos.add(new Todo(3, "in28Minutes", "Learn Hibernate", new Date(), 349 | false)); 350 | } 351 | 352 | public List retrieveTodos(String user) { 353 | List filteredTodos = new ArrayList(); 354 | for (Todo todo : todos) { 355 | if (todo.getUser().equals(user)) { 356 | filteredTodos.add(todo); 357 | } 358 | } 359 | return filteredTodos; 360 | } 361 | 362 | public void addTodo(String name, String desc, Date targetDate, 363 | boolean isDone) { 364 | todos.add(new Todo(++todoCount, name, desc, targetDate, isDone)); 365 | } 366 | 367 | public void deleteTodo(int id) { 368 | Iterator iterator = todos.iterator(); 369 | while (iterator.hasNext()) { 370 | Todo todo = iterator.next(); 371 | if (todo.getId() == id) { 372 | iterator.remove(); 373 | } 374 | } 375 | } 376 | } 377 | ``` 378 | --- 379 | ### src/main/java/com/in28minutes/springboot/web/SpringBootFirstWebApplication.java 380 | 381 | ```java 382 | package com.in28minutes.springboot.web; 383 | 384 | import org.springframework.boot.SpringApplication; 385 | import org.springframework.boot.autoconfigure.SpringBootApplication; 386 | import org.springframework.context.annotation.ComponentScan; 387 | 388 | @SpringBootApplication 389 | @ComponentScan("com.in28minutes.springboot.web") 390 | public class SpringBootFirstWebApplication { 391 | 392 | public static void main(String[] args) { 393 | SpringApplication.run(SpringBootFirstWebApplication.class, args); 394 | } 395 | } 396 | ``` 397 | --- 398 | ### src/main/resources/application.properties 399 | 400 | ``` 401 | spring.mvc.view.prefix=/WEB-INF/jsp/ 402 | spring.mvc.view.suffix=.jsp 403 | logging.level.org.springframework.web=INFO 404 | ``` 405 | --- 406 | ### src/main/webapp/WEB-INF/jsp/list-todos.jsp 407 | 408 | ``` 409 | <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%> 410 | 411 | 412 | 413 | 414 | Todo's for ${name} 415 | 417 | 418 | 419 | 420 |
421 | 422 | 423 | 424 | 425 | 426 | 427 | 428 | 429 | 430 | 431 | 432 | 433 | 434 | 435 | 436 | 437 | 438 | 439 |
Your todos are
DescriptionTarget DateIs it Done?
${todo.desc}${todo.targetDate}${todo.done}
440 | 441 | 442 | 443 | 444 |
445 | 446 | 447 | 448 | ``` 449 | --- 450 | ### src/main/webapp/WEB-INF/jsp/login.jsp 451 | 452 | ``` 453 | 454 | 455 | 456 | First Web Application 457 | 458 | 459 | 460 | ${errorMessage} 461 |
462 | Name : 463 | Password : 464 | 465 |
466 | 467 | 468 | 469 | ``` 470 | --- 471 | ### src/main/webapp/WEB-INF/jsp/todo.jsp 472 | 473 | ``` 474 | 475 | 476 | 477 | First Web Application 478 | 479 | 480 | 481 | ADD Todo Page for ${name} 482 | 483 |
484 | Description : 485 | 486 |
487 | 488 | 489 | 490 | 491 | ``` 492 | --- 493 | ### src/main/webapp/WEB-INF/jsp/welcome.jsp 494 | 495 | ``` 496 | 497 | 498 | 499 | First Web Application 500 | 501 | 502 | 503 | Welcome ${name}!! Click here to manage your todo's. 504 | 505 | 506 | 507 | ``` 508 | --- 509 | ### src/test/java/com/in28minutes/springboot/web/SpringBootFirstWebApplicationTests.java 510 | 511 | ```java 512 | package com.in28minutes.springboot.web; 513 | 514 | import org.junit.Test; 515 | import org.junit.runner.RunWith; 516 | import org.springframework.boot.test.context.SpringBootTest; 517 | import org.springframework.test.context.junit4.SpringRunner; 518 | 519 | @RunWith(SpringRunner.class) 520 | @SpringBootTest 521 | public class SpringBootFirstWebApplicationTests { 522 | 523 | @Test 524 | public void contextLoads() { 525 | } 526 | 527 | } 528 | ``` 529 | --- 530 | ### todo.txt 531 | 532 | ``` 533 | LoginController -> adds name to model 534 | welcome.jsp -> shows ${name} 535 | 536 | TodoController -> redirects to list-todos.jsp 537 | ${name} is empty 538 | 539 | 540 | Component, Service, Repository, Controller 541 | Autowired 542 | ComponentScan 543 | 544 | Field dummyService in com.in28minutes.springboot.web.controller.LoginController 545 | required a bean of type 'com.in28minutes.dummy.DummyService' 546 | that could not be found. 547 | 548 | Spring Boot Starter Parent 549 | Spring Boot Starter Web 550 | @SpringBootApplication 551 | Auto Configuration 552 | 553 | Dispatcher Servlet 554 | 555 | /login => "login" 556 | 557 | "login" => src/main/webapp/WEB-INF/jsp/login.jsp 558 | 559 | 560 | Search for a view named "login" 561 | 562 | 563 | 564 | /login => LoginController 565 | ``` 566 | --- 567 | -------------------------------------------------------------------------------- /Step15.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/in28minutes/SpringBootWebApplicationStepByStep/7611e5a098e49023ee03ce1446e465a4c567da45/Step15.zip -------------------------------------------------------------------------------- /Step16.md: -------------------------------------------------------------------------------- 1 | ## What we will do: 2 | - Add functionality to delete a todo 3 | 4 | ## Useful Snippets 5 | ``` 6 | Delete 8 | ``` 9 | ## Files List 10 | 11 | ### pom.xml 12 | 13 | ```xml 14 | 15 | 17 | 4.0.0 18 | 19 | com.in28minutes.springboot.web 20 | spring-boot-first-web-application 21 | 0.0.1-SNAPSHOT 22 | jar 23 | 24 | spring-boot-first-web-application 25 | Demo project for Spring Boot 26 | 27 | 28 | org.springframework.boot 29 | spring-boot-starter-parent 30 | 1.4.3.RELEASE 31 | 32 | 33 | 34 | 35 | UTF-8 36 | UTF-8 37 | 1.8 38 | 39 | 40 | 41 | 42 | org.springframework.boot 43 | spring-boot-starter-web 44 | 45 | 46 | 47 | javax.servlet 48 | jstl 49 | 50 | 51 | 52 | org.webjars 53 | bootstrap 54 | 3.3.6 55 | 56 | 57 | 58 | org.webjars 59 | jquery 60 | 1.9.1 61 | 62 | 63 | 64 | org.apache.tomcat.embed 65 | tomcat-embed-jasper 66 | provided 67 | 68 | 69 | 70 | org.springframework.boot 71 | spring-boot-devtools 72 | runtime 73 | 74 | 75 | 76 | org.springframework.boot 77 | spring-boot-starter-test 78 | test 79 | 80 | 81 | 82 | 83 | 84 | 85 | org.springframework.boot 86 | spring-boot-maven-plugin 87 | 88 | 89 | 90 | 91 | 92 | ``` 93 | --- 94 | ### src/main/java/com/in28minutes/springboot/web/controller/LoginController.java 95 | 96 | ```java 97 | package com.in28minutes.springboot.web.controller; 98 | 99 | import org.springframework.beans.factory.annotation.Autowired; 100 | import org.springframework.stereotype.Controller; 101 | import org.springframework.ui.ModelMap; 102 | import org.springframework.web.bind.annotation.RequestMapping; 103 | import org.springframework.web.bind.annotation.RequestMethod; 104 | import org.springframework.web.bind.annotation.RequestParam; 105 | import org.springframework.web.bind.annotation.SessionAttributes; 106 | 107 | import com.in28minutes.springboot.web.service.LoginService; 108 | 109 | @Controller 110 | @SessionAttributes("name") 111 | public class LoginController { 112 | 113 | @Autowired 114 | LoginService service; 115 | 116 | @RequestMapping(value="/login", method = RequestMethod.GET) 117 | public String showLoginPage(ModelMap model){ 118 | return "login"; 119 | } 120 | 121 | @RequestMapping(value="/login", method = RequestMethod.POST) 122 | public String showWelcomePage(ModelMap model, @RequestParam String name, @RequestParam String password){ 123 | 124 | boolean isValidUser = service.validateUser(name, password); 125 | 126 | if (!isValidUser) { 127 | model.put("errorMessage", "Invalid Credentials"); 128 | return "login"; 129 | } 130 | 131 | model.put("name", name); 132 | model.put("password", password); 133 | 134 | return "welcome"; 135 | } 136 | 137 | } 138 | ``` 139 | --- 140 | ### src/main/java/com/in28minutes/springboot/web/controller/TodoController.java 141 | 142 | ```java 143 | package com.in28minutes.springboot.web.controller; 144 | 145 | import java.util.Date; 146 | 147 | import org.springframework.beans.factory.annotation.Autowired; 148 | import org.springframework.stereotype.Controller; 149 | import org.springframework.ui.ModelMap; 150 | import org.springframework.web.bind.annotation.RequestMapping; 151 | import org.springframework.web.bind.annotation.RequestMethod; 152 | import org.springframework.web.bind.annotation.RequestParam; 153 | import org.springframework.web.bind.annotation.SessionAttributes; 154 | 155 | import com.in28minutes.springboot.web.service.LoginService; 156 | import com.in28minutes.springboot.web.service.TodoService; 157 | 158 | @Controller 159 | @SessionAttributes("name") 160 | public class TodoController { 161 | 162 | @Autowired 163 | TodoService service; 164 | 165 | @RequestMapping(value="/list-todos", method = RequestMethod.GET) 166 | public String showTodos(ModelMap model){ 167 | String name = (String) model.get("name"); 168 | model.put("todos", service.retrieveTodos(name)); 169 | return "list-todos"; 170 | } 171 | 172 | @RequestMapping(value="/add-todo", method = RequestMethod.GET) 173 | public String showAddTodoPage(ModelMap model){ 174 | return "todo"; 175 | } 176 | 177 | @RequestMapping(value="/delete-todo", method = RequestMethod.GET) 178 | public String deleteTodo(@RequestParam int id){ 179 | service.deleteTodo(id); 180 | return "redirect:/list-todos"; 181 | } 182 | 183 | @RequestMapping(value="/add-todo", method = RequestMethod.POST) 184 | public String addTodo(ModelMap model, @RequestParam String desc){ 185 | service.addTodo((String) model.get("name"), desc, new Date(), false); 186 | return "redirect:/list-todos"; 187 | } 188 | 189 | } 190 | ``` 191 | --- 192 | ### src/main/java/com/in28minutes/springboot/web/model/Todo.java 193 | 194 | ```java 195 | package com.in28minutes.springboot.web.model; 196 | 197 | import java.util.Date; 198 | 199 | public class Todo { 200 | private int id; 201 | private String user; 202 | private String desc; 203 | private Date targetDate; 204 | private boolean isDone; 205 | 206 | public Todo(int id, String user, String desc, Date targetDate, 207 | boolean isDone) { 208 | super(); 209 | this.id = id; 210 | this.user = user; 211 | this.desc = desc; 212 | this.targetDate = targetDate; 213 | this.isDone = isDone; 214 | } 215 | 216 | public int getId() { 217 | return id; 218 | } 219 | 220 | public void setId(int id) { 221 | this.id = id; 222 | } 223 | 224 | public String getUser() { 225 | return user; 226 | } 227 | 228 | public void setUser(String user) { 229 | this.user = user; 230 | } 231 | 232 | public String getDesc() { 233 | return desc; 234 | } 235 | 236 | public void setDesc(String desc) { 237 | this.desc = desc; 238 | } 239 | 240 | public Date getTargetDate() { 241 | return targetDate; 242 | } 243 | 244 | public void setTargetDate(Date targetDate) { 245 | this.targetDate = targetDate; 246 | } 247 | 248 | public boolean isDone() { 249 | return isDone; 250 | } 251 | 252 | public void setDone(boolean isDone) { 253 | this.isDone = isDone; 254 | } 255 | 256 | @Override 257 | public int hashCode() { 258 | final int prime = 31; 259 | int result = 1; 260 | result = prime * result + id; 261 | return result; 262 | } 263 | 264 | @Override 265 | public boolean equals(Object obj) { 266 | if (this == obj) { 267 | return true; 268 | } 269 | if (obj == null) { 270 | return false; 271 | } 272 | if (getClass() != obj.getClass()) { 273 | return false; 274 | } 275 | Todo other = (Todo) obj; 276 | if (id != other.id) { 277 | return false; 278 | } 279 | return true; 280 | } 281 | 282 | @Override 283 | public String toString() { 284 | return String.format( 285 | "Todo [id=%s, user=%s, desc=%s, targetDate=%s, isDone=%s]", id, 286 | user, desc, targetDate, isDone); 287 | } 288 | 289 | } 290 | ``` 291 | --- 292 | ### src/main/java/com/in28minutes/springboot/web/service/LoginService.java 293 | 294 | ```java 295 | package com.in28minutes.springboot.web.service; 296 | 297 | import org.springframework.stereotype.Component; 298 | import org.springframework.stereotype.Service; 299 | 300 | @Service 301 | public class LoginService { 302 | 303 | public boolean validateUser(String userid, String password) { 304 | // in28minutes, dummy 305 | return userid.equalsIgnoreCase("in28minutes") 306 | && password.equalsIgnoreCase("dummy"); 307 | } 308 | 309 | } 310 | ``` 311 | --- 312 | ### src/main/java/com/in28minutes/springboot/web/service/TodoService.java 313 | 314 | ```java 315 | package com.in28minutes.springboot.web.service; 316 | 317 | import java.util.ArrayList; 318 | import java.util.Date; 319 | import java.util.Iterator; 320 | import java.util.List; 321 | 322 | import org.springframework.stereotype.Service; 323 | 324 | import com.in28minutes.springboot.web.model.Todo; 325 | 326 | @Service 327 | public class TodoService { 328 | private static List todos = new ArrayList(); 329 | private static int todoCount = 3; 330 | 331 | static { 332 | todos.add(new Todo(1, "in28Minutes", "Learn Spring MVC", new Date(), 333 | false)); 334 | todos.add(new Todo(2, "in28Minutes", "Learn Struts", new Date(), false)); 335 | todos.add(new Todo(3, "in28Minutes", "Learn Hibernate", new Date(), 336 | false)); 337 | } 338 | 339 | public List retrieveTodos(String user) { 340 | List filteredTodos = new ArrayList(); 341 | for (Todo todo : todos) { 342 | if (todo.getUser().equals(user)) { 343 | filteredTodos.add(todo); 344 | } 345 | } 346 | return filteredTodos; 347 | } 348 | 349 | public void addTodo(String name, String desc, Date targetDate, 350 | boolean isDone) { 351 | todos.add(new Todo(++todoCount, name, desc, targetDate, isDone)); 352 | } 353 | 354 | public void deleteTodo(int id) { 355 | Iterator iterator = todos.iterator(); 356 | while (iterator.hasNext()) { 357 | Todo todo = iterator.next(); 358 | if (todo.getId() == id) { 359 | iterator.remove(); 360 | } 361 | } 362 | } 363 | } 364 | ``` 365 | --- 366 | ### src/main/java/com/in28minutes/springboot/web/SpringBootFirstWebApplication.java 367 | 368 | ```java 369 | package com.in28minutes.springboot.web; 370 | 371 | import org.springframework.boot.SpringApplication; 372 | import org.springframework.boot.autoconfigure.SpringBootApplication; 373 | import org.springframework.context.annotation.ComponentScan; 374 | 375 | @SpringBootApplication 376 | @ComponentScan("com.in28minutes.springboot.web") 377 | public class SpringBootFirstWebApplication { 378 | 379 | public static void main(String[] args) { 380 | SpringApplication.run(SpringBootFirstWebApplication.class, args); 381 | } 382 | } 383 | ``` 384 | --- 385 | ### src/main/resources/application.properties 386 | 387 | ``` 388 | spring.mvc.view.prefix=/WEB-INF/jsp/ 389 | spring.mvc.view.suffix=.jsp 390 | logging.level.org.springframework.web=INFO 391 | ``` 392 | --- 393 | ### src/main/webapp/WEB-INF/jsp/list-todos.jsp 394 | 395 | ``` 396 | <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%> 397 | 398 | 399 | 400 | 401 | Todo's for ${name} 402 | 404 | 405 | 406 | 407 |
408 | 409 | 410 | 411 | 412 | 413 | 414 | 415 | 416 | 417 | 418 | 419 | 420 | 421 | 422 | 423 | 424 | 425 | 426 | 427 | 428 |
Your todos are
DescriptionTarget DateIs it Done?
${todo.desc}${todo.targetDate}${todo.done}Delete
429 | 430 | 431 | 432 | 433 |
434 | 435 | 436 | 437 | ``` 438 | --- 439 | ### src/main/webapp/WEB-INF/jsp/login.jsp 440 | 441 | ``` 442 | 443 | 444 | 445 | First Web Application 446 | 447 | 448 | 449 | ${errorMessage} 450 |
451 | Name : 452 | Password : 453 | 454 |
455 | 456 | 457 | 458 | ``` 459 | --- 460 | ### src/main/webapp/WEB-INF/jsp/todo.jsp 461 | 462 | ``` 463 | 464 | 465 | 466 | First Web Application 467 | 468 | 469 | 470 | ADD Todo Page for ${name} 471 | 472 |
473 | Description : 474 | 475 |
476 | 477 | 478 | 479 | 480 | ``` 481 | --- 482 | ### src/main/webapp/WEB-INF/jsp/welcome.jsp 483 | 484 | ``` 485 | 486 | 487 | 488 | First Web Application 489 | 490 | 491 | 492 | Welcome ${name}!! Click here to manage your todo's. 493 | 494 | 495 | 496 | ``` 497 | --- 498 | ### src/test/java/com/in28minutes/springboot/web/SpringBootFirstWebApplicationTests.java 499 | 500 | ```java 501 | package com.in28minutes.springboot.web; 502 | 503 | import org.junit.Test; 504 | import org.junit.runner.RunWith; 505 | import org.springframework.boot.test.context.SpringBootTest; 506 | import org.springframework.test.context.junit4.SpringRunner; 507 | 508 | @RunWith(SpringRunner.class) 509 | @SpringBootTest 510 | public class SpringBootFirstWebApplicationTests { 511 | 512 | @Test 513 | public void contextLoads() { 514 | } 515 | 516 | } 517 | ``` 518 | --- 519 | ### todo.txt 520 | 521 | ``` 522 | LoginController -> adds name to model 523 | welcome.jsp -> shows ${name} 524 | 525 | TodoController -> redirects to list-todos.jsp 526 | ${name} is empty 527 | 528 | 529 | Component, Service, Repository, Controller 530 | Autowired 531 | ComponentScan 532 | 533 | Field dummyService in com.in28minutes.springboot.web.controller.LoginController 534 | required a bean of type 'com.in28minutes.dummy.DummyService' 535 | that could not be found. 536 | 537 | Spring Boot Starter Parent 538 | Spring Boot Starter Web 539 | @SpringBootApplication 540 | Auto Configuration 541 | 542 | Dispatcher Servlet 543 | 544 | /login => "login" 545 | 546 | "login" => src/main/webapp/WEB-INF/jsp/login.jsp 547 | 548 | 549 | Search for a view named "login" 550 | 551 | 552 | 553 | /login => LoginController 554 | ``` 555 | --- 556 | -------------------------------------------------------------------------------- /Step16.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/in28minutes/SpringBootWebApplicationStepByStep/7611e5a098e49023ee03ce1446e465a4c567da45/Step16.zip -------------------------------------------------------------------------------- /Step17.md: -------------------------------------------------------------------------------- 1 | ## What we will do: 2 | In this short step: 3 | - Format Add Todo Page 4 | - Add Html5 Form Validations 5 | 6 | ## Useful Snippets 7 | ``` 8 |
9 | 10 | 11 |
12 | 13 | ``` 14 | 15 | ## Files List 16 | 17 | ### pom.xml 18 | 19 | ```xml 20 | 21 | 23 | 4.0.0 24 | 25 | com.in28minutes.springboot.web 26 | spring-boot-first-web-application 27 | 0.0.1-SNAPSHOT 28 | jar 29 | 30 | spring-boot-first-web-application 31 | Demo project for Spring Boot 32 | 33 | 34 | org.springframework.boot 35 | spring-boot-starter-parent 36 | 1.4.3.RELEASE 37 | 38 | 39 | 40 | 41 | UTF-8 42 | UTF-8 43 | 1.8 44 | 45 | 46 | 47 | 48 | org.springframework.boot 49 | spring-boot-starter-web 50 | 51 | 52 | 53 | javax.servlet 54 | jstl 55 | 56 | 57 | 58 | org.webjars 59 | bootstrap 60 | 3.3.6 61 | 62 | 63 | 64 | org.webjars 65 | jquery 66 | 1.9.1 67 | 68 | 69 | 70 | org.apache.tomcat.embed 71 | tomcat-embed-jasper 72 | provided 73 | 74 | 75 | 76 | org.springframework.boot 77 | spring-boot-devtools 78 | runtime 79 | 80 | 81 | 82 | org.springframework.boot 83 | spring-boot-starter-test 84 | test 85 | 86 | 87 | 88 | 89 | 90 | 91 | org.springframework.boot 92 | spring-boot-maven-plugin 93 | 94 | 95 | 96 | 97 | 98 | ``` 99 | --- 100 | ### src/main/java/com/in28minutes/springboot/web/controller/LoginController.java 101 | 102 | ```java 103 | package com.in28minutes.springboot.web.controller; 104 | 105 | import org.springframework.beans.factory.annotation.Autowired; 106 | import org.springframework.stereotype.Controller; 107 | import org.springframework.ui.ModelMap; 108 | import org.springframework.web.bind.annotation.RequestMapping; 109 | import org.springframework.web.bind.annotation.RequestMethod; 110 | import org.springframework.web.bind.annotation.RequestParam; 111 | import org.springframework.web.bind.annotation.SessionAttributes; 112 | 113 | import com.in28minutes.springboot.web.service.LoginService; 114 | 115 | @Controller 116 | @SessionAttributes("name") 117 | public class LoginController { 118 | 119 | @Autowired 120 | LoginService service; 121 | 122 | @RequestMapping(value="/login", method = RequestMethod.GET) 123 | public String showLoginPage(ModelMap model){ 124 | return "login"; 125 | } 126 | 127 | @RequestMapping(value="/login", method = RequestMethod.POST) 128 | public String showWelcomePage(ModelMap model, @RequestParam String name, @RequestParam String password){ 129 | 130 | boolean isValidUser = service.validateUser(name, password); 131 | 132 | if (!isValidUser) { 133 | model.put("errorMessage", "Invalid Credentials"); 134 | return "login"; 135 | } 136 | 137 | model.put("name", name); 138 | model.put("password", password); 139 | 140 | return "welcome"; 141 | } 142 | 143 | } 144 | ``` 145 | --- 146 | ### src/main/java/com/in28minutes/springboot/web/controller/TodoController.java 147 | 148 | ```java 149 | package com.in28minutes.springboot.web.controller; 150 | 151 | import java.util.Date; 152 | 153 | import org.springframework.beans.factory.annotation.Autowired; 154 | import org.springframework.stereotype.Controller; 155 | import org.springframework.ui.ModelMap; 156 | import org.springframework.web.bind.annotation.RequestMapping; 157 | import org.springframework.web.bind.annotation.RequestMethod; 158 | import org.springframework.web.bind.annotation.RequestParam; 159 | import org.springframework.web.bind.annotation.SessionAttributes; 160 | 161 | import com.in28minutes.springboot.web.service.LoginService; 162 | import com.in28minutes.springboot.web.service.TodoService; 163 | 164 | @Controller 165 | @SessionAttributes("name") 166 | public class TodoController { 167 | 168 | @Autowired 169 | TodoService service; 170 | 171 | @RequestMapping(value="/list-todos", method = RequestMethod.GET) 172 | public String showTodos(ModelMap model){ 173 | String name = (String) model.get("name"); 174 | model.put("todos", service.retrieveTodos(name)); 175 | return "list-todos"; 176 | } 177 | 178 | @RequestMapping(value="/add-todo", method = RequestMethod.GET) 179 | public String showAddTodoPage(ModelMap model){ 180 | return "todo"; 181 | } 182 | 183 | @RequestMapping(value="/delete-todo", method = RequestMethod.GET) 184 | public String deleteTodo(@RequestParam int id){ 185 | service.deleteTodo(id); 186 | return "redirect:/list-todos"; 187 | } 188 | 189 | @RequestMapping(value="/add-todo", method = RequestMethod.POST) 190 | public String addTodo(ModelMap model, @RequestParam String desc){ 191 | service.addTodo((String) model.get("name"), desc, new Date(), false); 192 | return "redirect:/list-todos"; 193 | } 194 | 195 | } 196 | ``` 197 | --- 198 | ### src/main/java/com/in28minutes/springboot/web/model/Todo.java 199 | 200 | ```java 201 | package com.in28minutes.springboot.web.model; 202 | 203 | import java.util.Date; 204 | 205 | public class Todo { 206 | private int id; 207 | private String user; 208 | private String desc; 209 | private Date targetDate; 210 | private boolean isDone; 211 | 212 | public Todo(int id, String user, String desc, Date targetDate, 213 | boolean isDone) { 214 | super(); 215 | this.id = id; 216 | this.user = user; 217 | this.desc = desc; 218 | this.targetDate = targetDate; 219 | this.isDone = isDone; 220 | } 221 | 222 | public int getId() { 223 | return id; 224 | } 225 | 226 | public void setId(int id) { 227 | this.id = id; 228 | } 229 | 230 | public String getUser() { 231 | return user; 232 | } 233 | 234 | public void setUser(String user) { 235 | this.user = user; 236 | } 237 | 238 | public String getDesc() { 239 | return desc; 240 | } 241 | 242 | public void setDesc(String desc) { 243 | this.desc = desc; 244 | } 245 | 246 | public Date getTargetDate() { 247 | return targetDate; 248 | } 249 | 250 | public void setTargetDate(Date targetDate) { 251 | this.targetDate = targetDate; 252 | } 253 | 254 | public boolean isDone() { 255 | return isDone; 256 | } 257 | 258 | public void setDone(boolean isDone) { 259 | this.isDone = isDone; 260 | } 261 | 262 | @Override 263 | public int hashCode() { 264 | final int prime = 31; 265 | int result = 1; 266 | result = prime * result + id; 267 | return result; 268 | } 269 | 270 | @Override 271 | public boolean equals(Object obj) { 272 | if (this == obj) { 273 | return true; 274 | } 275 | if (obj == null) { 276 | return false; 277 | } 278 | if (getClass() != obj.getClass()) { 279 | return false; 280 | } 281 | Todo other = (Todo) obj; 282 | if (id != other.id) { 283 | return false; 284 | } 285 | return true; 286 | } 287 | 288 | @Override 289 | public String toString() { 290 | return String.format( 291 | "Todo [id=%s, user=%s, desc=%s, targetDate=%s, isDone=%s]", id, 292 | user, desc, targetDate, isDone); 293 | } 294 | 295 | } 296 | ``` 297 | --- 298 | ### src/main/java/com/in28minutes/springboot/web/service/LoginService.java 299 | 300 | ```java 301 | package com.in28minutes.springboot.web.service; 302 | 303 | import org.springframework.stereotype.Component; 304 | import org.springframework.stereotype.Service; 305 | 306 | @Service 307 | public class LoginService { 308 | 309 | public boolean validateUser(String userid, String password) { 310 | // in28minutes, dummy 311 | return userid.equalsIgnoreCase("in28minutes") 312 | && password.equalsIgnoreCase("dummy"); 313 | } 314 | 315 | } 316 | ``` 317 | --- 318 | ### src/main/java/com/in28minutes/springboot/web/service/TodoService.java 319 | 320 | ```java 321 | package com.in28minutes.springboot.web.service; 322 | 323 | import java.util.ArrayList; 324 | import java.util.Date; 325 | import java.util.Iterator; 326 | import java.util.List; 327 | 328 | import org.springframework.stereotype.Service; 329 | 330 | import com.in28minutes.springboot.web.model.Todo; 331 | 332 | @Service 333 | public class TodoService { 334 | private static List todos = new ArrayList(); 335 | private static int todoCount = 3; 336 | 337 | static { 338 | todos.add(new Todo(1, "in28Minutes", "Learn Spring MVC", new Date(), 339 | false)); 340 | todos.add(new Todo(2, "in28Minutes", "Learn Struts", new Date(), false)); 341 | todos.add(new Todo(3, "in28Minutes", "Learn Hibernate", new Date(), 342 | false)); 343 | } 344 | 345 | public List retrieveTodos(String user) { 346 | List filteredTodos = new ArrayList(); 347 | for (Todo todo : todos) { 348 | if (todo.getUser().equals(user)) { 349 | filteredTodos.add(todo); 350 | } 351 | } 352 | return filteredTodos; 353 | } 354 | 355 | public void addTodo(String name, String desc, Date targetDate, 356 | boolean isDone) { 357 | todos.add(new Todo(++todoCount, name, desc, targetDate, isDone)); 358 | } 359 | 360 | public void deleteTodo(int id) { 361 | Iterator iterator = todos.iterator(); 362 | while (iterator.hasNext()) { 363 | Todo todo = iterator.next(); 364 | if (todo.getId() == id) { 365 | iterator.remove(); 366 | } 367 | } 368 | } 369 | } 370 | ``` 371 | --- 372 | ### src/main/java/com/in28minutes/springboot/web/SpringBootFirstWebApplication.java 373 | 374 | ```java 375 | package com.in28minutes.springboot.web; 376 | 377 | import org.springframework.boot.SpringApplication; 378 | import org.springframework.boot.autoconfigure.SpringBootApplication; 379 | import org.springframework.context.annotation.ComponentScan; 380 | 381 | @SpringBootApplication 382 | @ComponentScan("com.in28minutes.springboot.web") 383 | public class SpringBootFirstWebApplication { 384 | 385 | public static void main(String[] args) { 386 | SpringApplication.run(SpringBootFirstWebApplication.class, args); 387 | } 388 | } 389 | ``` 390 | --- 391 | ### src/main/resources/application.properties 392 | 393 | ``` 394 | spring.mvc.view.prefix=/WEB-INF/jsp/ 395 | spring.mvc.view.suffix=.jsp 396 | logging.level.org.springframework.web=INFO 397 | ``` 398 | --- 399 | ### src/main/webapp/WEB-INF/jsp/list-todos.jsp 400 | 401 | ``` 402 | <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%> 403 | 404 | 405 | 406 | 407 | Todo's for ${name} 408 | 410 | 411 | 412 | 413 |
414 | 415 | 416 | 417 | 418 | 419 | 420 | 421 | 422 | 423 | 424 | 425 | 426 | 427 | 428 | 429 | 430 | 432 | 433 | 434 | 435 |
Your todos are
DescriptionTarget DateIs it Done?
${todo.desc}${todo.targetDate}${todo.done}Delete
436 |
437 | Add a Todo 438 |
439 |
440 | 441 | 442 | 443 | 444 | 445 | 446 | 447 | ``` 448 | --- 449 | ### src/main/webapp/WEB-INF/jsp/login.jsp 450 | 451 | ``` 452 | 453 | 454 | 455 | First Web Application 456 | 457 | 458 | 459 | ${errorMessage} 460 |
461 | Name : 462 | Password : 463 | 464 |
465 | 466 | 467 | 468 | ``` 469 | --- 470 | ### src/main/webapp/WEB-INF/jsp/todo.jsp 471 | 472 | ``` 473 | 474 | 475 | 476 | First Web Application 477 | 479 | 480 | 481 | 482 | 483 |
484 |
485 |
486 | 487 | 489 |
490 | 491 | 492 |
493 |
494 | 495 | 496 | 497 | 498 | 499 | 500 | 501 | ``` 502 | --- 503 | ### src/main/webapp/WEB-INF/jsp/welcome.jsp 504 | 505 | ``` 506 | 507 | 508 | 509 | First Web Application 510 | 511 | 512 | 513 | Welcome ${name}!! Click here to manage your todo's. 514 | 515 | 516 | 517 | ``` 518 | --- 519 | ### src/test/java/com/in28minutes/springboot/web/SpringBootFirstWebApplicationTests.java 520 | 521 | ```java 522 | package com.in28minutes.springboot.web; 523 | 524 | import org.junit.Test; 525 | import org.junit.runner.RunWith; 526 | import org.springframework.boot.test.context.SpringBootTest; 527 | import org.springframework.test.context.junit4.SpringRunner; 528 | 529 | @RunWith(SpringRunner.class) 530 | @SpringBootTest 531 | public class SpringBootFirstWebApplicationTests { 532 | 533 | @Test 534 | public void contextLoads() { 535 | } 536 | 537 | } 538 | ``` 539 | --- 540 | ### todo.txt 541 | 542 | ``` 543 | LoginController -> adds name to model 544 | welcome.jsp -> shows ${name} 545 | 546 | TodoController -> redirects to list-todos.jsp 547 | ${name} is empty 548 | 549 | 550 | Component, Service, Repository, Controller 551 | Autowired 552 | ComponentScan 553 | 554 | Field dummyService in com.in28minutes.springboot.web.controller.LoginController 555 | required a bean of type 'com.in28minutes.dummy.DummyService' 556 | that could not be found. 557 | 558 | Spring Boot Starter Parent 559 | Spring Boot Starter Web 560 | @SpringBootApplication 561 | Auto Configuration 562 | 563 | Dispatcher Servlet 564 | 565 | /login => "login" 566 | 567 | "login" => src/main/webapp/WEB-INF/jsp/login.jsp 568 | 569 | 570 | Search for a view named "login" 571 | 572 | 573 | 574 | /login => LoginController 575 | ``` 576 | --- 577 | -------------------------------------------------------------------------------- /Step18.md: -------------------------------------------------------------------------------- 1 | ## What we will do: 2 | - Lets use a command bean for Todo 3 | - Add Validations 4 | - 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. 5 | - org.hibernate:hibernate-validator 6 | 7 | ## Useful Snippets 8 | ``` 9 | <%@taglib uri="http://www.springframework.org/tags/form" prefix="form"%> 10 | 11 | 12 |
13 | Description 14 | 15 |
16 |
17 | 18 | @Size(min = 10, message = "Enter atleast 10 Characters.") 19 | 20 | @Valid Todo todo, BindingResult result 21 | 22 | if (result.hasErrors()) 23 | return "todo"; 24 | 25 | 26 | 27 | ``` 28 | ## Files List 29 | 30 | ### pom.xml 31 | 32 | ```xml 33 | 34 | 36 | 4.0.0 37 | 38 | com.in28minutes.springboot.web 39 | spring-boot-first-web-application 40 | 0.0.1-SNAPSHOT 41 | jar 42 | 43 | spring-boot-first-web-application 44 | Demo project for Spring Boot 45 | 46 | 47 | org.springframework.boot 48 | spring-boot-starter-parent 49 | 1.4.3.RELEASE 50 | 51 | 52 | 53 | 54 | UTF-8 55 | UTF-8 56 | 1.8 57 | 58 | 59 | 60 | 61 | org.springframework.boot 62 | spring-boot-starter-web 63 | 64 | 65 | 66 | javax.servlet 67 | jstl 68 | 69 | 70 | 71 | org.webjars 72 | bootstrap 73 | 3.3.6 74 | 75 | 76 | 77 | org.webjars 78 | jquery 79 | 1.9.1 80 | 81 | 82 | 83 | org.apache.tomcat.embed 84 | tomcat-embed-jasper 85 | provided 86 | 87 | 88 | 89 | org.springframework.boot 90 | spring-boot-devtools 91 | runtime 92 | 93 | 94 | 95 | org.springframework.boot 96 | spring-boot-starter-test 97 | test 98 | 99 | 100 | 101 | 102 | 103 | 104 | org.springframework.boot 105 | spring-boot-maven-plugin 106 | 107 | 108 | 109 | 110 | 111 | ``` 112 | --- 113 | ### src/main/java/com/in28minutes/springboot/web/controller/LoginController.java 114 | 115 | ```java 116 | package com.in28minutes.springboot.web.controller; 117 | 118 | import org.springframework.beans.factory.annotation.Autowired; 119 | import org.springframework.stereotype.Controller; 120 | import org.springframework.ui.ModelMap; 121 | import org.springframework.web.bind.annotation.RequestMapping; 122 | import org.springframework.web.bind.annotation.RequestMethod; 123 | import org.springframework.web.bind.annotation.RequestParam; 124 | import org.springframework.web.bind.annotation.SessionAttributes; 125 | 126 | import com.in28minutes.springboot.web.service.LoginService; 127 | 128 | @Controller 129 | @SessionAttributes("name") 130 | public class LoginController { 131 | 132 | @Autowired 133 | LoginService service; 134 | 135 | @RequestMapping(value="/login", method = RequestMethod.GET) 136 | public String showLoginPage(ModelMap model){ 137 | return "login"; 138 | } 139 | 140 | @RequestMapping(value="/login", method = RequestMethod.POST) 141 | public String showWelcomePage(ModelMap model, @RequestParam String name, @RequestParam String password){ 142 | 143 | boolean isValidUser = service.validateUser(name, password); 144 | 145 | if (!isValidUser) { 146 | model.put("errorMessage", "Invalid Credentials"); 147 | return "login"; 148 | } 149 | 150 | model.put("name", name); 151 | model.put("password", password); 152 | 153 | return "welcome"; 154 | } 155 | 156 | } 157 | ``` 158 | --- 159 | ### src/main/java/com/in28minutes/springboot/web/controller/TodoController.java 160 | 161 | ```java 162 | package com.in28minutes.springboot.web.controller; 163 | 164 | import java.util.Date; 165 | 166 | import javax.validation.Valid; 167 | 168 | import org.springframework.beans.factory.annotation.Autowired; 169 | import org.springframework.stereotype.Controller; 170 | import org.springframework.ui.ModelMap; 171 | import org.springframework.validation.BindingResult; 172 | import org.springframework.web.bind.annotation.RequestMapping; 173 | import org.springframework.web.bind.annotation.RequestMethod; 174 | import org.springframework.web.bind.annotation.RequestParam; 175 | import org.springframework.web.bind.annotation.SessionAttributes; 176 | 177 | import com.in28minutes.springboot.web.model.Todo; 178 | import com.in28minutes.springboot.web.service.LoginService; 179 | import com.in28minutes.springboot.web.service.TodoService; 180 | 181 | @Controller 182 | @SessionAttributes("name") 183 | public class TodoController { 184 | 185 | @Autowired 186 | TodoService service; 187 | 188 | @RequestMapping(value = "/list-todos", method = RequestMethod.GET) 189 | public String showTodos(ModelMap model) { 190 | String name = (String) model.get("name"); 191 | model.put("todos", service.retrieveTodos(name)); 192 | return "list-todos"; 193 | } 194 | 195 | @RequestMapping(value = "/add-todo", method = RequestMethod.GET) 196 | public String showAddTodoPage(ModelMap model) { 197 | model.addAttribute("todo", new Todo(0, (String) model.get("name"), "Default Desc", 198 | new Date(), false)); 199 | return "todo"; 200 | } 201 | 202 | @RequestMapping(value = "/delete-todo", method = RequestMethod.GET) 203 | public String deleteTodo(@RequestParam int id) { 204 | service.deleteTodo(id); 205 | return "redirect:/list-todos"; 206 | } 207 | 208 | @RequestMapping(value = "/add-todo", method = RequestMethod.POST) 209 | public String addTodo(ModelMap model, @Valid Todo todo, BindingResult result) { 210 | 211 | if(result.hasErrors()){ 212 | return "todo"; 213 | } 214 | 215 | service.addTodo((String) model.get("name"), todo.getDesc(), new Date(), 216 | false); 217 | return "redirect:/list-todos"; 218 | } 219 | } 220 | ``` 221 | --- 222 | ### src/main/java/com/in28minutes/springboot/web/model/Todo.java 223 | 224 | ```java 225 | package com.in28minutes.springboot.web.model; 226 | 227 | import java.util.Date; 228 | 229 | import javax.validation.constraints.Size; 230 | 231 | public class Todo { 232 | private int id; 233 | private String user; 234 | 235 | @Size(min=10, message="Enter at least 10 Characters...") 236 | private String desc; 237 | 238 | private Date targetDate; 239 | private boolean isDone; 240 | 241 | public Todo() { 242 | super(); 243 | } 244 | 245 | public Todo(int id, String user, String desc, Date targetDate, 246 | boolean isDone) { 247 | super(); 248 | this.id = id; 249 | this.user = user; 250 | this.desc = desc; 251 | this.targetDate = targetDate; 252 | this.isDone = isDone; 253 | } 254 | 255 | public int getId() { 256 | return id; 257 | } 258 | 259 | public void setId(int id) { 260 | this.id = id; 261 | } 262 | 263 | public String getUser() { 264 | return user; 265 | } 266 | 267 | public void setUser(String user) { 268 | this.user = user; 269 | } 270 | 271 | public String getDesc() { 272 | return desc; 273 | } 274 | 275 | public void setDesc(String desc) { 276 | this.desc = desc; 277 | } 278 | 279 | public Date getTargetDate() { 280 | return targetDate; 281 | } 282 | 283 | public void setTargetDate(Date targetDate) { 284 | this.targetDate = targetDate; 285 | } 286 | 287 | public boolean isDone() { 288 | return isDone; 289 | } 290 | 291 | public void setDone(boolean isDone) { 292 | this.isDone = isDone; 293 | } 294 | 295 | @Override 296 | public int hashCode() { 297 | final int prime = 31; 298 | int result = 1; 299 | result = prime * result + id; 300 | return result; 301 | } 302 | 303 | @Override 304 | public boolean equals(Object obj) { 305 | if (this == obj) { 306 | return true; 307 | } 308 | if (obj == null) { 309 | return false; 310 | } 311 | if (getClass() != obj.getClass()) { 312 | return false; 313 | } 314 | Todo other = (Todo) obj; 315 | if (id != other.id) { 316 | return false; 317 | } 318 | return true; 319 | } 320 | 321 | @Override 322 | public String toString() { 323 | return String.format( 324 | "Todo [id=%s, user=%s, desc=%s, targetDate=%s, isDone=%s]", id, 325 | user, desc, targetDate, isDone); 326 | } 327 | 328 | } 329 | ``` 330 | --- 331 | ### src/main/java/com/in28minutes/springboot/web/service/LoginService.java 332 | 333 | ```java 334 | package com.in28minutes.springboot.web.service; 335 | 336 | import org.springframework.stereotype.Component; 337 | import org.springframework.stereotype.Service; 338 | 339 | @Service 340 | public class LoginService { 341 | 342 | public boolean validateUser(String userid, String password) { 343 | // in28minutes, dummy 344 | return userid.equalsIgnoreCase("in28minutes") 345 | && password.equalsIgnoreCase("dummy"); 346 | } 347 | 348 | } 349 | ``` 350 | --- 351 | ### src/main/java/com/in28minutes/springboot/web/service/TodoService.java 352 | 353 | ```java 354 | package com.in28minutes.springboot.web.service; 355 | 356 | import java.util.ArrayList; 357 | import java.util.Date; 358 | import java.util.Iterator; 359 | import java.util.List; 360 | 361 | import org.springframework.stereotype.Service; 362 | 363 | import com.in28minutes.springboot.web.model.Todo; 364 | 365 | @Service 366 | public class TodoService { 367 | private static List todos = new ArrayList(); 368 | private static int todoCount = 3; 369 | 370 | static { 371 | todos.add(new Todo(1, "in28Minutes", "Learn Spring MVC", new Date(), 372 | false)); 373 | todos.add(new Todo(2, "in28Minutes", "Learn Struts", new Date(), false)); 374 | todos.add(new Todo(3, "in28Minutes", "Learn Hibernate", new Date(), 375 | false)); 376 | } 377 | 378 | public List retrieveTodos(String user) { 379 | List filteredTodos = new ArrayList(); 380 | for (Todo todo : todos) { 381 | if (todo.getUser().equals(user)) { 382 | filteredTodos.add(todo); 383 | } 384 | } 385 | return filteredTodos; 386 | } 387 | 388 | public void addTodo(String name, String desc, Date targetDate, 389 | boolean isDone) { 390 | todos.add(new Todo(++todoCount, name, desc, targetDate, isDone)); 391 | } 392 | 393 | public void deleteTodo(int id) { 394 | Iterator iterator = todos.iterator(); 395 | while (iterator.hasNext()) { 396 | Todo todo = iterator.next(); 397 | if (todo.getId() == id) { 398 | iterator.remove(); 399 | } 400 | } 401 | } 402 | } 403 | ``` 404 | --- 405 | ### src/main/java/com/in28minutes/springboot/web/SpringBootFirstWebApplication.java 406 | 407 | ```java 408 | package com.in28minutes.springboot.web; 409 | 410 | import org.springframework.boot.SpringApplication; 411 | import org.springframework.boot.autoconfigure.SpringBootApplication; 412 | import org.springframework.context.annotation.ComponentScan; 413 | 414 | @SpringBootApplication 415 | @ComponentScan("com.in28minutes.springboot.web") 416 | public class SpringBootFirstWebApplication { 417 | 418 | public static void main(String[] args) { 419 | SpringApplication.run(SpringBootFirstWebApplication.class, args); 420 | } 421 | } 422 | ``` 423 | --- 424 | ### src/main/resources/application.properties 425 | 426 | ``` 427 | spring.mvc.view.prefix=/WEB-INF/jsp/ 428 | spring.mvc.view.suffix=.jsp 429 | logging.level.org.springframework.web=INFO 430 | ``` 431 | --- 432 | ### src/main/webapp/WEB-INF/jsp/list-todos.jsp 433 | 434 | ``` 435 | <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%> 436 | 437 | 438 | 439 | 440 | Todo's for ${name} 441 | 443 | 444 | 445 | 446 |
447 | 448 | 449 | 450 | 451 | 452 | 453 | 454 | 455 | 456 | 457 | 458 | 459 | 460 | 461 | 462 | 463 | 465 | 466 | 467 | 468 |
Your todos are
DescriptionTarget DateIs it Done?
${todo.desc}${todo.targetDate}${todo.done}Delete
469 |
470 | Add a Todo 471 |
472 |
473 | 474 | 475 | 476 | 477 | 478 | 479 | 480 | ``` 481 | --- 482 | ### src/main/webapp/WEB-INF/jsp/login.jsp 483 | 484 | ``` 485 | 486 | 487 | 488 | First Web Application 489 | 490 | 491 | 492 | ${errorMessage} 493 |
494 | Name : 495 | Password : 496 | 497 |
498 | 499 | 500 | 501 | ``` 502 | --- 503 | ### src/main/webapp/WEB-INF/jsp/todo.jsp 504 | 505 | ``` 506 | <%@taglib uri="http://www.springframework.org/tags/form" prefix="form"%> 507 | 508 | 509 | 510 | First Web Application 511 | 513 | 514 | 515 | 516 | 517 |
518 | 519 |
520 | Description 521 | 523 | 524 |
525 | 526 | 527 |
528 |
529 | 530 | 531 | 532 | 533 | 534 | 535 | 536 | ``` 537 | --- 538 | ### src/main/webapp/WEB-INF/jsp/welcome.jsp 539 | 540 | ``` 541 | 542 | 543 | 544 | First Web Application 545 | 546 | 547 | 548 | Welcome ${name}!! Click here to manage your todo's. 549 | 550 | 551 | 552 | ``` 553 | --- 554 | ### src/test/java/com/in28minutes/springboot/web/SpringBootFirstWebApplicationTests.java 555 | 556 | ```java 557 | package com.in28minutes.springboot.web; 558 | 559 | import org.junit.Test; 560 | import org.junit.runner.RunWith; 561 | import org.springframework.boot.test.context.SpringBootTest; 562 | import org.springframework.test.context.junit4.SpringRunner; 563 | 564 | @RunWith(SpringRunner.class) 565 | @SpringBootTest 566 | public class SpringBootFirstWebApplicationTests { 567 | 568 | @Test 569 | public void contextLoads() { 570 | } 571 | 572 | } 573 | ``` 574 | --- 575 | ### todo.txt 576 | 577 | ``` 578 | Implementing Server Side Validation 579 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 580 | Command Bean or Form Backing Bean 581 | 582 | Add Validation 583 | Use Validation on Controller 584 | Display Errors in View 585 | 586 | Command Bean 587 | ~~~~~~~~~~~~ 588 | Controller 589 | View - Spring Form Tags 590 | 591 | 592 | 593 | LoginController -> adds name to model 594 | welcome.jsp -> shows ${name} 595 | 596 | TodoController -> redirects to list-todos.jsp 597 | ${name} is empty 598 | 599 | 600 | Component, Service, Repository, Controller 601 | Autowired 602 | ComponentScan 603 | 604 | Field dummyService in com.in28minutes.springboot.web.controller.LoginController 605 | required a bean of type 'com.in28minutes.dummy.DummyService' 606 | that could not be found. 607 | 608 | Spring Boot Starter Parent 609 | Spring Boot Starter Web 610 | @SpringBootApplication 611 | Auto Configuration 612 | 613 | Dispatcher Servlet 614 | 615 | /login => "login" 616 | 617 | "login" => src/main/webapp/WEB-INF/jsp/login.jsp 618 | 619 | 620 | Search for a view named "login" 621 | 622 | 623 | 624 | /login => LoginController 625 | ``` 626 | --- 627 | -------------------------------------------------------------------------------- /Step18.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/in28minutes/SpringBootWebApplicationStepByStep/7611e5a098e49023ee03ce1446e465a4c567da45/Step18.zip -------------------------------------------------------------------------------- /Step19.md: -------------------------------------------------------------------------------- 1 | ## What we will do: 2 | - Add Update Functionality 3 | - Lets Use the Same JSP as earlier. 4 | 5 | ## Useful Snippets 6 | ``` 7 | public Todo retrieveTodo(int id) { 8 | for (Todo todo : todos) { 9 | if (todo.getId() == id) 10 | return todo; 11 | } 12 | return null; 13 | } 14 | 15 | public void updateTodo(Todo todo) { 16 | todos.remove(todo); 17 | todos.add(todo); 18 | } 19 | 20 | todo.setUser("in28Minutes"); //TODO:Remove Hardcoding Later 21 | service.updateTodo(todo); 22 | 23 | 24 | ``` 25 | 26 | ## Files List 27 | 28 | ### pom.xml 29 | 30 | ```xml 31 | 32 | 34 | 4.0.0 35 | 36 | com.in28minutes.springboot.web 37 | spring-boot-first-web-application 38 | 0.0.1-SNAPSHOT 39 | jar 40 | 41 | spring-boot-first-web-application 42 | Demo project for Spring Boot 43 | 44 | 45 | org.springframework.boot 46 | spring-boot-starter-parent 47 | 1.4.3.RELEASE 48 | 49 | 50 | 51 | 52 | UTF-8 53 | UTF-8 54 | 1.8 55 | 56 | 57 | 58 | 59 | org.springframework.boot 60 | spring-boot-starter-web 61 | 62 | 63 | 64 | javax.servlet 65 | jstl 66 | 67 | 68 | 69 | org.webjars 70 | bootstrap 71 | 3.3.6 72 | 73 | 74 | 75 | org.webjars 76 | jquery 77 | 1.9.1 78 | 79 | 80 | 81 | org.apache.tomcat.embed 82 | tomcat-embed-jasper 83 | provided 84 | 85 | 86 | 87 | org.springframework.boot 88 | spring-boot-devtools 89 | runtime 90 | 91 | 92 | 93 | org.springframework.boot 94 | spring-boot-starter-test 95 | test 96 | 97 | 98 | 99 | 100 | 101 | 102 | org.springframework.boot 103 | spring-boot-maven-plugin 104 | 105 | 106 | 107 | 108 | 109 | ``` 110 | --- 111 | ### src/main/java/com/in28minutes/springboot/web/controller/LoginController.java 112 | 113 | ```java 114 | package com.in28minutes.springboot.web.controller; 115 | 116 | import org.springframework.beans.factory.annotation.Autowired; 117 | import org.springframework.stereotype.Controller; 118 | import org.springframework.ui.ModelMap; 119 | import org.springframework.web.bind.annotation.RequestMapping; 120 | import org.springframework.web.bind.annotation.RequestMethod; 121 | import org.springframework.web.bind.annotation.RequestParam; 122 | import org.springframework.web.bind.annotation.SessionAttributes; 123 | 124 | import com.in28minutes.springboot.web.service.LoginService; 125 | 126 | @Controller 127 | @SessionAttributes("name") 128 | public class LoginController { 129 | 130 | @Autowired 131 | LoginService service; 132 | 133 | @RequestMapping(value="/login", method = RequestMethod.GET) 134 | public String showLoginPage(ModelMap model){ 135 | return "login"; 136 | } 137 | 138 | @RequestMapping(value="/login", method = RequestMethod.POST) 139 | public String showWelcomePage(ModelMap model, @RequestParam String name, @RequestParam String password){ 140 | 141 | boolean isValidUser = service.validateUser(name, password); 142 | 143 | if (!isValidUser) { 144 | model.put("errorMessage", "Invalid Credentials"); 145 | return "login"; 146 | } 147 | 148 | model.put("name", name); 149 | model.put("password", password); 150 | 151 | return "welcome"; 152 | } 153 | 154 | } 155 | ``` 156 | --- 157 | ### src/main/java/com/in28minutes/springboot/web/controller/TodoController.java 158 | 159 | ```java 160 | package com.in28minutes.springboot.web.controller; 161 | 162 | import java.util.Date; 163 | 164 | import javax.validation.Valid; 165 | 166 | import org.springframework.beans.factory.annotation.Autowired; 167 | import org.springframework.stereotype.Controller; 168 | import org.springframework.ui.ModelMap; 169 | import org.springframework.validation.BindingResult; 170 | import org.springframework.web.bind.annotation.RequestMapping; 171 | import org.springframework.web.bind.annotation.RequestMethod; 172 | import org.springframework.web.bind.annotation.RequestParam; 173 | import org.springframework.web.bind.annotation.SessionAttributes; 174 | 175 | import com.in28minutes.springboot.web.model.Todo; 176 | import com.in28minutes.springboot.web.service.LoginService; 177 | import com.in28minutes.springboot.web.service.TodoService; 178 | 179 | @Controller 180 | @SessionAttributes("name") 181 | public class TodoController { 182 | 183 | @Autowired 184 | TodoService service; 185 | 186 | @RequestMapping(value = "/list-todos", method = RequestMethod.GET) 187 | public String showTodos(ModelMap model) { 188 | String name = (String) model.get("name"); 189 | model.put("todos", service.retrieveTodos(name)); 190 | return "list-todos"; 191 | } 192 | 193 | @RequestMapping(value = "/add-todo", method = RequestMethod.GET) 194 | public String showAddTodoPage(ModelMap model) { 195 | model.addAttribute("todo", new Todo(0, (String) model.get("name"), 196 | "Default Desc", new Date(), false)); 197 | return "todo"; 198 | } 199 | 200 | @RequestMapping(value = "/delete-todo", method = RequestMethod.GET) 201 | public String deleteTodo(@RequestParam int id) { 202 | service.deleteTodo(id); 203 | return "redirect:/list-todos"; 204 | } 205 | 206 | @RequestMapping(value = "/update-todo", method = RequestMethod.GET) 207 | public String showUpdateTodoPage(@RequestParam int id, ModelMap model) { 208 | Todo todo = service.retrieveTodo(id); 209 | model.put("todo", todo); 210 | return "todo"; 211 | } 212 | 213 | @RequestMapping(value = "/update-todo", method = RequestMethod.POST) 214 | public String updateTodo(ModelMap model, @Valid Todo todo, BindingResult result) { 215 | 216 | if (result.hasErrors()) { 217 | return "todo"; 218 | } 219 | 220 | todo.setUser((String) model.get("name")); 221 | 222 | service.updateTodo(todo); 223 | 224 | return "redirect:/list-todos"; 225 | } 226 | 227 | @RequestMapping(value = "/add-todo", method = RequestMethod.POST) 228 | public String addTodo(ModelMap model, @Valid Todo todo, BindingResult result) { 229 | 230 | if (result.hasErrors()) { 231 | return "todo"; 232 | } 233 | 234 | service.addTodo((String) model.get("name"), todo.getDesc(), new Date(), 235 | false); 236 | return "redirect:/list-todos"; 237 | } 238 | } 239 | ``` 240 | --- 241 | ### src/main/java/com/in28minutes/springboot/web/model/Todo.java 242 | 243 | ```java 244 | package com.in28minutes.springboot.web.model; 245 | 246 | import java.util.Date; 247 | 248 | import javax.validation.constraints.Size; 249 | 250 | public class Todo { 251 | private int id; 252 | private String user; 253 | 254 | @Size(min=10, message="Enter at least 10 Characters...") 255 | private String desc; 256 | 257 | private Date targetDate; 258 | private boolean isDone; 259 | 260 | public Todo() { 261 | super(); 262 | } 263 | 264 | public Todo(int id, String user, String desc, Date targetDate, 265 | boolean isDone) { 266 | super(); 267 | this.id = id; 268 | this.user = user; 269 | this.desc = desc; 270 | this.targetDate = targetDate; 271 | this.isDone = isDone; 272 | } 273 | 274 | public int getId() { 275 | return id; 276 | } 277 | 278 | public void setId(int id) { 279 | this.id = id; 280 | } 281 | 282 | public String getUser() { 283 | return user; 284 | } 285 | 286 | public void setUser(String user) { 287 | this.user = user; 288 | } 289 | 290 | public String getDesc() { 291 | return desc; 292 | } 293 | 294 | public void setDesc(String desc) { 295 | this.desc = desc; 296 | } 297 | 298 | public Date getTargetDate() { 299 | return targetDate; 300 | } 301 | 302 | public void setTargetDate(Date targetDate) { 303 | this.targetDate = targetDate; 304 | } 305 | 306 | public boolean isDone() { 307 | return isDone; 308 | } 309 | 310 | public void setDone(boolean isDone) { 311 | this.isDone = isDone; 312 | } 313 | 314 | @Override 315 | public int hashCode() { 316 | final int prime = 31; 317 | int result = 1; 318 | result = prime * result + id; 319 | return result; 320 | } 321 | 322 | @Override 323 | public boolean equals(Object obj) { 324 | if (this == obj) { 325 | return true; 326 | } 327 | if (obj == null) { 328 | return false; 329 | } 330 | if (getClass() != obj.getClass()) { 331 | return false; 332 | } 333 | Todo other = (Todo) obj; 334 | if (id != other.id) { 335 | return false; 336 | } 337 | return true; 338 | } 339 | 340 | @Override 341 | public String toString() { 342 | return String.format( 343 | "Todo [id=%s, user=%s, desc=%s, targetDate=%s, isDone=%s]", id, 344 | user, desc, targetDate, isDone); 345 | } 346 | 347 | } 348 | ``` 349 | --- 350 | ### src/main/java/com/in28minutes/springboot/web/service/LoginService.java 351 | 352 | ```java 353 | package com.in28minutes.springboot.web.service; 354 | 355 | import org.springframework.stereotype.Component; 356 | import org.springframework.stereotype.Service; 357 | 358 | @Service 359 | public class LoginService { 360 | 361 | public boolean validateUser(String userid, String password) { 362 | // in28minutes, dummy 363 | return userid.equalsIgnoreCase("in28minutes") 364 | && password.equalsIgnoreCase("dummy"); 365 | } 366 | 367 | } 368 | ``` 369 | --- 370 | ### src/main/java/com/in28minutes/springboot/web/service/TodoService.java 371 | 372 | ```java 373 | package com.in28minutes.springboot.web.service; 374 | 375 | import java.util.ArrayList; 376 | import java.util.Date; 377 | import java.util.Iterator; 378 | import java.util.List; 379 | 380 | import org.springframework.stereotype.Service; 381 | 382 | import com.in28minutes.springboot.web.model.Todo; 383 | 384 | @Service 385 | public class TodoService { 386 | private static List todos = new ArrayList(); 387 | private static int todoCount = 3; 388 | 389 | static { 390 | todos.add(new Todo(1, "in28Minutes", "Learn Spring MVC", new Date(), 391 | false)); 392 | todos.add(new Todo(2, "in28Minutes", "Learn Struts", new Date(), false)); 393 | todos.add(new Todo(3, "in28Minutes", "Learn Hibernate", new Date(), 394 | false)); 395 | } 396 | 397 | public List retrieveTodos(String user) { 398 | List filteredTodos = new ArrayList(); 399 | for (Todo todo : todos) { 400 | if (todo.getUser().equalsIgnoreCase(user)) { 401 | filteredTodos.add(todo); 402 | } 403 | } 404 | return filteredTodos; 405 | } 406 | 407 | public Todo retrieveTodo(int id) { 408 | for (Todo todo : todos) { 409 | if (todo.getId()==id) { 410 | return todo; 411 | } 412 | } 413 | return null; 414 | } 415 | 416 | public void updateTodo(Todo todo){ 417 | todos.remove(todo); 418 | todos.add(todo); 419 | } 420 | 421 | public void addTodo(String name, String desc, Date targetDate, 422 | boolean isDone) { 423 | todos.add(new Todo(++todoCount, name, desc, targetDate, isDone)); 424 | } 425 | 426 | public void deleteTodo(int id) { 427 | Iterator iterator = todos.iterator(); 428 | while (iterator.hasNext()) { 429 | Todo todo = iterator.next(); 430 | if (todo.getId() == id) { 431 | iterator.remove(); 432 | } 433 | } 434 | } 435 | } 436 | ``` 437 | --- 438 | ### src/main/java/com/in28minutes/springboot/web/SpringBootFirstWebApplication.java 439 | 440 | ```java 441 | package com.in28minutes.springboot.web; 442 | 443 | import org.springframework.boot.SpringApplication; 444 | import org.springframework.boot.autoconfigure.SpringBootApplication; 445 | import org.springframework.context.annotation.ComponentScan; 446 | 447 | @SpringBootApplication 448 | @ComponentScan("com.in28minutes.springboot.web") 449 | public class SpringBootFirstWebApplication { 450 | 451 | public static void main(String[] args) { 452 | SpringApplication.run(SpringBootFirstWebApplication.class, args); 453 | } 454 | } 455 | ``` 456 | --- 457 | ### src/main/resources/application.properties 458 | 459 | ``` 460 | spring.mvc.view.prefix=/WEB-INF/jsp/ 461 | spring.mvc.view.suffix=.jsp 462 | logging.level.org.springframework.web=INFO 463 | ``` 464 | --- 465 | ### src/main/webapp/WEB-INF/jsp/list-todos.jsp 466 | 467 | ``` 468 | <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%> 469 | 470 | 471 | 472 | 473 | Todo's for ${name} 474 | 476 | 477 | 478 | 479 |
480 | 481 | 482 | 483 | 484 | 485 | 486 | 487 | 488 | 489 | 490 | 491 | 492 | 493 | 494 | 495 | 496 | 497 | 499 | 501 | 502 | 503 | 504 |
Your todos are
DescriptionTarget DateIs it Done?
${todo.desc}${todo.targetDate}${todo.done}UpdateDelete
505 |
506 | Add a Todo 507 |
508 |
509 | 510 | 511 | 512 | 513 | 514 | 515 | 516 | ``` 517 | --- 518 | ### src/main/webapp/WEB-INF/jsp/login.jsp 519 | 520 | ``` 521 | 522 | 523 | 524 | First Web Application 525 | 526 | 527 | 528 | ${errorMessage} 529 |
530 | Name : 531 | Password : 532 | 533 |
534 | 535 | 536 | 537 | ``` 538 | --- 539 | ### src/main/webapp/WEB-INF/jsp/todo.jsp 540 | 541 | ``` 542 | <%@taglib uri="http://www.springframework.org/tags/form" prefix="form"%> 543 | 544 | 545 | 546 | First Web Application 547 | 549 | 550 | 551 | 552 | 553 |
554 | 555 | 556 | 557 | 558 |
559 | Description 560 | 562 | 563 |
564 | 565 | 566 |
567 |
568 | 569 | 570 | 571 | 572 | 573 | 574 | 575 | ``` 576 | --- 577 | ### src/main/webapp/WEB-INF/jsp/welcome.jsp 578 | 579 | ``` 580 | 581 | 582 | 583 | First Web Application 584 | 585 | 586 | 587 | Welcome ${name}!! Click here to manage your todo's. 588 | 589 | 590 | 591 | ``` 592 | --- 593 | ### src/test/java/com/in28minutes/springboot/web/SpringBootFirstWebApplicationTests.java 594 | 595 | ```java 596 | package com.in28minutes.springboot.web; 597 | 598 | import org.junit.Test; 599 | import org.junit.runner.RunWith; 600 | import org.springframework.boot.test.context.SpringBootTest; 601 | import org.springframework.test.context.junit4.SpringRunner; 602 | 603 | @RunWith(SpringRunner.class) 604 | @SpringBootTest 605 | public class SpringBootFirstWebApplicationTests { 606 | 607 | @Test 608 | public void contextLoads() { 609 | } 610 | 611 | } 612 | ``` 613 | --- 614 | ### todo.txt 615 | 616 | ``` 617 | Implementing Server Side Validation 618 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 619 | Command Bean or Form Backing Bean 620 | 621 | Add Validation 622 | Use Validation on Controller 623 | Display Errors in View 624 | 625 | Command Bean 626 | ~~~~~~~~~~~~ 627 | Controller 628 | View - Spring Form Tags 629 | 630 | 631 | 632 | LoginController -> adds name to model 633 | welcome.jsp -> shows ${name} 634 | 635 | TodoController -> redirects to list-todos.jsp 636 | ${name} is empty 637 | 638 | 639 | Component, Service, Repository, Controller 640 | Autowired 641 | ComponentScan 642 | 643 | Field dummyService in com.in28minutes.springboot.web.controller.LoginController 644 | required a bean of type 'com.in28minutes.dummy.DummyService' 645 | that could not be found. 646 | 647 | Spring Boot Starter Parent 648 | Spring Boot Starter Web 649 | @SpringBootApplication 650 | Auto Configuration 651 | 652 | Dispatcher Servlet 653 | 654 | /login => "login" 655 | 656 | "login" => src/main/webapp/WEB-INF/jsp/login.jsp 657 | 658 | 659 | Search for a view named "login" 660 | 661 | 662 | 663 | /login => LoginController 664 | ``` 665 | --- 666 | -------------------------------------------------------------------------------- /Step19.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/in28minutes/SpringBootWebApplicationStepByStep/7611e5a098e49023ee03ce1446e465a4c567da45/Step19.zip -------------------------------------------------------------------------------- /Step20.md: -------------------------------------------------------------------------------- 1 | ## What we will do: 2 | - Make real use of the Target Date Field 3 | - initBinder method 4 | 5 | ## Useful Snippets 6 | ``` 7 | <%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt"%> 8 | 10 | 11 | 12 | @InitBinder 13 | protected void initBinder(WebDataBinder binder) { 14 | SimpleDateFormat dateFormat = new SimpleDateFormat("dd/MM/yyyy"); 15 | binder.registerCustomEditor(Date.class, new CustomDateEditor( 16 | dateFormat, false)); 17 | } 18 | 19 | 20 | org.webjars 21 | bootstrap-datepicker 22 | 1.0.1 23 | 24 | 25 | 27 | 28 | 33 | 34 | ``` 35 | 36 | ## Files List 37 | 38 | ### pom.xml 39 | 40 | ```xml 41 | 42 | 44 | 4.0.0 45 | 46 | com.in28minutes.springboot.web 47 | spring-boot-first-web-application 48 | 0.0.1-SNAPSHOT 49 | jar 50 | 51 | spring-boot-first-web-application 52 | Demo project for Spring Boot 53 | 54 | 55 | org.springframework.boot 56 | spring-boot-starter-parent 57 | 1.4.3.RELEASE 58 | 59 | 60 | 61 | 62 | UTF-8 63 | UTF-8 64 | 1.8 65 | 66 | 67 | 68 | 69 | org.springframework.boot 70 | spring-boot-starter-web 71 | 72 | 73 | 74 | javax.servlet 75 | jstl 76 | 77 | 78 | 79 | org.webjars 80 | bootstrap 81 | 3.3.6 82 | 83 | 84 | 85 | org.webjars 86 | bootstrap-datepicker 87 | 1.0.1 88 | 89 | 90 | 91 | org.webjars 92 | jquery 93 | 1.9.1 94 | 95 | 96 | 97 | org.apache.tomcat.embed 98 | tomcat-embed-jasper 99 | provided 100 | 101 | 102 | 103 | org.springframework.boot 104 | spring-boot-devtools 105 | runtime 106 | 107 | 108 | 109 | org.springframework.boot 110 | spring-boot-starter-test 111 | test 112 | 113 | 114 | 115 | 116 | 117 | 118 | org.springframework.boot 119 | spring-boot-maven-plugin 120 | 121 | 122 | 123 | 124 | 125 | ``` 126 | --- 127 | ### src/main/java/com/in28minutes/springboot/web/controller/LoginController.java 128 | 129 | ```java 130 | package com.in28minutes.springboot.web.controller; 131 | 132 | import org.springframework.beans.factory.annotation.Autowired; 133 | import org.springframework.stereotype.Controller; 134 | import org.springframework.ui.ModelMap; 135 | import org.springframework.web.bind.annotation.RequestMapping; 136 | import org.springframework.web.bind.annotation.RequestMethod; 137 | import org.springframework.web.bind.annotation.RequestParam; 138 | import org.springframework.web.bind.annotation.SessionAttributes; 139 | 140 | import com.in28minutes.springboot.web.service.LoginService; 141 | 142 | @Controller 143 | @SessionAttributes("name") 144 | public class LoginController { 145 | 146 | @Autowired 147 | LoginService service; 148 | 149 | @RequestMapping(value="/login", method = RequestMethod.GET) 150 | public String showLoginPage(ModelMap model){ 151 | return "login"; 152 | } 153 | 154 | @RequestMapping(value="/login", method = RequestMethod.POST) 155 | public String showWelcomePage(ModelMap model, @RequestParam String name, @RequestParam String password){ 156 | 157 | boolean isValidUser = service.validateUser(name, password); 158 | 159 | if (!isValidUser) { 160 | model.put("errorMessage", "Invalid Credentials"); 161 | return "login"; 162 | } 163 | 164 | model.put("name", name); 165 | model.put("password", password); 166 | 167 | return "welcome"; 168 | } 169 | 170 | } 171 | ``` 172 | --- 173 | ### src/main/java/com/in28minutes/springboot/web/controller/TodoController.java 174 | 175 | ```java 176 | package com.in28minutes.springboot.web.controller; 177 | 178 | import java.text.SimpleDateFormat; 179 | import java.util.Date; 180 | 181 | import javax.validation.Valid; 182 | 183 | import org.springframework.beans.factory.annotation.Autowired; 184 | import org.springframework.beans.propertyeditors.CustomDateEditor; 185 | import org.springframework.stereotype.Controller; 186 | import org.springframework.ui.ModelMap; 187 | import org.springframework.validation.BindingResult; 188 | import org.springframework.web.bind.WebDataBinder; 189 | import org.springframework.web.bind.annotation.InitBinder; 190 | import org.springframework.web.bind.annotation.RequestMapping; 191 | import org.springframework.web.bind.annotation.RequestMethod; 192 | import org.springframework.web.bind.annotation.RequestParam; 193 | import org.springframework.web.bind.annotation.SessionAttributes; 194 | 195 | import com.in28minutes.springboot.web.model.Todo; 196 | import com.in28minutes.springboot.web.service.LoginService; 197 | import com.in28minutes.springboot.web.service.TodoService; 198 | 199 | @Controller 200 | @SessionAttributes("name") 201 | public class TodoController { 202 | 203 | @Autowired 204 | TodoService service; 205 | 206 | @InitBinder 207 | public void initBinder(WebDataBinder binder) { 208 | // Date - dd/MM/yyyy 209 | SimpleDateFormat dateFormat = new SimpleDateFormat("dd/MM/yyyy"); 210 | binder.registerCustomEditor(Date.class, new CustomDateEditor( 211 | dateFormat, false)); 212 | } 213 | 214 | @RequestMapping(value = "/list-todos", method = RequestMethod.GET) 215 | public String showTodos(ModelMap model) { 216 | String name = (String) model.get("name"); 217 | model.put("todos", service.retrieveTodos(name)); 218 | return "list-todos"; 219 | } 220 | 221 | @RequestMapping(value = "/add-todo", method = RequestMethod.GET) 222 | public String showAddTodoPage(ModelMap model) { 223 | model.addAttribute("todo", new Todo(0, (String) model.get("name"), 224 | "Default Desc", new Date(), false)); 225 | return "todo"; 226 | } 227 | 228 | @RequestMapping(value = "/delete-todo", method = RequestMethod.GET) 229 | public String deleteTodo(@RequestParam int id) { 230 | service.deleteTodo(id); 231 | return "redirect:/list-todos"; 232 | } 233 | 234 | @RequestMapping(value = "/update-todo", method = RequestMethod.GET) 235 | public String showUpdateTodoPage(@RequestParam int id, ModelMap model) { 236 | Todo todo = service.retrieveTodo(id); 237 | model.put("todo", todo); 238 | return "todo"; 239 | } 240 | 241 | @RequestMapping(value = "/update-todo", method = RequestMethod.POST) 242 | public String updateTodo(ModelMap model, @Valid Todo todo, 243 | BindingResult result) { 244 | 245 | if (result.hasErrors()) { 246 | return "todo"; 247 | } 248 | 249 | todo.setUser((String) model.get("name")); 250 | 251 | service.updateTodo(todo); 252 | 253 | return "redirect:/list-todos"; 254 | } 255 | 256 | @RequestMapping(value = "/add-todo", method = RequestMethod.POST) 257 | public String addTodo(ModelMap model, @Valid Todo todo, BindingResult result) { 258 | 259 | if (result.hasErrors()) { 260 | return "todo"; 261 | } 262 | 263 | service.addTodo((String) model.get("name"), todo.getDesc(), todo.getTargetDate(), 264 | false); 265 | return "redirect:/list-todos"; 266 | } 267 | } 268 | ``` 269 | --- 270 | ### src/main/java/com/in28minutes/springboot/web/model/Todo.java 271 | 272 | ```java 273 | package com.in28minutes.springboot.web.model; 274 | 275 | import java.util.Date; 276 | 277 | import javax.validation.constraints.Size; 278 | 279 | public class Todo { 280 | private int id; 281 | private String user; 282 | 283 | @Size(min=10, message="Enter at least 10 Characters...") 284 | private String desc; 285 | 286 | private Date targetDate; 287 | private boolean isDone; 288 | 289 | public Todo() { 290 | super(); 291 | } 292 | 293 | public Todo(int id, String user, String desc, Date targetDate, 294 | boolean isDone) { 295 | super(); 296 | this.id = id; 297 | this.user = user; 298 | this.desc = desc; 299 | this.targetDate = targetDate; 300 | this.isDone = isDone; 301 | } 302 | 303 | public int getId() { 304 | return id; 305 | } 306 | 307 | public void setId(int id) { 308 | this.id = id; 309 | } 310 | 311 | public String getUser() { 312 | return user; 313 | } 314 | 315 | public void setUser(String user) { 316 | this.user = user; 317 | } 318 | 319 | public String getDesc() { 320 | return desc; 321 | } 322 | 323 | public void setDesc(String desc) { 324 | this.desc = desc; 325 | } 326 | 327 | public Date getTargetDate() { 328 | return targetDate; 329 | } 330 | 331 | public void setTargetDate(Date targetDate) { 332 | this.targetDate = targetDate; 333 | } 334 | 335 | public boolean isDone() { 336 | return isDone; 337 | } 338 | 339 | public void setDone(boolean isDone) { 340 | this.isDone = isDone; 341 | } 342 | 343 | @Override 344 | public int hashCode() { 345 | final int prime = 31; 346 | int result = 1; 347 | result = prime * result + id; 348 | return result; 349 | } 350 | 351 | @Override 352 | public boolean equals(Object obj) { 353 | if (this == obj) { 354 | return true; 355 | } 356 | if (obj == null) { 357 | return false; 358 | } 359 | if (getClass() != obj.getClass()) { 360 | return false; 361 | } 362 | Todo other = (Todo) obj; 363 | if (id != other.id) { 364 | return false; 365 | } 366 | return true; 367 | } 368 | 369 | @Override 370 | public String toString() { 371 | return String.format( 372 | "Todo [id=%s, user=%s, desc=%s, targetDate=%s, isDone=%s]", id, 373 | user, desc, targetDate, isDone); 374 | } 375 | 376 | } 377 | ``` 378 | --- 379 | ### src/main/java/com/in28minutes/springboot/web/service/LoginService.java 380 | 381 | ```java 382 | package com.in28minutes.springboot.web.service; 383 | 384 | import org.springframework.stereotype.Component; 385 | import org.springframework.stereotype.Service; 386 | 387 | @Service 388 | public class LoginService { 389 | 390 | public boolean validateUser(String userid, String password) { 391 | // in28minutes, dummy 392 | return userid.equalsIgnoreCase("in28minutes") 393 | && password.equalsIgnoreCase("dummy"); 394 | } 395 | 396 | } 397 | ``` 398 | --- 399 | ### src/main/java/com/in28minutes/springboot/web/service/TodoService.java 400 | 401 | ```java 402 | package com.in28minutes.springboot.web.service; 403 | 404 | import java.util.ArrayList; 405 | import java.util.Date; 406 | import java.util.Iterator; 407 | import java.util.List; 408 | 409 | import org.springframework.stereotype.Service; 410 | 411 | import com.in28minutes.springboot.web.model.Todo; 412 | 413 | @Service 414 | public class TodoService { 415 | private static List todos = new ArrayList(); 416 | private static int todoCount = 3; 417 | 418 | static { 419 | todos.add(new Todo(1, "in28Minutes", "Learn Spring MVC", new Date(), 420 | false)); 421 | todos.add(new Todo(2, "in28Minutes", "Learn Struts", new Date(), false)); 422 | todos.add(new Todo(3, "in28Minutes", "Learn Hibernate", new Date(), 423 | false)); 424 | } 425 | 426 | public List retrieveTodos(String user) { 427 | List filteredTodos = new ArrayList(); 428 | for (Todo todo : todos) { 429 | if (todo.getUser().equalsIgnoreCase(user)) { 430 | filteredTodos.add(todo); 431 | } 432 | } 433 | return filteredTodos; 434 | } 435 | 436 | public Todo retrieveTodo(int id) { 437 | for (Todo todo : todos) { 438 | if (todo.getId()==id) { 439 | return todo; 440 | } 441 | } 442 | return null; 443 | } 444 | 445 | public void updateTodo(Todo todo){ 446 | todos.remove(todo); 447 | todos.add(todo); 448 | } 449 | 450 | public void addTodo(String name, String desc, Date targetDate, 451 | boolean isDone) { 452 | todos.add(new Todo(++todoCount, name, desc, targetDate, isDone)); 453 | } 454 | 455 | public void deleteTodo(int id) { 456 | Iterator iterator = todos.iterator(); 457 | while (iterator.hasNext()) { 458 | Todo todo = iterator.next(); 459 | if (todo.getId() == id) { 460 | iterator.remove(); 461 | } 462 | } 463 | } 464 | } 465 | ``` 466 | --- 467 | ### src/main/java/com/in28minutes/springboot/web/SpringBootFirstWebApplication.java 468 | 469 | ```java 470 | package com.in28minutes.springboot.web; 471 | 472 | import org.springframework.boot.SpringApplication; 473 | import org.springframework.boot.autoconfigure.SpringBootApplication; 474 | import org.springframework.context.annotation.ComponentScan; 475 | 476 | @SpringBootApplication 477 | @ComponentScan("com.in28minutes.springboot.web") 478 | public class SpringBootFirstWebApplication { 479 | 480 | public static void main(String[] args) { 481 | SpringApplication.run(SpringBootFirstWebApplication.class, args); 482 | } 483 | } 484 | ``` 485 | --- 486 | ### src/main/resources/application.properties 487 | 488 | ``` 489 | spring.mvc.view.prefix=/WEB-INF/jsp/ 490 | spring.mvc.view.suffix=.jsp 491 | logging.level.org.springframework.web=INFO 492 | ``` 493 | --- 494 | ### src/main/webapp/WEB-INF/jsp/list-todos.jsp 495 | 496 | ``` 497 | <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%> 498 | <%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt"%> 499 | 500 | 501 | 502 | 503 | Todo's for ${name} 504 | 506 | 507 | 508 | 509 |
510 | 511 | 512 | 513 | 514 | 515 | 516 | 517 | 518 | 519 | 520 | 521 | 522 | 523 | 524 | 525 | 526 | 527 | 529 | 531 | 532 | 533 | 534 |
Your todos are
DescriptionTarget DateIs it Done?
${todo.desc}${todo.done}UpdateDelete
535 |
536 | Add a Todo 537 |
538 |
539 | 540 | 541 | 542 | 543 | 544 | 545 | 546 | ``` 547 | --- 548 | ### src/main/webapp/WEB-INF/jsp/login.jsp 549 | 550 | ``` 551 | 552 | 553 | 554 | First Web Application 555 | 556 | 557 | 558 | ${errorMessage} 559 |
560 | Name : 561 | Password : 562 | 563 |
564 | 565 | 566 | 567 | ``` 568 | --- 569 | ### src/main/webapp/WEB-INF/jsp/todo.jsp 570 | 571 | ``` 572 | <%@taglib uri="http://www.springframework.org/tags/form" prefix="form"%> 573 | 574 | 575 | 576 | First Web Application 577 | 579 | 580 | 581 | 582 | 583 |
584 | 585 | 586 | 587 | 588 |
589 | Description 590 | 592 | 593 |
594 | 595 |
596 | Target Date 597 | 599 | 600 |
601 | 602 | 603 |
604 |
605 | 606 | 607 | 608 | 610 | 615 | 616 | 617 | 618 | ``` 619 | --- 620 | ### src/main/webapp/WEB-INF/jsp/welcome.jsp 621 | 622 | ``` 623 | 624 | 625 | 626 | First Web Application 627 | 628 | 629 | 630 | Welcome ${name}!! Click here to manage your todo's. 631 | 632 | 633 | 634 | ``` 635 | --- 636 | ### src/test/java/com/in28minutes/springboot/web/SpringBootFirstWebApplicationTests.java 637 | 638 | ```java 639 | package com.in28minutes.springboot.web; 640 | 641 | import org.junit.Test; 642 | import org.junit.runner.RunWith; 643 | import org.springframework.boot.test.context.SpringBootTest; 644 | import org.springframework.test.context.junit4.SpringRunner; 645 | 646 | @RunWith(SpringRunner.class) 647 | @SpringBootTest 648 | public class SpringBootFirstWebApplicationTests { 649 | 650 | @Test 651 | public void contextLoads() { 652 | } 653 | 654 | } 655 | ``` 656 | --- 657 | ### todo.txt 658 | 659 | ``` 660 | Implementing Server Side Validation 661 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 662 | Command Bean or Form Backing Bean 663 | 664 | Add Validation 665 | Use Validation on Controller 666 | Display Errors in View 667 | 668 | Command Bean 669 | ~~~~~~~~~~~~ 670 | Controller 671 | View - Spring Form Tags 672 | 673 | 674 | 675 | LoginController -> adds name to model 676 | welcome.jsp -> shows ${name} 677 | 678 | TodoController -> redirects to list-todos.jsp 679 | ${name} is empty 680 | 681 | 682 | Component, Service, Repository, Controller 683 | Autowired 684 | ComponentScan 685 | 686 | Field dummyService in com.in28minutes.springboot.web.controller.LoginController 687 | required a bean of type 'com.in28minutes.dummy.DummyService' 688 | that could not be found. 689 | 690 | Spring Boot Starter Parent 691 | Spring Boot Starter Web 692 | @SpringBootApplication 693 | Auto Configuration 694 | 695 | Dispatcher Servlet 696 | 697 | /login => "login" 698 | 699 | "login" => src/main/webapp/WEB-INF/jsp/login.jsp 700 | 701 | 702 | Search for a view named "login" 703 | 704 | 705 | 706 | /login => LoginController 707 | ``` 708 | --- 709 | -------------------------------------------------------------------------------- /Step20.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/in28minutes/SpringBootWebApplicationStepByStep/7611e5a098e49023ee03ce1446e465a4c567da45/Step20.zip -------------------------------------------------------------------------------- /Step21.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/in28minutes/SpringBootWebApplicationStepByStep/7611e5a098e49023ee03ce1446e465a4c567da45/Step21.zip -------------------------------------------------------------------------------- /Step22.md: -------------------------------------------------------------------------------- 1 | ## What we will do: 2 | - Prepare for Using Spring Security 3 | - Remove All the Login Related Functionality 4 | - Make Welcome the default page - with some hardcoding to start with. 5 | - Refactor getLoggedInUserName 6 | - Update Home Page Link in navigation 7 | 8 | ## Files List 9 | 10 | ### pom.xml 11 | 12 | ```xml 13 | 14 | 16 | 4.0.0 17 | 18 | com.in28minutes.springboot.web 19 | spring-boot-first-web-application 20 | 0.0.1-SNAPSHOT 21 | jar 22 | 23 | spring-boot-first-web-application 24 | Demo project for Spring Boot 25 | 26 | 27 | org.springframework.boot 28 | spring-boot-starter-parent 29 | 1.4.3.RELEASE 30 | 31 | 32 | 33 | 34 | UTF-8 35 | UTF-8 36 | 1.8 37 | 38 | 39 | 40 | 41 | org.springframework.boot 42 | spring-boot-starter-web 43 | 44 | 45 | 46 | javax.servlet 47 | jstl 48 | 49 | 50 | 51 | org.webjars 52 | bootstrap 53 | 3.3.6 54 | 55 | 56 | 57 | org.webjars 58 | bootstrap-datepicker 59 | 1.0.1 60 | 61 | 62 | 63 | org.webjars 64 | jquery 65 | 1.9.1 66 | 67 | 68 | 69 | org.apache.tomcat.embed 70 | tomcat-embed-jasper 71 | provided 72 | 73 | 74 | 75 | org.springframework.boot 76 | spring-boot-devtools 77 | runtime 78 | 79 | 80 | 81 | org.springframework.boot 82 | spring-boot-starter-test 83 | test 84 | 85 | 86 | 87 | 88 | 89 | 90 | org.springframework.boot 91 | spring-boot-maven-plugin 92 | 93 | 94 | 95 | 96 | 97 | ``` 98 | --- 99 | ### src/main/java/com/in28minutes/springboot/web/controller/LoginController.java 100 | 101 | ```java 102 | package com.in28minutes.springboot.web.controller; 103 | 104 | import org.springframework.beans.factory.annotation.Autowired; 105 | import org.springframework.stereotype.Controller; 106 | import org.springframework.ui.ModelMap; 107 | import org.springframework.web.bind.annotation.RequestMapping; 108 | import org.springframework.web.bind.annotation.RequestMethod; 109 | import org.springframework.web.bind.annotation.SessionAttributes; 110 | 111 | import com.in28minutes.springboot.web.service.LoginService; 112 | 113 | @Controller 114 | @SessionAttributes("name") 115 | public class LoginController { 116 | 117 | @Autowired 118 | LoginService service; 119 | 120 | @RequestMapping(value = "/", method = RequestMethod.GET) 121 | public String showLoginPage(ModelMap model) { 122 | model.put("name", "in28Minutes"); 123 | return "welcome"; 124 | } 125 | 126 | } 127 | ``` 128 | --- 129 | ### src/main/java/com/in28minutes/springboot/web/controller/TodoController.java 130 | 131 | ```java 132 | package com.in28minutes.springboot.web.controller; 133 | 134 | import java.text.SimpleDateFormat; 135 | import java.util.Date; 136 | 137 | import javax.validation.Valid; 138 | 139 | import org.springframework.beans.factory.annotation.Autowired; 140 | import org.springframework.beans.propertyeditors.CustomDateEditor; 141 | import org.springframework.stereotype.Controller; 142 | import org.springframework.ui.ModelMap; 143 | import org.springframework.validation.BindingResult; 144 | import org.springframework.web.bind.WebDataBinder; 145 | import org.springframework.web.bind.annotation.InitBinder; 146 | import org.springframework.web.bind.annotation.RequestMapping; 147 | import org.springframework.web.bind.annotation.RequestMethod; 148 | import org.springframework.web.bind.annotation.RequestParam; 149 | import org.springframework.web.bind.annotation.SessionAttributes; 150 | 151 | import com.in28minutes.springboot.web.model.Todo; 152 | import com.in28minutes.springboot.web.service.LoginService; 153 | import com.in28minutes.springboot.web.service.TodoService; 154 | 155 | @Controller 156 | @SessionAttributes("name") 157 | public class TodoController { 158 | 159 | @Autowired 160 | TodoService service; 161 | 162 | @InitBinder 163 | public void initBinder(WebDataBinder binder) { 164 | // Date - dd/MM/yyyy 165 | SimpleDateFormat dateFormat = new SimpleDateFormat("dd/MM/yyyy"); 166 | binder.registerCustomEditor(Date.class, new CustomDateEditor( 167 | dateFormat, false)); 168 | } 169 | 170 | @RequestMapping(value = "/list-todos", method = RequestMethod.GET) 171 | public String showTodos(ModelMap model) { 172 | String name = getLoggedInUserName(model); 173 | model.put("todos", service.retrieveTodos(name)); 174 | return "list-todos"; 175 | } 176 | 177 | private String getLoggedInUserName(ModelMap model) { 178 | return (String) model.get("name"); 179 | } 180 | 181 | @RequestMapping(value = "/add-todo", method = RequestMethod.GET) 182 | public String showAddTodoPage(ModelMap model) { 183 | model.addAttribute("todo", new Todo(0, getLoggedInUserName(model), 184 | "Default Desc", new Date(), false)); 185 | return "todo"; 186 | } 187 | 188 | @RequestMapping(value = "/delete-todo", method = RequestMethod.GET) 189 | public String deleteTodo(@RequestParam int id) { 190 | service.deleteTodo(id); 191 | return "redirect:/list-todos"; 192 | } 193 | 194 | @RequestMapping(value = "/update-todo", method = RequestMethod.GET) 195 | public String showUpdateTodoPage(@RequestParam int id, ModelMap model) { 196 | Todo todo = service.retrieveTodo(id); 197 | model.put("todo", todo); 198 | return "todo"; 199 | } 200 | 201 | @RequestMapping(value = "/update-todo", method = RequestMethod.POST) 202 | public String updateTodo(ModelMap model, @Valid Todo todo, 203 | BindingResult result) { 204 | 205 | if (result.hasErrors()) { 206 | return "todo"; 207 | } 208 | 209 | todo.setUser(getLoggedInUserName(model)); 210 | 211 | service.updateTodo(todo); 212 | 213 | return "redirect:/list-todos"; 214 | } 215 | 216 | @RequestMapping(value = "/add-todo", method = RequestMethod.POST) 217 | public String addTodo(ModelMap model, @Valid Todo todo, BindingResult result) { 218 | 219 | if (result.hasErrors()) { 220 | return "todo"; 221 | } 222 | 223 | service.addTodo(getLoggedInUserName(model), todo.getDesc(), todo.getTargetDate(), 224 | false); 225 | return "redirect:/list-todos"; 226 | } 227 | } 228 | ``` 229 | --- 230 | ### src/main/java/com/in28minutes/springboot/web/model/Todo.java 231 | 232 | ```java 233 | package com.in28minutes.springboot.web.model; 234 | 235 | import java.util.Date; 236 | 237 | import javax.validation.constraints.Size; 238 | 239 | public class Todo { 240 | private int id; 241 | private String user; 242 | 243 | @Size(min=10, message="Enter at least 10 Characters...") 244 | private String desc; 245 | 246 | private Date targetDate; 247 | private boolean isDone; 248 | 249 | public Todo() { 250 | super(); 251 | } 252 | 253 | public Todo(int id, String user, String desc, Date targetDate, 254 | boolean isDone) { 255 | super(); 256 | this.id = id; 257 | this.user = user; 258 | this.desc = desc; 259 | this.targetDate = targetDate; 260 | this.isDone = isDone; 261 | } 262 | 263 | public int getId() { 264 | return id; 265 | } 266 | 267 | public void setId(int id) { 268 | this.id = id; 269 | } 270 | 271 | public String getUser() { 272 | return user; 273 | } 274 | 275 | public void setUser(String user) { 276 | this.user = user; 277 | } 278 | 279 | public String getDesc() { 280 | return desc; 281 | } 282 | 283 | public void setDesc(String desc) { 284 | this.desc = desc; 285 | } 286 | 287 | public Date getTargetDate() { 288 | return targetDate; 289 | } 290 | 291 | public void setTargetDate(Date targetDate) { 292 | this.targetDate = targetDate; 293 | } 294 | 295 | public boolean isDone() { 296 | return isDone; 297 | } 298 | 299 | public void setDone(boolean isDone) { 300 | this.isDone = isDone; 301 | } 302 | 303 | @Override 304 | public int hashCode() { 305 | final int prime = 31; 306 | int result = 1; 307 | result = prime * result + id; 308 | return result; 309 | } 310 | 311 | @Override 312 | public boolean equals(Object obj) { 313 | if (this == obj) { 314 | return true; 315 | } 316 | if (obj == null) { 317 | return false; 318 | } 319 | if (getClass() != obj.getClass()) { 320 | return false; 321 | } 322 | Todo other = (Todo) obj; 323 | if (id != other.id) { 324 | return false; 325 | } 326 | return true; 327 | } 328 | 329 | @Override 330 | public String toString() { 331 | return String.format( 332 | "Todo [id=%s, user=%s, desc=%s, targetDate=%s, isDone=%s]", id, 333 | user, desc, targetDate, isDone); 334 | } 335 | 336 | } 337 | ``` 338 | --- 339 | ### src/main/java/com/in28minutes/springboot/web/service/LoginService.java 340 | 341 | ```java 342 | package com.in28minutes.springboot.web.service; 343 | 344 | import org.springframework.stereotype.Component; 345 | import org.springframework.stereotype.Service; 346 | 347 | @Service 348 | public class LoginService { 349 | 350 | public boolean validateUser(String userid, String password) { 351 | // in28minutes, dummy 352 | return userid.equalsIgnoreCase("in28minutes") 353 | && password.equalsIgnoreCase("dummy"); 354 | } 355 | 356 | } 357 | ``` 358 | --- 359 | ### src/main/java/com/in28minutes/springboot/web/service/TodoService.java 360 | 361 | ```java 362 | package com.in28minutes.springboot.web.service; 363 | 364 | import java.util.ArrayList; 365 | import java.util.Date; 366 | import java.util.Iterator; 367 | import java.util.List; 368 | 369 | import org.springframework.stereotype.Service; 370 | 371 | import com.in28minutes.springboot.web.model.Todo; 372 | 373 | @Service 374 | public class TodoService { 375 | private static List todos = new ArrayList(); 376 | private static int todoCount = 3; 377 | 378 | static { 379 | todos.add(new Todo(1, "in28Minutes", "Learn Spring MVC", new Date(), 380 | false)); 381 | todos.add(new Todo(2, "in28Minutes", "Learn Struts", new Date(), false)); 382 | todos.add(new Todo(3, "in28Minutes", "Learn Hibernate", new Date(), 383 | false)); 384 | } 385 | 386 | public List retrieveTodos(String user) { 387 | List filteredTodos = new ArrayList(); 388 | for (Todo todo : todos) { 389 | if (todo.getUser().equalsIgnoreCase(user)) { 390 | filteredTodos.add(todo); 391 | } 392 | } 393 | return filteredTodos; 394 | } 395 | 396 | public Todo retrieveTodo(int id) { 397 | for (Todo todo : todos) { 398 | if (todo.getId()==id) { 399 | return todo; 400 | } 401 | } 402 | return null; 403 | } 404 | 405 | public void updateTodo(Todo todo){ 406 | todos.remove(todo); 407 | todos.add(todo); 408 | } 409 | 410 | public void addTodo(String name, String desc, Date targetDate, 411 | boolean isDone) { 412 | todos.add(new Todo(++todoCount, name, desc, targetDate, isDone)); 413 | } 414 | 415 | public void deleteTodo(int id) { 416 | Iterator iterator = todos.iterator(); 417 | while (iterator.hasNext()) { 418 | Todo todo = iterator.next(); 419 | if (todo.getId() == id) { 420 | iterator.remove(); 421 | } 422 | } 423 | } 424 | } 425 | ``` 426 | --- 427 | ### src/main/java/com/in28minutes/springboot/web/SpringBootFirstWebApplication.java 428 | 429 | ```java 430 | package com.in28minutes.springboot.web; 431 | 432 | import org.springframework.boot.SpringApplication; 433 | import org.springframework.boot.autoconfigure.SpringBootApplication; 434 | import org.springframework.context.annotation.ComponentScan; 435 | 436 | @SpringBootApplication 437 | @ComponentScan("com.in28minutes.springboot.web") 438 | public class SpringBootFirstWebApplication { 439 | 440 | public static void main(String[] args) { 441 | SpringApplication.run(SpringBootFirstWebApplication.class, args); 442 | } 443 | } 444 | ``` 445 | --- 446 | ### src/main/resources/application.properties 447 | 448 | ``` 449 | spring.mvc.view.prefix=/WEB-INF/jsp/ 450 | spring.mvc.view.suffix=.jsp 451 | logging.level.org.springframework.web=INFO 452 | ``` 453 | --- 454 | ### src/main/webapp/WEB-INF/jsp/common/footer.jspf 455 | 456 | ``` 457 | 458 | 459 | 461 | 466 | 467 | 468 | 469 | ``` 470 | --- 471 | ### src/main/webapp/WEB-INF/jsp/common/header.jspf 472 | 473 | ``` 474 | <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%> 475 | <%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt"%> 476 | <%@taglib uri="http://www.springframework.org/tags/form" prefix="form"%> 477 | 478 | 479 | 480 | 481 | First Web Application 482 | 484 | 485 | 486 | 487 | 488 | ``` 489 | --- 490 | ### src/main/webapp/WEB-INF/jsp/common/navigation.jspf 491 | 492 | ``` 493 | 504 | ``` 505 | --- 506 | ### src/main/webapp/WEB-INF/jsp/list-todos.jsp 507 | 508 | ``` 509 | <%@ include file="common/header.jspf" %> 510 | <%@ include file="common/navigation.jspf" %> 511 | 512 |
513 | 514 | 515 | 516 | 517 | 518 | 519 | 520 | 521 | 522 | 523 | 524 | 525 | 526 | 527 | 528 | 529 | 530 | 532 | 534 | 535 | 536 | 537 |
Your todos are
DescriptionTarget DateIs it Done?
${todo.desc}${todo.done}UpdateDelete
538 |
539 | Add a Todo 540 |
541 |
542 | <%@ include file="common/footer.jspf" %> 543 | ``` 544 | --- 545 | ### src/main/webapp/WEB-INF/jsp/todo.jsp 546 | 547 | ``` 548 | <%@ include file="common/header.jspf" %> 549 | <%@ include file="common/navigation.jspf" %> 550 |
551 | 552 | 553 |
554 | Description 555 | 557 | 558 |
559 | 560 |
561 | Target Date 562 | 564 | 565 |
566 | 567 | 568 |
569 |
570 | <%@ include file="common/footer.jspf" %> 571 | ``` 572 | --- 573 | ### src/main/webapp/WEB-INF/jsp/welcome.jsp 574 | 575 | ``` 576 | <%@ include file="common/header.jspf"%> 577 | <%@ include file="common/navigation.jspf"%> 578 |
579 | Welcome ${name}!! Click here to manage your 580 | todo's. 581 |
582 | <%@ include file="common/footer.jspf"%> 583 | ``` 584 | --- 585 | ### src/test/java/com/in28minutes/springboot/web/SpringBootFirstWebApplicationTests.java 586 | 587 | ```java 588 | package com.in28minutes.springboot.web; 589 | 590 | import org.junit.Test; 591 | import org.junit.runner.RunWith; 592 | import org.springframework.boot.test.context.SpringBootTest; 593 | import org.springframework.test.context.junit4.SpringRunner; 594 | 595 | @RunWith(SpringRunner.class) 596 | @SpringBootTest 597 | public class SpringBootFirstWebApplicationTests { 598 | 599 | @Test 600 | public void contextLoads() { 601 | } 602 | 603 | } 604 | ``` 605 | --- 606 | ### todo.txt 607 | 608 | ``` 609 | Implementing Server Side Validation 610 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 611 | Command Bean or Form Backing Bean 612 | 613 | Add Validation 614 | Use Validation on Controller 615 | Display Errors in View 616 | 617 | Command Bean 618 | ~~~~~~~~~~~~ 619 | Controller 620 | View - Spring Form Tags 621 | 622 | 623 | 624 | LoginController -> adds name to model 625 | welcome.jsp -> shows ${name} 626 | 627 | TodoController -> redirects to list-todos.jsp 628 | ${name} is empty 629 | 630 | 631 | Component, Service, Repository, Controller 632 | Autowired 633 | ComponentScan 634 | 635 | Field dummyService in com.in28minutes.springboot.web.controller.LoginController 636 | required a bean of type 'com.in28minutes.dummy.DummyService' 637 | that could not be found. 638 | 639 | Spring Boot Starter Parent 640 | Spring Boot Starter Web 641 | @SpringBootApplication 642 | Auto Configuration 643 | 644 | Dispatcher Servlet 645 | 646 | /login => "login" 647 | 648 | "login" => src/main/webapp/WEB-INF/jsp/login.jsp 649 | 650 | 651 | Search for a view named "login" 652 | 653 | 654 | 655 | /login => LoginController 656 | ``` 657 | --- 658 | -------------------------------------------------------------------------------- /Step22.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/in28minutes/SpringBootWebApplicationStepByStep/7611e5a098e49023ee03ce1446e465a4c567da45/Step22.zip -------------------------------------------------------------------------------- /Step23.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/in28minutes/SpringBootWebApplicationStepByStep/7611e5a098e49023ee03ce1446e465a4c567da45/Step23.zip -------------------------------------------------------------------------------- /Step24.md: -------------------------------------------------------------------------------- 1 | ## What we will do: 2 | - Remove Hardcoding of User Name 3 | - Remove LoginService 4 | - Rename LoginController to WelcomeController 5 | - Add Logout Functionality 6 | 7 | ## Useful Snippets 8 | ``` 9 | private String getLoggedInUserName(ModelMap model) { 10 | Object principal = SecurityContextHolder.getContext() 11 | .getAuthentication().getPrincipal(); 12 | 13 | if (principal instanceof UserDetails) 14 | return ((UserDetails) principal).getUsername(); 15 | 16 | return principal.toString(); 17 | } 18 | 19 | 22 | 23 | @RequestMapping(value = "/logout", method = RequestMethod.GET) 24 | public String logout(HttpServletRequest request, 25 | HttpServletResponse response) { 26 | Authentication auth = SecurityContextHolder.getContext() 27 | .getAuthentication(); 28 | if (auth != null) { 29 | new SecurityContextLogoutHandler().logout(request, response, auth); 30 | } 31 | return "redirect:/"; 32 | } 33 | 34 | ``` 35 | -------------------------------------------------------------------------------- /Step25.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/in28minutes/SpringBootWebApplicationStepByStep/7611e5a098e49023ee03ce1446e465a4c567da45/Step25.zip -------------------------------------------------------------------------------- /Step27.md: -------------------------------------------------------------------------------- 1 | ## What we will do: 2 | - One More Spring Rest Services. 3 | - @PathVariable("id") int id 4 | - http://localhost:8080/rest/todos/1 5 | 6 | ## Useful Snippets 7 | 8 | ``` 9 | @RequestMapping(value = "/rest/todos/{id}", method = RequestMethod.GET) 10 | public Todo retrieveTodo(@PathVariable("id") int id) { 11 | return service.retrieveTodo(id); 12 | } 13 | 14 | ``` 15 | ## Files List 16 | -------------------------------------------------------------------------------- /Step27.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/in28minutes/SpringBootWebApplicationStepByStep/7611e5a098e49023ee03ce1446e465a4c567da45/Step27.zip -------------------------------------------------------------------------------- /StepReference.md: -------------------------------------------------------------------------------- 1 | ## What You Will Learn during this Step: 2 | - First 3 | - Second 4 | - Third 5 | 6 | ## Useful Snippets and References 7 | First Snippet 8 | ``` 9 | ``` 10 | Second Snippet 11 | ``` 12 | ``` 13 | 14 | ## Exercises 15 | 16 | ## Files List 17 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | com.in28minutes.springboot.web 7 | spring-boot-first-web-application-git 8 | 0.0.1-SNAPSHOT 9 | jar 10 | 11 | spring-boot-first-web-application 12 | Demo project for Spring Boot 13 | 14 | 15 | org.springframework.boot 16 | spring-boot-starter-parent 17 | 1.4.3.RELEASE 18 | 19 | 20 | 21 | 22 | UTF-8 23 | UTF-8 24 | 1.8 25 | 26 | 27 | 28 | 29 | org.springframework.boot 30 | spring-boot-starter-web 31 | 32 | 33 | 34 | org.springframework.boot 35 | spring-boot-starter-security 36 | 37 | 38 | 39 | javax.servlet 40 | jstl 41 | 42 | 43 | 44 | org.webjars 45 | bootstrap 46 | 3.3.6 47 | 48 | 49 | 50 | org.webjars 51 | bootstrap-datepicker 52 | 1.0.1 53 | 54 | 55 | 56 | org.webjars 57 | jquery 58 | 1.9.1 59 | 60 | 61 | 62 | org.apache.tomcat.embed 63 | tomcat-embed-jasper 64 | provided 65 | 66 | 67 | 68 | org.springframework.boot 69 | spring-boot-devtools 70 | runtime 71 | 72 | 73 | 74 | org.springframework.boot 75 | spring-boot-starter-test 76 | test 77 | 78 | 79 | 80 | 81 | 82 | 83 | org.springframework.boot 84 | spring-boot-maven-plugin 85 | 86 | 87 | 88 | 89 | 90 | -------------------------------------------------------------------------------- /src/main/java/com/in28minutes/springboot/web/SpringBootFirstWebApplication.java: -------------------------------------------------------------------------------- 1 | package com.in28minutes.springboot.web; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | import org.springframework.context.annotation.ComponentScan; 6 | 7 | @SpringBootApplication 8 | @ComponentScan("com.in28minutes.springboot.web") 9 | public class SpringBootFirstWebApplication { 10 | 11 | public static void main(String[] args) { 12 | SpringApplication.run(SpringBootFirstWebApplication.class, args); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/com/in28minutes/springboot/web/controller/ErrorController.java: -------------------------------------------------------------------------------- 1 | package com.in28minutes.springboot.web.controller; 2 | 3 | import javax.servlet.http.HttpServletRequest; 4 | import javax.servlet.http.HttpServletResponse; 5 | 6 | import org.springframework.stereotype.Controller; 7 | import org.springframework.web.bind.annotation.ExceptionHandler; 8 | import org.springframework.web.servlet.ModelAndView; 9 | 10 | @Controller("error") 11 | public class ErrorController { 12 | 13 | @ExceptionHandler(Exception.class) 14 | public ModelAndView handleException 15 | (HttpServletRequest request, Exception ex){ 16 | ModelAndView mv = new ModelAndView(); 17 | 18 | mv.addObject("exception", ex.getLocalizedMessage()); 19 | mv.addObject("url", request.getRequestURL()); 20 | 21 | mv.setViewName("error"); 22 | return mv; 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/com/in28minutes/springboot/web/controller/LogoutController.java: -------------------------------------------------------------------------------- 1 | package com.in28minutes.springboot.web.controller; 2 | 3 | import javax.servlet.http.HttpServletRequest; 4 | import javax.servlet.http.HttpServletResponse; 5 | 6 | import org.springframework.security.core.Authentication; 7 | import org.springframework.security.core.context.SecurityContextHolder; 8 | import org.springframework.security.core.userdetails.UserDetails; 9 | import org.springframework.security.web.authentication.logout.LogoutHandler; 10 | import org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler; 11 | import org.springframework.stereotype.Controller; 12 | import org.springframework.ui.ModelMap; 13 | import org.springframework.web.bind.annotation.RequestMapping; 14 | import org.springframework.web.bind.annotation.RequestMethod; 15 | 16 | @Controller 17 | public class LogoutController { 18 | 19 | @RequestMapping(value = "/logout", method = RequestMethod.GET) 20 | public String logout(HttpServletRequest request, 21 | HttpServletResponse response) { 22 | 23 | Authentication authentication = SecurityContextHolder.getContext() 24 | .getAuthentication(); 25 | 26 | if (authentication != null) { 27 | new SecurityContextLogoutHandler().logout(request, response, 28 | authentication); 29 | } 30 | 31 | return "redirect:/"; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/com/in28minutes/springboot/web/controller/TodoController.java: -------------------------------------------------------------------------------- 1 | package com.in28minutes.springboot.web.controller; 2 | 3 | import java.text.SimpleDateFormat; 4 | import java.util.Date; 5 | 6 | import javax.validation.Valid; 7 | 8 | import org.springframework.beans.factory.annotation.Autowired; 9 | import org.springframework.beans.propertyeditors.CustomDateEditor; 10 | import org.springframework.security.core.context.SecurityContextHolder; 11 | import org.springframework.security.core.userdetails.UserDetails; 12 | import org.springframework.stereotype.Controller; 13 | import org.springframework.ui.ModelMap; 14 | import org.springframework.validation.BindingResult; 15 | import org.springframework.web.bind.WebDataBinder; 16 | import org.springframework.web.bind.annotation.InitBinder; 17 | import org.springframework.web.bind.annotation.RequestMapping; 18 | import org.springframework.web.bind.annotation.RequestMethod; 19 | import org.springframework.web.bind.annotation.RequestParam; 20 | 21 | import com.in28minutes.springboot.web.model.Todo; 22 | import com.in28minutes.springboot.web.service.TodoService; 23 | 24 | @Controller 25 | public class TodoController { 26 | 27 | @Autowired 28 | TodoService service; 29 | 30 | @InitBinder 31 | public void initBinder(WebDataBinder binder) { 32 | // Date - dd/MM/yyyy 33 | SimpleDateFormat dateFormat = new SimpleDateFormat("dd/MM/yyyy"); 34 | binder.registerCustomEditor(Date.class, new CustomDateEditor( 35 | dateFormat, false)); 36 | } 37 | 38 | @RequestMapping(value = "/list-todos", method = RequestMethod.GET) 39 | public String showTodos(ModelMap model) { 40 | String name = getLoggedInUserName(model); 41 | model.put("todos", service.retrieveTodos(name)); 42 | return "list-todos"; 43 | } 44 | 45 | private String getLoggedInUserName(ModelMap model) { 46 | Object principal = SecurityContextHolder.getContext() 47 | .getAuthentication().getPrincipal(); 48 | 49 | if (principal instanceof UserDetails) { 50 | return ((UserDetails) principal).getUsername(); 51 | } 52 | 53 | return principal.toString(); 54 | } 55 | 56 | @RequestMapping(value = "/add-todo", method = RequestMethod.GET) 57 | public String showAddTodoPage(ModelMap model) { 58 | model.addAttribute("todo", new Todo(0, getLoggedInUserName(model), 59 | "Default Desc", new Date(), false)); 60 | return "todo"; 61 | } 62 | 63 | @RequestMapping(value = "/delete-todo", method = RequestMethod.GET) 64 | public String deleteTodo(@RequestParam int id) { 65 | 66 | if(id==1) 67 | throw new RuntimeException("Something went wrong"); 68 | 69 | service.deleteTodo(id); 70 | return "redirect:/list-todos"; 71 | } 72 | 73 | @RequestMapping(value = "/update-todo", method = RequestMethod.GET) 74 | public String showUpdateTodoPage(@RequestParam int id, ModelMap model) { 75 | Todo todo = service.retrieveTodo(id); 76 | model.put("todo", todo); 77 | return "todo"; 78 | } 79 | 80 | @RequestMapping(value = "/update-todo", method = RequestMethod.POST) 81 | public String updateTodo(ModelMap model, @Valid Todo todo, 82 | BindingResult result) { 83 | 84 | if (result.hasErrors()) { 85 | return "todo"; 86 | } 87 | 88 | todo.setUser(getLoggedInUserName(model)); 89 | 90 | service.updateTodo(todo); 91 | 92 | return "redirect:/list-todos"; 93 | } 94 | 95 | @RequestMapping(value = "/add-todo", method = RequestMethod.POST) 96 | public String addTodo(ModelMap model, @Valid Todo todo, BindingResult result) { 97 | 98 | if (result.hasErrors()) { 99 | return "todo"; 100 | } 101 | 102 | service.addTodo(getLoggedInUserName(model), todo.getDesc(), todo.getTargetDate(), 103 | false); 104 | return "redirect:/list-todos"; 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /src/main/java/com/in28minutes/springboot/web/controller/WelcomeController.java: -------------------------------------------------------------------------------- 1 | package com.in28minutes.springboot.web.controller; 2 | 3 | import org.springframework.security.core.context.SecurityContextHolder; 4 | import org.springframework.security.core.userdetails.UserDetails; 5 | import org.springframework.stereotype.Controller; 6 | import org.springframework.ui.ModelMap; 7 | import org.springframework.web.bind.annotation.RequestMapping; 8 | import org.springframework.web.bind.annotation.RequestMethod; 9 | 10 | @Controller 11 | public class WelcomeController { 12 | 13 | @RequestMapping(value = "/", method = RequestMethod.GET) 14 | public String showWelcomePage(ModelMap model) { 15 | model.put("name", getLoggedinUserName()); 16 | return "welcome"; 17 | } 18 | 19 | private String getLoggedinUserName() { 20 | Object principal = SecurityContextHolder.getContext() 21 | .getAuthentication().getPrincipal(); 22 | 23 | if (principal instanceof UserDetails) { 24 | return ((UserDetails) principal).getUsername(); 25 | } 26 | 27 | return principal.toString(); 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/com/in28minutes/springboot/web/model/Todo.java: -------------------------------------------------------------------------------- 1 | package com.in28minutes.springboot.web.model; 2 | 3 | import java.util.Date; 4 | 5 | import javax.validation.constraints.Size; 6 | 7 | public class Todo { 8 | private int id; 9 | private String user; 10 | 11 | @Size(min=10, message="Enter at least 10 Characters...") 12 | private String desc; 13 | 14 | private Date targetDate; 15 | private boolean isDone; 16 | 17 | public Todo() { 18 | super(); 19 | } 20 | 21 | public Todo(int id, String user, String desc, Date targetDate, 22 | boolean isDone) { 23 | super(); 24 | this.id = id; 25 | this.user = user; 26 | this.desc = desc; 27 | this.targetDate = targetDate; 28 | this.isDone = isDone; 29 | } 30 | 31 | public int getId() { 32 | return id; 33 | } 34 | 35 | public void setId(int id) { 36 | this.id = id; 37 | } 38 | 39 | public String getUser() { 40 | return user; 41 | } 42 | 43 | public void setUser(String user) { 44 | this.user = user; 45 | } 46 | 47 | public String getDesc() { 48 | return desc; 49 | } 50 | 51 | public void setDesc(String desc) { 52 | this.desc = desc; 53 | } 54 | 55 | public Date getTargetDate() { 56 | return targetDate; 57 | } 58 | 59 | public void setTargetDate(Date targetDate) { 60 | this.targetDate = targetDate; 61 | } 62 | 63 | public boolean isDone() { 64 | return isDone; 65 | } 66 | 67 | public void setDone(boolean isDone) { 68 | this.isDone = isDone; 69 | } 70 | 71 | @Override 72 | public int hashCode() { 73 | final int prime = 31; 74 | int result = 1; 75 | result = prime * result + id; 76 | return result; 77 | } 78 | 79 | @Override 80 | public boolean equals(Object obj) { 81 | if (this == obj) { 82 | return true; 83 | } 84 | if (obj == null) { 85 | return false; 86 | } 87 | if (getClass() != obj.getClass()) { 88 | return false; 89 | } 90 | Todo other = (Todo) obj; 91 | if (id != other.id) { 92 | return false; 93 | } 94 | return true; 95 | } 96 | 97 | @Override 98 | public String toString() { 99 | return String.format( 100 | "Todo [id=%s, user=%s, desc=%s, targetDate=%s, isDone=%s]", id, 101 | user, desc, targetDate, isDone); 102 | } 103 | 104 | } -------------------------------------------------------------------------------- /src/main/java/com/in28minutes/springboot/web/security/SecurityConfiguration.java: -------------------------------------------------------------------------------- 1 | package com.in28minutes.springboot.web.security; 2 | 3 | import org.springframework.beans.factory.annotation.Autowired; 4 | import org.springframework.context.annotation.Configuration; 5 | import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; 6 | import org.springframework.security.config.annotation.web.builders.HttpSecurity; 7 | import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; 8 | 9 | @Configuration 10 | public class SecurityConfiguration extends WebSecurityConfigurerAdapter{ 11 | //Create User - in28Minutes/dummy 12 | @Autowired 13 | public void configureGlobalSecurity(AuthenticationManagerBuilder auth) 14 | throws Exception { 15 | auth.inMemoryAuthentication().withUser("in28Minutes").password("dummy") 16 | .roles("USER", "ADMIN"); 17 | } 18 | 19 | @Override 20 | protected void configure(HttpSecurity http) throws Exception { 21 | http.authorizeRequests().antMatchers("/login").permitAll() 22 | .antMatchers("/", "/*todo*/**").access("hasRole('USER')").and() 23 | .formLogin(); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/com/in28minutes/springboot/web/service/TodoService.java: -------------------------------------------------------------------------------- 1 | package com.in28minutes.springboot.web.service; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Date; 5 | import java.util.Iterator; 6 | import java.util.List; 7 | 8 | import org.springframework.stereotype.Service; 9 | 10 | import com.in28minutes.springboot.web.model.Todo; 11 | 12 | @Service 13 | public class TodoService { 14 | private static List todos = new ArrayList(); 15 | private static int todoCount = 3; 16 | 17 | static { 18 | todos.add(new Todo(1, "in28Minutes", "Learn Spring MVC", new Date(), 19 | false)); 20 | todos.add(new Todo(2, "in28Minutes", "Learn Struts", new Date(), false)); 21 | todos.add(new Todo(3, "in28Minutes", "Learn Hibernate", new Date(), 22 | false)); 23 | } 24 | 25 | public List retrieveTodos(String user) { 26 | List filteredTodos = new ArrayList(); 27 | for (Todo todo : todos) { 28 | if (todo.getUser().equalsIgnoreCase(user)) { 29 | filteredTodos.add(todo); 30 | } 31 | } 32 | return filteredTodos; 33 | } 34 | 35 | public Todo retrieveTodo(int id) { 36 | for (Todo todo : todos) { 37 | if (todo.getId()==id) { 38 | return todo; 39 | } 40 | } 41 | return null; 42 | } 43 | 44 | public void updateTodo(Todo todo){ 45 | todos.remove(todo); 46 | todos.add(todo); 47 | } 48 | 49 | public void addTodo(String name, String desc, Date targetDate, 50 | boolean isDone) { 51 | todos.add(new Todo(++todoCount, name, desc, targetDate, isDone)); 52 | } 53 | 54 | public void deleteTodo(int id) { 55 | Iterator iterator = todos.iterator(); 56 | while (iterator.hasNext()) { 57 | Todo todo = iterator.next(); 58 | if (todo.getId() == id) { 59 | iterator.remove(); 60 | } 61 | } 62 | } 63 | } -------------------------------------------------------------------------------- /src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | spring.mvc.view.prefix=/WEB-INF/jsp/ 2 | spring.mvc.view.suffix=.jsp 3 | logging.level.org.springframework.web=INFO -------------------------------------------------------------------------------- /src/main/webapp/WEB-INF/jsp/common/footer.jspf: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/main/webapp/WEB-INF/jsp/common/header.jspf: -------------------------------------------------------------------------------- 1 | <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%> 2 | <%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt"%> 3 | <%@taglib uri="http://www.springframework.org/tags/form" prefix="form"%> 4 | 5 | 6 | 7 | 8 | First Web Application 9 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /src/main/webapp/WEB-INF/jsp/common/navigation.jspf: -------------------------------------------------------------------------------- 1 | 2 | 16 | -------------------------------------------------------------------------------- /src/main/webapp/WEB-INF/jsp/error.jsp: -------------------------------------------------------------------------------- 1 | <%@ include file="common/header.jspf"%> 2 | <%@ include file="common/navigation.jspf"%> 3 |
4 | An exception occurred! Please contact Support! 5 |
6 | <%@ include file="common/footer.jspf"%> -------------------------------------------------------------------------------- /src/main/webapp/WEB-INF/jsp/list-todos.jsp: -------------------------------------------------------------------------------- 1 | <%@ include file="common/header.jspf" %> 2 | <%@ include file="common/navigation.jspf" %> 3 | 4 |
5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 24 | 26 | 27 | 28 | 29 |
Your todos are
DescriptionTarget DateIs it Done?
${todo.desc}${todo.done}UpdateDelete
30 |
31 | Add a Todo 32 |
33 |
34 | <%@ include file="common/footer.jspf" %> -------------------------------------------------------------------------------- /src/main/webapp/WEB-INF/jsp/todo.jsp: -------------------------------------------------------------------------------- 1 | <%@ include file="common/header.jspf" %> 2 | <%@ include file="common/navigation.jspf" %> 3 |
4 | 5 | 6 |
7 | Description 8 | 10 | 11 |
12 | 13 |
14 | Target Date 15 | 17 | 18 |
19 | 20 | 21 |
22 |
23 | <%@ include file="common/footer.jspf" %> -------------------------------------------------------------------------------- /src/main/webapp/WEB-INF/jsp/welcome.jsp: -------------------------------------------------------------------------------- 1 | <%@ include file="common/header.jspf"%> 2 | <%@ include file="common/navigation.jspf"%> 3 |
4 | Welcome ${name}!! Click here to manage your 5 | todo's. 6 |
7 | <%@ include file="common/footer.jspf"%> -------------------------------------------------------------------------------- /src/test/java/com/in28minutes/springboot/web/SpringBootFirstWebApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.in28minutes.springboot.web; 2 | 3 | import org.junit.Test; 4 | import org.junit.runner.RunWith; 5 | import org.springframework.boot.test.context.SpringBootTest; 6 | import org.springframework.test.context.junit4.SpringRunner; 7 | 8 | @RunWith(SpringRunner.class) 9 | @SpringBootTest 10 | public class SpringBootFirstWebApplicationTests { 11 | 12 | @Test 13 | public void contextLoads() { 14 | } 15 | 16 | } 17 | --------------------------------------------------------------------------------