Enterprise Java

Getting started with Quarkus

Quarkus – A Kubernetes Native Java stack tailored for OpenJDK HotSpot and GraalVM, crafted from the best of breed Java libraries and standards. – is a container-first framework optimized for fast boot times and low memory consumption. The framework is build on top of many popular Java libraries and it provides support for building standard REST as well as reactive and message-driven microservices. Thanks to the fast startup times and low memory usage Quarkus can also be used to implement functions in serverless environment. Quarkus gives a lot of possibilities to develop apps faster thanks to unified configuration, amazing live reloading features and tooling support.

Learn how to get started with Quarkus and build a PetClinic REST API.

This blog post covers:

  • Requirements for development environment
  • Creating new project
  • Developing, building and running the application with Java 11
  • Datasource configuration with Postgres and Flyway
  • CRUD service with pagination
  • Creating integration tests
  • Live reload and debugging
  • Dockerizing the application (both native and non-native)

About PetClinic API

I decided to re-use the PetClinic model I used in this blog post Spring Boot and Spring Data REST.

Basically, it is a basic CRUD service for managing an imaginary PetClinic: pets, vets, visits etc.

Prerequisities

Docker

Docker will be used for running the dockerized version of the service itself but it will be also used to run the PostgreSQL server.

JDK 11 with GraalVM

The PetClinic API will be built with Java 11 therefore JDK 11 must be installed. For building native executables GraalVM 19.3+ must be present and since it is build on top of OpenJDK 11 this will be the best choice for this tutorial. The easiest way to install (and manage multiple versions of) Java SDKs is with SDKMAN!

Learn how to manage multiple Java SDKs with SDKMAN! with ease

To support native images, make sure to install all needed dependencies. More info can be found in the GraalVM documentation: https://www.graalvm.org/docs/reference-manual/native-image/

GraalVM official documentation: GraalVM

Terminal

The service was developed on macOS with iTerm2 and oh-my-zsh. I also use httpie as my default HTTP client.

IntelliJ

My prefered IDE is IntelliJ and I used this while working on this project.

Learn more about the tools I used on macOS in this article: macOS: essential tools for (Java) developer

Run PostgreSQL with Docker

The application will connect to Postgres server and depending on the profile (dev, test, prod) different configuration will be applied. For this we will need three servers to be running: each with different database name, port and credentials. To simplify the setup, Docker can be utilized.

Dev database

  • Create and run the container:
1
$ docker run --name petclinic-db-dev -p 5433:5432 -e POSTGRES_DB=petclinic-dev -e POSTGRES_USER=petclinic-dev -e POSTGRES_PASSWORD=petclinic-dev -d postgres:alpine
  • Run previously stopped container:
1
$ docker start petclinic-db-dev

Test database

  • Create and run the container:
1
$ docker run --name petclinic-db-test -p 5434:5432 -e POSTGRES_DB=petclinic-test -e POSTGRES_USER=petclinic-test -e POSTGRES_PASSWORD=petclinic-test -d postgres:alpine
  • Run previously stopped container:
1
$ docker start petclinic-db-test

Prod database

  • Create and run the container:
1
$ docker run --name petclinic-db -p 5432:5432 -e POSTGRES_DB=petclinic -e POSTGRES_USER=petclinic -e POSTGRES_PASSWORD=petclinic -d postgres:alpine
  • Run previously stopped container:
1
$ docker start petclinic-db

Getting started

Bootstrap the application

You can bootstrap the application using Maven in command line or you can use the online generator. The online generator allows exploring the extensions and technologies that Quarkus application can be made of and it does not require local Maven installation. You can access the generator here: https://code.quarkus.io

The following extensions are needed to build PetClinic API service:

  • RESTEasy JAX-RS – REST framework implementing JAX-RS and more
  • RESTEasy Jackson – Jackson serialization support for RESTEasy
  • SmallRye OpenAPI – Document your REST APIs with OpenAPI – comes with Swagger UI
  • Hibernate ORM with Panache – Define your persistent model in Hibernate ORM with Panache
  • Hibernate Validator – Validate data coming to your REST endpoints
  • JDBC Driver – PostgreSQL – PostgreSQL database connector
  • Flyway – Handle your database schema migrations

