Configuring A Quarkus Application With Profiles

This blog post follows the first one I wrote about Configuring A Quarkus Application. So, if you want to know how to configure a Quarkus application using a Unified Configuration (application.properties file), Microprofile Config or system properties, please refer to this previous post. In this blog post I will show you how to use Profiles in Quarkus.

Use Case

I’ll use the same example as the previous post: we have one REST Endpoint that uses a repository to persist and retrieve books from a database:

  • BookResource: a JAX-RS endpoint with a set of methods to GET, POST, UPDATE and DELETE a book using the BookRepository
  • BookRepository: transactional repository dealing with the EntityManager to persist, retrieve, update and delete data from a relational database
  • Book: JPA entity representing a book
  • application.properties: property file where we configure the Quarkus application depending on a profile
  • import-dev.sql and import-test.sql to import data in the database depending on the profile
  • BookResourceTest: Quarkus test class using RESTAssured to test the BookResource
  • Postgres is used in development and H2 is used in test

This is a simple application in terms of business logic. The idea of this blog post is to show you how Quarkus can configure this application depending on a profile (dev, test, prod, your own profile…).

Quarkus Profiles

Quarkus supports the notion of Profiles (if you know Spring Profile, it’s a bit similar). These allow you to have multiple configuration per profile in the same application.properties file. The syntax is %{profile}.config.key=value. So if our application needs a variable called isbn.prefix the following configuration will set the value 1234 for all profiles, but this value gets overridden for the profile dev and test.

isbn.prefix=1234
%dev.isbn.suffix=DEV
%test.isbn.suffix=TEST

By default Quarkus comes with three profiles:

  • dev – Activated when in development mode (i.e. mvn quarkus:dev)
  • test – Activated when running tests (i.e. mvn test)
  • prod – The default profile when not running in development or test mode

And as you will see below, it’s quite easy to create your own profile and build your application using your own profile configuration.

Configuring the Application with Profiles

By default, Quarkus configures an application by reading the application.properties file located under the src/main/resources directory. In the previous post I showed you how to have an application.properties file for development and another one for testing. But using profiles, you don’t have to have separate application.properties files: you centralize all the configuration in a single property files and use profiles to differentiate development and testing.

In the following application.properties file we have global properties that are available for all profiles (eg. isbn.prefix or quarkus.datasource.url) but we also override some properties depending on the profile (eg. %dev.isbn.suffix or %test.isbn.suffix) :

# GLOBAL
isbn.prefix=1234
quarkus.datasource.url = jdbc:postgresql://localhost:5432/cdbookstoreDB
quarkus.datasource.driver = org.postgresql.Driver
quarkus.datasource.username = cdbookstoreDB
quarkus.datasource.password = h2g2

quarkus.hibernate-orm.database.generation=drop-and-create
quarkus.hibernate-orm.log.sql=false

quarkus.log.category."org.agoncal".level=WARN


# DEV
%dev.quarkus.hibernate-orm.log.sql=true
%dev.quarkus.hibernate-orm.sql-load-script = import-dev.sql

%dev.quarkus.log.category."org.agoncal".level=INFO

%dev.isbn.suffix=DEV


# TEST
%test.quarkus.datasource.url = jdbc:h2:mem:cdbookstoreDB
%test.quarkus.datasource.driver = org.h2.Driver

%test.quarkus.hibernate-orm.database.generation=drop-and-create
%test.quarkus.hibernate-orm.log.sql=true
%test.quarkus.hibernate-orm.sql-load-script = import-test.sql

%test.quarkus.log.category."org.agoncal".level=DEBUG

%test.isbn.suffix=TEST

This single application.properties file informs Quarkus to use an H2 database in test (%test.quarkus.datasource.driver = org.h2.Driver) and Postgres for all the other profiles (quarkus.datasource.driver = org.postgresql.Driver).

Executing SQL Depending on the Profile

If you use Hibernate, you might know that to execute some SQL statements at startup (eg. load data into the database), you just add an import.sql file in the root of your resources directory. This behavior has been extended with profiles. You can suffix the profile name and have a file called import-dev.sql with SQL statements only for the development profile or import-test.sql for the test profile.

Creating a new Profile

Quarkus comes with 3 profiles (dev, test, prod) but we might need other profiles. This is quite easy to do as Quarkus will simply use the quarkus-profile system property or the QUARKUS_PROFILE environment variable. Let’s say with have a staging environment and want to set some specific values for this environment. It’s just a matter of adding these variables with the prefix %staging in the application.properties:

# STAGING
%staging.quarkus.log.category."org.agoncal".level=INFO
%staging.isbn.suffix=STAG

Then, you set the system variable. For example, if you want to develop using the staging profile, just execute: mvn -Dquarkus-profile=staging compile quarkus:dev.

Displaying the Profile at Startup

If we create many profiles we might end-up lost and wonder with which profile a particular application has been built with. So what about displaying the current profile at startup? For that, we can use a nice little Quarkus trick: Quarkus gives us the ability to execute code at the application initialization and termination time. That’s because Quarkus relies on CDI and that we just need to observe the StartupEvent and ShutdownEvent. Thanks to the io.quarkus.runtime.configuration.ProfileManager, it’s just a matter of invoking a method to display the active profile.

@ApplicationScoped
public class ApplicationLifeCycle {

    private static final Logger LOGGER = LoggerFactory.getLogger("ApplicationLifeCycle");

    void onStart(@Observes StartupEvent ev) {
        LOGGER.info("The application is starting with profile " + ProfileManager.getActiveProfile());
    }
}

Executing with Different Profiles

The Microprofile Config also allows you to configure the application by passing system properties (and also environment variables). You can do so when running on HotSpot (by passing variables to the JVM such as java -Disbn.prefix=PROD) or pass properties to the executable binary. Let’s do that.

One of the beauties of Quarkus is that it can be used to build native code thanks to Graal VM. To do so, you need to have setup Graal VM, set the GRAALVM_HOME variable. Then, leave Maven and the Quarkus plugin do the rest just by executing a mvn clean package -Pnative command. You end up with a binary file in the target folder (in the case of this sample, the file is 52Mb and is called configuration-1.0-runner). You can then execute the binary passing properties with a -D. As you can see below, we override the isbn.prefix property:

$ mvn compile quarkus:dev

INFO  [io.qua.dep.QuarkusAugmentor] (main) Beginning quarkus augmentation
INFO  [io.qua.dep.QuarkusAugmentor] (main) Quarkus augmentation completed in 658ms
INFO  [ApplicationLifeCycle] (main) The application is starting with profile dev
$ mvn -Dquarkus-profile=staging compile quarkus:dev

INFO  [io.qua.dep.QuarkusAugmentor] (main) Beginning quarkus augmentation
INFO  [io.qua.dep.QuarkusAugmentor] (main) Quarkus augmentation completed in 613ms
INFO  [ApplicationLifeCycle] (main) The application is starting with profile staging

Conclusion

Microprofile Config brings configuration out of the box, but Quarkus goes further by implementing the notion of profiles. Because our applications do not behave the same and don’t use the same resources while they are being developed or in production, you can have properties per profile in the application.properties file. Quarkus comes with a dev, test and prod profile but you can create your owns.

Download the code, give it a try and leave some comments.

References

Categories: Java

Tagged as:

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s