> <
Helidon logo and hibernate
Category: Technicals

Problem

If you're reading this it means that you have faced the same problem that I have.

So what's problem?

To config the hibernate search in helidon we have to use persistence.xml or hibernate.properties but none of which supports setting/reading from environment variables.
and unlike spring and quarkus, Helidon does not support hibernate search as of now (version 2.5.1), which means we can't use the application.yml and other Helidon configuration options to set the configs for hibernate search.

Why do we need to use environment variables?

There are multipele reasons, one of which is that It's not safe to have the credentials in source code and reading from environment variables is a known solution to that, yet it not the safest way and has its own downsides.

Solution

As you probably know we can use JVM properties to set the hibernate configuration in persistence.xml in following format:

<property name="hibernate.search.backend.hosts" value="${SEARCH_BACKEND_URL}" /> 

At first I've tried to use JVM properties to pass the values from environment variables to persistence.xml:

java -jar -DSEARCH_BACKEND_URL=$ENV_VAR_NAME MyApp.jar

Note: Also we can pass the Hibernate config directly (using JVM properties) insted of setting them in persistence.xml but that's essentially the same approch.

With this approch when I tried to build the Jlink image with provided dockerfile I didnt see a way to set the JVM properties to since Jlink image used an start script to run the app
and I didn't want to make my own image using jlink or anything out of the provided ways, so tried to add the JVM propeties options to start script by changing the built steps but with no luck.

Then I've subbmited an issue on helidon github asking if is there any upcoming support for hibernate search or an approch to this problem,
and as you can see no luck there and we have to wait and see if hibernate search support will come to helidon anytime soon or not.
After that I just noticed that I've just missed that the start script accepts the JVM properties as an input parameter:

ENTRYPOINT /bin/bash MyApp/bin/start --jvm -DSEARCH_BACKEND_URL=$BACKEND_SEARCH_URL

At this point there was a working way to set hibernate search credentials and configs from enviroment variables,
It was working well in container but the problem was that to run it in IDE I had to config IDE/project to pass the JVM properties for hibrenate search config,
and it didnt felt right that extera config was needed to run the project inside an IDE which lead me to a way that I think is the cleanest approch as of now which is reading the enviroment variables and setting them as jvm property.

As you probebly already know that in helidon a container managed orm well get configured on startup so we need to set JVM properties before that happens, And as helidon documentation shows the start point of application is a main method that starts the server which we can change (in pom.xml properties) and use our own main class, and insted of running the server ourself I rather to let helidon do it for me but before that I want to set my JVM properties, so the approch here is to add our own main class
and call the helidon's default main meothd is ours but set the configs before calling.

 

Example

If we have our hibernate config (persistence.xml) in following format then we can proceed

<property name="hibernate.search.backend.hosts" value="${SEARCH_BACKEND_URL}" /> 

 

Creating a configuration class (Optional)


package space.dastyar.myapp;

public class Configuration {

    public void setup() {
        hibernateSearch();
    }

    private void hibernateSearch() {

        var host = Optional.ofNullable(System.getenv("SEARCH_BACKEND_URL"));
        var protocol = Optional.ofNullable(System.getenv("APP_SEARCH_PROTOCOL"));
        var user = Optional.ofNullable(System.getenv("SEARCH_USER"));
        var password = Optional.ofNullable(System.getenv("SEARCH_PASSWORD"));

        System.setProperty("SEARCH_BACKEND_URL", host.orElse("localhost:9200"));
        System.setProperty("SEARCH_BACKEND_PROTOCOL", protocol.orElse("http"));
        System.setProperty("SEARCH_USER", user.orElse("elastic"));
        System.setProperty("SEARCH_PASSWORD", password.orElse("search_password"));
    }
}

 

Creating the main


package space.dastyar.myapp;

import io.helidon.microprofile.cdi.Main;

public class App{
    
    public static void main(String[] args) {
        new Configuration().setup();
        Main.main(args);
    }
}

Note that before starting the server we dont have dependency injection hence we have to create an instance of configuraion class manually.

 

Adding the main class in maven's pom.xml properites:


<properties>
    <mainClass>space.dastyar.myapp.App</mainClass>
    ....
</properties>

and that's it.

End

Let me know your thoughts on the matter.