Once the dependencies are selected, you can download the zip, extract it and start developing the service.

The downloaded project has a standard Maven project layout. It contains the Maven Wrapper so no local Maven installation is required to develop the project. You will also notice src/main/docker with Docker files for both native and JVM image.

The main configuration file – application.properties – is located in src/main/resources. This folder also holds META-INF/resources folder for static resources of the application, like index.html file.

Set Java version to 11 in pom.xml as well as in Docker files

The online generator generates project with Java 8 by default, so in order to use Java 11 some adjustments are needed.

  • In pom.xml of the generated project change the Java version:
1
2
<maven.compiler.source>11</maven.compiler.source>
    <maven.compiler.target>11</maven.compiler.target>
  • In src/main/docker/Dockerfile.jvm set ARG JAVA_PACKAGE=java-11-openjdk-headless

Run the project in development mode

Once the changes are made you can start the application. Open your terminal, navigate to project’s folder and run the following command:

1
$ ./mvnw compile quarkus:dev

Note: Quarkus has three buit-in modes: dev, test and prod depending on how you run the application.

Developing in IntelliJ

In IntelliJ you simply open the project’s folder or the pom.xml. (File > Open). The project can be started only with Maven. This can be done with Maven run configurations as there is no main class to start the application like for example in Spring Boot.

For me the best expierience while developing with Quarkus was when I was running the application in the terminal, outside the IntelliJ.

Debugging

When Quarkus application is executed in dev mode it starts with the debug protocol enabled (on port 5005). To debug Quarkus application in IntelliJ you need to attach a debugger to a running proces via Run > Attach to Process. I had no troubles with debugging the application.

Note: You can run the application in a dev mode with debugging disabled: ./mvnw quarkus:dev -Ddebug=false, but honestly I did not notice any performance issues with debugger enabled by default.

Live Reload

Live reload is on of the strongest features of Quarkus in my opinion. It works amazing. Basically you can change anything you want in the source code, execute the request and the application re-loads in a blink of an eye. I was refacoring classes and packages, moving files around, adding and removing endpoints and all of this with no single restart.

Data source configuration

All properties go to src/main/resources/application.properties.

Default data source properties (prod)

1
2
3
4
quarkus.datasource.url=jdbc:postgresql://localhost:5432/petclinic
quarkus.datasource.driver=org.postgresql.Driver
quarkus.datasource.username=petclinic
quarkus.datasource.password=petclinic

Dev data source properties (dev)

To set mode (or profile) specific properties use the %mode:

1
2
3
%dev.quarkus.datasource.url=jdbc:postgresql://localhost:5433/petclinic-dev
%dev.quarkus.datasource.username=petclinic-dev
%dev.quarkus.datasource.password=petclinic-dev

Test data source properties (test)

1
2
3
%test.quarkus.datasource.url=jdbc:postgresql://localhost:5434/petclinic-test
%test.quarkus.datasource.username=petclinic-test
%test.quarkus.datasource.password=petclinic-test

See also: https://quarkus.io/guides/datasource

Flyway migration

To utilize Flyway create db/migration folder in src/main/resources and add you migration files. My first migration file is called V1.0.0__PetClinic.sql and it contains all the schema (DDL) and the sample data for the service.

Note: Quarkus supports SQL import that can be configured via quarkus.hibernate-orm.sql-load-script for each profile, but I could not make it work. See the issue I reported on Github: https://github.com/quarkusio/quarkus/issues/7358

See also: https://quarkus.io/guides/flyway

JPA Entities

The PetClinic’s domain model is relatively simple, but it consists of some unidirectional and bi-directional associations, as well as basic inheritance which makes it a bit better than simple Hello World kind of model.

Please note that in this example the JPA entities are returned directly in JAX-RS resources by corresponding Panache repositories (see below), therefore entities classes contain a mix of JPA and Jackson annotations.

For example:

01
02
03
04
05
06
07
08
09
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
@Entity
@Table(name = "visits")
public class Visit extends BaseEntity {
 
    @Column(name = "visit_date")
    @JsonFormat(pattern = "yyyy/MM/dd HH:mm")
    private LocalDateTime date;
 
    @NotEmpty
    @Column(name = "description")
    private String description;
 
