├── LICENSE ├── Procfile ├── README.md ├── build.gradle ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── run_tomcat ├── src └── main │ └── java │ └── com │ └── jcs │ └── todomvc │ ├── Application.java │ ├── ResourceWithUrl.java │ ├── SimpleCORSFilter.java │ ├── Todo.java │ └── TodosController.java └── system.properties /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2014 jcsantosbr 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Procfile: -------------------------------------------------------------------------------- 1 | web: ./run_tomcat 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | todo-backend-spring4-java8 2 | ========================== 3 | 4 | Updated version: https://github.com/jcsantosbr/todo-backend-springboot2-java12 5 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | repositories { 3 | maven { url "http://repo.spring.io/libs-release" } 4 | mavenLocal() 5 | mavenCentral() 6 | } 7 | dependencies { 8 | classpath("org.springframework.boot:spring-boot-gradle-plugin:1.1.4.RELEASE") 9 | } 10 | } 11 | 12 | apply plugin: 'java' 13 | apply plugin: 'eclipse' 14 | apply plugin: 'idea' 15 | apply plugin: 'spring-boot' 16 | 17 | jar { 18 | baseName = 'todo-backend' 19 | version = '0.1.0' 20 | } 21 | 22 | repositories { 23 | mavenLocal() 24 | mavenCentral() 25 | maven { url "http://repo.spring.io/libs-release" } 26 | } 27 | 28 | dependencies { 29 | compile("org.springframework.boot:spring-boot-starter-web") 30 | compile("com.fasterxml.jackson.core:jackson-databind") 31 | compile("org.springframework.hateoas:spring-hateoas") 32 | compile("org.springframework.plugin:spring-plugin-core:1.1.0.RELEASE") 33 | compile("com.jayway.jsonpath:json-path:0.9.1") 34 | testCompile("org.springframework.boot:spring-boot-starter-test") 35 | testCompile("junit:junit") 36 | } 37 | 38 | task wrapper(type: Wrapper) { 39 | gradleVersion = '1.11' 40 | } 41 | 42 | task stage(dependsOn: ['build']) 43 | 44 | mainClassName = "com.jcs.todomvc.Application" -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jcsantosbr/todo-backend-spring4-java8/b5664e387c11a089747d3f2ac56cef5ca476e4bb/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Tue Aug 05 21:14:00 PDT 2014 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-1.11-bin.zip 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # For Cygwin, ensure paths are in UNIX format before anything is touched. 46 | if $cygwin ; then 47 | [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` 48 | fi 49 | 50 | # Attempt to set APP_HOME 51 | # Resolve links: $0 may be a link 52 | PRG="$0" 53 | # Need this for relative symlinks. 54 | while [ -h "$PRG" ] ; do 55 | ls=`ls -ld "$PRG"` 56 | link=`expr "$ls" : '.*-> \(.*\)$'` 57 | if expr "$link" : '/.*' > /dev/null; then 58 | PRG="$link" 59 | else 60 | PRG=`dirname "$PRG"`"/$link" 61 | fi 62 | done 63 | SAVED="`pwd`" 64 | cd "`dirname \"$PRG\"`/" >&- 65 | APP_HOME="`pwd -P`" 66 | cd "$SAVED" >&- 67 | 68 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 69 | 70 | # Determine the Java command to use to start the JVM. 71 | if [ -n "$JAVA_HOME" ] ; then 72 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 73 | # IBM's JDK on AIX uses strange locations for the executables 74 | JAVACMD="$JAVA_HOME/jre/sh/java" 75 | else 76 | JAVACMD="$JAVA_HOME/bin/java" 77 | fi 78 | if [ ! -x "$JAVACMD" ] ; then 79 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 80 | 81 | Please set the JAVA_HOME variable in your environment to match the 82 | location of your Java installation." 83 | fi 84 | else 85 | JAVACMD="java" 86 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 87 | 88 | Please set the JAVA_HOME variable in your environment to match the 89 | location of your Java installation." 90 | fi 91 | 92 | # Increase the maximum file descriptors if we can. 93 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 94 | MAX_FD_LIMIT=`ulimit -H -n` 95 | if [ $? -eq 0 ] ; then 96 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 97 | MAX_FD="$MAX_FD_LIMIT" 98 | fi 99 | ulimit -n $MAX_FD 100 | if [ $? -ne 0 ] ; then 101 | warn "Could not set maximum file descriptor limit: $MAX_FD" 102 | fi 103 | else 104 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 105 | fi 106 | fi 107 | 108 | # For Darwin, add options to specify how the application appears in the dock 109 | if $darwin; then 110 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 111 | fi 112 | 113 | # For Cygwin, switch paths to Windows format before running java 114 | if $cygwin ; then 115 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 116 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 158 | function splitJvmOpts() { 159 | JVM_OPTS=("$@") 160 | } 161 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 162 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 163 | 164 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 165 | -------------------------------------------------------------------------------- /run_tomcat: -------------------------------------------------------------------------------- 1 | export SERVER_PORT=$PORT 2 | java -jar build/libs/todo-backend-0.1.0.jar 3 | -------------------------------------------------------------------------------- /src/main/java/com/jcs/todomvc/Application.java: -------------------------------------------------------------------------------- 1 | package com.jcs.todomvc; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.EnableAutoConfiguration; 5 | import org.springframework.context.annotation.ComponentScan; 6 | 7 | @ComponentScan 8 | @EnableAutoConfiguration 9 | public class Application { 10 | 11 | public static void main(String[] args) { 12 | SpringApplication.run(Application.class, args); 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/com/jcs/todomvc/ResourceWithUrl.java: -------------------------------------------------------------------------------- 1 | package com.jcs.todomvc; 2 | 3 | import com.fasterxml.jackson.annotation.JsonUnwrapped; 4 | 5 | import javax.xml.bind.annotation.XmlAnyElement; 6 | import javax.xml.bind.annotation.XmlRootElement; 7 | 8 | @XmlRootElement 9 | public class ResourceWithUrl { 10 | 11 | private T content; 12 | private String url; 13 | 14 | public ResourceWithUrl() { 15 | } 16 | 17 | public ResourceWithUrl(T content, String url) { 18 | this.content = content; 19 | this.url = url; 20 | } 21 | 22 | @JsonUnwrapped 23 | @XmlAnyElement 24 | public T getContent() { 25 | return content; 26 | } 27 | 28 | public void setContent(T content) { 29 | this.content = content; 30 | } 31 | 32 | public String getUrl() { 33 | return url; 34 | } 35 | 36 | public void setUrl(String url) { 37 | this.url = url; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/com/jcs/todomvc/SimpleCORSFilter.java: -------------------------------------------------------------------------------- 1 | package com.jcs.todomvc; 2 | 3 | 4 | import java.io.IOException; 5 | import javax.servlet.Filter; 6 | import javax.servlet.FilterChain; 7 | import javax.servlet.FilterConfig; 8 | import javax.servlet.ServletException; 9 | import javax.servlet.ServletRequest; 10 | import javax.servlet.ServletResponse; 11 | import javax.servlet.http.HttpServletResponse; 12 | import org.springframework.stereotype.Component; 13 | 14 | @Component 15 | public class SimpleCORSFilter implements Filter { 16 | 17 | public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { 18 | HttpServletResponse response = (HttpServletResponse) res; 19 | response.setHeader("Access-Control-Allow-Origin", "*"); 20 | response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE, PATCH"); 21 | response.setHeader("Access-Control-Max-Age", "3600"); 22 | response.setHeader("Access-Control-Allow-Headers", "x-requested-with, origin, content-type, accept"); 23 | chain.doFilter(req, res); 24 | } 25 | 26 | public void init(FilterConfig filterConfig) {} 27 | 28 | public void destroy() {} 29 | 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/com/jcs/todomvc/Todo.java: -------------------------------------------------------------------------------- 1 | package com.jcs.todomvc; 2 | 3 | public class Todo { 4 | 5 | private int id; 6 | private String title; 7 | private Boolean completed; 8 | private Integer order; 9 | 10 | public Todo() { 11 | } 12 | 13 | public Todo(String title) { 14 | this.title = title; 15 | } 16 | 17 | public Todo(int id, String title, Boolean completed, Integer order) { 18 | this.id = id; 19 | this.title = title; 20 | this.completed = completed; 21 | this.order = order; 22 | } 23 | 24 | public String getTitle() { 25 | return title; 26 | } 27 | 28 | public void setTitle(String title) { 29 | this.title = title; 30 | } 31 | 32 | @Override 33 | public boolean equals(Object o) { 34 | if (this == o) return true; 35 | if (o == null || getClass() != o.getClass()) return false; 36 | 37 | Todo todo = (Todo) o; 38 | 39 | if (id != todo.id) return false; 40 | 41 | return true; 42 | } 43 | 44 | @Override 45 | public int hashCode() { 46 | return id; 47 | } 48 | 49 | public boolean isCompleted() { 50 | return nonNull(completed,false); 51 | } 52 | 53 | public void setCompleted(boolean completed) { 54 | this.completed = completed; 55 | } 56 | 57 | public int getOrder() { 58 | return nonNull(order, 0); 59 | } 60 | 61 | public void setOrder(Integer order) { 62 | this.order = order; 63 | } 64 | 65 | public Todo merge(Todo newTodo) { 66 | return new Todo(id, 67 | nonNull(newTodo.title, title), 68 | nonNull(newTodo.completed, completed), 69 | nonNull(newTodo.order, order)); 70 | } 71 | 72 | public long getId() { 73 | return id; 74 | } 75 | 76 | public void setId(int id) { 77 | this.id = id; 78 | } 79 | 80 | private T nonNull(T value, T defaultValue) { 81 | return value == null ? defaultValue : value; 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/main/java/com/jcs/todomvc/TodosController.java: -------------------------------------------------------------------------------- 1 | package com.jcs.todomvc; 2 | 3 | import org.springframework.http.HttpEntity; 4 | import org.springframework.http.HttpStatus; 5 | import org.springframework.http.ResponseEntity; 6 | import org.springframework.web.bind.annotation.PathVariable; 7 | import org.springframework.web.bind.annotation.RequestBody; 8 | import org.springframework.web.bind.annotation.RequestMapping; 9 | import org.springframework.web.bind.annotation.RestController; 10 | 11 | import java.util.*; 12 | import java.util.stream.Collectors; 13 | 14 | import static org.springframework.hateoas.mvc.ControllerLinkBuilder.linkTo; 15 | import static org.springframework.hateoas.mvc.ControllerLinkBuilder.methodOn; 16 | import static org.springframework.http.HttpStatus.*; 17 | import static org.springframework.web.bind.annotation.RequestMethod.*; 18 | 19 | @RestController 20 | @RequestMapping(value = "/todos") 21 | public class TodosController { 22 | 23 | private Set todos = new HashSet<>(); 24 | 25 | @RequestMapping(method = GET) 26 | public HttpEntity> listAll() { 27 | List resourceWithUrls = todos.stream().map(todo -> toResource(todo)).collect(Collectors.toList()); 28 | return new ResponseEntity<>(resourceWithUrls, OK); 29 | } 30 | 31 | @RequestMapping(value = "/{todo-id}", method = GET) 32 | public HttpEntity getTodo(@PathVariable("todo-id") long id) { 33 | 34 | Optional todoOptional = tryToFindById(id); 35 | 36 | if (!todoOptional.isPresent()) 37 | return new ResponseEntity<>(NOT_FOUND); 38 | 39 | return respondWithResource(todoOptional.get(), OK); 40 | } 41 | 42 | private Optional tryToFindById(long id) { 43 | return todos.stream().filter(todo -> todo.getId() == id).findFirst(); 44 | } 45 | 46 | @RequestMapping(method = POST, headers = {"Content-type=application/json"}) 47 | public HttpEntity saveTodo(@RequestBody Todo todo) { 48 | todo.setId(todos.size() + 1); 49 | todos.add(todo); 50 | 51 | return respondWithResource(todo, CREATED); 52 | } 53 | 54 | @RequestMapping(method = DELETE) 55 | public void deleteAllTodos() { 56 | todos.clear(); 57 | } 58 | 59 | @RequestMapping(value = "/{todo-id}", method = DELETE) 60 | public void deleteOneTodo(@PathVariable("todo-id") long id) { 61 | Optional todoOptional = tryToFindById(id); 62 | 63 | if ( todoOptional.isPresent() ) { 64 | todos.remove(todoOptional.get()); 65 | } 66 | } 67 | 68 | @RequestMapping(value = "/{todo-id}", method = PATCH, headers = {"Content-type=application/json"}) 69 | public HttpEntity updateTodo(@PathVariable("todo-id") long id, @RequestBody Todo newTodo) { 70 | Optional todoOptional = tryToFindById(id); 71 | 72 | if ( !todoOptional.isPresent() ) { 73 | return new ResponseEntity<>(NOT_FOUND); 74 | } else if ( newTodo == null ) { 75 | return new ResponseEntity<>(BAD_REQUEST); 76 | } 77 | 78 | todos.remove(todoOptional.get()); 79 | 80 | Todo mergedTodo = todoOptional.get().merge(newTodo); 81 | todos.add(mergedTodo); 82 | 83 | return respondWithResource(mergedTodo, OK); 84 | } 85 | 86 | 87 | private String getHref(Todo todo) { 88 | return linkTo(methodOn(this.getClass()).getTodo(todo.getId())).withSelfRel().getHref(); 89 | } 90 | 91 | private ResourceWithUrl toResource(Todo todo) { 92 | return new ResourceWithUrl(todo, getHref(todo)); 93 | } 94 | 95 | private HttpEntity respondWithResource(Todo todo, HttpStatus statusCode) { 96 | ResourceWithUrl resourceWithUrl = toResource(todo); 97 | 98 | return new ResponseEntity<>(resourceWithUrl, statusCode); 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /system.properties: -------------------------------------------------------------------------------- 1 | java.runtime.version=1.8 2 | --------------------------------------------------------------------------------