From e0a235fcc5d9fee4b0499a445fd99b3454a3e820 Mon Sep 17 00:00:00 2001 From: Thomas Peetz Date: Sat, 20 Dec 2025 04:13:41 +0100 Subject: [PATCH 1/5] change Dockerfile for kontor-spring to multistage and add kontor-javalin --- docker-compose.yml | 17 ++++++++++ kontor-javalin/Dockerfile | 31 ++++++++++++++++--- .../main/java/de/thpeetz/kontor/api/Main.java | 8 ++--- kontor-spring/Dockerfile | 24 ++++++++++++-- .../src/main/resources/logback-spring.xml | 2 +- 5 files changed, 70 insertions(+), 12 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 086b985..86a575d 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -113,6 +113,23 @@ services: depends_on: postgres: condition: service_healthy + kontor-javalin: + build: + context: ./kontor-javalin + dockerfile: Dockerfile + tags: + - kontor-javalin:0.2.0-SNAPSHOT + image: kontor-javalin:0.2.0-SNAPSHOT + restart: unless-stopped + networks: + - database + - integration + - frontend + ports: + - 8400:8400 + depends_on: + postgres: + condition: service_healthy networks: integration: diff --git a/kontor-javalin/Dockerfile b/kontor-javalin/Dockerfile index 1cfd701..be483c1 100644 --- a/kontor-javalin/Dockerfile +++ b/kontor-javalin/Dockerfile @@ -1,5 +1,28 @@ -FROM alpine/java:21-jdk +# ----------------------------------------------------------------------- # +FROM gradle:9.2.1-jdk AS builder WORKDIR / -ADD build/libs/kontor-spring-0.2.0-SNAPSHOT.jar app.jar -EXPOSE 8000 -CMD ["java", "-jar", "-Dspring.profiles.active=prod", "-Dvaadin.productionMode=true", "app.jar"] +COPY ./api/src/main/ ./api/src/main/ +COPY ./api/build.gradle ./api/ +COPY ./models/src/main/ ./models/src/main/ +COPY ./models/build.gradle ./models/ +COPY ./services/src/main/ ./services/src/main/ +COPY ./services/build.gradle ./services/ +COPY ./build.gradle ./ +COPY ./gradle.properties ./ +COPY ./settings.gradle ./ +COPY ./gradle/libs.versions.toml ./gradle/ +RUN gradle build --no-daemon + +# ----------------------------------------------------------------------- # +FROM alpine/java:21-jdk AS run + +RUN adduser --system appuser +USER appuser + +COPY --from=builder --chown=appuser:appuser /api/build/libs/api-0.2.0-SNAPSHOT.jar app.jar + +EXPOSE 8400 +USER appuser + +CMD ["java", "-jar", "app.jar"] + diff --git a/kontor-javalin/api/src/main/java/de/thpeetz/kontor/api/Main.java b/kontor-javalin/api/src/main/java/de/thpeetz/kontor/api/Main.java index 32ee536..5f887a5 100644 --- a/kontor-javalin/api/src/main/java/de/thpeetz/kontor/api/Main.java +++ b/kontor-javalin/api/src/main/java/de/thpeetz/kontor/api/Main.java @@ -1,16 +1,16 @@ package de.thpeetz.kontor.api; -import com.fasterxml.jackson.databind.ObjectMapper; -import io.javalin.Javalin; import de.thpeetz.kontor.services.api.PersonReader; +import io.javalin.Javalin; +import java.util.ServiceLoader; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import com.fasterxml.jackson.databind.ObjectMapper; -import java.util.ServiceLoader; public class Main { private static Logger logger = LoggerFactory.getLogger(Main.class); - private static short port = 7312; + private static short port = 8400; public static void main(String[] args) { var personReader = ServiceLoader.load(PersonReader.class).findFirst().get(); // <-- Getting an implementation diff --git a/kontor-spring/Dockerfile b/kontor-spring/Dockerfile index 1cfd701..655c32e 100644 --- a/kontor-spring/Dockerfile +++ b/kontor-spring/Dockerfile @@ -1,5 +1,23 @@ -FROM alpine/java:21-jdk +# ----------------------------------------------------------------------- # +FROM gradle:8.7-jdk AS builder WORKDIR / -ADD build/libs/kontor-spring-0.2.0-SNAPSHOT.jar app.jar -EXPOSE 8000 +COPY ./src/main/ ./src/main/ +COPY ./frontend/ ./frontend/ +COPY ./build.gradle ./ +COPY ./gradle.properties ./ +COPY ./settings.gradle ./ +COPY ./gradle/libs.versions.toml ./gradle/ +RUN gradle bootJar --no-daemon + +# ----------------------------------------------------------------------- # +FROM alpine/java:21-jdk AS run + +RUN adduser --system appuser +USER appuser + +COPY --from=builder --chown=appuser:appuser /build/libs/kontor-spring-0.2.0-SNAPSHOT.jar app.jar + +EXPOSE 8100 +USER appuser CMD ["java", "-jar", "-Dspring.profiles.active=prod", "-Dvaadin.productionMode=true", "app.jar"] + diff --git a/kontor-spring/src/main/resources/logback-spring.xml b/kontor-spring/src/main/resources/logback-spring.xml index b085f8c..a8f961d 100644 --- a/kontor-spring/src/main/resources/logback-spring.xml +++ b/kontor-spring/src/main/resources/logback-spring.xml @@ -1,7 +1,7 @@ - + From d3ccc2a46ba2aaa79ba71939c4721d8f430bbbd9 Mon Sep 17 00:00:00 2001 From: Thomas Peetz Date: Sun, 21 Dec 2025 19:27:49 +0100 Subject: [PATCH 2/5] create logs directory in Dockerfile for kontor-spring --- kontor-spring/Dockerfile | 4 +++- kontor-spring/src/main/resources/logback-spring.xml | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/kontor-spring/Dockerfile b/kontor-spring/Dockerfile index 655c32e..575c521 100644 --- a/kontor-spring/Dockerfile +++ b/kontor-spring/Dockerfile @@ -12,10 +12,12 @@ RUN gradle bootJar --no-daemon # ----------------------------------------------------------------------- # FROM alpine/java:21-jdk AS run +RUN mkdir -p /logs + RUN adduser --system appuser -USER appuser COPY --from=builder --chown=appuser:appuser /build/libs/kontor-spring-0.2.0-SNAPSHOT.jar app.jar +RUN chown appuser:root /logs EXPOSE 8100 USER appuser diff --git a/kontor-spring/src/main/resources/logback-spring.xml b/kontor-spring/src/main/resources/logback-spring.xml index a8f961d..702b86e 100644 --- a/kontor-spring/src/main/resources/logback-spring.xml +++ b/kontor-spring/src/main/resources/logback-spring.xml @@ -1,7 +1,7 @@ - + From 7ef616a2a9feb0979bbec52daf15cb22060ae647 Mon Sep 17 00:00:00 2001 From: Thomas Peetz Date: Sun, 21 Dec 2025 19:42:22 +0100 Subject: [PATCH 3/5] add task fatJar --- kontor-javalin/api/build.gradle | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/kontor-javalin/api/build.gradle b/kontor-javalin/api/build.gradle index e2cb6db..2934926 100644 --- a/kontor-javalin/api/build.gradle +++ b/kontor-javalin/api/build.gradle @@ -15,3 +15,22 @@ application { mainClass = "de.thpeetz.kontor.api.Main" mainModule = "de.thpeetz.kontor.api" } + +task fatJar(type: Jar) { + manifest { + attributes 'Main-Class': 'de.thpeetz.kontor.api.Main' + //attributes ( + // 'Main-Class': 'de.thpeetz.kontor.api.Main', + // 'Main-Module': 'de.thpeetz.kontor.api' + //) + } + //archiveBaseName = 'all-in-one-jar' + duplicatesStrategy = DuplicatesStrategy.EXCLUDE + from { configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) } } + with jar +} + +build.dependsOn fatJar +distZip.dependsOn fatJar +distTar.dependsOn fatJar +startScripts.dependsOn fatJar From 3c4e182832a4b9baff7bb823403b25b45f228078 Mon Sep 17 00:00:00 2001 From: Thomas Peetz Date: Sun, 21 Dec 2025 20:14:31 +0100 Subject: [PATCH 4/5] added running kontor-javalin with example API --- kontor-javalin/Dockerfile | 11 ++---- kontor-javalin/api/build.gradle | 36 ------------------- .../api/src/main/java/module-info.java | 10 ------ kontor-javalin/build.gradle | 26 ++++++++++---- kontor-javalin/models/build.gradle | 4 --- .../models/src/main/java/module-info.java | 4 --- kontor-javalin/services/build.gradle | 7 ---- .../services/src/main/java/module-info.java | 11 ------ kontor-javalin/settings.gradle | 5 --- .../main/java/de/thpeetz/kontor/api/Main.java | 13 ++++--- .../java/de/thpeetz/kontor/models/Person.java | 0 .../kontor/services/api/PersonReader.java | 1 - .../inmemory/InMemoryPersonReader.java | 1 + 13 files changed, 30 insertions(+), 99 deletions(-) delete mode 100644 kontor-javalin/api/build.gradle delete mode 100644 kontor-javalin/api/src/main/java/module-info.java delete mode 100644 kontor-javalin/models/build.gradle delete mode 100644 kontor-javalin/models/src/main/java/module-info.java delete mode 100644 kontor-javalin/services/build.gradle delete mode 100644 kontor-javalin/services/src/main/java/module-info.java rename kontor-javalin/{api => }/src/main/java/de/thpeetz/kontor/api/Main.java (58%) rename kontor-javalin/{models => }/src/main/java/de/thpeetz/kontor/models/Person.java (100%) rename kontor-javalin/{services => }/src/main/java/de/thpeetz/kontor/services/api/PersonReader.java (99%) rename kontor-javalin/{services => }/src/main/java/de/thpeetz/kontor/services/inmemory/InMemoryPersonReader.java (99%) diff --git a/kontor-javalin/Dockerfile b/kontor-javalin/Dockerfile index be483c1..92ef840 100644 --- a/kontor-javalin/Dockerfile +++ b/kontor-javalin/Dockerfile @@ -1,12 +1,7 @@ # ----------------------------------------------------------------------- # -FROM gradle:9.2.1-jdk AS builder +FROM gradle:9.2.1-jdk21 AS builder WORKDIR / -COPY ./api/src/main/ ./api/src/main/ -COPY ./api/build.gradle ./api/ -COPY ./models/src/main/ ./models/src/main/ -COPY ./models/build.gradle ./models/ -COPY ./services/src/main/ ./services/src/main/ -COPY ./services/build.gradle ./services/ +COPY ./src/main/ ./src/main/ COPY ./build.gradle ./ COPY ./gradle.properties ./ COPY ./settings.gradle ./ @@ -19,7 +14,7 @@ FROM alpine/java:21-jdk AS run RUN adduser --system appuser USER appuser -COPY --from=builder --chown=appuser:appuser /api/build/libs/api-0.2.0-SNAPSHOT.jar app.jar +COPY --from=builder --chown=appuser:appuser /build/libs/kontor-javalin-0.2.0-SNAPSHOT.jar app.jar EXPOSE 8400 USER appuser diff --git a/kontor-javalin/api/build.gradle b/kontor-javalin/api/build.gradle deleted file mode 100644 index 2934926..0000000 --- a/kontor-javalin/api/build.gradle +++ /dev/null @@ -1,36 +0,0 @@ -plugins { - id 'application' -} - -dependencies { - implementation project(":models") // We need this dependency for serializing persons to JSON - implementation project(":services") // We'll make use of the InMemoryPersonReader soon - - implementation libs.javalin // Pulling in Javalin - implementation libs.jackson // For JSON serialization of persons - implementation libs.slf4j // To see some Javalin logging -} - -application { - mainClass = "de.thpeetz.kontor.api.Main" - mainModule = "de.thpeetz.kontor.api" -} - -task fatJar(type: Jar) { - manifest { - attributes 'Main-Class': 'de.thpeetz.kontor.api.Main' - //attributes ( - // 'Main-Class': 'de.thpeetz.kontor.api.Main', - // 'Main-Module': 'de.thpeetz.kontor.api' - //) - } - //archiveBaseName = 'all-in-one-jar' - duplicatesStrategy = DuplicatesStrategy.EXCLUDE - from { configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) } } - with jar -} - -build.dependsOn fatJar -distZip.dependsOn fatJar -distTar.dependsOn fatJar -startScripts.dependsOn fatJar diff --git a/kontor-javalin/api/src/main/java/module-info.java b/kontor-javalin/api/src/main/java/module-info.java deleted file mode 100644 index de9b448..0000000 --- a/kontor-javalin/api/src/main/java/module-info.java +++ /dev/null @@ -1,10 +0,0 @@ -module de.thpeetz.kontor.api { - requires io.javalin; - requires com.fasterxml.jackson.databind; - requires org.slf4j; - requires kotlin.stdlib; - - requires de.thpeetz.kontor.services; - - uses de.thpeetz.kontor.services.api.PersonReader; -} diff --git a/kontor-javalin/build.gradle b/kontor-javalin/build.gradle index d25e5a5..468a464 100644 --- a/kontor-javalin/build.gradle +++ b/kontor-javalin/build.gradle @@ -1,14 +1,28 @@ plugins { - id 'org.javamodularity.moduleplugin' version '2.0.0' apply false + id 'java' } -subprojects { - repositories { - mavenCentral() - } - apply plugin: "org.javamodularity.moduleplugin" +repositories { + mavenCentral() } +dependencies { + implementation libs.javalin // Pulling in Javalin + implementation libs.jackson // For JSON serialization of persons + implementation libs.slf4j // To see some Javalin logging +} + +task fatJar(type: Jar) { + manifest { + attributes 'Main-Class': 'de.thpeetz.kontor.api.Main' + } + duplicatesStrategy = DuplicatesStrategy.EXCLUDE + from { configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) } } + with jar +} + +build.dependsOn fatJar + wrapper { gradleVersion = "9.2.1" } diff --git a/kontor-javalin/models/build.gradle b/kontor-javalin/models/build.gradle deleted file mode 100644 index 483b849..0000000 --- a/kontor-javalin/models/build.gradle +++ /dev/null @@ -1,4 +0,0 @@ -plugins { - id 'java-library' -} - diff --git a/kontor-javalin/models/src/main/java/module-info.java b/kontor-javalin/models/src/main/java/module-info.java deleted file mode 100644 index 5cb3b7a..0000000 --- a/kontor-javalin/models/src/main/java/module-info.java +++ /dev/null @@ -1,4 +0,0 @@ -module de.thpeetz.kontor.models { - // We're exporting the only package we have in this subproject - exports de.thpeetz.kontor.models; -} diff --git a/kontor-javalin/services/build.gradle b/kontor-javalin/services/build.gradle deleted file mode 100644 index 7d12b82..0000000 --- a/kontor-javalin/services/build.gradle +++ /dev/null @@ -1,7 +0,0 @@ -plugins { - id 'java-library' -} - -dependencies { - implementation project(":models") // <-- add dependency to models subproject -} diff --git a/kontor-javalin/services/src/main/java/module-info.java b/kontor-javalin/services/src/main/java/module-info.java deleted file mode 100644 index 9112de2..0000000 --- a/kontor-javalin/services/src/main/java/module-info.java +++ /dev/null @@ -1,11 +0,0 @@ -import de.thpeetz.kontor.services.api.PersonReader; -import de.thpeetz.kontor.services.inmemory.InMemoryPersonReader; - -module de.thpeetz.kontor.services { - exports de.thpeetz.kontor.services.api; - - requires de.thpeetz.kontor.models; - - // We're telling the ServiceLoader that the InMemoryPersonReader provides the implementation for the PersonReader interface - provides PersonReader with InMemoryPersonReader; -} diff --git a/kontor-javalin/settings.gradle b/kontor-javalin/settings.gradle index d245e05..d6456e7 100644 --- a/kontor-javalin/settings.gradle +++ b/kontor-javalin/settings.gradle @@ -1,6 +1 @@ rootProject.name = 'kontor-javalin' - -include ':models' -include ':services' -include ':api' - diff --git a/kontor-javalin/api/src/main/java/de/thpeetz/kontor/api/Main.java b/kontor-javalin/src/main/java/de/thpeetz/kontor/api/Main.java similarity index 58% rename from kontor-javalin/api/src/main/java/de/thpeetz/kontor/api/Main.java rename to kontor-javalin/src/main/java/de/thpeetz/kontor/api/Main.java index 5f887a5..1a259be 100644 --- a/kontor-javalin/api/src/main/java/de/thpeetz/kontor/api/Main.java +++ b/kontor-javalin/src/main/java/de/thpeetz/kontor/api/Main.java @@ -1,21 +1,17 @@ package de.thpeetz.kontor.api; -import de.thpeetz.kontor.services.api.PersonReader; +import de.thpeetz.kontor.services.inmemory.InMemoryPersonReader; import io.javalin.Javalin; -import java.util.ServiceLoader; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.fasterxml.jackson.databind.ObjectMapper; - public class Main { private static Logger logger = LoggerFactory.getLogger(Main.class); private static short port = 8400; public static void main(String[] args) { - var personReader = ServiceLoader.load(PersonReader.class).findFirst().get(); // <-- Getting an implementation - // for the PersonReader interface - // from the ServiceLoader + var personReader = new InMemoryPersonReader(); var objMapper = new ObjectMapper(); var result = objMapper.valueToTree(personReader.getAll()); @@ -23,7 +19,10 @@ public class Main { var app = Javalin.create().start(port); app.get("/ping", ctx -> ctx.result("pong")); - app.get("/persons", ctx -> ctx.json(result)); + app.get("/persons", ctx -> { + logger.info("persons called"); + ctx.json(result); + }); logger.info("API's alive for real :-)))"); } diff --git a/kontor-javalin/models/src/main/java/de/thpeetz/kontor/models/Person.java b/kontor-javalin/src/main/java/de/thpeetz/kontor/models/Person.java similarity index 100% rename from kontor-javalin/models/src/main/java/de/thpeetz/kontor/models/Person.java rename to kontor-javalin/src/main/java/de/thpeetz/kontor/models/Person.java diff --git a/kontor-javalin/services/src/main/java/de/thpeetz/kontor/services/api/PersonReader.java b/kontor-javalin/src/main/java/de/thpeetz/kontor/services/api/PersonReader.java similarity index 99% rename from kontor-javalin/services/src/main/java/de/thpeetz/kontor/services/api/PersonReader.java rename to kontor-javalin/src/main/java/de/thpeetz/kontor/services/api/PersonReader.java index 032567a..a864fbe 100644 --- a/kontor-javalin/services/src/main/java/de/thpeetz/kontor/services/api/PersonReader.java +++ b/kontor-javalin/src/main/java/de/thpeetz/kontor/services/api/PersonReader.java @@ -1,7 +1,6 @@ package de.thpeetz.kontor.services.api; import de.thpeetz.kontor.models.Person; - import java.util.List; public interface PersonReader { diff --git a/kontor-javalin/services/src/main/java/de/thpeetz/kontor/services/inmemory/InMemoryPersonReader.java b/kontor-javalin/src/main/java/de/thpeetz/kontor/services/inmemory/InMemoryPersonReader.java similarity index 99% rename from kontor-javalin/services/src/main/java/de/thpeetz/kontor/services/inmemory/InMemoryPersonReader.java rename to kontor-javalin/src/main/java/de/thpeetz/kontor/services/inmemory/InMemoryPersonReader.java index 4600d74..39442c7 100644 --- a/kontor-javalin/services/src/main/java/de/thpeetz/kontor/services/inmemory/InMemoryPersonReader.java +++ b/kontor-javalin/src/main/java/de/thpeetz/kontor/services/inmemory/InMemoryPersonReader.java @@ -6,6 +6,7 @@ import java.util.List; import de.thpeetz.kontor.services.api.PersonReader; public class InMemoryPersonReader implements PersonReader { + @Override public List getAll() { return List.of( From 11b13e0bf454658f136de862bec401a5c0b867cd Mon Sep 17 00:00:00 2001 From: Thomas Peetz Date: Sun, 21 Dec 2025 21:15:08 +0100 Subject: [PATCH 5/5] add healthcheck for kontor-javalin --- docker-compose.yml | 5 +++++ kontor-javalin/Dockerfile | 4 +++- .../src/main/java/de/thpeetz/kontor/api/Main.java | 8 ++++++++ 3 files changed, 16 insertions(+), 1 deletion(-) diff --git a/docker-compose.yml b/docker-compose.yml index 86a575d..3fc8ad8 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -121,6 +121,11 @@ services: - kontor-javalin:0.2.0-SNAPSHOT image: kontor-javalin:0.2.0-SNAPSHOT restart: unless-stopped + healthcheck: + test: ["CMD", "curl", "-f", "http://kontor-javalin:8400/health"] + interval: 10s + timeout: 5s + retries: 3 networks: - database - integration diff --git a/kontor-javalin/Dockerfile b/kontor-javalin/Dockerfile index 92ef840..9e50434 100644 --- a/kontor-javalin/Dockerfile +++ b/kontor-javalin/Dockerfile @@ -9,7 +9,9 @@ COPY ./gradle/libs.versions.toml ./gradle/ RUN gradle build --no-daemon # ----------------------------------------------------------------------- # -FROM alpine/java:21-jdk AS run +FROM alpine/java:21-jre AS run + +RUN apk --no-cache add curl RUN adduser --system appuser USER appuser diff --git a/kontor-javalin/src/main/java/de/thpeetz/kontor/api/Main.java b/kontor-javalin/src/main/java/de/thpeetz/kontor/api/Main.java index 1a259be..41b2baa 100644 --- a/kontor-javalin/src/main/java/de/thpeetz/kontor/api/Main.java +++ b/kontor-javalin/src/main/java/de/thpeetz/kontor/api/Main.java @@ -2,6 +2,9 @@ package de.thpeetz.kontor.api; import de.thpeetz.kontor.services.inmemory.InMemoryPersonReader; import io.javalin.Javalin; + +import java.util.HashMap; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.fasterxml.jackson.databind.ObjectMapper; @@ -19,6 +22,11 @@ public class Main { var app = Javalin.create().start(port); app.get("/ping", ctx -> ctx.result("pong")); + app.get("/health", ctx -> { + HashMap status = new HashMap<>(); + status.put("status", "ok"); + ctx.json(status); + }); app.get("/persons", ctx -> { logger.info("persons called"); ctx.json(result);