    @ManyToOne
    @JoinColumn(name = "pet_id")
    private Pet pet;
 
    @ManyToOne
    @JoinColumn(name = "vet_id")
    private Vet vet;
 
    public Visit() {
        this.date = LocalDateTime.now();
    }
}
 
@Entity
@Table(name = "vets",
        uniqueConstraints =
        @UniqueConstraint(columnNames = {"first_name", "last_name"})
)
public class Vet extends Person {
 
    @ManyToMany(fetch = FetchType.EAGER)
    @JoinTable(name = "vet_specialties", joinColumns = @JoinColumn(name = "vet_id"),
            inverseJoinColumns = @JoinColumn(name = "specialty_id"))
    @JsonIgnore
    private Set<Specialty> specialties;
 
    @OneToMany(cascade = CascadeType.ALL, mappedBy = "vet", fetch = FetchType.EAGER)
    @JsonIgnore
    private Set<Visit> visits;
}

All the entities are located in pl.codeleak.samples.petclinic.model package.

Hibernate ORM with Panache

If you are familiar with Spring, I guess you have heard of Spring Data project. Hibernate ORM with Panache has similar goal in my opinion: it simplifies JPA development by removing the need of doing repeative and tedious work. Panache supports sorting, pagination, java.util.Optional and java.utitl.stream.Stream etc.

You have two approaches to work with Panache: creating entities with PanacheEntity or creating repositories with PanacheRepository. I tried both approaches in this project, but due to some issues with inheritance in entities I decided to stick to old-fashioned way.

A basic repository definition with Hibernate ORM with Panache:

1
2
3
4
5
public class OwnerRepository implements PanacheRepository<Owner> {
    List<Owner> findByLastName(String lastName) {
        return list("lastName", lastName);
    }
}

All the repositories are located in pl.codeleak.samples.petclinic.repository package.

See also: https://quarkus.io/guides/hibernate-orm-panache

Creating REST API

JAX-RS Resources

Quarkus utilizes JAX-RS with RESTEasy. To create API endpoints we need to create JAX-RS resources:

01
02
03
04
05
06
07
08
09
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
@Path(OwnerResource.RESOURCE_PATH)
@Produces(MediaType.APPLICATION_JSON)
public class OwnerResource {
 
    public static final String RESOURCE_PATH = "/owners";
 
    @Context
    UriInfo uriInfo;
 
    @Inject
    OwnerRepository ownerRepository;
 
    @Inject
    PetRepository petRepository;
 
    @GET
    public Response getAll(@BeanParam PageRequest pageRequest) {
         
    }
 
    @GET
    @Path("{id}")
    public Response getOne(@PathParam("id") Long id) {
 
    }
 
    @GET
    @Path("{id}/pets")
    public List<Pet> getPets(@PathParam("id") Long id) {
     
    }
 
    @POST
    @Consumes(MediaType.APPLICATION_JSON)
    @Transactional
    public Response create(@Valid Owner owner) {
     
    }
}

Dependency injection is done with CDI – Context and Dependency Injection. The resource objects will be automatically configured by Quarkus. All other dependencies must be configured for dependency injection with CDI annotations.

For example, the repositories can be annotated with @ApplicationScoped and then injected with @Inject:

01
02
03
04
05
06
07
08
09
10
11
@ApplicationScoped
public class OwnerRepository implements PanacheRepository<Owner> {
    List<Owner> findByLastName(String lastName) {
        return list("lastName", lastName);
    }
}
 
@ApplicationScoped
public class PetRepository implements PanacheRepository<Pet> {
 
}

All the resources are located in pl.codeleak.samples.petclinic.api package.

See also: https://quarkus.io/guides/cdi-reference

Pagination

As mentioned earlier, Panache provides support for paginated results. We can easily utilize this in our resources easily:

1
2
3
4
5
6
@GET
public Response getAll(@BeanParam PageRequest pageRequest) {
    return Response.ok(((PanacheRepository) petRepository).findAll()
                    .page(Page.of(pageRequest.getPageNum(), pageRequest.getPageSize()))
                    .list()).build();
}

The PageRequest is a bean that holds the pageNum and pageSize query parameters:

01
02
03
04
05
06
07
08
09
10
11
public class PageRequest {
 
