diff --git a/enterprise-patterns/pom.xml b/enterprise-patterns/pom.xml
new file mode 100644
index 0000000000..036a61c44a
--- /dev/null
+++ b/enterprise-patterns/pom.xml
@@ -0,0 +1,35 @@
+
+
+ 4.0.0
+
+ com.baeldung.enterprise.patterns
+ enterprise-patterns-parent
+ pom
+
+ spring-dispatcher-servlet
+
+
+
+ com.baeldung
+ parent-modules
+ 1.0.0-SNAPSHOT
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ 3.5.1
+
+ 1.8
+ 1.8
+
+
+
+
+
+
diff --git a/enterprise-patterns/spring-dispatcher-servlet/pom.xml b/enterprise-patterns/spring-dispatcher-servlet/pom.xml
new file mode 100644
index 0000000000..e7220eec59
--- /dev/null
+++ b/enterprise-patterns/spring-dispatcher-servlet/pom.xml
@@ -0,0 +1,73 @@
+
+
+ 4.0.0
+
+ spring-dispatcher-servlet
+ 1.0.0-SNAPSHOT
+ war
+
+
+ com.baeldung.enterprise.patterns
+ enterprise-patterns-parent
+ 1.0.0-SNAPSHOT
+ ..
+
+
+
+
+ javax.servlet
+ javax.servlet-api
+ 3.1.0
+ provided
+
+
+ org.springframework
+ spring-webmvc
+ 4.3.2.RELEASE
+
+
+ org.thymeleaf
+ thymeleaf-spring4
+ 3.0.1.RELEASE
+
+
+ org.apache.logging.log4j
+ log4j-slf4j-impl
+ 2.6.2
+
+
+ org.apache.logging.log4j
+ log4j-core
+ 2.6.2
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+
+
+ org.apache.maven.plugins
+ maven-war-plugin
+ 3.0.0
+
+ false
+
+
+
+ org.eclipse.jetty
+ jetty-maven-plugin
+ 9.4.0.M1
+
+
+ /
+
+
+
+
+
+
diff --git a/enterprise-patterns/spring-dispatcher-servlet/src/main/java/com/baeldung/enterprise/patterns/dispatcher/servlet/RootConfiguration.java b/enterprise-patterns/spring-dispatcher-servlet/src/main/java/com/baeldung/enterprise/patterns/dispatcher/servlet/RootConfiguration.java
new file mode 100644
index 0000000000..9b22e411c5
--- /dev/null
+++ b/enterprise-patterns/spring-dispatcher-servlet/src/main/java/com/baeldung/enterprise/patterns/dispatcher/servlet/RootConfiguration.java
@@ -0,0 +1,19 @@
+package com.baeldung.enterprise.patterns.dispatcher.servlet;
+
+import com.baeldung.enterprise.patterns.dispatcher.servlet.models.Task;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+import java.util.*;
+
+@Configuration
+public class RootConfiguration {
+ @Bean
+ public Map> taskList() {
+ Map> taskMap = new HashMap<>();
+ List taskList = new ArrayList<>();
+ taskList.add(new Task("Clean the dishes!", new Date()));
+ taskMap.put("Cid", taskList);
+ return taskMap;
+ }
+}
diff --git a/enterprise-patterns/spring-dispatcher-servlet/src/main/java/com/baeldung/enterprise/patterns/dispatcher/servlet/WebConfiguration.java b/enterprise-patterns/spring-dispatcher-servlet/src/main/java/com/baeldung/enterprise/patterns/dispatcher/servlet/WebConfiguration.java
new file mode 100644
index 0000000000..90f4f48f50
--- /dev/null
+++ b/enterprise-patterns/spring-dispatcher-servlet/src/main/java/com/baeldung/enterprise/patterns/dispatcher/servlet/WebConfiguration.java
@@ -0,0 +1,41 @@
+package com.baeldung.enterprise.patterns.dispatcher.servlet;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.ComponentScan;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.servlet.config.annotation.EnableWebMvc;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
+import org.thymeleaf.spring4.SpringTemplateEngine;
+import org.thymeleaf.spring4.view.ThymeleafViewResolver;
+import org.thymeleaf.templatemode.TemplateMode;
+import org.thymeleaf.templateresolver.ServletContextTemplateResolver;
+
+import javax.servlet.ServletContext;
+
+@Configuration
+@ComponentScan("com.baeldung.enterprise.patterns.dispatcher.servlet.web")
+@EnableWebMvc
+public class WebConfiguration extends WebMvcConfigurerAdapter {
+ @Bean
+ public ServletContextTemplateResolver templateResolver(ServletContext servletContext) {
+ ServletContextTemplateResolver templateResolver = new ServletContextTemplateResolver(servletContext);
+ templateResolver.setPrefix("/WEB-INF/views/");
+ templateResolver.setSuffix(".html");
+ templateResolver.setTemplateMode(TemplateMode.HTML);
+ return templateResolver;
+ }
+
+ @Bean
+ public SpringTemplateEngine templateEngine(ServletContextTemplateResolver templateResolver) {
+ SpringTemplateEngine templateEngine = new SpringTemplateEngine();
+ templateEngine.setTemplateResolver(templateResolver);
+ return templateEngine;
+ }
+
+ @Bean
+ public ThymeleafViewResolver viewResolver(SpringTemplateEngine templateEngine) {
+ ThymeleafViewResolver viewResolver = new ThymeleafViewResolver();
+ viewResolver.setTemplateEngine(templateEngine);
+ return viewResolver;
+ }
+}
diff --git a/enterprise-patterns/spring-dispatcher-servlet/src/main/java/com/baeldung/enterprise/patterns/dispatcher/servlet/WebInitializer.java b/enterprise-patterns/spring-dispatcher-servlet/src/main/java/com/baeldung/enterprise/patterns/dispatcher/servlet/WebInitializer.java
new file mode 100644
index 0000000000..a544b2cbac
--- /dev/null
+++ b/enterprise-patterns/spring-dispatcher-servlet/src/main/java/com/baeldung/enterprise/patterns/dispatcher/servlet/WebInitializer.java
@@ -0,0 +1,20 @@
+package com.baeldung.enterprise.patterns.dispatcher.servlet;
+
+import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
+
+public class WebInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
+ @Override
+ protected Class>[] getRootConfigClasses() {
+ return new Class>[]{RootConfiguration.class};
+ }
+
+ @Override
+ protected Class>[] getServletConfigClasses() {
+ return new Class>[]{WebConfiguration.class};
+ }
+
+ @Override
+ protected String[] getServletMappings() {
+ return new String[]{"/example/*"};
+ }
+}
diff --git a/enterprise-patterns/spring-dispatcher-servlet/src/main/java/com/baeldung/enterprise/patterns/dispatcher/servlet/models/Task.java b/enterprise-patterns/spring-dispatcher-servlet/src/main/java/com/baeldung/enterprise/patterns/dispatcher/servlet/models/Task.java
new file mode 100644
index 0000000000..3a84f156a8
--- /dev/null
+++ b/enterprise-patterns/spring-dispatcher-servlet/src/main/java/com/baeldung/enterprise/patterns/dispatcher/servlet/models/Task.java
@@ -0,0 +1,41 @@
+package com.baeldung.enterprise.patterns.dispatcher.servlet.models;
+
+import org.springframework.format.annotation.DateTimeFormat;
+
+import java.util.Date;
+
+public class Task {
+
+ private String description;
+
+ @DateTimeFormat(pattern = "yyyy-MM-dd'T'hh:mm")
+ private Date due;
+
+ public Task() {
+ }
+
+ public Task(Date due) {
+ this.due = due;
+ }
+
+ public Task(String description, Date due) {
+ this.description = description;
+ this.due = due;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
+ public Date getDue() {
+ return due;
+ }
+
+ public void setDue(Date due) {
+ this.due = due;
+ }
+}
diff --git a/enterprise-patterns/spring-dispatcher-servlet/src/main/java/com/baeldung/enterprise/patterns/dispatcher/servlet/web/GlobalDefaultExceptionHandler.java b/enterprise-patterns/spring-dispatcher-servlet/src/main/java/com/baeldung/enterprise/patterns/dispatcher/servlet/web/GlobalDefaultExceptionHandler.java
new file mode 100644
index 0000000000..0c44d0a5e7
--- /dev/null
+++ b/enterprise-patterns/spring-dispatcher-servlet/src/main/java/com/baeldung/enterprise/patterns/dispatcher/servlet/web/GlobalDefaultExceptionHandler.java
@@ -0,0 +1,24 @@
+package com.baeldung.enterprise.patterns.dispatcher.servlet.web;
+
+import org.springframework.core.annotation.AnnotationUtils;
+import org.springframework.web.bind.annotation.ControllerAdvice;
+import org.springframework.web.bind.annotation.ExceptionHandler;
+import org.springframework.web.bind.annotation.ResponseStatus;
+import org.springframework.web.servlet.ModelAndView;
+
+import javax.servlet.http.HttpServletRequest;
+
+@ControllerAdvice
+public class GlobalDefaultExceptionHandler {
+ @ExceptionHandler(Exception.class)
+ public ModelAndView defaultErrorHandler(HttpServletRequest request, Exception e) throws Exception {
+ if (AnnotationUtils.findAnnotation(e.getClass(), ResponseStatus.class) != null) {
+ throw e;
+ }
+ ModelAndView modelAndView = new ModelAndView();
+ modelAndView.addObject("exception", e);
+ modelAndView.addObject("url", request.getRequestURL());
+ modelAndView.setViewName("error");
+ return modelAndView;
+ }
+}
diff --git a/enterprise-patterns/spring-dispatcher-servlet/src/main/java/com/baeldung/enterprise/patterns/dispatcher/servlet/web/HomeController.java b/enterprise-patterns/spring-dispatcher-servlet/src/main/java/com/baeldung/enterprise/patterns/dispatcher/servlet/web/HomeController.java
new file mode 100644
index 0000000000..6e4e437a44
--- /dev/null
+++ b/enterprise-patterns/spring-dispatcher-servlet/src/main/java/com/baeldung/enterprise/patterns/dispatcher/servlet/web/HomeController.java
@@ -0,0 +1,26 @@
+package com.baeldung.enterprise.patterns.dispatcher.servlet.web;
+
+import com.baeldung.enterprise.patterns.dispatcher.servlet.models.Task;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Controller;
+import org.springframework.ui.Model;
+import org.springframework.web.bind.annotation.GetMapping;
+
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+@Controller
+public class HomeController {
+ @Autowired
+ private Map> taskMap;
+
+ @GetMapping("/*")
+ public String home(Model model) {
+ List users = taskMap.keySet().stream()
+ .sorted()
+ .collect(Collectors.toList());
+ model.addAttribute("users", users);
+ return "home";
+ }
+}
diff --git a/enterprise-patterns/spring-dispatcher-servlet/src/main/java/com/baeldung/enterprise/patterns/dispatcher/servlet/web/TaskController.java b/enterprise-patterns/spring-dispatcher-servlet/src/main/java/com/baeldung/enterprise/patterns/dispatcher/servlet/web/TaskController.java
new file mode 100644
index 0000000000..6930464f81
--- /dev/null
+++ b/enterprise-patterns/spring-dispatcher-servlet/src/main/java/com/baeldung/enterprise/patterns/dispatcher/servlet/web/TaskController.java
@@ -0,0 +1,57 @@
+package com.baeldung.enterprise.patterns.dispatcher.servlet.web;
+
+import com.baeldung.enterprise.patterns.dispatcher.servlet.models.Task;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Controller;
+import org.springframework.ui.Model;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+@Controller
+@RequestMapping("/tasks")
+public class TaskController {
+ @Autowired
+ private Map> taskMap;
+
+ @GetMapping("/{username}/list")
+ public String listForm(
+ Model model,
+ @PathVariable("username") String username
+ ) {
+ List tasks = taskMap.get(username).stream()
+ .sorted((t1, t2) -> t1.getDue().compareTo(t2.getDue()))
+ .collect(Collectors.toList());
+ model.addAttribute("username", username);
+ model.addAttribute("tasks", tasks);
+ return "task-list";
+ }
+
+ @GetMapping("/{username}/add")
+ public String addForm(
+ Model model,
+ @PathVariable("username") String username
+ ) {
+ model.addAttribute("username", username);
+ model.addAttribute("task", new Task(new Date()));
+ return "task-add";
+ }
+
+ @PostMapping("/{username}/add")
+ public String addSubmit(
+ @PathVariable("username") String username,
+ @ModelAttribute Task task
+ ) {
+ List taskList = taskMap.get(username);
+ if (taskList == null) {
+ taskList = new ArrayList<>();
+ }
+ taskList.add(task);
+ taskMap.put(username, taskList);
+ return "redirect:list";
+ }
+}
diff --git a/enterprise-patterns/spring-dispatcher-servlet/src/main/resources/log4j2.xml b/enterprise-patterns/spring-dispatcher-servlet/src/main/resources/log4j2.xml
new file mode 100644
index 0000000000..fb18e8279a
--- /dev/null
+++ b/enterprise-patterns/spring-dispatcher-servlet/src/main/resources/log4j2.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/enterprise-patterns/spring-dispatcher-servlet/src/main/webapp/WEB-INF/views/error.html b/enterprise-patterns/spring-dispatcher-servlet/src/main/webapp/WEB-INF/views/error.html
new file mode 100644
index 0000000000..c1b362e51a
--- /dev/null
+++ b/enterprise-patterns/spring-dispatcher-servlet/src/main/webapp/WEB-INF/views/error.html
@@ -0,0 +1,11 @@
+
+
+
+ Error
+
+
+ Error:
+
+
+
+
diff --git a/enterprise-patterns/spring-dispatcher-servlet/src/main/webapp/WEB-INF/views/home.html b/enterprise-patterns/spring-dispatcher-servlet/src/main/webapp/WEB-INF/views/home.html
new file mode 100644
index 0000000000..1a333ed641
--- /dev/null
+++ b/enterprise-patterns/spring-dispatcher-servlet/src/main/webapp/WEB-INF/views/home.html
@@ -0,0 +1,14 @@
+
+
+
+ Welcome to TaskTools!
+
+
+ TaskTools
+
+
+
diff --git a/enterprise-patterns/spring-dispatcher-servlet/src/main/webapp/WEB-INF/views/task-add.html b/enterprise-patterns/spring-dispatcher-servlet/src/main/webapp/WEB-INF/views/task-add.html
new file mode 100644
index 0000000000..70f010e58d
--- /dev/null
+++ b/enterprise-patterns/spring-dispatcher-servlet/src/main/webapp/WEB-INF/views/task-add.html
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/enterprise-patterns/spring-dispatcher-servlet/src/main/webapp/WEB-INF/views/task-list.html b/enterprise-patterns/spring-dispatcher-servlet/src/main/webapp/WEB-INF/views/task-list.html
new file mode 100644
index 0000000000..2fe628456f
--- /dev/null
+++ b/enterprise-patterns/spring-dispatcher-servlet/src/main/webapp/WEB-INF/views/task-list.html
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
+
+
+ Home
+ Add new
+
+
+