diff --git a/Dockerfile b/Dockerfile
index 4c9fe85..52ee4fd 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -54,7 +54,7 @@ WORKDIR $APP_DIR
COPY --from=builder /minimal-jre $JAVA_HOME
# Copy packaged app from builder image.
-COPY --from=builder --chown=$USER_NAME:$GROUP_NAME /usr/src/myapp/target/app.jar .
+COPY --from=builder --chown=$USER_NAME:$GROUP_NAME /usr/src/myapp/target/app-runner.jar ./app.jar
# Run the application.
USER $USER_NAME:$GROUP_NAME
diff --git a/README.md b/README.md
index fb7bf2f..db9af72 100644
--- a/README.md
+++ b/README.md
@@ -6,7 +6,7 @@
[![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)
A template project to create a Docker image for a Java application.
-The example application uses Spring Boot to expose an HTTP endpoint.
+The example application uses Quarkus to expose an HTTP endpoint.
> **Golang developer?** Check out https://github.com/miguno/golang-docker-build-tutorial
@@ -71,8 +71,8 @@ $ docker run -p 8123:8123 miguno/java-docker-build-tutorial:latest
running container.
```shell
-$ curl http://localhost:8123/greeting
-{"id":1,"name":"Hello, World!"}
+$ curl http://localhost:8123/status
+{"status":"idle"}
```
# Usage with just
@@ -111,10 +111,10 @@ on Windows).
$ ./mvnw clean package
# Run the application locally.
-$ java -jar target/app.jar
+$ java -jar target/app-runner.jar
# Alternatively, you can run the application via Maven.
-$ ./mvnw spring-boot:run
+$ ./mvnw quarkus:dev
```
# References
diff --git a/justfile b/justfile
index a64de9e..c75f185 100644
--- a/justfile
+++ b/justfile
@@ -17,6 +17,14 @@ system-info:
@echo "os: {{os()}}"
@echo "os family: {{os_family()}}"
+# build the native application locally (requires GraalVM)
+build-native:
+ @./mvnw install -Dnative
+
+# run the application locally (in Quarkus development mode) with hot reload
+dev:
+ @./mvnw quarkus:dev
+
# create a docker image (requires Docker)
docker-image-create:
@echo "Creating a docker image ..."
@@ -31,10 +39,15 @@ docker-image-run:
@echo "Running container from docker image ..."
@./start_container.sh
+# package the application to create an uber jar
+package:
+ @./mvnw package
+
+# run the application locally.
+run: package
+ @java -jar target/app-runner.jar
+
# send request to the app's HTTP endpoint (requires running container)
send-request-to-app:
- curl http://localhost:8123/greeting
+ curl http://localhost:8123/status
-# Run the application locally
-run:
- @./mvnw spring-boot:run
diff --git a/pom.xml b/pom.xml
index fe918ed..8f8b3e4 100644
--- a/pom.xml
+++ b/pom.xml
@@ -3,12 +3,7 @@
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
4.0.0
-
- org.springframework.boot
- spring-boot-starter-parent
- 3.2.5
-
-
+
com.miguno
java-docker-build
jar
@@ -19,30 +14,35 @@
Apache License 2.0
- http://www.apache.org/licenses/LICENSE-2.0.html
+ https://www.apache.org/licenses/LICENSE-2.0.html
repo
+ 3.13.0
17
- 3.1.6
5.10.2
- 2.1.3
- 2.3.1
+ true
${java.version}
UTF-8
+ true
+ quarkus-bom
+ io.quarkus.platform
+ 3.10.0
+ 3.2.5
- org.glassfish.jersey
- jersey-bom
- ${jersey.version}
+ ${quarkus.platform.group-id}
+ ${quarkus.platform.artifact-id}
+ ${quarkus.platform.version}
pom
import
+
org.junit
junit-bom
@@ -54,44 +54,21 @@
-
- org.springframework.boot
- spring-boot-starter-web
+ io.quarkus
+ quarkus-rest-jackson
- org.springframework.boot
- spring-boot-starter-test
+ io.quarkus
+ quarkus-junit5
test
- org.glassfish.jersey.containers
- jersey-container-grizzly2-http
-
-
-
- org.glassfish.jersey.inject
- jersey-hk2
-
-
- javax.inject
- javax.inject
-
-
-
-
-
- jakarta.activation
- jakarta.activation-api
- ${jakarta.activation-api.version}
-
-
-
- javax.xml.bind
- jaxb-api
- ${jaxb-api.version}
+ io.rest-assured
+ rest-assured
+ test
@@ -99,28 +76,26 @@
junit-jupiter
test
-
-
app
-
-
- org.springframework.boot
- spring-boot-maven-plugin
-
-
org.apache.maven.plugins
maven-compiler-plugin
- 3.13.0
+ ${compiler-plugin.version}
${java.version}
@@ -130,7 +105,13 @@
org.apache.maven.plugins
maven-surefire-plugin
- 3.2.5
+ ${surefire-plugin.version}
+
+
+ org.jboss.logmanager.LogManager
+ ${maven.home}
+
+
org.junit.jupiter
@@ -140,33 +121,57 @@
-
-
+
+
+ native
+
+
+ native
+
+
+
+ native
+
+
+
+
+ org.apache.maven.plugins
+ maven-failsafe-plugin
+ ${surefire-plugin.version}
+
+
+
+ integration-test
+ verify
+
+
+
+ ${project.build.directory}/${project.build.finalName}-runner
+ org.jboss.logmanager.LogManager
+ ${maven.home}
+
+
+
+
+
+
+
+
+
+
diff --git a/src/main/java/com/miguno/javadockerbuild/App.java b/src/main/java/com/miguno/javadockerbuild/App.java
deleted file mode 100644
index 1b9233f..0000000
--- a/src/main/java/com/miguno/javadockerbuild/App.java
+++ /dev/null
@@ -1,13 +0,0 @@
-package com.miguno.javadockerbuild;
-
-import org.springframework.boot.SpringApplication;
-import org.springframework.boot.autoconfigure.SpringBootApplication;
-
-@SpringBootApplication
-public class App {
-
- public static void main(String[] args) {
- SpringApplication.run(App.class, args);
- }
-
-}
diff --git a/src/main/java/com/miguno/javadockerbuild/Greeting.java b/src/main/java/com/miguno/javadockerbuild/Greeting.java
deleted file mode 100644
index 174fcb0..0000000
--- a/src/main/java/com/miguno/javadockerbuild/Greeting.java
+++ /dev/null
@@ -1,10 +0,0 @@
-package com.miguno.javadockerbuild;
-
-/**
- * {@link Greeting} is resource representation class, from which the eventual
- * JSON response message is being derived.
- *
- * @param id The unique ID of the response.
- * @param name The name to be greeted.
- */
-public record Greeting(long id, String name) { }
diff --git a/src/main/java/com/miguno/javadockerbuild/GreetingController.java b/src/main/java/com/miguno/javadockerbuild/GreetingController.java
deleted file mode 100644
index 80d4904..0000000
--- a/src/main/java/com/miguno/javadockerbuild/GreetingController.java
+++ /dev/null
@@ -1,24 +0,0 @@
-package com.miguno.javadockerbuild;
-
-import java.util.concurrent.atomic.AtomicLong;
-
-import org.springframework.web.bind.annotation.GetMapping;
-import org.springframework.web.bind.annotation.RequestParam;
-import org.springframework.web.bind.annotation.RestController;
-
-// The @RestController annotation marks this class as a controller, where every
-// method returns a domain object instead of a view. It is shorthand for
-// including both @Controller and @ResponseBody.
-@RestController
-public class GreetingController {
-
- private static final String template = "Hello, %s!";
- private final AtomicLong counter = new AtomicLong();
-
- @GetMapping("/greeting")
- public Greeting greeting(@RequestParam(value = "name", defaultValue = "World") String name) {
- // The new Greeting instance is automatically converted to JSON via
- // Spring’s HTTP message converter support (which uses Jackson 2).
- return new Greeting(counter.incrementAndGet(), String.format(template, name));
- }
-}
\ No newline at end of file
diff --git a/src/main/java/com/miguno/javadockerbuild/LoggingFilter.java b/src/main/java/com/miguno/javadockerbuild/LoggingFilter.java
new file mode 100644
index 0000000..7267be3
--- /dev/null
+++ b/src/main/java/com/miguno/javadockerbuild/LoggingFilter.java
@@ -0,0 +1,32 @@
+package com.miguno.javadockerbuild;
+
+import jakarta.ws.rs.container.ContainerRequestContext;
+import jakarta.ws.rs.container.ContainerRequestFilter;
+import jakarta.ws.rs.core.Context;
+import jakarta.ws.rs.core.UriInfo;
+import jakarta.ws.rs.ext.Provider;
+
+import org.jboss.logging.Logger;
+
+import io.vertx.core.http.HttpServerRequest;
+
+@Provider
+public class LoggingFilter implements ContainerRequestFilter {
+
+ private static final Logger LOG = Logger.getLogger(LoggingFilter.class);
+
+ @Context
+ UriInfo info;
+
+ @Context
+ HttpServerRequest request;
+
+ @Override
+ public void filter(ContainerRequestContext context) {
+ // Uncomment to enable HTTP request logging.
+ //final String method = context.getMethod();
+ //final String path = info.getPath();
+ //final String address = request.remoteAddress().toString();
+ //LOG.infof("Request %s %s from IP %s", method, path, address);
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/miguno/javadockerbuild/Status.java b/src/main/java/com/miguno/javadockerbuild/Status.java
new file mode 100644
index 0000000..861edc5
--- /dev/null
+++ b/src/main/java/com/miguno/javadockerbuild/Status.java
@@ -0,0 +1,10 @@
+package com.miguno.javadockerbuild;
+
+public class Status {
+
+ public String status;
+
+ public Status(String status) {
+ this.status = status;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/miguno/javadockerbuild/StatusResource.java b/src/main/java/com/miguno/javadockerbuild/StatusResource.java
new file mode 100644
index 0000000..c442ec6
--- /dev/null
+++ b/src/main/java/com/miguno/javadockerbuild/StatusResource.java
@@ -0,0 +1,16 @@
+package com.miguno.javadockerbuild;
+
+import jakarta.ws.rs.GET;
+import jakarta.ws.rs.Path;
+
+@Path("/status")
+public class StatusResource {
+
+ final private Status status = new Status("idle");
+
+ @GET
+ public Status list() {
+ return status;
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties
index 6a46f86..ee9a9a8 100644
--- a/src/main/resources/application.properties
+++ b/src/main/resources/application.properties
@@ -1,2 +1,2 @@
-spring.application.name=java-docker-build
-server.port=8123
+quarkus.http.port=8123
+quarkus.package.jar.type=uber-jar
diff --git a/src/test/java/com/miguno/javadockerbuild/AppTests.java b/src/test/java/com/miguno/javadockerbuild/AppTests.java
deleted file mode 100644
index 55ec34e..0000000
--- a/src/test/java/com/miguno/javadockerbuild/AppTests.java
+++ /dev/null
@@ -1,18 +0,0 @@
-package com.miguno.javadockerbuild;
-
-import org.junit.jupiter.api.Test;
-import org.springframework.boot.test.context.SpringBootTest;
-
-// The @SpringBootTest annotation tells Spring Boot to look for a main
-// configuration class (one with @SpringBootApplication, for instance)
-// and use that to start a Spring application context.
-@SpringBootTest
-class AppTests {
-
- @Test
- void contextLoads() {
- // This test is a simple sanity check test that will fail if the
- // application context cannot start.
- }
-
-}
diff --git a/src/test/java/com/miguno/javadockerbuild/GreetingControllerTest.java b/src/test/java/com/miguno/javadockerbuild/GreetingControllerTest.java
deleted file mode 100644
index 08d256f..0000000
--- a/src/test/java/com/miguno/javadockerbuild/GreetingControllerTest.java
+++ /dev/null
@@ -1,42 +0,0 @@
-package com.miguno.javadockerbuild;
-
-import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
-import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
-import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
-
-import org.junit.jupiter.api.Test;
-
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
-import org.springframework.boot.test.context.SpringBootTest;
-import org.springframework.http.MediaType;
-import org.springframework.test.web.servlet.MockMvc;
-
-// By default, @SpringBootTest does not start the web server, but instead sets
-// up a mock environment for testing web endpoints. Thus, this test covers
-// almost but not entirely the full stack.
-// https://docs.spring.io/spring-boot/docs/current/reference/html/features.html#features.testing
-@SpringBootTest
-@AutoConfigureMockMvc
-class GreetingControllerTest {
-
- @Autowired
- private MockMvc mockMvc;
-
- @Test
- void shouldReturnDefaultResponse() throws Exception {
- this.mockMvc.perform(get("/greeting").contentType(MediaType.APPLICATION_JSON))
- .andDo(print())
- .andExpect(status().isOk())
- .andExpect(jsonPath("$.name").value("Hello, World!"));
- }
-
- @Test
- void shouldReturnParameterizedResponse() throws Exception {
- var name = "Foo";
- this.mockMvc.perform(get("/greeting?name=" + name).contentType(MediaType.APPLICATION_JSON))
- .andDo(print())
- .andExpect(status().isOk())
- .andExpect(jsonPath("$.name").value("Hello, " + name + "!"));
- }
-}
\ No newline at end of file
diff --git a/src/test/java/com/miguno/javadockerbuild/StatusResourceIT.java b/src/test/java/com/miguno/javadockerbuild/StatusResourceIT.java
new file mode 100644
index 0000000..6fa823c
--- /dev/null
+++ b/src/test/java/com/miguno/javadockerbuild/StatusResourceIT.java
@@ -0,0 +1,5 @@
+package com.miguno.javadockerbuild;
+import io.quarkus.test.junit.QuarkusIntegrationTest;
+
+@QuarkusIntegrationTest
+public class StatusResourceIT extends StatusResourceTest { }
\ No newline at end of file
diff --git a/src/test/java/com/miguno/javadockerbuild/StatusResourceTest.java b/src/test/java/com/miguno/javadockerbuild/StatusResourceTest.java
new file mode 100644
index 0000000..e995b24
--- /dev/null
+++ b/src/test/java/com/miguno/javadockerbuild/StatusResourceTest.java
@@ -0,0 +1,21 @@
+package com.miguno.javadockerbuild;
+
+import static io.restassured.RestAssured.given;
+import static org.hamcrest.CoreMatchers.is;
+
+import io.quarkus.test.junit.QuarkusTest;
+import org.junit.jupiter.api.Test;
+
+@QuarkusTest
+public class StatusResourceTest {
+
+ @Test
+ public void verifyList() {
+ given()
+ .when().get("/status")
+ .then()
+ .statusCode(200)
+ .body("status", is("idle"));
+ }
+
+}
\ No newline at end of file
diff --git a/start_container.sh b/start_container.sh
index c0b2df6..c8c971a 100755
--- a/start_container.sh
+++ b/start_container.sh
@@ -14,6 +14,6 @@ declare -r DOCKER_OPTIONS="--platform linux/amd64"
# Import environment variables from .env
set -o allexport && source .env && set +o allexport
echo "Starting container for image '$DOCKER_IMAGE_NAME:$DOCKER_IMAGE_TAG', exposing port ${APP_PORT}/tcp"
-echo "- Run 'curl http://localhost:${APP_PORT}/greeting' to send a test request to the containerized app."
+echo "- Run 'curl http://localhost:${APP_PORT}/status' to send a test request to the containerized app."
echo "- Enter Ctrl-C to stop the container."
docker run $DOCKER_OPTIONS -p "$APP_PORT:$APP_PORT" "$DOCKER_IMAGE_NAME":"$DOCKER_IMAGE_TAG"