Search

Dark theme | Light theme
Showing posts with label Helidon:Helpings. Show all posts
Showing posts with label Helidon:Helpings. Show all posts

February 20, 2025

Helidon SE Helpings: Serving Observe Endpoints On Different Port

When you enable the /observe endpoints you can configure them to be served on a different port than the application. By default the endpoints are available on the same port as the application. But you can define an extra named socket with another port number in the configuration of the WebServer instance. And in the configuration of the ObserveFeature instance you can define the socket name that should be used for the observe endpoints.

You can use configuration properties or code to configure the extra socket.

February 17, 2025

Helidon SE Helpings: Add Git Information To Info Endpoint

In a previous post you learned how to add information to the /observe/info endpoint. You can also add Git information to the endpoint. For example you can add the Git commit id so you can see check, when the application is running in a production environment, which Git commit for the code was deployed. In order to achieve this you must first generate a properties file with all Git information. The next step is to process this file in your Helidon SE application and add the properties to the /observe/info endpoint.

February 11, 2025

Helidon SE Helpings: Adding Information To Info Endpoint

It is possible to add an endpoint to Helidon SE that can show information about the application. You can add custom information to this endpoint. In order to enable the endpoint you need to add the dependency io.helidon.webserver.observe:helidon-webserver-observe-info to your pom.xml file. This will add the endpoint /observe/info to your application. You can add key-value pairs to your configuration or code that will be exposed in the endpoint.

February 8, 2025

Helidon SE Helpings: Return Response Based On Request Accept Header

Suppose you want to return a response based on the Accept header of the request. If the Accept header is application/json you want to return a JSON response and if the Accept header is application/xml you want to return an XML response. You can use the isAccepted(MediaType) of the ServerRequestHeaders class to check if the value of the request header Accept is equal to the specified media type. The method returns true if the media type is defined for the request header Accept and false if not.

February 7, 2025

Helidon SE Helpings: Configure Memory Health Check

You can configure a memory health check in Helidon SE. A memory health check will return a status of UP if the memory usage is below a certain threshold percentage and DOWN if the memory usage is above the threshold percentage. The default threshold percentage is 98%. To add the memory health check you need to add the dependency io.helidon.health:helidon-health-checks to your pom.xml file. This dependency contains three health checks: disk space usage, memory usage and dead lock detection.

January 29, 2025

Helidon SE Helpings: Configure Disk Space Health Check

In Helidon SE you can enable a health check for the disk space usage. If the disk space usage is above a certain threshold then the health check will fail. To enable the disk space health check you need to add the dependency io.helidon.health:helidon-health-checks to your pom.xml file. The dependency contains three health checks: disk space usage, memory usage and dead lock detection. To configure the disk space health check you need to set the configuration property server.features.observe.observers.health.helidon.health.diskSpace.thresholdPercent to the threshold percentage. Or programmatically set the value in your application code. The default value is 99.999, which means that in real life the health check will not fail. You need to set a lower percentage in order to see the health check fail. For example when you set the value to 95.0 then the health check will fail when the disk space usage is above 95% or less than 5% of the disk space is available. You can also configure the path to check for disk space usage. The default path is the current working directory, but it can be set to another path. You need to set the configuration property server.features.observe.observers.health.helidon.health.diskSpace.path to change the path.

January 24, 2025

Helidon SE Helpings: Show Details For Health Endpoint

With Helidon SE you can add a health endpoint to your application by simply adding a dependency. In your pom.xml you have to add the dependency io.helidon.webserver.observe:helidon-webserver-observe-health. This adds a new endpoint /health to your application. When your application is up and running the /health endpoint will return the HTTP status code 204 with an empty body. If your application is not healthy then the HTTP status code 503 is returned. In case of an error an HTTP status code 500 is sent to the client.

October 24, 2024

Helidon SE Helpings: Default Configuration Sources During Testing

In a previous blog post we learned about the default input sources that are used by Helidon SE. The list of input sources is different based on which artifacts are on the classpath of our application. When we write tests for code in our application that uses the default configuration created by Config.create() we must take into account that different input sources are used. Also here it is based on the artifacts that are on the classpath. That means that different files with configuration data are loaded, eg. a file application-test.conf when we have the artifact helidon-config-hocon and a file application-test.yml if the artifact helidon-config-yaml is on the classpath.

