diff --git a/02.Spring-Boot-Web-Application/2-3-1-upgrade.md b/02.Spring-Boot-Web-Application/2-3-1-upgrade.md
new file mode 100644
index 0000000..fe94494
--- /dev/null
+++ b/02.Spring-Boot-Web-Application/2-3-1-upgrade.md
@@ -0,0 +1,826 @@
+
+
+## Complete Code Example
+
+
+### /pom.xml
+
+```xml
+
+
+ 4.0.0
+
+ com.in28minutes.springboot.web
+ spring-boot-first-web-application-git
+ 0.0.1-SNAPSHOT
+ jar
+
+ spring-boot-first-web-application
+ Demo project for Spring Boot
+
+
+ org.springframework.boot
+ spring-boot-starter-parent
+ 2.3.1.RELEASE
+
+
+
+
+ UTF-8
+ UTF-8
+ 1.8
+ 3.1.1
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-validation
+
+
+
+ org.springframework.boot
+ spring-boot-starter-data-jpa
+
+
+
+ com.h2database
+ h2
+ runtime
+
+
+
+ org.springframework.boot
+ spring-boot-starter-security
+
+
+
+ javax.servlet
+ jstl
+
+
+
+ org.webjars
+ bootstrap
+ 3.3.6
+
+
+
+ org.webjars
+ bootstrap-datepicker
+ 1.0.1
+
+
+
+ org.webjars
+ jquery
+ 1.9.1
+
+
+
+ org.apache.tomcat.embed
+ tomcat-embed-jasper
+ provided
+
+
+
+ org.springframework.boot
+ spring-boot-devtools
+ runtime
+
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+
+
+
+
+ spring-milestones
+ Spring Milestones
+ https://repo.spring.io/milestones
+
+
+
+
+
+ spring-milestones
+ Spring Milestones
+ https://repo.spring.io/milestones
+
+
+
+
+```
+---
+
+### /src/main/java/com/in28minutes/springboot/web/SpringBootFirstWebApplication.java
+
+```java
+package com.in28minutes.springboot.web;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.context.annotation.ComponentScan;
+
+@SpringBootApplication
+@ComponentScan("com.in28minutes.springboot.web")
+public class SpringBootFirstWebApplication {
+
+ public static void main(String[] args) {
+ SpringApplication.run(SpringBootFirstWebApplication.class, args);
+ }
+}
+```
+---
+
+### /src/main/java/com/in28minutes/springboot/web/controller/ErrorController.java
+
+```java
+package com.in28minutes.springboot.web.controller;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.ExceptionHandler;
+import org.springframework.web.servlet.ModelAndView;
+
+@Controller("error")
+public class ErrorController {
+
+ @ExceptionHandler(Exception.class)
+ public ModelAndView handleException
+ (HttpServletRequest request, Exception ex){
+ ModelAndView mv = new ModelAndView();
+
+ mv.addObject("exception", ex.getLocalizedMessage());
+ mv.addObject("url", request.getRequestURL());
+
+ mv.setViewName("error");
+ return mv;
+ }
+
+}
+```
+---
+
+### /src/main/java/com/in28minutes/springboot/web/controller/LogoutController.java
+
+```java
+package com.in28minutes.springboot.web.controller;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.security.core.userdetails.UserDetails;
+import org.springframework.security.web.authentication.logout.LogoutHandler;
+import org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler;
+import org.springframework.stereotype.Controller;
+import org.springframework.ui.ModelMap;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+
+@Controller
+public class LogoutController {
+
+ @RequestMapping(value = "/logout", method = RequestMethod.GET)
+ public String logout(HttpServletRequest request,
+ HttpServletResponse response) {
+
+ Authentication authentication = SecurityContextHolder.getContext()
+ .getAuthentication();
+
+ if (authentication != null) {
+ new SecurityContextLogoutHandler().logout(request, response,
+ authentication);
+ }
+
+ return "redirect:/";
+ }
+}
+```
+---
+
+### /src/main/java/com/in28minutes/springboot/web/controller/TodoController.java
+
+```java
+package com.in28minutes.springboot.web.controller;
+
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+import javax.validation.Valid;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.propertyeditors.CustomDateEditor;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.security.core.userdetails.UserDetails;
+import org.springframework.stereotype.Controller;
+import org.springframework.ui.ModelMap;
+import org.springframework.validation.BindingResult;
+import org.springframework.web.bind.WebDataBinder;
+import org.springframework.web.bind.annotation.InitBinder;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.RequestParam;
+
+import com.in28minutes.springboot.web.model.Todo;
+import com.in28minutes.springboot.web.service.TodoRepository;
+
+@Controller
+public class TodoController {
+
+ @Autowired
+ TodoRepository repository;
+
+ @InitBinder
+ public void initBinder(WebDataBinder binder) {
+ // Date - dd/MM/yyyy
+ SimpleDateFormat dateFormat = new SimpleDateFormat("dd/MM/yyyy");
+ binder.registerCustomEditor(Date.class, new CustomDateEditor(
+ dateFormat, false));
+ }
+
+ @RequestMapping(value = "/list-todos", method = RequestMethod.GET)
+ public String showTodos(ModelMap model) {
+ String name = getLoggedInUserName(model);
+ model.put("todos", repository.findByUser(name));
+ //model.put("todos", service.retrieveTodos(name));
+ return "list-todos";
+ }
+
+ private String getLoggedInUserName(ModelMap model) {
+ Object principal = SecurityContextHolder.getContext()
+ .getAuthentication().getPrincipal();
+
+ if (principal instanceof UserDetails) {
+ return ((UserDetails) principal).getUsername();
+ }
+
+ return principal.toString();
+ }
+
+ @RequestMapping(value = "/add-todo", method = RequestMethod.GET)
+ public String showAddTodoPage(ModelMap model) {
+ model.addAttribute("todo", new Todo(0, getLoggedInUserName(model),
+ "Default Desc", new Date(), false));
+ return "todo";
+ }
+
+ @RequestMapping(value = "/delete-todo", method = RequestMethod.GET)
+ public String deleteTodo(@RequestParam int id) {
+
+ //if(id==1)
+ //throw new RuntimeException("Something went wrong");
+ repository.deleteById(id);
+ //service.deleteTodo(id);
+ return "redirect:/list-todos";
+ }
+
+ @RequestMapping(value = "/update-todo", method = RequestMethod.GET)
+ public String showUpdateTodoPage(@RequestParam int id, ModelMap model) {
+ Todo todo = repository.findById(id).get();
+ //Todo todo = service.retrieveTodo(id);
+ model.put("todo", todo);
+ return "todo";
+ }
+
+ @RequestMapping(value = "/update-todo", method = RequestMethod.POST)
+ public String updateTodo(ModelMap model, @Valid Todo todo,
+ BindingResult result) {
+
+ if (result.hasErrors()) {
+ return "todo";
+ }
+
+ todo.setUser(getLoggedInUserName(model));
+
+ repository.save(todo);
+ //service.updateTodo(todo);
+
+ return "redirect:/list-todos";
+ }
+
+ @RequestMapping(value = "/add-todo", method = RequestMethod.POST)
+ public String addTodo(ModelMap model, @Valid Todo todo, BindingResult result) {
+
+ if (result.hasErrors()) {
+ return "todo";
+ }
+
+ todo.setUser(getLoggedInUserName(model));
+ repository.save(todo);
+ /*service.addTodo(getLoggedInUserName(model), todo.getDesc(), todo.getTargetDate(),
+ false);*/
+ return "redirect:/list-todos";
+ }
+}
+```
+---
+
+### /src/main/java/com/in28minutes/springboot/web/controller/WelcomeController.java
+
+```java
+package com.in28minutes.springboot.web.controller;
+
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.security.core.userdetails.UserDetails;
+import org.springframework.stereotype.Controller;
+import org.springframework.ui.ModelMap;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+
+@Controller
+public class WelcomeController {
+
+ @RequestMapping(value = "/", method = RequestMethod.GET)
+ public String showWelcomePage(ModelMap model) {
+ model.put("name", getLoggedinUserName());
+ return "welcome";
+ }
+
+ private String getLoggedinUserName() {
+ Object principal = SecurityContextHolder.getContext()
+ .getAuthentication().getPrincipal();
+
+ if (principal instanceof UserDetails) {
+ return ((UserDetails) principal).getUsername();
+ }
+
+ return principal.toString();
+ }
+
+}
+```
+---
+
+### /src/main/java/com/in28minutes/springboot/web/model/Todo.java
+
+```java
+package com.in28minutes.springboot.web.model;
+
+import java.util.Date;
+
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.Id;
+import javax.validation.constraints.Size;
+
+@Entity
+public class Todo {
+
+ @Id
+ @GeneratedValue
+ private int id;
+
+ private String user;
+
+ @Size(min=10, message="Enter at least 10 Characters...")
+ private String desc;
+
+ private Date targetDate;
+ private boolean isDone;
+
+ public Todo() {
+ super();
+ }
+
+ public Todo(int id, String user, String desc, Date targetDate,
+ boolean isDone) {
+ super();
+ this.id = id;
+ this.user = user;
+ this.desc = desc;
+ this.targetDate = targetDate;
+ this.isDone = isDone;
+ }
+
+ public int getId() {
+ return id;
+ }
+
+ public void setId(int id) {
+ this.id = id;
+ }
+
+ public String getUser() {
+ return user;
+ }
+
+ public void setUser(String user) {
+ this.user = user;
+ }
+
+ public String getDesc() {
+ return desc;
+ }
+
+ public void setDesc(String desc) {
+ this.desc = desc;
+ }
+
+ public Date getTargetDate() {
+ return targetDate;
+ }
+
+ public void setTargetDate(Date targetDate) {
+ this.targetDate = targetDate;
+ }
+
+ public boolean isDone() {
+ return isDone;
+ }
+
+ public void setDone(boolean isDone) {
+ this.isDone = isDone;
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + id;
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+ Todo other = (Todo) obj;
+ if (id != other.id) {
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ public String toString() {
+ return String.format(
+ "Todo [id=%s, user=%s, desc=%s, targetDate=%s, isDone=%s]", id,
+ user, desc, targetDate, isDone);
+ }
+
+}
+```
+---
+
+### /src/main/java/com/in28minutes/springboot/web/security/SecurityConfiguration.java
+
+```java
+package com.in28minutes.springboot.web.security;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
+import org.springframework.security.config.annotation.web.builders.HttpSecurity;
+import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
+import org.springframework.security.crypto.password.NoOpPasswordEncoder;
+
+@Configuration
+public class SecurityConfiguration extends WebSecurityConfigurerAdapter{
+ //Create User - in28Minutes/dummy
+ @Autowired
+ public void configureGlobalSecurity(AuthenticationManagerBuilder auth)
+ throws Exception {
+ auth.inMemoryAuthentication()
+ .passwordEncoder(NoOpPasswordEncoder.getInstance())
+ .withUser("in28Minutes").password("dummy")
+ .roles("USER", "ADMIN");
+ }
+
+ @Override
+ protected void configure(HttpSecurity http) throws Exception {
+ http.authorizeRequests().antMatchers("/login", "/h2-console/**").permitAll()
+ .antMatchers("/", "/*todo*/**").access("hasRole('USER')").and()
+ .formLogin();
+
+ http.csrf().disable();
+ http.headers().frameOptions().disable();
+ }
+}
+```
+---
+
+### /src/main/java/com/in28minutes/springboot/web/service/TodoRepository.java
+
+```java
+package com.in28minutes.springboot.web.service;
+
+import java.util.List;
+
+import org.springframework.data.jpa.repository.JpaRepository;
+
+import com.in28minutes.springboot.web.model.Todo;
+
+public interface TodoRepository extends JpaRepository{
+ List findByUser(String user);
+
+ //service.retrieveTodos(name)
+
+ //service.deleteTodo(id);
+ //service.retrieveTodo(id)
+ //service.updateTodo(todo)
+ //service.addTodo(getLoggedInUserName(model), todo.getDesc(), todo.getTargetDate(),false);
+}
+```
+---
+
+### /src/main/java/com/in28minutes/springboot/web/service/TodoService.java
+
+```java
+package com.in28minutes.springboot.web.service;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.Iterator;
+import java.util.List;
+
+import org.springframework.stereotype.Service;
+
+import com.in28minutes.springboot.web.model.Todo;
+
+@Service
+public class TodoService {
+ private static List todos = new ArrayList();
+ private static int todoCount = 3;
+
+ static {
+ todos.add(new Todo(1, "in28Minutes", "Learn Spring MVC", new Date(),
+ false));
+ todos.add(new Todo(2, "in28Minutes", "Learn Struts", new Date(), false));
+ todos.add(new Todo(3, "in28Minutes", "Learn Hibernate", new Date(),
+ false));
+ }
+
+ public List retrieveTodos(String user) {
+ List filteredTodos = new ArrayList();
+ for (Todo todo : todos) {
+ if (todo.getUser().equalsIgnoreCase(user)) {
+ filteredTodos.add(todo);
+ }
+ }
+ return filteredTodos;
+ }
+
+ public Todo retrieveTodo(int id) {
+ for (Todo todo : todos) {
+ if (todo.getId()==id) {
+ return todo;
+ }
+ }
+ return null;
+ }
+
+ public void updateTodo(Todo todo){
+ todos.remove(todo);
+ todos.add(todo);
+ }
+
+ public void addTodo(String name, String desc, Date targetDate,
+ boolean isDone) {
+ todos.add(new Todo(++todoCount, name, desc, targetDate, isDone));
+ }
+
+ public void deleteTodo(int id) {
+ Iterator iterator = todos.iterator();
+ while (iterator.hasNext()) {
+ Todo todo = iterator.next();
+ if (todo.getId() == id) {
+ iterator.remove();
+ }
+ }
+ }
+}
+```
+---
+
+### /src/main/resources/application.properties
+
+```properties
+spring.mvc.view.prefix=/WEB-INF/jsp/
+spring.mvc.view.suffix=.jsp
+logging.level.org.springframework.web=INFO
+
+spring.jpa.show-sql=true
+spring.h2.console.enabled=true
+```
+---
+
+### /src/main/resources/data.sql
+
+```
+insert into TODO
+values(10001, 'Learn Spring Boot', false, sysdate(), 'in28Minutes');
+insert into TODO
+values(10002, 'Learn Angular JS', false, sysdate(), 'in28Minutes');
+insert into TODO
+values(10003, 'Learn to Dance', false, sysdate(), 'in28Minutes');
+```
+---
+
+### /src/main/webapp/WEB-INF/jsp/common/footer.jspf
+
+```
+
+
+
+
+
+
+