    @QueryParam("pageNum")
    @DefaultValue("0")
    private int pageNum;
 
    @QueryParam("pageSize")
    @DefaultValue("10")
    private int pageSize;
 
}

Executing paginated request can be done easily with httpie:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
$ http get :8080/owners pageNum==0 pageSize==2
 
HTTP/1.1 200 OK
Content-Length: 250
Content-Type: application/json
 
[
    {
        "address": "110 W. Liberty St.",
        "city": "Madison",
        "firstName": "George",
        "id": 1,
        "lastName": "Franklin",
        "telephone": "6085551023"
    },
    {
        "address": "638 Cardinal Ave.",
        "city": "Sun Prairie",
        "firstName": "Betty",
        "id": 2,
        "lastName": "Davis",
        "telephone": "6085551749"
    }
]

Transactions

Creating a new object in JPA requires an active transaction. In order to bind the transaction to current method in a resource object use @Transactional, otherwise an exception will be thrown during the execution of the method:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
@POST
@Consumes(MediaType.APPLICATION_JSON)
@Transactional
public Response create(@Valid Owner owner) {
 
    ownerRepository.persist(owner);
 
    var location = uriInfo.getAbsolutePathBuilder()
            .path("{id}")
            .resolveTemplate("id", owner.getId())
            .build();
 
    return Response.created(location).build();
}

Create new resource with httpie:

01
02
03
04
05
06
07
08
09
10
11
12
$ http post :8080/owners <<< '
{
    "address": "110 W. Liberty St.",
    "city": "Madison",
    "firstName": "George",
    "lastName": "Franklin",
    "telephone": "6085551023"
}'
 
HTTP/1.1 201 Created
Content-Length: 0
Location: http://localhost:8080/owners/1042

Validation

The project uses Hibernate Validator extension. With this extension you can use standard Hibernate validation annotations (e.g. @NotBlank) and when the input parameter to the resource methods is annotated with @Valid the validation will be automatically triggerred and an error response will be returned to the client calling that method.

Example response for the following request:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
$ http post :8080/owners <<< '{}'
 
HTTP/1.1 400 Bad Request
Content-Length: 626
Content-Type: application/json
validation-exception: true
 
{
    "classViolations": [],
    "exception": null,
    "parameterViolations": [
        {
            "constraintType": "PARAMETER",
            "message": "must not be empty",
            "path": "create.owner.address",
            "value": ""
        },
         
        ...
 
        {
            "constraintType": "PARAMETER",
            "message": "must not be empty",
            "path": "create.owner.telephone",
            "value": ""
        }
    ],
    "propertyViolations": [],
    "returnValueViolations": []
}

Note on live reload fumctionality: you can make any change to the source code and execute new request with httpie. The application reloads quickly and you get immediate results. No restarts are needed.

See also: https://quarkus.io/guides/validation

Java 8 Date & Time support

java.util.time types are supported during JSON serialization and deserialization when the RESTEasy Jackson extension is in the project.

In the below example a visit date with be serialized and deserialized in the format provided by @JsonFormat annotation:

1
2
3
4
5
6
7
8
9
@Entity
@Table(name = "visits")
public class Visit extends BaseEntity {
 
    @Column(name = "visit_date")
    @JsonFormat(pattern = "yyyy/MM/dd HH:mm")
    private LocalDateTime date;
 
}

Check how date is serialized using htppie:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
$ http get :8080/visits/1
 
HTTP/1.1 200 OK
Content-Length: 174
Content-Type: application/json
 
{
    "date": "2013/01/01 00:00",
    "description": "rabies shot",
    "id": 1,
    "pet": {
        "birthDate": "2012/09/04",
        "id": 7,
        "name": "Samantha"
    },
    "vet": {
        "firstName": "Helen",
        "id": 2,
        "lastName": "Leary"
    }
}

You can also store the visit using the required datetime format in the request body:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
$ http post :8080/visits <<< '
{
    "date": "2020/01/01 00:00",
    "description": "lorem ipsum",
    "pet": {
        "id": 7
    },
    "vet": {
        "id": 2
    }
}'
 
HTTP/1.1 201 Created
Content-Length: 0
Location: http://localhost:8080/visits/1042

OpenAPI / Swagger support