If we use the artifact helidon-config then the following input sources are searched with the following order of preference:

  1. System environment variables
  2. Java system properties
  3. a file META-INF/microprofile-config-test.properties on the classpath,
  4. a file META-INF/microprofile-config.properties on the classpath.

Notice that there is no input source that looks for a file application.properties on the classpath, but when we run our application that is a valid input source. Also the order of the input sources system environment variables and Java system properties switched. So the input sources for a default configuration created with Config.create() is different during run-time and when we test our application.

In the following test we create a default configuration using Config.create() and get the configuration property app.message that is set using a system environment variable, Java system property and the file META-INF/microprofile-config-test.properties placed in src/test/resources (which will be on the classpath). In order to test with setting a system environment variable in our test code we use the library com.github.stefanbirkner:system-lambda by adding the following dependency:

<dependency>
    <groupId>com.github.stefanbirkner</groupId>
    <artifactId>system-lambda</artifactId>
    <version>1.2.1</version>
    <scope>test</scope>
</dependency>

Our properties file looks like this:

# File: src/test/resources/META-INF/microprofile-config-test.properties
app.message=Hello from classpath:META-INF/microprofile-config-test.properties

Our test class has three test methods to check the value of the configuration property app.message:

// File: src/test/java/mrhaki/helidon/DefaultConfigTest.java
package mrhaki.helidon;

import io.helidon.config.Config;
import org.junit.jupiter.api.Test;

import static com.github.stefanbirkner.systemlambda.SystemLambda.restoreSystemProperties;
import static com.github.stefanbirkner.systemlambda.SystemLambda.withEnvironmentVariable;
import static org.assertj.core.api.Assertions.assertThat;

public class DefaultConfigTest {

    @Test
    void defaultConfig() throws Exception {
        // expect
        withEnvironmentVariable("APP_MESSAGE", "Hello from environment variable")
                .execute(() -> {
                    final Config config = Config.create();

                    assertThat(config.get("app.message").asString().asOptional())
                            .hasValue("Hello from environment variable");
                });
    }

    @Test
    void withSystemProperties() throws Exception {
        restoreSystemProperties(() -> {
            System.setProperty("app.message", "Hello from Java system property");
            assertThat(Config.create().get("app.message").asString().asOptional())
                    .hasValue("Hello from Java system property");
        });
    }

    @Test
    void withConfigTestProperties() {
        // given
        final Config config = Config.create();

        // expect
        assertThat(config.get("app.message").asString().asOptional())
                .hasValue("Hello from classpath:META-INF/microprofile-config-test.properties");
    }
}

