From da0f93feb54b384f397665e7795fcd575f059f2a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20R=C3=A4del?= Date: Wed, 19 Oct 2016 15:19:30 +0200 Subject: [PATCH] BAEL-86: - Split controller into interfaces and implementations. - Added ability to up/download files. --- .../dispatcher/servlet/models/Attachment.java | 58 +++++++++ .../dispatcher/servlet/models/Task.java | 12 ++ .../servlet/web/AttachmentController.java | 17 +++ .../servlet/web/AttachmentControllerImpl.java | 45 +++++++ .../servlet/web/HomeController.java | 23 +--- .../servlet/web/HomeControllerImpl.java | 25 ++++ .../servlet/web/TaskController.java | 73 ++++++------ .../servlet/web/TaskControllerImpl.java | 111 ++++++++++++++++++ .../src/main/resources/messages.properties | 12 ++ .../src/main/resources/messages_de.properties | 12 ++ .../src/main/webapp/WEB-INF/views/error.html | 1 + .../src/main/webapp/WEB-INF/views/home.html | 1 + .../main/webapp/WEB-INF/views/task-add.html | 7 +- .../webapp/WEB-INF/views/task-attach.html | 17 +++ .../main/webapp/WEB-INF/views/task-get.html | 28 +++++ .../main/webapp/WEB-INF/views/task-list.html | 2 + .../src/main/webapp/public/css/base.css | 8 ++ 17 files changed, 391 insertions(+), 61 deletions(-) create mode 100644 spring-dispatcher-servlet/src/main/java/com/baeldung/spring/dispatcher/servlet/models/Attachment.java create mode 100644 spring-dispatcher-servlet/src/main/java/com/baeldung/spring/dispatcher/servlet/web/AttachmentController.java create mode 100644 spring-dispatcher-servlet/src/main/java/com/baeldung/spring/dispatcher/servlet/web/AttachmentControllerImpl.java create mode 100644 spring-dispatcher-servlet/src/main/java/com/baeldung/spring/dispatcher/servlet/web/HomeControllerImpl.java create mode 100644 spring-dispatcher-servlet/src/main/java/com/baeldung/spring/dispatcher/servlet/web/TaskControllerImpl.java create mode 100644 spring-dispatcher-servlet/src/main/webapp/WEB-INF/views/task-attach.html create mode 100644 spring-dispatcher-servlet/src/main/webapp/WEB-INF/views/task-get.html create mode 100644 spring-dispatcher-servlet/src/main/webapp/public/css/base.css diff --git a/spring-dispatcher-servlet/src/main/java/com/baeldung/spring/dispatcher/servlet/models/Attachment.java b/spring-dispatcher-servlet/src/main/java/com/baeldung/spring/dispatcher/servlet/models/Attachment.java new file mode 100644 index 0000000000..1d6248650f --- /dev/null +++ b/spring-dispatcher-servlet/src/main/java/com/baeldung/spring/dispatcher/servlet/models/Attachment.java @@ -0,0 +1,58 @@ +package com.baeldung.spring.dispatcher.servlet.models; + +import java.util.UUID; + +public class Attachment { + private String id; + + private String name; + + private String description; + + public Attachment() { + this.id = UUID.randomUUID().toString(); + } + + public Attachment(String name, String description) { + this(); + this.name = name; + this.description = description; + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Attachment that = (Attachment) o; + return id.equals(that.id); + } + + @Override + public int hashCode() { + return id.hashCode(); + } +} diff --git a/spring-dispatcher-servlet/src/main/java/com/baeldung/spring/dispatcher/servlet/models/Task.java b/spring-dispatcher-servlet/src/main/java/com/baeldung/spring/dispatcher/servlet/models/Task.java index d672948112..1e6a533e3a 100644 --- a/spring-dispatcher-servlet/src/main/java/com/baeldung/spring/dispatcher/servlet/models/Task.java +++ b/spring-dispatcher-servlet/src/main/java/com/baeldung/spring/dispatcher/servlet/models/Task.java @@ -3,6 +3,8 @@ package com.baeldung.spring.dispatcher.servlet.models; import org.springframework.format.annotation.DateTimeFormat; import java.util.Date; +import java.util.HashSet; +import java.util.Set; public class Task { private String description; @@ -10,6 +12,8 @@ public class Task { @DateTimeFormat(pattern = "yyyy-MM-dd'T'hh:mm") private Date due; + private Set attachments = new HashSet<>(); + public Task() { } @@ -37,4 +41,12 @@ public class Task { public void setDue(Date due) { this.due = due; } + + public Set getAttachments() { + return attachments; + } + + public void setAttachments(Set attachments) { + this.attachments = attachments; + } } diff --git a/spring-dispatcher-servlet/src/main/java/com/baeldung/spring/dispatcher/servlet/web/AttachmentController.java b/spring-dispatcher-servlet/src/main/java/com/baeldung/spring/dispatcher/servlet/web/AttachmentController.java new file mode 100644 index 0000000000..2521004ff1 --- /dev/null +++ b/spring-dispatcher-servlet/src/main/java/com/baeldung/spring/dispatcher/servlet/web/AttachmentController.java @@ -0,0 +1,17 @@ +package com.baeldung.spring.dispatcher.servlet.web; + +import org.springframework.core.io.FileSystemResource; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; + +@RequestMapping("/attachments") +public interface AttachmentController { + @GetMapping("/{attachmentId}") + ResponseEntity getAttachment( + @PathVariable("attachmentId") String attachmentId, + @RequestParam(name = "download", required = false, defaultValue = "false") boolean forcedDownload + ); +} diff --git a/spring-dispatcher-servlet/src/main/java/com/baeldung/spring/dispatcher/servlet/web/AttachmentControllerImpl.java b/spring-dispatcher-servlet/src/main/java/com/baeldung/spring/dispatcher/servlet/web/AttachmentControllerImpl.java new file mode 100644 index 0000000000..75a15cf657 --- /dev/null +++ b/spring-dispatcher-servlet/src/main/java/com/baeldung/spring/dispatcher/servlet/web/AttachmentControllerImpl.java @@ -0,0 +1,45 @@ +package com.baeldung.spring.dispatcher.servlet.web; + +import com.baeldung.spring.dispatcher.servlet.models.Task; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.io.FileSystemResource; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestParam; + +import java.net.URLConnection; +import java.util.Collection; +import java.util.List; +import java.util.Map; + +@Controller +public class AttachmentControllerImpl implements AttachmentController { + @Autowired + private Map> taskMap; + + @Override + public ResponseEntity getAttachment( + @PathVariable("attachmentId") String attachmentId, + @RequestParam(name = "download", required = false, defaultValue = "false") boolean forcedDownload + ) { + FileSystemResource resource = new FileSystemResource("/tmp/" + attachmentId); + HttpHeaders headers = new HttpHeaders(); + taskMap.values().stream() + .flatMap(Collection::stream) + .flatMap(t -> t.getAttachments().stream()) + .filter(a -> a.getId().equals(attachmentId)) + .findFirst() + .ifPresent(a -> { + headers.add("Content-Disposition", + "attachment; filename=" + a.getName()); + headers.add("Content-Type", forcedDownload ? + MediaType.APPLICATION_OCTET_STREAM_VALUE : + URLConnection.guessContentTypeFromName(a.getName())); + }); + return new ResponseEntity<>(resource, headers, HttpStatus.OK); + } +} diff --git a/spring-dispatcher-servlet/src/main/java/com/baeldung/spring/dispatcher/servlet/web/HomeController.java b/spring-dispatcher-servlet/src/main/java/com/baeldung/spring/dispatcher/servlet/web/HomeController.java index 0a7d81958e..5ac39fadb4 100644 --- a/spring-dispatcher-servlet/src/main/java/com/baeldung/spring/dispatcher/servlet/web/HomeController.java +++ b/spring-dispatcher-servlet/src/main/java/com/baeldung/spring/dispatcher/servlet/web/HomeController.java @@ -1,28 +1,13 @@ package com.baeldung.spring.dispatcher.servlet.web; -import com.baeldung.spring.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 org.springframework.web.bind.annotation.RequestMapping; -import java.util.List; -import java.util.Map; -import java.util.stream.Collectors; - -@Controller @RequestMapping("/") -public class HomeController { - @Autowired - private Map> taskMap; - +public interface HomeController { @GetMapping("/*") - public String home(Model model) { - List users = taskMap.keySet().stream() - .sorted() - .collect(Collectors.toList()); - model.addAttribute("users", users); - return "home"; - } + String home( + Model model + ); } diff --git a/spring-dispatcher-servlet/src/main/java/com/baeldung/spring/dispatcher/servlet/web/HomeControllerImpl.java b/spring-dispatcher-servlet/src/main/java/com/baeldung/spring/dispatcher/servlet/web/HomeControllerImpl.java new file mode 100644 index 0000000000..66d869ca67 --- /dev/null +++ b/spring-dispatcher-servlet/src/main/java/com/baeldung/spring/dispatcher/servlet/web/HomeControllerImpl.java @@ -0,0 +1,25 @@ +package com.baeldung.spring.dispatcher.servlet.web; + +import com.baeldung.spring.dispatcher.servlet.models.Task; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; + +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +@Controller +public class HomeControllerImpl implements HomeController { + @Autowired + private Map> taskMap; + + @Override + public String home(Model model) { + List users = taskMap.keySet().stream() + .sorted() + .collect(Collectors.toList()); + model.addAttribute("users", users); + return "home"; + } +} diff --git a/spring-dispatcher-servlet/src/main/java/com/baeldung/spring/dispatcher/servlet/web/TaskController.java b/spring-dispatcher-servlet/src/main/java/com/baeldung/spring/dispatcher/servlet/web/TaskController.java index 2450e9244d..eff93ffb2f 100644 --- a/spring-dispatcher-servlet/src/main/java/com/baeldung/spring/dispatcher/servlet/web/TaskController.java +++ b/spring-dispatcher-servlet/src/main/java/com/baeldung/spring/dispatcher/servlet/web/TaskController.java @@ -1,54 +1,49 @@ package com.baeldung.spring.dispatcher.servlet.web; import com.baeldung.spring.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 org.springframework.web.multipart.MultipartFile; -import java.util.*; -import java.util.stream.Collectors; - -@Controller @RequestMapping("/tasks") -public class TaskController { - @Autowired - private Map> taskMap; - +public interface TaskController { @GetMapping("/{username}/list") - public String listForm( - Model model, - @PathVariable("username") String username - ) { - List tasks = taskMap.get(username).stream() - .sorted(Comparator.comparing(Task::getDue)) - .collect(Collectors.toList()); - model.addAttribute("username", username); - model.addAttribute("tasks", tasks); - return "task-list"; - } + String listTasks( + @PathVariable("username") String username, + Model model + ); @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"; - } + String addTask( + @PathVariable("username") String username, + Model model + ); @PostMapping("/{username}/add") - public String addSubmit( + String addTask( @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"; - } + ); + + @GetMapping("/{username}/get/{id}") + String getTask( + @PathVariable("username") String username, + @PathVariable("id") int id, + Model model + ); + + @GetMapping("/{username}/get/{id}/attach") + String attachToTask( + @PathVariable("username") String username, + @PathVariable("id") int id, + Model model + ); + + @PostMapping("/{username}/get/{id}/attach") + String attachToTask( + @PathVariable("username") String username, + @PathVariable("id") int id, + @RequestParam("attachment") MultipartFile attachment, + @RequestParam("description") String description + ); } diff --git a/spring-dispatcher-servlet/src/main/java/com/baeldung/spring/dispatcher/servlet/web/TaskControllerImpl.java b/spring-dispatcher-servlet/src/main/java/com/baeldung/spring/dispatcher/servlet/web/TaskControllerImpl.java new file mode 100644 index 0000000000..464e58aa54 --- /dev/null +++ b/spring-dispatcher-servlet/src/main/java/com/baeldung/spring/dispatcher/servlet/web/TaskControllerImpl.java @@ -0,0 +1,111 @@ +package com.baeldung.spring.dispatcher.servlet.web; + +import com.baeldung.spring.dispatcher.servlet.models.Attachment; +import com.baeldung.spring.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.ModelAttribute; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.multipart.MultipartFile; + +import java.io.*; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.*; +import java.util.stream.Collectors; + +@Controller +public class TaskControllerImpl implements TaskController { + @Autowired + private Map> taskMap; + + @Override + public String listTasks( + @PathVariable("username") String username, + Model model + ) { + List tasks = taskMap.get(username).stream() + .sorted(Comparator.comparing(Task::getDue)) + .collect(Collectors.toList()); + model.addAttribute("username", username); + model.addAttribute("tasks", tasks); + return "task-list"; + } + + @Override + public String addTask( + @PathVariable("username") String username, + Model model + ) { + model.addAttribute("username", username); + model.addAttribute("task", new Task(new Date())); + return "task-add"; + } + + @Override + public String addTask( + @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"; + } + + @Override + public String getTask( + @PathVariable("username") String username, + @PathVariable("id") int id, + Model model + ) { + Task task = taskMap.get(username).get(id); + model.addAttribute("username", username); + model.addAttribute("id", id); + model.addAttribute("task", task); + return "task-get"; + } + + @Override + public String attachToTask( + @PathVariable("username") String username, + @PathVariable("id") int id, + Model model + ) { + model.addAttribute("username", username); + model.addAttribute("id", id); + return "task-attach"; + } + + @Override + public String attachToTask( + @PathVariable("username") String username, + @PathVariable("id") int id, + @RequestParam("attachment") MultipartFile multipartFile, + @RequestParam("description") String description + ) { + Task task = taskMap.get(username).get(id); + Attachment attachment = new Attachment(multipartFile.getOriginalFilename(), + description); + task.getAttachments().add(attachment); + try (InputStream inputStream = + new BufferedInputStream(multipartFile.getInputStream()); + OutputStream outputStream = + new BufferedOutputStream(Files.newOutputStream( + Paths.get("/tmp", attachment.getId())))) { + byte[] buf = new byte[1024 * 16]; + int len; + while ((len = inputStream.read(buf)) != -1) { + outputStream.write(buf, 0, len); + } + } catch (IOException e) { + throw new RuntimeException("Failed to upload file!", e); + } + return "redirect:./"; + } +} diff --git a/spring-dispatcher-servlet/src/main/resources/messages.properties b/spring-dispatcher-servlet/src/main/resources/messages.properties index 63f0c63a4d..c36eb5aa42 100644 --- a/spring-dispatcher-servlet/src/main/resources/messages.properties +++ b/spring-dispatcher-servlet/src/main/resources/messages.properties @@ -4,7 +4,19 @@ task.add.due=due task.add.header=Adding a task to {0}''s list: task.add.submit=Submit task.add.title={0}: task add +task.attach.attachment=attached file +task.attach.description=description +task.attach.header=task #{0}: attach file +task.attach.submit=Submit +task.attach.title=#{0}: Attach file +task.get.attach=Attach file +task.get.attachments=attachments: +task.get.download=Download +task.get.header=task #{0}: +task.get.list=Back +task.get.title={0}: task details task.list.add-new=Add new +task.list.details=Details task.list.header={0}''s tasks: task.list.home=Home task.list.title={0}: task list diff --git a/spring-dispatcher-servlet/src/main/resources/messages_de.properties b/spring-dispatcher-servlet/src/main/resources/messages_de.properties index 8bda8fa698..184b72101c 100644 --- a/spring-dispatcher-servlet/src/main/resources/messages_de.properties +++ b/spring-dispatcher-servlet/src/main/resources/messages_de.properties @@ -4,7 +4,19 @@ task.add.due=f\u00e4llig task.add.header=F\u00fcge eine Aufgabe zu {0}''s Liste hinzu: task.add.submit=Senden task.add.title={0}: Task hinzuf\u00fcgen +task.attach.attachment=Angeh\u00e4ngte Datei +task.attach.description=Beschreibung +task.attach.header=Task #{0}: Datei anh\u00e4ngen +task.attach.submit=Senden +task.attach.title=#{0}: Datei anh\u00e4ngen +task.get.attach=Datei anh\u00e4ngen +task.get.attachments=Anh\u00e4nge: +task.get.download=Download +task.get.header=Task #{0}: +task.get.list=Zur\u00fcck +task.get.title={0}: Task Details task.list.add-new=Neuer Task +task.list.details=Details task.list.header={0}''s Tasks: task.list.home=Startseite task.list.title={0}: Task Liste diff --git a/spring-dispatcher-servlet/src/main/webapp/WEB-INF/views/error.html b/spring-dispatcher-servlet/src/main/webapp/WEB-INF/views/error.html index cc1ea12789..8f0f6afca7 100644 --- a/spring-dispatcher-servlet/src/main/webapp/WEB-INF/views/error.html +++ b/spring-dispatcher-servlet/src/main/webapp/WEB-INF/views/error.html @@ -2,6 +2,7 @@ Error + diff --git a/spring-dispatcher-servlet/src/main/webapp/WEB-INF/views/home.html b/spring-dispatcher-servlet/src/main/webapp/WEB-INF/views/home.html index e26079de96..6adec7bb12 100644 --- a/spring-dispatcher-servlet/src/main/webapp/WEB-INF/views/home.html +++ b/spring-dispatcher-servlet/src/main/webapp/WEB-INF/views/home.html @@ -2,6 +2,7 @@ + diff --git a/spring-dispatcher-servlet/src/main/webapp/WEB-INF/views/task-add.html b/spring-dispatcher-servlet/src/main/webapp/WEB-INF/views/task-add.html index 68b9b233c5..520486f52e 100644 --- a/spring-dispatcher-servlet/src/main/webapp/WEB-INF/views/task-add.html +++ b/spring-dispatcher-servlet/src/main/webapp/WEB-INF/views/task-add.html @@ -2,14 +2,15 @@ +

- - - + + +
diff --git a/spring-dispatcher-servlet/src/main/webapp/WEB-INF/views/task-attach.html b/spring-dispatcher-servlet/src/main/webapp/WEB-INF/views/task-attach.html new file mode 100644 index 0000000000..23246f330b --- /dev/null +++ b/spring-dispatcher-servlet/src/main/webapp/WEB-INF/views/task-attach.html @@ -0,0 +1,17 @@ + + + + + + + + +

+
+ + + +
+ + diff --git a/spring-dispatcher-servlet/src/main/webapp/WEB-INF/views/task-get.html b/spring-dispatcher-servlet/src/main/webapp/WEB-INF/views/task-get.html new file mode 100644 index 0000000000..e7b35b6780 --- /dev/null +++ b/spring-dispatcher-servlet/src/main/webapp/WEB-INF/views/task-get.html @@ -0,0 +1,28 @@ + + + + + + + + +

+

+

+
+

+
+
+
+ + +
+
+
+

+ + +

+ + diff --git a/spring-dispatcher-servlet/src/main/webapp/WEB-INF/views/task-list.html b/spring-dispatcher-servlet/src/main/webapp/WEB-INF/views/task-list.html index cc5e0a396d..0671f24ba3 100644 --- a/spring-dispatcher-servlet/src/main/webapp/WEB-INF/views/task-list.html +++ b/spring-dispatcher-servlet/src/main/webapp/WEB-INF/views/task-list.html @@ -2,6 +2,7 @@ + @@ -10,6 +11,7 @@
  • +
  • diff --git a/spring-dispatcher-servlet/src/main/webapp/public/css/base.css b/spring-dispatcher-servlet/src/main/webapp/public/css/base.css new file mode 100644 index 0000000000..4d3c2597cf --- /dev/null +++ b/spring-dispatcher-servlet/src/main/webapp/public/css/base.css @@ -0,0 +1,8 @@ +a.button { + -webkit-appearance: button; + -moz-appearance: button; + appearance: button; + text-decoration: none; + color: #2196f3; + background-color: #e0e0e0; +}