SmallRye OpenAPI extension takes care of providing API documentation and SwaggerUI is enabled in the dev mode.

The default endpoints are:

  • OpenAPI documentaion – /openapi
  • SwaggerUI – /swaggerui

See also: https://quarkus.io/guides/openapi-swaggerui

Integration tests

Quarkus uses JUnit 5 and RESTAssured for integration testing. Tests can be created using @QuarkusTest annotations and they are executed with test profile active by default.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
@QuarkusTest
public class PetResourceTest {
 
    @Test
    public void pagedList() {
        given()
                .when().get("/pets?pageNum=0&pageSize=2")
                .then()
                .statusCode(200)
                .body(
                        "$.size()", is(2),
                        "name", containsInAnyOrder("Leo", "Basil")
                );
    }
}

Quarkus tests require the application to be running. There are possibilities to replace selected beans in test by using CDI @Alternate beans definitions. The alternate beans must be placed in src/test/java.

Note: Thanks to the profiles support you can easily configure the datasource for the test profile with a seperate database container. See Test data source properties.

See also: https://quarkus.io/guides/getting-started-testing

Packaging and running the application

The application can be packaged ./mvnw package.

It produces the executable quarkus-petclinic-api-1.0.0-runner.jar file in /target directory with the dependencies are copied into the target/lib directory.

1
2
3
4
5
6
7
8
[INFO] [io.quarkus.deployment.pkg.steps.JarResultBuildStep] Building thin jar: /Users/rafal.borowiec/Projects/quarkus/quarkus-petclinic-api/target/quarkus-petclinic-api-1.0.0-runner.jar
[INFO] [io.quarkus.deployment.QuarkusAugmentor] Quarkus augmentation completed in 1888ms
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  15.868 s
[INFO] Finished at: 2020-02-23T19:18:25+01:00
[INFO] ------------------------------------------------------------------------

The application is now runnable using java -jar target/quarkus-petclinic-api-1.0.0-runner.jar.

1
2
3
2020-02-23 19:19:10,169 INFO  [io.quarkus] (main) quarkus-petclinic-api 1.0.0 (running on Quarkus 1.2.1.Final) started in 2.011s. Listening on: http://0.0.0.0:8080
2020-02-23 19:19:10,171 INFO  [io.quarkus] (main) Profile prod activated.
2020-02-23 19:19:10,171 INFO  [io.quarkus] (main) Installed features: [agroal, cdi, flyway, hibernate-orm, hibernate-orm-panache, hibernate-validator, jdbc-postgresql, narayana-jta, rest-client, resteasy, resteasy-jackson, smallrye-openapi]

Note: The uber-jar can be packaged with ./mvnw clean package -DskipTests=true -Dquarkus.package.uber-jar=true

Create a Docker container that runs the application in JVM mode

1
2
3
4
5
$ ./mvnw clean package
$ docker build -f src/main/docker/Dockerfile.jvm -t quarkus/petclinic-api-jvm .
 
Successfully built 1a5d963fedfa
Successfully tagged quarkus/petclinic-api-jvm:latest

Run the container with a link do the Postgres database container and override the datasource url with environment variable:

