Ein fokussiertes Mini‑Projekt für die umgestellte Stunde: ein Quarkus‑REST‑Service, der die verbleibenden Tage bis zur Rente liefert, lokal entwickeln und anschließend auf einem Raspberry Pi deployen.

Idee und Ziel
-Ein kleiner REST‑Endpunkt liefert JSON mit: verbleibenden Tagen
– Projekt: Quarkus, Java 21, RESTEasy Reactive, Jackson.

-Deployment: fast-jar mit quarkus-run.jar auf dem Raspberry Pi als systemd‑Service.
Ok, start, zuerst den Java Endpunkt:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
package de.wenzlaff.twcount; import java.time.LocalDate; import java.time.temporal.ChronoUnit; import jakarta.ws.rs.GET; import jakarta.ws.rs.Path; import jakarta.ws.rs.Produces; import jakarta.ws.rs.core.MediaType; /** * REST Endpunkt. * * @author Thomas Wenzlaff * */ @Path("/countdown") public class CountdownResource { private static final LocalDate TARGET_DATE = LocalDate.of(2027, 6, 1); @GET @Produces(MediaType.APPLICATION_JSON) public CountdownResponse getDaysCountdown() { LocalDate now = LocalDate.now(); long daysRemaining = ChronoUnit.DAYS.between(now, TARGET_DATE); // Falls das Ziel-Datum bereits vergangen ist, korrigiere auf 0 if (daysRemaining < 0) { daysRemaining = 0; } return new CountdownResponse(daysRemaining); } } |
Noch ein kleines BE:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
package de.wenzlaff.twcount; /** * BE. * * @author Thomas Wenzlaff * */ public class CountdownResponse { private long daysRemaining; public CountdownResponse(long daysRemaining) { this.daysRemaining = daysRemaining; } public long getDaysRemaining() { return daysRemaining; } public void setDaysRemaining(long daysRemaining) { this.daysRemaining = daysRemaining; } } |
Die pom.xml ist auch nicht groß:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 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 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 |
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>de.wenzlaff.twcount</groupId> <artifactId>de.wenzlaff.twcount</artifactId> <version>1.0.0</version> <properties> <compiler-plugin.version>3.14.0</compiler-plugin.version> <maven.compiler.release>21</maven.compiler.release> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <quarkus.platform.artifact-id>quarkus-bom</quarkus.platform.artifact-id> <quarkus.platform.group-id>io.quarkus.platform</quarkus.platform.group-id> <quarkus.platform.version>3.28.5</quarkus.platform.version> <skipITs>true</skipITs> <surefire-plugin.version>3.5.4</surefire-plugin.version> </properties> <dependencyManagement> <dependencies> <dependency> <groupId>${quarkus.platform.group-id}</groupId> <artifactId>${quarkus.platform.artifact-id}</artifactId> <version>${quarkus.platform.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <dependencies> <dependency> <groupId>io.quarkus</groupId> <artifactId>quarkus-rest</artifactId> </dependency> <dependency> <groupId>io.quarkus</groupId> <artifactId>quarkus-rest-jackson</artifactId> </dependency> <dependency> <groupId>io.quarkus</groupId> <artifactId>quarkus-arc</artifactId> </dependency> <dependency> <groupId>io.quarkus</groupId> <artifactId>quarkus-smallrye-openapi</artifactId> </dependency> <dependency> <groupId>io.quarkus</groupId> <artifactId>quarkus-junit5</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>io.rest-assured</groupId> <artifactId>rest-assured</artifactId> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>${quarkus.platform.group-id}</groupId> <artifactId>quarkus-maven-plugin</artifactId> <version>${quarkus.platform.version}</version> <extensions>true</extensions> <executions> <execution> <goals> <goal>build</goal> <goal>generate-code</goal> <goal>generate-code-tests</goal> <goal>native-image-agent</goal> </goals> </execution> </executions> </plugin> <plugin> <artifactId>maven-compiler-plugin</artifactId> <version>${compiler-plugin.version}</version> <configuration> <parameters>true</parameters> </configuration> </plugin> <plugin> <artifactId>maven-surefire-plugin</artifactId> <version>${surefire-plugin.version}</version> <configuration> <argLine>--add-opens java.base/java.lang=ALL-UNNAMED</argLine> <systemPropertyVariables> <java.util.logging.manager>org.jboss.logmanager.LogManager</java.util.logging.manager> <maven.home>${maven.home}</maven.home> </systemPropertyVariables> </configuration> </plugin> <plugin> <artifactId>maven-failsafe-plugin</artifactId> <version>${surefire-plugin.version}</version> <executions> <execution> <goals> <goal>integration-test</goal> <goal>verify</goal> </goals> </execution> </executions> <configuration> <argLine>--add-opens java.base/java.lang=ALL-UNNAMED</argLine> <systemPropertyVariables> <native.image.path>${project.build.directory}/${project.build.finalName}-runner</native.image.path> <java.util.logging.manager>org.jboss.logmanager.LogManager</java.util.logging.manager> <maven.home>${maven.home}</maven.home> </systemPropertyVariables> </configuration> </plugin> </plugins> </build> <profiles> <profile> <id>native</id> <activation> <property> <name>native</name> </property> </activation> <properties> <quarkus.package.jar.enabled>false</quarkus.package.jar.enabled> <skipITs>false</skipITs> <quarkus.native.enabled>true</quarkus.native.enabled> </properties> </profile> </profiles> </project> |
So, nun noch ein JUnit Test, das muss sein:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
package de.wenzlaff.twcount; import static org.junit.jupiter.api.Assertions.assertEquals; import java.time.LocalDate; import java.time.temporal.ChronoUnit; import org.junit.jupiter.api.Test; import io.quarkus.test.common.http.TestHTTPEndpoint; import io.quarkus.test.junit.QuarkusTest; import io.restassured.RestAssured; import io.restassured.http.ContentType; /** * Test mit gleichen Erwartungswerten wie Ressource. * * @author Thomas Wenzlaff * */ @QuarkusTest @TestHTTPEndpoint(CountdownResource.class) class CountdownResourceTest { @Test void testCountdownEndpoint() { LocalDate now = LocalDate.now(); long targetDays = ChronoUnit.DAYS.between(now, LocalDate.of(2027, 6, 1)); Integer expected = (int) Math.max(targetDays, 0); Integer actual = RestAssured.given().accept(ContentType.JSON).when().get() // Pfad kommt aus @TestHTTPEndpoint .then().statusCode(200).extract().path("daysRemaining"); assertEquals(expected, actual); } } |
So es läuft. Dann build und deploy:
mvn clean package -Dquarkus.package.jar.type=fast-jar
# in das target/quarkus-app Verzeichnis und alles auf den Pi kopieren
scp -r quarkus-app pi@vier:/home/pi
Swagger UI läuft auch mit: http://localhost:9009/q/swagger-ui/

Dann noch ein system Service für den PI einfach diese Datei anlegen /etc/systemd/system/quarkus.service
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
# /etc/systemd/system/quarkus.service [Unit] Description=Quarkus Fast-JAR Service After=network-online.target [Service] User=pi WorkingDirectory=/home/pi/quarkus-app ExecStart=/usr/bin/java -Dquarkus.http.host=IP-ADRESSE -jar /home/pi/quarkus-app/quarkus-run.jar Restart=on-failure RestartSec=5 [Install] WantedBy=multi-user.target |
Und aktivieren:
sudo systemctl enable quarkus.service
sudo systemctl daemon-reload
sudo systemctl start quarkus
So, dann noch den Service in n8n aufrufen und an ntfy versenden, das ist aber in der einen Stunde nicht mehr möglich. Aber nach GitLab habe ich es dann doch noch mit Pipeline gebracht.
