├── .gitignore ├── blade ├── src │ └── main │ │ ├── resources │ │ └── application.properties │ │ └── java │ │ └── nl │ │ └── jaapcoomans │ │ └── microframeworks │ │ └── blade │ │ ├── JacksonJsonSupport.java │ │ ├── BladeApplication.java │ │ └── TodoRestController.java ├── Dockerfile └── pom.xml ├── quarkus ├── src │ └── main │ │ ├── resources │ │ └── application.properties │ │ └── java │ │ └── nl │ │ └── jaapcoomans │ │ └── microframeworks │ │ └── quarkus │ │ ├── QuarkusApplication.java │ │ ├── HelloWorldController.java │ │ └── TodoController.java ├── Dockerfile └── pom.xml ├── helidon-se ├── src │ └── main │ │ ├── resources │ │ └── application.yaml │ │ └── java │ │ └── nl │ │ └── jaapcoomans │ │ └── demo │ │ └── microframeworks │ │ └── helidon │ │ └── se │ │ ├── HelloWorldRestController.java │ │ ├── HelidonApplication.java │ │ └── TodoRestController.java ├── Dockerfile └── pom.xml ├── helidon-mp ├── src │ └── main │ │ ├── resources │ │ └── META-INF │ │ │ ├── microprofile-config.properties │ │ │ └── beans.xml │ │ └── java │ │ └── nl │ │ └── jaapcoomans │ │ └── microframeworks │ │ └── helidon │ │ └── mp │ │ ├── HelidonMpApplication.java │ │ ├── HelloWorldApplication.java │ │ ├── HelloWorldController.java │ │ ├── CorsFilter.java │ │ ├── TodoApplication.java │ │ └── TodoController.java ├── Dockerfile └── pom.xml ├── micronaut ├── src │ └── main │ │ ├── resources │ │ └── application.yaml │ │ └── java │ │ └── nl │ │ └── jaapcoomans │ │ └── demo │ │ └── microframeworks │ │ └── micronaut │ │ ├── MicronautApplication.java │ │ ├── TodoFactory.java │ │ ├── HelloWorldController.java │ │ └── TodoController.java ├── Dockerfile └── pom.xml ├── README.md ├── ktor ├── src │ └── main │ │ ├── resources │ │ └── application.conf │ │ └── kotlin │ │ └── nl │ │ └── jaapcoomans │ │ └── demo │ │ └── microframeworks │ │ └── ktor │ │ ├── KtorApplication.kt │ │ └── TodoBackend.kt ├── Dockerfile └── pom.xml ├── kumuluzee ├── Dockerfile ├── src │ └── main │ │ ├── resources │ │ └── META-INF │ │ │ └── beans.xml │ │ └── java │ │ └── nl │ │ └── jaapcoomans │ │ └── demo │ │ └── microframeworks │ │ └── kumuluzee │ │ ├── KumuluzEEApplication.java │ │ ├── TodoBackendFactory.java │ │ ├── HelloWorldController.java │ │ └── TodoController.java └── pom.xml ├── spark ├── Dockerfile ├── src │ └── main │ │ └── java │ │ └── nl │ │ └── jaapcoomans │ │ └── microframeworks │ │ └── spark │ │ ├── JacksonResponseTransformer.java │ │ ├── SparkApplication.java │ │ └── TodoRestController.java └── pom.xml ├── armeria ├── Dockerfile ├── src │ └── main │ │ └── java │ │ └── nl │ │ └── jaapcoomans │ │ └── microframeworks │ │ └── armeria │ │ ├── ArmeriaApplication.java │ │ └── TodoRestController.java └── pom.xml ├── javalin ├── Dockerfile ├── src │ └── main │ │ └── java │ │ └── nl │ │ └── jaapcoomans │ │ └── microframeworks │ │ └── javalin │ │ ├── JavalinApplication.java │ │ └── TodoRestController.java └── pom.xml ├── jooby ├── Dockerfile ├── src │ └── main │ │ └── java │ │ └── nl │ │ └── jaapcoomans │ │ └── demo │ │ └── microframeworks │ │ └── jooby │ │ ├── JoobyApplication.java │ │ └── TodoRestController.java └── pom.xml ├── http4k ├── Dockerfile ├── src │ └── main │ │ └── kotlin │ │ └── nl │ │ └── jaapcoomans │ │ └── demo │ │ └── microframeworks │ │ └── http4k │ │ ├── Http4kApplication.kt │ │ └── TodoRestController.kt └── pom.xml ├── minijax ├── Dockerfile ├── src │ └── main │ │ └── java │ │ └── nl │ │ └── jaapcoomans │ │ └── microframeworks │ │ └── minijax │ │ ├── HelloWorldController.java │ │ ├── MinijaxApplication.java │ │ └── TodoController.java └── pom.xml ├── pippo ├── Dockerfile ├── src │ └── main │ │ └── java │ │ └── nl │ │ └── jaapcoomans │ │ └── demo │ │ └── microframeworks │ │ └── pippo │ │ ├── PippoApplication.java │ │ └── TodoRestController.java └── pom.xml ├── ratpack ├── Dockerfile ├── pom.xml └── src │ └── main │ └── java │ └── nl │ └── jaapcoomans │ └── demo │ └── microframeworks │ └── ratpack │ ├── RatpackApplication.java │ └── TodoRestController.java ├── dropwizard ├── Dockerfile ├── src │ └── main │ │ └── java │ │ └── nl │ │ └── jaapcoomans │ │ └── microframeworks │ │ └── dropwizard │ │ ├── HelloWorldController.java │ │ ├── DropWizardApplication.java │ │ └── TodoRestController.java └── pom.xml ├── todo-backend ├── src │ └── main │ │ └── java │ │ └── nl │ │ └── jaapcoomans │ │ └── demo │ │ └── microframeworks │ │ └── todo │ │ ├── domain │ │ ├── TodoRepository.java │ │ ├── PartialTodo.java │ │ ├── TodoService.java │ │ └── Todo.java │ │ ├── api │ │ ├── CreateTodoCommand.java │ │ └── TodoDTO.java │ │ └── peristsence │ │ └── InMemoryTodoRepository.java └── pom.xml ├── todo-backend-test ├── src │ └── test │ │ └── java │ │ └── nl │ │ └── jaapcoomans │ │ └── demo │ │ └── microframeworks │ │ └── todo │ │ └── test │ │ ├── JavalinTodoTest.java │ │ ├── Http4kTodoBackendTest.java │ │ ├── KtorTodoBackendTest.java │ │ ├── BladeTodoBackendTest.java │ │ ├── JoobyTodoBackendTest.java │ │ ├── PippoTodoBackendTest.java │ │ ├── SparkTodoBackendTest.java │ │ ├── ArmeriaTodoBackendTest.java │ │ ├── MinijaxTodoBackendTest.java │ │ ├── RatpackTodoBackendTest.java │ │ ├── HelidonMpTodoBackendTest.java │ │ ├── HelidonSeTodoBackendTest.java │ │ ├── KumuluzEeTodoBackendTest.java │ │ ├── QuarkusTodoBackendTest.java │ │ ├── DropWizardTodoBackendTest.java │ │ ├── MicronautTodoBackendTest.java │ │ └── BaseTodoBackendTest.java └── pom.xml ├── LICENSE ├── docker-compose.yaml └── pom.xml /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | *.iml 3 | target 4 | -------------------------------------------------------------------------------- /blade/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | server.port=8080 2 | -------------------------------------------------------------------------------- /quarkus/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | quarkus.http.cors=true -------------------------------------------------------------------------------- /helidon-se/src/main/resources/application.yaml: -------------------------------------------------------------------------------- 1 | server: 2 | port: 8080 3 | host: 0.0.0.0 4 | -------------------------------------------------------------------------------- /helidon-mp/src/main/resources/META-INF/microprofile-config.properties: -------------------------------------------------------------------------------- 1 | server.port=8080 2 | server.host=0.0.0.0 3 | -------------------------------------------------------------------------------- /micronaut/src/main/resources/application.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | micronaut: 4 | server: 5 | cors: 6 | enabled: true 7 | 8 | todos: 9 | baseUrl: http://localhost:8080/todos -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 15 Microframeworks for the JVM 2 | This repo contains 15 different implementations, using 15 different microframeworks, of TODO backend and a simple 3 | Hello World endpoint. 4 | 5 | -------------------------------------------------------------------------------- /ktor/src/main/resources/application.conf: -------------------------------------------------------------------------------- 1 | ktor { 2 | deployment { 3 | port = 8080 4 | } 5 | 6 | application { 7 | modules = [ nl.jaapcoomans.demo.microframeworks.ktor.KtorApplicationKt.module ] 8 | } 9 | } -------------------------------------------------------------------------------- /blade/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM adoptopenjdk/openjdk16:jre-16.0.1_9-alpine 2 | 3 | WORKDIR /opt/application 4 | 5 | COPY target/libs/* ./libs/ 6 | COPY target/application.jar ./application.jar 7 | 8 | ENTRYPOINT java -cp "/opt/application/application.jar:/opt/application/libs/*" nl.jaapcoomans.microframeworks.blade.BladeApplication -------------------------------------------------------------------------------- /kumuluzee/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM adoptopenjdk/openjdk15:jre-15.0.2_7-alpine 2 | 3 | WORKDIR /opt/application 4 | 5 | COPY target/libs/* ./libs/ 6 | COPY target/application.jar ./application.jar 7 | 8 | ENTRYPOINT java --illegal-access=warn -cp "/opt/application/application.jar:/opt/application/libs/*" com.kumuluz.ee.EeApplication -------------------------------------------------------------------------------- /spark/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM adoptopenjdk/openjdk16:jre-16.0.1_9-alpine 2 | 3 | WORKDIR /opt/application 4 | 5 | COPY target/libs/* ./libs/ 6 | COPY target/application.jar ./application.jar 7 | 8 | ENTRYPOINT java -cp "/opt/application/application.jar:/opt/application/libs/*" nl.jaapcoomans.microframeworks.spark.SparkApplication -------------------------------------------------------------------------------- /armeria/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM adoptopenjdk/openjdk16:jre-16.0.1_9-alpine 2 | 3 | WORKDIR /opt/application 4 | 5 | COPY target/libs/* ./libs/ 6 | COPY target/application.jar ./application.jar 7 | 8 | ENTRYPOINT java -cp "/opt/application/application.jar:/opt/application/libs/*" nl.jaapcoomans.microframeworks.armeria.ArmeriaApplication -------------------------------------------------------------------------------- /javalin/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM adoptopenjdk/openjdk16:jre-16.0.1_9-alpine 2 | 3 | WORKDIR /opt/application 4 | 5 | COPY target/libs/* ./libs/ 6 | COPY target/application.jar ./application.jar 7 | 8 | ENTRYPOINT java -cp "/opt/application/application.jar:/opt/application/libs/*" nl.jaapcoomans.microframeworks.javalin.JavalinApplication -------------------------------------------------------------------------------- /jooby/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM adoptopenjdk/openjdk16:jre-16.0.1_9-alpine 2 | 3 | WORKDIR /opt/application 4 | 5 | COPY target/libs/* ./libs/ 6 | COPY target/application.jar ./application.jar 7 | 8 | ENTRYPOINT java -cp "/opt/application/application.jar:/opt/application/libs/*" nl.jaapcoomans.demo.microframeworks.jooby.JoobyApplication -------------------------------------------------------------------------------- /ktor/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM adoptopenjdk/openjdk16:jre-16.0.1_9-alpine 2 | 3 | WORKDIR /opt/application 4 | 5 | COPY target/libs/* ./libs/ 6 | COPY target/application.jar ./application.jar 7 | 8 | ENTRYPOINT java -cp "/opt/application/application.jar:/opt/application/libs/*" nl.jaapcoomans.demo.microframeworks.ktor.KtorApplicationKt -------------------------------------------------------------------------------- /http4k/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM adoptopenjdk/openjdk16:jre-16.0.1_9-alpine 2 | 3 | WORKDIR /opt/application 4 | 5 | COPY target/libs/* ./libs/ 6 | COPY target/application.jar ./application.jar 7 | 8 | ENTRYPOINT java -cp "/opt/application/application.jar:/opt/application/libs/*" nl.jaapcoomans.demo.microframeworks.http4k.Http4kApplicationKt -------------------------------------------------------------------------------- /minijax/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM adoptopenjdk/openjdk16:jre-16.0.1_9-alpine 2 | 3 | WORKDIR /opt/application 4 | 5 | COPY target/libs/* ./libs/ 6 | COPY target/application.jar ./application.jar 7 | 8 | ENTRYPOINT java -cp "/opt/application/application.jar:/opt/application/libs/*" nl.jaapcoomans.microframeworks.minijax.MinijaxApplication 9 | -------------------------------------------------------------------------------- /pippo/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM adoptopenjdk/openjdk16:jre-16.0.1_9-alpine 2 | 3 | WORKDIR /opt/application 4 | 5 | COPY target/libs/* ./libs/ 6 | COPY target/application.jar ./application.jar 7 | 8 | ENTRYPOINT java -cp "/opt/application/application.jar:/opt/application/libs/*" nl.jaapcoomans.demo.microframeworks.pippo.PippoApplication 9 | -------------------------------------------------------------------------------- /ratpack/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM adoptopenjdk/openjdk16:jre-16.0.1_9-alpine 2 | 3 | WORKDIR /opt/application 4 | 5 | COPY target/libs/* ./libs/ 6 | COPY target/application.jar ./application.jar 7 | 8 | ENTRYPOINT java -cp "/opt/application/application.jar:/opt/application/libs/*" nl.jaapcoomans.demo.microframeworks.ratpack.RatpackApplication -------------------------------------------------------------------------------- /helidon-mp/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM adoptopenjdk/openjdk16:jre-16.0.1_9-alpine 2 | 3 | WORKDIR /opt/application 4 | 5 | COPY target/libs/* ./libs/ 6 | COPY target/application.jar ./application.jar 7 | 8 | ENTRYPOINT java -cp "/opt/application/application.jar:/opt/application/libs/*" nl.jaapcoomans.microframeworks.helidon.mp.HelidonMpApplication -------------------------------------------------------------------------------- /helidon-se/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM adoptopenjdk/openjdk16:jre-16.0.1_9-alpine 2 | 3 | WORKDIR /opt/application 4 | 5 | COPY target/libs/* ./libs/ 6 | COPY target/application.jar ./application.jar 7 | 8 | ENTRYPOINT java -cp "/opt/application/application.jar:/opt/application/libs/*" nl.jaapcoomans.demo.microframeworks.helidon.se.HelidonApplication -------------------------------------------------------------------------------- /dropwizard/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM adoptopenjdk/openjdk16:jre-16.0.1_9-alpine 2 | 3 | WORKDIR /opt/application 4 | 5 | COPY target/libs/* ./libs/ 6 | COPY target/application.jar ./application.jar 7 | 8 | ENTRYPOINT java -cp "/opt/application/application.jar:/opt/application/libs/*" nl.jaapcoomans.microframeworks.dropwizard.DropWizardApplication server -------------------------------------------------------------------------------- /micronaut/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM adoptopenjdk/openjdk16:jre-16.0.1_9-alpine 2 | 3 | WORKDIR /opt/application 4 | 5 | COPY target/libs/* ./libs/ 6 | COPY target/application.jar ./application.jar 7 | 8 | ENTRYPOINT java -cp "/opt/application/application.jar:/opt/application/libs/*" nl.jaapcoomans.demo.microframeworks.micronaut.MicronautApplication server -------------------------------------------------------------------------------- /kumuluzee/src/main/resources/META-INF/beans.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | -------------------------------------------------------------------------------- /micronaut/src/main/java/nl/jaapcoomans/demo/microframeworks/micronaut/MicronautApplication.java: -------------------------------------------------------------------------------- 1 | package nl.jaapcoomans.demo.microframeworks.micronaut; 2 | 3 | import io.micronaut.runtime.Micronaut; 4 | 5 | public class MicronautApplication { 6 | public static void main(String[] args) { 7 | Micronaut.run(MicronautApplication.class); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /todo-backend/src/main/java/nl/jaapcoomans/demo/microframeworks/todo/domain/TodoRepository.java: -------------------------------------------------------------------------------- 1 | package nl.jaapcoomans.demo.microframeworks.todo.domain; 2 | 3 | import java.util.List; 4 | import java.util.Optional; 5 | import java.util.UUID; 6 | 7 | public interface TodoRepository { 8 | Todo persist(Todo todo); 9 | 10 | List findAll(); 11 | 12 | void deleteAll(); 13 | 14 | Optional findById(UUID id); 15 | 16 | void delete(UUID id); 17 | } 18 | -------------------------------------------------------------------------------- /helidon-mp/src/main/java/nl/jaapcoomans/microframeworks/helidon/mp/HelidonMpApplication.java: -------------------------------------------------------------------------------- 1 | package nl.jaapcoomans.microframeworks.helidon.mp; 2 | 3 | import io.helidon.microprofile.server.Server; 4 | 5 | public class HelidonMpApplication { 6 | public static void main(String[] args) { 7 | Server.builder() 8 | .addApplication(new HelloWorldApplication()) 9 | .addApplication(new TodoApplication()) 10 | .build() 11 | .start(); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /helidon-mp/src/main/java/nl/jaapcoomans/microframeworks/helidon/mp/HelloWorldApplication.java: -------------------------------------------------------------------------------- 1 | package nl.jaapcoomans.microframeworks.helidon.mp; 2 | 3 | import javax.enterprise.context.ApplicationScoped; 4 | import javax.ws.rs.ApplicationPath; 5 | import javax.ws.rs.core.Application; 6 | import java.util.Set; 7 | 8 | @ApplicationScoped 9 | @ApplicationPath("/") 10 | public class HelloWorldApplication extends Application { 11 | @Override 12 | public Set> getClasses() { 13 | return Set.of(HelloWorldController.class); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /todo-backend/src/main/java/nl/jaapcoomans/demo/microframeworks/todo/api/CreateTodoCommand.java: -------------------------------------------------------------------------------- 1 | package nl.jaapcoomans.demo.microframeworks.todo.api; 2 | 3 | public class CreateTodoCommand { 4 | private String title; 5 | private int order; 6 | 7 | public String getTitle() { 8 | return title; 9 | } 10 | 11 | public void setTitle(String title) { 12 | this.title = title; 13 | } 14 | 15 | public int getOrder() { 16 | return order; 17 | } 18 | 19 | public void setOrder(int order) { 20 | this.order = order; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /micronaut/src/main/java/nl/jaapcoomans/demo/microframeworks/micronaut/TodoFactory.java: -------------------------------------------------------------------------------- 1 | package nl.jaapcoomans.demo.microframeworks.micronaut; 2 | 3 | import io.micronaut.context.annotation.Bean; 4 | import io.micronaut.context.annotation.Factory; 5 | import nl.jaapcoomans.demo.microframeworks.todo.domain.TodoService; 6 | import nl.jaapcoomans.demo.microframeworks.todo.peristsence.InMemoryTodoRepository; 7 | 8 | @Factory 9 | public class TodoFactory { 10 | @Bean() 11 | public TodoService todoService() { 12 | return new TodoService(new InMemoryTodoRepository()); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /kumuluzee/src/main/java/nl/jaapcoomans/demo/microframeworks/kumuluzee/KumuluzEEApplication.java: -------------------------------------------------------------------------------- 1 | package nl.jaapcoomans.demo.microframeworks.kumuluzee; 2 | 3 | import javax.ws.rs.ApplicationPath; 4 | import javax.ws.rs.core.Application; 5 | import java.util.HashSet; 6 | import java.util.Set; 7 | 8 | @ApplicationPath("") 9 | public class KumuluzEEApplication extends Application { 10 | @Override 11 | public Set> getClasses() { 12 | Set> classes = new HashSet<>(); 13 | classes.add(HelloWorldController.class); 14 | classes.add(TodoController.class); 15 | return classes; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /todo-backend/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | todo-backend 8 | 9 | 10 | parent 11 | nl.jaapcoomans.demo.microframeworks 12 | 0.0.1-SNAPSHOT 13 | 14 | 15 | 16 | 1.8 17 | 18 | -------------------------------------------------------------------------------- /kumuluzee/src/main/java/nl/jaapcoomans/demo/microframeworks/kumuluzee/TodoBackendFactory.java: -------------------------------------------------------------------------------- 1 | package nl.jaapcoomans.demo.microframeworks.kumuluzee; 2 | 3 | import nl.jaapcoomans.demo.microframeworks.todo.domain.TodoRepository; 4 | import nl.jaapcoomans.demo.microframeworks.todo.domain.TodoService; 5 | import nl.jaapcoomans.demo.microframeworks.todo.peristsence.InMemoryTodoRepository; 6 | 7 | import javax.enterprise.context.ApplicationScoped; 8 | import javax.enterprise.inject.Produces; 9 | 10 | @ApplicationScoped 11 | public class TodoBackendFactory { 12 | @Produces 13 | public TodoRepository todoRepository() { 14 | return new InMemoryTodoRepository(); 15 | } 16 | 17 | @Produces 18 | public TodoService todoService() { 19 | return new TodoService(todoRepository()); 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /todo-backend-test/src/test/java/nl/jaapcoomans/demo/microframeworks/todo/test/JavalinTodoTest.java: -------------------------------------------------------------------------------- 1 | package nl.jaapcoomans.demo.microframeworks.todo.test; 2 | 3 | import org.testcontainers.containers.GenericContainer; 4 | import org.testcontainers.images.builder.ImageFromDockerfile; 5 | import org.testcontainers.junit.jupiter.Container; 6 | import org.testcontainers.junit.jupiter.Testcontainers; 7 | 8 | import java.nio.file.Paths; 9 | 10 | @Testcontainers 11 | class JavalinTodoTest extends BaseTodoBackendTest { 12 | private static final ImageFromDockerfile javalinImage = new ImageFromDockerfile().withFileFromPath(".", Paths.get("../javalin")); 13 | 14 | @Container 15 | private static final GenericContainer javalinContainer = new GenericContainer<>(javalinImage).withExposedPorts(7000); 16 | 17 | final GenericContainergetContainer() { 18 | return javalinContainer; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /todo-backend-test/src/test/java/nl/jaapcoomans/demo/microframeworks/todo/test/Http4kTodoBackendTest.java: -------------------------------------------------------------------------------- 1 | package nl.jaapcoomans.demo.microframeworks.todo.test; 2 | 3 | import org.testcontainers.containers.GenericContainer; 4 | import org.testcontainers.images.builder.ImageFromDockerfile; 5 | import org.testcontainers.junit.jupiter.Container; 6 | import org.testcontainers.junit.jupiter.Testcontainers; 7 | 8 | import java.nio.file.Paths; 9 | 10 | @Testcontainers 11 | class Http4kTodoBackendTest extends BaseTodoBackendTest { 12 | private static final ImageFromDockerfile http4kImage = new ImageFromDockerfile().withFileFromPath(".", Paths.get("../http4k")); 13 | 14 | @Container 15 | private static final GenericContainer http4kContainer = new GenericContainer<>(http4kImage).withExposedPorts(8080); 16 | 17 | final GenericContainer getContainer() { 18 | return http4kContainer; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /todo-backend-test/src/test/java/nl/jaapcoomans/demo/microframeworks/todo/test/KtorTodoBackendTest.java: -------------------------------------------------------------------------------- 1 | package nl.jaapcoomans.demo.microframeworks.todo.test; 2 | 3 | import org.testcontainers.containers.GenericContainer; 4 | import org.testcontainers.images.builder.ImageFromDockerfile; 5 | import org.testcontainers.junit.jupiter.Container; 6 | import org.testcontainers.junit.jupiter.Testcontainers; 7 | 8 | import java.nio.file.Paths; 9 | 10 | @Testcontainers 11 | class KtorTodoBackendTest extends BaseTodoBackendTest { 12 | private static final ImageFromDockerfile ktorImage = new ImageFromDockerfile().withFileFromPath(".", Paths.get("../ktor")); 13 | 14 | @Container 15 | private static final GenericContainer ktorContainer = new GenericContainer<>(ktorImage).withExposedPorts(8080); 16 | 17 | @Override 18 | GenericContainer getContainer() { 19 | return ktorContainer; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /todo-backend-test/src/test/java/nl/jaapcoomans/demo/microframeworks/todo/test/BladeTodoBackendTest.java: -------------------------------------------------------------------------------- 1 | package nl.jaapcoomans.demo.microframeworks.todo.test; 2 | 3 | import org.testcontainers.containers.GenericContainer; 4 | import org.testcontainers.images.builder.ImageFromDockerfile; 5 | import org.testcontainers.junit.jupiter.Container; 6 | import org.testcontainers.junit.jupiter.Testcontainers; 7 | 8 | import java.nio.file.Paths; 9 | 10 | @Testcontainers 11 | class BladeTodoBackendTest extends BaseTodoBackendTest { 12 | private static final ImageFromDockerfile bladeImage = new ImageFromDockerfile().withFileFromPath(".", Paths.get("../blade")); 13 | 14 | @Container 15 | private static final GenericContainer bladeContainer = new GenericContainer<>(bladeImage).withExposedPorts(8080); 16 | 17 | @Override 18 | GenericContainer getContainer() { 19 | return bladeContainer; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /todo-backend-test/src/test/java/nl/jaapcoomans/demo/microframeworks/todo/test/JoobyTodoBackendTest.java: -------------------------------------------------------------------------------- 1 | package nl.jaapcoomans.demo.microframeworks.todo.test; 2 | 3 | import org.testcontainers.containers.GenericContainer; 4 | import org.testcontainers.images.builder.ImageFromDockerfile; 5 | import org.testcontainers.junit.jupiter.Container; 6 | import org.testcontainers.junit.jupiter.Testcontainers; 7 | 8 | import java.nio.file.Paths; 9 | 10 | @Testcontainers 11 | class JoobyTodoBackendTest extends BaseTodoBackendTest { 12 | private static final ImageFromDockerfile joobyImage = new ImageFromDockerfile().withFileFromPath(".", Paths.get("../jooby")); 13 | 14 | @Container 15 | private static final GenericContainer joobyContainer = new GenericContainer<>(joobyImage).withExposedPorts(8080); 16 | 17 | @Override 18 | GenericContainer getContainer() { 19 | return joobyContainer; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /todo-backend-test/src/test/java/nl/jaapcoomans/demo/microframeworks/todo/test/PippoTodoBackendTest.java: -------------------------------------------------------------------------------- 1 | package nl.jaapcoomans.demo.microframeworks.todo.test; 2 | 3 | import org.testcontainers.containers.GenericContainer; 4 | import org.testcontainers.images.builder.ImageFromDockerfile; 5 | import org.testcontainers.junit.jupiter.Container; 6 | import org.testcontainers.junit.jupiter.Testcontainers; 7 | 8 | import java.nio.file.Paths; 9 | 10 | @Testcontainers 11 | class PippoTodoBackendTest extends BaseTodoBackendTest { 12 | private static final ImageFromDockerfile pippoImage = new ImageFromDockerfile().withFileFromPath(".", Paths.get("../pippo")); 13 | 14 | @Container 15 | private static final GenericContainer pippoContainer = new GenericContainer<>(pippoImage).withExposedPorts(8080); 16 | 17 | @Override 18 | GenericContainer getContainer() { 19 | return pippoContainer; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /todo-backend-test/src/test/java/nl/jaapcoomans/demo/microframeworks/todo/test/SparkTodoBackendTest.java: -------------------------------------------------------------------------------- 1 | package nl.jaapcoomans.demo.microframeworks.todo.test; 2 | 3 | import org.testcontainers.containers.GenericContainer; 4 | import org.testcontainers.images.builder.ImageFromDockerfile; 5 | import org.testcontainers.junit.jupiter.Container; 6 | import org.testcontainers.junit.jupiter.Testcontainers; 7 | 8 | import java.nio.file.Paths; 9 | 10 | @Testcontainers 11 | class SparkTodoBackendTest extends BaseTodoBackendTest { 12 | private static final ImageFromDockerfile sparkImage = new ImageFromDockerfile().withFileFromPath(".", Paths.get("../spark")); 13 | 14 | @Container 15 | private static final GenericContainer sparkContainer = new GenericContainer<>(sparkImage).withExposedPorts(8080); 16 | 17 | @Override 18 | GenericContainer getContainer() { 19 | return sparkContainer; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /todo-backend-test/src/test/java/nl/jaapcoomans/demo/microframeworks/todo/test/ArmeriaTodoBackendTest.java: -------------------------------------------------------------------------------- 1 | package nl.jaapcoomans.demo.microframeworks.todo.test; 2 | 3 | import org.testcontainers.containers.GenericContainer; 4 | import org.testcontainers.images.builder.ImageFromDockerfile; 5 | import org.testcontainers.junit.jupiter.Container; 6 | import org.testcontainers.junit.jupiter.Testcontainers; 7 | 8 | import java.nio.file.Paths; 9 | 10 | @Testcontainers 11 | class ArmeriaTodoBackendTest extends BaseTodoBackendTest { 12 | private static final ImageFromDockerfile armeriaImage = new ImageFromDockerfile().withFileFromPath(".", Paths.get("../armeria")); 13 | 14 | @Container 15 | private static final GenericContainer armeriaContainer = new GenericContainer<>(armeriaImage).withExposedPorts(8080); 16 | 17 | @Override 18 | GenericContainer getContainer() { 19 | return armeriaContainer; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /todo-backend-test/src/test/java/nl/jaapcoomans/demo/microframeworks/todo/test/MinijaxTodoBackendTest.java: -------------------------------------------------------------------------------- 1 | package nl.jaapcoomans.demo.microframeworks.todo.test; 2 | 3 | import org.testcontainers.containers.GenericContainer; 4 | import org.testcontainers.images.builder.ImageFromDockerfile; 5 | import org.testcontainers.junit.jupiter.Container; 6 | import org.testcontainers.junit.jupiter.Testcontainers; 7 | 8 | import java.nio.file.Paths; 9 | 10 | @Testcontainers 11 | class MinijaxTodoBackendTest extends BaseTodoBackendTest{ 12 | private static final ImageFromDockerfile minijaxImage = new ImageFromDockerfile().withFileFromPath(".", Paths.get("../minijax")); 13 | 14 | @Container 15 | private static final GenericContainer minijaxContainer = new GenericContainer<>(minijaxImage).withExposedPorts(8080); 16 | 17 | @Override 18 | GenericContainer getContainer() { 19 | return minijaxContainer; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /todo-backend-test/src/test/java/nl/jaapcoomans/demo/microframeworks/todo/test/RatpackTodoBackendTest.java: -------------------------------------------------------------------------------- 1 | package nl.jaapcoomans.demo.microframeworks.todo.test; 2 | 3 | import org.testcontainers.containers.GenericContainer; 4 | import org.testcontainers.images.builder.ImageFromDockerfile; 5 | import org.testcontainers.junit.jupiter.Container; 6 | import org.testcontainers.junit.jupiter.Testcontainers; 7 | 8 | import java.nio.file.Paths; 9 | 10 | @Testcontainers 11 | class RatpackTodoBackendTest extends BaseTodoBackendTest { 12 | private static final ImageFromDockerfile ratpackImage = new ImageFromDockerfile().withFileFromPath(".", Paths.get("../ratpack")); 13 | 14 | @Container 15 | private static final GenericContainer ratpackContainer = new GenericContainer<>(ratpackImage).withExposedPorts(8080); 16 | 17 | @Override 18 | GenericContainer getContainer() { 19 | return ratpackContainer; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /todo-backend/src/main/java/nl/jaapcoomans/demo/microframeworks/todo/domain/PartialTodo.java: -------------------------------------------------------------------------------- 1 | package nl.jaapcoomans.demo.microframeworks.todo.domain; 2 | 3 | import java.util.Optional; 4 | 5 | public class PartialTodo { 6 | private String title; 7 | private Boolean completed; 8 | private Integer order; 9 | 10 | public void setTitle(String title) { 11 | this.title = title; 12 | } 13 | 14 | public void setCompleted(Boolean completed) { 15 | this.completed = completed; 16 | } 17 | 18 | public void setOrder(Integer order) { 19 | this.order = order; 20 | } 21 | 22 | Optional title() { 23 | return Optional.ofNullable(this.title); 24 | } 25 | 26 | Optional completed() { 27 | return Optional.ofNullable(this.completed); 28 | } 29 | 30 | Optional order() { 31 | return Optional.ofNullable(this.order); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /todo-backend-test/src/test/java/nl/jaapcoomans/demo/microframeworks/todo/test/HelidonMpTodoBackendTest.java: -------------------------------------------------------------------------------- 1 | package nl.jaapcoomans.demo.microframeworks.todo.test; 2 | 3 | import org.testcontainers.containers.GenericContainer; 4 | import org.testcontainers.images.builder.ImageFromDockerfile; 5 | import org.testcontainers.junit.jupiter.Container; 6 | import org.testcontainers.junit.jupiter.Testcontainers; 7 | 8 | import java.nio.file.Paths; 9 | 10 | @Testcontainers 11 | class HelidonMpTodoBackendTest extends BaseTodoBackendTest { 12 | private static final ImageFromDockerfile helidonImage = new ImageFromDockerfile().withFileFromPath(".", Paths.get("../helidon-mp")); 13 | 14 | @Container 15 | private static final GenericContainer helidonContainer = new GenericContainer<>(helidonImage).withExposedPorts(8080); 16 | 17 | @Override 18 | GenericContainer getContainer() { 19 | return helidonContainer; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /todo-backend-test/src/test/java/nl/jaapcoomans/demo/microframeworks/todo/test/HelidonSeTodoBackendTest.java: -------------------------------------------------------------------------------- 1 | package nl.jaapcoomans.demo.microframeworks.todo.test; 2 | 3 | import org.testcontainers.containers.GenericContainer; 4 | import org.testcontainers.images.builder.ImageFromDockerfile; 5 | import org.testcontainers.junit.jupiter.Container; 6 | import org.testcontainers.junit.jupiter.Testcontainers; 7 | 8 | import java.nio.file.Paths; 9 | 10 | @Testcontainers 11 | class HelidonSeTodoBackendTest extends BaseTodoBackendTest { 12 | private static final ImageFromDockerfile helidonImage = new ImageFromDockerfile().withFileFromPath(".", Paths.get("../helidon-se")); 13 | 14 | @Container 15 | private static final GenericContainer helidonContainer = new GenericContainer<>(helidonImage).withExposedPorts(8080); 16 | 17 | @Override 18 | GenericContainer getContainer() { 19 | return helidonContainer; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /todo-backend-test/src/test/java/nl/jaapcoomans/demo/microframeworks/todo/test/KumuluzEeTodoBackendTest.java: -------------------------------------------------------------------------------- 1 | package nl.jaapcoomans.demo.microframeworks.todo.test; 2 | 3 | import org.testcontainers.containers.GenericContainer; 4 | import org.testcontainers.images.builder.ImageFromDockerfile; 5 | import org.testcontainers.junit.jupiter.Container; 6 | import org.testcontainers.junit.jupiter.Testcontainers; 7 | 8 | import java.nio.file.Paths; 9 | 10 | @Testcontainers 11 | class KumuluzEeTodoBackendTest extends BaseTodoBackendTest { 12 | private static final ImageFromDockerfile kumuluzEeImage = new ImageFromDockerfile().withFileFromPath(".", Paths.get("../kumuluzee")); 13 | 14 | @Container 15 | private static final GenericContainer kumuluzEeContainer = new GenericContainer<>(kumuluzEeImage).withExposedPorts(8080); 16 | 17 | @Override 18 | GenericContainer getContainer() { 19 | return kumuluzEeContainer; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /todo-backend-test/src/test/java/nl/jaapcoomans/demo/microframeworks/todo/test/QuarkusTodoBackendTest.java: -------------------------------------------------------------------------------- 1 | package nl.jaapcoomans.demo.microframeworks.todo.test; 2 | 3 | import org.testcontainers.containers.GenericContainer; 4 | import org.testcontainers.images.builder.ImageFromDockerfile; 5 | import org.testcontainers.junit.jupiter.Container; 6 | import org.testcontainers.junit.jupiter.Testcontainers; 7 | 8 | import java.nio.file.Paths; 9 | 10 | @Testcontainers 11 | public class QuarkusTodoBackendTest extends BaseTodoBackendTest { 12 | private static final ImageFromDockerfile quarkusImage = new ImageFromDockerfile() 13 | .withFileFromPath(".", Paths.get("../quarkus")); 14 | 15 | @Container 16 | private static final GenericContainer quarkusContainer = new GenericContainer<>(quarkusImage).withExposedPorts(8080); 17 | 18 | @Override 19 | GenericContainer getContainer() { 20 | return quarkusContainer; 21 | } 22 | } -------------------------------------------------------------------------------- /quarkus/src/main/java/nl/jaapcoomans/microframeworks/quarkus/QuarkusApplication.java: -------------------------------------------------------------------------------- 1 | package nl.jaapcoomans.microframeworks.quarkus; 2 | 3 | import io.quarkus.runtime.Quarkus; 4 | import io.quarkus.runtime.annotations.QuarkusMain; 5 | import nl.jaapcoomans.demo.microframeworks.todo.domain.TodoRepository; 6 | import nl.jaapcoomans.demo.microframeworks.todo.domain.TodoService; 7 | import nl.jaapcoomans.demo.microframeworks.todo.peristsence.InMemoryTodoRepository; 8 | 9 | import javax.enterprise.inject.Produces; 10 | 11 | @QuarkusMain 12 | public class QuarkusApplication { 13 | public static void main(String... args) { 14 | Quarkus.run(args); 15 | } 16 | 17 | @Produces 18 | public TodoService todoService(TodoRepository todoRepository) { 19 | return new TodoService(todoRepository); 20 | } 21 | 22 | @Produces 23 | public TodoRepository todoRepository() { 24 | return new InMemoryTodoRepository(); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /todo-backend-test/src/test/java/nl/jaapcoomans/demo/microframeworks/todo/test/DropWizardTodoBackendTest.java: -------------------------------------------------------------------------------- 1 | package nl.jaapcoomans.demo.microframeworks.todo.test; 2 | 3 | import org.testcontainers.containers.GenericContainer; 4 | import org.testcontainers.images.builder.ImageFromDockerfile; 5 | import org.testcontainers.junit.jupiter.Container; 6 | import org.testcontainers.junit.jupiter.Testcontainers; 7 | 8 | import java.nio.file.Paths; 9 | 10 | @Testcontainers 11 | class DropWizardTodoBackendTest extends BaseTodoBackendTest { 12 | private static final ImageFromDockerfile dropWizardImage = new ImageFromDockerfile().withFileFromPath(".", Paths.get("../dropwizard")); 13 | 14 | @Container 15 | private static final GenericContainer dropWizardContainer = new GenericContainer<>(dropWizardImage).withExposedPorts(8080); 16 | 17 | @Override 18 | GenericContainer getContainer() { 19 | return dropWizardContainer; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /todo-backend-test/src/test/java/nl/jaapcoomans/demo/microframeworks/todo/test/MicronautTodoBackendTest.java: -------------------------------------------------------------------------------- 1 | package nl.jaapcoomans.demo.microframeworks.todo.test; 2 | 3 | import org.testcontainers.containers.GenericContainer; 4 | import org.testcontainers.images.builder.ImageFromDockerfile; 5 | import org.testcontainers.junit.jupiter.Container; 6 | import org.testcontainers.junit.jupiter.Testcontainers; 7 | 8 | import java.nio.file.Paths; 9 | 10 | @Testcontainers 11 | public class MicronautTodoBackendTest extends BaseTodoBackendTest { 12 | private static final ImageFromDockerfile micronautImage = new ImageFromDockerfile().withFileFromPath(".", Paths.get("../micronaut")); 13 | 14 | @Container 15 | private static final GenericContainer micronautContainer = new GenericContainer<>(micronautImage).withExposedPorts(8080); 16 | 17 | @Override 18 | GenericContainer getContainer() { 19 | return micronautContainer; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /dropwizard/src/main/java/nl/jaapcoomans/microframeworks/dropwizard/HelloWorldController.java: -------------------------------------------------------------------------------- 1 | package nl.jaapcoomans.microframeworks.dropwizard; 2 | 3 | import javax.ws.rs.GET; 4 | import javax.ws.rs.Path; 5 | import javax.ws.rs.PathParam; 6 | import javax.ws.rs.Produces; 7 | import javax.ws.rs.core.MediaType; 8 | 9 | @Path("/hello") 10 | @Produces(MediaType.TEXT_PLAIN) 11 | public class HelloWorldController { 12 | @GET 13 | public String helloWorld() { 14 | return "Hello World!"; 15 | } 16 | 17 | @GET 18 | @Path("/{name}") 19 | public String hello(@PathParam("name") String name) { 20 | return String.format("Hello, %s!", capitalize(name)); 21 | } 22 | 23 | private static String capitalize(String name) { 24 | if (name.length() < 2) { 25 | return name.toUpperCase(); 26 | } else { 27 | return name.substring(0, 1).toUpperCase() + name.substring(1).toLowerCase(); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /minijax/src/main/java/nl/jaapcoomans/microframeworks/minijax/HelloWorldController.java: -------------------------------------------------------------------------------- 1 | package nl.jaapcoomans.microframeworks.minijax; 2 | 3 | import jakarta.ws.rs.GET; 4 | import jakarta.ws.rs.Path; 5 | import jakarta.ws.rs.PathParam; 6 | import jakarta.ws.rs.Produces; 7 | import jakarta.ws.rs.core.MediaType; 8 | 9 | public class HelloWorldController { 10 | @GET 11 | @Path("/hello") 12 | public String helloworld() { 13 | return "Hello World!"; 14 | } 15 | 16 | @GET 17 | @Path("/hello/{name}") 18 | @Produces(MediaType.TEXT_PLAIN) 19 | public String hello(@PathParam("name") String name) { 20 | return String.format("Hello, %s!", capitalize(name)); 21 | } 22 | 23 | private static String capitalize(String name) { 24 | if (name.length() < 2) { 25 | return name.toUpperCase(); 26 | } else { 27 | return name.substring(0, 1).toUpperCase() + name.substring(1).toLowerCase(); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /quarkus/src/main/java/nl/jaapcoomans/microframeworks/quarkus/HelloWorldController.java: -------------------------------------------------------------------------------- 1 | package nl.jaapcoomans.microframeworks.quarkus; 2 | 3 | import javax.ws.rs.GET; 4 | import javax.ws.rs.Path; 5 | import javax.ws.rs.PathParam; 6 | import javax.ws.rs.Produces; 7 | import javax.ws.rs.core.MediaType; 8 | 9 | @Path("/hello") 10 | public class HelloWorldController { 11 | @GET 12 | @Produces(MediaType.TEXT_PLAIN) 13 | public String helloWorld() { 14 | return "Hello World!"; 15 | } 16 | 17 | @GET 18 | @Path("/{name}") 19 | @Produces(MediaType.TEXT_PLAIN) 20 | public String hello(@PathParam("name") String name) { 21 | return String.format("Hello, %s!", capitalize(name)); 22 | } 23 | 24 | private static String capitalize(String name) { 25 | if (name.length() < 2) { 26 | return name.toUpperCase(); 27 | } else { 28 | return name.substring(0, 1).toUpperCase() + name.substring(1).toLowerCase(); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /kumuluzee/src/main/java/nl/jaapcoomans/demo/microframeworks/kumuluzee/HelloWorldController.java: -------------------------------------------------------------------------------- 1 | package nl.jaapcoomans.demo.microframeworks.kumuluzee; 2 | 3 | import javax.ws.rs.GET; 4 | import javax.ws.rs.Path; 5 | import javax.ws.rs.PathParam; 6 | import javax.ws.rs.Produces; 7 | import javax.ws.rs.core.MediaType; 8 | 9 | @Path("/hello") 10 | public class HelloWorldController { 11 | 12 | @GET 13 | @Produces(MediaType.TEXT_PLAIN) 14 | public String helloWorld() { 15 | return "Hello World!"; 16 | } 17 | 18 | @GET 19 | @Path("/{name}") 20 | @Produces(MediaType.TEXT_PLAIN) 21 | public String helloWorld(@PathParam("name") String name) { 22 | return String.format("Hello, %s!", capitalize(name)); 23 | } 24 | 25 | private String capitalize(String name) { 26 | if (name.length() < 2) { 27 | return name.toUpperCase(); 28 | } else { 29 | return name.substring(0, 1).toUpperCase() + name.substring(1).toLowerCase(); 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /helidon-mp/src/main/java/nl/jaapcoomans/microframeworks/helidon/mp/HelloWorldController.java: -------------------------------------------------------------------------------- 1 | package nl.jaapcoomans.microframeworks.helidon.mp; 2 | 3 | import javax.enterprise.context.RequestScoped; 4 | import javax.ws.rs.GET; 5 | import javax.ws.rs.Path; 6 | import javax.ws.rs.PathParam; 7 | import javax.ws.rs.Produces; 8 | import javax.ws.rs.core.MediaType; 9 | 10 | @RequestScoped 11 | @Path("/hello") 12 | public class HelloWorldController { 13 | @GET 14 | @Produces(MediaType.TEXT_PLAIN) 15 | public String helloWorld() { 16 | return "Hello World!"; 17 | } 18 | 19 | @GET 20 | @Path("/{name}") 21 | @Produces(MediaType.TEXT_PLAIN) 22 | public String hello(@PathParam("name") String name) { 23 | return String.format("Hello, %s!", capitalize(name)); 24 | } 25 | 26 | private static String capitalize(String name) { 27 | if (name.length() < 2) { 28 | return name.toUpperCase(); 29 | } else { 30 | return name.substring(0, 1).toUpperCase() + name.substring(1).toLowerCase(); 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /helidon-mp/src/main/java/nl/jaapcoomans/microframeworks/helidon/mp/CorsFilter.java: -------------------------------------------------------------------------------- 1 | package nl.jaapcoomans.microframeworks.helidon.mp; 2 | 3 | import javax.ws.rs.HttpMethod; 4 | import javax.ws.rs.container.ContainerRequestContext; 5 | import javax.ws.rs.container.ContainerResponseContext; 6 | import javax.ws.rs.container.ContainerResponseFilter; 7 | import javax.ws.rs.ext.Provider; 8 | 9 | @Provider 10 | public class CorsFilter implements ContainerResponseFilter { 11 | 12 | @Override 13 | public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext) { 14 | if (!HttpMethod.OPTIONS.equals(requestContext.getMethod())) { 15 | responseContext.getHeaders().add("Access-Control-Allow-Origin", "*"); 16 | responseContext.getHeaders().add("Access-Control-Allow-Credentials", "true"); 17 | responseContext.getHeaders().add("Access-Control-Allow-Headers", "content-type"); 18 | responseContext.getHeaders().add("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS, HEAD"); 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /micronaut/src/main/java/nl/jaapcoomans/demo/microframeworks/micronaut/HelloWorldController.java: -------------------------------------------------------------------------------- 1 | package nl.jaapcoomans.demo.microframeworks.micronaut; 2 | 3 | import io.micronaut.http.MediaType; 4 | import io.micronaut.http.annotation.Controller; 5 | import io.micronaut.http.annotation.Get; 6 | import io.micronaut.http.annotation.PathVariable; 7 | import io.reactivex.Single; 8 | 9 | @Controller("/hello") 10 | public class HelloWorldController { 11 | @Get(produces = MediaType.TEXT_PLAIN) 12 | public Single helloWorld() { 13 | return Single.just("Hello World!"); 14 | } 15 | 16 | @Get(uri = "/{name}", produces = MediaType.TEXT_PLAIN) 17 | public Single helloWorld(@PathVariable String name) { 18 | return Single.just(String.format("Hello, %s!", capitalize(name))); 19 | } 20 | 21 | private String capitalize(String name) { 22 | if (name.length() < 2) { 23 | return name.toUpperCase(); 24 | } else { 25 | return name.substring(0, 1).toUpperCase() + name.substring(1).toLowerCase(); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /spark/src/main/java/nl/jaapcoomans/microframeworks/spark/JacksonResponseTransformer.java: -------------------------------------------------------------------------------- 1 | package nl.jaapcoomans.microframeworks.spark; 2 | 3 | import com.fasterxml.jackson.core.JsonProcessingException; 4 | import com.fasterxml.jackson.databind.ObjectMapper; 5 | import spark.ResponseTransformer; 6 | 7 | import java.util.Optional; 8 | 9 | public class JacksonResponseTransformer implements ResponseTransformer { 10 | private ObjectMapper objectMapper = new ObjectMapper(); 11 | 12 | @Override 13 | public String render(Object model) { 14 | if (model instanceof Optional) { 15 | Optional optional = (Optional) model; 16 | return optional 17 | .map(this::serialize) 18 | .orElse(""); 19 | } 20 | 21 | return this.serialize(model); 22 | } 23 | 24 | private String serialize(Object model) { 25 | try { 26 | return objectMapper.writeValueAsString(model); 27 | } catch (JsonProcessingException e) { 28 | throw new RuntimeException("Failed to serialize to JSON.", e); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /helidon-mp/src/main/java/nl/jaapcoomans/microframeworks/helidon/mp/TodoApplication.java: -------------------------------------------------------------------------------- 1 | package nl.jaapcoomans.microframeworks.helidon.mp; 2 | 3 | import nl.jaapcoomans.demo.microframeworks.todo.domain.TodoRepository; 4 | import nl.jaapcoomans.demo.microframeworks.todo.domain.TodoService; 5 | import nl.jaapcoomans.demo.microframeworks.todo.peristsence.InMemoryTodoRepository; 6 | 7 | import javax.enterprise.context.ApplicationScoped; 8 | import javax.enterprise.inject.Produces; 9 | import javax.ws.rs.ApplicationPath; 10 | import javax.ws.rs.core.Application; 11 | import java.util.Set; 12 | 13 | @ApplicationScoped 14 | @ApplicationPath("/todos") 15 | public class TodoApplication extends Application { 16 | @Produces 17 | public TodoRepository todoRepository() { 18 | return new InMemoryTodoRepository(); 19 | } 20 | 21 | @Produces 22 | public TodoService todoService(TodoRepository todoRepository) { 23 | return new TodoService(todoRepository); 24 | } 25 | 26 | @Override 27 | public Set> getClasses() { 28 | return Set.of(TodoController.class, CorsFilter.class); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Jaap Coomans 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 | -------------------------------------------------------------------------------- /helidon-mp/src/main/resources/META-INF/beans.xml: -------------------------------------------------------------------------------- 1 | 2 | 19 | 20 | 26 | 27 | -------------------------------------------------------------------------------- /minijax/src/main/java/nl/jaapcoomans/microframeworks/minijax/MinijaxApplication.java: -------------------------------------------------------------------------------- 1 | package nl.jaapcoomans.microframeworks.minijax; 2 | 3 | import nl.jaapcoomans.demo.microframeworks.todo.domain.TodoService; 4 | import nl.jaapcoomans.demo.microframeworks.todo.peristsence.InMemoryTodoRepository; 5 | import org.minijax.Minijax; 6 | import org.minijax.json.JsonFeature; 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | 10 | public class MinijaxApplication { 11 | private static final Logger LOGGER = LoggerFactory.getLogger(MinijaxApplication.class); 12 | 13 | public static void main(String[] args) { 14 | long startTime = System.currentTimeMillis(); 15 | 16 | var todoRepository = new InMemoryTodoRepository(); 17 | 18 | new Minijax() 19 | .port(8080) 20 | .register(JsonFeature.class) 21 | .register(HelloWorldController.class) 22 | .register(new TodoService(todoRepository)) 23 | .register(TodoController.class) 24 | .allowCors("/") 25 | .start(); 26 | 27 | LOGGER.info("Started in {} ms", System.currentTimeMillis() - startTime); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /helidon-se/src/main/java/nl/jaapcoomans/demo/microframeworks/helidon/se/HelloWorldRestController.java: -------------------------------------------------------------------------------- 1 | package nl.jaapcoomans.demo.microframeworks.helidon.se; 2 | 3 | import io.helidon.webserver.Routing; 4 | import io.helidon.webserver.ServerRequest; 5 | import io.helidon.webserver.ServerResponse; 6 | import io.helidon.webserver.Service; 7 | 8 | public class HelloWorldRestController implements Service { 9 | @Override 10 | public void update(Routing.Rules rules) { 11 | rules 12 | .get("/", this::helloWorld) 13 | .get("/{name}", this::hello); 14 | } 15 | 16 | private void helloWorld(ServerRequest request, ServerResponse response) { 17 | response.send("Hello World!"); 18 | } 19 | 20 | private void hello(ServerRequest request, ServerResponse response) { 21 | response.send(String.format("Hello, %s!", capitalize(request.path().param("name")))); 22 | } 23 | 24 | private static String capitalize(String name) { 25 | if (name.length() < 2) { 26 | return name.toUpperCase(); 27 | } else { 28 | return name.substring(0, 1).toUpperCase() + name.substring(1).toLowerCase(); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /todo-backend/src/main/java/nl/jaapcoomans/demo/microframeworks/todo/peristsence/InMemoryTodoRepository.java: -------------------------------------------------------------------------------- 1 | package nl.jaapcoomans.demo.microframeworks.todo.peristsence; 2 | 3 | import nl.jaapcoomans.demo.microframeworks.todo.domain.Todo; 4 | import nl.jaapcoomans.demo.microframeworks.todo.domain.TodoRepository; 5 | 6 | import java.util.ArrayList; 7 | import java.util.HashMap; 8 | import java.util.List; 9 | import java.util.Map; 10 | import java.util.Optional; 11 | import java.util.UUID; 12 | 13 | public class InMemoryTodoRepository implements TodoRepository { 14 | private static final Map TODOS = new HashMap<>(); 15 | 16 | @Override 17 | public List findAll() { 18 | return new ArrayList<>(TODOS.values()); 19 | } 20 | 21 | @Override 22 | public Optional findById(UUID id) { 23 | return Optional.ofNullable(TODOS.get(id)); 24 | } 25 | 26 | @Override 27 | public void delete(UUID id) { 28 | TODOS.remove(id); 29 | } 30 | 31 | @Override 32 | public Todo persist(Todo todo) { 33 | TODOS.put(todo.getId(), todo); 34 | return todo; 35 | } 36 | 37 | @Override 38 | public void deleteAll() { 39 | TODOS.clear(); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /todo-backend/src/main/java/nl/jaapcoomans/demo/microframeworks/todo/domain/TodoService.java: -------------------------------------------------------------------------------- 1 | package nl.jaapcoomans.demo.microframeworks.todo.domain; 2 | 3 | import java.util.List; 4 | import java.util.Optional; 5 | import java.util.UUID; 6 | 7 | public class TodoService { 8 | private TodoRepository repository; 9 | 10 | public TodoService(TodoRepository repository) { 11 | this.repository = repository; 12 | } 13 | 14 | public List findAll() { 15 | return this.repository.findAll(); 16 | } 17 | 18 | public Optional findById(UUID id) { 19 | return repository.findById(id); 20 | } 21 | 22 | public Todo createNewTodo(String title, int order) { 23 | Todo todo = Todo.create(title, order); 24 | 25 | return this.repository.persist(todo); 26 | } 27 | 28 | public void deleteAll() { 29 | this.repository.deleteAll(); 30 | } 31 | 32 | public void delete(UUID id) { 33 | this.repository.delete(id); 34 | } 35 | 36 | public Optional updateTodo(UUID id, PartialTodo command) { 37 | return this.repository.findById(id) 38 | .map(todo -> todo.update(command)) 39 | .map(repository::persist); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /quarkus/Dockerfile: -------------------------------------------------------------------------------- 1 | #FROM registry.access.redhat.com/ubi8/ubi-minimal:8.3 2 | FROM adoptopenjdk/openjdk16:jre-16.0.1_9-alpine 3 | 4 | ARG RUN_JAVA_VERSION=1.3.8 5 | ENV LANG='en_US.UTF-8' LANGUAGE='en_US:en' 6 | # Install the run-java script 7 | ADD https://repo1.maven.org/maven2/io/fabric8/run-java-sh/${RUN_JAVA_VERSION}/run-java-sh-${RUN_JAVA_VERSION}-sh.sh /deployments/run-java.sh 8 | 9 | # Also set up permissions for user `1001` 10 | RUN chown 1001 /deployments \ 11 | && chmod "g+rwX" /deployments \ 12 | && chown 1001:root /deployments \ 13 | && chown 1001 /deployments/run-java.sh \ 14 | && chmod 540 /deployments/run-java.sh 15 | 16 | # Configure the JAVA_OPTIONS, you can add -XshowSettings:vm to also display the heap size. 17 | ENV JAVA_OPTIONS="-Dquarkus.http.host=0.0.0.0 -Djava.util.logging.manager=org.jboss.logmanager.LogManager" 18 | # We make four distinct layers so if there are application changes the library layers can be re-used 19 | COPY --chown=1001 target/quarkus-app/lib/ /deployments/lib/ 20 | COPY --chown=1001 target/quarkus-app/*.jar /deployments/ 21 | COPY --chown=1001 target/quarkus-app/app/ /deployments/app/ 22 | COPY --chown=1001 target/quarkus-app/quarkus/ /deployments/quarkus/ 23 | 24 | EXPOSE 8080 25 | USER 1001 26 | 27 | ENTRYPOINT [ "/deployments/run-java.sh" ] 28 | -------------------------------------------------------------------------------- /blade/src/main/java/nl/jaapcoomans/microframeworks/blade/JacksonJsonSupport.java: -------------------------------------------------------------------------------- 1 | package nl.jaapcoomans.microframeworks.blade; 2 | 3 | import com.blade.kit.ReflectKit; 4 | import com.blade.kit.json.JsonSupport; 5 | import com.fasterxml.jackson.core.JsonProcessingException; 6 | import com.fasterxml.jackson.databind.ObjectMapper; 7 | 8 | import java.io.IOException; 9 | import java.lang.reflect.Type; 10 | 11 | public class JacksonJsonSupport implements JsonSupport { 12 | private ObjectMapper objectMapper = new ObjectMapper(); 13 | 14 | @Override 15 | public String toString(Object data) { 16 | try { 17 | return this.objectMapper.writeValueAsString(data); 18 | } catch (JsonProcessingException e) { 19 | throw new RuntimeException("Error while serializing to JSON: " + e.getMessage(), e); 20 | } 21 | } 22 | 23 | @Override 24 | public T formJson(String json, Type type) { 25 | try { 26 | @SuppressWarnings("unchecked") 27 | Class clazz = (Class) ReflectKit.typeToClass(type); 28 | return this.objectMapper.readValue(json, clazz); 29 | } catch (IOException e) { 30 | throw new RuntimeException("Error while parsing JSON: " + e.getMessage(), e); 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /docker-compose.yaml: -------------------------------------------------------------------------------- 1 | version: "3" 2 | 3 | services: 4 | armeria: 5 | build: armeria 6 | ports: 7 | - "7001:8080" 8 | blade: 9 | build: blade 10 | ports: 11 | - "7002:8080" 12 | dropwizard: 13 | build: dropwizard 14 | ports: 15 | - "7003:8080" 16 | helidon-mp: 17 | build: helidon-mp 18 | ports: 19 | - "7004:8080" 20 | helidon-se: 21 | build: helidon-se 22 | ports: 23 | - "7005:8080" 24 | http4k: 25 | build: http4k 26 | ports: 27 | - "7006:8080" 28 | javalin: 29 | build: javalin 30 | ports: 31 | - "7007:7000" 32 | jooby: 33 | build: jooby 34 | ports: 35 | - "7008:8080" 36 | ktor: 37 | build: ktor 38 | ports: 39 | - "7009:8080" 40 | kumuluzee: 41 | build: kumuluzee 42 | ports: 43 | - "7010:8080" 44 | micronaut: 45 | build: micronaut 46 | ports: 47 | - "7011:8080" 48 | minijax: 49 | build: minijax 50 | ports: 51 | - "7012:8080" 52 | pippo: 53 | build: pippo 54 | ports: 55 | - "7013:8080" 56 | ratpack: 57 | build: ratpack 58 | ports: 59 | - "7014:8080" 60 | spark: 61 | build: spark 62 | ports: 63 | - "7015:8080" 64 | quarkus: 65 | build: quarkus 66 | ports: 67 | - "7016:8080" -------------------------------------------------------------------------------- /todo-backend/src/main/java/nl/jaapcoomans/demo/microframeworks/todo/api/TodoDTO.java: -------------------------------------------------------------------------------- 1 | package nl.jaapcoomans.demo.microframeworks.todo.api; 2 | 3 | import nl.jaapcoomans.demo.microframeworks.todo.domain.Todo; 4 | 5 | import java.util.UUID; 6 | 7 | public class TodoDTO { 8 | private UUID id; 9 | private String title; 10 | private String url; 11 | private boolean completed; 12 | private int order; 13 | 14 | private TodoDTO(UUID id, String title, String url, boolean completed, int order) { 15 | this.id = id; 16 | this.title = title; 17 | this.url = url; 18 | this.completed = completed; 19 | this.order = order; 20 | } 21 | 22 | public static TodoDTO from(Todo todo, String url) { 23 | return new TodoDTO( 24 | todo.getId(), 25 | todo.getTitle(), 26 | url, 27 | todo.isCompleted(), 28 | todo.getOrder() 29 | ); 30 | } 31 | 32 | public UUID getId() { 33 | return id; 34 | } 35 | 36 | public String getTitle() { 37 | return title; 38 | } 39 | 40 | public String getUrl() { 41 | return url; 42 | } 43 | 44 | public boolean isCompleted() { 45 | return completed; 46 | } 47 | 48 | public int getOrder() { 49 | return order; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /todo-backend/src/main/java/nl/jaapcoomans/demo/microframeworks/todo/domain/Todo.java: -------------------------------------------------------------------------------- 1 | package nl.jaapcoomans.demo.microframeworks.todo.domain; 2 | 3 | import java.util.UUID; 4 | 5 | public class Todo { 6 | private UUID id; 7 | private String title; 8 | private boolean completed; 9 | private int order; 10 | 11 | private Todo(UUID id, String title, boolean completed, int order) { 12 | this.id = id; 13 | this.title = title; 14 | this.completed = completed; 15 | this.order = order; 16 | } 17 | 18 | static Todo create(String title, int order) { 19 | return new Todo(UUID.randomUUID(), title, false, order); 20 | } 21 | 22 | Todo changeTitle(String title) { 23 | return new Todo(this.id, title, this.completed, this.order); 24 | } 25 | 26 | Todo complete() { 27 | return new Todo(this.id, this.title, true, this.order); 28 | } 29 | 30 | Todo restore() { 31 | return new Todo(this.id, this.title, false, this.order); 32 | } 33 | 34 | Todo reorder(int order) { 35 | return new Todo(this.id, this.title, this.completed, order); 36 | } 37 | 38 | public UUID getId() { 39 | return id; 40 | } 41 | 42 | public String getTitle() { 43 | return title; 44 | } 45 | 46 | public boolean isCompleted() { 47 | return completed; 48 | } 49 | 50 | public int getOrder() { 51 | return order; 52 | } 53 | 54 | Todo update(PartialTodo todo) { 55 | return new Todo( 56 | this.id, 57 | todo.title().orElse(this.title), 58 | todo.completed().orElse(this.completed), 59 | todo.order().orElse(this.order) 60 | ); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /ktor/src/main/kotlin/nl/jaapcoomans/demo/microframeworks/ktor/KtorApplication.kt: -------------------------------------------------------------------------------- 1 | package nl.jaapcoomans.demo.microframeworks.ktor 2 | 3 | import io.ktor.application.Application 4 | import io.ktor.application.ApplicationStarted 5 | import io.ktor.application.call 6 | import io.ktor.application.install 7 | import io.ktor.application.log 8 | import io.ktor.features.CORS 9 | import io.ktor.features.ContentNegotiation 10 | import io.ktor.http.ContentType 11 | import io.ktor.http.HttpMethod 12 | import io.ktor.jackson.jackson 13 | import io.ktor.response.respondText 14 | import io.ktor.routing.get 15 | import io.ktor.routing.routing 16 | import java.util.Locale 17 | 18 | fun main(args: Array): Unit = io.ktor.server.netty.EngineMain.main(args) 19 | 20 | fun Application.module() { 21 | val startTime = System.currentTimeMillis() 22 | 23 | install(ContentNegotiation) { 24 | jackson {} 25 | } 26 | install(CORS) { 27 | method(HttpMethod.Get) 28 | method(HttpMethod.Post) 29 | method(HttpMethod.Delete) 30 | method(HttpMethod.Patch) 31 | method(HttpMethod.Options) 32 | anyHost() 33 | allowNonSimpleContentTypes = true 34 | allowSameOrigin = false 35 | } 36 | 37 | routing { 38 | get("/hello") { 39 | call.respondText("Hello World!", ContentType.Text.Plain) 40 | } 41 | get("/hello/{name}") { 42 | call.respondText("Hello, ${call.parameters["name"]?.capitalize()}!", ContentType.Text.Plain) 43 | } 44 | 45 | todoRoutes() 46 | } 47 | 48 | environment.monitor.subscribe(ApplicationStarted) { 49 | val bootTime = System.currentTimeMillis() - startTime 50 | log.info("Started in ${bootTime} ms."); 51 | } 52 | } 53 | 54 | fun String.capitalize(): String { 55 | return this.replaceFirstChar { if (it.isLowerCase()) it.titlecase(Locale.getDefault()) else it.toString() } 56 | } -------------------------------------------------------------------------------- /minijax/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | minijax 7 | jar 8 | 9 | 10 | nl.jaapcoomans.demo.microframeworks 11 | parent 12 | 0.0.1-SNAPSHOT 13 | 14 | 15 | 16 | 0.5.10 17 | 18 | 19 | 20 | 21 | org.minijax 22 | minijax-undertow 23 | ${minijax.version} 24 | 25 | 26 | org.minijax 27 | minijax-json 28 | ${minijax.version} 29 | 30 | 31 | 32 | 33 | nl.jaapcoomans.demo.microframeworks 34 | todo-backend 35 | 0.0.1-SNAPSHOT 36 | 37 | 38 | 39 | 40 | org.slf4j 41 | slf4j-simple 42 | 43 | 44 | 45 | 46 | 47 | 48 | org.apache.maven.plugins 49 | maven-jar-plugin 50 | 51 | 52 | org.apache.maven.plugins 53 | maven-dependency-plugin 54 | 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /spark/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | spark 7 | jar 8 | 9 | 10 | nl.jaapcoomans.demo.microframeworks 11 | parent 12 | 0.0.1-SNAPSHOT 13 | 14 | 15 | 16 | 2.9.3 17 | 2.13.4.2 18 | 19 | 20 | 21 | 22 | com.sparkjava 23 | spark-core 24 | ${spark.version} 25 | 26 | 27 | 28 | 29 | nl.jaapcoomans.demo.microframeworks 30 | todo-backend 31 | 0.0.1-SNAPSHOT 32 | 33 | 34 | 35 | 36 | org.slf4j 37 | slf4j-simple 38 | 39 | 40 | 41 | 42 | com.fasterxml.jackson.core 43 | jackson-databind 44 | ${jackson.version} 45 | 46 | 47 | 48 | 49 | 50 | 51 | org.apache.maven.plugins 52 | maven-jar-plugin 53 | 54 | 55 | org.apache.maven.plugins 56 | maven-dependency-plugin 57 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /dropwizard/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | dropwizard 7 | jar 8 | 9 | 10 | nl.jaapcoomans.demo.microframeworks 11 | parent 12 | 0.0.1-SNAPSHOT 13 | 14 | 15 | 16 | 2.0.21 17 | 2.12.3 18 | 19 | 20 | 21 | 22 | 23 | io.dropwizard 24 | dropwizard-dependencies 25 | ${dropwizard.version} 26 | pom 27 | import 28 | 29 | 30 | 31 | 32 | 33 | 34 | io.dropwizard 35 | dropwizard-core 36 | ${dropwizard.version} 37 | 38 | 39 | 40 | 41 | nl.jaapcoomans.demo.microframeworks 42 | todo-backend 43 | 0.0.1-SNAPSHOT 44 | 45 | 46 | 47 | 48 | 49 | 50 | org.apache.maven.plugins 51 | maven-jar-plugin 52 | 53 | 54 | org.apache.maven.plugins 55 | maven-dependency-plugin 56 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /javalin/src/main/java/nl/jaapcoomans/microframeworks/javalin/JavalinApplication.java: -------------------------------------------------------------------------------- 1 | package nl.jaapcoomans.microframeworks.javalin; 2 | 3 | import io.javalin.Javalin; 4 | import io.javalin.apibuilder.EndpointGroup; 5 | import io.javalin.core.JavalinConfig; 6 | import io.javalin.http.Context; 7 | import nl.jaapcoomans.demo.microframeworks.todo.domain.TodoService; 8 | import nl.jaapcoomans.demo.microframeworks.todo.peristsence.InMemoryTodoRepository; 9 | 10 | import static io.javalin.apibuilder.ApiBuilder.get; 11 | import static io.javalin.apibuilder.ApiBuilder.path; 12 | 13 | public class JavalinApplication { 14 | private static final int PORT = 7000; 15 | 16 | public static void main(String[] args) { 17 | Javalin.create(JavalinApplication::configure) 18 | .routes(() -> { 19 | path("/hello", helloWorldApi()); 20 | path("/todos", todoApi()); 21 | }) 22 | .start(PORT); 23 | } 24 | 25 | private static void configure(JavalinConfig config) { 26 | config.enableCorsForAllOrigins(); 27 | } 28 | 29 | private static EndpointGroup helloWorldApi() { 30 | return () -> { 31 | get(JavalinApplication::helloWorld); 32 | get("/:name", JavalinApplication::hello); 33 | }; 34 | } 35 | 36 | private static EndpointGroup todoApi() { 37 | var todoRepository = new InMemoryTodoRepository(); 38 | var todoService = new TodoService(todoRepository); 39 | var todoController = new TodoRestController(todoService, "http://localhost:7000/todos"); 40 | 41 | return todoController.defineEndpoints(); 42 | } 43 | 44 | private static void hello(Context context) { 45 | context.result(String.format("Hello, %s!", capitalize(context.pathParam("name")))); 46 | } 47 | 48 | private static void helloWorld(Context context) { 49 | context.result("Hello World!"); 50 | } 51 | 52 | private static String capitalize(String name) { 53 | if (name.length() < 2) { 54 | return name.toUpperCase(); 55 | } else { 56 | return name.substring(0, 1).toUpperCase() + name.substring(1).toLowerCase(); 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /dropwizard/src/main/java/nl/jaapcoomans/microframeworks/dropwizard/DropWizardApplication.java: -------------------------------------------------------------------------------- 1 | package nl.jaapcoomans.microframeworks.dropwizard; 2 | 3 | import io.dropwizard.Application; 4 | import io.dropwizard.Configuration; 5 | import io.dropwizard.setup.Environment; 6 | import nl.jaapcoomans.demo.microframeworks.todo.domain.TodoService; 7 | import nl.jaapcoomans.demo.microframeworks.todo.peristsence.InMemoryTodoRepository; 8 | import org.eclipse.jetty.servlets.CrossOriginFilter; 9 | 10 | import javax.servlet.DispatcherType; 11 | import javax.servlet.FilterRegistration; 12 | import java.util.EnumSet; 13 | 14 | public class DropWizardApplication extends Application { 15 | public static void main(String[] args) throws Exception { 16 | var application = new DropWizardApplication(); 17 | application.run(args); 18 | } 19 | 20 | @Override 21 | public void run(Configuration configuration, Environment environment) { 22 | this.enableCors(environment); 23 | 24 | var helloWorldController = new HelloWorldController(); 25 | environment.jersey().register(helloWorldController); 26 | 27 | var todoRestController = createTodoBackend(); 28 | environment.jersey().register(todoRestController); 29 | } 30 | 31 | private void enableCors(Environment environment) { 32 | final FilterRegistration.Dynamic cors = environment.servlets().addFilter("CORS", CrossOriginFilter.class); 33 | 34 | cors.setInitParameter("allowedOrigins", "*"); 35 | cors.setInitParameter("allowedHeaders", "Content-Type"); 36 | cors.setInitParameter("allowedMethods", "OPTIONS,GET,PUT,POST,DELETE,HEAD"); 37 | 38 | // Add URL mapping 39 | cors.addMappingForUrlPatterns(EnumSet.allOf(DispatcherType.class), true, "/*"); 40 | 41 | // Do not pass preflight requests on to application. 42 | cors.setInitParameter(CrossOriginFilter.CHAIN_PREFLIGHT_PARAM, "false"); 43 | } 44 | 45 | private TodoRestController createTodoBackend() { 46 | var repository = new InMemoryTodoRepository(); 47 | var todoService = new TodoService(repository); 48 | 49 | return new TodoRestController(todoService, "http://localhost:8080/todos"); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /http4k/src/main/kotlin/nl/jaapcoomans/demo/microframeworks/http4k/Http4kApplication.kt: -------------------------------------------------------------------------------- 1 | package nl.jaapcoomans.demo.microframeworks.http4k 2 | 3 | import nl.jaapcoomans.demo.microframeworks.todo.domain.TodoService 4 | import nl.jaapcoomans.demo.microframeworks.todo.peristsence.InMemoryTodoRepository 5 | import org.http4k.core.Method.DELETE 6 | import org.http4k.core.Method.GET 7 | import org.http4k.core.Method.PATCH 8 | import org.http4k.core.Method.POST 9 | import org.http4k.core.Request 10 | import org.http4k.core.Response 11 | import org.http4k.core.Status.Companion.OK 12 | import org.http4k.filter.AllowAllOriginPolicy 13 | import org.http4k.filter.CorsPolicy 14 | import org.http4k.filter.ServerFilters 15 | import org.http4k.routing.bind 16 | import org.http4k.routing.path 17 | import org.http4k.routing.routes 18 | import org.http4k.server.Jetty 19 | import org.http4k.server.asServer 20 | import java.util.Locale 21 | 22 | fun main() { 23 | val todoRepository = InMemoryTodoRepository() 24 | val todoService = TodoService(todoRepository) 25 | val todoRestController = TodoRestController(todoService) 26 | 27 | val app = routes( 28 | "/hello" bind GET to { Response(OK).body("Hello World!") }, 29 | "/hello/{name}" bind GET to helloController, 30 | "/todos" bind GET to todoRestController::getAll, 31 | "/todos" bind POST to todoRestController::createTodo, 32 | "/todos" bind DELETE to todoRestController::deleteAll, 33 | "/todos/{id}" bind GET to todoRestController::getTodo, 34 | "/todos/{id}" bind PATCH to todoRestController::patchTodo, 35 | "/todos/{id}" bind DELETE to todoRestController::deleteTodo 36 | ).withFilter(ServerFilters.Cors(CorsPolicy( 37 | originPolicy = AllowAllOriginPolicy, 38 | headers = listOf("Content-Type"), 39 | methods = listOf(GET, POST, DELETE, PATCH) 40 | ))) 41 | 42 | val server = app.asServer(Jetty(8080)) 43 | 44 | server.start() 45 | } 46 | 47 | val helloController = fun(req: Request): Response { 48 | return Response(OK).body("Hello, ${req.path("name")?.capitalize()}!") 49 | } 50 | 51 | fun String.capitalize(): String { 52 | return this.replaceFirstChar { if (it.isLowerCase()) it.titlecase(Locale.getDefault()) else it.toString() } 53 | } 54 | 55 | -------------------------------------------------------------------------------- /kumuluzee/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | kumuluzee 8 | 9 | 10 | parent 11 | nl.jaapcoomans.demo.microframeworks 12 | 0.0.1-SNAPSHOT 13 | 14 | 15 | 16 | 11 17 | 3.12.1 18 | 19 | 20 | 21 | 22 | 23 | com.kumuluz.ee 24 | kumuluzee-bom 25 | ${kumuluzee.version} 26 | pom 27 | import 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | com.kumuluz.ee 36 | kumuluzee-microProfile-3.3 37 | 38 | 39 | com.kumuluz.ee.cors 40 | kumuluzee-cors 41 | 1.0.6 42 | 43 | 44 | 45 | 46 | nl.jaapcoomans.demo.microframeworks 47 | todo-backend 48 | 0.0.1-SNAPSHOT 49 | 50 | 51 | 52 | 53 | 54 | 55 | org.apache.maven.plugins 56 | maven-jar-plugin 57 | 58 | 59 | org.apache.maven.plugins 60 | maven-dependency-plugin 61 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /jooby/src/main/java/nl/jaapcoomans/demo/microframeworks/jooby/JoobyApplication.java: -------------------------------------------------------------------------------- 1 | package nl.jaapcoomans.demo.microframeworks.jooby; 2 | 3 | import io.jooby.Context; 4 | import io.jooby.CorsHandler; 5 | import io.jooby.MediaType; 6 | import io.jooby.ServerOptions; 7 | import io.jooby.json.JacksonModule; 8 | import nl.jaapcoomans.demo.microframeworks.todo.domain.TodoService; 9 | import nl.jaapcoomans.demo.microframeworks.todo.peristsence.InMemoryTodoRepository; 10 | 11 | import javax.annotation.Nonnull; 12 | 13 | import static io.jooby.Jooby.runApp; 14 | 15 | public class JoobyApplication { 16 | private static final int PORT = 8080; 17 | 18 | public static void main(String[] args) { 19 | long startTime = System.currentTimeMillis(); 20 | 21 | var jacksonModule = new JacksonModule(); 22 | 23 | var todoRepository = new InMemoryTodoRepository(); 24 | var todoService = new TodoService(todoRepository); 25 | var todoController = new TodoRestController(todoService, "http://localhost:8080/todos"); 26 | 27 | runApp(args, app -> { 28 | app.setServerOptions(new ServerOptions().setPort(PORT)); 29 | app.encoder(MediaType.json, jacksonModule); 30 | app.decoder(MediaType.json, jacksonModule); 31 | app.decorator(new CorsHandler()); 32 | 33 | app.get("/hello", JoobyApplication::helloWorld); 34 | app.get("/hello/{name}", JoobyApplication::hello); 35 | 36 | todoController.initializeRoutes(app); 37 | 38 | app.onStarted(() -> { 39 | long bootTime = System.currentTimeMillis() - startTime; 40 | System.out.println("Started in " + bootTime + "ms."); 41 | }); 42 | }); 43 | } 44 | 45 | private static String helloWorld(Context context) { 46 | return "Hello World!"; 47 | } 48 | 49 | @Nonnull 50 | private static String hello(Context context) { 51 | var name = context.path("name").value(); 52 | return String.format("Hello, %s!", capitalize(name)); 53 | } 54 | 55 | private static String capitalize(String name) { 56 | if (name.length() < 2) { 57 | return name.toUpperCase(); 58 | } else { 59 | return name.substring(0, 1).toUpperCase() + name.substring(1).toLowerCase(); 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /blade/src/main/java/nl/jaapcoomans/microframeworks/blade/BladeApplication.java: -------------------------------------------------------------------------------- 1 | package nl.jaapcoomans.microframeworks.blade; 2 | 3 | import com.blade.Blade; 4 | import com.blade.kit.JsonKit; 5 | import com.blade.mvc.RouteContext; 6 | import com.blade.security.web.cors.CorsConfiger; 7 | import nl.jaapcoomans.demo.microframeworks.todo.domain.TodoService; 8 | import nl.jaapcoomans.demo.microframeworks.todo.peristsence.InMemoryTodoRepository; 9 | 10 | import java.util.List; 11 | 12 | public class BladeApplication { 13 | public static void main(String[] args) { 14 | /* 15 | * The default JsonKit is not configurable and fails to correctly serialize UUID. Luckily the actual 16 | * serialization is pluggable. Therefore created a custom implementation based on Jackson. 17 | */ 18 | JsonKit.jsonSupprt(new JacksonJsonSupport()); 19 | 20 | Blade application = Blade.of() 21 | .enableCors(true, corsConfiger()) 22 | .get("/hello", BladeApplication::helloWorld) 23 | .get("/hello/:name", BladeApplication::hello); 24 | 25 | createTodoBackend(application); 26 | 27 | application.start(BladeApplication.class, args); 28 | } 29 | 30 | private static CorsConfiger corsConfiger() { 31 | return CorsConfiger.builder() 32 | .allowedHeaders(List.of("Content-Type")) 33 | .build(); 34 | } 35 | 36 | private static void createTodoBackend(Blade application) { 37 | var todoRepository = new InMemoryTodoRepository(); 38 | var todoService = new TodoService(todoRepository); 39 | var todoApi = new TodoRestController(todoService, "http://localhost:7000/todos"); 40 | 41 | todoApi.initializeRoutes(application); 42 | } 43 | 44 | private static void helloWorld(RouteContext context) { 45 | context.text("Hello World!"); 46 | } 47 | 48 | private static void hello(RouteContext context) { 49 | String name = context.pathString("name"); 50 | context.text(String.format("Hello, %s!", capitalize(name))); 51 | } 52 | 53 | private static String capitalize(String name) { 54 | if (name.length() < 2) { 55 | return name.toUpperCase(); 56 | } else { 57 | return name.substring(0, 1).toUpperCase() + name.substring(1).toLowerCase(); 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /armeria/src/main/java/nl/jaapcoomans/microframeworks/armeria/ArmeriaApplication.java: -------------------------------------------------------------------------------- 1 | package nl.jaapcoomans.microframeworks.armeria; 2 | 3 | import com.linecorp.armeria.common.HttpRequest; 4 | import com.linecorp.armeria.common.HttpResponse; 5 | import com.linecorp.armeria.server.Server; 6 | import com.linecorp.armeria.server.ServerBuilder; 7 | import com.linecorp.armeria.server.ServiceRequestContext; 8 | import nl.jaapcoomans.demo.microframeworks.todo.domain.TodoService; 9 | import nl.jaapcoomans.demo.microframeworks.todo.peristsence.InMemoryTodoRepository; 10 | import org.slf4j.Logger; 11 | import org.slf4j.LoggerFactory; 12 | 13 | import java.util.concurrent.CompletableFuture; 14 | 15 | public class ArmeriaApplication { 16 | private static final int PORT = 8080; 17 | private static final Logger LOGGER = LoggerFactory.getLogger(ArmeriaApplication.class); 18 | 19 | public static void main(String[] args) { 20 | final long startTime = System.currentTimeMillis(); 21 | 22 | ServerBuilder sb = Server.builder(); 23 | sb.http(PORT); 24 | 25 | sb.service("/hello", ArmeriaApplication::helloWorld); 26 | sb.service("/hello/{name}", ArmeriaApplication::hello); 27 | 28 | TodoRestController todoBackendApi = createTodoBackend(); 29 | sb.annotatedService(todoBackendApi); 30 | 31 | Server server = sb.build(); 32 | CompletableFuture future = server.start(); 33 | future.join(); 34 | 35 | LOGGER.info("Started in {} ms", System.currentTimeMillis() - startTime); 36 | } 37 | 38 | private static TodoRestController createTodoBackend() { 39 | var todoRepository = new InMemoryTodoRepository(); 40 | var todoService = new TodoService(todoRepository); 41 | return new TodoRestController(todoService, "http://localhost:8080/todos"); 42 | } 43 | 44 | private static HttpResponse helloWorld(ServiceRequestContext context, HttpRequest request) { 45 | return HttpResponse.of("Hello World!"); 46 | } 47 | 48 | private static HttpResponse hello(ServiceRequestContext context, HttpRequest request) { 49 | String name = context.pathParam("name"); 50 | return HttpResponse.of("Hello, %s!", capitalize(name)); 51 | } 52 | 53 | private static String capitalize(String name) { 54 | if (name.length() < 2) { 55 | return name.toUpperCase(); 56 | } else { 57 | return name.substring(0, 1).toUpperCase() + name.substring(1).toLowerCase(); 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /ratpack/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | ratpack 7 | jar 8 | 9 | 10 | nl.jaapcoomans.demo.microframeworks 11 | parent 12 | 0.0.1-SNAPSHOT 13 | 14 | 15 | 16 | 1.8.2 17 | 18 | 19 | 20 | 21 | io.ratpack 22 | ratpack-core 23 | ${ratpack.version} 24 | 25 | 26 | 27 | 28 | nl.jaapcoomans.demo.microframeworks 29 | todo-backend 30 | 0.0.1-SNAPSHOT 31 | 32 | 33 | 34 | 35 | org.slf4j 36 | slf4j-simple 37 | 38 | 39 | 40 | 41 | org.junit.jupiter 42 | junit-jupiter-api 43 | 44 | 45 | org.junit.jupiter 46 | junit-jupiter-engine 47 | 48 | 49 | org.mockito 50 | mockito-junit-jupiter 51 | 52 | 53 | org.assertj 54 | assertj-core 55 | 56 | 57 | 58 | 59 | 60 | 61 | org.apache.maven.plugins 62 | maven-jar-plugin 63 | 64 | 65 | org.apache.maven.plugins 66 | maven-dependency-plugin 67 | 68 | 69 | 70 | 71 | -------------------------------------------------------------------------------- /armeria/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | armeria 7 | jar 8 | 9 | 10 | nl.jaapcoomans.demo.microframeworks 11 | parent 12 | 0.0.1-SNAPSHOT 13 | 14 | 15 | 16 | 1.24.3 17 | 18 | 19 | 20 | 21 | com.linecorp.armeria 22 | armeria 23 | ${armeria.version} 24 | 25 | 26 | 27 | 28 | nl.jaapcoomans.demo.microframeworks 29 | todo-backend 30 | 0.0.1-SNAPSHOT 31 | 32 | 33 | 34 | 35 | org.slf4j 36 | slf4j-simple 37 | 38 | 39 | 40 | 41 | org.junit.jupiter 42 | junit-jupiter-api 43 | 44 | 45 | org.junit.jupiter 46 | junit-jupiter-engine 47 | 48 | 49 | org.mockito 50 | mockito-junit-jupiter 51 | 52 | 53 | org.assertj 54 | assertj-core 55 | 56 | 57 | 58 | 59 | 60 | 61 | org.apache.maven.plugins 62 | maven-jar-plugin 63 | 64 | 65 | org.apache.maven.plugins 66 | maven-dependency-plugin 67 | 68 | 69 | 70 | 71 | -------------------------------------------------------------------------------- /pippo/src/main/java/nl/jaapcoomans/demo/microframeworks/pippo/PippoApplication.java: -------------------------------------------------------------------------------- 1 | package nl.jaapcoomans.demo.microframeworks.pippo; 2 | 3 | import nl.jaapcoomans.demo.microframeworks.todo.domain.TodoService; 4 | import nl.jaapcoomans.demo.microframeworks.todo.peristsence.InMemoryTodoRepository; 5 | import ro.pippo.core.HttpConstants; 6 | import ro.pippo.core.Pippo; 7 | import ro.pippo.core.route.CorsHandler; 8 | import ro.pippo.core.route.RouteContext; 9 | 10 | public class PippoApplication { 11 | private static final int PORT = 8080; 12 | 13 | public static void main(String[] args) { 14 | Pippo pippo = new Pippo(); 15 | pippo.getServer().getSettings().host("0.0.0.0"); 16 | pippo.getServer().setPort(PORT); 17 | 18 | pippo.ANY("/.*", createCorsHandler()); 19 | pippo.GET("/hello", PippoApplication::helloWorld); 20 | pippo.GET("/hello/{name}", PippoApplication::hello); 21 | 22 | var todoRestController = createTodoBackend(); 23 | todoRestController.initializeRoutes(pippo); 24 | 25 | pippo.start(); 26 | } 27 | 28 | private static CorsHandler createCorsHandler() { 29 | var corsHandler = new CorsHandler("*"); 30 | corsHandler.allowHeaders(HttpConstants.Header.CONTENT_TYPE); 31 | corsHandler.allowMethods("GET,POST,PATCH,DELETE"); 32 | 33 | return corsHandler; 34 | } 35 | 36 | private static void initializeCors(Pippo pippo) { 37 | var corsHandler = new CorsHandler("*"); 38 | corsHandler.allowHeaders(HttpConstants.Header.CONTENT_TYPE); 39 | corsHandler.allowMethods("GET,POST,PATCH,DELETE"); 40 | 41 | pippo.ANY("/.*", corsHandler); 42 | } 43 | 44 | private static TodoRestController createTodoBackend() { 45 | var todoRepository = new InMemoryTodoRepository(); 46 | var todoService = new TodoService(todoRepository); 47 | 48 | return new TodoRestController(todoService, "http://localhost:8080/todos"); 49 | } 50 | 51 | private static void helloWorld(RouteContext context) { 52 | context.text().send("Hello World!"); 53 | } 54 | 55 | private static void hello(RouteContext context) { 56 | var name = context.getParameter("name").toString(); 57 | context.text().send(String.format("Hello, %s!", capitalize(name))); 58 | } 59 | 60 | private static String capitalize(String name) { 61 | if (name.length() < 2) { 62 | return name.toUpperCase(); 63 | } else { 64 | return name.substring(0, 1).toUpperCase() + name.substring(1).toLowerCase(); 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /todo-backend-test/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | todo-backend-test 8 | 9 | 10 | parent 11 | nl.jaapcoomans.demo.microframeworks 12 | 0.0.1-SNAPSHOT 13 | 14 | 15 | 16 | 1.15.3 17 | 18 | 19 | 20 | 21 | org.slf4j 22 | slf4j-simple 23 | test 24 | 25 | 26 | 27 | org.junit.jupiter 28 | junit-jupiter-api 29 | ${junit.version} 30 | test 31 | 32 | 33 | org.junit.jupiter 34 | junit-jupiter-engine 35 | ${junit.version} 36 | test 37 | 38 | 39 | io.rest-assured 40 | rest-assured 41 | 4.3.3 42 | test 43 | 44 | 45 | org.assertj 46 | assertj-core 47 | ${assertj.version} 48 | test 49 | 50 | 51 | org.testcontainers 52 | testcontainers 53 | ${testcontainers.version} 54 | test 55 | 56 | 57 | org.hamcrest 58 | hamcrest-core 59 | 60 | 61 | 62 | 63 | org.testcontainers 64 | junit-jupiter 65 | ${testcontainers.version} 66 | test 67 | 68 | 69 | -------------------------------------------------------------------------------- /helidon-se/src/main/java/nl/jaapcoomans/demo/microframeworks/helidon/se/HelidonApplication.java: -------------------------------------------------------------------------------- 1 | package nl.jaapcoomans.demo.microframeworks.helidon.se; 2 | 3 | import io.helidon.media.jackson.JacksonSupport; 4 | import io.helidon.webserver.Routing; 5 | import io.helidon.webserver.WebServer; 6 | import io.helidon.webserver.cors.CorsSupport; 7 | import io.helidon.webserver.cors.CrossOriginConfig; 8 | import nl.jaapcoomans.demo.microframeworks.todo.domain.TodoService; 9 | import nl.jaapcoomans.demo.microframeworks.todo.peristsence.InMemoryTodoRepository; 10 | 11 | public class HelidonApplication { 12 | public static void main(final String[] args) { 13 | long startTime = System.currentTimeMillis(); 14 | var server = WebServer.builder(HelidonApplication::createRouting) 15 | .addMediaSupport(JacksonSupport.create()) 16 | .port(8080).build(); 17 | 18 | server.start() 19 | .thenAccept(ws -> { 20 | long bootTime = System.currentTimeMillis() - startTime; 21 | System.out.println( 22 | "WEB server is up! http://localhost:" + ws.port() + "/ in " + bootTime + "ms"); 23 | ws.whenShutdown().thenRun(() 24 | -> System.out.println("WEB server is DOWN. Good bye!")); 25 | }) 26 | .exceptionally(t -> { 27 | System.err.println("Startup failed: " + t.getMessage()); 28 | t.printStackTrace(System.err); 29 | return null; 30 | }); 31 | } 32 | 33 | private static Routing createRouting() { 34 | HelloWorldRestController helloWorldRestController = new HelloWorldRestController(); 35 | 36 | TodoRestController todoRestController = createTodoBackend(); 37 | 38 | return Routing.builder() 39 | .register("/hello", helloWorldRestController) 40 | .register("/todos", createCorsSupport(), todoRestController) 41 | .build(); 42 | } 43 | 44 | private static CorsSupport createCorsSupport() { 45 | return CorsSupport.builder() 46 | .addCrossOrigin(CrossOriginConfig.builder() 47 | .allowOrigins("*") 48 | .allowMethods("GET", "POST", "PUT", "DELETE") 49 | .build()) 50 | .addCrossOrigin(CrossOriginConfig.create()) 51 | .build(); 52 | } 53 | 54 | private static TodoRestController createTodoBackend() { 55 | var repository = new InMemoryTodoRepository(); 56 | var todoService = new TodoService(repository); 57 | 58 | return new TodoRestController(todoService, "http://localhost:8080/todos"); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /ktor/src/main/kotlin/nl/jaapcoomans/demo/microframeworks/ktor/TodoBackend.kt: -------------------------------------------------------------------------------- 1 | package nl.jaapcoomans.demo.microframeworks.ktor 2 | 3 | import io.ktor.application.call 4 | import io.ktor.http.HttpStatusCode 5 | import io.ktor.request.receive 6 | import io.ktor.response.respond 7 | import io.ktor.routing.Route 8 | import io.ktor.routing.delete 9 | import io.ktor.routing.get 10 | import io.ktor.routing.patch 11 | import io.ktor.routing.post 12 | import io.ktor.routing.route 13 | import nl.jaapcoomans.demo.microframeworks.todo.api.CreateTodoCommand 14 | import nl.jaapcoomans.demo.microframeworks.todo.api.TodoDTO 15 | import nl.jaapcoomans.demo.microframeworks.todo.domain.PartialTodo 16 | import nl.jaapcoomans.demo.microframeworks.todo.domain.Todo 17 | import nl.jaapcoomans.demo.microframeworks.todo.domain.TodoService 18 | import nl.jaapcoomans.demo.microframeworks.todo.peristsence.InMemoryTodoRepository 19 | import java.util.UUID 20 | 21 | val todoRepository = InMemoryTodoRepository() 22 | val todoService = TodoService(todoRepository) 23 | 24 | fun Route.todoRoutes() { 25 | route("/todos") { 26 | get { 27 | call.respond(todoService.findAll().map { it.wrap() }) 28 | } 29 | get("/{id}") { 30 | val id = call.parameters["id"] 31 | val todo = todoService.findById(UUID.fromString(id)) 32 | .map { it.wrap() } 33 | .orElse(null) 34 | 35 | when(todo) { 36 | null -> call.respond(HttpStatusCode.NotFound) 37 | else -> call.respond(todo) 38 | } 39 | } 40 | post { 41 | val command = call.receive() 42 | val todo = todoService.createNewTodo(command.title, command.order) 43 | call.respond(todo.wrap()) 44 | } 45 | delete { 46 | todoService.deleteAll() 47 | call.respond(HttpStatusCode.NoContent) 48 | } 49 | patch("/{id}") { 50 | val id = call.parameters["id"] 51 | val command = call.receive() 52 | 53 | val todo = todoService.updateTodo(UUID.fromString(id), command).map { it.wrap() } 54 | if (todo.isPresent) { 55 | call.respond(todo.get()) 56 | } else { 57 | call.respond(HttpStatusCode.NotFound) 58 | } 59 | } 60 | delete("/{id}") { 61 | val id = call.parameters["id"] 62 | todoService.delete(UUID.fromString(id)) 63 | call.respond(HttpStatusCode.NoContent) 64 | } 65 | } 66 | } 67 | 68 | fun Todo.wrap(): TodoDTO { 69 | return TodoDTO.from(this, createUrl()) 70 | } 71 | 72 | fun Todo.createUrl(): String { 73 | return "http://localhost:8080/todos/$id" 74 | } -------------------------------------------------------------------------------- /blade/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | blade 7 | jar 8 | 9 | 10 | nl.jaapcoomans.demo.microframeworks 11 | parent 12 | 0.0.1-SNAPSHOT 13 | 14 | 15 | 16 | 2.0.15.RELEASE 17 | 2.13.4.2 18 | 19 | 20 | 21 | 22 | com.bladejava 23 | blade-mvc 24 | ${blade.version} 25 | 26 | 27 | 28 | 29 | nl.jaapcoomans.demo.microframeworks 30 | todo-backend 31 | 0.0.1-SNAPSHOT 32 | 33 | 34 | 35 | 36 | com.fasterxml.jackson.core 37 | jackson-databind 38 | ${jackson.version} 39 | 40 | 41 | 42 | 43 | org.slf4j 44 | slf4j-simple 45 | 46 | 47 | 48 | 49 | org.junit.jupiter 50 | junit-jupiter-api 51 | 52 | 53 | org.junit.jupiter 54 | junit-jupiter-engine 55 | 56 | 57 | org.mockito 58 | mockito-junit-jupiter 59 | 60 | 61 | org.assertj 62 | assertj-core 63 | 64 | 65 | 66 | 67 | 68 | 69 | org.apache.maven.plugins 70 | maven-jar-plugin 71 | 72 | 73 | org.apache.maven.plugins 74 | maven-dependency-plugin 75 | 76 | 77 | 78 | 79 | -------------------------------------------------------------------------------- /pippo/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | pippo 7 | jar 8 | 9 | 10 | nl.jaapcoomans.demo.microframeworks 11 | parent 12 | 0.0.1-SNAPSHOT 13 | 14 | 15 | 16 | 1.13.1 17 | 18 | 19 | 20 | 21 | ro.pippo 22 | pippo-core 23 | ${pippo.version} 24 | 25 | 26 | ro.pippo 27 | pippo-jetty 28 | ${pippo.version} 29 | 30 | 31 | ro.pippo 32 | pippo-jackson 33 | ${pippo.version} 34 | 35 | 36 | 37 | 38 | nl.jaapcoomans.demo.microframeworks 39 | todo-backend 40 | 0.0.1-SNAPSHOT 41 | 42 | 43 | 44 | 45 | org.slf4j 46 | slf4j-simple 47 | 48 | 49 | 50 | 51 | org.junit.jupiter 52 | junit-jupiter-api 53 | 54 | 55 | org.junit.jupiter 56 | junit-jupiter-engine 57 | 58 | 59 | org.mockito 60 | mockito-junit-jupiter 61 | 62 | 63 | org.assertj 64 | assertj-core 65 | 66 | 67 | 68 | 69 | 70 | 71 | org.apache.maven.plugins 72 | maven-jar-plugin 73 | 74 | 75 | org.apache.maven.plugins 76 | maven-dependency-plugin 77 | 78 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /jooby/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | jooby 7 | jar 8 | 9 | 10 | nl.jaapcoomans.demo.microframeworks 11 | parent 12 | 0.0.1-SNAPSHOT 13 | 14 | 15 | 16 | 2.9.6 17 | 18 | 19 | 20 | 21 | 22 | io.jooby 23 | jooby-bom 24 | ${jooby.version} 25 | pom 26 | import 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | io.jooby 35 | jooby-netty 36 | 37 | 38 | io.jooby 39 | jooby-jackson 40 | 41 | 42 | 43 | 44 | nl.jaapcoomans.demo.microframeworks 45 | todo-backend 46 | 0.0.1-SNAPSHOT 47 | 48 | 49 | 50 | 51 | org.slf4j 52 | slf4j-simple 53 | 54 | 55 | 56 | 57 | org.junit.jupiter 58 | junit-jupiter-api 59 | 60 | 61 | org.junit.jupiter 62 | junit-jupiter-engine 63 | 64 | 65 | org.mockito 66 | mockito-junit-jupiter 67 | 68 | 69 | org.assertj 70 | assertj-core 71 | 72 | 73 | 74 | 75 | 76 | 77 | org.apache.maven.plugins 78 | maven-jar-plugin 79 | 80 | 81 | org.apache.maven.plugins 82 | maven-dependency-plugin 83 | 84 | 85 | 86 | 87 | -------------------------------------------------------------------------------- /http4k/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | http4k 7 | jar 8 | 9 | 10 | nl.jaapcoomans.demo.microframeworks 11 | parent 12 | 0.0.1-SNAPSHOT 13 | 14 | 15 | 16 | 4.9.1.0 17 | 18 | 19 | 20 | 21 | 22 | org.http4k 23 | http4k-core 24 | ${http4k.version} 25 | 26 | 27 | org.http4k 28 | http4k-server-jetty 29 | ${http4k.version} 30 | 31 | 32 | org.http4k 33 | http4k-format-jackson 34 | ${http4k.version} 35 | 36 | 37 | 38 | 39 | nl.jaapcoomans.demo.microframeworks 40 | todo-backend 41 | 0.0.1-SNAPSHOT 42 | 43 | 44 | 45 | 46 | org.jetbrains.kotlin 47 | kotlin-stdlib 48 | 49 | 50 | 51 | 52 | org.slf4j 53 | slf4j-simple 54 | 55 | 56 | 57 | 58 | org.junit.jupiter 59 | junit-jupiter-api 60 | 61 | 62 | org.junit.jupiter 63 | junit-jupiter-engine 64 | 65 | 66 | 67 | 68 | src/main/kotlin 69 | 70 | 71 | 72 | org.jetbrains.kotlin 73 | kotlin-maven-plugin 74 | 75 | 76 | org.apache.maven.plugins 77 | maven-jar-plugin 78 | 79 | 80 | org.apache.maven.plugins 81 | maven-dependency-plugin 82 | 83 | 84 | 85 | 86 | -------------------------------------------------------------------------------- /dropwizard/src/main/java/nl/jaapcoomans/microframeworks/dropwizard/TodoRestController.java: -------------------------------------------------------------------------------- 1 | package nl.jaapcoomans.microframeworks.dropwizard; 2 | 3 | import nl.jaapcoomans.demo.microframeworks.todo.api.CreateTodoCommand; 4 | import nl.jaapcoomans.demo.microframeworks.todo.api.TodoDTO; 5 | import nl.jaapcoomans.demo.microframeworks.todo.domain.PartialTodo; 6 | import nl.jaapcoomans.demo.microframeworks.todo.domain.Todo; 7 | import nl.jaapcoomans.demo.microframeworks.todo.domain.TodoService; 8 | 9 | import javax.ws.rs.Consumes; 10 | import javax.ws.rs.DELETE; 11 | import javax.ws.rs.GET; 12 | import javax.ws.rs.PATCH; 13 | import javax.ws.rs.POST; 14 | import javax.ws.rs.Path; 15 | import javax.ws.rs.PathParam; 16 | import javax.ws.rs.Produces; 17 | import javax.ws.rs.core.MediaType; 18 | import javax.ws.rs.core.Response; 19 | import java.util.List; 20 | import java.util.UUID; 21 | import java.util.stream.Collectors; 22 | 23 | @Path("/todos") 24 | public class TodoRestController { 25 | private TodoService todoService; 26 | private String baseUrl; 27 | 28 | TodoRestController(TodoService todoService, String baseUrl) { 29 | this.todoService = todoService; 30 | this.baseUrl = baseUrl; 31 | } 32 | 33 | @GET 34 | @Produces(MediaType.APPLICATION_JSON) 35 | public List getAll() { 36 | return this.todoService.findAll().stream() 37 | .map(this::wrap) 38 | .collect(Collectors.toList()); 39 | } 40 | 41 | @GET 42 | @Path("/{id}") 43 | @Produces(MediaType.APPLICATION_JSON) 44 | public Response getTodo(@PathParam("id") String id) { 45 | return this.todoService.findById(UUID.fromString(id)) 46 | .map(this::wrap) 47 | .map(body -> Response.ok(body).build()) 48 | .orElse(Response.status(Response.Status.NOT_FOUND).build()); 49 | } 50 | 51 | @POST 52 | @Produces(MediaType.APPLICATION_JSON) 53 | @Consumes(MediaType.APPLICATION_JSON) 54 | public TodoDTO createTodo(CreateTodoCommand command) { 55 | Todo result = this.todoService.createNewTodo(command.getTitle(), command.getOrder()); 56 | return this.wrap(result); 57 | } 58 | 59 | @DELETE 60 | public Response deleteAll() { 61 | this.todoService.deleteAll(); 62 | return Response.noContent().build(); 63 | } 64 | 65 | @PATCH 66 | @Path("/{id}") 67 | @Produces(MediaType.APPLICATION_JSON) 68 | @Consumes(MediaType.APPLICATION_JSON) 69 | public TodoDTO patchTodo(@PathParam("id") String id, PartialTodo command) { 70 | return this.todoService.updateTodo(UUID.fromString(id), command) 71 | .map(this::wrap) 72 | .orElse(null); 73 | } 74 | 75 | @DELETE 76 | @Path("/{id}") 77 | public Response deleteTodo(@PathParam("id") UUID id) { 78 | this.todoService.delete(id); 79 | return Response.noContent().build(); 80 | } 81 | 82 | private TodoDTO wrap(Todo todo) { 83 | return TodoDTO.from(todo, createUrl(todo)); 84 | } 85 | 86 | private String createUrl(Todo todo) { 87 | return this.baseUrl + "/" + todo.getId().toString(); 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /ratpack/src/main/java/nl/jaapcoomans/demo/microframeworks/ratpack/RatpackApplication.java: -------------------------------------------------------------------------------- 1 | package nl.jaapcoomans.demo.microframeworks.ratpack; 2 | 3 | import nl.jaapcoomans.demo.microframeworks.todo.domain.TodoService; 4 | import nl.jaapcoomans.demo.microframeworks.todo.peristsence.InMemoryTodoRepository; 5 | import org.slf4j.Logger; 6 | import org.slf4j.LoggerFactory; 7 | import ratpack.handling.Context; 8 | import ratpack.http.HttpMethod; 9 | import ratpack.http.Status; 10 | import ratpack.server.RatpackServer; 11 | import ratpack.server.RatpackServerSpec; 12 | import ratpack.server.ServerConfig; 13 | 14 | public class RatpackApplication { 15 | private static final Logger LOGGER = LoggerFactory.getLogger(RatpackApplication.class); 16 | private static final int PORT = 8080; 17 | 18 | public static void main(String[] args) throws Exception { 19 | long startTime = System.currentTimeMillis(); 20 | 21 | RatpackServer.start(RatpackApplication::init); 22 | 23 | LOGGER.info("Started in {} ms", System.currentTimeMillis() - startTime); 24 | } 25 | 26 | private static void init(RatpackServerSpec server) { 27 | var todoRestController = createTodoBackend(); 28 | 29 | server.serverConfig(ServerConfig.embedded().port(PORT)) 30 | .handlers(chain -> { 31 | chain.all(RatpackApplication::cors) 32 | .get("hello", RatpackApplication::helloWorld) 33 | .get("hello/:name", RatpackApplication::hello); 34 | todoRestController.initializeEndpoints(chain); 35 | }); 36 | } 37 | 38 | private static TodoRestController createTodoBackend() { 39 | var todoRepository = new InMemoryTodoRepository(); 40 | var todoService = new TodoService(todoRepository); 41 | return new TodoRestController(todoService, "http://localhost:8080/todos"); 42 | } 43 | 44 | private static void cors(Context context) { 45 | context.header("Access-Control-Allow-Origin", "*"); 46 | if (context.getRequest().getMethod() == HttpMethod.OPTIONS) { 47 | context.header("Access-Control-Allow-Methods", "GET,POST,PATCH,DELETE"); 48 | context.header("Access-Control-Allow-Headers", "content-type"); 49 | context.getResponse().status(Status.OK).send(); 50 | } else { 51 | context.next(); 52 | } 53 | } 54 | 55 | private static void helloWorld(Context context) { 56 | context.getResponse() 57 | .contentType("text/plain") 58 | .send("Hello World"); 59 | } 60 | 61 | private static void hello(Context context) { 62 | var name = context.getPathTokens().get("name"); 63 | context.getResponse() 64 | .contentType("text/plain") 65 | .send(String.format("Hello, %s!", capitalize(name))); 66 | } 67 | 68 | private static String capitalize(String name) { 69 | if (name.length() < 2) { 70 | return name.toUpperCase(); 71 | } else { 72 | return name.substring(0, 1).toUpperCase() + name.substring(1).toLowerCase(); 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /http4k/src/main/kotlin/nl/jaapcoomans/demo/microframeworks/http4k/TodoRestController.kt: -------------------------------------------------------------------------------- 1 | package nl.jaapcoomans.demo.microframeworks.http4k 2 | 3 | import nl.jaapcoomans.demo.microframeworks.todo.api.CreateTodoCommand 4 | import nl.jaapcoomans.demo.microframeworks.todo.api.TodoDTO 5 | import nl.jaapcoomans.demo.microframeworks.todo.domain.PartialTodo 6 | import nl.jaapcoomans.demo.microframeworks.todo.domain.Todo 7 | import nl.jaapcoomans.demo.microframeworks.todo.domain.TodoService 8 | import org.http4k.core.ContentType 9 | import org.http4k.core.Request 10 | import org.http4k.core.Response 11 | import org.http4k.core.Status 12 | import org.http4k.format.Jackson 13 | import org.http4k.format.Jackson.asJsonObject 14 | import org.http4k.routing.path 15 | import java.util.UUID 16 | 17 | class TodoRestController(val todoService: TodoService) { 18 | private val json = Jackson 19 | 20 | fun getAll(req: Request): Response { 21 | val todos = todoService.findAll().map { it.wrap() } 22 | return Response(Status.OK) 23 | .header("Content-Type", ContentType.APPLICATION_JSON.value) 24 | .body(todos.asJsonObject().toPrettyString()) 25 | } 26 | 27 | fun createTodo(req: Request): Response { 28 | val command = json.asA(req.bodyString(), CreateTodoCommand::class) 29 | 30 | val todo = todoService.createNewTodo(command.title, command.order) 31 | 32 | return Response(Status.OK) 33 | .header("Content-Type", ContentType.APPLICATION_JSON.value) 34 | .body(todo.wrap().asJsonObject().toPrettyString()) 35 | } 36 | 37 | fun getTodo(req: Request): Response { 38 | val id = req.path("id") 39 | val todo = todoService.findById(UUID.fromString(id)) 40 | return if (todo.isPresent) { 41 | Response(Status.OK) 42 | .header("Content-Type", ContentType.APPLICATION_JSON.value) 43 | .body(todo.get().wrap().asJsonObject().toPrettyString()) 44 | } else { 45 | Response(Status.NOT_FOUND) 46 | } 47 | } 48 | 49 | fun patchTodo(req: Request): Response { 50 | val id = req.path("id") 51 | val command = json.asA(req.bodyString(), PartialTodo::class) 52 | 53 | val todo = todoService.updateTodo(UUID.fromString(id), command) 54 | return if (todo.isPresent) { 55 | Response(Status.OK) 56 | .header("Content-Type", ContentType.APPLICATION_JSON.value) 57 | .body(todo.get().wrap().asJsonObject().toPrettyString()) 58 | } else { 59 | Response(Status.NOT_FOUND) 60 | } 61 | } 62 | 63 | fun deleteTodo(req: Request): Response { 64 | val id = req.path("id") 65 | todoService.delete(UUID.fromString(id)) 66 | return Response(Status.NO_CONTENT) 67 | } 68 | 69 | fun deleteAll(req: Request): Response { 70 | todoService.deleteAll() 71 | return Response(Status.NO_CONTENT) 72 | } 73 | 74 | private fun Todo.wrap(): TodoDTO { 75 | return TodoDTO.from(this, createUrl()) 76 | } 77 | 78 | private fun Todo.createUrl(): String { 79 | return "http://localhost:8080/todos/$id" 80 | } 81 | } -------------------------------------------------------------------------------- /spark/src/main/java/nl/jaapcoomans/microframeworks/spark/SparkApplication.java: -------------------------------------------------------------------------------- 1 | package nl.jaapcoomans.microframeworks.spark; 2 | 3 | import nl.jaapcoomans.demo.microframeworks.todo.domain.TodoService; 4 | import nl.jaapcoomans.demo.microframeworks.todo.peristsence.InMemoryTodoRepository; 5 | import spark.Request; 6 | import spark.Response; 7 | 8 | import static spark.Spark.before; 9 | import static spark.Spark.delete; 10 | import static spark.Spark.get; 11 | import static spark.Spark.options; 12 | import static spark.Spark.patch; 13 | import static spark.Spark.path; 14 | import static spark.Spark.port; 15 | import static spark.Spark.post; 16 | 17 | public class SparkApplication { 18 | private static final int PORT = 8080; 19 | 20 | public static void main(String[] args) { 21 | port(PORT); 22 | addCORS(); 23 | 24 | get("/hello", SparkApplication::helloWorld); 25 | get("/hello/:name", SparkApplication::hello); 26 | 27 | setupTodoBackend(); 28 | } 29 | 30 | private static void setupTodoBackend() { 31 | var todoRepository = new InMemoryTodoRepository(); 32 | var todoService = new TodoService(todoRepository); 33 | var todoRestController = new TodoRestController(todoService, "http://localhost:8080/todos"); 34 | 35 | var jsonTransformer = new JacksonResponseTransformer(); 36 | 37 | path("/todos", () -> { 38 | get("", todoRestController::getAll, jsonTransformer); 39 | post("", todoRestController::createTodo, jsonTransformer); 40 | delete("", todoRestController::deleteAll); 41 | path("/:id", () -> { 42 | get("", todoRestController::getTodo, jsonTransformer); 43 | delete("", todoRestController::deleteTodo); 44 | patch("", todoRestController::patchTodo, jsonTransformer); 45 | }); 46 | }); 47 | } 48 | 49 | private static String helloWorld(Request request, Response response) { 50 | return "Hello World!"; 51 | } 52 | 53 | private static String hello(Request request, Response response) { 54 | return String.format("Hello, %s!", capitalize(request.params("name"))); 55 | } 56 | 57 | private static String capitalize(String name) { 58 | if (name.length() < 2) { 59 | return name.toUpperCase(); 60 | } else { 61 | return name.substring(0, 1).toUpperCase() + name.substring(1).toLowerCase(); 62 | } 63 | } 64 | 65 | private static void addCORS() { 66 | before((request, response) -> { 67 | response.header("Access-Control-Allow-Origin", "*"); 68 | }); 69 | 70 | options("/*", (request, response) -> { 71 | String accessControlRequestHeaders = request.headers("Access-Control-Request-Headers"); 72 | if (accessControlRequestHeaders != null) { 73 | response.header("Access-Control-Allow-Headers", accessControlRequestHeaders); 74 | } 75 | 76 | String accessControlRequestMethod = request.headers("Access-Control-Request-Method"); 77 | if (accessControlRequestMethod != null) { 78 | response.header("Access-Control-Allow-Methods", accessControlRequestMethod); 79 | } 80 | 81 | return "OK"; 82 | }); 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /blade/src/main/java/nl/jaapcoomans/microframeworks/blade/TodoRestController.java: -------------------------------------------------------------------------------- 1 | package nl.jaapcoomans.microframeworks.blade; 2 | 3 | import com.blade.Blade; 4 | import com.blade.mvc.RouteContext; 5 | import com.blade.mvc.http.HttpMethod; 6 | import nl.jaapcoomans.demo.microframeworks.todo.api.CreateTodoCommand; 7 | import nl.jaapcoomans.demo.microframeworks.todo.api.TodoDTO; 8 | import nl.jaapcoomans.demo.microframeworks.todo.domain.PartialTodo; 9 | import nl.jaapcoomans.demo.microframeworks.todo.domain.Todo; 10 | import nl.jaapcoomans.demo.microframeworks.todo.domain.TodoService; 11 | 12 | import java.util.List; 13 | import java.util.UUID; 14 | import java.util.stream.Collectors; 15 | 16 | class TodoRestController { 17 | private static final String ID_PATH_PARAM = "id"; 18 | 19 | private TodoService todoService; 20 | private String baseUrl; 21 | 22 | TodoRestController(TodoService todoService, String baseUrl) { 23 | this.todoService = todoService; 24 | this.baseUrl = baseUrl; 25 | } 26 | 27 | void initializeRoutes(Blade application) { 28 | application.contextPath("/todos") 29 | .get("/", this::getAll) 30 | .post("/", this::createTodo) 31 | .delete("/", this::deleteAll) 32 | .get("/:id", this::getTodo) 33 | .delete("/:id", this::deleteTodo) 34 | .routeMatcher().addRoute("/:id", this::patchTodo, HttpMethod.PATCH); 35 | } 36 | 37 | private void getAll(RouteContext context) { 38 | List todos = this.todoService.findAll().stream() 39 | .map(this::wrap) 40 | .collect(Collectors.toList()); 41 | 42 | context.json(todos); 43 | } 44 | 45 | private void getTodo(RouteContext context) { 46 | UUID id = UUID.fromString(context.pathString(ID_PATH_PARAM)); 47 | 48 | this.todoService.findById(id) 49 | .map(this::wrap) 50 | .ifPresentOrElse(context::json, () -> context.status(404)); 51 | } 52 | 53 | private void createTodo(RouteContext context) { 54 | var command = context.request().bindWithBody(CreateTodoCommand.class); 55 | 56 | Todo result = this.todoService.createNewTodo(command.getTitle(), command.getOrder()); 57 | context.json(this.wrap(result)); 58 | } 59 | 60 | private void deleteAll(RouteContext context) { 61 | this.todoService.deleteAll(); 62 | context.status(204); 63 | } 64 | 65 | private void patchTodo(RouteContext context) { 66 | UUID id = UUID.fromString(context.pathString(ID_PATH_PARAM)); 67 | 68 | var command = context.request().bindWithBody(PartialTodo.class); 69 | 70 | this.todoService.updateTodo(id, command) 71 | .map(this::wrap) 72 | .ifPresentOrElse(context::json, () -> context.status(404)); 73 | } 74 | 75 | private void deleteTodo(RouteContext context) { 76 | UUID id = UUID.fromString(context.pathString(ID_PATH_PARAM)); 77 | this.todoService.delete(id); 78 | context.status(204); 79 | } 80 | 81 | private TodoDTO wrap(Todo todo) { 82 | return TodoDTO.from(todo, createUrl(todo)); 83 | } 84 | 85 | private String createUrl(Todo todo) { 86 | return this.baseUrl + "/" + todo.getId().toString(); 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /kumuluzee/src/main/java/nl/jaapcoomans/demo/microframeworks/kumuluzee/TodoController.java: -------------------------------------------------------------------------------- 1 | package nl.jaapcoomans.demo.microframeworks.kumuluzee; 2 | 3 | import com.kumuluz.ee.cors.annotations.CrossOrigin; 4 | import nl.jaapcoomans.demo.microframeworks.todo.api.CreateTodoCommand; 5 | import nl.jaapcoomans.demo.microframeworks.todo.api.TodoDTO; 6 | import nl.jaapcoomans.demo.microframeworks.todo.domain.PartialTodo; 7 | import nl.jaapcoomans.demo.microframeworks.todo.domain.Todo; 8 | import nl.jaapcoomans.demo.microframeworks.todo.domain.TodoService; 9 | 10 | import javax.inject.Inject; 11 | import javax.ws.rs.Consumes; 12 | import javax.ws.rs.DELETE; 13 | import javax.ws.rs.GET; 14 | import javax.ws.rs.PATCH; 15 | import javax.ws.rs.POST; 16 | import javax.ws.rs.Path; 17 | import javax.ws.rs.PathParam; 18 | import javax.ws.rs.Produces; 19 | import javax.ws.rs.core.MediaType; 20 | import javax.ws.rs.core.Response; 21 | import java.util.List; 22 | import java.util.UUID; 23 | import java.util.stream.Collectors; 24 | 25 | @Path("/todos") 26 | @CrossOrigin(allowOrigin = "*", allowGenericHttpRequests = true) 27 | public class TodoController { 28 | private TodoService todoService; 29 | 30 | private String baseUrl = "http://localhost:8080/todos"; 31 | 32 | @Inject 33 | public TodoController(TodoService todoService) { 34 | this.todoService = todoService; 35 | } 36 | 37 | @GET 38 | @Produces(MediaType.APPLICATION_JSON) 39 | public List getAll() { 40 | return this.todoService.findAll().stream() 41 | .map(this::wrap) 42 | .collect(Collectors.toList()); 43 | } 44 | 45 | @GET 46 | @Path("/{id}") 47 | @Produces(MediaType.APPLICATION_JSON) 48 | public Response getTodo(@PathParam("id") String id) { 49 | return this.todoService.findById(UUID.fromString(id)) 50 | .map(this::wrap) 51 | .map(body -> Response.ok(body).build()) 52 | .orElse(Response.status(Response.Status.NOT_FOUND).build()); 53 | } 54 | 55 | @POST 56 | @Produces(MediaType.APPLICATION_JSON) 57 | @Consumes(MediaType.APPLICATION_JSON) 58 | public TodoDTO createTodo(CreateTodoCommand command) { 59 | Todo result = this.todoService.createNewTodo(command.getTitle(), command.getOrder()); 60 | return this.wrap(result); 61 | } 62 | 63 | @DELETE 64 | public Response deleteAll() { 65 | this.todoService.deleteAll(); 66 | return Response.noContent().build(); 67 | } 68 | 69 | @PATCH 70 | @Path("/{id}") 71 | @Produces(MediaType.APPLICATION_JSON) 72 | @Consumes(MediaType.APPLICATION_JSON) 73 | public TodoDTO patchTodo(@PathParam("id") String id, PartialTodo command) { 74 | return this.todoService.updateTodo(UUID.fromString(id), command) 75 | .map(this::wrap) 76 | .orElse(null); 77 | } 78 | 79 | @DELETE 80 | @Path("/{id}") 81 | public Response deleteTodo(@PathParam("id") UUID id) { 82 | this.todoService.delete(id); 83 | return Response.noContent().build(); 84 | } 85 | 86 | private TodoDTO wrap(Todo todo) { 87 | return TodoDTO.from(todo, createUrl(todo)); 88 | } 89 | 90 | private String createUrl(Todo todo) { 91 | return this.baseUrl + "/" + todo.getId().toString(); 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /ktor/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | ktor 7 | jar 8 | 9 | 10 | nl.jaapcoomans.demo.microframeworks 11 | parent 12 | 0.0.1-SNAPSHOT 13 | 14 | 15 | 16 | 1.5.4 17 | 18 | 19 | 20 | 21 | jcenter 22 | https://jcenter.bintray.com 23 | 24 | 25 | 26 | 27 | 28 | 29 | io.ktor 30 | ktor-server-core 31 | ${ktor.version} 32 | 33 | 34 | io.ktor 35 | ktor-server-netty 36 | ${ktor.version} 37 | 38 | 39 | io.ktor 40 | ktor-jackson 41 | ${ktor.version} 42 | 43 | 44 | 45 | 46 | nl.jaapcoomans.demo.microframeworks 47 | todo-backend 48 | 0.0.1-SNAPSHOT 49 | 50 | 51 | 52 | 53 | org.jetbrains.kotlin 54 | kotlin-stdlib 55 | 56 | 57 | 58 | 59 | org.slf4j 60 | slf4j-simple 61 | 62 | 63 | 64 | 65 | org.junit.jupiter 66 | junit-jupiter-api 67 | 68 | 69 | org.junit.jupiter 70 | junit-jupiter-engine 71 | 72 | 73 | 74 | 75 | src/main/kotlin 76 | src/test/kotlin 77 | 78 | 79 | 80 | org.jetbrains.kotlin 81 | kotlin-maven-plugin 82 | 83 | 84 | org.apache.maven.plugins 85 | maven-jar-plugin 86 | 87 | 88 | org.apache.maven.plugins 89 | maven-dependency-plugin 90 | 91 | 92 | 93 | 94 | -------------------------------------------------------------------------------- /pippo/src/main/java/nl/jaapcoomans/demo/microframeworks/pippo/TodoRestController.java: -------------------------------------------------------------------------------- 1 | package nl.jaapcoomans.demo.microframeworks.pippo; 2 | 3 | import nl.jaapcoomans.demo.microframeworks.todo.api.CreateTodoCommand; 4 | import nl.jaapcoomans.demo.microframeworks.todo.api.TodoDTO; 5 | import nl.jaapcoomans.demo.microframeworks.todo.domain.PartialTodo; 6 | import nl.jaapcoomans.demo.microframeworks.todo.domain.Todo; 7 | import nl.jaapcoomans.demo.microframeworks.todo.domain.TodoService; 8 | import ro.pippo.core.Pippo; 9 | import ro.pippo.core.route.RouteContext; 10 | 11 | import java.util.List; 12 | import java.util.UUID; 13 | import java.util.stream.Collectors; 14 | 15 | public class TodoRestController { 16 | private static final String ID_PATH_PARAM = "id"; 17 | 18 | private TodoService todoService; 19 | private String baseUrl; 20 | 21 | TodoRestController(TodoService todoService, String baseUrl) { 22 | this.todoService = todoService; 23 | this.baseUrl = baseUrl; 24 | } 25 | 26 | void initializeRoutes(Pippo pippo) { 27 | pippo.GET("/todos", this::getAll); 28 | pippo.POST("/todos", this::createTodo); 29 | pippo.DELETE("/todos", this::deleteAll); 30 | pippo.GET("/todos/{id}", this::getTodo); 31 | pippo.PATCH("/todos/{id}", this::patchTodo); 32 | pippo.DELETE("/todos/{id}", this::deleteTodo); 33 | } 34 | 35 | private void getAll(RouteContext context) { 36 | List todos = this.todoService.findAll().stream() 37 | .map(this::wrap) 38 | .collect(Collectors.toList()); 39 | 40 | context.json().send(todos); 41 | } 42 | 43 | private void getTodo(RouteContext context) { 44 | UUID id = UUID.fromString(context.getParameter(ID_PATH_PARAM).toString()); 45 | 46 | this.todoService.findById(id) 47 | .map(this::wrap) 48 | .ifPresentOrElse(context.json()::send, () -> context.status(404)); 49 | } 50 | 51 | private void createTodo(RouteContext context) { 52 | var command = context.createEntityFromBody(CreateTodoCommand.class); 53 | 54 | Todo result = this.todoService.createNewTodo(command.getTitle(), command.getOrder()); 55 | context.json().send(this.wrap(result)); 56 | } 57 | 58 | private void deleteAll(RouteContext context) { 59 | this.todoService.deleteAll(); 60 | context.status(204); 61 | } 62 | 63 | private void patchTodo(RouteContext context) { 64 | var id = UUID.fromString(context.getParameter(ID_PATH_PARAM) 65 | .toString()); 66 | 67 | var command = context.createEntityFromBody(PartialTodo.class); 68 | 69 | this.todoService.updateTodo(id, command) 70 | .map(this::wrap) 71 | .ifPresentOrElse(context.json()::send, 72 | () -> context.status(404)); 73 | } 74 | 75 | private void deleteTodo(RouteContext context) { 76 | UUID id = UUID.fromString(context.getParameter(ID_PATH_PARAM).toString()); 77 | this.todoService.delete(id); 78 | context.status(204); 79 | } 80 | 81 | private TodoDTO wrap(Todo todo) { 82 | return TodoDTO.from(todo, createUrl(todo)); 83 | } 84 | 85 | private String createUrl(Todo todo) { 86 | return this.baseUrl + "/" + todo.getId().toString(); 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /javalin/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | javalin 7 | jar 8 | 9 | 10 | nl.jaapcoomans.demo.microframeworks 11 | parent 12 | 0.0.1-SNAPSHOT 13 | 14 | 15 | 16 | 3.13.7 17 | 2.13.4.2 18 | 19 | 20 | 21 | 22 | 23 | io.javalin 24 | javalin 25 | ${javalin.version} 26 | 27 | 28 | 29 | 30 | nl.jaapcoomans.demo.microframeworks 31 | todo-backend 32 | 0.0.1-SNAPSHOT 33 | 34 | 35 | 36 | 37 | org.slf4j 38 | slf4j-simple 39 | 40 | 41 | 42 | 43 | com.fasterxml.jackson.core 44 | jackson-databind 45 | ${jackson.version} 46 | 47 | 48 | com.fasterxml.jackson.datatype 49 | jackson-datatype-jsr310 50 | ${jackson.version} 51 | 52 | 53 | com.fasterxml.jackson.module 54 | jackson-module-parameter-names 55 | ${jackson.version} 56 | 57 | 58 | 59 | 60 | org.junit.jupiter 61 | junit-jupiter-api 62 | 63 | 64 | org.junit.jupiter 65 | junit-jupiter-engine 66 | 67 | 68 | org.mockito 69 | mockito-junit-jupiter 70 | 71 | 72 | org.assertj 73 | assertj-core 74 | 75 | 76 | 77 | 78 | 79 | 80 | org.apache.maven.plugins 81 | maven-jar-plugin 82 | 83 | 84 | org.apache.maven.plugins 85 | maven-dependency-plugin 86 | 87 | 88 | 89 | 90 | -------------------------------------------------------------------------------- /jooby/src/main/java/nl/jaapcoomans/demo/microframeworks/jooby/TodoRestController.java: -------------------------------------------------------------------------------- 1 | package nl.jaapcoomans.demo.microframeworks.jooby; 2 | 3 | import io.jooby.Context; 4 | import io.jooby.Jooby; 5 | import io.jooby.StatusCode; 6 | import io.jooby.exception.StatusCodeException; 7 | import nl.jaapcoomans.demo.microframeworks.todo.api.CreateTodoCommand; 8 | import nl.jaapcoomans.demo.microframeworks.todo.api.TodoDTO; 9 | import nl.jaapcoomans.demo.microframeworks.todo.domain.PartialTodo; 10 | import nl.jaapcoomans.demo.microframeworks.todo.domain.Todo; 11 | import nl.jaapcoomans.demo.microframeworks.todo.domain.TodoService; 12 | 13 | import javax.annotation.Nonnull; 14 | import java.util.List; 15 | import java.util.UUID; 16 | import java.util.stream.Collectors; 17 | 18 | public class TodoRestController { 19 | private static final String ID_PATH_PARAM = "id"; 20 | 21 | private TodoService todoService; 22 | private String baseUrl; 23 | 24 | TodoRestController(TodoService todoService, String baseUrl) { 25 | this.todoService = todoService; 26 | this.baseUrl = baseUrl; 27 | } 28 | 29 | void initializeRoutes(Jooby app) { 30 | app.get("/todos", this::getAll); 31 | app.post("/todos", this::createTodo); 32 | app.delete("/todos", this::deleteAll); 33 | app.get("/todos/{id}", this::getTodo); 34 | app.patch("/todos/{id}", this::patchTodo); 35 | app.delete("/todos/{id}", this::deleteTodo); 36 | } 37 | 38 | @Nonnull 39 | private List getAll(Context context) { 40 | return this.todoService.findAll().stream() 41 | .map(this::wrap) 42 | .collect(Collectors.toList()); 43 | } 44 | 45 | @Nonnull 46 | private TodoDTO getTodo(Context context) { 47 | UUID id = UUID.fromString(context.path(ID_PATH_PARAM).value()); 48 | 49 | return this.todoService.findById(id) 50 | .map(this::wrap) 51 | .orElseThrow(() -> new StatusCodeException(StatusCode.NOT_FOUND)); 52 | } 53 | 54 | private TodoDTO createTodo(Context context) { 55 | var command = context.body(CreateTodoCommand.class); 56 | 57 | Todo result = this.todoService.createNewTodo(command.getTitle(), command.getOrder()); 58 | return this.wrap(result); 59 | } 60 | 61 | private Context deleteAll(Context context) { 62 | this.todoService.deleteAll(); 63 | context.send(StatusCode.NO_CONTENT); 64 | return context; 65 | } 66 | 67 | @Nonnull 68 | private TodoDTO patchTodo(Context context) { 69 | UUID id = UUID.fromString(context.path(ID_PATH_PARAM).value()); 70 | 71 | var command = context.body(PartialTodo.class); 72 | 73 | return this.todoService.updateTodo(id, command) 74 | .map(this::wrap) 75 | .orElseThrow(() -> new StatusCodeException(StatusCode.NOT_FOUND)); 76 | } 77 | 78 | private Context deleteTodo(Context context) { 79 | UUID id = UUID.fromString(context.path(ID_PATH_PARAM).value()); 80 | this.todoService.delete(id); 81 | context.send(StatusCode.NO_CONTENT); 82 | return context; 83 | } 84 | 85 | private TodoDTO wrap(Todo todo) { 86 | return TodoDTO.from(todo, createUrl(todo)); 87 | } 88 | 89 | private String createUrl(Todo todo) { 90 | return this.baseUrl + "/" + todo.getId().toString(); 91 | } 92 | 93 | } 94 | -------------------------------------------------------------------------------- /javalin/src/main/java/nl/jaapcoomans/microframeworks/javalin/TodoRestController.java: -------------------------------------------------------------------------------- 1 | package nl.jaapcoomans.microframeworks.javalin; 2 | 3 | import io.javalin.apibuilder.EndpointGroup; 4 | import io.javalin.http.Context; 5 | import nl.jaapcoomans.demo.microframeworks.todo.api.CreateTodoCommand; 6 | import nl.jaapcoomans.demo.microframeworks.todo.api.TodoDTO; 7 | import nl.jaapcoomans.demo.microframeworks.todo.domain.PartialTodo; 8 | import nl.jaapcoomans.demo.microframeworks.todo.domain.Todo; 9 | import nl.jaapcoomans.demo.microframeworks.todo.domain.TodoService; 10 | 11 | import java.util.List; 12 | import java.util.UUID; 13 | import java.util.stream.Collectors; 14 | 15 | import static io.javalin.apibuilder.ApiBuilder.delete; 16 | import static io.javalin.apibuilder.ApiBuilder.get; 17 | import static io.javalin.apibuilder.ApiBuilder.patch; 18 | import static io.javalin.apibuilder.ApiBuilder.post; 19 | 20 | class TodoRestController { 21 | private static final String ID_PATH_PARAM = "id"; 22 | private static final String ID_PATH = "/:" + ID_PATH_PARAM; 23 | 24 | private TodoService todoService; 25 | private String baseUrl; 26 | 27 | TodoRestController(TodoService todoService, String baseUrl) { 28 | this.todoService = todoService; 29 | this.baseUrl = baseUrl; 30 | } 31 | 32 | EndpointGroup defineEndpoints() { 33 | return () -> { 34 | get(this::getAll); 35 | post(this::createTodo); 36 | delete(this::deleteAll); 37 | get(ID_PATH, this::getTodo); 38 | patch(ID_PATH, this::patchTodo); 39 | delete(ID_PATH, this::deleteTodo); 40 | }; 41 | } 42 | 43 | void getAll(Context context) { 44 | List todos = this.todoService.findAll().stream() 45 | .map(this::wrap) 46 | .collect(Collectors.toList()); 47 | 48 | context.json(todos); 49 | } 50 | 51 | void getTodo(Context context) { 52 | UUID id = UUID.fromString(context.pathParam(ID_PATH_PARAM)); 53 | 54 | this.todoService.findById(id) 55 | .map(this::wrap) 56 | .ifPresentOrElse(context::json, () -> context.status(404)); 57 | } 58 | 59 | void createTodo(Context context) { 60 | var command = context.bodyAsClass(CreateTodoCommand.class); 61 | 62 | Todo result = this.todoService.createNewTodo(command.getTitle(), command.getOrder()); 63 | context.json(this.wrap(result)); 64 | } 65 | 66 | void deleteAll(Context context) { 67 | this.todoService.deleteAll(); 68 | context.status(204); 69 | } 70 | 71 | void patchTodo(Context context) { 72 | UUID id = UUID.fromString(context.pathParam(ID_PATH_PARAM)); 73 | 74 | var command = context.bodyAsClass(PartialTodo.class); 75 | 76 | this.todoService.updateTodo(id, command) 77 | .map(this::wrap) 78 | .ifPresentOrElse(context::json, () -> context.status(404)); 79 | } 80 | 81 | void deleteTodo(Context context) { 82 | UUID id = UUID.fromString(context.pathParam(ID_PATH_PARAM)); 83 | this.todoService.delete(id); 84 | context.status(204); 85 | } 86 | 87 | private TodoDTO wrap(Todo todo) { 88 | return TodoDTO.from(todo, createUrl(todo)); 89 | } 90 | 91 | private String createUrl(Todo todo) { 92 | return this.baseUrl + "/" + todo.getId().toString(); 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /micronaut/src/main/java/nl/jaapcoomans/demo/microframeworks/micronaut/TodoController.java: -------------------------------------------------------------------------------- 1 | package nl.jaapcoomans.demo.microframeworks.micronaut; 2 | 3 | import io.micronaut.context.annotation.Value; 4 | import io.micronaut.http.HttpResponse; 5 | import io.micronaut.http.HttpResponseFactory; 6 | import io.micronaut.http.HttpStatus; 7 | import io.micronaut.http.MediaType; 8 | import io.micronaut.http.annotation.Controller; 9 | import io.micronaut.http.annotation.Delete; 10 | import io.micronaut.http.annotation.Get; 11 | import io.micronaut.http.annotation.Patch; 12 | import io.micronaut.http.annotation.PathVariable; 13 | import io.micronaut.http.annotation.Post; 14 | import nl.jaapcoomans.demo.microframeworks.todo.api.CreateTodoCommand; 15 | import nl.jaapcoomans.demo.microframeworks.todo.api.TodoDTO; 16 | import nl.jaapcoomans.demo.microframeworks.todo.domain.PartialTodo; 17 | import nl.jaapcoomans.demo.microframeworks.todo.domain.Todo; 18 | import nl.jaapcoomans.demo.microframeworks.todo.domain.TodoService; 19 | 20 | import javax.inject.Inject; 21 | import java.util.List; 22 | import java.util.Optional; 23 | import java.util.UUID; 24 | import java.util.stream.Collectors; 25 | 26 | @Controller("/todos") 27 | public class TodoController { 28 | private TodoService todoService; 29 | private String baseUrl; 30 | 31 | @Inject 32 | public TodoController(TodoService todoService, @Value("${todos.baseUrl}") String baseUrl) { 33 | this.todoService = todoService; 34 | this.baseUrl = baseUrl; 35 | } 36 | 37 | @Get(produces = MediaType.APPLICATION_JSON) 38 | public List getAll() { 39 | return this.todoService.findAll().stream() 40 | .map(this::wrap) 41 | .collect(Collectors.toList()); 42 | } 43 | 44 | @Get(uri = "/{id}", produces = MediaType.APPLICATION_JSON) 45 | public HttpResponse getTodo(@PathVariable String id) { 46 | return this.todoService.findById(UUID.fromString(id)) 47 | .map(this::wrap) 48 | .map(HttpResponseFactory.INSTANCE::ok) 49 | .orElse(HttpResponseFactory.INSTANCE.status(HttpStatus.NOT_FOUND)); 50 | } 51 | 52 | @Post(produces = MediaType.APPLICATION_JSON, consumes = MediaType.APPLICATION_JSON) 53 | public TodoDTO createTodo(CreateTodoCommand command) { 54 | Todo result = this.todoService.createNewTodo(command.getTitle(), command.getOrder()); 55 | return this.wrap(result); 56 | } 57 | 58 | @Delete 59 | public HttpResponse deleteAll() { 60 | this.todoService.deleteAll(); 61 | return HttpResponseFactory.INSTANCE.status(HttpStatus.NO_CONTENT); 62 | } 63 | 64 | @Patch(uri = "/{id}", 65 | produces = MediaType.APPLICATION_JSON, 66 | consumes = MediaType.APPLICATION_JSON) 67 | public Optional patchTodo(@PathVariable String id, 68 | PartialTodo command) { 69 | 70 | return this.todoService.updateTodo(UUID.fromString(id), command) 71 | .map(this::wrap); 72 | } 73 | 74 | @Delete(uri = "/{id}") 75 | public HttpResponse deleteTodo(@PathVariable UUID id) { 76 | this.todoService.delete(id); 77 | return HttpResponseFactory.INSTANCE.status(HttpStatus.NO_CONTENT); 78 | } 79 | 80 | private TodoDTO wrap(Todo todo) { 81 | return TodoDTO.from(todo, createUrl(todo)); 82 | } 83 | 84 | private String createUrl(Todo todo) { 85 | return this.baseUrl + "/" + todo.getId().toString(); 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /quarkus/src/main/java/nl/jaapcoomans/microframeworks/quarkus/TodoController.java: -------------------------------------------------------------------------------- 1 | package nl.jaapcoomans.microframeworks.quarkus; 2 | 3 | import nl.jaapcoomans.demo.microframeworks.todo.api.CreateTodoCommand; 4 | import nl.jaapcoomans.demo.microframeworks.todo.api.TodoDTO; 5 | import nl.jaapcoomans.demo.microframeworks.todo.domain.PartialTodo; 6 | import nl.jaapcoomans.demo.microframeworks.todo.domain.Todo; 7 | import nl.jaapcoomans.demo.microframeworks.todo.domain.TodoService; 8 | 9 | import javax.enterprise.context.RequestScoped; 10 | import javax.inject.Inject; 11 | import javax.ws.rs.Consumes; 12 | import javax.ws.rs.DELETE; 13 | import javax.ws.rs.GET; 14 | import javax.ws.rs.OPTIONS; 15 | import javax.ws.rs.PATCH; 16 | import javax.ws.rs.POST; 17 | import javax.ws.rs.Path; 18 | import javax.ws.rs.PathParam; 19 | import javax.ws.rs.Produces; 20 | import javax.ws.rs.core.Context; 21 | import javax.ws.rs.core.MediaType; 22 | import javax.ws.rs.core.Response; 23 | import javax.ws.rs.core.UriBuilder; 24 | import javax.ws.rs.core.UriInfo; 25 | import java.util.List; 26 | import java.util.UUID; 27 | import java.util.stream.Collectors; 28 | 29 | @Path("/todos") 30 | public class TodoController { 31 | private final TodoService todoService; 32 | 33 | @Context 34 | private UriInfo uriInfo; 35 | 36 | @Inject 37 | public TodoController(TodoService todoService) { 38 | this.todoService = todoService; 39 | } 40 | 41 | @GET 42 | @Produces(MediaType.APPLICATION_JSON) 43 | public List getAll() { 44 | return this.todoService.findAll().stream() 45 | .map(this::wrap) 46 | .collect(Collectors.toList()); 47 | } 48 | 49 | @GET 50 | @Path("/{id}") 51 | @Produces(MediaType.APPLICATION_JSON) 52 | public Response getTodo(@PathParam("id") String id) { 53 | return this.todoService.findById(UUID.fromString(id)) 54 | .map(this::wrap) 55 | .map(body -> Response.ok(body).build()) 56 | .orElse(Response.status(Response.Status.NOT_FOUND).build()); 57 | } 58 | 59 | @POST 60 | @Produces(MediaType.APPLICATION_JSON) 61 | @Consumes(MediaType.APPLICATION_JSON) 62 | public TodoDTO createTodo(CreateTodoCommand command) { 63 | Todo result = this.todoService.createNewTodo(command.getTitle(), command.getOrder()); 64 | return this.wrap(result); 65 | } 66 | 67 | @DELETE 68 | public Response deleteAll() { 69 | this.todoService.deleteAll(); 70 | return Response.noContent().build(); 71 | } 72 | 73 | @PATCH 74 | @Path("/{id}") 75 | @Produces(MediaType.APPLICATION_JSON) 76 | @Consumes(MediaType.APPLICATION_JSON) 77 | public TodoDTO patchTodo(@PathParam("id") String id, PartialTodo command) { 78 | return this.todoService.updateTodo(UUID.fromString(id), command) 79 | .map(this::wrap) 80 | .orElse(null); 81 | } 82 | 83 | @DELETE 84 | @Path("/{id}") 85 | public Response deleteTodo(@PathParam("id") UUID id) { 86 | this.todoService.delete(id); 87 | return Response.noContent().build(); 88 | } 89 | 90 | private TodoDTO wrap(Todo todo) { 91 | return TodoDTO.from(todo, createUrl(todo)); 92 | } 93 | 94 | private String createUrl(Todo todo) { 95 | return this.uriInfo.getBaseUriBuilder() 96 | .path("todos") 97 | .path(todo.getId().toString()) 98 | .build() 99 | .toString(); 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /spark/src/main/java/nl/jaapcoomans/microframeworks/spark/TodoRestController.java: -------------------------------------------------------------------------------- 1 | package nl.jaapcoomans.microframeworks.spark; 2 | 3 | import com.fasterxml.jackson.databind.ObjectMapper; 4 | import nl.jaapcoomans.demo.microframeworks.todo.api.CreateTodoCommand; 5 | import nl.jaapcoomans.demo.microframeworks.todo.api.TodoDTO; 6 | import nl.jaapcoomans.demo.microframeworks.todo.domain.PartialTodo; 7 | import nl.jaapcoomans.demo.microframeworks.todo.domain.Todo; 8 | import nl.jaapcoomans.demo.microframeworks.todo.domain.TodoService; 9 | import org.eclipse.jetty.http.HttpStatus; 10 | import spark.Request; 11 | import spark.Response; 12 | 13 | import java.io.IOException; 14 | import java.util.List; 15 | import java.util.Optional; 16 | import java.util.UUID; 17 | import java.util.stream.Collectors; 18 | 19 | class TodoRestController { 20 | private static final String ID_PATH_PARAM = "id"; 21 | 22 | private TodoService todoService; 23 | private String baseUrl; 24 | 25 | private ObjectMapper objectMapper = new ObjectMapper(); 26 | 27 | TodoRestController(TodoService todoService, String baseUrl) { 28 | this.todoService = todoService; 29 | this.baseUrl = baseUrl; 30 | } 31 | 32 | List getAll(Request request, Response response) { 33 | response.header("Content-Type", "application/json"); 34 | 35 | return this.todoService.findAll().stream() 36 | .map(this::wrap) 37 | .collect(Collectors.toList()); 38 | } 39 | 40 | 41 | Optional getTodo(Request request, Response response) { 42 | UUID id = UUID.fromString(request.params(ID_PATH_PARAM)); 43 | 44 | response.header("Content-Type", "application/json"); 45 | 46 | var result = this.todoService.findById(id) 47 | .map(this::wrap); 48 | 49 | if (result.isEmpty()) { 50 | response.status(404); 51 | } 52 | 53 | return result; 54 | } 55 | 56 | 57 | TodoDTO createTodo(Request request, Response response) throws Exception { 58 | var command = deserialize(request.body(), CreateTodoCommand.class); 59 | 60 | response.header("Content-Type", "application/json"); 61 | 62 | Todo result = this.todoService.createNewTodo(command.getTitle(), command.getOrder()); 63 | return this.wrap(result); 64 | } 65 | 66 | String deleteAll(Request request, Response response) { 67 | this.todoService.deleteAll(); 68 | response.status(HttpStatus.NO_CONTENT_204); 69 | return ""; 70 | } 71 | 72 | 73 | Optional patchTodo(Request request, Response response) { 74 | var id = UUID.fromString(request.params(ID_PATH_PARAM)); 75 | var command = deserialize(request.body(), PartialTodo.class); 76 | 77 | response.header("Content-Type", "application/json"); 78 | 79 | return this.todoService.updateTodo(id, command) 80 | .map(this::wrap); 81 | } 82 | 83 | String deleteTodo(Request request, Response response) { 84 | UUID id = UUID.fromString(request.params(ID_PATH_PARAM)); 85 | this.todoService.delete(id); 86 | 87 | response.status(204); 88 | return ""; 89 | } 90 | 91 | private T deserialize(String json, Class clazz) { 92 | try { 93 | return objectMapper.readValue(json, clazz); 94 | } catch (IOException e) { 95 | throw new RuntimeException("Failed to serialize to JSON"); 96 | } 97 | } 98 | 99 | private TodoDTO wrap(Todo todo) { 100 | return TodoDTO.from(todo, createUrl(todo)); 101 | } 102 | 103 | private String createUrl(Todo todo) { 104 | return this.baseUrl + "/" + todo.getId().toString(); 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /helidon-se/src/main/java/nl/jaapcoomans/demo/microframeworks/helidon/se/TodoRestController.java: -------------------------------------------------------------------------------- 1 | package nl.jaapcoomans.demo.microframeworks.helidon.se; 2 | 3 | import io.helidon.common.http.Http; 4 | import io.helidon.webserver.Handler; 5 | import io.helidon.webserver.Routing; 6 | import io.helidon.webserver.ServerRequest; 7 | import io.helidon.webserver.ServerResponse; 8 | import io.helidon.webserver.Service; 9 | import nl.jaapcoomans.demo.microframeworks.todo.api.CreateTodoCommand; 10 | import nl.jaapcoomans.demo.microframeworks.todo.api.TodoDTO; 11 | import nl.jaapcoomans.demo.microframeworks.todo.domain.PartialTodo; 12 | import nl.jaapcoomans.demo.microframeworks.todo.domain.Todo; 13 | import nl.jaapcoomans.demo.microframeworks.todo.domain.TodoService; 14 | 15 | import java.util.List; 16 | import java.util.UUID; 17 | import java.util.stream.Collectors; 18 | 19 | import static io.helidon.common.http.Http.Status.NOT_FOUND_404; 20 | import static io.helidon.common.http.Http.Status.NO_CONTENT_204; 21 | 22 | public class TodoRestController implements Service { 23 | private static final String ID_PATH_PARAM = "id"; 24 | 25 | private static final Http.RequestMethod PATCH = Http.RequestMethod.create("PATCH"); 26 | 27 | private final TodoService todoService; 28 | private final String baseUrl; 29 | 30 | TodoRestController(TodoService todoService, String baseUrl) { 31 | this.todoService = todoService; 32 | this.baseUrl = baseUrl; 33 | } 34 | 35 | @Override 36 | public void update(Routing.Rules rules) { 37 | rules.get("/", this::getAll) 38 | .post("/", Handler.create(CreateTodoCommand.class, this::createTodo)) 39 | .delete("/", this::deleteAll) 40 | .get("/{id}", this::getTodo) 41 | .delete("/{id}", this::deleteTodo) 42 | .anyOf(List.of(PATCH), "/{id}", Handler.create(PartialTodo.class, this::patchTodo)); 43 | } 44 | 45 | private void getAll(ServerRequest request, ServerResponse response) { 46 | List todos = this.todoService.findAll().stream() 47 | .map(this::wrap) 48 | .collect(Collectors.toList()); 49 | 50 | response.send(todos); 51 | } 52 | 53 | private void getTodo(ServerRequest request, ServerResponse response) { 54 | UUID id = UUID.fromString(request.path().param(ID_PATH_PARAM)); 55 | 56 | this.todoService.findById(id) 57 | .map(this::wrap) 58 | .ifPresentOrElse(response::send, 59 | () -> response.status(NOT_FOUND_404).send()); 60 | } 61 | 62 | private void createTodo(ServerRequest request, ServerResponse response, CreateTodoCommand command) { 63 | Todo result = this.todoService.createNewTodo(command.getTitle(), command.getOrder()); 64 | response.send(this.wrap(result)); 65 | } 66 | 67 | private void deleteAll(ServerRequest request, ServerResponse response) { 68 | this.todoService.deleteAll(); 69 | response.status(NO_CONTENT_204).send(); 70 | } 71 | 72 | private void patchTodo(ServerRequest request, ServerResponse response, PartialTodo command) { 73 | UUID id = UUID.fromString(request.path().param(ID_PATH_PARAM)); 74 | 75 | this.todoService.updateTodo(id, command) 76 | .map(this::wrap) 77 | .ifPresentOrElse(response::send, () -> response.status(NOT_FOUND_404).send()); 78 | } 79 | 80 | private void deleteTodo(ServerRequest request, ServerResponse response) { 81 | UUID id = UUID.fromString(request.path().param(ID_PATH_PARAM)); 82 | this.todoService.delete(id); 83 | response.status(NO_CONTENT_204).send(); 84 | } 85 | 86 | private TodoDTO wrap(Todo todo) { 87 | return TodoDTO.from(todo, createUrl(todo)); 88 | } 89 | 90 | private String createUrl(Todo todo) { 91 | return this.baseUrl + "/" + todo.getId().toString(); 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /minijax/src/main/java/nl/jaapcoomans/microframeworks/minijax/TodoController.java: -------------------------------------------------------------------------------- 1 | package nl.jaapcoomans.microframeworks.minijax; 2 | 3 | import jakarta.inject.Inject; 4 | import jakarta.ws.rs.Consumes; 5 | import jakarta.ws.rs.DELETE; 6 | import jakarta.ws.rs.GET; 7 | import jakarta.ws.rs.OPTIONS; 8 | import jakarta.ws.rs.PATCH; 9 | import jakarta.ws.rs.POST; 10 | import jakarta.ws.rs.Path; 11 | import jakarta.ws.rs.PathParam; 12 | import jakarta.ws.rs.Produces; 13 | import jakarta.ws.rs.core.MediaType; 14 | import jakarta.ws.rs.core.Response; 15 | import nl.jaapcoomans.demo.microframeworks.todo.api.CreateTodoCommand; 16 | import nl.jaapcoomans.demo.microframeworks.todo.api.TodoDTO; 17 | import nl.jaapcoomans.demo.microframeworks.todo.domain.PartialTodo; 18 | import nl.jaapcoomans.demo.microframeworks.todo.domain.Todo; 19 | import nl.jaapcoomans.demo.microframeworks.todo.domain.TodoService; 20 | 21 | import java.util.List; 22 | import java.util.Optional; 23 | import java.util.UUID; 24 | import java.util.stream.Collectors; 25 | 26 | @Path("/todos") 27 | public class TodoController { 28 | private TodoService todoService; 29 | 30 | private String baseUrl = "http://localhost:8080/todos"; 31 | 32 | @Inject 33 | public TodoController(TodoService todoService) { 34 | this.todoService = todoService; 35 | } 36 | 37 | @OPTIONS 38 | public Response options() { 39 | return corsResponse(); 40 | } 41 | 42 | @OPTIONS 43 | @Path("{id:.*}") 44 | public Response options2() { 45 | return corsResponse(); 46 | } 47 | 48 | private static Response corsResponse() { 49 | return Response.ok() 50 | .header("Access-Control-Allow-Origin", "*") 51 | .header("Access-Control-Allow-Headers", "content-type") 52 | .header("Access-Control-Allow-Credentials", "true") 53 | .header("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS, HEAD") 54 | .build(); 55 | } 56 | 57 | @GET 58 | @Produces(MediaType.APPLICATION_JSON) 59 | public List getAll() { 60 | return this.todoService.findAll().stream() 61 | .map(this::wrap) 62 | .collect(Collectors.toList()); 63 | } 64 | 65 | @GET 66 | @Path("/{id}") 67 | @Produces(MediaType.APPLICATION_JSON) 68 | public TodoDTO getTodo(@PathParam("id") String id) { 69 | return this.todoService.findById(UUID.fromString(id)) 70 | .map(this::wrap) 71 | .orElse(null); 72 | } 73 | 74 | @POST 75 | @Produces(MediaType.APPLICATION_JSON) 76 | @Consumes(MediaType.APPLICATION_JSON) 77 | public TodoDTO createTodo(CreateTodoCommand command) { 78 | Todo result = this.todoService.createNewTodo(command.getTitle(), command.getOrder()); 79 | return this.wrap(result); 80 | } 81 | 82 | @DELETE 83 | public Response deleteAll() { 84 | this.todoService.deleteAll(); 85 | return Response.noContent().build(); 86 | } 87 | 88 | @PATCH 89 | @Path("/{id}") 90 | @Produces(MediaType.APPLICATION_JSON) 91 | @Consumes(MediaType.APPLICATION_JSON) 92 | public TodoDTO patchTodo(@PathParam("id") String id, PartialTodo command) { 93 | return this.todoService.updateTodo(UUID.fromString(id), command) 94 | .map(this::wrap) 95 | .orElse(null); 96 | } 97 | 98 | @DELETE 99 | @Path("/{id}") 100 | public Response deleteTodo(@PathParam("id") UUID id) { 101 | this.todoService.delete(id); 102 | return Response.noContent().build(); 103 | } 104 | 105 | private TodoDTO wrap(Todo todo) { 106 | return TodoDTO.from(todo, createUrl(todo)); 107 | } 108 | 109 | private String createUrl(Todo todo) { 110 | return this.baseUrl + "/" + todo.getId().toString(); 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /helidon-se/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | nl.jaapcoomans.demo.microframeworks 7 | helidon-se 8 | jar 9 | 0.0.1-SNAPSHOT 10 | 11 | 12 | io.helidon.applications 13 | helidon-se 14 | 2.3.0 15 | 16 | 17 | 18 | 19 | 16 20 | ${java.version} 21 | ${java.version} 22 | 23 | 24 | 25 | 26 | io.helidon.bundles 27 | helidon-bundles-webserver 28 | 29 | 30 | io.helidon.media 31 | helidon-media-jackson 32 | 33 | 34 | io.helidon.webserver 35 | helidon-webserver-cors 36 | 37 | 38 | 39 | 40 | nl.jaapcoomans.demo.microframeworks 41 | todo-backend 42 | 0.0.1-SNAPSHOT 43 | 44 | 45 | 46 | 47 | org.slf4j 48 | slf4j-simple 49 | 50 | 51 | 52 | 53 | org.junit.jupiter 54 | junit-jupiter-api 55 | 56 | 57 | org.junit.jupiter 58 | junit-jupiter-engine 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | org.apache.maven.plugins 67 | maven-jar-plugin 68 | 69 | application 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | org.apache.maven.plugins 78 | maven-dependency-plugin 79 | 3.1.1 80 | 81 | 82 | dependencies 83 | package 84 | 85 | copy-dependencies 86 | 87 | 88 | target/libs 89 | 90 | 91 | 92 | 93 | false 94 | false 95 | true 96 | runtime 97 | 98 | 99 | 100 | 101 | 102 | -------------------------------------------------------------------------------- /helidon-mp/src/main/java/nl/jaapcoomans/microframeworks/helidon/mp/TodoController.java: -------------------------------------------------------------------------------- 1 | package nl.jaapcoomans.microframeworks.helidon.mp; 2 | 3 | import nl.jaapcoomans.demo.microframeworks.todo.api.CreateTodoCommand; 4 | import nl.jaapcoomans.demo.microframeworks.todo.api.TodoDTO; 5 | import nl.jaapcoomans.demo.microframeworks.todo.domain.PartialTodo; 6 | import nl.jaapcoomans.demo.microframeworks.todo.domain.Todo; 7 | import nl.jaapcoomans.demo.microframeworks.todo.domain.TodoService; 8 | 9 | import javax.enterprise.context.RequestScoped; 10 | import javax.inject.Inject; 11 | import javax.ws.rs.Consumes; 12 | import javax.ws.rs.DELETE; 13 | import javax.ws.rs.GET; 14 | import javax.ws.rs.OPTIONS; 15 | import javax.ws.rs.PATCH; 16 | import javax.ws.rs.POST; 17 | import javax.ws.rs.Path; 18 | import javax.ws.rs.PathParam; 19 | import javax.ws.rs.Produces; 20 | import javax.ws.rs.core.MediaType; 21 | import javax.ws.rs.core.Response; 22 | import java.util.List; 23 | import java.util.UUID; 24 | import java.util.stream.Collectors; 25 | 26 | @RequestScoped 27 | @Path("/") 28 | public class TodoController { 29 | private TodoService todoService; 30 | 31 | private String baseUrl = "http://localhost:8080/todos"; 32 | 33 | @Inject 34 | public TodoController(TodoService todoService) { 35 | this.todoService = todoService; 36 | } 37 | 38 | @OPTIONS 39 | public Response options() { 40 | return corsResponse(); 41 | } 42 | 43 | @OPTIONS 44 | @Path("{id:.*}") 45 | public Response options2() { 46 | return corsResponse(); 47 | } 48 | 49 | private static Response corsResponse() { 50 | return Response.ok() 51 | .header("Access-Control-Allow-Origin", "*") 52 | .header("Access-Control-Allow-Headers", "content-type") 53 | .header("Access-Control-Allow-Credentials", "true") 54 | .header("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS, HEAD") 55 | .build(); 56 | } 57 | 58 | 59 | @GET 60 | @Produces(MediaType.APPLICATION_JSON) 61 | public List getAll() { 62 | return this.todoService.findAll().stream() 63 | .map(this::wrap) 64 | .collect(Collectors.toList()); 65 | } 66 | 67 | @GET 68 | @Path("/{id}") 69 | @Produces(MediaType.APPLICATION_JSON) 70 | public Response getTodo(@PathParam("id") String id) { 71 | return this.todoService.findById(UUID.fromString(id)) 72 | .map(this::wrap) 73 | .map(body -> Response.ok(body).build()) 74 | .orElse(Response.status(Response.Status.NOT_FOUND).build()); 75 | } 76 | 77 | @POST 78 | @Produces(MediaType.APPLICATION_JSON) 79 | @Consumes(MediaType.APPLICATION_JSON) 80 | public TodoDTO createTodo(CreateTodoCommand command) { 81 | Todo result = this.todoService.createNewTodo(command.getTitle(), command.getOrder()); 82 | return this.wrap(result); 83 | } 84 | 85 | @DELETE 86 | public Response deleteAll() { 87 | this.todoService.deleteAll(); 88 | return Response.noContent().build(); 89 | } 90 | 91 | @PATCH 92 | @Path("/{id}") 93 | @Produces(MediaType.APPLICATION_JSON) 94 | @Consumes(MediaType.APPLICATION_JSON) 95 | public TodoDTO patchTodo(@PathParam("id") String id, PartialTodo command) { 96 | return this.todoService.updateTodo(UUID.fromString(id), command) 97 | .map(this::wrap) 98 | .orElse(null); 99 | } 100 | 101 | @DELETE 102 | @Path("/{id}") 103 | public Response deleteTodo(@PathParam("id") UUID id) { 104 | this.todoService.delete(id); 105 | return Response.noContent().build(); 106 | } 107 | 108 | private TodoDTO wrap(Todo todo) { 109 | return TodoDTO.from(todo, createUrl(todo)); 110 | } 111 | 112 | private String createUrl(Todo todo) { 113 | return this.baseUrl + "/" + todo.getId().toString(); 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /armeria/src/main/java/nl/jaapcoomans/microframeworks/armeria/TodoRestController.java: -------------------------------------------------------------------------------- 1 | package nl.jaapcoomans.microframeworks.armeria; 2 | 3 | import com.fasterxml.jackson.core.JsonProcessingException; 4 | import com.fasterxml.jackson.databind.ObjectMapper; 5 | import com.linecorp.armeria.common.HttpMethod; 6 | import com.linecorp.armeria.common.HttpResponse; 7 | import com.linecorp.armeria.common.HttpStatus; 8 | import com.linecorp.armeria.common.MediaType; 9 | import com.linecorp.armeria.server.ServiceRequestContext; 10 | import com.linecorp.armeria.server.annotation.ConsumesJson; 11 | import com.linecorp.armeria.server.annotation.Delete; 12 | import com.linecorp.armeria.server.annotation.Get; 13 | import com.linecorp.armeria.server.annotation.Param; 14 | import com.linecorp.armeria.server.annotation.Patch; 15 | import com.linecorp.armeria.server.annotation.Post; 16 | import com.linecorp.armeria.server.annotation.ProducesJson; 17 | import com.linecorp.armeria.server.annotation.StatusCode; 18 | import com.linecorp.armeria.server.annotation.decorator.CorsDecorator; 19 | import nl.jaapcoomans.demo.microframeworks.todo.api.CreateTodoCommand; 20 | import nl.jaapcoomans.demo.microframeworks.todo.api.TodoDTO; 21 | import nl.jaapcoomans.demo.microframeworks.todo.domain.PartialTodo; 22 | import nl.jaapcoomans.demo.microframeworks.todo.domain.Todo; 23 | import nl.jaapcoomans.demo.microframeworks.todo.domain.TodoService; 24 | 25 | import java.util.List; 26 | import java.util.UUID; 27 | import java.util.stream.Collectors; 28 | 29 | @CorsDecorator(origins = "*", allowedRequestMethods = HttpMethod.GET, allowedRequestHeaders = "Content-Type") 30 | class TodoRestController { 31 | private ObjectMapper objectMapper = new ObjectMapper(); 32 | 33 | private TodoService todoService; 34 | private String baseUrl; 35 | 36 | TodoRestController(TodoService todoService, String baseUrl) { 37 | this.todoService = todoService; 38 | this.baseUrl = baseUrl; 39 | } 40 | 41 | @Get("/todos") 42 | @ProducesJson 43 | public List getAll() { 44 | return this.todoService.findAll().stream() 45 | .map(this::wrap) 46 | .collect(Collectors.toList()); 47 | } 48 | 49 | @Get("/todos/{id}") 50 | @ProducesJson 51 | public HttpResponse getTodo(@Param("id") String id) throws JsonProcessingException { 52 | return this.todoService.findById(UUID.fromString(id)) 53 | .map(this::wrap) 54 | .map(todo -> HttpResponse.of(HttpStatus.OK, MediaType.JSON, 55 | this.convertToJson(todo))) 56 | .orElse(HttpResponse.of(404)); 57 | } 58 | 59 | private String convertToJson(TodoDTO todo) { 60 | try { 61 | return objectMapper.writeValueAsString(todo); 62 | } catch (JsonProcessingException e) { 63 | throw new RuntimeException(e); 64 | } 65 | } 66 | 67 | @Post("/todos") 68 | @ProducesJson 69 | @ConsumesJson 70 | public TodoDTO createTodo(CreateTodoCommand command) { 71 | Todo todo = this.todoService.createNewTodo(command.getTitle(), 72 | command.getOrder()); 73 | return this.wrap(todo); 74 | } 75 | 76 | @Delete("/todos") 77 | @StatusCode(204) 78 | public void deleteAll() { 79 | this.todoService.deleteAll(); 80 | } 81 | 82 | @Patch("/todos/{id}") 83 | @ConsumesJson 84 | @ProducesJson 85 | public TodoDTO patchTodo(@Param("id") String id, PartialTodo command) { 86 | return this.todoService.updateTodo(UUID.fromString(id), command) 87 | .map(this::wrap) 88 | .orElse(null); 89 | } 90 | 91 | @Delete("/todos/{id}") 92 | @StatusCode(204) 93 | public void deleteTodo(@Param("id") String id) { 94 | this.todoService.delete(UUID.fromString(id)); 95 | } 96 | 97 | private TodoDTO wrap(Todo todo) { 98 | return TodoDTO.from(todo, createUrl(todo)); 99 | } 100 | 101 | private String createUrl(Todo todo) { 102 | return this.baseUrl + "/" + todo.getId().toString(); 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /ratpack/src/main/java/nl/jaapcoomans/demo/microframeworks/ratpack/TodoRestController.java: -------------------------------------------------------------------------------- 1 | package nl.jaapcoomans.demo.microframeworks.ratpack; 2 | 3 | import nl.jaapcoomans.demo.microframeworks.todo.api.CreateTodoCommand; 4 | import nl.jaapcoomans.demo.microframeworks.todo.api.TodoDTO; 5 | import nl.jaapcoomans.demo.microframeworks.todo.domain.PartialTodo; 6 | import nl.jaapcoomans.demo.microframeworks.todo.domain.Todo; 7 | import nl.jaapcoomans.demo.microframeworks.todo.domain.TodoService; 8 | import ratpack.handling.Chain; 9 | import ratpack.handling.Context; 10 | import ratpack.http.Status; 11 | import ratpack.jackson.Jackson; 12 | 13 | import java.util.List; 14 | import java.util.UUID; 15 | import java.util.stream.Collectors; 16 | 17 | import static ratpack.handling.Handlers.byMethod; 18 | import static ratpack.jackson.Jackson.fromJson; 19 | import static ratpack.jackson.Jackson.json; 20 | 21 | public class TodoRestController { 22 | private static final String ID_PATH_PARAM = "id"; 23 | 24 | private TodoService todoService; 25 | private String baseUrl; 26 | 27 | TodoRestController(TodoService todoService, String baseUrl) { 28 | this.todoService = todoService; 29 | this.baseUrl = baseUrl; 30 | } 31 | 32 | void initializeEndpoints(Chain chain) throws Exception { 33 | chain 34 | .path("todos", byMethod(chain.getRegistry(), spec -> 35 | spec.get(this::getAll) 36 | .post(this::createTodo) 37 | .delete(this::deleteAll) 38 | ) 39 | ) 40 | .path("todos/:id", byMethod(chain.getRegistry(), spec -> 41 | spec.get(this::getTodo) 42 | .delete(this::deleteTodo) 43 | .patch(this::patchTodo) 44 | ) 45 | ); 46 | } 47 | 48 | void getAll(Context context) { 49 | List todos = this.todoService.findAll().stream() 50 | .map(this::wrap) 51 | .collect(Collectors.toList()); 52 | 53 | context.render(json(todos)); 54 | } 55 | 56 | void getTodo(Context context) { 57 | UUID id = UUID.fromString(context.getPathTokens().get(ID_PATH_PARAM)); 58 | 59 | this.todoService.findById(id) 60 | .map(this::wrap) 61 | .map(Jackson::json) 62 | .ifPresentOrElse(context::render, () -> context.getResponse().status(Status.NOT_FOUND).send()); 63 | } 64 | 65 | void createTodo(Context context) { 66 | context.parse(fromJson(CreateTodoCommand.class)) 67 | .map(command -> this.todoService.createNewTodo(command.getTitle(), command.getOrder())) 68 | .map(this::wrap) 69 | .map(Jackson::json) 70 | .then(context::render); 71 | } 72 | 73 | void deleteAll(Context context) { 74 | this.todoService.deleteAll(); 75 | context.getResponse().status(Status.NO_CONTENT).send(); 76 | } 77 | 78 | void patchTodo(Context context) { 79 | UUID id = UUID.fromString(context.getPathTokens().get(ID_PATH_PARAM)); 80 | 81 | context.parse(fromJson(PartialTodo.class)) 82 | .map(command -> this.todoService.updateTodo(id, command)) 83 | .then(todo -> todo.map(this::wrap) 84 | .map(Jackson::json) 85 | .ifPresentOrElse(context::render, () -> context.getResponse().status(Status.NOT_FOUND).send()) 86 | ); 87 | } 88 | 89 | void deleteTodo(Context context) { 90 | UUID id = UUID.fromString(context.getPathTokens().get(ID_PATH_PARAM)); 91 | this.todoService.delete(id); 92 | context.getResponse().status(Status.NO_CONTENT).send(); 93 | } 94 | 95 | private TodoDTO wrap(Todo todo) { 96 | return TodoDTO.from(todo, createUrl(todo)); 97 | } 98 | 99 | private String createUrl(Todo todo) { 100 | return this.baseUrl + "/" + todo.getId().toString(); 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /quarkus/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | quarkus 7 | jar 8 | 9 | 10 | nl.jaapcoomans.demo.microframeworks 11 | parent 12 | 0.0.1-SNAPSHOT 13 | 14 | 15 | 16 | 1.13.6.Final 17 | 18 | 19 | 20 | 21 | 22 | io.quarkus 23 | quarkus-universe-bom 24 | ${quarkus.version} 25 | pom 26 | import 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | io.quarkus 35 | quarkus-resteasy 36 | 37 | 38 | io.quarkus 39 | quarkus-resteasy-jackson 40 | 41 | 42 | io.quarkus 43 | quarkus-arc 44 | 45 | 46 | 47 | 48 | nl.jaapcoomans.demo.microframeworks 49 | todo-backend 50 | 0.0.1-SNAPSHOT 51 | 52 | 53 | 54 | 55 | 56 | 57 | io.quarkus 58 | quarkus-maven-plugin 59 | ${quarkus.version} 60 | true 61 | 62 | 63 | 64 | build 65 | generate-code 66 | generate-code-tests 67 | 68 | 69 | 70 | 71 | 72 | org.apache.maven.plugins 73 | maven-compiler-plugin 74 | 3.8.1 75 | 76 | true 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | -------------------------------------------------------------------------------- /helidon-mp/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | nl.jaapcoomans.demo.microframeworks 7 | helidon-mp 8 | jar 9 | 0.0.1-SNAPSHOT 10 | 11 | 12 | io.helidon.applications 13 | helidon-mp 14 | 2.3.0 15 | 16 | 17 | 18 | 19 | 16 20 | ${java.version} 21 | ${java.version} 22 | 23 | 24 | 25 | 26 | io.helidon.microprofile.bundles 27 | helidon-microprofile 28 | 29 | 30 | org.glassfish.jersey.media 31 | jersey-media-json-binding 32 | 33 | 34 | org.jboss 35 | jandex 36 | runtime 37 | true 38 | 39 | 40 | jakarta.activation 41 | jakarta.activation-api 42 | runtime 43 | 44 | 45 | 46 | 47 | nl.jaapcoomans.demo.microframeworks 48 | todo-backend 49 | 0.0.1-SNAPSHOT 50 | 51 | 52 | 53 | 54 | org.slf4j 55 | slf4j-simple 56 | 57 | 58 | 59 | 60 | org.junit.jupiter 61 | junit-jupiter-api 62 | 63 | 64 | org.junit.jupiter 65 | junit-jupiter-engine 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | org.apache.maven.plugins 74 | maven-jar-plugin 75 | 76 | application 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | org.apache.maven.plugins 85 | maven-dependency-plugin 86 | 3.1.1 87 | 88 | 89 | dependencies 90 | package 91 | 92 | copy-dependencies 93 | 94 | 95 | target/libs 96 | 97 | 98 | 99 | 100 | false 101 | false 102 | true 103 | runtime 104 | 105 | 106 | 107 | 108 | 109 | -------------------------------------------------------------------------------- /micronaut/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | micronaut 8 | 9 | 10 | parent 11 | nl.jaapcoomans.demo.microframeworks 12 | 0.0.1-SNAPSHOT 13 | 14 | 15 | 16 | 2.5.4 17 | 18 | 19 | 20 | 21 | 22 | io.micronaut 23 | micronaut-bom 24 | ${micronaut.version} 25 | pom 26 | import 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | io.micronaut 35 | micronaut-http-server-netty 36 | 37 | 38 | 39 | 40 | nl.jaapcoomans.demo.microframeworks 41 | todo-backend 42 | 0.0.1-SNAPSHOT 43 | 44 | 45 | 46 | 47 | org.slf4j 48 | slf4j-simple 49 | 50 | 51 | 52 | 53 | 54 | 55 | org.apache.maven.plugins 56 | maven-compiler-plugin 57 | 3.7.0 58 | 59 | 60 | -parameters 61 | 62 | 63 | 64 | io.micronaut 65 | micronaut-inject-java 66 | ${micronaut.version} 67 | 68 | 69 | io.micronaut 70 | micronaut-validation 71 | ${micronaut.version} 72 | 73 | 74 | 75 | 76 | 77 | test-compile 78 | 79 | testCompile 80 | 81 | 82 | 83 | -parameters 84 | 85 | 86 | 87 | io.micronaut 88 | micronaut-inject-java 89 | ${micronaut.version} 90 | 91 | 92 | io.micronaut 93 | micronaut-validation 94 | ${micronaut.version} 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | org.apache.maven.plugins 103 | maven-jar-plugin 104 | 105 | 106 | org.apache.maven.plugins 107 | maven-dependency-plugin 108 | 109 | 110 | 111 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | nl.jaapcoomans.demo.microframeworks 7 | parent 8 | pom 9 | 0.0.1-SNAPSHOT 10 | 11 | 12 | UTF-8 13 | 14 | 16 15 | ${java.version} 16 | ${java.version} 17 | 18 | 1.6.0 19 | 20 | 21 | 3.19.0 22 | 5.7.2 23 | 3.10.0 24 | 25 | 26 | 27 | armeria 28 | blade 29 | dropwizard 30 | helidon-mp 31 | helidon-se 32 | http4k 33 | javalin 34 | jooby 35 | ktor 36 | kumuluzee 37 | micronaut 38 | minijax 39 | pippo 40 | quarkus 41 | ratpack 42 | spark 43 | 44 | todo-backend 45 | todo-backend-test 46 | 47 | 48 | 49 | 50 | 51 | org.slf4j 52 | slf4j-simple 53 | 1.7.28 54 | 55 | 56 | 57 | 58 | org.jetbrains.kotlin 59 | kotlin-stdlib 60 | ${kotlin.version} 61 | 62 | 63 | 64 | 65 | org.junit.jupiter 66 | junit-jupiter-api 67 | ${junit.version} 68 | test 69 | 70 | 71 | org.junit.jupiter 72 | junit-jupiter-engine 73 | ${junit.version} 74 | test 75 | 76 | 77 | org.mockito 78 | mockito-junit-jupiter 79 | ${mockito.version} 80 | test 81 | 82 | 83 | org.assertj 84 | assertj-core 85 | ${assertj.version} 86 | test 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | org.apache.maven.plugins 96 | maven-jar-plugin 97 | 3.2.0 98 | 99 | application 100 | 101 | 102 | 103 | org.jetbrains.kotlin 104 | kotlin-maven-plugin 105 | ${kotlin.version} 106 | 107 | 108 | compile 109 | compile 110 | 111 | compile 112 | 113 | 114 | 115 | test-compile 116 | test-compile 117 | 118 | test-compile 119 | 120 | 121 | 122 | 123 | 124 | org.apache.maven.plugins 125 | maven-dependency-plugin 126 | 3.1.1 127 | 128 | 129 | dependencies 130 | package 131 | 132 | copy-dependencies 133 | 134 | 135 | target/libs 136 | 137 | 138 | 139 | 140 | false 141 | false 142 | true 143 | runtime 144 | 145 | 146 | 147 | 148 | 149 | 150 | -------------------------------------------------------------------------------- /todo-backend-test/src/test/java/nl/jaapcoomans/demo/microframeworks/todo/test/BaseTodoBackendTest.java: -------------------------------------------------------------------------------- 1 | package nl.jaapcoomans.demo.microframeworks.todo.test; 2 | 3 | import io.restassured.RestAssured; 4 | import org.apache.http.HttpHeaders; 5 | import org.junit.jupiter.api.DisplayName; 6 | import org.junit.jupiter.api.MethodOrderer; 7 | import org.junit.jupiter.api.Order; 8 | import org.junit.jupiter.api.Test; 9 | import org.junit.jupiter.api.TestMethodOrder; 10 | import org.testcontainers.containers.GenericContainer; 11 | 12 | import static org.hamcrest.CoreMatchers.anyOf; 13 | import static org.hamcrest.CoreMatchers.equalTo; 14 | import static org.hamcrest.CoreMatchers.not; 15 | import static org.hamcrest.CoreMatchers.notNullValue; 16 | import static org.hamcrest.CoreMatchers.startsWith; 17 | import static org.hamcrest.Matchers.containsString; 18 | import static org.hamcrest.Matchers.empty; 19 | import static org.hamcrest.Matchers.emptyOrNullString; 20 | import static org.hamcrest.Matchers.equalToIgnoringCase; 21 | import static org.hamcrest.Matchers.hasSize; 22 | 23 | @TestMethodOrder(MethodOrderer.OrderAnnotation.class) 24 | abstract class BaseTodoBackendTest { 25 | private static final String API_ROOT = "/todos"; 26 | 27 | abstract GenericContainer getContainer(); 28 | 29 | String getBaseUrl() { 30 | return "http://" + getContainer().getContainerIpAddress() + ":" + getContainer().getFirstMappedPort(); 31 | } 32 | 33 | String replacePort(String url) { 34 | return url.replaceFirst("[1-9][0-9]{1,4}", getContainer().getFirstMappedPort().toString()); 35 | } 36 | 37 | @Test 38 | @Order(1) 39 | @DisplayName("the api root responds to a GET (i.e. the server is up and accessible, CORS headers are set up)") 40 | final void getApiRoot() { 41 | RestAssured.given() 42 | .header("Access-Control-Request-Headers", HttpHeaders.CONTENT_TYPE) 43 | .header("Access-Control-Request-Method", "GET") 44 | .header("Origin", "http://crossorigin.example.org") 45 | .baseUri(getBaseUrl()) 46 | .when().options(API_ROOT) 47 | .then() 48 | .header("Access-Control-Allow-Origin", notNullValue()) 49 | .header("Access-Control-Allow-Headers", anyOf(equalToIgnoringCase(HttpHeaders.CONTENT_TYPE), containsString(HttpHeaders.CONTENT_TYPE), equalTo("*"))) 50 | .header("Access-Control-Allow-Methods", notNullValue()); 51 | 52 | RestAssured.given() 53 | .header(HttpHeaders.ACCEPT, "application/json") 54 | .baseUri(getBaseUrl()) 55 | .when().get(API_ROOT) 56 | .then() 57 | .statusCode(200) 58 | .body("$", empty()); 59 | } 60 | 61 | @Test 62 | @Order(2) 63 | @DisplayName("the api root responds to a POST with the todo which was posted to it") 64 | void postApiRoot() { 65 | RestAssured.given() 66 | .header(HttpHeaders.CONTENT_TYPE, "application/json") 67 | .baseUri(getBaseUrl()) 68 | .body("{\"title\": \"Test 123\", \"order\": 1 }") 69 | .when().post(API_ROOT) 70 | .then() 71 | .statusCode(200) 72 | .body("id", not(emptyOrNullString())) 73 | .body("title", equalTo("Test 123")) 74 | .body("url", startsWith("http://")) 75 | .body("order", equalTo(1)) 76 | .body("completed", equalTo(false)); 77 | } 78 | 79 | @Test 80 | @Order(3) 81 | @DisplayName("the api root responds successfully to a DELETE, after a DELETE the api root responds to a GET with a JSON representation of an empty array") 82 | void deleteApiRoot() { 83 | RestAssured.given() 84 | .baseUri(getBaseUrl()) 85 | .when().delete(API_ROOT) 86 | .then() 87 | .statusCode(204); 88 | } 89 | 90 | @Test 91 | @Order(4) 92 | @DisplayName("after a DELETE the api root responds to a GET with a JSON representation of an empty array") 93 | void afterDeleteApiRoot() { 94 | RestAssured.given() 95 | .header(HttpHeaders.ACCEPT, "application/json") 96 | .baseUri(getBaseUrl()) 97 | .when().get(API_ROOT) 98 | .then() 99 | .statusCode(200) 100 | .body("$", empty()); 101 | } 102 | 103 | @Test 104 | @Order(5) 105 | @DisplayName("adds a new todo to the list of todos at the root url") 106 | void addTodo() { 107 | RestAssured.given() 108 | .header(HttpHeaders.CONTENT_TYPE, "application/json") 109 | .baseUri(getBaseUrl()) 110 | .body("{\"title\": \"Test 1\", \"order\": 1 }") 111 | .when().post(API_ROOT) 112 | .then() 113 | .statusCode(200); 114 | 115 | RestAssured.given() 116 | .header(HttpHeaders.ACCEPT, "application/json") 117 | .baseUri(getBaseUrl()) 118 | .when().get(API_ROOT) 119 | .then() 120 | .statusCode(200) 121 | .body("$", hasSize(1)); 122 | } 123 | 124 | @Test 125 | @Order(6) 126 | @DisplayName("sets up a new todo as initially not completed") 127 | void todoInitiallyNotCompleted() { 128 | RestAssured.given() 129 | .header(HttpHeaders.CONTENT_TYPE, "application/json") 130 | .baseUri(getBaseUrl()) 131 | .body("{\"title\": \"Test 2\", \"order\": 2 }") 132 | .when().post(API_ROOT) 133 | .then() 134 | .statusCode(200) 135 | .body("completed", equalTo(false)); 136 | } 137 | 138 | @Test 139 | @Order(7) 140 | @DisplayName("each new todo has a url") 141 | void newTodoHasUrl() { 142 | RestAssured.given() 143 | .header(HttpHeaders.CONTENT_TYPE, "application/json") 144 | .baseUri(getBaseUrl()) 145 | .body("{\"title\": \"Test 3\", \"order\": 3 }") 146 | .when().post(API_ROOT) 147 | .then() 148 | .statusCode(200) 149 | .body("url", startsWith("http://")); 150 | } 151 | 152 | @Test 153 | @Order(8) 154 | @DisplayName("each new todo has a url, which returns a todo") 155 | void newTodoHasUrlWhichReturnsTodo() { 156 | var response = RestAssured.given() 157 | .header(HttpHeaders.CONTENT_TYPE, "application/json") 158 | .baseUri(getBaseUrl()) 159 | .body("{\"title\": \"Test 4\", \"order\": 4 }") 160 | .when().post(API_ROOT); 161 | 162 | response 163 | .then() 164 | .statusCode(200) 165 | .body("url", startsWith("http://")); 166 | 167 | var url = response.body().jsonPath().get("url").toString(); 168 | 169 | var response2 = RestAssured.given() 170 | .header(HttpHeaders.ACCEPT, "application/json") 171 | .baseUri(replacePort(url)) 172 | .when().get(); 173 | response2.then() 174 | .statusCode(200) 175 | .body("id", not(emptyOrNullString())) 176 | .body("title", equalTo("Test 4")) 177 | .body("url", equalTo(url)) 178 | .body("order", equalTo(4)) 179 | .body("completed", equalTo(false)); 180 | } 181 | 182 | @Test 183 | @Order(9) 184 | @DisplayName("can navigate from a list of todos to an individual todo via urls") 185 | void navigateFromListToIndividual() { 186 | var url = RestAssured.given() 187 | .header(HttpHeaders.ACCEPT, "application/json") 188 | .baseUri(getBaseUrl()) 189 | .when().get(API_ROOT) 190 | .body().jsonPath().get("[0].url").toString(); 191 | 192 | RestAssured.given() 193 | .header(HttpHeaders.CONTENT_TYPE, "application/json") 194 | .baseUri(replacePort(url)) 195 | .when().get() 196 | .then() 197 | .statusCode(200) 198 | .body("id", not(emptyOrNullString())) 199 | .body("title", not(emptyOrNullString())) 200 | .body("url", equalTo(url)) 201 | .body("order", notNullValue()) 202 | .body("completed", equalTo(false)); 203 | } 204 | 205 | @Test 206 | @Order(10) 207 | @DisplayName("can change the todo's title by PATCHing to the todo's url") 208 | void changeTitle() { 209 | var url = RestAssured.given() 210 | .header(HttpHeaders.ACCEPT, "application/json") 211 | .baseUri(getBaseUrl()) 212 | .when().get(API_ROOT) 213 | .body().jsonPath().get("[0].url").toString(); 214 | 215 | RestAssured.given() 216 | .header(HttpHeaders.CONTENT_TYPE, "application/json") 217 | .baseUri(replacePort(url)) 218 | .body("{\"title\": \"Changed\"}") 219 | .when().patch() 220 | .then() 221 | .statusCode(200) 222 | .body("title", equalTo("Changed")); 223 | } 224 | 225 | @Test 226 | @Order(11) 227 | @DisplayName("can change the todo's completedness by PATCHing to the todo's url") 228 | void changeCompleted() { 229 | var url = RestAssured.given() 230 | .header(HttpHeaders.ACCEPT, "application/json") 231 | .baseUri(getBaseUrl()) 232 | .when().get(API_ROOT) 233 | .body().jsonPath().get("[0].url").toString(); 234 | 235 | RestAssured.given() 236 | .header(HttpHeaders.CONTENT_TYPE, "application/json") 237 | .baseUri(replacePort(url)) 238 | .body("{\"completed\": true}") 239 | .when().patch() 240 | .then() 241 | .statusCode(200) 242 | .body("completed", equalTo(true)); 243 | } 244 | 245 | @Test 246 | @Order(12) 247 | @DisplayName("changes to a todo are persisted and show up when re-fetching the todo") 248 | void changesArePersisted() { 249 | var url = RestAssured.given() 250 | .header(HttpHeaders.ACCEPT, "application/json") 251 | .baseUri(getBaseUrl()) 252 | .when().get(API_ROOT) 253 | .body().jsonPath().get("[0].url").toString(); 254 | 255 | RestAssured.given() 256 | .header(HttpHeaders.CONTENT_TYPE, "application/json") 257 | .baseUri(replacePort(url)) 258 | .body("{\"title\": \"Persisted\"}") 259 | .when().patch() 260 | .then().statusCode(200); 261 | 262 | RestAssured.given() 263 | .header(HttpHeaders.ACCEPT, "application/json") 264 | .baseUri(replacePort(url)) 265 | .when().get() 266 | .then().body("title", equalTo("Persisted")); 267 | } 268 | 269 | @Test 270 | @Order(13) 271 | @DisplayName("can delete a todo making a DELETE request to the todo's url") 272 | void deleteTodo() { 273 | var url = RestAssured.given() 274 | .header(HttpHeaders.ACCEPT, "application/json") 275 | .baseUri(getBaseUrl()) 276 | .when().get(API_ROOT) 277 | .body().jsonPath().get("[0].url").toString(); 278 | 279 | RestAssured.given() 280 | .baseUri(replacePort(url)) 281 | .when().delete() 282 | .then().statusCode(204); 283 | 284 | RestAssured.given() 285 | .header(HttpHeaders.ACCEPT, "application/json") 286 | .baseUri(replacePort(url)) 287 | .when().get() 288 | .then().statusCode(404); 289 | } 290 | } 291 | --------------------------------------------------------------------------------