1
2
3
4
5
6
$ docker run -i --rm -p 8080:8080 --link petclinic-db -e QUARKUS_DATASOURCE_URL='jdbc:postgresql://petclinic-db/petclinic' quarkus/petclinic-api-jvm
 
 
2020-02-23 20:39:18,949 INFO  [io.quarkus] (main) quarkus-petclinic-api 1.0.0 (running on Quarkus 1.2.1.Final) started in 3.475s. Listening on: http://0.0.0.0:8080
2020-02-23 20:39:18,949 INFO  [io.quarkus] (main) Profile prod activated.
2020-02-23 20:39:18,949 INFO  [io.quarkus] (main) Installed features: [agroal, cdi, flyway, hibernate-orm, hibernate-orm-panache, hibernate-validator, jdbc-postgresql, narayana-jta, rest-client, resteasy, resteasy-jackson, smallrye-openapi

Note: petclinic-db is a name of the Postgres container created here: Prod database. We also need to pass the datasource url. Read more about overriding the configuration properties at runtime: Overriding properties at runtime

Create native executable

You can create a native executable using the following command:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
$ ./mvnw package -Pnative
 
[INFO] [io.quarkus.deployment.pkg.steps.NativeImageBuildStep] Building native image from /Users/rafal.borowiec/Projects/quarkus/quarkus-petclinic-api/target/quarkus-petclinic-api-1.0.0-native-image-source-jar/quarkus-petclinic-api-1.0.0-runner.jar
 
...
 
[quarkus-petclinic-api-1.0.0-runner:50503]   (typeflow):  72,535.72 ms
[quarkus-petclinic-api-1.0.0-runner:50503]    (objects):  49,325.68 ms
[quarkus-petclinic-api-1.0.0-runner:50503]   (features):   3,115.04 ms
[quarkus-petclinic-api-1.0.0-runner:50503]     analysis: 135,220.10 ms
[quarkus-petclinic-api-1.0.0-runner:50503]     (clinit):   1,966.77 ms
[quarkus-petclinic-api-1.0.0-runner:50503]     universe:   6,919.51 ms
[quarkus-petclinic-api-1.0.0-runner:50503]      (parse):  13,679.33 ms
[quarkus-petclinic-api-1.0.0-runner:50503]     (inline):  18,193.40 ms
[quarkus-petclinic-api-1.0.0-runner:50503]    (compile):  70,849.75 ms
[quarkus-petclinic-api-1.0.0-runner:50503]      compile: 111,062.75 ms
[quarkus-petclinic-api-1.0.0-runner:50503]        image:   8,843.46 ms
[quarkus-petclinic-api-1.0.0-runner:50503]        write:   1,789.58 ms
[quarkus-petclinic-api-1.0.0-runner:50503]      [total]: 282,727.03 ms
[INFO] [io.quarkus.deployment.QuarkusAugmentor] Quarkus augmentation completed in 287304ms
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  04:58 min
[INFO] Finished at: 2020-02-23T19:25:10+01:00
[INFO] ------------------------------------------------------------------------

The process of creating the native executable takes quite some time but it is worth waiting for it to finish to see the startup time of application:

1
2
3
4
5
6
$ ./target/quarkus-petclinic-api-1.0.0-runner
 
 
2020-02-23 19:26:03,959 INFO  [io.quarkus] (main) quarkus-petclinic-api 1.0.0 (running on Quarkus 1.2.1.Final) started in 0.066s. Listening on: http://0.0.0.0:8080
2020-02-23 19:26:03,959 INFO  [io.quarkus] (main) Profile prod activated.
2020-02-23 19:26:03,959 INFO  [io.quarkus] (main) Installed features: [agroal, cdi, flyway, hibernate-orm, hibernate-orm-panache, hibernate-validator, jdbc-postgresql, narayana-jta, rest-client, resteasy, resteasy-jackson, smallrye-openapi]

0.67 seconds for native executable to start comparing to 2 seconds for the JVM version.

Create a Docker container that runs the application in native mode

By default, the native executable is created in the format supported by your operating system. Because the container may not use the same executable format as the one produced by your operating system, Maven build can produce an executable from inside a container:

1
$ ./mvnw package -Pnative -Dquarkus.native.container-build=true

To adjust the version of the builder image you need to set quarkus.native.builder-image property:

1
$ ./mvnw clean package -Pnative -DskipTests=true -Dquarkus.native.container-build=true -Dquarkus.native.builder-image=quay.io/quarkus/ubi-quarkus-native-image:20.0.0-java11

And now, build and run the container:

1
2
3
$ docker build -f src/main/docker/Dockerfile.native -t quarkus/petclinic-api .
 
$ docker run -i --rm -p 8080:8080 quarkus/petclinic-api

Note: More on building native executables can be found in the Quarkus documentation: https://quarkus.io/guides/building-native-image

Source code

The source code for this article can be found on Github: https://github.com/kolorobot/quarkus-petclinic-api

Published on Java Code Geeks with permission by Rafal Borowiec, partner at our JCG program. See the original article here: Getting started with Quarkus

Opinions expressed by Java Code Geeks contributors are their own.

Rafal Borowiec

Software developer, Team Leader, Agile practitioner, occasional blogger, lecturer. Open Source enthusiast, quality oriented and open-minded.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Back to top button