When we apply the artifact helidon-config-hocon in our pom.xml file then the following input sources are searched with the following order of preference:

  1. System environment variables,
  2. Java system properties,
  3. a file application-test.json,
  4. a file application-test.conf,
  5. classpath:application-test.json,
  6. a file application-test.conf on the classpath,
  7. a file application.json,
  8. a file application.conf,
  9. a file application.json on the classpath,
  10. a file application.conf on the classpath,
  11. a file META-INF/microprofile-config-test.properties on the classpath,
  12. a file META-INF/microprofile-config.properties.` on the classpath

If we would use the artifact helidon-config-yaml then the following input sources are searched with the following order of preference:

  1. System environment variables,
  2. Java system properties,
  3. a file application-test.yml,
  4. a file application-test.yaml,
  5. a file application-test.yml on the classpath,
  6. a file application-test.yaml on the classpath,
  7. a file application.yml,
  8. a file application.yaml,
  9. a file application.yml on the classpath,
  10. a file application.yaml on the classpath,
  11. a file META-INF/microprofile-config-test.properties on the classpath,
  12. a file META-INF/microprofile-config.properties on the classpath.

Written with Helidon SE 4.1.2.

October 17, 2024

Helidon SE Helpings: Default Configuration Sources

When we use Helidon SE we can use the Config class to pass configuration properties to our application. The static method create() creates a default configuration. The Config class is then configured to support different input sources. This configuration reads configuration properties from the following sources in order:

  1. Java system properties,
  2. system environment variables,
  3. a file on the classpath that has the name application.properties (based on default config parser that is part of the artifact helidon-config).

The last input source behaves differently based on which classes that can parse a configuration file are on the classpath of our application. If we use the helidon-config artifact on the classpath then the configuration file read is application.properties. To read a JSON formatted configuration file we must add the helidon-config-hocon artifact to the classpath. The file that is read is application.json. With the same artifact we can read a HOCON formatted configuration file that is named application.conf. Finally if we add the helidon-config-yaml artifact to the classpath we can read a YAML formatted configuration file that is named application.yaml or application.yml. Helidon SE will only read one configuration file from the classpath with the following order of preference:

  1. application.yaml or application.yml,
  2. application.conf,
  3. application.json,
  4. application.properties.

In the following example class we create a default configuration using Config.create() and we show the contents of the configuration property app.message:

// File: src/main/java/mrhaki/helidon/Application.java
package mrhaki.helidon;

import io.helidon.config.Config;

public class Application {

    public static void main(String[] args) {
        // Create the default configuration.
        // Configuration properties are read from in order:
        // - from Java system properties
        // - from system environment variables
        // - from a file on the classpath that has the name
        //   'application.properties' (based on default config
        //   parser that is part of the artifact helidon-config).
        Config config = Config.create();

        // Get the configuration property app.message.
        // If the property is not set, the fallback value
        // is defined as "Hello from application code".
        String message = config.get("app.message")
                               .asString()
                               .orElse("Hello from application code");

        // Print the value of the configuration property to System.out.
        System.out.printf("app.message = %s%n", message);
    }
}

In our pom.xml we first only have the dependency for the artifact helidon-config:

...
<dependencies>
    <dependency>
        <groupId>io.helidon.config</groupId>
        <artifactId>helidon-config</artifactId>
    </dependency>
</dependencies>
...

Let’s build our application and run it without any configuration properties and rely on the default value that we defined in our code:

$ helidon build
[INFO] Scanning for projects...
[INFO] ------------------------------------------------------------------------
[INFO] Detecting the operating system and CPU architecture
[INFO] ------------------------------------------------------------------------
[INFO] os.detected.name: osx
[INFO] os.detected.arch: x86_64
[INFO] os.detected.version: 15.0
[INFO] os.detected.version.major: 15
[INFO] os.detected.version.minor: 0
[INFO] os.detected.classifier: osx-x86_64
[INFO]
[INFO] -----------------------< mrhaki.helidon:config >------------------------
[INFO] Building config 0.0.0-SNAPSHOT
[INFO]   from pom.xml
...

$ java -jar target/config.jar
app.message = Hello from application code

Next we add the file application.properties to the directory src/main/resources. This will put the file in the JAR file we build and make it available on the classpath:

# File: src/main/resources/application.properties
app.message=Hello from 'application.properties'

When we build and run our application again we see that the value of the configuration property app.message is read from the file application.properties on the classpath:

$ helidon build
...

$ java -jar target/config.jar
app.message = Hello from 'application.properties'

Our code also support setting the configuration property using environment variables. The value set by the environment variable APP_MESSAGE will overrule the value found in application.properties:

$ APP_MESSAGE="Hello from environment variable" java -jar target/config.jar
app.message = Hello from environment variable

We can overrule the value of the environment variable by setting the configuration property using the Java system properties:

$ APP_MESSAGE="Hello from environment variable" java -Dapp.message="Hello from Java system property" -jar target/config.jar
app.message = Hello from Java system property

If we replace the artifact helidon-config with helidon-config-hocon we can read a file named application.json from the classpath. First we change our dependency in the pom.xml:

...
<dependencies>
    <dependency>
        <groupId>io.helidon.config</groupId>
        <artifactId>helidon-config-hocon</artifactId>
    </dependency>
</dependencies>
...

Next we add the file application.json in src/main/resources:

{
  "app": {
    "message": "Hello from 'aplication.json'"
  }
}

We can rebuild our application and run it to see the following output:

$ helidon build
...

$ java -jar target/config.jar
app.message = Hello from 'aplication.json'

Instead of a JSON file we can also use file with the extension .conf written in HOCON format. The following example file application.conf in src/main/resources sets the configuration property app.message:

// File: src/main/resources/application.conf
app {
    message = Hello from 'application.conf'
}

When we build and run our application we see the following output:

$ helidon build
...

$ java -jar target/config.jar
app.message = Hello from 'application.conf'

To support a configuration file with the name application.yaml or application.yml in YAML format we must add the artifact helidon-config-yaml as dependency:

...
<dependencies>
    <dependency>
        <groupId>io.helidon.config</groupId>
        <artifactId>helidon-config-yaml</artifactId>
    </dependency>
</dependencies>
...

Our example application.yaml will look like this:

# File: src/amin/resources/application.yaml
app:
  message: Hello from 'application.yaml'

For the final time we build and run the application to show the output:

$ helidon build
...

$ java -jar target/config.jar
app.message = Hello from 'application.yaml'

Written with Helidon SE 4.1.2.

October 12, 2024

Helidon SE Helpings: Starting Web Server On A Random Port

Helidon SE provides a web server using Java virtual threads. When we configure the web server we can specify a specific port number the server will listen on for incoming request. If we want to use a random port number we must specify the value 0. Helidon will then start the web server on a random port number that is available on our machine.

The following example shows how to start a web server on a random port number. We use Helidon SE to write our code:

package mrhaki.helidon;

import io.helidon.logging.common.LogConfig;
import io.helidon.webserver.WebServer;

public class Application {
    public static void main(String[] args) {
        // Load logging configuration.
        LogConfig.configureRuntime();

        // Configure web server on a random port number.
        WebServer server = WebServer.builder()
            .port(0)  // Use random port number
            .build()
            .start();

        // Print port number the server is listening on.
        System.out.println("WEB server is up at http://localhost:" + server.port());
    }
}

When we start our application we see the following output:

2024.10.11 17:27:36.005 Logging at runtime configured using classpath: /logging.properties
2024.10.11 17:27:36.606 Helidon SE 4.1.2 features: [Config, Encoding, Media, WebServer]
2024.10.11 17:27:36.621 [0x2326f965] http://0.0.0.0:61685 bound for socket '@default'
2024.10.11 17:27:36.639 Started all channels in 28 milliseconds. 863 milliseconds since JVM startup. Java 21.0.4+7-LTS
WEB server is up at http://localhost:61685

The next time we start our application we see a different port number:

2024.10.11 17:28:11.283 Logging at runtime configured using classpath: /logging.properties
2024.10.11 17:28:11.852 Helidon SE 4.1.2 features: [Config, Encoding, Media, WebServer]
2024.10.11 17:28:11.873 [0x28b386dd] http://0.0.0.0:61698 bound for socket '@default'
2024.10.11 17:28:11.892 Started all channels in 41 milliseconds. 835 milliseconds since JVM startup. Java 21.0.4+7-LTS
WEB server is up at http://localhost:61698

We can also use the Helidon Configuration API to configure the web server to use a random port number. We can for example set the port number to 0 in the application.yaml file. In the following example we initialize standard configuration and use it configure the webserver:

package mrhaki.helidon;

import io.helidon.config.Config;
import io.helidon.logging.common.LogConfig;
import io.helidon.webserver.WebServer;

public class Application {
    public static void main(String[] args) {
        // Load logging configuration.
        LogConfig.configureRuntime();

        // Initialize the configuration.
        Config config = Config.create();
        Config.global(config);

        // Configure web server on a random port number.
        WebServer server = WebServer.builder()
            .config(config.get("server"))  // Get port number from configuration.
            .build()
            .start();

        // Print port number the server is listening on.
        System.out.println("WEB server is up at http://localhost:" + server.port());
    }
}

With our new configuration we can use an environment variable SERVER_PORT to set the port number to 0 for our web server. The configuration could also be defined in an application.yaml file:

server:
  port: 0

Written with Helidon SE 4.1.2.